dorfteich/apps/api/package.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

63 lines
1.8 KiB
JSON

{
"name": "@dorfteich/api",
"version": "0.0.0",
"private": true,
"description": "Dorfteich REST API server",
"license": "MIT",
"scripts": {
"build": "prisma generate && nest build",
"start": "node dist/main.js",
"start:dev": "prisma generate && nest start --watch",
"typecheck": "tsc --noEmit",
"test": "vitest run --passWithNoTests",
"db:migrate:dev": "prisma migrate dev",
"db:seed": "tsx prisma/seed.ts",
"fixtures:regenerate": "tsx prisma/fixtures/regenerate.ts",
"search:reindex": "tsx src/search/reindex.cli.ts"
},
"dependencies": {
"@dorfteich/shared": "workspace:*",
"@nestjs/common": "^11.0.0",
"@nestjs/core": "^11.0.0",
"@nestjs/platform-express": "^11.0.0",
"@prisma/client": "^6.3.0",
"argon2": "^0.44.0",
"cookie-parser": "^1.4.7",
"dompurify": "^3.4.11",
"fractional-indexing": "^4.0.0",
"i18next": "^26.3.4",
"jsdom": "^26.1.0",
"multer": "^2.1.1",
"nestjs-pino": "^4.3.0",
"nodemailer": "^9.0.3",
"pino": "^9.6.0",
"pino-http": "^10.4.0",
"prisma": "^6.3.0",
"prosemirror-model": "^1.25.9",
"prosemirror-state": "^1.4.4",
"prosemirror-view": "^1.42.0",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.0",
"y-prosemirror": "^1.3.7",
"y-protocols": "^1.0.7",
"yjs": "^13.6.31",
"zod": "^3.25.76"
},
"devDependencies": {
"@nestjs/cli": "^11.0.0",
"@nestjs/testing": "^11.0.0",
"@swc/core": "^1.10.0",
"@types/cookie-parser": "^1.4.10",
"@types/express": "^5.0.0",
"@types/jsdom": "^28.0.3",
"@types/multer": "^2.0.0",
"@types/nodemailer": "^8.0.1",
"@types/supertest": "^6.0.0",
"pino-pretty": "^13.0.0",
"supertest": "^7.0.0",
"tsx": "^4.19.0",
"unplugin-swc": "^1.5.0",
"vitest": "^3.0.0"
}
}