# External Integrations ## Database: PostgreSQL 16 **Location**: `backend/internal/db/db.go`, `backend/internal/db/queries.go` ### Connection - **Driver**: `github.com/jackc/pgx/v5` (pgx connection pool) - **Default URL**: `postgres://simplefin:simplefin@localhost:5432/simplefindb?sslmode=disable` - **Environment Variable**: `DATABASE_URL` - **Connection Pool**: `pgxpool.Pool` with automatic management ### Schema **Tables** (from `backend/migrations/001_initial.sql`): 1. **users** — id (UUID), email, password_hash, oidc_subject, display_name, preferred_locale, timestamps 2. **categories** — id (UUID), user_id (FK), name, type (enum), icon, sort_order, timestamps 3. **budgets** — id (UUID), user_id (FK), name, start_date, end_date, currency, carryover_amount, timestamps 4. **budget_items** — id (UUID), budget_id (FK), category_id (FK), budgeted_amount, actual_amount, notes, timestamps 5. **schema_migrations** — version tracking **Enum**: `category_type` — bill, variable_expense, debt, saving, investment, income **Numeric precision**: `NUMERIC(12, 2)` in PostgreSQL, `shopspring/decimal` in Go ### Migration Runner - Custom implementation in `backend/internal/db/db.go` - Reads `.sql` files from embedded filesystem (`embed.FS`) - Tracks applied versions in `schema_migrations` table - Sequential numbering: `001_initial.sql`, `002_...`, etc. ## Authentication ### Local Auth (Active) **Location**: `backend/internal/auth/auth.go` - **Password hashing**: bcrypt with `DefaultCost` - **Session**: JWT (HS256) in HTTP-only cookie - **Secret**: `SESSION_SECRET` environment variable - **Token TTL**: 7 days - **Cookie**: HttpOnly, SameSite=Lax, MaxAge=7 days **Routes**: - `POST /api/auth/register` — Create account - `POST /api/auth/login` — Login - `POST /api/auth/logout` — Clear cookie - `GET /api/auth/me` — Current user ### OIDC (Planned, Not Implemented) **Location**: `backend/internal/api/handlers.go` (stubs returning 501) - Routes: `GET /api/auth/oidc`, `GET /api/auth/oidc/callback` - Environment variables defined in `compose.yml`: `OIDC_ISSUER`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET` - DB support ready: `oidc_subject` column, `GetUserByOIDCSubject()`, `UpsertOIDCUser()` queries ## CORS **Location**: `backend/internal/api/router.go` ```go AllowedOrigins: ["http://localhost:5173", "http://localhost:8080"] AllowedMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"] AllowedHeaders: ["Content-Type"] AllowCredentials: true ``` ## Frontend API Client **Location**: `frontend/src/lib/api.ts` - Base URL: `/api` (proxied to `http://localhost:8080` via Vite in dev) - Uses native Fetch API with `credentials: 'include'` - Custom `ApiError` class for error handling - Typed endpoints: `auth.*`, `categories.*`, `budgets.*`, `budgetItems.*`, `settings.*` ## Internationalization (i18n) **Location**: `frontend/src/i18n/` - **Library**: i18next + react-i18next - **Languages**: English (en, default), German (de) - **Keys**: ~87 per language (auth, navigation, dashboard, budget, categories, settings) - **User preference**: Stored in DB (`preferred_locale` column) ## Deployment ### Docker (Multi-stage Build) **Location**: `Dockerfile` 1. **Stage 1** (bun): Build frontend → `frontend/dist` 2. **Stage 2** (golang:1.24-alpine): Build Go binary with embedded frontend + migrations 3. **Stage 3** (alpine): Runtime with `ca-certificates`, port 8080 ### Docker Compose **Location**: `compose.yml` - **app**: Go server (:8080), depends on db - **db**: PostgreSQL 16 (:5432), health check, `pgdata` volume ### Environment Variables | Variable | Purpose | Required | |----------|---------|----------| | `DATABASE_URL` | PostgreSQL connection string | Yes | | `SESSION_SECRET` | JWT signing secret | Yes (production) | | `OIDC_ISSUER` | OIDC provider URL | No (future) | | `OIDC_CLIENT_ID` | OIDC client ID | No (future) | | `OIDC_CLIENT_SECRET` | OIDC client secret | No (future) | ## External Services **None currently integrated.** No third-party APIs, webhooks, payment processors, email providers, or message queues.