dorfteich/packages/shared/i18n/en/errors.json
Claude Opus 4.8 30891f99cf
All checks were successful
CD / Build and push images (push) Successful in 4m2s
CI / Lint, typecheck, test (push) Successful in 2m46s
CI / Auth e2e pack (push) Successful in 3m45s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 8s
CD / Smoke tests against Test (push) Successful in 1m18s
CD / Promote to Int (push) Successful in 12s
Add non-image attachments with allowlist, SVG policy, and file managers (#61)
Extend uploads (#27, ADR 0011) beyond images to a configurable general
attachment allowlist, plus the page attachments section and the Pond Admin
file manager.

Backend:
- Two instance settings: `upload.allowedExtensions` (lowercase, dot-stripped,
  images always allowed regardless) and `upload.svgPolicy` (reject | sanitize).
- FilesService.resolveUpload: raster images still decided by magic bytes; SVG
  is sanitized with DOMPurify (scripts, event handlers, foreignObject stripped)
  or rejected per policy; everything else is admitted only if its extension is
  on the allowlist. A sanitized SVG's stored bytes are re-accounted so
  pond_usage matches disk.
- Downloads set `Content-Disposition: attachment` for every non-raster type
  (office files, PDFs, SVG) with `nosniff`, so they can never execute inline;
  raster images stay inline for page embeds.
- New endpoints: `GET /ponds/:id/files` (pond_admin: all files + usage + orphan
  flag), `POST /pages/:id/files` and `GET /pages/:id/files` (page-write/read:
  the attachments section). New error code `upload_type_not_allowed` (de+en).

Frontend:
- Page attachments section (AttachmentsPanel): upload, list with type glyph,
  size, and uploader, insert-as-link into the document (an internal media link
  that downloads, never renders inline), and delete. Toggled in the editor.
- Pond file manager (PondFileManager) in pond settings for Pond Admins: every
  file with its referencing page (or an orphan flag) and storage usage.
- Admin uploads settings form (allowlist + SVG policy). New `files` i18n
  namespace (de+en).

Tests:
- files.e2e.db.test.ts: allowlisted non-image accepted and served as a
  download; disallowed extension rejected; renamed-.html-as-.png still fails;
  SVG sanitized (scripts/handlers stripped) and reject-mode rejects; page
  attachment listing; pond file manager usage/orphan; non-admin denied.
- New e2e pack apps/web/e2e/attachments.spec.ts (+ CI step): upload → list →
  insert link (verified attachment disposition + nosniff), disallowed-type
  error, pond file manager usage/orphan.

Local: typecheck, lint, i18n:check, build all green; api-db 184, shared 121,
web 50; attachments pack 3/3, members 3/3, content 5/5. Adds dompurify + jsdom
to the api for server-side SVG sanitization.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
2026-07-10 02:52:40 +02:00

72 lines
3.9 KiB
JSON

{
"bad_request": "The request is invalid.",
"unauthorized": "Please sign in to continue.",
"forbidden": "You do not have permission for this action.",
"not_found": "The requested resource does not exist.",
"conflict": "The request conflicts with the current state.",
"gone": "This resource is no longer available.",
"payload_too_large": "The submitted data is too large.",
"rate_limited": "Too many requests — please try again later.",
"internal_error": "Internal server error.",
"registration_closed": "Registration is currently closed on this instance.",
"token_invalid": "This link is invalid or has expired.",
"login_failed": "Username/e-mail or password is incorrect.",
"login_backoff": "Too many failed attempts — please wait a few minutes.",
"email_unverified": "Please confirm your e-mail address first.",
"account_disabled": "This account has been disabled.",
"password_incorrect": "The current password is incorrect.",
"csrf_origin_mismatch": "The request came from an unexpected origin.",
"cannot_revoke_current_session": "Use sign-out to end your current session.",
"personal_pond_undeletable": "The personal pond cannot be deleted.",
"quota_exceeded": "The quota has been reached (limit: {{limit}}).",
"slug_taken": "This slug is already taken in this pond.",
"page_document_too_large": "The page is too large (limit: {{limitBytes}} bytes).",
"invalid_page_state": "The submitted page content is invalid.",
"page_trashed": "This page has been moved to the trash.",
"label_name_taken": "A label with this name already exists at this level.",
"label_cycle": "A label cannot be moved into its own subtree.",
"label_depth_exceeded": "Labels can be nested at most {{max}} levels deep.",
"label_has_pages": "This label still has pages assigned; confirm to detach them.",
"label_wrong_pond": "This label belongs to a different pond.",
"unsupported_file_type": "This file type is not supported.",
"upload_type_not_allowed": "This file type is not allowed on this instance.",
"file_too_large": "The file is too large (limit: {{limitBytes}} bytes).",
"network": "The server could not be reached.",
"grant_exists": "This grant already exists.",
"grant_pond_admin_scope": "A Pond Admin grant must apply to the whole pond and a specific user.",
"grant_pond_admin_personal_pond": "A personal pond's only administrator is its owner.",
"grant_subject_id_mismatch": "The grant's subject is inconsistent.",
"grant_scope_id_mismatch": "The grant's scope is inconsistent.",
"grant_scope_not_found": "The label or page this grant points to does not exist in this pond.",
"grant_subject_not_found": "This user does not exist.",
"grant_last_admin": "The last Pond Admin cannot be removed.",
"member_not_found": "No user with that username or e-mail exists.",
"member_exists": "This user is already a member of this pond.",
"member_not_a_member": "This user is not a member of this pond.",
"member_is_owner": "The pond owner's membership cannot be changed here.",
"cannot_modify_self": "You cannot perform this action on your own account.",
"last_site_admin": "The last Site Admin cannot be removed.",
"validation": {
"required": "This field is required.",
"taken": "This value is already taken.",
"username": {
"tooShort": "The username needs at least 3 characters.",
"tooLong": "The username can have at most 32 characters.",
"charset": "Only letters, digits, and hyphens are allowed."
},
"password": {
"tooShort": "The password needs at least 10 characters.",
"tooLong": "The password can have at most 128 characters.",
"tooCommon": "This password is too common."
},
"email": {
"invalid": "Please enter a valid e-mail address."
},
"displayName": {
"required": "Please enter a display name."
},
"labelColor": "Please enter a colour like #a1b2c3.",
"tooLong": "The input is too long."
}
}