curo1305 1cdc532fff Add doc-service tests, AI category suggestions, LM Studio default
- pytest suite for doc-service: 20+ tests covering category CRUD,
  document upload/get/delete/patch, ownership isolation, category
  assignment, AI processing (mock), and live PDF tests (auto-skipped
  when tests/pdfs/ is empty)
- Minimal in-memory PDF builder in conftest so tests run without any
  fixture files; real PDFs can be dropped into tests/pdfs/ to activate
  live extraction tests
- AI prompt updated to return suggested_categories (2–5 short names)
- Frontend: SuggestionChip component in DocumentRow shows AI-suggested
  categories after processing; "Assign" links to an existing category,
  "Create & Assign" creates it first, ✕ dismisses locally
- Default AI provider changed to LM Studio at
  http://host.docker.internal:1234/v1 (host.docker.internal resolves
  to the macOS host from inside Docker Desktop)
- tests/pdfs/ directory tracked via .gitkeep; *.pdf excluded by .gitignore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 11:27:57 +02:00

destroying_sap

A fullstack SaaS web application built with FastAPI, React, and PostgreSQL.

Stack

Layer Tech
Backend FastAPI (async), SQLAlchemy 2, Alembic, PostgreSQL 16
Auth JWT bearer tokens (RS256), bcrypt password hashing
Frontend React 18, TypeScript, Vite, React Router v6, TanStack Query

Current State

  • User registration and login (JWT RS256 auth, 8-hour expiry)
  • Protected dashboard with nav bar
  • /api/users/me — authenticated user info
  • /api/profile/me — GET/PUT personal profile (position, phone, date of birth, address)
  • Admin-only user management at /admin: list, add, delete, toggle active
  • All input sanitized before reaching the DB (null-byte rejection, length caps, format validation)
  • PDF Documents app (/apps/documents): upload PDFs, async text extraction (pdfplumber), AI classification via Anthropic / Ollama / LM Studio, per-user categories, file download
  • Admin settings per app at /apps/documents/settings/admin: AI provider (cloud or local), upload limits; config stored in /config/doc_service_config.json on a shared Docker volume
  • /apps launcher hub — one card per installed app with Open + Settings links
  • 4 separate Docker containers: db, backend, doc-service, frontend
  • All containers run as non-root users (UID 1001 for app containers, UID 70 for db)
  • Network-isolated: only the frontend exposes a host port (80/5173); all backend services are unreachable from outside Docker
  • Dev environment seeds a test user automatically on startup (test@example.com / Test123!)
  • Password policy: min 8 chars, upper + lowercase, digit, special character, no common words
  • Pre-commit security hook (scripts/security_check.py) runs inside Docker on every commit

Containers

Container Image Host port Network User (UID:GID) Description
db postgres:16-alpine none backend-net 70:70 PostgreSQL database
backend custom (python:3.12-slim) none backend-net 1001:1001 FastAPI management API + proxy to doc-service
doc-service custom (python:3.12-slim) none backend-net 1001:1001 PDF extraction microservice
frontend custom (nginxinc/nginx-unprivileged:alpine) 80 (prod) / 5173 (dev) backend-net + frontend-net 1001:1001 React UI + nginx reverse proxy

Networks:

  • backend-net — db, backend, doc-service, and frontend reverse proxy; no host ports bound; outbound internet access allowed (needed for cloud AI API calls)
  • frontend-net — frontend only; this is where the single host port (80/5173) is bound

Volumes:

  • postgres_data — PostgreSQL data files
  • doc_data — uploaded PDF files (mounted into doc-service at /data/documents)
  • app_config — per-service runtime config JSON files (mounted into backend and doc-service at /config)

The frontend nginx proxies /api/* to backend:8000 via backend-net. The backend proxies /api/documents/* and /api/documents/categories/* to doc-service:8001. No backend, doc-service, or database port is ever exposed to the host.

Installation

Prerequisites

  • Docker + Docker Compose

Production

git clone <repo>
cd destroying_sap
cp .env.example backend/.env
python scripts/generate_jwt_keys.py  # paste output into backend/.env
docker compose up --build -d
  • Frontend: http://localhost
  • API docs: not directly accessible from host (backend port not exposed)

After first start, configure the AI provider at /apps/documents/settings/admin (admin login required).

Development (hot reload)

docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
  • Frontend (Vite): http://localhost:5173
  • Backend: reachable by frontend via Docker network only (not exposed to host)

Local (no Docker)

1. Start PostgreSQL

docker compose up db -d

2. Backend

cd backend
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
cp ../.env.example .env
alembic upgrade head
uvicorn app.main:app --reload

3. doc-service

cd features/doc-service
python -m venv .venv && source .venv/bin/activate
pip install -e .
alembic upgrade head
uvicorn app.main:app --host 0.0.0.0 --port 8001 --reload

4. Frontend

cd frontend && npm install && npm run dev

Environment Variables

Copy .env.example to backend/.env and adjust:

Variable Default Description
DATABASE_URL postgresql+asyncpg://postgres:password@localhost:5432/destroying_sap Async PostgreSQL URL
JWT_PRIVATE_KEY RS256 private key PEM (generate with scripts/generate_jwt_keys.py)
JWT_PUBLIC_KEY RS256 public key PEM (generate with scripts/generate_jwt_keys.py)
CORS_ORIGINS ["http://localhost:5173"] Allowed frontend origins
APP_CONFIG_DIR /config Directory for per-service runtime config JSON files
DOC_SERVICE_URL http://doc-service:8001 Internal URL of the doc-service (set by docker-compose)

doc-service reads DATABASE_URL, DATA_DIR, and CONFIG_PATH from its own environment (set in docker-compose.yml).

Development

# Backend lint + format
cd backend && ruff check . && ruff format .

# Backend tests
cd backend && pytest

# Frontend type check + lint
cd frontend && npm run typecheck && npm run lint

# New DB migration — main backend
cd backend && alembic revision --autogenerate -m "describe change"
cd backend && alembic upgrade head

# New DB migration — doc-service
cd features/doc-service && alembic revision --autogenerate -m "describe change"
cd features/doc-service && alembic upgrade head
S
Description
No description provided
Readme 1.4 MiB
Languages
Python 53.2%
TypeScript 44.1%
Dockerfile 1%
CSS 0.9%
Shell 0.5%
Other 0.2%