#303: operator-uploaded fonts — storage, API, PDF embedding, backup #311
No reviewers
Labels
No Label
area:auth
area:docs
area:export
area:ops
area:storage
area:supply-chain
auth
backend
blocked
collab
deployment
docs
effort:L
effort:M
effort:S
frontend
plugins
qa
vs-nfd
vs-nfd:blocker
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: stwaidele/dorfteich#311
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "issue-303-custom-fonts"
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?
Closes #303.
ADR 0016 first
The ADR said adding a font is a catalog PR plus an image rebuild, with no runtime font management surface. It also anticipated this feature under "Alternatives considered": arbitrary font upload … may become a Site-Admin-level feature later. The amendment (
§Decisions taken in #303) takes that option and answers the two objections the ADR itself raised:The part that would have failed silently
CUSTOM_FONTS_DIRdefaults to./data/fonts— a sibling of uploads and plugins, deliberately not insideFONTS_DIR, which is baked into the image: anything written there is lost on the next deploy and never reaches a backup.Registering it took more than the issue assumed. The backup image bakes its volume paths into its own
ENV("self-sufficient without compose env" — #71's lesson) and reads no*_DIRfrom compose at all. Without the Dockerfile entry the nightly archive would have skipped the directory without a word, becausecreateArchivetolerates absent directories by design. Both Dockerfiles now carry the path.The list of archived directories moved into
apps/backup/src/data-dirs.tsand feeds both the archive and the restore, so the two cannot drift apart. #306 and #307 add one line there instead of building a second mechanism.Smaller notes
fontStack/fontEntrygained anextraparameter — uploaded families are runtime data and cannot live in a static package. The catalog is searched first, and a colliding family name is rejected at upload, so a custom font can never shadow a catalog one.fontStackalready falls back to the system stack for an unknown family, so ponds using a deleted font degrade rather than break, and re-uploading restores them. The count of affected ponds is shown before deleting and recorded in the audit entry, so the change is not silent.font.uploaded,font.deleted);docs/architecture/security.mdgained the upload surface.prisma formatrealigned the file;git diff -wshows 46 added lines and nothing else.Verification
api full suite against a fresh database: 102 files, 571 tests green. The upload suite writes into a real temporary directory and reads the bytes back off disk, so the storage layer is exercised rather than mocked, and it covers: upload → on-disk bytes → unauthenticated serving, a PNG renamed to
.woff2rejected, a catalog family name refused, a WOFF-only weight refused, every management route closed to non-admins, and the usage count plus a deletion that leaves the pond working.Not verified here: an end-to-end PDF export embedding a custom font — that needs a Gotenberg sidecar. The code path is the same one the catalog fonts use, with the base directory swapped, and the fallback-on-missing-file behaviour is unchanged. Worth a manual check on a stage once #304 makes the font selectable in the UI.