feat(branding): per-scope favicon, global-only v1 (LC-142) #162
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/lc-142-branding-favicon"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Per-scope favicon (global-only v1), deferred from LC-96. Operators who set a custom logo can now set a matching favicon on
/admin/branding, served from a dynamic/branding/faviconroute mirroring/branding/logo.Changes
0036adds nullablefavicon_upload_idtobranding(FK-free, same rationale aslogo_upload_id).Brandingstruct,get/resolve, andupsertcarry the column;upsertgrew afavicon_upload_idparameter (both callers updated). Enclave form passes the existing value through (favicons are global-only).GET /branding/faviconreturns the custom favicon when set, else falls back to the bundledassets/favicon.svg, sobase.htmllinks it unconditionally with no per-page threading. The<link>drops itstypehint (served format varies). The unread-mention favicon swap restores to/branding/faviconon clear.faviconfield;persist_logo_fieldgeneralized topersist_brand_file(is_favicon)which widens accepted types to SVG + ICO. SVG (text) is sniffed vialooks_like_svgsinceinfercan't classify it. 1 MiB cap + content-addressed storage unchanged.has_favicon).Notes
Tests
Four new cases in
routes_branding.rs: favicon route falls back to the static SVG when unset,upsertround-tripsfavicon_upload_id,base.htmllinks the dynamic favicon, admin page shows the preview when set. Migration0036appended to the hand-rolled migration lists.just check,just test,just test-saasall green.🤖 Generated with Claude Code
Admins can upload an SVG favicon, which is served from the directly-navigable /branding/favicon route as image/svg+xml. SVGs can embed <script>, which runs when the resource is opened directly in a tab (not when used as a <link rel=icon>). Add Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox to the served-upload branch so an uploaded SVG is inert on direct access. The favicon still renders normally; the logo route is unaffected (it never accepted SVG). New test uploads a script-bearing SVG and asserts the sandbox CSP is present. Also align the unread-mention favicon swap with the static link: both now use /branding/favicon?v={asset_version}, so clearing mentions does not refetch an unversioned URL. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>