curo1305 e2c55556ac Switch JWT signing from HS256 to RS256 (4096-bit RSA)
- Replace symmetric SECRET_KEY with JWT_PRIVATE_KEY / JWT_PUBLIC_KEY (PEM)
- Add iat claim to every token
- Add expand_newlines validator in config for single-line .env PEM values
- Add scripts/generate_jwt_keys.py key-generation helper
- Update security-auditor agent JWT checklist with RS256 enforcement rules
- Mark RS256 as done in TODO.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 23:00:35 +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)
  • 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 Port User (UID:GID) Description
db postgres:16-alpine 5432 70:70 (postgres) PostgreSQL database
backend custom (python:3.12-slim) 8000 1001:1001 (appuser) FastAPI management API
frontend custom (nginxinc/nginx-unprivileged:alpine) 80 1001:1001 (appuser) React UI served by nginx (internal port 8080)

The frontend nginx container proxies /api/* to the backend container internally — no CORS headers needed in production.

Installation

Prerequisites

  • Docker + Docker Compose

Production

git clone <repo>
cd destroying_sap
cp .env.example backend/.env   # edit SECRET_KEY at minimum
docker compose up --build -d

Development (hot reload)

docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build

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
SECRET_KEY change-me-in-production JWT signing key
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%