The migration file was added by feat/aiia-66-source-selection but was not
registered in _journal.json, so it never runs on deploy. This caused
'source_scope' column-missing errors on document insert.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
pdf-parse v2 depends on @napi-rs/canvas (native module) which fails in
Next.js standalone Docker builds — native binaries aren't traced/copied
to the standalone output, causing DOMMatrix is not defined at runtime.
Replaced pdf-parse entirely with pdfjs-dist legacy build which works
natively in Node.js without canvas or DOM API dependencies:
- New src/lib/pdf.ts: extractTextFromPdf() using pdfjs-dist/legacy/build
- Worker file explicitly imported so Next.js file tracer includes it
- Updated all call sites: documents, norms/parse, contracts
- Removed pdf-parse from dependencies, added pdfjs-dist directly
- Changed serverExternalPackages from pdf-parse to pdfjs-dist
Verified: build succeeds, both pdf.mjs and pdf.worker.mjs present in
.next/standalone, text extraction works in standalone context.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add DELETE /api/documents/:id endpoint that removes the DB record,
cleans up the stored file from disk, and logs an audit event. Add a
"Loeschen" button to the DokumentUpload component with confirmation
dialog.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
git is not available inside the node:20-alpine Docker image, so
git rev-parse in next.config.ts falls back to 'dev'. Now the commit
hash is passed as a COMMIT_HASH build arg from the host where git
is available, ensuring the footer displays the real commit hash.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The build hash was only in the sidebar which could be hidden or cut off.
Added a proper footer to the main content area so the commit hash is
always visible at the bottom of every dashboard page.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The old v1 API (`pdfParse(buffer)`) triggered DOMMatrix dependency via
pdfjs-dist canvas rendering path. The v2 API (`new PDFParse({ data })` +
`getText()`) uses a text-only code path that works in Node.js without
DOM/canvas polyfills.
Updated all three call sites:
- src/lib/documents/index.ts (generic document extraction)
- src/app/api/norms/parse/route.ts (norm PDF parsing)
- src/lib/contracts/index.ts (contract text extraction)
- src/types/pdf-parse.d.ts (updated type declarations for v2)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add DELETE /api/documents/:id endpoint that removes the DB record,
cleans up the stored file from disk, and logs an audit event. Add a
"Loeschen" button to the DokumentUpload component with confirmation
dialog.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Fix PDF extraction: detect scanned documents (no text layer), encrypted PDFs,
empty files, and missing files with clear German error messages
- Add error logging to extraction pipeline (was silently swallowed)
- Return errorMessage in document list API so UI can display failure reasons
- Add GET /api/documents/[id] endpoint for status polling
- Rewrite DokumentUpload component with:
- Auto-polling every 2s while documents are processing
- Visual step-by-step progress indicator (Hochgeladen → Extrahiere Text → Fertig)
- Error message display when extraction fails
- Debug toggle showing document ID, MIME type, size, timestamps
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add source scope (case/global) to documents, enabling users to select
which uploaded documents the AI considers during analysis. Includes
schema migration, API support, reusable source selection UI component,
and integration into the analyse form.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
drizzle-orm v0.45.2 declares a migrator export but does not ship the
actual .js file, causing all migrations to silently fail on every deploy.
This was the root cause of the missing "documents" table (AIIA-62).
The new script reads the drizzle journal and executes SQL files directly
via pg, with its own tracking table for idempotent re-runs.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Document upload failed with "invalid input syntax for type uuid" when
decision_id or norm_instrument_id were sent as empty strings from
FormData. The ?? operator only catches null/undefined, not "".
Added emptyToUndefined() to sanitize all optional UUID fields at the
API boundary before they reach the DB insert.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Dockerfile: create /app/uploads with correct ownership before switching
to the non-root nextjs user, fixing EACCES: permission denied on mkdir
- analyse-form.tsx: send 'title' and 'query' fields (not 'question') to
match the /api/analyses endpoint contract, fixing 400 rejection that
showed "Analyse konnte nicht gestartet werden"
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Next.js 16 deprecated the "middleware" file convention in favor of "proxy".
This fixes the build warning reported during manual deploy.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Inject git commit short hash at build time via NEXT_PUBLIC_BUILD_HASH
- Display build hash in sidebar footer for version tracking
- Add Gitea Actions workflow to auto-deploy on push to master (SSH → pull → rebuild)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Texts >10k chars are now split at § boundaries and parsed in separate
AI calls, then merged. This prevents maxOutputTokens truncation that
caused "AI returned invalid JSON" on large imports (~50k+ chars).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
AI routes now use requirePermission() + ctx.tenantId to get the tenant,
ensuring getModelForTenant() is always called with the correct tenant ID
so that stored API keys are used. Fixes norms/parse (was falling back to
getModel()) and analyses/structured (was trusting x-tenant-id header).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The entrypoint.sh migration script (migrate.mjs) imports
drizzle-orm/node-postgres/migrator which is never imported by app code.
Next.js standalone trace therefore omits it from node_modules, causing
the container to crash on startup before server.js runs.
Adding drizzle-orm and pg to serverExternalPackages ensures the full
packages are copied into the standalone output, fixing the migration
crash.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The "Netzwerkfehler beim Speichern des Schlüssels" was caused by two issues:
1. ENCRYPTION_KEY env var was not passed to the Docker container, so
AES-256-GCM encrypt() threw at runtime on every POST/PATCH.
2. The 0003_tenant_api_keys migration was not in the drizzle journal
and no migration runner existed in the Docker image.
Changes:
- docker-compose.yml: pass ENCRYPTION_KEY to app container
- .env.example: document ENCRYPTION_KEY with generation command
- .gitignore: allow .env.example to be tracked
- Dockerfile: include drizzle/ migrations and entrypoint script
- entrypoint.sh: run migrations before starting the app
- migrate.mjs: runtime migration script using drizzle-orm migrator
- drizzle journal: register 0003_tenant_api_keys migration
Co-Authored-By: Paperclip <noreply@paperclip.ing>
When selecting Anthropic or OpenAI as provider, the form now shows
an inline API key input field that creates or replaces the key via
the existing CRUD API. Includes key hint display and status indicator.
Resolves AIIA-49
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Zeigt im AI-Provider Formular einen Status-Indikator an, ob ein API-Schluessel
fuer den gewaehlten Provider (Anthropic/OpenAI) hinterlegt ist. Entfernt Ollama
aus dem API-Key Dropdown, da Ollama keine API-Keys benoetigt.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The import page now accepts PDF files in addition to TXT.
Backend uses pdf-parse to extract text from uploaded PDFs
before sending to AI for paragraph parsing.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Neuer /normen/import Bildschirm: Gesetzestext als Fliesstext einfuegen
oder TXT-Datei hochladen, KI zerlegt automatisch in Paragraphen,
Vorschau mit Bearbeitungsmoeglichkeit, dann Import ins Regelwerk.
- POST /api/norms/parse: AI-gestuetztes Parsing von Gesetzestexten
- /normen/import: Mehrstufiges Frontend (Eingabe -> Vorschau -> Import)
- Link zum Fliesstext-Import auf der Normen-Uebersichtsseite
Co-Authored-By: Paperclip <noreply@paperclip.ing>
PostgreSQL SET commands do not support parameterized queries ($1),
causing "syntax error at or near $1" on all tenant-scoped operations.
Replaced with set_config('app.tenant_id', $1, true) which supports
parameters safely. Also added BEGIN/COMMIT transaction wrapping since
set_config(..., true) requires a transaction for LOCAL scope.
Fixed SQL injection vulnerability in tenant.ts which used unescaped
string interpolation.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The static "new" segment was missing, so Next.js treated "new" as a
UUID parameter for [id]/page.tsx, causing a Postgres "invalid input
syntax for type uuid" error. Adding cases/new/page.tsx with a create
form resolves the server error.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- API routes: GET/POST /api/cases (list + create), GET/PATCH/DELETE /api/cases/[id]
- Cases list page with search, status filter, and pagination
- Case detail page showing linked analyses and proceedings
- Sidebar navigation: added "Fälle" link after Dashboard
- Tenant isolation via withTenantDb + requirePermission on all API routes
- Audit logging on all case operations
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add Ollama as a third AI provider option alongside Anthropic and OpenAI.
Uses the OpenAI-compatible API endpoint that Ollama exposes, configured
via OLLAMA_URL and OLLAMA_MODEL env vars. Provider selection is now
tenant-aware via DB settings, with env var fallback.
- New provider type 'ollama' in AIProvider union
- Tenant-aware getModelForTenant() reads AI config from tenant settings jsonb
- Admin settings UI on /einstellungen for provider/model selection
- API route GET/PATCH /api/settings/ai for tenant AI config
- Updated all AI call sites (analysis, structured-analysis, contracts)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- GET /api/decisions: Add requirePermission('decisions:read'), use
withTenantDb() for RLS enforcement, add application-level tenant
filter (own tenant OR published+anonymized)
- POST /api/decisions: Add requirePermission('decisions:write'), use
withTenantDb(), set tenantId from authenticated session context
instead of accepting it from request body (prevents tenant spoofing)
Addresses DSGVO Art. 32 (security of processing) and Art. 5(1)(f)
(integrity and confidentiality).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Implement § 61 NV Bühne non-renewal deadline calculation with tiered
protection (standard 31.10., extended 31.07. for 15+ years, special
protection for over-55), tariff-based compensation calculation with
Gagenklassen and Dienstalterszulage, and Spielzeit seasonal logic
(1.8.–31.7. with Probenzeit). Includes DB schema (contracts,
compensationRules, nonRenewalDeadlines), migration, and three API
endpoints under /api/nv-buehne/.
Co-Authored-By: Paperclip <noreply@paperclip.ing>