No description
- Python 47.3%
- Go 18.4%
- TypeScript 14.3%
- Svelte 13.4%
- PHP 5%
- Other 1.4%
Catch the IntegrityError raised when a concurrent provision/create loses the race on uq_services_provisioner_extservice, then re-fetch and return the existing service idempotently (200) inside the transaction. The insert runs in a SAVEPOINT so only the losing insert is rolled back, not the caller's unit of work. Adds a real-MySQL race test asserting 200 (not 500) and no double allocation. task: D4 follow-up |
||
|---|---|---|
| .forgejo/workflows | ||
| backend | ||
| deploy | ||
| docs | ||
| frontend | ||
| proxy | ||
| scripts | ||
| whmcs-module | ||
| .env.example | ||
| .gitignore | ||
| README.md | ||
app-base
A reusable, runnable application starter repo: FastAPI + MySQL/MariaDB + Redis backend with a complete opaque-token auth system, and a SvelteKit static-SPA frontend with a custom Bootstrap theme. CI runs on Forgejo Actions with real MySQL + Redis service containers and coverage gates.
This repo is intentionally generic. A real application is created by
forking it and layering domain logic on top; the base files are meant to be
configured, not edited, so periodic git merge upstream/main stays clean.
This is Part 1 (
app-base). It contains no domain logic.
Stack
| Layer | Tech |
|---|---|
| Backend | Python 3.11+, FastAPI, SQLAlchemy 2.x, Alembic, Redis |
| Auth | Single unified opaque bearer token (no JWT); header or cookie |
| Frontend | SvelteKit + adapter-static (SPA), Bootstrap 5 + custom SCSS theme |
| CI | Forgejo Actions (GitHub-Actions-compatible) |
| Dev infra | docker-compose (MySQL + Redis), nginx (static + SPA fallback) |
Layout
backend/ FastAPI app (app/core, app/models, app/schemas, app/api, app/services), alembic/, tests/
frontend/ SvelteKit static SPA (src/lib/{api,stores,components,ui,auth}, src/routes)
docs/ Generic contracts: security-spec, testing-spec, auth-contract, base-schema
deploy/ docker-compose.yml (mysql+redis), nginx.conf
.forgejo/ CI workflows
scripts/ seed / fixtures helpers
Quick start (development)
# 1. Secrets
cp .env.example .env # then edit values (never commit .env)
# 2. Infra (MySQL + Redis)
docker compose -f deploy/docker-compose.yml up -d
# 3. Backend
cd backend
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
alembic upgrade head
python ../scripts/seed.py # create a dev admin user
uvicorn app.main:app --reload # http://localhost:8000 (docs at /docs in dev)
# 4. Frontend
cd ../frontend
npm install
npm run dev # http://localhost:5173
Auth model (summary)
- One opaque, high-entropy token per session/key. Stored only as a SHA-256
hash (Redis for sessions, MySQL
api_tokensfor long-lived/named keys, with a Redis cache). - Resolved from either an
Authorization: Bearer <token>header or an httpOnly+Secure+SameSite=Laxcookie holding the same token. One validation path. - Scopes:
admin,provision,node, reservedclient. - Cookie-authenticated state-changing requests require a double-submit
X-CSRF-Token. Bearer-header requests are CSRF-exempt. - Instant revocation via Redis eviction / cache invalidation.
See docs/auth-contract.md and
docs/security-spec.md for the full contract.
Testing
# Backend (needs MySQL + Redis reachable; see .env TEST_DATABASE_URL / REDIS_URL)
cd backend && pytest # enforces >=90% coverage on app/services + app/api
# Frontend
cd frontend
npm run test:unit # Vitest + Testing Library (coverage gate)
npm run test:e2e # Playwright against the built SPA
CI runs all of the above on every push; see .forgejo/workflows/ci.yml.
License
Internal starter; license TBD.