diff --git a/docs/api/uploads.md b/docs/api/uploads.md index 79c96a5..f8a604b 100644 --- a/docs/api/uploads.md +++ b/docs/api/uploads.md @@ -3,7 +3,7 @@ type: api title: Uploads Endpoints description: Admin-only multipart upload endpoints for site logos, category icons, and challenge files. tags: [api, uploads, multipart, admin] -timestamp: 2026-07-22T13:05:30Z +timestamp: 2026-07-22T19:18:00Z --- # Endpoints @@ -37,6 +37,19 @@ valid admin session and the standard CSRF cookie/header pair. * An accepted logo keeps its sanitized filename, is written directly under `UPLOAD_DIR`, and returns `{ publicUrl, originalFilename }`, with `publicUrl` in the form `/uploads/{safeFilename}`. +* Category icons are decoded with Sharp and normalized to a 128-by-128 PNG. + When `categoryId` is supplied, the file is written to + `UPLOAD_DIR/icons/{categoryId}.png` (overwriting any previous icon for that + category) and the response carries `{ id: "{categoryId}.png", publicUrl, + width: 128, height: 128, mimeType: "image/png", storedPath, size, + originalFilename }`. +* Category-icon validation is strict: a missing `file`, an oversized upload, + a corrupt/non-image payload, or a buffer that Sharp cannot decode returns + `400 Bad Request`. Invalid category-icon payloads use the message + `Category icon must be a valid PNG, JPEG, GIF, or WebP image.` and **are + not persisted** — the existing icon file (if any) on disk is left untouched. + The multipart MIME type, the filename extension, and any browser `accept` + metadata are not trusted as proof of validity. * A missing `file` part, an oversized upload, a corrupt image, or a decoded logo in another format returns `400 Bad Request`. Invalid logo payloads use the message `Logo must be a valid PNG, JPEG, GIF, or WebP image.` and are not diff --git a/docs/architecture/key-files.md b/docs/architecture/key-files.md index 0a2c4d3..d7e3a95 100644 --- a/docs/architecture/key-files.md +++ b/docs/architecture/key-files.md @@ -3,7 +3,7 @@ type: architecture title: Key Files Index description: One-line responsibility for important source and contract-test files, including strict event-window validation and the public bootstrap SSE listener. tags: [architecture, key-files, event-window, validation, default-challenge-ip, sse, bootstrap, migrations] -timestamp: 2026-07-22T18:37:00Z +timestamp: 2026-07-22T19:18:00Z --- # Backend @@ -25,7 +25,7 @@ timestamp: 2026-07-22T18:37:00Z | `backend/src/modules/admin/general.service.ts` | Reads/writes global settings, filters available theme files, and publishes the `general` SSE notification after updates. | | `backend/src/modules/auth/auth.controller.ts` | Registers authentication and account endpoints. | | `backend/src/modules/auth/auth.service.ts` | Handles sessions, authentication, registration, and password changes. | -| `backend/src/modules/uploads/uploads.controller.ts` | Registers admin-only multipart uploads, including Sharp-backed site-logo format validation. | +| `backend/src/modules/uploads/uploads.controller.ts` | Registers admin-only multipart uploads, including Sharp-backed site-logo format validation and strict category-icon decode (rejects undecodable / non-image buffers with HTTP 400 before any filesystem write so existing icons are preserved). | | `backend/src/modules/admin/dto/general.dto.ts` | Zod contract for `PUT /api/v1/admin/general/settings`; both event timestamps are required ISO-8601 values and end must be strictly after start. | | `tests/backend/admin-general-event-window.spec.ts` | Focused contract tests for valid, empty, malformed, equal, and reversed event-window timestamps. | | `tests/backend/admin-general-service.spec.ts` | General-settings schema and service tests, including per-field empty datetime failures and settings-event emission. | @@ -52,7 +52,7 @@ timestamp: 2026-07-22T18:37:00Z | `frontend/src/app/features/shell/tabs/quick-tabs.component.ts` | Main shell navigation tabs. | | `frontend/src/app/features/shell/change-password/change-password-modal.component.ts` | Change-password form modal. | | `frontend/src/app/features/admin/general.component.ts` | `AdminGeneralComponent` reactive form for `/admin/general` — per-field inline error rendering (page-title + event-start + event-end), logo upload wiring, welcome Markdown preview, event-state derivation, and SSE `general` event handling. | -| `frontend/src/app/features/admin/categories/category-form-modal.component.ts` | Standalone OnPush modal for create + edit; owns the `CategoryFormGroup`, exposes the pure `syncCategoryForm` helper, and reacts to `open` / `mode` / `category` signal inputs via a `markForCheck` effect so edit prefill reaches the DOM. | +| `frontend/src/app/features/admin/categories/category-form-modal.component.ts` | Standalone OnPush modal for create + edit; owns the `CategoryFormGroup`, exposes the pure `syncCategoryForm` helper, binds `[formGroup]` on its template `