curo1305 0d34867a69 Add PDF document service with AI extraction and per-app settings
- New `features/doc-service` FastAPI microservice: PDF upload, async
  text extraction (pdfplumber), AI classification via Anthropic/Ollama/
  LM Studio, per-user categories, file download
- Alembic migration isolated with `alembic_version_doc_service` table
- Main backend: httpx proxy routers for /api/documents/* and
  /api/documents/categories/*, admin settings API at /api/settings/*
- Runtime config in /config/doc_service_config.json (shared Docker
  volume); api_key masking on reads; atomic write with os.replace()
- Frontend: DocumentsPage, DocumentAdminSettingsPage, updated AppsPage
  launcher hub, simplified Nav (removed Settings link), new routes
- docker-compose: doc-service service, doc_data + app_config volumes,
  removed internal:true from backend-net for outbound AI API calls
- Fix pre-commit hook: probe Docker socket path so git subprocess picks
  up Docker Desktop on macOS
- Fix security_check.py: use sys.executable for bandit so venv python
  is used instead of system python

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 05:28:11 +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, bcrypt password hashing
Frontend React 18, TypeScript, Vite, React Router v6, TanStack Query

Current State

  • User registration and login (JWT auth)
  • Protected dashboard with nav bar (Dashboard | Profile | Logout)
  • /api/users/me — authenticated user info
  • /api/profile/me — GET/PUT personal profile (position, phone, date of birth, address)
  • Profile data stored in a dedicated profiles table; auto-created on first access
  • Admin role flag (is_superuser) stored in users table; exposed as is_admin in API (false for regular users, true for admins)
  • Admin-only user management at /admin: list all users, add users, delete users, toggle active status
  • All input sanitized before reaching the DB (null-byte rejection, length caps, format validation)
  • 3 separate Docker containers: db (PostgreSQL), backend (FastAPI), frontend (nginx)
  • All containers run as non-root users (UID 1001 for backend and frontend, UID 70 for db)
  • Network-isolated: only the frontend exposes a host port (80/5173); db and backend 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
frontend custom (nginxinc/nginx-unprivileged:alpine) 80 (prod) / 5173 (dev) backend-net + frontend-net 1001:1001 React UI + nginx reverse proxy

Networks:

  • backend-net (internal: true) — db, backend, and frontend reverse proxy communicate here; no host routing
  • frontend-net — frontend only; this is where the single host port (80/5173) is bound

The frontend nginx proxies /api/* to backend:8000 via backend-net. No backend 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); access via docker compose exec backend or add a dev-only port mapping

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   # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
cp ../.env.example .env
alembic upgrade head
uvicorn app.main:app --reload

3. 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

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 (after changing models)
cd backend && alembic revision --autogenerate -m "describe change"
cd backend && 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%