mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-07-25 08:09:41 +02:00
fix: dynamically load theme from API endpoint (#2688)
* dynamically load theme from API endpoint * add documentation
This commit is contained in:
parent
18b7e3ac9a
commit
75e95817a3
6 changed files with 132 additions and 5 deletions
|
@ -75,3 +75,24 @@ Changing the webworker settings may cause unforeseen memory leak issues with Mea
|
||||||
| LDAP_ID_ATTRIBUTE | uid | The LDAP attribute that maps to the user's id |
|
| LDAP_ID_ATTRIBUTE | uid | The LDAP attribute that maps to the user's id |
|
||||||
| LDAP_NAME_ATTRIBUTE | name | The LDAP attribute that maps to the user's name |
|
| LDAP_NAME_ATTRIBUTE | name | The LDAP attribute that maps to the user's name |
|
||||||
| LDAP_MAIL_ATTRIBUTE | mail | The LDAP attribute that maps to the user's email |
|
| LDAP_MAIL_ATTRIBUTE | mail | The LDAP attribute that maps to the user's email |
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
| Variables | Default | Description |
|
||||||
|
| --------------------- | :-----: | --------------------------- |
|
||||||
|
| THEME_LIGHT_PRIMARY | #E58325 | Light Theme Config Variable |
|
||||||
|
| THEME_LIGHT_ACCENT | #007A99 | Light Theme Config Variable |
|
||||||
|
| THEME_LIGHT_SECONDARY | #973542 | Light Theme Config Variable |
|
||||||
|
| THEME_LIGHT_SUCCESS | #43A047 | Light Theme Config Variable |
|
||||||
|
| THEME_LIGHT_INFO | #1976D2 | Light Theme Config Variable |
|
||||||
|
| THEME_LIGHT_WARNING | #FF6D00 | Light Theme Config Variable |
|
||||||
|
| THEME_LIGHT_ERROR | #EF5350 | Light Theme Config Variable |
|
||||||
|
| THEME_DARK_PRIMARY | #E58325 | Dark Theme Config Variable |
|
||||||
|
| THEME_DARK_ACCENT | #007A99 | Dark Theme Config Variable |
|
||||||
|
| THEME_DARK_SECONDARY | #973542 | Dark Theme Config Variable |
|
||||||
|
| THEME_DARK_SUCCESS | #43A047 | Dark Theme Config Variable |
|
||||||
|
| THEME_DARK_INFO | #1976D2 | Dark Theme Config Variable |
|
||||||
|
| THEME_DARK_WARNING | #FF6D00 | Dark Theme Config Variable |
|
||||||
|
| THEME_DARK_ERROR | #EF5350 | Dark Theme Config Variable |
|
||||||
|
|
|
@ -1,7 +1,60 @@
|
||||||
import { Plugin } from "@nuxt/types"
|
import { Plugin } from "@nuxt/types";
|
||||||
|
|
||||||
const themePlugin: Plugin = ({ $vuetify, $config }) => {
|
export interface ThemeConfig {
|
||||||
$vuetify.theme.themes = $config.themes as typeof $vuetify.theme.themes
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
let __cachedTheme: ThemeConfig | undefined;
|
||||||
|
|
||||||
|
async function fetchTheme(): Promise<ThemeConfig | undefined> {
|
||||||
|
const route = "/api/app/about/theme";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(route);
|
||||||
|
const data = await response.json();
|
||||||
|
return data as ThemeConfig;
|
||||||
|
} catch (error) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const themePlugin: Plugin = async ({ $vuetify, $config }) => {
|
||||||
|
let theme = __cachedTheme;
|
||||||
|
if (!theme) {
|
||||||
|
theme = await fetchTheme();
|
||||||
|
__cachedTheme = theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theme) {
|
||||||
|
$vuetify.theme.themes.light.primary = theme.lightPrimary;
|
||||||
|
$vuetify.theme.themes.light.accent = theme.lightAccent;
|
||||||
|
$vuetify.theme.themes.light.secondary = theme.lightSecondary;
|
||||||
|
$vuetify.theme.themes.light.success = theme.lightSuccess;
|
||||||
|
$vuetify.theme.themes.light.info = theme.lightInfo;
|
||||||
|
$vuetify.theme.themes.light.warning = theme.lightWarning;
|
||||||
|
$vuetify.theme.themes.light.error = theme.lightError;
|
||||||
|
|
||||||
|
$vuetify.theme.themes.dark.primary = theme.darkPrimary;
|
||||||
|
$vuetify.theme.themes.dark.accent = theme.darkAccent;
|
||||||
|
$vuetify.theme.themes.dark.secondary = theme.darkSecondary;
|
||||||
|
$vuetify.theme.themes.dark.success = theme.darkSuccess;
|
||||||
|
$vuetify.theme.themes.dark.info = theme.darkInfo;
|
||||||
|
$vuetify.theme.themes.dark.warning = theme.darkWarning;
|
||||||
|
$vuetify.theme.themes.dark.error = theme.darkError;
|
||||||
|
}
|
||||||
|
|
||||||
if ($config.useDark) {
|
if ($config.useDark) {
|
||||||
$vuetify.theme.dark = true;
|
$vuetify.theme.dark = true;
|
||||||
|
|
|
@ -3,6 +3,8 @@ from pathlib import Path
|
||||||
|
|
||||||
from pydantic import BaseSettings, NoneStr, validator
|
from pydantic import BaseSettings, NoneStr, validator
|
||||||
|
|
||||||
|
from mealie.core.settings.themes import Theme
|
||||||
|
|
||||||
from .db_providers import AbstractDBProvider, db_provider_factory
|
from .db_providers import AbstractDBProvider, db_provider_factory
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,6 +25,8 @@ def determine_secrets(data_dir: Path, production: bool) -> str:
|
||||||
|
|
||||||
|
|
||||||
class AppSettings(BaseSettings):
|
class AppSettings(BaseSettings):
|
||||||
|
theme: Theme = Theme()
|
||||||
|
|
||||||
PRODUCTION: bool
|
PRODUCTION: bool
|
||||||
BASE_URL: str = "http://localhost:8080"
|
BASE_URL: str = "http://localhost:8080"
|
||||||
"""trailing slashes are trimmed (ex. `http://localhost:8080/` becomes ``http://localhost:8080`)"""
|
"""trailing slashes are trimmed (ex. `http://localhost:8080/` becomes ``http://localhost:8080`)"""
|
||||||
|
|
22
mealie/core/settings/themes.py
Normal file
22
mealie/core/settings/themes.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
from pydantic import BaseSettings
|
||||||
|
|
||||||
|
|
||||||
|
class Theme(BaseSettings):
|
||||||
|
light_primary: str = "#E58325"
|
||||||
|
light_accent: str = "#007A99"
|
||||||
|
light_secondary: str = "#973542"
|
||||||
|
light_success: str = "#43A047"
|
||||||
|
light_info: str = "#1976D2"
|
||||||
|
light_warning: str = "#FF6D00"
|
||||||
|
light_error: str = "#EF5350"
|
||||||
|
|
||||||
|
dark_primary: str = "#E58325"
|
||||||
|
dark_accent: str = "#007A99"
|
||||||
|
dark_secondary: str = "#973542"
|
||||||
|
dark_success: str = "#43A047"
|
||||||
|
dark_info: str = "#1976D2"
|
||||||
|
dark_warning: str = "#FF6D00"
|
||||||
|
dark_error: str = "#EF5350"
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
env_prefix = "theme_"
|
|
@ -1,8 +1,8 @@
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter, Response
|
||||||
|
|
||||||
from mealie.core.config import get_app_settings
|
from mealie.core.config import get_app_settings
|
||||||
from mealie.core.settings.static import APP_VERSION
|
from mealie.core.settings.static import APP_VERSION
|
||||||
from mealie.schema.admin.about import AppInfo
|
from mealie.schema.admin.about import AppInfo, AppTheme
|
||||||
|
|
||||||
router = APIRouter(prefix="/about")
|
router = APIRouter(prefix="/about")
|
||||||
|
|
||||||
|
@ -18,3 +18,12 @@ def get_app_info():
|
||||||
production=settings.PRODUCTION,
|
production=settings.PRODUCTION,
|
||||||
allow_signup=settings.ALLOW_SIGNUP,
|
allow_signup=settings.ALLOW_SIGNUP,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/theme", response_model=AppTheme)
|
||||||
|
def get_app_theme(resp: Response):
|
||||||
|
"""Get's the current theme settings"""
|
||||||
|
settings = get_app_settings()
|
||||||
|
|
||||||
|
resp.headers["Cache-Control"] = "public, max-age=604800"
|
||||||
|
return AppTheme(**settings.theme.dict())
|
||||||
|
|
|
@ -16,6 +16,24 @@ class AppInfo(MealieModel):
|
||||||
allow_signup: bool
|
allow_signup: bool
|
||||||
|
|
||||||
|
|
||||||
|
class AppTheme(MealieModel):
|
||||||
|
light_primary: str = "#E58325"
|
||||||
|
light_accent: str = "#007A99"
|
||||||
|
light_secondary: str = "#973542"
|
||||||
|
light_success: str = "#43A047"
|
||||||
|
light_info: str = "#1976D2"
|
||||||
|
light_warning: str = "#FF6D00"
|
||||||
|
light_error: str = "#EF5350"
|
||||||
|
|
||||||
|
dark_primary: str = "#E58325"
|
||||||
|
dark_accent: str = "#007A99"
|
||||||
|
dark_secondary: str = "#973542"
|
||||||
|
dark_success: str = "#43A047"
|
||||||
|
dark_info: str = "#1976D2"
|
||||||
|
dark_warning: str = "#FF6D00"
|
||||||
|
dark_error: str = "#EF5350"
|
||||||
|
|
||||||
|
|
||||||
class AdminAboutInfo(AppInfo):
|
class AdminAboutInfo(AppInfo):
|
||||||
versionLatest: str
|
versionLatest: str
|
||||||
api_port: int
|
api_port: int
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue