PDF documents with Mermaid diagrams, styled with purple theme. Includes build script for regenerating PDFs from Markdown sources. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
41 KiB
| pdf_options | stylesheet | body_class | css | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
markdown-body | 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
- Architecture Overview
- Installation & Deployment
- Environment Variables Reference
- Authentication Configuration
- Role-Based Access Control
- Admin Panel Operations
- Template Pipeline
- AI Provider Configuration
- Database Administration
- Background Jobs & Workers
- Nginx & Networking
- Storage & File Management
- Monitoring & Logging
- Backup & Recovery
- Security Hardening
- Scaling & Performance
- Troubleshooting
- API Reference
1. Architecture Overview
1.1 System Architecture
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
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
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
# 1. Clone repository
git clone <repository-url>
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
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
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+uvpackage 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 |
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
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)
- Register an application in Azure Portal > Azure AD > App Registrations
- Set the Redirect URI to:
https://your-domain.com/api/v1/auth/callback - Create a client secret under Certificates & Secrets
- Configure environment variables:
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
- 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_PASSWORDenvironment 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
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
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
- Find the user in the list
- Click the role dropdown
- Select new role:
super_admin,client_admin, oruser
You cannot change your own role. This prevents accidental lockout.
Deactivating Users
- Click Deactivate on the user row
- Confirm the action
- User's
is_activeis set to false — they can no longer log in - Their presentations remain in the system
Transferring Ownership
Before deactivating a user (e.g., for GDPR compliance):
- Use the transfer ownership endpoint to move all presentations to another user
- Then deactivate the original user
6.2 Client Management
Path: Admin > Clients
Creating a Client
- Click "+ New Client"
- Enter the client name
- A URL-safe slug is auto-generated
- 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
- Navigate to the client's team page
- Click "+ Add Member"
- Search and select a user from the dropdown
- 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
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
- Navigate to Admin > Clients > [Client] > Master Decks
- Click "Upload PPTX" and select a
.pptxfile - The deck enters "pending" status, then "processing"
- Auto-polling every 5 seconds shows current status
- 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):
- Click the reparse dropdown on the deck card
- Choose "Reparse (slides)" or "Reparse (layouts)"
- All existing layouts are replaced with freshly parsed versions
8. AI Provider Configuration
8.1 Settings Page
Path: Admin > Settings (Super Admin only)
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
- Open Admin > Settings
- Select the LLM Provider from the dropdown
- The Model dropdown auto-populates with available models
- Enter the API Key if not already set (shown as "Set" badge if configured)
- Click "Test" to verify connectivity
- Click "Save Changes"
8.3 Configuring Image Provider
- Select the Image Provider from the dropdown
- Ensure the required API key is set (e.g., Google API key for NanoBanana Pro)
- 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_KEYis set) - Environment variables serve as defaults — database values override them
9. Database Administration
9.1 Schema Overview
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
# 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
# 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
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)
- Load request from PresentationModel
- Fetch brand context (colors, fonts, voice rules)
- Generate outlines via LLM
- Generate per-slide structure and content
- Run image generation for each slide
- Save results to database
- Update JobModel progress (0–100%)
Master Deck Parsing (parse_master_deck_task)
- Extract XML layouts/slides from PPTX
- Convert PPTX to PDF via LibreOffice
- Split PDF into per-page screenshots
- Send each screenshot + XML to LLM vision
- Store generated React TSX code
- 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
# 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
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:
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
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
flowchart LR
A[Presentation Created] -->|retention_days exceeded| B[Soft Delete<br/>deleted_at = now]
B -->|30 days later| C[Hard Purge<br/>Files removed from disk]
- Retention days configured per client in
ClientModel.retention_days - Soft delete runs daily at 2:00 AM UTC (sets
deleted_attimestamp) - 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:
# 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
# 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
# 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
# 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
flowchart LR
A[Deploy Stack] --> B[Restore DB] --> C[alembic upgrade] --> D[Restore app_data] --> E[Start Services] --> F[Verify]
- Deploy fresh Docker Compose stack
- Start PostgreSQL and Redis first
- Restore database from latest
pg_dump - Run
alembic upgrade headto ensure schema is current - Restore
app_datavolume from backup - Start remaining services
- 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:
origins = [
"https://your-domain.com",
"https://app.your-domain.com",
]
15.3 API Key Encryption
Enable at-rest encryption for stored API keys:
# 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
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
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_atteammembershipmodel(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
# 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
# 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