mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-07-24 15:49:42 +02:00
Fix/fix block registration (#1059)
* fix disable button * add backend env for restricting registration * update state management * add allow_signup to app info * move allow_signup to backend only * cleanup docker-compose * potential darkmode fix * fix missing variable * add banner on login page * use random bools for tests * fix initial state bug * fix state reset
This commit is contained in:
parent
3c2744a3da
commit
13e157827c
20 changed files with 107 additions and 52 deletions
|
@ -12,7 +12,6 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- 9091:3000
|
- 9091:3000
|
||||||
environment:
|
environment:
|
||||||
- ALLOW_SIGNUP=true
|
|
||||||
- API_URL=http://mealie-api:9000
|
- API_URL=http://mealie-api:9000
|
||||||
|
|
||||||
# =====================================
|
# =====================================
|
||||||
|
@ -45,6 +44,8 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- 9092:9000
|
- 9092:9000
|
||||||
environment:
|
environment:
|
||||||
|
ALLOW_SIGNUP: "false"
|
||||||
|
|
||||||
DB_ENGINE: sqlite # Optional: 'sqlite', 'postgres'
|
DB_ENGINE: sqlite # Optional: 'sqlite', 'postgres'
|
||||||
# =====================================
|
# =====================================
|
||||||
# Postgres Config
|
# Postgres Config
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
| API_PORT | 9000 | The port exposed by backend API. **Do not change this if you're running in Docker** |
|
| API_PORT | 9000 | The port exposed by backend API. **Do not change this if you're running in Docker** |
|
||||||
| API_DOCS | True | Turns on/off access to the API documentation locally. |
|
| API_DOCS | True | Turns on/off access to the API documentation locally. |
|
||||||
| TZ | UTC | Must be set to get correct date/time on the server |
|
| TZ | UTC | Must be set to get correct date/time on the server |
|
||||||
|
| ALLOW_SIGNUP | true | Allow user sign-up without token (should match frontend env) |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,4 +61,4 @@ Changing the webworker settings may cause unforeseen memory leak issues with Mea
|
||||||
| LDAP_AUTH_ENABLED | False | Authenticate via an external LDAP server in addidion to built-in Mealie auth |
|
| LDAP_AUTH_ENABLED | False | Authenticate via an external LDAP server in addidion to built-in Mealie auth |
|
||||||
| LDAP_SERVER_URL | None | LDAP server URL (e.g. ldap://ldap.example.com) |
|
| LDAP_SERVER_URL | None | LDAP server URL (e.g. ldap://ldap.example.com) |
|
||||||
| LDAP_BIND_TEMPLATE | None | Templated DN for users, `{}` will be replaced with the username (e.g. `cn={},dc=example,dc=com`) |
|
| LDAP_BIND_TEMPLATE | None | Templated DN for users, `{}` will be replaced with the username (e.g. `cn={},dc=example,dc=com`) |
|
||||||
| LDAP_ADMIN_FILTER | None | Optional LDAP filter, which tells Mealie the LDAP user is an admin (e.g. `(memberOf=cn=admins,dc=example,dc=com)`) |
|
| LDAP_ADMIN_FILTER | None | Optional LDAP filter, which tells Mealie the LDAP user is an admin (e.g. `(memberOf=cn=admins,dc=example,dc=com)`) |
|
||||||
|
|
|
@ -4,12 +4,11 @@
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
|
||||||
| Variables | Default | Description |
|
| Variables | Default | Description |
|
||||||
| ------------ | :--------------------: | ----------------------------------- |
|
| --------- | :--------------------: | ------------------------- |
|
||||||
| ALLOW_SIGNUP | true | Allows anyone to sign-up for Mealie |
|
| API_URL | http://mealie-api:9000 | URL to proxy API requests |
|
||||||
| API_URL | http://mealie-api:9000 | URL to proxy API requests |
|
|
||||||
|
|
||||||
### Themeing
|
### Themeing
|
||||||
Setting the following environmental variables will change the theme of the frontend. Note that the themes are the same for all users. This is a break-change when migration from v0.x.x -> 1.x.x.
|
Setting the following environmental variables will change the theme of the frontend. Note that the themes are the same for all users. This is a break-change when migration from v0.x.x -> 1.x.x.
|
||||||
|
|
||||||
| Variables | Default | Description |
|
| Variables | Default | Description |
|
||||||
|
|
|
@ -18,13 +18,12 @@ services:
|
||||||
- mealie-api
|
- mealie-api
|
||||||
environment:
|
environment:
|
||||||
# Set Frontend ENV Variables Here
|
# Set Frontend ENV Variables Here
|
||||||
- ALLOW_SIGNUP=true
|
|
||||||
- API_URL=http://mealie-api:9000 # (1)
|
- API_URL=http://mealie-api:9000 # (1)
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- "9925:3000" # (2)
|
- "9925:3000" # (2)
|
||||||
volumes:
|
volumes:
|
||||||
- mealie-data:/app/data/ # (3)
|
- mealie-data:/app/data/ # (3)
|
||||||
mealie-api:
|
mealie-api:
|
||||||
image: hkotel/mealie:api-nightly
|
image: hkotel/mealie:api-nightly
|
||||||
container_name: mealie-api
|
container_name: mealie-api
|
||||||
|
@ -34,6 +33,7 @@ services:
|
||||||
- mealie-data:/app/data/
|
- mealie-data:/app/data/
|
||||||
environment:
|
environment:
|
||||||
# Set Backend ENV Variables Here
|
# Set Backend ENV Variables Here
|
||||||
|
- ALLOW_SIGNUP=true
|
||||||
- PUID=1000
|
- PUID=1000
|
||||||
- PGID=1000
|
- PGID=1000
|
||||||
- TZ=America/Anchorage
|
- TZ=America/Anchorage
|
||||||
|
@ -64,7 +64,7 @@ volumes:
|
||||||
|
|
||||||
<!-- Updating This? Be Sure to also update the SQLite Annotations -->
|
<!-- Updating This? Be Sure to also update the SQLite Annotations -->
|
||||||
|
|
||||||
1. Whoa whoa whoa, what is this nonsense? The API_URL is the URL the frontend container uses to proxy api requests to the backend server. In this example, the name `mealie-api` resolves to the `mealie-api` container which runs the API server on port 9000. This allows you to access the API without exposing an additional port on the host.
|
1. Whoa whoa whoa, what is this nonsense? The API_URL is the URL the frontend container uses to proxy api requests to the backend server. In this example, the name `mealie-api` resolves to the `mealie-api` container which runs the API server on port 9000. This allows you to access the API without exposing an additional port on the host.
|
||||||
<br/> <br/> **Note** that both containers must be on the same docker-network for this to work.
|
<br/> <br/> **Note** that both containers must be on the same docker-network for this to work.
|
||||||
2. To access the mealie interface you only need to expose port 3000 on the mealie-frontend container. Here we expose port 9925 on the host, feel free to change this to any port you like.
|
2. To access the mealie interface you only need to expose port 3000 on the mealie-frontend container. Here we expose port 9925 on the host, feel free to change this to any port you like.
|
||||||
3. Mounting the data directory to the frontend is now required to access the images/assets directory. This can be mounted read-only. Internally the frontend containers runs a Caddy proxy server that serves the assets requested to reduce load on the backend API.
|
3. Mounting the data directory to the frontend is now required to access the images/assets directory. This can be mounted read-only. Internally the frontend containers runs a Caddy proxy server that serves the assets requested to reduce load on the backend API.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Installing with SQLite
|
# Installing with SQLite
|
||||||
|
|
||||||
SQLite is a popular, open source, self-contained, zero-configuration database that is the ideal choice for Mealie when you have 1-20 Users. Below is a ready to use docker-compose.yaml file for deploying Mealie on your server.
|
SQLite is a popular, open source, self-contained, zero-configuration database that is the ideal choice for Mealie when you have 1-20 Users. Below is a ready to use docker-compose.yaml file for deploying Mealie on your server.
|
||||||
|
|
||||||
**For Environmental Variable Configuration See:**
|
**For Environmental Variable Configuration See:**
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ services:
|
||||||
container_name: mealie-frontend
|
container_name: mealie-frontend
|
||||||
environment:
|
environment:
|
||||||
# Set Frontend ENV Variables Here
|
# Set Frontend ENV Variables Here
|
||||||
- ALLOW_SIGNUP=true
|
|
||||||
- API_URL=http://mealie-api:9000 # (1)
|
- API_URL=http://mealie-api:9000 # (1)
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
|
@ -30,6 +29,7 @@ services:
|
||||||
- mealie-data:/app/data/
|
- mealie-data:/app/data/
|
||||||
environment:
|
environment:
|
||||||
# Set Backend ENV Variables Here
|
# Set Backend ENV Variables Here
|
||||||
|
- ALLOW_SIGNUP=true
|
||||||
- PUID=1000
|
- PUID=1000
|
||||||
- PGID=1000
|
- PGID=1000
|
||||||
- TZ=America/Anchorage
|
- TZ=America/Anchorage
|
||||||
|
@ -45,7 +45,7 @@ volumes:
|
||||||
|
|
||||||
<!-- Updating This? Be Sure to also update the Postgres Annotations -->
|
<!-- Updating This? Be Sure to also update the Postgres Annotations -->
|
||||||
|
|
||||||
1. Whoa whoa whoa, what is this nonsense? The API_URL is the URL the frontend container uses to proxy api requests to the backend server. In this example, the name `mealie-api` resolves to the `mealie-api` container which runs the API server on port 9000. This allows you to access the API without exposing an additional port on the host.
|
1. Whoa whoa whoa, what is this nonsense? The API_URL is the URL the frontend container uses to proxy api requests to the backend server. In this example, the name `mealie-api` resolves to the `mealie-api` container which runs the API server on port 9000. This allows you to access the API without exposing an additional port on the host.
|
||||||
<br/> <br/> **Note** that both containers must be on the same docker-network for this to work.
|
<br/> <br/> **Note** that both containers must be on the same docker-network for this to work.
|
||||||
2. To access the mealie interface you only need to expose port 3000 on the mealie-frontend container. Here we expose port 9925 on the host, feel free to change this to any port you like.
|
2. To access the mealie interface you only need to expose port 3000 on the mealie-frontend container. Here we expose port 9925 on the host, feel free to change this to any port you like.
|
||||||
3. Mounting the data directory to the frontend is now required to access the images/assets directory. This can be mounted read-only. Internally the frontend containers runs a Caddy proxy server that serves the assets requested to reduce load on the backend API.
|
3. Mounting the data directory to the frontend is now required to access the images/assets directory. This can be mounted read-only. Internally the frontend containers runs a Caddy proxy server that serves the assets requested to reduce load on the backend API.
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
|
export { useAppInfo } from "./use-app-info";
|
||||||
export { useStaticRoutes } from "./static-routes";
|
export { useStaticRoutes } from "./static-routes";
|
||||||
export { useAdminApi, useUserApi } from "./api-client";
|
export { useAdminApi, useUserApi } from "./api-client";
|
||||||
|
|
11
frontend/composables/api/use-app-info.ts
Normal file
11
frontend/composables/api/use-app-info.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { Ref, useAsync } from "@nuxtjs/composition-api";
|
||||||
|
import { useAsyncKey } from "../use-utils";
|
||||||
|
import { AppInfo } from "~/types/api-types/admin";
|
||||||
|
|
||||||
|
export function useAppInfo(): Ref<AppInfo | null> {
|
||||||
|
return useAsync(async () => {
|
||||||
|
// We use fetch here to reduce need for additional dependencies
|
||||||
|
const data = await fetch("/api/app/about").then((res) => res.json());
|
||||||
|
return data as AppInfo;
|
||||||
|
}, useAsyncKey());
|
||||||
|
}
|
|
@ -2,6 +2,12 @@
|
||||||
<v-app dark>
|
<v-app dark>
|
||||||
<TheSnackbar />
|
<TheSnackbar />
|
||||||
|
|
||||||
|
<v-banner v-if="isDemo" sticky>
|
||||||
|
<div class="text-center">
|
||||||
|
<b> This is a Demo for version: {{ version }} </b> | Username: changeme@email.com | Password: demo
|
||||||
|
</div>
|
||||||
|
</v-banner>
|
||||||
|
|
||||||
<v-main>
|
<v-main>
|
||||||
<v-scroll-x-transition>
|
<v-scroll-x-transition>
|
||||||
<Nuxt />
|
<Nuxt />
|
||||||
|
@ -11,9 +17,23 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "@nuxtjs/composition-api";
|
import { computed, defineComponent } from "@nuxtjs/composition-api";
|
||||||
import TheSnackbar from "~/components/Layout/TheSnackbar.vue";
|
import TheSnackbar from "~/components/Layout/TheSnackbar.vue";
|
||||||
|
import { useAppInfo } from "~/composables/api";
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { TheSnackbar },
|
components: { TheSnackbar },
|
||||||
|
setup() {
|
||||||
|
const appInfo = useAppInfo();
|
||||||
|
|
||||||
|
const isDemo = computed(() => appInfo?.value?.demoStatus || false);
|
||||||
|
|
||||||
|
const version = computed(() => appInfo?.value?.version || "unknown");
|
||||||
|
|
||||||
|
return {
|
||||||
|
appInfo,
|
||||||
|
isDemo,
|
||||||
|
version,
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -26,7 +26,6 @@ export default {
|
||||||
|
|
||||||
env: {
|
env: {
|
||||||
GLOBAL_MIDDLEWARE: process.env.GLOBAL_MIDDLEWARE || null,
|
GLOBAL_MIDDLEWARE: process.env.GLOBAL_MIDDLEWARE || null,
|
||||||
ALLOW_SIGNUP: process.env.ALLOW_SIGNUP || true,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
router: {
|
router: {
|
||||||
|
@ -220,10 +219,6 @@ export default {
|
||||||
|
|
||||||
publicRuntimeConfig: {
|
publicRuntimeConfig: {
|
||||||
GLOBAL_MIDDLEWARE: process.env.GLOBAL_MIDDLEWARE || null,
|
GLOBAL_MIDDLEWARE: process.env.GLOBAL_MIDDLEWARE || null,
|
||||||
ALLOW_SIGNUP: process.env.ALLOW_SIGNUP || true,
|
|
||||||
envProps: {
|
|
||||||
allowSignup: process.env.ALLOW_SIGNUP || true,
|
|
||||||
},
|
|
||||||
SUB_PATH: process.env.SUB_PATH || "",
|
SUB_PATH: process.env.SUB_PATH || "",
|
||||||
axios: {
|
axios: {
|
||||||
browserBaseURL: process.env.SUB_PATH || "",
|
browserBaseURL: process.env.SUB_PATH || "",
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
fluid
|
fluid
|
||||||
class="d-flex justify-center align-center"
|
class="d-flex justify-center align-center"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-off-white': !$vuetify.theme.dark,
|
'bg-off-white': !$vuetify.theme.dark && !isDark,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<v-card tag="section" class="d-flex flex-column align-center" width="600px">
|
<v-card tag="section" class="d-flex flex-column align-center" width="600px">
|
||||||
|
@ -108,6 +108,8 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref, useContext, computed, reactive } from "@nuxtjs/composition-api";
|
import { defineComponent, ref, useContext, computed, reactive } from "@nuxtjs/composition-api";
|
||||||
|
import { useDark } from "@vueuse/core";
|
||||||
|
import { useAppInfo } from "~/composables/api";
|
||||||
import { alert } from "~/composables/use-toast";
|
import { alert } from "~/composables/use-toast";
|
||||||
import { useToggleDarkMode } from "~/composables/use-utils";
|
import { useToggleDarkMode } from "~/composables/use-utils";
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -115,9 +117,9 @@ export default defineComponent({
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const toggleDark = useToggleDarkMode();
|
const toggleDark = useToggleDarkMode();
|
||||||
|
const isDark = useDark();
|
||||||
|
|
||||||
const { $auth } = useContext();
|
const { $auth } = useContext();
|
||||||
const context = useContext();
|
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
email: "",
|
email: "",
|
||||||
|
@ -127,7 +129,9 @@ export default defineComponent({
|
||||||
|
|
||||||
const loggingIn = ref(false);
|
const loggingIn = ref(false);
|
||||||
|
|
||||||
const allowSignup = computed(() => context.env.ALLOW_SIGNUP as boolean);
|
const appInfo = useAppInfo();
|
||||||
|
|
||||||
|
const allowSignup = computed(() => appInfo.value?.allowSignup || false);
|
||||||
|
|
||||||
async function authenticate() {
|
async function authenticate() {
|
||||||
if (form.email.length === 0 || form.password.length === 0) {
|
if (form.email.length === 0 || form.password.length === 0) {
|
||||||
|
@ -148,6 +152,7 @@ export default defineComponent({
|
||||||
// See https://github.com/nuxt-community/axios-module/issues/550
|
// See https://github.com/nuxt-community/axios-module/issues/550
|
||||||
// Import $axios from useContext()
|
// Import $axios from useContext()
|
||||||
// if ($axios.isAxiosError(error) && error.response?.status === 401) {
|
// if ($axios.isAxiosError(error) && error.response?.status === 401) {
|
||||||
|
// @ts-ignore - see above
|
||||||
if (error.response?.status === 401) {
|
if (error.response?.status === 401) {
|
||||||
alert.error("Invalid Credentials");
|
alert.error("Invalid Credentials");
|
||||||
} else {
|
} else {
|
||||||
|
@ -158,6 +163,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
isDark,
|
||||||
form,
|
form,
|
||||||
loggingIn,
|
loggingIn,
|
||||||
allowSignup,
|
allowSignup,
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<v-form ref="domRegisterForm" @submit.prevent="register()">
|
<v-form ref="domRegisterForm" @submit.prevent="register()">
|
||||||
<div class="d-flex justify-center my-2">
|
<div class="d-flex justify-center my-2">
|
||||||
<v-btn-toggle v-model="joinGroup" mandatory tile group color="primary">
|
<v-btn-toggle v-model="joinGroup" mandatory tile group color="primary">
|
||||||
<v-btn :value="false" small @click="joinGroup = false"> Create a Group </v-btn>
|
<v-btn :value="false" small @click="toggleJoinGroup"> Create a Group </v-btn>
|
||||||
<v-btn :value="true" small @click="joinGroup = true"> Join a Group </v-btn>
|
<v-btn :value="true" small @click="toggleJoinGroup"> Join a Group </v-btn>
|
||||||
</v-btn-toggle>
|
</v-btn-toggle>
|
||||||
</div>
|
</div>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
|
@ -99,12 +99,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, reactive, toRefs, ref, useRouter, watch } from "@nuxtjs/composition-api";
|
import { computed, defineComponent, reactive, toRefs, ref, useRouter } from "@nuxtjs/composition-api";
|
||||||
import { validators } from "@/composables/use-validators";
|
import { validators } from "@/composables/use-validators";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { alert } from "~/composables/use-toast";
|
import { alert } from "~/composables/use-toast";
|
||||||
import { useRouterQuery } from "@/composables/use-router";
|
import { useRouteQuery } from "@/composables/use-router";
|
||||||
import { VForm} from "~/types/vuetify";
|
import { VForm } from "~/types/vuetify";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
layout: "basic",
|
layout: "basic",
|
||||||
|
@ -117,18 +117,22 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
const allowSignup = computed(() => process.env.AllOW_SIGNUP);
|
const allowSignup = computed(() => process.env.AllOW_SIGNUP);
|
||||||
|
|
||||||
const token = useRouterQuery("token");
|
const token = useRouteQuery("token");
|
||||||
|
|
||||||
watch(token, (newToken) => {
|
if (token.value) {
|
||||||
if (newToken) {
|
|
||||||
form.groupToken = newToken;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (token) {
|
|
||||||
state.joinGroup = true;
|
state.joinGroup = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleJoinGroup() {
|
||||||
|
if (state.joinGroup) {
|
||||||
|
state.joinGroup = false;
|
||||||
|
token.value = "";
|
||||||
|
} else {
|
||||||
|
state.joinGroup = true;
|
||||||
|
form.group = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const domRegisterForm = ref<VForm | null>(null);
|
const domRegisterForm = ref<VForm | null>(null);
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
|
@ -163,6 +167,7 @@ export default defineComponent({
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token,
|
token,
|
||||||
|
toggleJoinGroup,
|
||||||
domRegisterForm,
|
domRegisterForm,
|
||||||
validators,
|
validators,
|
||||||
allowSignup,
|
allowSignup,
|
||||||
|
|
|
@ -9,6 +9,7 @@ export interface AdminAboutInfo {
|
||||||
production: boolean;
|
production: boolean;
|
||||||
version: string;
|
version: string;
|
||||||
demoStatus: boolean;
|
demoStatus: boolean;
|
||||||
|
allowSignup: boolean;
|
||||||
versionLatest: string;
|
versionLatest: string;
|
||||||
apiPort: number;
|
apiPort: number;
|
||||||
apiDocs: boolean;
|
apiDocs: boolean;
|
||||||
|
@ -29,6 +30,7 @@ export interface AppInfo {
|
||||||
production: boolean;
|
production: boolean;
|
||||||
version: string;
|
version: string;
|
||||||
demoStatus: boolean;
|
demoStatus: boolean;
|
||||||
|
allowSignup: boolean;
|
||||||
}
|
}
|
||||||
export interface AppStatistics {
|
export interface AppStatistics {
|
||||||
totalRecipes: number;
|
totalRecipes: number;
|
||||||
|
|
|
@ -11,9 +11,12 @@ export interface ErrorResponse {
|
||||||
exception?: string;
|
exception?: string;
|
||||||
}
|
}
|
||||||
export interface FileTokenResponse {
|
export interface FileTokenResponse {
|
||||||
file_token: string;
|
fileToken: string;
|
||||||
}
|
}
|
||||||
export interface SuccessResponse {
|
export interface SuccessResponse {
|
||||||
message: string;
|
message: string;
|
||||||
error?: boolean;
|
error?: boolean;
|
||||||
}
|
}
|
||||||
|
export interface ValidationResponse {
|
||||||
|
valid?: boolean;
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ class AppSettings(BaseSettings):
|
||||||
TOKEN_TIME: int = 48 # Time in Hours
|
TOKEN_TIME: int = 48 # Time in Hours
|
||||||
SECRET: str
|
SECRET: str
|
||||||
|
|
||||||
|
ALLOW_SIGNUP: bool = True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def DOCS_URL(self) -> str | None:
|
def DOCS_URL(self) -> str | None:
|
||||||
return "/docs" if self.API_DOCS else None
|
return "/docs" if self.API_DOCS else None
|
||||||
|
@ -119,8 +121,8 @@ def app_settings_constructor(data_dir: Path, production: bool, env_file: Path, e
|
||||||
directly, but rather through this factory function.
|
directly, but rather through this factory function.
|
||||||
"""
|
"""
|
||||||
app_settings = AppSettings(
|
app_settings = AppSettings(
|
||||||
_env_file=env_file,
|
_env_file=env_file, # type: ignore
|
||||||
_env_file_encoding=env_encoding,
|
_env_file_encoding=env_encoding, # type: ignore
|
||||||
**{"SECRET": determine_secrets(data_dir, production)},
|
**{"SECRET": determine_secrets(data_dir, production)},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ class AdminAboutController(BaseAdminController):
|
||||||
db_type=settings.DB_ENGINE,
|
db_type=settings.DB_ENGINE,
|
||||||
db_url=settings.DB_URL_PUBLIC,
|
db_url=settings.DB_URL_PUBLIC,
|
||||||
default_group=settings.DEFAULT_GROUP,
|
default_group=settings.DEFAULT_GROUP,
|
||||||
|
allow_signup=settings.ALLOW_SIGNUP,
|
||||||
)
|
)
|
||||||
|
|
||||||
@router.get("/statistics", response_model=AppStatistics)
|
@router.get("/statistics", response_model=AppStatistics)
|
||||||
|
|
|
@ -15,4 +15,5 @@ async def get_app_info():
|
||||||
version=APP_VERSION,
|
version=APP_VERSION,
|
||||||
demo_status=settings.IS_DEMO,
|
demo_status=settings.IS_DEMO,
|
||||||
production=settings.PRODUCTION,
|
production=settings.PRODUCTION,
|
||||||
|
allow_signup=settings.ALLOW_SIGNUP,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
from fastapi import APIRouter, status
|
from fastapi import APIRouter, HTTPException, status
|
||||||
|
|
||||||
|
from mealie.core.config import get_app_settings
|
||||||
from mealie.repos.all_repositories import get_repositories
|
from mealie.repos.all_repositories import get_repositories
|
||||||
from mealie.routes._base import BasePublicController, controller
|
from mealie.routes._base import BasePublicController, controller
|
||||||
|
from mealie.schema.response import ErrorResponse
|
||||||
from mealie.schema.user.registration import CreateUserRegistration
|
from mealie.schema.user.registration import CreateUserRegistration
|
||||||
from mealie.schema.user.user import UserOut
|
from mealie.schema.user.user import UserOut
|
||||||
from mealie.services.user_services.registration_service import RegistrationService
|
from mealie.services.user_services.registration_service import RegistrationService
|
||||||
|
@ -13,5 +15,12 @@ router = APIRouter(prefix="/register")
|
||||||
class RegistrationController(BasePublicController):
|
class RegistrationController(BasePublicController):
|
||||||
@router.post("", response_model=UserOut, status_code=status.HTTP_201_CREATED)
|
@router.post("", response_model=UserOut, status_code=status.HTTP_201_CREATED)
|
||||||
def register_new_user(self, data: CreateUserRegistration):
|
def register_new_user(self, data: CreateUserRegistration):
|
||||||
|
settings = get_app_settings()
|
||||||
|
|
||||||
|
if not settings.ALLOW_SIGNUP and data.group_token is None or data.group_token == "":
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN, detail=ErrorResponse.respond("User Registration is Disabled")
|
||||||
|
)
|
||||||
|
|
||||||
registration_service = RegistrationService(self.deps.logger, get_repositories(self.deps.session))
|
registration_service = RegistrationService(self.deps.logger, get_repositories(self.deps.session))
|
||||||
return registration_service.register_user(data)
|
return registration_service.register_user(data)
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from fastapi_camelcase import CamelModel
|
from fastapi_camelcase import CamelModel
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,6 +13,7 @@ class AppInfo(CamelModel):
|
||||||
production: bool
|
production: bool
|
||||||
version: str
|
version: str
|
||||||
demo_status: bool
|
demo_status: bool
|
||||||
|
allow_signup: bool
|
||||||
|
|
||||||
|
|
||||||
class AdminAboutInfo(AppInfo):
|
class AdminAboutInfo(AppInfo):
|
||||||
|
@ -22,7 +21,7 @@ class AdminAboutInfo(AppInfo):
|
||||||
api_port: int
|
api_port: int
|
||||||
api_docs: bool
|
api_docs: bool
|
||||||
db_type: str
|
db_type: str
|
||||||
db_url: Path
|
db_url: str | None
|
||||||
default_group: str
|
default_group: str
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,8 @@ def test_get_preferences(api_client: TestClient, unique_user: TestUser) -> None:
|
||||||
|
|
||||||
preferences = response.json()
|
preferences = response.json()
|
||||||
|
|
||||||
# Spot Check Defaults
|
assert preferences["recipePublic"] in {True, False}
|
||||||
assert preferences["recipePublic"] is True
|
assert preferences["recipeShowNutrition"] in {True, False}
|
||||||
assert preferences["recipeShowNutrition"] is False
|
|
||||||
|
|
||||||
|
|
||||||
def test_preferences_in_group(api_client: TestClient, unique_user: TestUser) -> None:
|
def test_preferences_in_group(api_client: TestClient, unique_user: TestUser) -> None:
|
||||||
|
@ -31,8 +30,8 @@ def test_preferences_in_group(api_client: TestClient, unique_user: TestUser) ->
|
||||||
assert group["preferences"] is not None
|
assert group["preferences"] is not None
|
||||||
|
|
||||||
# Spot Check
|
# Spot Check
|
||||||
assert group["preferences"]["recipePublic"] is True
|
assert group["preferences"]["recipePublic"] in {True, False}
|
||||||
assert group["preferences"]["recipeShowNutrition"] is False
|
assert group["preferences"]["recipeShowNutrition"] in {True, False}
|
||||||
|
|
||||||
|
|
||||||
def test_update_preferences(api_client: TestClient, unique_user: TestUser) -> None:
|
def test_update_preferences(api_client: TestClient, unique_user: TestUser) -> None:
|
||||||
|
|
|
@ -16,13 +16,13 @@ def random_bool() -> bool:
|
||||||
return bool(random.getrandbits(1))
|
return bool(random.getrandbits(1))
|
||||||
|
|
||||||
|
|
||||||
def user_registration_factory() -> CreateUserRegistration:
|
def user_registration_factory(advanced=None, private=None) -> CreateUserRegistration:
|
||||||
return CreateUserRegistration(
|
return CreateUserRegistration(
|
||||||
group=random_string(),
|
group=random_string(),
|
||||||
email=random_email(),
|
email=random_email(),
|
||||||
username=random_string(),
|
username=random_string(),
|
||||||
password="fake-password",
|
password="fake-password",
|
||||||
password_confirm="fake-password",
|
password_confirm="fake-password",
|
||||||
advanced=False,
|
advanced=advanced or random_bool(),
|
||||||
private=False,
|
private=private or random_bool(),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue