feat: Migrate PWA manifest to backend (#7331)

Co-authored-by: Michael Genson <genson.michael@gmail.com>
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
This commit is contained in:
Brian Choromanski
2026-04-16 11:59:21 -04:00
committed by GitHub
parent 781a08ef54
commit fb545962dd
3 changed files with 128 additions and 145 deletions

View File

@@ -234,151 +234,7 @@ export default defineNuxtConfig({
periodicSyncForUpdates: 120,
},
includeAssets: ["favicon.ico", "apple-touch-icon.png", "safari-pinned-tab.svg"],
manifest: {
name: "Mealie",
short_name: "Mealie",
id: "/",
start_url: "/",
scope: "/",
display: "standalone",
background_color: "#FFFFFF",
theme_color: process.env.THEME_LIGHT_PRIMARY || "#E58325",
description: "Mealie is a recipe management and meal planning app",
lang: "en",
display_override: [
"standalone",
"minimal-ui",
"browser",
"window-controls-overlay",
],
categories: ["food", "lifestyle"],
prefer_related_applications: false,
handle_links: "preferred",
launch_handler: {
client_mode: ["focus-existing", "auto"],
},
edge_side_panel: {
preferred_width: 400,
},
share_target: {
action: "/r/create/url",
method: "GET",
enctype: "application/x-www-form-urlencoded",
params: {
text: "recipe_import_url",
},
},
icons: [
{
src: "/icons/android-chrome-192x192.png",
sizes: "192x192",
type: "image/png",
purpose: "any",
},
{
src: "/icons/android-chrome-512x512.png",
sizes: "512x512",
type: "image/png",
purpose: "any",
},
{
src: "/icons/android-chrome-maskable-192x192.png",
sizes: "192x192",
type: "image/png",
purpose: "maskable",
},
{
src: "/icons/android-chrome-maskable-512x512.png",
sizes: "512x512",
type: "image/png",
purpose: "maskable",
},
],
screenshots: [
{
src: "/screenshots/home-narrow.png",
sizes: "1600x2420",
form_factor: "narrow",
label: "Home Page",
},
{
src: "/screenshots/recipe-narrow.png",
sizes: "1600x2420",
form_factor: "narrow",
label: "Recipe Page",
},
{
src: "/screenshots/editor-narrow.png",
sizes: "1600x2420",
form_factor: "narrow",
label: "Editor Page",
},
{
src: "/screenshots/parser-narrow.png",
sizes: "1600x2420",
form_factor: "narrow",
label: "Parser Page",
},
{
src: "/screenshots/home-wide.png",
sizes: "2560x1460",
form_factor: "wide",
label: "Home Page",
},
{
src: "/screenshots/recipe-wide.png",
sizes: "2560x1460",
form_factor: "wide",
label: "Recipe Page",
},
{
src: "/screenshots/editor-wide.png",
sizes: "2560x1460",
form_factor: "wide",
label: "Editor Page",
},
{
src: "/screenshots/parser-wide.png",
sizes: "2560x1460",
form_factor: "wide",
label: "Parser Page",
},
],
shortcuts: [
{
name: "Shopping Lists",
short_name: "Shopping Lists",
description: "Open the shopping lists",
url: "/shopping-lists",
icons: [
{
src: "/icons/mdiFormatListChecks-192x192.png",
sizes: "192x192",
},
{
src: "/icons/mdiFormatListChecks-96x96.png",
sizes: "96x96",
},
],
},
{
name: "Meal Planner",
short_name: "Meal Planner",
description: "Open the meal planner",
url: "/household/mealplan/planner/view",
icons: [
{
src: "/icons/mdiCalendarMultiselect-192x192.png",
sizes: "192x192",
},
{
src: "/icons/mdiCalendarMultiselect-96x96.png",
sizes: "96x96",
},
],
},
],
},
manifest: false, // This is served via the backend, see mealie/routes/spa/manifest.py
},
// Vuetify module configuration: https://go.nuxtjs.dev/config-vuetify

View File

@@ -16,6 +16,7 @@ from mealie.core.config import get_app_settings
from mealie.core.dependencies.dependencies import try_get_current_user
from mealie.db.db_setup import generate_session
from mealie.repos.repository_factory import AllRepositories
from mealie.routes.spa.manifest import serve_manifest
from mealie.schema.recipe.recipe import Recipe
from mealie.schema.user.user import PrivateUser
@@ -251,4 +252,5 @@ def mount_spa(app: FastAPI):
app.get("/g/{group_slug}/r/{recipe_slug}", include_in_schema=False)(serve_recipe_with_meta)
app.get("/g/{group_slug}/shared/r/{token_id}", include_in_schema=False)(serve_shared_recipe_with_meta)
app.get("/manifest.webmanifest", include_in_schema=False)(serve_manifest)
app.mount("/", SPAStaticFiles(directory=__app_settings.STATIC_FILES, html=True), name="spa")

View File

@@ -0,0 +1,125 @@
import json
from urllib.parse import urlparse
from fastapi import Response
from mealie.core.config import get_app_settings
def serve_manifest():
settings = get_app_settings()
sub_path = urlparse(settings.BASE_URL).path or "/"
manifest = {
"name": "Mealie",
"short_name": "Mealie",
"id": "/",
"start_url": sub_path,
"scope": sub_path,
"display": "standalone",
"background_color": "#1E1E1E",
"theme_color": settings.theme.light_primary,
"description": "Mealie is a recipe management and meal planning app",
"lang": "en",
"display_override": ["standalone", "minimal-ui", "browser", "window-controls-overlay"],
"categories": ["food", "lifestyle"],
"prefer_related_applications": False,
"handle_links": "preferred",
"launch_handler": {"client_mode": ["focus-existing", "auto"]},
"edge_side_panel": {"preferred_width": 400},
"share_target": {
"action": "/r/create/url",
"method": "GET",
"enctype": "application/x-www-form-urlencoded",
"params": {"text": "recipe_import_url"},
},
"icons": [
{"src": "/icons/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png", "purpose": "any"},
{"src": "/icons/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "any"},
{
"src": "/icons/android-chrome-maskable-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable",
},
{
"src": "/icons/android-chrome-maskable-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable",
},
],
"screenshots": [
{
"src": "/screenshots/home-narrow.png",
"sizes": "1600x2420",
"form_factor": "narrow",
"label": "Home Page",
},
{
"src": "/screenshots/recipe-narrow.png",
"sizes": "1600x2420",
"form_factor": "narrow",
"label": "Recipe Page",
},
{
"src": "/screenshots/editor-narrow.png",
"sizes": "1600x2420",
"form_factor": "narrow",
"label": "Editor Page",
},
{
"src": "/screenshots/parser-narrow.png",
"sizes": "1600x2420",
"form_factor": "narrow",
"label": "Parser Page",
},
{"src": "/screenshots/home-wide.png", "sizes": "2560x1460", "form_factor": "wide", "label": "Home Page"},
{
"src": "/screenshots/recipe-wide.png",
"sizes": "2560x1460",
"form_factor": "wide",
"label": "Recipe Page",
},
{
"src": "/screenshots/editor-wide.png",
"sizes": "2560x1460",
"form_factor": "wide",
"label": "Editor Page",
},
{
"src": "/screenshots/parser-wide.png",
"sizes": "2560x1460",
"form_factor": "wide",
"label": "Parser Page",
},
],
"shortcuts": [
{
"name": "Shopping Lists",
"short_name": "Shopping Lists",
"description": "Open the shopping lists",
"url": "/shopping-lists",
"icons": [
{"src": "/icons/mdiFormatListChecks-192x192.png", "sizes": "192x192"},
{"src": "/icons/mdiFormatListChecks-96x96.png", "sizes": "96x96"},
],
},
{
"name": "Meal Planner",
"short_name": "Meal Planner",
"description": "Open the meal planner",
"url": "/household/mealplan/planner/view",
"icons": [
{"src": "/icons/mdiCalendarMultiselect-192x192.png", "sizes": "192x192"},
{"src": "/icons/mdiCalendarMultiselect-96x96.png", "sizes": "96x96"},
],
},
],
}
return Response(
content=json.dumps(manifest),
media_type="application/manifest+json",
headers={"Cache-Control": "no-cache"},
)