feat(admin): Logo und Favicon per Bild-Upload austauschbar
Neue Gruppe "Logo und Favicon" unter Admin > Inhalte mit drei Bildfeldern: Logo (Kopfzeile), optionales Logo fuer dunkle Flaechen (Mobilmenue, Fusszeile, Admin-Leiste; faellt sonst auf das normale Logo zurueck) und Favicon. Leer bleibt alles wie bisher (gezeichnetes Waben-Zeichen bzw. Standard-Icon). - lib/brand.ts liest die Pfade und prueft sie erneut gegen das Upload-Muster, bevor sie in ein src-Attribut gelangen. - Statische app/icon.svg und app/favicon.ico ersetzt durch dynamische app/icon.tsx (512 px PNG) und app/apple-icon.tsx (180 px auf Creme); lib/brand-icon.ts rendert Upload oder Standard-Icon per sharp und liest Dateien nur ueber resolveUploadPath. Cache-Control no-cache, damit ein neues Favicon ohne Tage Verzoegerung erscheint. - /favicon.ico per Rewrite auf /icon, Manifest nutzt /icon. - Hochgeladenes Logo mit fester Hoehe und begrenzter Breite (Kopfzeile mobil 56 px, ab sm 112 px), damit breite Wortmarken nichts sprengen. Uploads bleiben auf Rasterbilder beschraenkt (kein SVG, XSS-Schutz). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
ae2726b0ee
commit
4f442ec06d
19 files changed
+217
-77
No files matched your search
@@ -0,0 +1,35 @@
|
||||
import { getContentValues } from "@/lib/content";
|
||||
import { isValidUploadPath } from "@/lib/uploads";
|
||||
|
||||
export interface BrandAssets {
|
||||
/** Logo fuer helle Flaechen (Kopfzeile); null = gezeichnetes Waben-Zeichen. */
|
||||
logo: string | null;
|
||||
/** Logo fuer dunkle Flaechen (Mobilmenue, Fusszeile, Admin-Leiste); faellt auf `logo` zurueck. */
|
||||
logoInverted: string | null;
|
||||
/** Quadratisches Favicon-Bild; null = Standard-Icon (siehe lib/brand-icon.ts). */
|
||||
favicon: string | null;
|
||||
}
|
||||
|
||||
export const BRAND_CONTENT_KEYS = ["brand.logo", "brand.logoInverted", "brand.favicon"] as const;
|
||||
|
||||
/**
|
||||
* Hochgeladene Marken-Bilder aus den Inhalten (Admin > Inhalte > Logo und
|
||||
* Favicon). Jeder Wert wird hier noch einmal gegen das Upload-Pfadmuster
|
||||
* geprueft, obwohl updateSiteContentAction das beim Speichern schon tut:
|
||||
* der Pfad landet ungefiltert in einem src-Attribut auf jeder Seite, ein
|
||||
* irgendwie in die Datenbank geratener fremder Wert (z.B. eine externe URL)
|
||||
* soll dort nie ankommen.
|
||||
*/
|
||||
export async function getBrandAssets(): Promise<BrandAssets> {
|
||||
// Nicht lokalisierte Felder — die Locale spielt fuer den Wert keine Rolle.
|
||||
const values = await getContentValues([...BRAND_CONTENT_KEYS], "de");
|
||||
const pick = (value: string | undefined): string | null =>
|
||||
value && isValidUploadPath(value) ? value : null;
|
||||
|
||||
const logo = pick(values["brand.logo"]);
|
||||
return {
|
||||
logo,
|
||||
logoInverted: pick(values["brand.logoInverted"]) ?? logo,
|
||||
favicon: pick(values["brand.favicon"]),
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user