diff --git a/docs/docs/changelog/v0.4.1.md b/docs/docs/changelog/v0.4.1.md
index 747fa6a8473a..2b2a6d7bcedb 100644
--- a/docs/docs/changelog/v0.4.1.md
+++ b/docs/docs/changelog/v0.4.1.md
@@ -12,4 +12,5 @@
- Fix Backup download blocked by authentication
- Random meal-planner will no longer duplicate recipes unless no other options
- New Quick Week button to generate next 5 day week of recipe slots.
+- Minor UI tweaks
diff --git a/frontend/src/components/Admin/MealPlanner/TimePickerDialog.vue b/frontend/src/components/Admin/MealPlanner/TimePickerDialog.vue
index eda11de4aa59..35423f9e818d 100644
--- a/frontend/src/components/Admin/MealPlanner/TimePickerDialog.vue
+++ b/frontend/src/components/Admin/MealPlanner/TimePickerDialog.vue
@@ -42,4 +42,7 @@ export default {
\ No newline at end of file
diff --git a/frontend/src/components/Recipe/RecipeViewer/Ingredients.vue b/frontend/src/components/Recipe/RecipeViewer/Ingredients.vue
index e3f79077a532..036a3516a60b 100644
--- a/frontend/src/components/Recipe/RecipeViewer/Ingredients.vue
+++ b/frontend/src/components/Recipe/RecipeViewer/Ingredients.vue
@@ -10,14 +10,14 @@
@@ -55,12 +55,8 @@ export default {
};
-
\ No newline at end of file
diff --git a/frontend/src/pages/Admin/MealPlanner/index.vue b/frontend/src/pages/Admin/MealPlanner/index.vue
index a1b953430890..e9266b53a449 100644
--- a/frontend/src/pages/Admin/MealPlanner/index.vue
+++ b/frontend/src/pages/Admin/MealPlanner/index.vue
@@ -57,28 +57,23 @@
{{ groupSettings.webhookTime }}
-
-
-
-
-
-
-
-
-
- mdi-webhook
- {{ $t("settings.webhooks.test-webhooks") }}
-
-
+
+
+
+
+ mdi-webhook
+ {{ $t("settings.webhooks.test-webhooks") }}
+
diff --git a/mealie/core/config.py b/mealie/core/config.py
index aca765dfff10..0f5e21c2f30c 100644
--- a/mealie/core/config.py
+++ b/mealie/core/config.py
@@ -1,8 +1,9 @@
import os
import secrets
from pathlib import Path
+from typing import Optional, Union
-import dotenv
+from pydantic import BaseSettings, Field, validator
APP_VERSION = "v0.4.0"
DB_VERSION = "v0.4.0"
@@ -11,7 +12,6 @@ CWD = Path(__file__).parent
BASE_DIR = CWD.parent.parent
ENV = BASE_DIR.joinpath(".env")
-dotenv.load_dotenv(ENV)
PRODUCTION = os.environ.get("ENV")
@@ -38,6 +38,11 @@ def determine_secrets(data_dir: Path, production: bool) -> str:
return new_secret
+# General
+DATA_DIR = determine_data_dir(PRODUCTION)
+LOGGER_FILE = DATA_DIR.joinpath("mealie.log")
+
+
class AppDirectories:
def __init__(self, cwd, data_dir) -> None:
self.DATA_DIR = data_dir
@@ -74,36 +79,51 @@ class AppDirectories:
dir.mkdir(parents=True, exist_ok=True)
-class AppSettings:
- def __init__(self, app_dirs: AppDirectories) -> None:
- global DB_VERSION
- self.PRODUCTION = bool(os.environ.get("ENV"))
- self.IS_DEMO = os.getenv("DEMO", "False") == "True"
- self.API_PORT = int(os.getenv("API_PORT", 9000))
- self.API = os.getenv("API_DOCS", "True") == "True"
- self.DOCS_URL = "/docs" if self.API else None
- self.REDOC_URL = "/redoc" if self.API else None
- self.SECRET = determine_secrets(app_dirs.DATA_DIR, self.PRODUCTION)
- self.DATABASE_TYPE = os.getenv("DB_TYPE", "sqlite")
-
- # Used to Set SQLite File Version
- self.SQLITE_FILE = None
- if self.DATABASE_TYPE == "sqlite":
- self.SQLITE_FILE = app_dirs.SQLITE_DIR.joinpath(f"mealie_{DB_VERSION}.sqlite")
- else:
- raise Exception("Unable to determine database type. Acceptible options are 'sqlite'")
-
- self.DEFAULT_GROUP = os.getenv("DEFAULT_GROUP", "Home")
- self.DEFAULT_PASSWORD = os.getenv("DEFAULT_PASSWORD", "MyPassword")
-
- # Not Used!
- self.SFTP_USERNAME = os.getenv("SFTP_USERNAME", None)
- self.SFTP_PASSWORD = os.getenv("SFTP_PASSWORD", None)
-
-
-# General
-DATA_DIR = determine_data_dir(PRODUCTION)
-LOGGER_FILE = DATA_DIR.joinpath("mealie.log")
-
app_dirs = AppDirectories(CWD, DATA_DIR)
-settings = AppSettings(app_dirs)
+
+
+class AppSettings(BaseSettings):
+ global DATA_DIR
+ PRODUCTION: bool = Field(False, env="ENV")
+ IS_DEMO: bool = False
+ API_PORT: int = 9000
+ API_DOCS: bool = True
+
+ @property
+ def DOCS_URL(self) -> str:
+ return "/docs" if self.API_DOCS else None
+
+ @property
+ def REDOC_URL(self) -> str:
+ return "/redoc" if self.API_DOCS else None
+
+ SECRET: str = determine_secrets(DATA_DIR, PRODUCTION)
+ DATABASE_TYPE: str = Field("sqlite", env="DB_TYPE")
+
+ @validator("DATABASE_TYPE", pre=True)
+ def validate_db_type(cls, v: str) -> Optional[str]:
+ if v != "sqlite":
+ raise ValueError("Unable to determine database type. Acceptible options are 'sqlite'")
+ else:
+ return v
+
+ # Used to Set SQLite File Version
+ SQLITE_FILE: Optional[Union[str, Path]]
+
+ @validator("SQLITE_FILE", pre=True)
+ def identify_sqlite_file(cls, v: str) -> Optional[str]:
+ return app_dirs.SQLITE_DIR.joinpath(f"mealie_{DB_VERSION}.sqlite")
+
+ DEFAULT_GROUP: str = "Home"
+ DEFAULT_PASSWORD: str = "MyPassword"
+
+ # Not Used!
+ SFTP_USERNAME: Optional[str]
+ SFTP_PASSWORD: Optional[str]
+
+ class Config:
+ env_file = BASE_DIR.joinpath(".env")
+ env_file_encoding = "utf-8"
+
+
+settings = AppSettings()
diff --git a/tests/unit_tests/test_config.py b/tests/unit_tests/test_config.py
index fc88f11b8d29..d434e12adfff 100644
--- a/tests/unit_tests/test_config.py
+++ b/tests/unit_tests/test_config.py
@@ -4,19 +4,39 @@ import pytest
from mealie.core.config import CWD, DATA_DIR, AppDirectories, AppSettings, determine_data_dir, determine_secrets
+def test_default_settings(monkeypatch):
+ monkeypatch.delenv("DEFAULT_GROUP", raising=False)
+ monkeypatch.delenv("DEFAULT_PASSWORD", raising=False)
+ monkeypatch.delenv("API_PORT", raising=False)
+ monkeypatch.delenv("API_DOCS", raising=False)
+ monkeypatch.delenv("DB_TYPE", raising=False)
+ monkeypatch.delenv("IS_DEMO", raising=False)
+
+ app_settings = AppSettings()
+
+ assert app_settings.DEFAULT_GROUP == "Home"
+ assert app_settings.DEFAULT_PASSWORD == "MyPassword"
+ assert app_settings.DATABASE_TYPE == "sqlite"
+ assert app_settings.API_PORT == 9000
+ assert app_settings.API_DOCS is True
+ assert app_settings.IS_DEMO is False
+
+ assert app_settings.REDOC_URL == "/redoc"
+ assert app_settings.DOCS_URL == "/docs"
+
+
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)
- app_dirs = AppDirectories(CWD, DATA_DIR)
- app_settings = AppSettings(app_dirs)
+ app_settings = AppSettings()
assert app_settings.DEFAULT_GROUP == "Test Group"
assert app_settings.DEFAULT_PASSWORD == "Test Password"
assert app_settings.API_PORT == 8000
- assert app_settings.API is False
+ assert app_settings.API_DOCS is False
assert app_settings.REDOC_URL is None
assert app_settings.DOCS_URL is None
@@ -25,9 +45,8 @@ def test_non_default_settings(monkeypatch):
def test_unknown_database(monkeypatch):
monkeypatch.setenv("DB_TYPE", "nonsense")
- with pytest.raises(Exception, match="Unable to determine database type. Acceptible options are 'sqlite'"):
- app_dirs = AppDirectories(CWD, DATA_DIR)
- AppSettings(app_dirs)
+ with pytest.raises(ValueError, match="Unable to determine database type. Acceptible options are 'sqlite'"):
+ AppSettings()
def test_secret_generation(tmp_path):