--- pdf_options: format: A4 margin: 25mm 20mm headerTemplate: '
Oliver DeckForge — System Administration Guide
' footerTemplate: '
/
' displayHeaderFooter: true stylesheet: - https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.5.1/github-markdown.min.css body_class: markdown-body css: |- body { font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; } .markdown-body { max-width: 100%; } h1 { color: #5146E5; border-bottom: 3px solid #5146E5; padding-bottom: 8px; } h2 { color: #3b3494; border-bottom: 1px solid #E9E8F8; padding-bottom: 6px; margin-top: 2em; } h3 { color: #4a4a4a; } h4 { color: #555; } table { width: 100%; } th { background: #5146E5; color: white; } td, th { padding: 8px 12px; } code { background: #f0eff8; color: #5146E5; padding: 2px 6px; border-radius: 4px; } pre code { background: #1e1e2e; color: #cdd6f4; display: block; padding: 16px; border-radius: 8px; } blockquote { border-left: 4px solid #5146E5; background: #f8f7ff; padding: 12px 16px; margin: 16px 0; } .tip { border-left: 4px solid #22c55e; background: #f0fdf4; padding: 12px 16px; margin: 16px 0; border-radius: 0 8px 8px 0; } .warning { border-left: 4px solid #f59e0b; background: #fffbeb; padding: 12px 16px; margin: 16px 0; border-radius: 0 8px 8px 0; } .danger { border-left: 4px solid #ef4444; background: #fef2f2; padding: 12px 16px; margin: 16px 0; border-radius: 0 8px 8px 0; } img { max-width: 100%; max-height: 280px; object-fit: contain; display: block; margin: 16px auto; } --- # Oliver DeckForge — System Administration Guide **Version 1.0** | Complete Setup, Configuration & Operations Reference --- ## Table of Contents 1. [Architecture Overview](#1-architecture-overview) 2. [Installation & Deployment](#2-installation--deployment) 3. [Environment Variables Reference](#3-environment-variables-reference) 4. [Authentication Configuration](#4-authentication-configuration) 5. [Role-Based Access Control](#5-role-based-access-control) 6. [Admin Panel Operations](#6-admin-panel-operations) 7. [Template Pipeline](#7-template-pipeline) 8. [AI Provider Configuration](#8-ai-provider-configuration) 9. [Database Administration](#9-database-administration) 10. [Background Jobs & Workers](#10-background-jobs--workers) 11. [Nginx & Networking](#11-nginx--networking) 12. [Storage & File Management](#12-storage--file-management) 13. [Monitoring & Logging](#13-monitoring--logging) 14. [Backup & Recovery](#14-backup--recovery) 15. [Security Hardening](#15-security-hardening) 16. [Scaling & Performance](#16-scaling--performance) 17. [Troubleshooting](#17-troubleshooting) 18. [API Reference](#18-api-reference) --- ## 1. Architecture Overview ### 1.1 System Architecture ```mermaid graph LR Browser([Browser]) --> NG[nginx :80] NG --> NX[Next.js :3000] NG --> FA[FastAPI :8000] FA --> PG[(PostgreSQL)] FA --> RD[(Redis)] FA --> VOL[/app_data/] ARQ[ARQ Worker] --> PG ARQ --> RD ARQ --> VOL FA --> AI{AI APIs} ARQ --> AI ``` ### 1.2 Service Overview | Service | Technology | Port | Purpose | |---------|-----------|------|---------| | **nginx** | nginx:alpine | 80 | Reverse proxy, static file serving, SSL termination | | **web** | Next.js 14 | 3000 | Frontend SPA, Puppeteer-based export | | **api** | FastAPI + SQLModel | 8000 | REST API, authentication, RBAC | | **worker** | ARQ (Python) | — | Background AI generation, parsing, retention | | **postgres** | PostgreSQL 16 | 5432 | Primary relational database | | **redis** | Redis 7 | 6379 | Job queue, caching | ### 1.3 Request Flow ```mermaid sequenceDiagram participant B as Browser participant N as nginx participant W as Next.js participant A as FastAPI participant M as Middleware participant DB as PostgreSQL B->>N: HTTP Request alt /api/v1/* N->>A: Proxy to backend A->>M: Auth + RBAC check M->>DB: Validate session DB-->>M: User context M-->>A: request.state.user A-->>N: JSON response else /* (all other) N->>W: Proxy to frontend W-->>N: HTML/JS/CSS end N-->>B: Response ``` ### 1.4 Data Flow: Presentation Generation ```mermaid flowchart LR A[Submit] --> B[Decompose] --> C[Outline] --> D[Template] D --> E[Job Queue] --> F[Worker] F --> G[AI Content] --> H[Images] --> I[Save DB] I --> J[SSE Notify] --> K[Editor] ``` --- ## 2. Installation & Deployment ### 2.1 Prerequisites | Requirement | Minimum | Recommended | |-------------|---------|-------------| | Docker | 20.10+ | 24.0+ | | Docker Compose | v2.0+ | v2.20+ | | RAM | 4 GB | 8 GB+ | | Disk | 10 GB | 50 GB+ (for generated assets) | | CPU | 2 cores | 4+ cores | ### 2.2 Quick Start ```bash # 1. Clone repository git clone cd ppt-tool # 2. Configure environment cp .env.example .env # Edit .env — set ANTHROPIC_API_KEY at minimum # 3. Build and start make dev # 4. Run migrations make migrate # 5. Seed default data make seed ``` The application is available at: - **http://localhost** — Full application (via nginx) - **http://localhost:3000** — Frontend directly - **http://localhost:8000** — API directly - **http://localhost/docs** — Swagger API documentation ### 2.3 Makefile Commands | Command | Description | |---------|-------------| | `make dev` | Build and start all services with logs | | `make build` | Build Docker images only | | `make up` | Start services in background (detached) | | `make down` | Stop and remove all containers | | `make migrate` | Run Alembic database migrations | | `make seed` | Seed default admin user and team | | `make test` | Run backend pytest suite | | `make test-e2e` | Run Cypress E2E tests | | `make test-all` | Run all tests | | `make logs` | Follow all container logs | | `make shell-api` | Open bash shell in API container | | `make shell-db` | Open psql shell in PostgreSQL | ### 2.4 Local Development (Without Docker) #### Backend ```bash cd backend python -m venv venv && source venv/bin/activate pip install -r requirements.txt export DATABASE_URL="postgresql+asyncpg://deckforge:deckforge@localhost:5432/deckforge" export REDIS_URL="redis://localhost:6379/0" export APP_DATA_DIRECTORY="./data" export ANTHROPIC_API_KEY="sk-ant-..." # Start API uvicorn api.main:app --reload --port 8000 # Start worker (separate terminal) python -m arq workers.main.WorkerSettings ``` #### Frontend ```bash cd frontend npm install npm run dev ``` The Next.js dev server proxies `/api/v1/` requests to `http://localhost:8000` via rewrites in `next.config.mjs`. ### 2.5 Docker Image Details **Backend Dockerfile** (multi-stage): - **Builder stage**: `python:3.11-slim-bookworm` + `uv` package manager - **Runtime stage**: Includes LibreOffice (PDF conversion), Chromium (browser automation), fontconfig, ONNX models - Exposes port 8000 **Frontend Dockerfile**: - **Base**: `node:20-alpine` - Includes Chromium for server-side Puppeteer PDF/PPTX export - Environment: `PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser` - Exposes port 3000 --- ## 3. Environment Variables Reference ### 3.1 Database & Infrastructure | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `POSTGRES_PASSWORD` | No | `deckforge` | PostgreSQL password | | `DATABASE_URL` | Auto | Set by docker-compose | Full async connection string | | `REDIS_URL` | No | `redis://redis:6379/0` | Redis connection string | | `APP_DATA_DIRECTORY` | No | `/app_data` | Path for images, exports, uploads | | `TEMP_DIRECTORY` | No | `/tmp/deckforge` | Temporary file storage | ### 3.2 Authentication | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `JWT_SECRET_KEY` | **Yes** | `change-me-...` | Secret for JWT signing (256-bit+) | | `AZURE_AD_TENANT_ID` | No | — | Azure AD tenant ID (blank = dev mode) | | `AZURE_AD_CLIENT_ID` | No | — | Azure AD app client ID | | `AZURE_AD_CLIENT_SECRET` | No | — | Azure AD app secret | | `AZURE_AD_REDIRECT_URI` | No | `http://localhost/api/v1/auth/callback` | OAuth callback URL | | `DEV_AUTH_PASSWORD` | No | `devpass123` | Dev bypass login password | ### 3.3 AI Providers | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `LLM` | No | `anthropic` | Primary LLM provider | | `ANTHROPIC_API_KEY` | **Yes*** | — | Claude API key | | `ANTHROPIC_MODEL` | No | `claude-sonnet-4-6` | Claude model ID | | `OPENAI_API_KEY` | No | — | OpenAI API key | | `OPENAI_MODEL` | No | — | OpenAI model ID | | `GOOGLE_API_KEY` | No | — | Google Gemini API key | | `GOOGLE_MODEL` | No | — | Google model ID | | `OLLAMA_URL` | No | — | Ollama server URL | | `OLLAMA_MODEL` | No | — | Ollama model name | | `IMAGE_PROVIDER` | No | `nanobanana_pro` | Image generation provider | | `DISABLE_IMAGE_GENERATION` | No | — | Set to disable image gen | *Required if using Anthropic (default provider) ### 3.4 Application Settings | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `CAN_CHANGE_KEYS` | No | `false` | Allow runtime API key changes | | `DISABLE_ANONYMOUS_TRACKING` | No | `true` | Disable analytics tracking | | `SETTINGS_ENCRYPTION_KEY` | No | — | Fernet key for encrypting stored API keys | | `EXTENDED_REASONING` | No | — | Enable LLM extended thinking | | `TOOL_CALLS` | No | — | Enable LLM tool use | | `WEB_GROUNDING` | No | — | Enable web search in generation | | `NEXT_INTERNAL_URL` | No | `http://web:3000` | Backend → frontend URL (Docker) | ### 3.5 Supported AI Providers **LLM Providers:** | Provider | Value | Models | |----------|-------|--------| | Anthropic | `anthropic` | claude-opus-4-6, claude-sonnet-4-6, claude-sonnet-4-5, claude-haiku-4-5 | | OpenAI | `openai` | gpt-4.1, gpt-4.1-mini, gpt-4o, o3, o4-mini | | Google | `google` | gemini-2.5-flash, gemini-2.5-pro, gemini-2.0-flash | | Ollama | `ollama` | Any locally installed model | | Custom | `custom` | Any OpenAI-compatible endpoint | **Image Providers:** | Provider | Value | Requirements | |----------|-------|-------------| | NanoBanana Pro | `nanobanana_pro` | Google API key | | Gemini Flash | `gemini_flash` | Google API key | | DALL-E 3 | `dall-e-3` | OpenAI API key | | GPT Image 1.5 | `gpt-image-1.5` | OpenAI API key | | Pexels | `pexels` | Pexels API key | | Pixabay | `pixabay` | Pixabay API key | | ComfyUI | `comfyui` | Local ComfyUI instance | --- ## 4. Authentication Configuration ### 4.1 Authentication Flow ```mermaid flowchart LR A[Login Page] --> B{Azure AD?} B -->|Yes| C[SSO] --> D[Callback] --> E{User exists?} B -->|No| F[Dev Login] --> E E -->|Yes| G[Update login] E -->|No| H[Create user] G --> I[JWT Cookie] H --> I ``` ### 4.2 Azure AD Setup (Production) 1. **Register an application** in Azure Portal > Azure AD > App Registrations 2. Set the **Redirect URI** to: `https://your-domain.com/api/v1/auth/callback` 3. Create a **client secret** under Certificates & Secrets 4. Configure environment variables: ```env AZURE_AD_TENANT_ID=your-tenant-id AZURE_AD_CLIENT_ID=your-client-id AZURE_AD_CLIENT_SECRET=your-client-secret AZURE_AD_REDIRECT_URI=https://your-domain.com/api/v1/auth/callback ``` 5. Grant API permissions: `User.Read` (delegated) ### 4.3 Development Mode When `AZURE_AD_TENANT_ID` is empty or not set, the system enables development authentication: - Login form with email + password fields - Password validated against `DEV_AUTH_PASSWORD` environment variable - Users are auto-created on first login - Default role: `user`
**Security Warning:** Development mode should never be used in production. Always configure Azure AD or another SSO provider for production deployments.
### 4.4 JWT Configuration | Parameter | Value | |-----------|-------| | Algorithm | HS256 | | Expiry | 24 hours | | Storage | `session_token` HTTP cookie | | Payload | `sub` (user UUID), `email`, `role`, `exp`, `iat` |
**Critical:** Change `JWT_SECRET_KEY` from the default value in production. Use a cryptographically random 256-bit key. All active sessions are invalidated when this key changes.
--- ## 5. Role-Based Access Control ### 5.1 Role Hierarchy ```mermaid graph LR SA[Super Admin] --> CA[Client Admin] --> U[User] SA -.->|Full access| ALL[All Clients] CA -.->|Scoped| CLIENT[Assigned Clients] U -.->|Basic| OWN[Own Data] ``` ### 5.2 Permission Matrix | Feature | Super Admin | Client Admin | User | |---------|:-----------:|:------------:|:----:| | **Presentations** | All | Client-scoped | Own only | | **Admin Panel** | Full | Limited | None | | **User Management** | CRUD all | View team members | — | | **Client Management** | CRUD all | View/edit assigned | — | | **Team Management** | All teams | Assigned client teams | — | | **Master Decks** | All | Client-scoped | — | | **Storage** | All clients | Client-scoped | — | | **Analytics** | Global + per-client | Client-scoped | — | | **Audit Logs** | All | Client-scoped | — | | **System Settings** | Full access | — | — | | **Brand Config** | All clients | Client-scoped | — | ### 5.3 Multi-Tenant Data Isolation ```mermaid flowchart LR A[Request] --> B[Auth] --> C[RBAC] --> D{Role} D -->|super_admin| E[No filter] D -->|client_admin| F[Team lookup → client_ids] D -->|user| G[Own data + team clients] ``` The `_resolve_client_filter()` pattern is used throughout: - **Super Admin with no client_id param** → Returns `None` (no filter, see all) - **Super Admin with client_id param** → Filters to specific client - **Client Admin** → Auto-scoped to accessible clients via `TeamMembershipModel` - **User** → Scoped to own data within accessible clients ### 5.4 Admin Panel Navigation The sidebar dynamically shows menu items based on role: | Menu Item | Super Admin | Client Admin | |-----------|:-----------:|:------------:| | Users | Yes | — | | Clients | Yes | Yes | | Storage | Yes | Yes | | Audit Log | Yes | Yes | | Analytics | Yes | Yes | | Settings | Yes | — | --- ## 6. Admin Panel Operations ### 6.1 User Management **Path:** Admin > Users (Super Admin only) #### Listing Users - Table with columns: Name, Email, Role, Status, Last Login - Filterable by active status and role #### Changing User Roles 1. Find the user in the list 2. Click the role dropdown 3. Select new role: `super_admin`, `client_admin`, or `user`
You cannot change your own role. This prevents accidental lockout.
#### Deactivating Users 1. Click **Deactivate** on the user row 2. Confirm the action 3. User's `is_active` is set to false — they can no longer log in 4. Their presentations remain in the system #### Transferring Ownership Before deactivating a user (e.g., for GDPR compliance): 1. Use the transfer ownership endpoint to move all presentations to another user 2. Then deactivate the original user ### 6.2 Client Management **Path:** Admin > Clients #### Creating a Client 1. Click **"+ New Client"** 2. Enter the client name 3. A URL-safe slug is auto-generated 4. A default team is auto-created for the client #### Client Settings Each client has configurable: | Setting | Description | |---------|-------------| | **Name** | Display name | | **Slug** | URL-safe identifier (unique) | | **Review Policy** | `self_approve` or `require_reviewer` | | **Retention Days** | Auto-delete presentations after N days (optional) | ### 6.3 Team Management **Path:** Admin > Clients > [Client] > Teams Teams group users within a client: - Each client has a **default team** (cannot be deleted) - Users can belong to multiple teams - Team membership determines client access for non-admin users #### Adding Team Members 1. Navigate to the client's team page 2. Click **"+ Add Member"** 3. Search and select a user from the dropdown 4. The user now has access to this client's data ### 6.4 Brand Configuration **Path:** Admin > Clients > [Client] > Brand Config Configure branding per client: | Setting | Description | |---------|-------------| | **Primary Colors** | Color picker, add/remove multiple colors | | **Secondary Colors** | Color picker, add/remove multiple colors | | **Fonts** | Heading, Body, and Accent font names | | **Logos** | Upload multiple logo images | | **Voice Rules** | Text guidelines for AI tone and style | | **Voice Examples** | Good/bad example pairs for AI training | | **Brand Guideline** | Upload a PDF/DOCX brand guide | Brand configuration is injected into AI prompts during presentation generation to ensure brand consistency. ### 6.5 Storage Management **Path:** Admin > Storage #### Summary Dashboard Four cards show: - **Presentations** count - **Export Files** count - **Master Decks** count - **Total Size** (formatted) #### Client Selector (Super Admin) Dropdown to filter by specific client or view all clients combined. #### Presentation Table - Columns: Title, Status, Created, Files, Size - Checkboxes for bulk selection - Per-row actions: Download PPTX, Delete #### Bulk Operations - Select multiple presentations via checkboxes - Click **"Delete Selected"** for bulk soft-delete #### Purge Files (Super Admin Only) - Amber banner shows count of soft-deleted presentations - **"Purge Files"** permanently removes files from disk - Returns statistics: files purged, bytes freed ### 6.6 Analytics Dashboard **Path:** Admin > Analytics #### Overview Metrics - Total Presentations (all-time) - This Month / This Week (30/7-day counts) - Active Users (distinct users, last 30 days) - Approval Rate (% approved or in_review) #### Usage Metrics - **Presentations per Day** — 14-day bar chart - **Top 10 Users** — ranked by presentation count #### Quality Metrics - **Status Distribution** — draft/in_review/approved breakdown - **Presentations with Comments** — count #### Performance Metrics - **Average Generation Time** — job completion duration - **Total Jobs** — all-time count - **Error Rate** — % failed jobs #### AI Usage (if tracking enabled) - Total AI Calls, Input/Output Tokens - Usage by Provider (bar chart) - Usage by Model (top 10) - Daily Usage Trend ### 6.7 Audit Logs **Path:** Admin > Audit Log All mutating API requests are logged automatically. #### Query Filters - **Action** — text search (e.g., "admin_delete") - **User ID** — filter by specific user - **Resource Type** — e.g., "presentation", "storage" - **Client ID** — scope to a client - **Date Range** — from/to date pickers #### Export - Click **"Export Audit Log"** - Choose format: CSV or JSON - Downloads up to 10,000 entries #### Logged Actions | Action | Trigger | |--------|---------| | `admin_delete` | Single presentation soft-delete | | `admin_bulk_delete` | Bulk presentation delete | | `admin_purge` | Hard-delete purged files | | Role changes, team membership updates, etc. | Various admin operations | --- ## 7. Template Pipeline ### 7.1 Master Deck → Template Flow ```mermaid flowchart LR A[Upload PPTX] --> B[Enqueue Job] B --> C{Parse Mode} C -->|layouts| D[slideLayouts XML] C -->|slides| E[slides XML] D --> F[PDF → Screenshots] E --> F F --> G[LLM Vision: Screenshot+XML → TSX] G --> H[Store Layouts] --> I[Register Template] ``` ### 7.2 Upload & Parse 1. Navigate to **Admin > Clients > [Client] > Master Decks** 2. Click **"Upload PPTX"** and select a `.pptx` file 3. The deck enters **"pending"** status, then **"processing"** 4. Auto-polling every 5 seconds shows current status 5. On completion, status changes to **"completed"** and layouts appear ### 7.3 Parse Modes | Mode | Source | Best For | |------|--------|----------| | **slides** (default) | Actual slides (`ppt/slides/`) | Decks with unique slide designs; 1:1 screenshot match | | **layouts** | Slide layouts (`ppt/slideLayouts/`) | Decks with reusable layout templates; may produce more layouts | ### 7.4 Layout Management After parsing, manage layouts in the expanded deck view: **Filtering & Search:** - Text search by layout name - Type filter dropdown (auto-detected from layout types) - Code filter: All / Has Code / Missing Code **Individual Actions:** - **Edit** — modify name, type, or React TSX code - **Delete** — remove with confirmation dialog **Bulk Actions:** - Toggle **Select Mode** to show checkboxes - **Select All** / **Deselect All** - **Delete Selected** — bulk remove
After deleting layouts, the system automatically re-registers the template by recreating `PresentationLayoutCodeModel` records for the remaining layouts.
### 7.5 Reparsing If layouts need to be regenerated (e.g., after an LLM model upgrade): 1. Click the reparse dropdown on the deck card 2. Choose **"Reparse (slides)"** or **"Reparse (layouts)"** 3. All existing layouts are replaced with freshly parsed versions --- ## 8. AI Provider Configuration ### 8.1 Settings Page **Path:** Admin > Settings (Super Admin only) ```mermaid flowchart LR A[Select LLM Provider] --> B[Fetch Available Models] B --> C[Select Model] C --> D[Enter API Key] D --> E[Test Connection] E -->|OK| F[Save Settings] E -->|Fail| G[Check Key / Network] ``` ### 8.2 Configuring LLM Provider 1. Open **Admin > Settings** 2. Select the **LLM Provider** from the dropdown 3. The **Model** dropdown auto-populates with available models 4. Enter the **API Key** if not already set (shown as "Set" badge if configured) 5. Click **"Test"** to verify connectivity 6. Click **"Save Changes"** ### 8.3 Configuring Image Provider 1. Select the **Image Provider** from the dropdown 2. Ensure the required API key is set (e.g., Google API key for NanoBanana Pro) 3. Save changes ### 8.4 Connection Testing The **"Test"** button performs a lightweight API call to validate the key: | Result | Display | |--------|---------| | Success | Green check + latency in ms | | Failure | Red X + error message | ### 8.5 Settings Persistence Settings are persisted to the database via `KeyValueSqlModel`: - Survive container restarts - API keys optionally encrypted at rest (if `SETTINGS_ENCRYPTION_KEY` is set) - Environment variables serve as defaults — database values override them --- ## 9. Database Administration ### 9.1 Schema Overview ```mermaid erDiagram UserModel ||--o{ TeamMembershipModel : "belongs to" TeamModel ||--o{ TeamMembershipModel : "has" ClientModel ||--o{ TeamModel : "owns" ClientModel ||--o{ BrandConfigModel : "has" ClientModel ||--o{ MasterDeckModel : "has" UserModel ||--o{ PresentationModel : "creates" ClientModel ||--o{ PresentationModel : "scopes" PresentationModel ||--o{ SlideModel : "contains" PresentationModel ||--o{ JobModel : "tracks" UserModel ||--o{ AuditLogModel : "generates" UserModel ||--o{ AIUsageModel : "tracks" MasterDeckModel ||--o{ TemplateModel : "registers as" TemplateModel ||--o{ PresentationLayoutCodeModel : "has layouts" ``` ### 9.2 Core Tables | Table | Purpose | Key Fields | |-------|---------|------------| | `usermodel` | User accounts | id, email, role, azure_oid, is_active | | `clientmodel` | Tenant organizations | id, name, slug, retention_days, review_policy | | `teammodel` | Team groupings | id, name, client_id, is_default | | `teammembershipmodel` | User↔Team links | user_id, team_id, assigned_by | | `presentationmodel` | Presentations | id, title, owner_id, client_id, status, content | | `slidemodel` | Individual slides | id, presentation, index, content, layout | | `jobmodel` | Background jobs | id, job_type, status, progress, error_message | | `masterdeck` | Master PPTX decks | id, client_id, layouts (JSON), parse_status | | `templatemodel` | Registered templates | id, name, description | | `presentationlayoutcodemodel` | Layout TSX code | presentation, layout_name, layout_code | | `brandconfigmodel` | Brand settings | client_id, colors, fonts, logos, voice_rules | | `auditlogmodel` | Audit trail | user_id, action, resource_type, ip_address | | `aiusagemodel` | AI usage metrics | provider, model, tokens, duration_ms | | `keyvaluesqlmodel` | KV settings store | key, value (JSON) | | `imageasset` | Generated images | id, path, is_uploaded | ### 9.3 Alembic Migrations ```bash # View migration history docker compose exec api alembic history # Apply all pending migrations make migrate # or: docker compose exec api alembic upgrade head # Generate new migration after model changes docker compose exec api alembic revision --autogenerate -m "description" # Rollback last migration docker compose exec api alembic downgrade -1 # View current revision docker compose exec api alembic current ```
Always review auto-generated migrations before applying. SQLAlchemy may miss rename operations (interpreting them as drop + create) or produce incorrect defaults.
### 9.4 Direct Database Access ```bash # Interactive psql make shell-db # Common queries SELECT COUNT(*) FROM usermodel; SELECT COUNT(*) FROM presentationmodel WHERE deleted_at IS NULL; SELECT status, COUNT(*) FROM jobmodel GROUP BY status; SELECT provider, SUM(total_tokens) FROM aiusagemodel GROUP BY provider; ``` --- ## 10. Background Jobs & Workers ### 10.1 ARQ Worker Configuration ```mermaid flowchart LR RD[(Redis)] --> W[ARQ Worker] W --> A[generate_presentation] W --> B[parse_master_deck] W --> C[Cron: cleanup 2AM / purge Mon 3AM] ``` | Setting | Value | Description | |---------|-------|-------------| | `max_jobs` | 5 | Maximum concurrent background jobs | | `job_timeout` | 1800s (30 min) | Per-job timeout | | `max_tries` | 3 | Retry attempts on failure | | `health_check_interval` | 30s | Health check frequency | ### 10.2 Job Types #### Presentation Generation (`generate_presentation_task`) 1. Load request from PresentationModel 2. Fetch brand context (colors, fonts, voice rules) 3. Generate outlines via LLM 4. Generate per-slide structure and content 5. Run image generation for each slide 6. Save results to database 7. Update JobModel progress (0–100%) #### Master Deck Parsing (`parse_master_deck_task`) 1. Extract XML layouts/slides from PPTX 2. Convert PPTX to PDF via LibreOffice 3. Split PDF into per-page screenshots 4. Send each screenshot + XML to LLM vision 5. Store generated React TSX code 6. Register as template #### Retention Cleanup (Cron — Daily) - Soft-deletes presentations exceeding client's `retention_days` - Runs at 2:00 AM UTC #### Retention Purge (Cron — Weekly) - Permanently deletes files for presentations soft-deleted 30+ days ago - Runs Monday 3:00 AM UTC ### 10.3 Monitoring Jobs ```bash # View worker logs docker compose logs -f worker # Check job status in database make shell-db # Then: SELECT id, job_type, status, progress, error_message FROM jobmodel ORDER BY created_at DESC LIMIT 20; ``` ### 10.4 Common Job Issues | Issue | Cause | Solution | |-------|-------|----------| | Job stuck at 0% | Worker crashed or no workers running | Restart worker: `docker compose restart worker` | | Job times out | LLM response too slow | Increase `job_timeout` in WorkerSettings | | Job fails repeatedly | Invalid API key or model | Check Settings page, test connection | | Queue backed up | Too many concurrent requests | Scale workers horizontally | --- ## 11. Nginx & Networking ### 11.1 Routing Rules ```mermaid flowchart LR A[Request :80] --> B{Path} B -->|/api/v1/*| C[FastAPI :8000] B -->|/app_data/*| D[Static files] B -->|/static/*| D B -->|/* catch-all| E[Next.js :3000] ``` ### 11.2 Key Configuration | Setting | Value | Purpose | |---------|-------|---------| | `client_max_body_size` | 100M | Allow large PPTX uploads | | `proxy_read_timeout` | 30m | Long-running LLM operations | | `proxy_connect_timeout` | 30m | Connection establishment | | `proxy_buffering` | off | SSE streaming support | | `chunked_transfer_encoding` | off | SSE streaming support | ### 11.3 SSL/TLS (Production) The default `nginx.conf` serves HTTP only. For production, add SSL: ```nginx server { listen 443 ssl http2; ssl_certificate /etc/ssl/certs/your-cert.pem; ssl_certificate_key /etc/ssl/private/your-key.pem; # ... existing location blocks ... } server { listen 80; return 301 https://$host$request_uri; } ``` ### 11.4 Inter-Service Communication ```mermaid flowchart LR API["api :8000"] -->|DATABASE_URL| PG["postgres :5432"] API -->|REDIS_URL| RD["redis :6379"] API -->|NEXT_INTERNAL_URL| WEB["web :3000"] WORKER["worker"] -->|DATABASE_URL| PG WORKER -->|REDIS_URL| RD WEB -->|API_INTERNAL_URL| API NG["nginx :80"] --> API NG --> WEB ``` All services communicate via Docker Compose's internal network. No ports need to be exposed to the host except: - **80** (nginx) — user access - **5432** (postgres) — optional, for direct DB access - **6379** (redis) — optional, for debugging --- ## 12. Storage & File Management ### 12.1 Directory Structure ``` /app_data/ ├── images/ # AI-generated images (UUID-named PNG files) ├── exports/ # Generated PPTX/PDF export files ├── uploads/ # User-uploaded documents (DOCX, PDF, TXT) ├── fonts/ # Custom font files └── master_decks/ # Master deck PPTX files and screenshots └── {deck_id}/ ├── original.pptx ├── screenshots/ │ ├── page_1.png │ ├── page_2.png │ └── ... └── pdf/ └── deck.pdf ``` ### 12.2 File Serving | Context | Serving Method | |---------|---------------| | **Docker (production)** | nginx serves `/app_data/` directly from volume | | **Local development** | FastAPI `StaticFiles` mount on `/app_data` | | **Frontend access** | Next.js rewrites `/app_data/*` to backend | ### 12.3 Retention Policy ```mermaid flowchart LR A[Presentation Created] -->|retention_days exceeded| B[Soft Delete
deleted_at = now] B -->|30 days later| C[Hard Purge
Files removed from disk] ``` - **Retention days** configured per client in `ClientModel.retention_days` - **Soft delete** runs daily at 2:00 AM UTC (sets `deleted_at` timestamp) - **Hard purge** runs weekly Monday at 3:00 AM UTC (removes files for items soft-deleted 30+ days ago) - **Manual purge** available via Admin > Storage > "Purge Files" button ### 12.4 Disk Space Monitoring Monitor the `app_data` volume: ```bash # Check volume usage docker compose exec api du -sh /app_data/* # Check available disk space docker compose exec api df -h /app_data ``` --- ## 13. Monitoring & Logging ### 13.1 Log Access ```bash # All services make logs # Specific service docker compose logs -f api docker compose logs -f worker docker compose logs -f postgres docker compose logs -f web # Last N lines docker compose logs --tail=100 api ``` ### 13.2 Audit Trail The `AuditMiddleware` automatically logs all mutating API requests: | Field | Content | |-------|---------| | `user_id` | Authenticated user's UUID | | `action` | Operation name (e.g., "admin_delete") | | `resource_type` | Entity type (e.g., "presentation") | | `resource_id` | Entity UUID | | `client_id` | Tenant context | | `details` | JSON with request/response metadata | | `ip_address` | Client IP address | | `created_at` | Timestamp (indexed for fast queries) | Audit logging is fire-and-forget (non-blocking) via `asyncio.create_task()`. ### 13.3 AI Usage Tracking The `AIUsageModel` tracks all LLM API calls: | Metric | Description | |--------|-------------| | Provider | anthropic, openai, google, ollama | | Model | Specific model ID | | Call Type | outline, content, vision, etc. | | Input Tokens | Tokens sent to the model | | Output Tokens | Tokens received | | Duration (ms) | Call latency | | Error Details | Error message if failed | View aggregated metrics at **Admin > Analytics > AI Usage**. ### 13.4 Health Checks | Service | Method | Interval | Timeout | |---------|--------|----------|---------| | PostgreSQL | `pg_isready` | 5s | 5s, 5 retries | | Redis | `redis-cli ping` | 5s | 5s, 5 retries | | API | Lifespan startup | At boot | — | | Worker | ARQ health check | 30s | — | --- ## 14. Backup & Recovery ### 14.1 Backup Components | Component | Location | Strategy | |-----------|----------|----------| | Database | `postgres_data` volume | `pg_dump` to file | | Redis | `redis_data` volume | Optional (transient queue data) | | Files | `app_data` volume | File-level backup or snapshot | | Settings | Database (KeyValueSqlModel) | Included in pg_dump | | Migrations | `backend/migrations/` | In git repository | ### 14.2 Database Backup ```bash # Full database dump docker compose exec postgres pg_dump -U deckforge deckforge > backup_$(date +%Y%m%d).sql # Compressed backup docker compose exec postgres pg_dump -U deckforge deckforge | gzip > backup_$(date +%Y%m%d).sql.gz # Restore from backup docker compose exec -T postgres psql -U deckforge deckforge < backup.sql ``` ### 14.3 File Backup ```bash # Backup app_data volume docker run --rm -v ppt-tool_app_data:/data -v $(pwd):/backup alpine \ tar czf /backup/app_data_$(date +%Y%m%d).tar.gz -C /data . # Restore app_data docker run --rm -v ppt-tool_app_data:/data -v $(pwd):/backup alpine \ tar xzf /backup/app_data_YYYYMMDD.tar.gz -C /data ``` ### 14.4 Disaster Recovery ```mermaid flowchart LR A[Deploy Stack] --> B[Restore DB] --> C[alembic upgrade] --> D[Restore app_data] --> E[Start Services] --> F[Verify] ``` 1. Deploy fresh Docker Compose stack 2. Start PostgreSQL and Redis first 3. Restore database from latest `pg_dump` 4. Run `alembic upgrade head` to ensure schema is current 5. Restore `app_data` volume from backup 6. Start remaining services 7. Verify data integrity via Admin Panel --- ## 15. Security Hardening ### 15.1 Production Checklist
Before deploying to production, complete every item:
| Item | Action | Priority | |------|--------|----------| | JWT Secret | Change `JWT_SECRET_KEY` to a random 256-bit key | Critical | | Dev Auth | Set `AZURE_AD_TENANT_ID` to disable dev bypass | Critical | | CORS | Restrict `allow_origins` from `*` to your domain | High | | SSL/TLS | Configure nginx with SSL certificates | High | | Database Password | Use a strong `POSTGRES_PASSWORD` | High | | API Keys | Set `SETTINGS_ENCRYPTION_KEY` for at-rest encryption | High | | Port Exposure | Remove host port mappings for postgres/redis | Medium | | Secrets | Move `.env` to Docker secrets or vault | Medium | | Rate Limiting | Add nginx rate limiting rules | Medium | | Monitoring | Set up external monitoring and alerting | Medium | ### 15.2 CORS Configuration The default CORS configuration allows all origins. For production, modify `backend/api/main.py`: ```python origins = [ "https://your-domain.com", "https://app.your-domain.com", ] ``` ### 15.3 API Key Encryption Enable at-rest encryption for stored API keys: ```bash # Generate a Fernet key python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" # Add to .env SETTINGS_ENCRYPTION_KEY=your-generated-fernet-key ``` ### 15.4 Middleware Execution Order ```mermaid flowchart LR A[Request] --> B[1.CORS] --> C[2.Auth] --> D[3.Audit] --> E[4.UserConfig] --> F[Handler] ``` Middlewares are added in reverse order in FastAPI (last added = first executed). --- ## 16. Scaling & Performance ### 16.1 Horizontal Scaling ```mermaid flowchart LR LB[Load Balancer] --> API[API x N] API --> PG[(Managed PostgreSQL)] API --> RD[(Managed Redis)] W[Workers x N] --> PG W --> RD API --> S3[S3 / CDN] W --> S3 ``` | Component | Scaling Strategy | |-----------|-----------------| | **API** | Run multiple replicas behind load balancer | | **Worker** | Run multiple instances (ARQ handles job locking) | | **PostgreSQL** | Use managed service (RDS, Cloud SQL) with read replicas | | **Redis** | Use managed service (ElastiCache) with clustering | | **Files** | Replace local volume with S3 + CDN | | **nginx** | Replace with cloud load balancer (ALB, Cloud Load Balancing) | ### 16.2 Vertical Scaling | Setting | Location | Effect | |---------|----------|--------| | `max_jobs` | `WorkerSettings` | More concurrent background jobs | | `job_timeout` | `WorkerSettings` | Allow longer-running LLM operations | | `worker_connections` | `nginx.conf` | More concurrent connections | | `client_max_body_size` | `nginx.conf` | Larger file uploads | ### 16.3 Performance Optimization - **Database indexes** already exist on: - `auditlogmodel.created_at` - `teammembershipmodel(user_id, team_id)` (unique) - `usermodel.email` (unique) - `clientmodel.slug` (unique) - **Async operations**: All database queries use `asyncpg` (async PostgreSQL driver) - **Sync LLM calls**: Wrapped in `asyncio.to_thread()` to avoid blocking the event loop - **SSE streaming**: Server-Sent Events for real-time progress (no polling overhead) - **Fire-and-forget audit**: Audit logs don't block request processing --- ## 17. Troubleshooting ### 17.1 Common Issues | Issue | Cause | Solution | |-------|-------|----------| | API won't start | PostgreSQL not ready | Wait for health check (15–25s) | | Worker jobs not processing | Redis down or worker crashed | `docker compose restart worker` | | Auth failures after restart | JWT_SECRET_KEY changed | All users must re-login | | File uploads fail | Disk full or wrong permissions | Check `du -sh /app_data/*` and permissions | | PPTX export 500 | Puppeteer / Chromium issue | Restart web service, check memory | | Slide edit timeout | LLM response too slow | Check provider status, increase timeouts | | Master deck stuck "processing" | Worker died during parse | Restart worker, reparse the deck | | Images not showing | Static files not served | Check FastAPI mounts and nginx config | | SSE not working | Proxy buffering enabled | Ensure nginx `proxy_buffering off` | ### 17.2 Diagnostic Commands ```bash # Service health docker compose ps # Container resource usage docker stats # API application logs docker compose logs --tail=50 api # Worker job processing logs docker compose logs --tail=50 worker # Database connection check docker compose exec postgres pg_isready -U deckforge # Redis connectivity docker compose exec redis redis-cli ping # Database query — check failed jobs docker compose exec postgres psql -U deckforge -c \ "SELECT id, job_type, status, error_message FROM jobmodel WHERE status='failed' ORDER BY created_at DESC LIMIT 10;" # Disk usage docker compose exec api du -sh /app_data/* ``` ### 17.3 Resetting the System ```bash # Full reset (WARNING: destroys all data) docker compose down -v docker compose up --build -d make migrate make seed ``` --- ## 18. API Reference ### 18.1 Authentication Endpoints | Method | Path | Description | |--------|------|-------------| | GET | `/api/v1/auth/login` | Redirect to Azure AD login | | GET | `/api/v1/auth/callback` | OAuth callback handler | | POST | `/api/v1/auth/dev-login` | Dev mode authentication | | GET | `/api/v1/auth/dev-status` | Check if dev mode is enabled | | POST | `/api/v1/auth/logout` | Clear session | | GET | `/api/v1/auth/me` | Current user info | ### 18.2 Admin Endpoints | Method | Path | Access | |--------|------|--------| | GET/PUT/DELETE | `/api/v1/admin/users/*` | Super Admin | | POST/GET/PUT/DELETE | `/api/v1/admin/clients/*` | Admin | | POST/GET/DELETE | `/api/v1/admin/teams/*` | Admin | | GET/PUT | `/api/v1/admin/settings` | Super Admin | | GET/POST | `/api/v1/admin/settings/models` | Super Admin | | POST | `/api/v1/admin/settings/test-connection` | Super Admin | | GET/DELETE/POST | `/api/v1/admin/storage/*` | Admin | | GET | `/api/v1/admin/analytics/*` | Admin | | GET | `/api/v1/admin/audit-log` | Admin | | GET/PUT/POST/DELETE | `/api/v1/admin/master-decks/*` | Admin | | GET/PUT/POST/DELETE | `/api/v1/admin/brand-config/*` | Admin | ### 18.3 Presentation Endpoints | Method | Path | Description | |--------|------|-------------| | POST | `/api/v1/ppt/presentation/create` | Create new presentation | | GET | `/api/v1/ppt/presentation/all` | List presentations | | GET | `/api/v1/ppt/presentation/{id}` | Get presentation detail | | PUT | `/api/v1/ppt/presentation/{id}` | Update presentation | | DELETE | `/api/v1/ppt/presentation/{id}` | Delete presentation | | POST | `/api/v1/ppt/presentation/decompose` | Decompose content | | POST | `/api/v1/ppt/presentation/prepare` | Prepare for generation | | GET | `/api/v1/ppt/presentation/{id}/review` | Get review status | | PUT | `/api/v1/ppt/presentation/{id}/status` | Change review status | | POST | `/api/v1/ppt/presentation/{id}/comment` | Add review comment | | POST | `/api/v1/ppt/jobs/generate` | Start generation job | | GET | `/api/v1/ppt/jobs/{id}/status` | Job status (SSE) | | POST | `/api/v1/ppt/export/pptx` | Export as PPTX | | POST | `/api/v1/ppt/export/pdf` | Export as PDF | ---
**Oliver DeckForge** | System Administration Guide Version 1.0 | © 2026 All Rights Reserved