From 3f2b3a7af43eae49547a3d8103cad85dd19eddc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20P=C3=B6ttker?= Date: Sat, 9 May 2026 11:06:11 +0200 Subject: [PATCH] chore: add project documentation, restrict email import permissions, and set date format in InboxPage --- CLAUDE.md | 134 ++++++++++++++++++ .../src/email/email-import.controller.ts | 2 +- paperless-frontend/src/pages/InboxPage.tsx | 1 + 3 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..2f95805 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,134 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Paperless Manager is a document automation platform that extends [Paperless-NGX](https://github.com/paperless-ngx/paperless-ngx). It provides: +- Scanner inbox management with PDF processing (splitting, rotation, barcode detection) +- Email import with attachment extraction +- Rule-based postprocessing (tag, export, send mail) +- OCR via Ollama (vision model) +- Label printing agent with SSE-based job queue + +UI labels and comments are in **German**. + +## Structure + +``` +paperless-backend/ # NestJS API (port 3100) +paperless-frontend/ # React 19 + Ant Design + Vite +docker-compose.yml # Production (backend + frontend/nginx + MySQL) +.env.example # All required environment variables +``` + +## Commands + +**Backend** (in `paperless-backend/`): +```bash +npm run start:dev # Dev server with watch mode +npm run build # Compile TypeScript +npm run test # Jest unit tests +npm run test:e2e # End-to-end tests +npm run lint # ESLint with auto-fix +``` + +**Frontend** (in `paperless-frontend/`): +```bash +npm run dev # Vite dev server (proxies /api to backend) +npm run build # TypeScript check + Vite build +npm run lint # ESLint +``` + +## Backend Architecture + +### Module Overview + +| Module | Purpose | +|--------|---------| +| `auth` | JWT (OIDC/Authentik) + API Key guards, permission system | +| `database` | TypeORM entities + MySQL config (synchronize: true) | +| `inbox` | Scanner document management (list, preview, rotate, split) | +| `paperless` | Paperless-NGX REST API client | +| `preprocessing` | PDF→images, OCR, QR extraction, task creation | +| `postprocessing` | Rule engine: filter conditions → actions | +| `email` | IMAP intake, PDF conversion, correspondent mapping | +| `email-download` | External email retrieval, ZUGFeRD invoice parsing | +| `barcode` | QR/barcode detection, template-based split actions | +| `label-print-agent` | SVG label rendering, RxJS-based print job queue + SSE | +| `inbox-postprocessor` | Applies edits/splits to PDFs, variable substitution | +| `scanner` | chokidar file system watcher for scan directory | +| `settings` | Global configuration | +| `user-settings` | Per-user SMTP and preferences | + +### Authentication & Permissions + +Global guards apply to all routes: +1. `JwtOrApiKeyGuard` — validates Bearer JWT (OIDC) or `X-API-Key` header +2. `PermissionsGuard` — enforces `@RequirePermissions()` decorator + +Decorate controllers/handlers with: +```typescript +@RequirePermissions(Permission.VIEW_SCANNER) +``` + +Permissions map from OIDC groups (`PM_Admin`, `PM_Belege`, etc.) to the `Permission` enum in `src/auth/permissions.enum.ts`. + +Use `@Public()` to bypass auth guards entirely. + +### Database + +- TypeORM with MySQL 8+, UTF8MB4, `synchronize: true` (schema auto-migrates) +- 23 entities in `src/database/entities/` +- JSON columns use transformers to normalize empty arrays/objects to `null` +- Key entities: `InboxDocument`, `Task`, `Email`, `Attachment`, `Postprocessing`, `BarcodeTemplate`, `LabelPrintJob`, `ApiKey`, `Setting` + +### Document Processing Pipeline + +**Preprocessing** (`preprocessing/document-pipeline.service.ts`): +1. PDF → PNG page images (200 DPI via pdf-lib + sharp) +2. QR code extraction from page 1 (jsqr) +3. OCR via Ollama (llava vision model) +4. Auto-generated internal document number (YYYY-000001) +5. DB task entry creation, archive original (GoBD compliance) + +**Postprocessing** (`postprocessing/postprocessing.service.ts`): +1. Load rules ordered by priority with filter conditions (JSON) +2. Evaluate filters (field operators: eq, contains, in, etc.) +3. Execute actions: tag, update field, send mail, export WebDAV +4. Log results; apply error tag on failure; stop if `NoFurther` flag set + +**Inbox Postprocessor** (`inbox-postprocessor/`): +- Applies virtual edits (page deletions, rotations, splits) stored in DB to original PDF +- Resolves variable templates from barcode/task data +- Returns PDF buffer for upload to Paperless-NGX + +### Label Print Agent + +- Uses RxJS `Subject` (`newJob$`) to stream print jobs via SSE to connected agents +- Jobs are locked for 5 minutes to prevent race conditions +- SVG templates rendered to PNG via `@resvg/resvg-js` +- API documented in `docs/BACKEND_API.md` + +## Frontend Architecture + +- **Auth**: OIDC flow via `oidc-client-ts`, `AuthContext` provides user identity and tokens +- **API layer**: Axios-based client methods in `src/api/` (one file per domain) +- **Routing**: React Router v7 in `src/App.tsx` +- **Key components**: `DocumentEditModal`, `PdfSplitViewer`, `WysiwygEditor` (TipTap), `BarcodePositioner` +- During dev, Vite proxies `/api` to `localhost:3100` + +## Environment Variables + +Key variables (see `.env.example` for full list): + +| Variable | Purpose | +|----------|---------| +| `DB_*` | MySQL connection | +| `PAPERLESS_URL`, `PAPERLESS_TOKEN` | Paperless-NGX API | +| `OIDC_ISSUER`, `OIDC_CLIENT_ID` | OIDC provider (Authentik) | +| `OLLAMA_URL`, `OLLAMA_MODEL` | OCR service | +| `SCANNER_WATCH_DIR`, `SCANNER_ARCHIVE_DIR` | File system paths | +| `BELEGNUMMER_GET_URL`, `BELEGNUMMER_SET_URL` | External invoice number API | +| `PORT` | Backend port (default 3100) | +| `VITE_API_URL` | Override API URL in frontend (dev only) | diff --git a/paperless-backend/src/email/email-import.controller.ts b/paperless-backend/src/email/email-import.controller.ts index 4ca5e6f..0a08fcf 100644 --- a/paperless-backend/src/email/email-import.controller.ts +++ b/paperless-backend/src/email/email-import.controller.ts @@ -132,7 +132,7 @@ export class EmailImportController { // --- Final Import --- @Post('execute') - @RequirePermissions(Permission.MANAGE_ALL) + @RequirePermissions(Permission.VIEW_MAIL) async executeImport(@Body() importData: any) { try { const result = await this.importService.executeImport(importData); diff --git a/paperless-frontend/src/pages/InboxPage.tsx b/paperless-frontend/src/pages/InboxPage.tsx index af556b9..5648aee 100644 --- a/paperless-frontend/src/pages/InboxPage.tsx +++ b/paperless-frontend/src/pages/InboxPage.tsx @@ -434,6 +434,7 @@ export default function InboxPage() { {field.type === 'date' ? ( setFieldValues((prev) => ({