mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-08-02 20:15:24 +02:00
feat: auto detect first login (#2722)
* 'hide' default email and password env variables * first login API endpoint * run code-generators * frontend indicators for default username and pw * remove old env variables from docs * fix env set variable * remove password from tests
This commit is contained in:
parent
71f95ca3c6
commit
bc575ec5ae
22 changed files with 234 additions and 120 deletions
|
@ -5,12 +5,10 @@
|
|||
### General
|
||||
|
||||
| Variables | Default | Description |
|
||||
| ---------------- | :-------------------: | ----------------------------------------------------------------------------------- |
|
||||
| ------------- | :-------------------: | ----------------------------------------------------------------------------------- |
|
||||
| PUID | 911 | UserID permissions between host OS and container |
|
||||
| PGID | 911 | GroupID permissions between host OS and container |
|
||||
| DEFAULT_GROUP | Home | The default group for users |
|
||||
| DEFAULT_EMAIL | changeme@example.com | The default username for the superuser |
|
||||
| DEFAULT_PASSWORD | MyPassword | The default password for the superuser |
|
||||
| BASE_URL | http://localhost:8080 | Used for Notifications |
|
||||
| TOKEN_TIME | 48 | The time in hours that a login/auth token is valid |
|
||||
| API_PORT | 9000 | The port exposed by backend API. **Do not change this if you're running in Docker** |
|
||||
|
|
|
@ -3,117 +3,117 @@ export const LOCALES = [
|
|||
{
|
||||
name: "繁體中文 (Chinese traditional)",
|
||||
value: "zh-TW",
|
||||
progress: 26,
|
||||
progress: 28,
|
||||
},
|
||||
{
|
||||
name: "简体中文 (Chinese simplified)",
|
||||
value: "zh-CN",
|
||||
progress: 34,
|
||||
progress: 65,
|
||||
},
|
||||
{
|
||||
name: "Tiếng Việt (Vietnamese)",
|
||||
value: "vi-VN",
|
||||
progress: 0,
|
||||
progress: 2,
|
||||
},
|
||||
{
|
||||
name: "Українська (Ukrainian)",
|
||||
value: "uk-UA",
|
||||
progress: 100,
|
||||
progress: 99,
|
||||
},
|
||||
{
|
||||
name: "Türkçe (Turkish)",
|
||||
value: "tr-TR",
|
||||
progress: 47,
|
||||
progress: 50,
|
||||
},
|
||||
{
|
||||
name: "Svenska (Swedish)",
|
||||
value: "sv-SE",
|
||||
progress: 60,
|
||||
progress: 71,
|
||||
},
|
||||
{
|
||||
name: "српски (Serbian)",
|
||||
value: "sr-SP",
|
||||
progress: 2,
|
||||
progress: 4,
|
||||
},
|
||||
{
|
||||
name: "Slovenian",
|
||||
value: "sl-SI",
|
||||
progress: 47,
|
||||
progress: 49,
|
||||
},
|
||||
{
|
||||
name: "Slovak",
|
||||
value: "sk-SK",
|
||||
progress: 99,
|
||||
progress: 97,
|
||||
},
|
||||
{
|
||||
name: "Pусский (Russian)",
|
||||
value: "ru-RU",
|
||||
progress: 31,
|
||||
progress: 99,
|
||||
},
|
||||
{
|
||||
name: "Română (Romanian)",
|
||||
value: "ro-RO",
|
||||
progress: 12,
|
||||
progress: 32,
|
||||
},
|
||||
{
|
||||
name: "Português (Portuguese)",
|
||||
value: "pt-PT",
|
||||
progress: 69,
|
||||
progress: 99,
|
||||
},
|
||||
{
|
||||
name: "Português do Brasil (Brazilian Portuguese)",
|
||||
value: "pt-BR",
|
||||
progress: 97,
|
||||
progress: 98,
|
||||
},
|
||||
{
|
||||
name: "Polski (Polish)",
|
||||
value: "pl-PL",
|
||||
progress: 99,
|
||||
progress: 97,
|
||||
},
|
||||
{
|
||||
name: "Norsk (Norwegian)",
|
||||
value: "no-NO",
|
||||
progress: 73,
|
||||
progress: 85,
|
||||
},
|
||||
{
|
||||
name: "Nederlands (Dutch)",
|
||||
value: "nl-NL",
|
||||
progress: 100,
|
||||
progress: 98,
|
||||
},
|
||||
{
|
||||
name: "Latvian",
|
||||
value: "lv-LV",
|
||||
progress: 0,
|
||||
progress: 1,
|
||||
},
|
||||
{
|
||||
name: "Lithuanian",
|
||||
value: "lt-LT",
|
||||
progress: 99,
|
||||
progress: 97,
|
||||
},
|
||||
{
|
||||
name: "한국어 (Korean)",
|
||||
value: "ko-KR",
|
||||
progress: 3,
|
||||
progress: 5,
|
||||
},
|
||||
{
|
||||
name: "日本語 (Japanese)",
|
||||
value: "ja-JP",
|
||||
progress: 9,
|
||||
progress: 11,
|
||||
},
|
||||
{
|
||||
name: "Italiano (Italian)",
|
||||
value: "it-IT",
|
||||
progress: 98,
|
||||
progress: 96,
|
||||
},
|
||||
{
|
||||
name: "Magyar (Hungarian)",
|
||||
value: "hu-HU",
|
||||
progress: 43,
|
||||
progress: 99,
|
||||
},
|
||||
{
|
||||
name: "Croatian",
|
||||
value: "hr-HR",
|
||||
progress: 100,
|
||||
progress: 97,
|
||||
},
|
||||
{
|
||||
name: "עברית (Hebrew)",
|
||||
|
@ -123,27 +123,27 @@ export const LOCALES = [
|
|||
{
|
||||
name: "Galician",
|
||||
value: "gl-ES",
|
||||
progress: 0,
|
||||
progress: 1,
|
||||
},
|
||||
{
|
||||
name: "Français (French)",
|
||||
value: "fr-FR",
|
||||
progress: 100,
|
||||
progress: 99,
|
||||
},
|
||||
{
|
||||
name: "French, Canada",
|
||||
value: "fr-CA",
|
||||
progress: 54,
|
||||
progress: 97,
|
||||
},
|
||||
{
|
||||
name: "Suomi (Finnish)",
|
||||
value: "fi-FI",
|
||||
progress: 31,
|
||||
progress: 95,
|
||||
},
|
||||
{
|
||||
name: "Español (Spanish)",
|
||||
value: "es-ES",
|
||||
progress: 59,
|
||||
progress: 76,
|
||||
},
|
||||
{
|
||||
name: "American English",
|
||||
|
@ -153,46 +153,46 @@ export const LOCALES = [
|
|||
{
|
||||
name: "British English",
|
||||
value: "en-GB",
|
||||
progress: 2,
|
||||
progress: 4,
|
||||
},
|
||||
{
|
||||
name: "Ελληνικά (Greek)",
|
||||
value: "el-GR",
|
||||
progress: 33,
|
||||
progress: 35,
|
||||
},
|
||||
{
|
||||
name: "Deutsch (German)",
|
||||
value: "de-DE",
|
||||
progress: 100,
|
||||
progress: 99,
|
||||
},
|
||||
{
|
||||
name: "Dansk (Danish)",
|
||||
value: "da-DK",
|
||||
progress: 90,
|
||||
progress: 100,
|
||||
},
|
||||
{
|
||||
name: "Čeština (Czech)",
|
||||
value: "cs-CZ",
|
||||
progress: 60,
|
||||
progress: 66,
|
||||
},
|
||||
{
|
||||
name: "Català (Catalan)",
|
||||
value: "ca-ES",
|
||||
progress: 54,
|
||||
progress: 61,
|
||||
},
|
||||
{
|
||||
name: "Bulgarian",
|
||||
value: "bg-BG",
|
||||
progress: 13,
|
||||
progress: 99,
|
||||
},
|
||||
{
|
||||
name: "العربية (Arabic)",
|
||||
value: "ar-SA",
|
||||
progress: 7,
|
||||
progress: 16,
|
||||
},
|
||||
{
|
||||
name: "Afrikaans (Afrikaans)",
|
||||
value: "af-ZA",
|
||||
progress: 0,
|
||||
progress: 96,
|
||||
},
|
||||
]
|
||||
|
|
|
@ -868,7 +868,9 @@
|
|||
"user-can-invite-other-to-group": "User can invite other to group",
|
||||
"user-can-manage-group": "User can manage group",
|
||||
"user-can-organize-group-data": "User can organize group data",
|
||||
"enable-advanced-features": "Enable advanced features"
|
||||
"enable-advanced-features": "Enable advanced features",
|
||||
"it-looks-like-this-is-your-first-time-logging-in": "It looks like this is your first time logging in.",
|
||||
"dont-want-to-see-this-anymore-be-sure-to-change-your-email": "Don't want to see this anymore? Be sure to change your email in your user settings!"
|
||||
},
|
||||
"language-dialog": {
|
||||
"translated": "translated",
|
||||
|
|
|
@ -34,6 +34,9 @@ export interface AppInfo {
|
|||
demoStatus: boolean;
|
||||
allowSignup: boolean;
|
||||
}
|
||||
export interface AppStartupInfo {
|
||||
isFirstLogin: boolean;
|
||||
}
|
||||
export interface AppStatistics {
|
||||
totalRecipes: number;
|
||||
totalUsers: number;
|
||||
|
@ -41,6 +44,22 @@ export interface AppStatistics {
|
|||
uncategorizedRecipes: number;
|
||||
untaggedRecipes: number;
|
||||
}
|
||||
export interface AppTheme {
|
||||
lightPrimary?: string;
|
||||
lightAccent?: string;
|
||||
lightSecondary?: string;
|
||||
lightSuccess?: string;
|
||||
lightInfo?: string;
|
||||
lightWarning?: string;
|
||||
lightError?: string;
|
||||
darkPrimary?: string;
|
||||
darkAccent?: string;
|
||||
darkSecondary?: string;
|
||||
darkSuccess?: string;
|
||||
darkInfo?: string;
|
||||
darkWarning?: string;
|
||||
darkError?: string;
|
||||
}
|
||||
export interface BackupOptions {
|
||||
recipes?: boolean;
|
||||
settings?: boolean;
|
||||
|
|
|
@ -6,7 +6,14 @@
|
|||
*/
|
||||
|
||||
export type WebhookType = "mealplan";
|
||||
export type SupportedMigrations = "nextcloud" | "chowdown" | "copymethat" | "paprika" | "mealie_alpha" | "tandoor" | "plantoeat";
|
||||
export type SupportedMigrations =
|
||||
| "nextcloud"
|
||||
| "chowdown"
|
||||
| "copymethat"
|
||||
| "paprika"
|
||||
| "mealie_alpha"
|
||||
| "tandoor"
|
||||
| "plantoeat";
|
||||
|
||||
export interface CreateGroupPreferences {
|
||||
privateGroup?: boolean;
|
||||
|
@ -263,39 +270,56 @@ export interface RecipeIngredient {
|
|||
}
|
||||
export interface IngredientUnit {
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
pluralAbbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
aliases?: IngredientUnitAlias[];
|
||||
id: string;
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
}
|
||||
export interface IngredientUnitAlias {
|
||||
name: string;
|
||||
}
|
||||
export interface CreateIngredientUnit {
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
pluralAbbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
aliases?: CreateIngredientUnitAlias[];
|
||||
}
|
||||
export interface CreateIngredientUnitAlias {
|
||||
name: string;
|
||||
}
|
||||
export interface IngredientFood {
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
labelId?: string;
|
||||
aliases?: IngredientFoodAlias[];
|
||||
id: string;
|
||||
label?: MultiPurposeLabelSummary;
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
}
|
||||
export interface IngredientFoodAlias {
|
||||
name: string;
|
||||
}
|
||||
export interface MultiPurposeLabelSummary {
|
||||
name: string;
|
||||
color?: string;
|
||||
|
@ -304,11 +328,16 @@ export interface MultiPurposeLabelSummary {
|
|||
}
|
||||
export interface CreateIngredientFood {
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
labelId?: string;
|
||||
aliases?: CreateIngredientFoodAlias[];
|
||||
}
|
||||
export interface CreateIngredientFoodAlias {
|
||||
name: string;
|
||||
}
|
||||
export interface ShoppingListCreate {
|
||||
name?: string;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
||||
*/
|
||||
|
||||
export type OrderByNullPosition = "first" | "last";
|
||||
export type OrderDirection = "asc" | "desc";
|
||||
|
||||
export interface ErrorResponse {
|
||||
|
@ -19,6 +20,7 @@ export interface PaginationQuery {
|
|||
page?: number;
|
||||
perPage?: number;
|
||||
orderBy?: string;
|
||||
orderByNullPosition?: OrderByNullPosition;
|
||||
orderDirection?: OrderDirection & string;
|
||||
queryFilter?: string;
|
||||
paginationSeed?: string;
|
||||
|
|
|
@ -7,6 +7,18 @@
|
|||
'bg-off-white': !$vuetify.theme.dark && !isDark,
|
||||
}"
|
||||
>
|
||||
<v-alert v-if="isFirstLogin" class="my-4" type="info" icon="mdi-information">
|
||||
<div>
|
||||
<p class="mb-3">
|
||||
{{ $tc('user.it-looks-like-this-is-your-first-time-logging-in')}}
|
||||
</p>
|
||||
<p class="mb-1"><strong>{{ $tc('user.username') }}:</strong> changeme@example.com</p>
|
||||
<p class="mb-3"><strong>{{ $tc('user.password') }}:</strong> MyPassword</p>
|
||||
<p>
|
||||
{{ $tc('user.dont-want-to-see-this-anymore-be-sure-to-change-your-email') }}
|
||||
</p>
|
||||
</div>
|
||||
</v-alert>
|
||||
<v-card tag="section" class="d-flex flex-column align-center" width="600px">
|
||||
<v-toolbar width="100%" color="primary" class="d-flex justify-center mb-4" dark>
|
||||
<v-toolbar-title class="headline text-h4"> Mealie </v-toolbar-title>
|
||||
|
@ -101,12 +113,14 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, useContext, computed, reactive, useRouter } from "@nuxtjs/composition-api";
|
||||
import { defineComponent, ref, useContext, computed, reactive, useRouter, useAsync } from "@nuxtjs/composition-api";
|
||||
import { useDark, whenever } from "@vueuse/core";
|
||||
import { useLoggedInState } from "~/composables/use-logged-in-state";
|
||||
import { useAppInfo } from "~/composables/api";
|
||||
import { usePasswordField } from "~/composables/use-passwords";
|
||||
import { alert } from "~/composables/use-toast";
|
||||
import { useAsyncKey } from "~/composables/use-utils";
|
||||
import { AppStartupInfo } from "~/lib/api/types/admin";
|
||||
|
||||
export default defineComponent({
|
||||
layout: "blank",
|
||||
|
@ -115,7 +129,7 @@ export default defineComponent({
|
|||
const isDark = useDark();
|
||||
|
||||
const router = useRouter();
|
||||
const { $auth, i18n } = useContext();
|
||||
const { $auth, i18n, $axios } = useContext();
|
||||
const { loggedIn } = useLoggedInState();
|
||||
const groupSlug = computed(() => $auth.user?.groupSlug);
|
||||
|
||||
|
@ -133,6 +147,13 @@ export default defineComponent({
|
|||
remember: false,
|
||||
});
|
||||
|
||||
const isFirstLogin = ref(false)
|
||||
|
||||
useAsync(async () => {
|
||||
const data = await $axios.get<AppStartupInfo>("/api/app/about/startup-info");
|
||||
isFirstLogin.value = data.data.isFirstLogin;
|
||||
}, useAsyncKey());
|
||||
|
||||
const loggingIn = ref(false);
|
||||
|
||||
const appInfo = useAppInfo();
|
||||
|
@ -182,6 +203,7 @@ export default defineComponent({
|
|||
passwordIcon,
|
||||
inputType,
|
||||
togglePasswordShow,
|
||||
isFirstLogin
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
@ -104,7 +104,6 @@ async def system_startup():
|
|||
indent=4,
|
||||
exclude={
|
||||
"SECRET",
|
||||
"DEFAULT_PASSWORD",
|
||||
"SFTP_PASSWORD",
|
||||
"SFTP_USERNAME",
|
||||
"DB_URL", # replace by DB_URL_PUBLIC for logs
|
||||
|
|
|
@ -85,8 +85,17 @@ class AppSettings(BaseSettings):
|
|||
return self.DB_PROVIDER.db_url_public if self.DB_PROVIDER else None
|
||||
|
||||
DEFAULT_GROUP: str = "Home"
|
||||
DEFAULT_EMAIL: str = "changeme@example.com"
|
||||
DEFAULT_PASSWORD: str = "MyPassword"
|
||||
|
||||
_DEFAULT_EMAIL: str = "changeme@example.com"
|
||||
"""
|
||||
This is the default email used for the first user created in the database. This is only used if no users
|
||||
exist in the database. it should no longer be set by end users.
|
||||
"""
|
||||
_DEFAULT_PASSWORD: str = "MyPassword"
|
||||
"""
|
||||
This is the default password used for the first user created in the database. This is only used if no users
|
||||
exist in the database. it should no longer be set by end users.
|
||||
"""
|
||||
|
||||
# ===============================================
|
||||
# Email Configuration
|
||||
|
|
|
@ -13,7 +13,7 @@ def dev_users() -> list[dict]:
|
|||
"full_name": "Jason",
|
||||
"username": "jason",
|
||||
"email": "jason@example.com",
|
||||
"password": hash_password(settings.DEFAULT_PASSWORD),
|
||||
"password": hash_password(settings._DEFAULT_PASSWORD),
|
||||
"group": settings.DEFAULT_GROUP,
|
||||
"admin": False,
|
||||
},
|
||||
|
@ -21,7 +21,7 @@ def dev_users() -> list[dict]:
|
|||
"full_name": "Bob",
|
||||
"username": "bob",
|
||||
"email": "bob@example.com",
|
||||
"password": hash_password(settings.DEFAULT_PASSWORD),
|
||||
"password": hash_password(settings._DEFAULT_PASSWORD),
|
||||
"group": settings.DEFAULT_GROUP,
|
||||
"admin": False,
|
||||
},
|
||||
|
@ -29,7 +29,7 @@ def dev_users() -> list[dict]:
|
|||
"full_name": "Sarah",
|
||||
"username": "sarah",
|
||||
"email": "sarah@example.com",
|
||||
"password": hash_password(settings.DEFAULT_PASSWORD),
|
||||
"password": hash_password(settings._DEFAULT_PASSWORD),
|
||||
"group": settings.DEFAULT_GROUP,
|
||||
"admin": False,
|
||||
},
|
||||
|
@ -37,7 +37,7 @@ def dev_users() -> list[dict]:
|
|||
"full_name": "Sammy",
|
||||
"username": "sammy",
|
||||
"email": "sammy@example.com",
|
||||
"password": hash_password(settings.DEFAULT_PASSWORD),
|
||||
"password": hash_password(settings._DEFAULT_PASSWORD),
|
||||
"group": settings.DEFAULT_GROUP,
|
||||
"admin": False,
|
||||
},
|
||||
|
@ -48,8 +48,8 @@ def default_user_init(db: AllRepositories):
|
|||
default_user = {
|
||||
"full_name": "Change Me",
|
||||
"username": "admin",
|
||||
"email": settings.DEFAULT_EMAIL,
|
||||
"password": hash_password(settings.DEFAULT_PASSWORD),
|
||||
"email": settings._DEFAULT_EMAIL,
|
||||
"password": hash_password(settings._DEFAULT_PASSWORD),
|
||||
"group": settings.DEFAULT_GROUP,
|
||||
"admin": True,
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
from fastapi import APIRouter, Response
|
||||
from fastapi import APIRouter, Depends, Response
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.config import get_app_settings
|
||||
from mealie.core.settings.static import APP_VERSION
|
||||
from mealie.schema.admin.about import AppInfo, AppTheme
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.db.models.users.users import User
|
||||
from mealie.schema.admin.about import AppInfo, AppStartupInfo, AppTheme
|
||||
|
||||
router = APIRouter(prefix="/about")
|
||||
|
||||
|
@ -20,6 +23,21 @@ def get_app_info():
|
|||
)
|
||||
|
||||
|
||||
@router.get("/startup-info", response_model=AppStartupInfo)
|
||||
def get_startup_info(session: Session = Depends(generate_session)):
|
||||
"""returns helpful startup information"""
|
||||
settings = get_app_settings()
|
||||
|
||||
is_first_login = False
|
||||
with session as db:
|
||||
if db.query(User).filter_by(email=settings._DEFAULT_EMAIL).count() > 0:
|
||||
is_first_login = True
|
||||
|
||||
return AppStartupInfo(
|
||||
is_first_login=is_first_login,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/theme", response_model=AppTheme)
|
||||
def get_app_theme(resp: Response):
|
||||
"""Get's the current theme settings"""
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# This file is auto-generated by gen_schema_exports.py
|
||||
from .about import AdminAboutInfo, AppInfo, AppStatistics, CheckAppConfig, DockerVolumeText
|
||||
from .about import AdminAboutInfo, AppInfo, AppStartupInfo, AppStatistics, AppTheme, CheckAppConfig, DockerVolumeText
|
||||
from .backup import AllBackups, BackupFile, BackupOptions, CreateBackup, ImportJob
|
||||
from .email import EmailReady, EmailSuccess, EmailTest
|
||||
from .maintenance import MaintenanceLogs, MaintenanceStorageDetails, MaintenanceSummary
|
||||
|
@ -27,7 +27,9 @@ __all__ = [
|
|||
"MaintenanceSummary",
|
||||
"AdminAboutInfo",
|
||||
"AppInfo",
|
||||
"AppStartupInfo",
|
||||
"AppStatistics",
|
||||
"AppTheme",
|
||||
"CheckAppConfig",
|
||||
"DockerVolumeText",
|
||||
"EmailReady",
|
||||
|
|
|
@ -34,6 +34,15 @@ class AppTheme(MealieModel):
|
|||
dark_error: str = "#EF5350"
|
||||
|
||||
|
||||
class AppStartupInfo(MealieModel):
|
||||
is_first_login: bool
|
||||
"""
|
||||
The applications best guess that a user hasn't logged in. Currently, it really
|
||||
on indicates that the 'changeme@example.com' user is still in the database. Once
|
||||
it is removed, this will always return False.
|
||||
"""
|
||||
|
||||
|
||||
class AdminAboutInfo(AppInfo):
|
||||
versionLatest: str
|
||||
api_port: int
|
||||
|
|
|
@ -92,6 +92,25 @@ __all__ = [
|
|||
"RecipeToolOut",
|
||||
"RecipeToolResponse",
|
||||
"RecipeToolSave",
|
||||
"RecipeTimelineEventCreate",
|
||||
"RecipeTimelineEventIn",
|
||||
"RecipeTimelineEventOut",
|
||||
"RecipeTimelineEventPagination",
|
||||
"RecipeTimelineEventUpdate",
|
||||
"TimelineEventImage",
|
||||
"TimelineEventType",
|
||||
"RecipeAsset",
|
||||
"RecipeSettings",
|
||||
"RecipeShareToken",
|
||||
"RecipeShareTokenCreate",
|
||||
"RecipeShareTokenSave",
|
||||
"RecipeShareTokenSummary",
|
||||
"RecipeDuplicate",
|
||||
"RecipeSlug",
|
||||
"RecipeZipTokenResponse",
|
||||
"SlugResponse",
|
||||
"UpdateImageResponse",
|
||||
"RecipeNote",
|
||||
"CategoryBase",
|
||||
"CategoryIn",
|
||||
"CategoryOut",
|
||||
|
@ -102,6 +121,12 @@ __all__ = [
|
|||
"TagIn",
|
||||
"TagOut",
|
||||
"TagSave",
|
||||
"RecipeCommentCreate",
|
||||
"RecipeCommentOut",
|
||||
"RecipeCommentPagination",
|
||||
"RecipeCommentSave",
|
||||
"RecipeCommentUpdate",
|
||||
"UserBase",
|
||||
"AssignCategories",
|
||||
"AssignSettings",
|
||||
"AssignTags",
|
||||
|
@ -109,34 +134,10 @@ __all__ = [
|
|||
"ExportBase",
|
||||
"ExportRecipes",
|
||||
"ExportTypes",
|
||||
"RecipeShareToken",
|
||||
"RecipeShareTokenCreate",
|
||||
"RecipeShareTokenSave",
|
||||
"RecipeShareTokenSummary",
|
||||
"ScrapeRecipe",
|
||||
"ScrapeRecipeTest",
|
||||
"RecipeCommentCreate",
|
||||
"RecipeCommentOut",
|
||||
"RecipeCommentPagination",
|
||||
"RecipeCommentSave",
|
||||
"RecipeCommentUpdate",
|
||||
"UserBase",
|
||||
"RecipeImageTypes",
|
||||
"CreateRecipe",
|
||||
"CreateRecipeBulk",
|
||||
"CreateRecipeByUrlBulk",
|
||||
"Recipe",
|
||||
"RecipeCategory",
|
||||
"RecipeCategoryPagination",
|
||||
"RecipeLastMade",
|
||||
"RecipePagination",
|
||||
"RecipeSummary",
|
||||
"RecipeTag",
|
||||
"RecipeTagPagination",
|
||||
"RecipeTool",
|
||||
"RecipeToolPagination",
|
||||
"IngredientReferences",
|
||||
"RecipeStep",
|
||||
"RecipeImageTypes",
|
||||
"Nutrition",
|
||||
"CreateIngredientFood",
|
||||
"CreateIngredientFoodAlias",
|
||||
"CreateIngredientUnit",
|
||||
|
@ -159,20 +160,19 @@ __all__ = [
|
|||
"SaveIngredientFood",
|
||||
"SaveIngredientUnit",
|
||||
"UnitFoodBase",
|
||||
"RecipeAsset",
|
||||
"RecipeTimelineEventCreate",
|
||||
"RecipeTimelineEventIn",
|
||||
"RecipeTimelineEventOut",
|
||||
"RecipeTimelineEventPagination",
|
||||
"RecipeTimelineEventUpdate",
|
||||
"TimelineEventImage",
|
||||
"TimelineEventType",
|
||||
"RecipeDuplicate",
|
||||
"RecipeSlug",
|
||||
"RecipeZipTokenResponse",
|
||||
"SlugResponse",
|
||||
"UpdateImageResponse",
|
||||
"Nutrition",
|
||||
"RecipeSettings",
|
||||
"RecipeNote",
|
||||
"CreateRecipe",
|
||||
"CreateRecipeBulk",
|
||||
"CreateRecipeByUrlBulk",
|
||||
"Recipe",
|
||||
"RecipeCategory",
|
||||
"RecipeCategoryPagination",
|
||||
"RecipeLastMade",
|
||||
"RecipePagination",
|
||||
"RecipeSummary",
|
||||
"RecipeTag",
|
||||
"RecipeTagPagination",
|
||||
"RecipeTool",
|
||||
"RecipeToolPagination",
|
||||
"ScrapeRecipe",
|
||||
"ScrapeRecipeTest",
|
||||
]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# This file is auto-generated by gen_schema_exports.py
|
||||
from .pagination import OrderDirection, PaginationBase, PaginationQuery, RecipeSearchQuery
|
||||
from .pagination import OrderByNullPosition, OrderDirection, PaginationBase, PaginationQuery, RecipeSearchQuery
|
||||
from .query_filter import LogicalOperator, QueryFilter, QueryFilterComponent, RelationalKeyword, RelationalOperator
|
||||
from .query_search import SearchFilter
|
||||
from .responses import ErrorResponse, FileTokenResponse, SuccessResponse
|
||||
|
@ -15,6 +15,7 @@ __all__ = [
|
|||
"QueryFilterComponent",
|
||||
"RelationalKeyword",
|
||||
"RelationalOperator",
|
||||
"OrderByNullPosition",
|
||||
"OrderDirection",
|
||||
"PaginationBase",
|
||||
"PaginationQuery",
|
||||
|
|
|
@ -116,7 +116,7 @@ class UserOut(UserBase):
|
|||
|
||||
@property
|
||||
def is_default_user(self) -> bool:
|
||||
return self.email == settings.DEFAULT_EMAIL.strip().lower()
|
||||
return self.email == settings._DEFAULT_EMAIL.strip().lower()
|
||||
|
||||
@classmethod
|
||||
def loader_options(cls) -> list[LoaderOption]:
|
||||
|
|
6
tests/fixtures/fixture_admin.py
vendored
6
tests/fixtures/fixture_admin.py
vendored
|
@ -10,7 +10,7 @@ from tests.utils import api_routes
|
|||
def admin_token(api_client: TestClient):
|
||||
settings = get_app_settings()
|
||||
|
||||
form_data = {"username": settings.DEFAULT_EMAIL, "password": settings.DEFAULT_PASSWORD}
|
||||
form_data = {"username": settings._DEFAULT_EMAIL, "password": settings._DEFAULT_PASSWORD}
|
||||
return utils.login(form_data, api_client)
|
||||
|
||||
|
||||
|
@ -18,7 +18,7 @@ def admin_token(api_client: TestClient):
|
|||
def admin_user(api_client: TestClient):
|
||||
settings = get_app_settings()
|
||||
|
||||
form_data = {"username": settings.DEFAULT_EMAIL, "password": settings.DEFAULT_PASSWORD}
|
||||
form_data = {"username": settings._DEFAULT_EMAIL, "password": settings._DEFAULT_PASSWORD}
|
||||
|
||||
token = utils.login(form_data, api_client)
|
||||
|
||||
|
@ -33,7 +33,7 @@ def admin_user(api_client: TestClient):
|
|||
yield utils.TestUser(
|
||||
_group_id=user_data.get("groupId"),
|
||||
user_id=user_data.get("id"),
|
||||
password=settings.DEFAULT_PASSWORD,
|
||||
password=settings._DEFAULT_PASSWORD,
|
||||
username=user_data.get("username"),
|
||||
email=user_data.get("email"),
|
||||
token=token,
|
||||
|
|
|
@ -36,7 +36,7 @@ def test_init_superuser(api_client: TestClient, admin_user: TestUser):
|
|||
assert admin_data["groupId"] == admin_user.group_id
|
||||
|
||||
assert admin_data["fullName"] == "Change Me"
|
||||
assert admin_data["email"] == settings.DEFAULT_EMAIL
|
||||
assert admin_data["email"] == settings._DEFAULT_EMAIL
|
||||
|
||||
|
||||
def test_create_user(api_client: TestClient, admin_token):
|
||||
|
@ -95,7 +95,7 @@ def test_update_other_user_as_not_admin(api_client: TestClient, unique_user: Tes
|
|||
update_data = {
|
||||
"id": unique_user.user_id,
|
||||
"fullName": "Updated Name",
|
||||
"email": settings.DEFAULT_EMAIL,
|
||||
"email": settings._DEFAULT_EMAIL,
|
||||
"group": "Home",
|
||||
"admin": True,
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ from tests.utils.fixture_schemas import TestUser
|
|||
def test_failed_login(api_client: TestClient):
|
||||
settings = get_app_settings()
|
||||
|
||||
form_data = {"username": settings.DEFAULT_EMAIL, "password": "WRONG_PASSWORD"}
|
||||
form_data = {"username": settings._DEFAULT_EMAIL, "password": "WRONG_PASSWORD"}
|
||||
response = api_client.post(api_routes.auth_token, data=form_data)
|
||||
|
||||
assert response.status_code == 401
|
||||
|
@ -23,7 +23,7 @@ def test_failed_login(api_client: TestClient):
|
|||
def test_superuser_login(api_client: TestClient, admin_token):
|
||||
settings = get_app_settings()
|
||||
|
||||
form_data = {"username": settings.DEFAULT_EMAIL, "password": settings.DEFAULT_PASSWORD}
|
||||
form_data = {"username": settings._DEFAULT_EMAIL, "password": settings._DEFAULT_PASSWORD}
|
||||
response = api_client.post(api_routes.auth_token, data=form_data)
|
||||
|
||||
assert response.status_code == 200
|
||||
|
|
|
@ -9,7 +9,6 @@ from mealie.core.settings.settings import AppSettings
|
|||
|
||||
def test_non_default_settings(monkeypatch):
|
||||
monkeypatch.setenv("DEFAULT_GROUP", "Test Group")
|
||||
monkeypatch.setenv("DEFAULT_PASSWORD", "Test Password")
|
||||
monkeypatch.setenv("API_PORT", "8000")
|
||||
monkeypatch.setenv("API_DOCS", "False")
|
||||
|
||||
|
@ -17,7 +16,6 @@ def test_non_default_settings(monkeypatch):
|
|||
app_settings = get_app_settings()
|
||||
|
||||
assert app_settings.DEFAULT_GROUP == "Test Group"
|
||||
assert app_settings.DEFAULT_PASSWORD == "Test Password"
|
||||
assert app_settings.API_PORT == 8000
|
||||
assert app_settings.API_DOCS is False
|
||||
|
||||
|
|
|
@ -45,6 +45,10 @@ admin_users_unlock = "/api/admin/users/unlock"
|
|||
"""`/api/admin/users/unlock`"""
|
||||
app_about = "/api/app/about"
|
||||
"""`/api/app/about`"""
|
||||
app_about_startup_info = "/api/app/about/startup-info"
|
||||
"""`/api/app/about/startup-info`"""
|
||||
app_about_theme = "/api/app/about/theme"
|
||||
"""`/api/app/about/theme`"""
|
||||
auth_refresh = "/api/auth/refresh"
|
||||
"""`/api/auth/refresh`"""
|
||||
auth_token = "/api/auth/token"
|
||||
|
@ -183,6 +187,8 @@ users_reset_password = "/api/users/reset-password"
|
|||
"""`/api/users/reset-password`"""
|
||||
users_self = "/api/users/self"
|
||||
"""`/api/users/self`"""
|
||||
users_self_group = "/api/users/self/group"
|
||||
"""`/api/users/self/group`"""
|
||||
utils_download = "/api/utils/download"
|
||||
"""`/api/utils/download`"""
|
||||
validators_group = "/api/validators/group"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue