Refactor Nginx configuration and update project dependencies
- Cleaned up the Nginx configuration for the Coolify deployment, ensuring proper formatting and organization. - Updated the `pyproject.toml` file to maintain consistent formatting. - Reformatted the `requirements.txt` file for better readability while keeping the dependencies unchanged.
This commit is contained in:
1 parent
3b8c30c289
commit
a4b874598d
88 files changed
+9766
-9766
No files matched your search
@@ -1,34 +1,34 @@
|
||||
# 1. Introduction & Goals
|
||||
|
||||
Describes the relevant requirements and the driving forces that software architects and the development team must consider. These include underlying business goals, essential features, essential functional requirements, quality goals for the architecture, and relevant stakeholders and their expectations.
|
||||
|
||||
## Requirements Overview
|
||||
|
||||
**Project name**: All You Can GET AI
|
||||
**URL**: [https://ai.allucanget.biz](https://ai.allucanget.biz)
|
||||
**Purpose**: Provide AI‑powered text, image, and video generation services via a web application.
|
||||
|
||||
Users can choose between different AI models for:
|
||||
|
||||
- Text generation
|
||||
- Text‑to‑image generation
|
||||
- Text‑to‑video generation
|
||||
- Image‑to‑video generation
|
||||
|
||||
Users can create accounts, log in, and view their generation history in a gallery. An admin dashboard allows managing users, models, and video generation jobs.
|
||||
|
||||
## Quality Goals
|
||||
|
||||
| Priority | Quality Goal | Scenario |
|
||||
| -------- | ----------------- | --------------------------------------------- |
|
||||
| 1 | High availability | Service is accessible 99.9% of the time |
|
||||
| 2 | Low latency | Generation endpoints respond within 200 ms |
|
||||
| 3 | Data privacy | User data is encrypted at rest and in transit |
|
||||
|
||||
## Stakeholders
|
||||
|
||||
| Role/Name | Contact | Expectations |
|
||||
| ------------- | ---------- | --------------------------------------------- |
|
||||
| Developer | Team | Clean APIs, testable code, good documentation |
|
||||
| End User | Public | Fast, reliable AI generation features |
|
||||
| Product Owner | Management | Feature completeness, uptime, cost control |
|
||||
# 1. Introduction & Goals
|
||||
|
||||
Describes the relevant requirements and the driving forces that software architects and the development team must consider. These include underlying business goals, essential features, essential functional requirements, quality goals for the architecture, and relevant stakeholders and their expectations.
|
||||
|
||||
## Requirements Overview
|
||||
|
||||
**Project name**: All You Can GET AI
|
||||
**URL**: [https://ai.allucanget.biz](https://ai.allucanget.biz)
|
||||
**Purpose**: Provide AI‑powered text, image, and video generation services via a web application.
|
||||
|
||||
Users can choose between different AI models for:
|
||||
|
||||
- Text generation
|
||||
- Text‑to‑image generation
|
||||
- Text‑to‑video generation
|
||||
- Image‑to‑video generation
|
||||
|
||||
Users can create accounts, log in, and view their generation history in a gallery. An admin dashboard allows managing users, models, and video generation jobs.
|
||||
|
||||
## Quality Goals
|
||||
|
||||
| Priority | Quality Goal | Scenario |
|
||||
| -------- | ----------------- | --------------------------------------------- |
|
||||
| 1 | High availability | Service is accessible 99.9% of the time |
|
||||
| 2 | Low latency | Generation endpoints respond within 200 ms |
|
||||
| 3 | Data privacy | User data is encrypted at rest and in transit |
|
||||
|
||||
## Stakeholders
|
||||
|
||||
| Role/Name | Contact | Expectations |
|
||||
| ------------- | ---------- | --------------------------------------------- |
|
||||
| Developer | Team | Clean APIs, testable code, good documentation |
|
||||
| End User | Public | Fast, reliable AI generation features |
|
||||
| Product Owner | Management | Feature completeness, uptime, cost control |
|
||||
+26
-26
@@ -1,26 +1,26 @@
|
||||
# 2. Architecture Constraints
|
||||
|
||||
Any requirement that constrains software architects in their freedom of design and implementation decisions or decision about the development process. These constraints sometimes go beyond individual systems and are valid for whole organizations and companies.
|
||||
|
||||
## Technical Constraints
|
||||
|
||||
| Constraint | Background / Motivation |
|
||||
| ----------------------------- | ------------------------------------------- |
|
||||
| Must run on Windows and Linux | Cross-platform developer environments |
|
||||
| FastAPI for backend | Async performance, OpenAPI support |
|
||||
| Flask for frontend | Lightweight UI serving |
|
||||
| DuckDB as embedded database | Lightweight, no separate DB server required |
|
||||
| External AI via openrouter.ai | Unified access to multiple AI models |
|
||||
|
||||
## Organizational Constraints
|
||||
|
||||
| Constraint | Background / Motivation |
|
||||
| ----------------- | --------------------------------- |
|
||||
| Open source stack | Cost reduction, community support |
|
||||
|
||||
## Conventions
|
||||
|
||||
| Convention | Background / Motivation |
|
||||
| -------------------- | --------------------------------------------------- |
|
||||
| Python 3.12+ | Modern language features, type hints |
|
||||
| pytest for all tests | Consistent test tooling across backend and frontend |
|
||||
# 2. Architecture Constraints
|
||||
|
||||
Any requirement that constrains software architects in their freedom of design and implementation decisions or decision about the development process. These constraints sometimes go beyond individual systems and are valid for whole organizations and companies.
|
||||
|
||||
## Technical Constraints
|
||||
|
||||
| Constraint | Background / Motivation |
|
||||
| ----------------------------- | ------------------------------------------- |
|
||||
| Must run on Windows and Linux | Cross-platform developer environments |
|
||||
| FastAPI for backend | Async performance, OpenAPI support |
|
||||
| Flask for frontend | Lightweight UI serving |
|
||||
| DuckDB as embedded database | Lightweight, no separate DB server required |
|
||||
| External AI via openrouter.ai | Unified access to multiple AI models |
|
||||
|
||||
## Organizational Constraints
|
||||
|
||||
| Constraint | Background / Motivation |
|
||||
| ----------------- | --------------------------------- |
|
||||
| Open source stack | Cost reduction, community support |
|
||||
|
||||
## Conventions
|
||||
|
||||
| Convention | Background / Motivation |
|
||||
| -------------------- | --------------------------------------------------- |
|
||||
| Python 3.12+ | Modern language features, type hints |
|
||||
| pytest for all tests | Consistent test tooling across backend and frontend |
|
||||
@@ -1,26 +1,26 @@
|
||||
# 3. Context and Scope
|
||||
|
||||
Delimits your system (i.e. your scope) from all its communication partners (neighboring systems and users, i.e. the context of your system). It thereby specifies the external interfaces.
|
||||
|
||||
## Business Context
|
||||
|
||||
Specification of **all** communication partners (users, IT-systems, …) with explanations of domain specific inputs and outputs or interfaces.
|
||||
|
||||
The system exposes REST APIs for authentication, user management, and AI generation. The Flask frontend consumes these APIs and serves pages at `http://ai.allucanget.biz`.
|
||||
|
||||
| Communication Partner | Input | Output |
|
||||
| --------------------- | ------------------------------ | ------------------------------ |
|
||||
| End User (browser) | Form submissions, API requests | Generated text, images, videos |
|
||||
| openrouter.ai API | Prompt + model selection | AI-generated content |
|
||||
| DuckDB | SQL queries | User data, session data |
|
||||
|
||||
## Technical Context
|
||||
|
||||
| Channel | Protocol | Description |
|
||||
| -------------------------------- | ---------- | ------------------ |
|
||||
| User ↔ Flask frontend | HTTPS | Browser-based UI |
|
||||
| Flask frontend ↔ FastAPI backend | HTTP REST | Internal API calls |
|
||||
| FastAPI backend ↔ openrouter.ai | HTTPS REST | AI model inference |
|
||||
| FastAPI backend ↔ DuckDB | In-process | Embedded DB access |
|
||||
|
||||
**Mapping Input/Output to Channels:** All user-facing traffic flows through HTTPS. The backend communicates with openrouter.ai over HTTPS. DuckDB is accessed in-process with no network channel.
|
||||
# 3. Context and Scope
|
||||
|
||||
Delimits your system (i.e. your scope) from all its communication partners (neighboring systems and users, i.e. the context of your system). It thereby specifies the external interfaces.
|
||||
|
||||
## Business Context
|
||||
|
||||
Specification of **all** communication partners (users, IT-systems, …) with explanations of domain specific inputs and outputs or interfaces.
|
||||
|
||||
The system exposes REST APIs for authentication, user management, and AI generation. The Flask frontend consumes these APIs and serves pages at `http://ai.allucanget.biz`.
|
||||
|
||||
| Communication Partner | Input | Output |
|
||||
| --------------------- | ------------------------------ | ------------------------------ |
|
||||
| End User (browser) | Form submissions, API requests | Generated text, images, videos |
|
||||
| openrouter.ai API | Prompt + model selection | AI-generated content |
|
||||
| DuckDB | SQL queries | User data, session data |
|
||||
|
||||
## Technical Context
|
||||
|
||||
| Channel | Protocol | Description |
|
||||
| -------------------------------- | ---------- | ------------------ |
|
||||
| User ↔ Flask frontend | HTTPS | Browser-based UI |
|
||||
| Flask frontend ↔ FastAPI backend | HTTP REST | Internal API calls |
|
||||
| FastAPI backend ↔ openrouter.ai | HTTPS REST | AI model inference |
|
||||
| FastAPI backend ↔ DuckDB | In-process | Embedded DB access |
|
||||
|
||||
**Mapping Input/Output to Channels:** All user-facing traffic flows through HTTPS. The backend communicates with openrouter.ai over HTTPS. DuckDB is accessed in-process with no network channel.
|
||||
@@ -1,23 +1,23 @@
|
||||
# 4. Solution Strategy
|
||||
|
||||
A short summary and explanation of the fundamental decisions and solution strategies that shape system architecture. It includes:
|
||||
|
||||
- Technology decisions
|
||||
- Decisions about the top-level decomposition of the system, e.g. usage of an architectural pattern or design pattern
|
||||
- Decisions on how to achieve key quality goals
|
||||
- Relevant organizational decisions, e.g. selecting a development process or delegating certain tasks to third parties
|
||||
|
||||
Micro‑service style: a single FastAPI process handles all API endpoints; a separate Flask process serves the UI.
|
||||
|
||||
| Quality Goal | Scenario | Solution Approach |
|
||||
| ----------------- | ------------------------ | --------------------------------------------------------- |
|
||||
| High availability | Service must be up 99.9% | Docker containers with health checks and restart policies |
|
||||
| Low latency | Responses < 200 ms | FastAPI async endpoints; DuckDB in-process |
|
||||
| Data privacy | Encrypted data | JWT auth, HTTPS everywhere, DuckDB file encryption |
|
||||
|
||||
**Key technology decisions:**
|
||||
|
||||
- **FastAPI** for async performance and automatic OpenAPI docs
|
||||
- **DuckDB** for a lightweight embedded DB with no separate server
|
||||
- **Flask** for a thin frontend layer with Jinja2 templates
|
||||
- **openrouter.ai** for unified access to multiple AI model providers
|
||||
# 4. Solution Strategy
|
||||
|
||||
A short summary and explanation of the fundamental decisions and solution strategies that shape system architecture. It includes:
|
||||
|
||||
- Technology decisions
|
||||
- Decisions about the top-level decomposition of the system, e.g. usage of an architectural pattern or design pattern
|
||||
- Decisions on how to achieve key quality goals
|
||||
- Relevant organizational decisions, e.g. selecting a development process or delegating certain tasks to third parties
|
||||
|
||||
Micro‑service style: a single FastAPI process handles all API endpoints; a separate Flask process serves the UI.
|
||||
|
||||
| Quality Goal | Scenario | Solution Approach |
|
||||
| ----------------- | ------------------------ | --------------------------------------------------------- |
|
||||
| High availability | Service must be up 99.9% | Docker containers with health checks and restart policies |
|
||||
| Low latency | Responses < 200 ms | FastAPI async endpoints; DuckDB in-process |
|
||||
| Data privacy | Encrypted data | JWT auth, HTTPS everywhere, DuckDB file encryption |
|
||||
|
||||
**Key technology decisions:**
|
||||
|
||||
- **FastAPI** for async performance and automatic OpenAPI docs
|
||||
- **DuckDB** for a lightweight embedded DB with no separate server
|
||||
- **Flask** for a thin frontend layer with Jinja2 templates
|
||||
- **openrouter.ai** for unified access to multiple AI model providers
|
||||
+110
-110
@@ -1,110 +1,110 @@
|
||||
# 5. Building Block View
|
||||
|
||||
Static decomposition of the system into building blocks (modules, components, subsystems, classes, interfaces, packages, libraries, frameworks, layers, partitions, tiers, functions, macros, operations, data structures, …) as well as their dependencies (relationships, associations, …).
|
||||
|
||||
## Level 1 – Whitebox Overall System
|
||||
|
||||
```text
|
||||
┌────────────────────────┐
|
||||
│ Frontend (Flask) │
|
||||
└───────┬────────────────┘
|
||||
│ REST API calls
|
||||
┌───────▼────────────────┐
|
||||
│ FastAPI Backend │
|
||||
│ ├─ Auth Service │
|
||||
│ ├─ User Service │
|
||||
│ ├─ AI Service │
|
||||
│ └─ DB Service (DuckDB)│
|
||||
└───────┬────────────────┘
|
||||
│ DB access
|
||||
┌───────▼────────────────┐
|
||||
│ DuckDB Database │
|
||||
└────────────────────────┘
|
||||
```
|
||||
|
||||
**Motivation:** Separating the UI (Flask) from the API (FastAPI) allows independent scaling and testing of each layer.
|
||||
|
||||
**Contained Building Blocks:**
|
||||
|
||||
| Name | Responsibility |
|
||||
| ---------------- | -------------------------------------------------- |
|
||||
| Frontend (Flask) | Serves HTML/CSS/JS UI, proxies requests to FastAPI |
|
||||
| FastAPI Backend | Handles auth, user management, AI generation API |
|
||||
| Auth Service | JWT issuance and validation |
|
||||
| User Service | CRUD operations for user accounts |
|
||||
| AI Service | Sends prompts to openrouter.ai, returns results |
|
||||
| DB Service | Reads/writes to DuckDB |
|
||||
| DuckDB Database | Persistent storage for users and session data |
|
||||
|
||||
## Level 2 – FastAPI Backend internals
|
||||
|
||||
### White Box Auth Service (`/auth`)
|
||||
|
||||
Handles JWT issuance, validation, and refresh token lifecycle.
|
||||
|
||||
| Method | Path | Auth required | Description |
|
||||
| ------ | ---------------- | :-----------: | ------------------------------------------------ |
|
||||
| POST | `/auth/register` | — | Create a new user account |
|
||||
| POST | `/auth/login` | — | Authenticate and receive access + refresh tokens |
|
||||
| POST | `/auth/refresh` | — | Rotate refresh token, get a new access token |
|
||||
| POST | `/auth/logout` | — | Revoke refresh token |
|
||||
|
||||
### White Box User Service (`/users`)
|
||||
|
||||
Self-service profile management and admin user CRUD.
|
||||
|
||||
| Method | Path | Auth required | Admin only | Description |
|
||||
| ------ | ------------------ | ------------- | ---------- | ---------------------------------------- |
|
||||
| GET | `/users/me` | ✓ | — | Get current user profile |
|
||||
| PUT | `/users/me` | ✓ | — | Update own email or password |
|
||||
| GET | `/users` | ✓ | ✓ | List all users |
|
||||
| DELETE | `/users/{id}` | ✓ | ✓ | Delete a user (cannot self-delete) |
|
||||
| PUT | `/users/{id}/role` | ✓ | ✓ | Change a user's role (`user` \| `admin`) |
|
||||
|
||||
### White Box Admin Service (`/admin`)
|
||||
|
||||
Operational endpoints for application management.
|
||||
|
||||
| Method | Path | Auth required | Admin only | Description |
|
||||
| ------ | --------------------------- | ------------- | ---------- | ------------------------------------------ |
|
||||
| GET | `/admin/stats` | ✓ | ✓ | User counts by role, token activity |
|
||||
| GET | `/admin/health/db` | ✓ | ✓ | DuckDB connectivity check |
|
||||
| POST | `/admin/tokens/purge` | ✓ | ✓ | Remove expired/revoked refresh tokens |
|
||||
| GET | `/admin/videos` | ✓ | ✓ | List all video jobs with user emails |
|
||||
| POST | `/admin/videos/{id}/cancel` | ✓ | ✓ | Cancel a queued/processing video job |
|
||||
| POST | `/admin/videos/{id}/retry` | ✓ | ✓ | Retry a failed/cancelled video job |
|
||||
| DELETE | `/admin/videos/{id}` | ✓ | ✓ | Permanently delete a video job |
|
||||
| POST | `/admin/videos/purge` | ✓ | ✓ | Delete old completed/failed/cancelled jobs |
|
||||
| POST | `/admin/videos/timed-out` | ✓ | ✓ | Mark stale processing jobs as failed |
|
||||
| GET | `/admin/models` | ✓ | ✓ | List cached OpenRouter models |
|
||||
| POST | `/admin/models/refresh` | ✓ | ✓ | Refresh model cache from OpenRouter |
|
||||
|
||||
### White Box AI Service (`/ai`, `/generate`)
|
||||
|
||||
Model listing and multi-modal generation via openrouter.ai.
|
||||
|
||||
| Method | Path | Auth required | Description |
|
||||
| ------ | ------------------------------ | ------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| GET | `/ai/models` | ✓ | List available OpenRouter models |
|
||||
| POST | `/ai/chat` | ✓ | Multi-turn chat completion |
|
||||
| POST | `/generate/text` | ✓ | Single-prompt text generation (optional system prompt) |
|
||||
| POST | `/generate/image` | ✓ | Text-to-image (DALL-E via `/images/generations` or FLUX/GPT-5 Image Mini via `/chat/completions` with `modalities`) |
|
||||
| POST | `/generate/video` | ✓ | Text-to-video (Sora 2 Pro, Veo 3.1 Fast) — returns `polling_url` |
|
||||
| POST | `/generate/video/from-image` | ✓ | Image-to-video — returns `polling_url` |
|
||||
| GET | `/generate/video/status` | ✓ | Poll video generation status via `polling_url` |
|
||||
| GET | `/generate/images` | ✓ | List current user's generated images |
|
||||
| GET | `/generate/images/{id}` | ✓ | Get a single generated image |
|
||||
| GET | `/generate/videos` | ✓ | List current user's video jobs |
|
||||
| GET | `/generate/videos/{id}` | ✓ | Get a single video job |
|
||||
| POST | `/generate/videos/{id}/cancel` | ✓ | Cancel a queued/processing video job |
|
||||
|
||||
**Video generation flow:** The `/generate/video` and `/generate/video/from-image` endpoints queue a job in the local database and return immediately with `status: "queued"`. A background worker (`video_worker.py`) submits the job to OpenRouter's `/api/v1/videos` endpoint, receives a `polling_url`, and polls it periodically until the job reaches `"completed"` or `"failed"`. The frontend polls `GET /generate/video/{id}/status` every 5 seconds to show live status updates.
|
||||
|
||||
**Image generation routing:** The router auto-detects the model type — models containing `"flux"` or `"gpt-5-image-mini"` are routed to `/chat/completions` with `modalities: ["image"]` (or `["image", "text"]` depending on cached output modalities), while others (e.g. DALL-E 3) use the legacy `/images/generations` endpoint.
|
||||
|
||||
### White Box DB Service (`db.py`)
|
||||
|
||||
- Singleton DuckDB connection, opened on FastAPI startup via lifespan hook.
|
||||
- `asyncio.Lock` serialises all write operations within the single process.
|
||||
- Schema migration runs automatically on first startup.
|
||||
- Tables: `users`, `refresh_tokens` (see Section 8 for schema details).
|
||||
# 5. Building Block View
|
||||
|
||||
Static decomposition of the system into building blocks (modules, components, subsystems, classes, interfaces, packages, libraries, frameworks, layers, partitions, tiers, functions, macros, operations, data structures, …) as well as their dependencies (relationships, associations, …).
|
||||
|
||||
## Level 1 – Whitebox Overall System
|
||||
|
||||
```text
|
||||
┌────────────────────────┐
|
||||
│ Frontend (Flask) │
|
||||
└───────┬────────────────┘
|
||||
│ REST API calls
|
||||
┌───────▼────────────────┐
|
||||
│ FastAPI Backend │
|
||||
│ ├─ Auth Service │
|
||||
│ ├─ User Service │
|
||||
│ ├─ AI Service │
|
||||
│ └─ DB Service (DuckDB)│
|
||||
└───────┬────────────────┘
|
||||
│ DB access
|
||||
┌───────▼────────────────┐
|
||||
│ DuckDB Database │
|
||||
└────────────────────────┘
|
||||
```
|
||||
|
||||
**Motivation:** Separating the UI (Flask) from the API (FastAPI) allows independent scaling and testing of each layer.
|
||||
|
||||
**Contained Building Blocks:**
|
||||
|
||||
| Name | Responsibility |
|
||||
| ---------------- | -------------------------------------------------- |
|
||||
| Frontend (Flask) | Serves HTML/CSS/JS UI, proxies requests to FastAPI |
|
||||
| FastAPI Backend | Handles auth, user management, AI generation API |
|
||||
| Auth Service | JWT issuance and validation |
|
||||
| User Service | CRUD operations for user accounts |
|
||||
| AI Service | Sends prompts to openrouter.ai, returns results |
|
||||
| DB Service | Reads/writes to DuckDB |
|
||||
| DuckDB Database | Persistent storage for users and session data |
|
||||
|
||||
## Level 2 – FastAPI Backend internals
|
||||
|
||||
### White Box Auth Service (`/auth`)
|
||||
|
||||
Handles JWT issuance, validation, and refresh token lifecycle.
|
||||
|
||||
| Method | Path | Auth required | Description |
|
||||
| ------ | ---------------- | :-----------: | ------------------------------------------------ |
|
||||
| POST | `/auth/register` | — | Create a new user account |
|
||||
| POST | `/auth/login` | — | Authenticate and receive access + refresh tokens |
|
||||
| POST | `/auth/refresh` | — | Rotate refresh token, get a new access token |
|
||||
| POST | `/auth/logout` | — | Revoke refresh token |
|
||||
|
||||
### White Box User Service (`/users`)
|
||||
|
||||
Self-service profile management and admin user CRUD.
|
||||
|
||||
| Method | Path | Auth required | Admin only | Description |
|
||||
| ------ | ------------------ | ------------- | ---------- | ---------------------------------------- |
|
||||
| GET | `/users/me` | ✓ | — | Get current user profile |
|
||||
| PUT | `/users/me` | ✓ | — | Update own email or password |
|
||||
| GET | `/users` | ✓ | ✓ | List all users |
|
||||
| DELETE | `/users/{id}` | ✓ | ✓ | Delete a user (cannot self-delete) |
|
||||
| PUT | `/users/{id}/role` | ✓ | ✓ | Change a user's role (`user` \| `admin`) |
|
||||
|
||||
### White Box Admin Service (`/admin`)
|
||||
|
||||
Operational endpoints for application management.
|
||||
|
||||
| Method | Path | Auth required | Admin only | Description |
|
||||
| ------ | --------------------------- | ------------- | ---------- | ------------------------------------------ |
|
||||
| GET | `/admin/stats` | ✓ | ✓ | User counts by role, token activity |
|
||||
| GET | `/admin/health/db` | ✓ | ✓ | DuckDB connectivity check |
|
||||
| POST | `/admin/tokens/purge` | ✓ | ✓ | Remove expired/revoked refresh tokens |
|
||||
| GET | `/admin/videos` | ✓ | ✓ | List all video jobs with user emails |
|
||||
| POST | `/admin/videos/{id}/cancel` | ✓ | ✓ | Cancel a queued/processing video job |
|
||||
| POST | `/admin/videos/{id}/retry` | ✓ | ✓ | Retry a failed/cancelled video job |
|
||||
| DELETE | `/admin/videos/{id}` | ✓ | ✓ | Permanently delete a video job |
|
||||
| POST | `/admin/videos/purge` | ✓ | ✓ | Delete old completed/failed/cancelled jobs |
|
||||
| POST | `/admin/videos/timed-out` | ✓ | ✓ | Mark stale processing jobs as failed |
|
||||
| GET | `/admin/models` | ✓ | ✓ | List cached OpenRouter models |
|
||||
| POST | `/admin/models/refresh` | ✓ | ✓ | Refresh model cache from OpenRouter |
|
||||
|
||||
### White Box AI Service (`/ai`, `/generate`)
|
||||
|
||||
Model listing and multi-modal generation via openrouter.ai.
|
||||
|
||||
| Method | Path | Auth required | Description |
|
||||
| ------ | ------------------------------ | ------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| GET | `/ai/models` | ✓ | List available OpenRouter models |
|
||||
| POST | `/ai/chat` | ✓ | Multi-turn chat completion |
|
||||
| POST | `/generate/text` | ✓ | Single-prompt text generation (optional system prompt) |
|
||||
| POST | `/generate/image` | ✓ | Text-to-image (DALL-E via `/images/generations` or FLUX/GPT-5 Image Mini via `/chat/completions` with `modalities`) |
|
||||
| POST | `/generate/video` | ✓ | Text-to-video (Sora 2 Pro, Veo 3.1 Fast) — returns `polling_url` |
|
||||
| POST | `/generate/video/from-image` | ✓ | Image-to-video — returns `polling_url` |
|
||||
| GET | `/generate/video/status` | ✓ | Poll video generation status via `polling_url` |
|
||||
| GET | `/generate/images` | ✓ | List current user's generated images |
|
||||
| GET | `/generate/images/{id}` | ✓ | Get a single generated image |
|
||||
| GET | `/generate/videos` | ✓ | List current user's video jobs |
|
||||
| GET | `/generate/videos/{id}` | ✓ | Get a single video job |
|
||||
| POST | `/generate/videos/{id}/cancel` | ✓ | Cancel a queued/processing video job |
|
||||
|
||||
**Video generation flow:** The `/generate/video` and `/generate/video/from-image` endpoints queue a job in the local database and return immediately with `status: "queued"`. A background worker (`video_worker.py`) submits the job to OpenRouter's `/api/v1/videos` endpoint, receives a `polling_url`, and polls it periodically until the job reaches `"completed"` or `"failed"`. The frontend polls `GET /generate/video/{id}/status` every 5 seconds to show live status updates.
|
||||
|
||||
**Image generation routing:** The router auto-detects the model type — models containing `"flux"` or `"gpt-5-image-mini"` are routed to `/chat/completions` with `modalities: ["image"]` (or `["image", "text"]` depending on cached output modalities), while others (e.g. DALL-E 3) use the legacy `/images/generations` endpoint.
|
||||
|
||||
### White Box DB Service (`db.py`)
|
||||
|
||||
- Singleton DuckDB connection, opened on FastAPI startup via lifespan hook.
|
||||
- `asyncio.Lock` serialises all write operations within the single process.
|
||||
- Schema migration runs automatically on first startup.
|
||||
- Tables: `users`, `refresh_tokens` (see Section 8 for schema details).
|
||||
+98
-98
@@ -1,98 +1,98 @@
|
||||
# 6. Runtime View
|
||||
|
||||
Describes concrete behavior and interactions of the system's building blocks in form of scenarios from the following areas:
|
||||
|
||||
- Important use cases or features: how do building blocks execute them?
|
||||
- Interactions at critical external interfaces: how do building blocks cooperate with users and neighboring systems?
|
||||
- Operation and administration: launch, start-up, stop
|
||||
- Error and exception scenarios
|
||||
|
||||
## Scenario 1: User Authentication
|
||||
|
||||
1. User submits login form in Flask frontend
|
||||
2. Flask POSTs credentials to `POST /auth/login`
|
||||
3. Auth Service validates credentials against DuckDB
|
||||
4. Auth Service returns JWT token
|
||||
5. Flask stores token in session cookie
|
||||
6. User is redirected to dashboard
|
||||
|
||||
## Scenario 2: AI Text Generation
|
||||
|
||||
1. User fills in text generation form in Flask frontend
|
||||
2. Flask POSTs prompt + model to `POST /generate/text` with JWT header
|
||||
3. Auth Service validates JWT
|
||||
4. AI Service sends prompt to openrouter.ai
|
||||
5. openrouter.ai returns generated text
|
||||
6. FastAPI returns result to Flask
|
||||
7. Flask renders result page for user
|
||||
|
||||
## Scenario 3: Image Generation
|
||||
|
||||
1. User submits image generation form with prompt, model, size, aspect ratio, and resolution
|
||||
2. Flask POSTs to `POST /generate/image` with JWT header
|
||||
3. Router auto-detects model type:
|
||||
- **FLUX / GPT-5 Image Mini**: calls `/chat/completions` with `modalities: ["image"]` and `image_config`
|
||||
- **DALL-E 3**: calls `/images/generations` with `size` and `n`
|
||||
4. Image URL (base64 data URL or hosted URL) returned to Flask
|
||||
5. Flask renders page with generated image(s)
|
||||
|
||||
## Scenario 3a: Image Generation with Aspect Ratio & Resolution
|
||||
|
||||
1. User selects aspect ratio (e.g. `16:9`) and resolution (`2K`) on the image generation form
|
||||
2. Flask POSTs `aspect_ratio` and `image_size` to `POST /generate/image`
|
||||
3. Backend passes these as `image_config` to the chat completions endpoint (for FLUX/GPT-5 Image Mini)
|
||||
4. Generated image respects the requested aspect ratio and resolution
|
||||
|
||||
## Scenario 4: Video Generation (Text-to-Video)
|
||||
|
||||
1. User submits video generation form with prompt, model, aspect ratio, resolution, and duration
|
||||
2. Flask POSTs to `POST /generate/video` with JWT header
|
||||
3. Auth Service validates JWT
|
||||
4. Backend inserts a row into `generated_videos` with `status: "queued"` and returns the DB job ID
|
||||
5. Flask renders result page with polling UI
|
||||
6. Background worker (`video_worker.py`) picks up queued jobs every 15 seconds:
|
||||
- Calls OpenRouter `POST /api/v1/videos` with model, prompt, and parameters
|
||||
- Receives `{"id": "...", "polling_url": "..."}` and updates the DB row to `status: "processing"`
|
||||
- Polls the `polling_url` every 15 seconds until `status` is `"completed"` or `"failed"`
|
||||
- Updates the DB row with the final status and video URL
|
||||
7. Frontend JavaScript polls `GET /generate/video/{db_id}/status` every 5 seconds
|
||||
8. When `status` becomes `"completed"`, the response includes `video_url` — the video is displayed in a `<video>` element
|
||||
9. If `status` becomes `"failed"`, an error message is shown
|
||||
10. User can click "Cancel Job" to mark the job as `"cancelled"` (stops local polling, does not stop the provider job)
|
||||
|
||||
## Scenario 4a: Video Generation (Image-to-Video)
|
||||
|
||||
1. User provides an image URL, motion prompt, model, aspect ratio, resolution, and duration
|
||||
2. Flask POSTs to `POST /generate/video/from-image` with JWT header
|
||||
3. Same background worker flow as Scenario 4, with `generation_type: "image_to_video"`
|
||||
|
||||
## Scenario 4b: Video Job Cancellation
|
||||
|
||||
1. User clicks "Cancel Job" on the video detail page or gallery pending card
|
||||
2. Frontend POSTs to `/generate/video/{id}/cancel`
|
||||
3. Backend verifies the job belongs to the user and is not in a terminal state
|
||||
4. Backend updates the DB row `status` to `"cancelled"`
|
||||
5. Frontend stops polling and updates the UI to show "Job cancelled"
|
||||
|
||||
## Scenario 5: Token Refresh
|
||||
|
||||
1. Access token expires (TTL 15 min)
|
||||
2. Client POSTs current refresh token to `POST /auth/refresh`
|
||||
3. Auth Service validates JTI against `refresh_tokens` table (not revoked, not expired)
|
||||
4. Old JTI is revoked; new JTI inserted into `refresh_tokens`
|
||||
5. New access token + new refresh token returned to client
|
||||
|
||||
## Scenario 6: Admin User Management
|
||||
|
||||
1. Admin logs in and receives access token with `role: admin`
|
||||
2. Admin GETs `/admin/stats` to view user and token counts
|
||||
3. Admin DELETEs `/users/{id}` to remove a user — refresh tokens for that user are cascade-deleted
|
||||
4. Admin PUTs `/users/{id}/role` to promote a user to admin or demote to user
|
||||
|
||||
## Scenario 7: User Profile Update
|
||||
|
||||
1. Authenticated user navigates to `/users/profile`
|
||||
2. User submits updated email and/or new password
|
||||
3. Flask POSTs to `PUT /users/me` with JWT header
|
||||
4. Auth Service validates credentials and updates user record in DuckDB
|
||||
5. Session `user_email` is updated; user sees success message
|
||||
# 6. Runtime View
|
||||
|
||||
Describes concrete behavior and interactions of the system's building blocks in form of scenarios from the following areas:
|
||||
|
||||
- Important use cases or features: how do building blocks execute them?
|
||||
- Interactions at critical external interfaces: how do building blocks cooperate with users and neighboring systems?
|
||||
- Operation and administration: launch, start-up, stop
|
||||
- Error and exception scenarios
|
||||
|
||||
## Scenario 1: User Authentication
|
||||
|
||||
1. User submits login form in Flask frontend
|
||||
2. Flask POSTs credentials to `POST /auth/login`
|
||||
3. Auth Service validates credentials against DuckDB
|
||||
4. Auth Service returns JWT token
|
||||
5. Flask stores token in session cookie
|
||||
6. User is redirected to dashboard
|
||||
|
||||
## Scenario 2: AI Text Generation
|
||||
|
||||
1. User fills in text generation form in Flask frontend
|
||||
2. Flask POSTs prompt + model to `POST /generate/text` with JWT header
|
||||
3. Auth Service validates JWT
|
||||
4. AI Service sends prompt to openrouter.ai
|
||||
5. openrouter.ai returns generated text
|
||||
6. FastAPI returns result to Flask
|
||||
7. Flask renders result page for user
|
||||
|
||||
## Scenario 3: Image Generation
|
||||
|
||||
1. User submits image generation form with prompt, model, size, aspect ratio, and resolution
|
||||
2. Flask POSTs to `POST /generate/image` with JWT header
|
||||
3. Router auto-detects model type:
|
||||
- **FLUX / GPT-5 Image Mini**: calls `/chat/completions` with `modalities: ["image"]` and `image_config`
|
||||
- **DALL-E 3**: calls `/images/generations` with `size` and `n`
|
||||
4. Image URL (base64 data URL or hosted URL) returned to Flask
|
||||
5. Flask renders page with generated image(s)
|
||||
|
||||
## Scenario 3a: Image Generation with Aspect Ratio & Resolution
|
||||
|
||||
1. User selects aspect ratio (e.g. `16:9`) and resolution (`2K`) on the image generation form
|
||||
2. Flask POSTs `aspect_ratio` and `image_size` to `POST /generate/image`
|
||||
3. Backend passes these as `image_config` to the chat completions endpoint (for FLUX/GPT-5 Image Mini)
|
||||
4. Generated image respects the requested aspect ratio and resolution
|
||||
|
||||
## Scenario 4: Video Generation (Text-to-Video)
|
||||
|
||||
1. User submits video generation form with prompt, model, aspect ratio, resolution, and duration
|
||||
2. Flask POSTs to `POST /generate/video` with JWT header
|
||||
3. Auth Service validates JWT
|
||||
4. Backend inserts a row into `generated_videos` with `status: "queued"` and returns the DB job ID
|
||||
5. Flask renders result page with polling UI
|
||||
6. Background worker (`video_worker.py`) picks up queued jobs every 15 seconds:
|
||||
- Calls OpenRouter `POST /api/v1/videos` with model, prompt, and parameters
|
||||
- Receives `{"id": "...", "polling_url": "..."}` and updates the DB row to `status: "processing"`
|
||||
- Polls the `polling_url` every 15 seconds until `status` is `"completed"` or `"failed"`
|
||||
- Updates the DB row with the final status and video URL
|
||||
7. Frontend JavaScript polls `GET /generate/video/{db_id}/status` every 5 seconds
|
||||
8. When `status` becomes `"completed"`, the response includes `video_url` — the video is displayed in a `<video>` element
|
||||
9. If `status` becomes `"failed"`, an error message is shown
|
||||
10. User can click "Cancel Job" to mark the job as `"cancelled"` (stops local polling, does not stop the provider job)
|
||||
|
||||
## Scenario 4a: Video Generation (Image-to-Video)
|
||||
|
||||
1. User provides an image URL, motion prompt, model, aspect ratio, resolution, and duration
|
||||
2. Flask POSTs to `POST /generate/video/from-image` with JWT header
|
||||
3. Same background worker flow as Scenario 4, with `generation_type: "image_to_video"`
|
||||
|
||||
## Scenario 4b: Video Job Cancellation
|
||||
|
||||
1. User clicks "Cancel Job" on the video detail page or gallery pending card
|
||||
2. Frontend POSTs to `/generate/video/{id}/cancel`
|
||||
3. Backend verifies the job belongs to the user and is not in a terminal state
|
||||
4. Backend updates the DB row `status` to `"cancelled"`
|
||||
5. Frontend stops polling and updates the UI to show "Job cancelled"
|
||||
|
||||
## Scenario 5: Token Refresh
|
||||
|
||||
1. Access token expires (TTL 15 min)
|
||||
2. Client POSTs current refresh token to `POST /auth/refresh`
|
||||
3. Auth Service validates JTI against `refresh_tokens` table (not revoked, not expired)
|
||||
4. Old JTI is revoked; new JTI inserted into `refresh_tokens`
|
||||
5. New access token + new refresh token returned to client
|
||||
|
||||
## Scenario 6: Admin User Management
|
||||
|
||||
1. Admin logs in and receives access token with `role: admin`
|
||||
2. Admin GETs `/admin/stats` to view user and token counts
|
||||
3. Admin DELETEs `/users/{id}` to remove a user — refresh tokens for that user are cascade-deleted
|
||||
4. Admin PUTs `/users/{id}/role` to promote a user to admin or demote to user
|
||||
|
||||
## Scenario 7: User Profile Update
|
||||
|
||||
1. Authenticated user navigates to `/users/profile`
|
||||
2. User submits updated email and/or new password
|
||||
3. Flask POSTs to `PUT /users/me` with JWT header
|
||||
4. Auth Service validates credentials and updates user record in DuckDB
|
||||
5. Session `user_email` is updated; user sees success message
|
||||
+83
-83
@@ -1,83 +1,83 @@
|
||||
# 7. Deployment View
|
||||
|
||||
Describes:
|
||||
|
||||
1. Technical infrastructure used to execute your system, with infrastructure elements like geographical locations, environments, computers, processors, channels and net topologies.
|
||||
2. Mapping of (software) building blocks to that infrastructure elements.
|
||||
|
||||
**See**: [Coolify Deployment Guide](./deployment/coolify.md) for detailed instructions.
|
||||
|
||||
## Infrastructure Level 1
|
||||
|
||||
Hosted on a single VM running docker containers, deployed via Coolify with Nixpacks to 192.168.88.18 for production.
|
||||
|
||||
Containers run behind nginx at 192.168.88.11 which handles TLS termination and reverse proxying to the frontend on port 12016 and backend on port 12015. The database is a file on the host filesystem at `data/app.db` accessed by the backend service.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Users[Users / Internet]
|
||||
Nginx[nginx reverse proxy\nTLS termination]
|
||||
Users -->|HTTPS| Nginx
|
||||
|
||||
subgraph Coolify Server
|
||||
direction TB
|
||||
subgraph AI Frontend
|
||||
AI_Frontend[AI Frontend\nFlask\nServes HTML/CSS/JS UI]
|
||||
end
|
||||
subgraph AI Backend
|
||||
AI_Backend[AI Backend\nFastAPI\nCommunicates with openrouter.ai API]
|
||||
db[(DuckDB Database\nFile: data/app.db)]
|
||||
AI_Backend --> db
|
||||
end
|
||||
AI_Frontend -->|BACKEND_URL:12015| AI_Backend
|
||||
end
|
||||
Nginx -->|12016| AI_Frontend
|
||||
```
|
||||
|
||||
**Motivation:** All three components run as Docker containers for simplicity and low operational overhead.
|
||||
|
||||
**Quality and/or Performance Features:** The frontend and backend are stateless; DuckDB persists data on the host filesystem.
|
||||
|
||||
**Mapping of Building Blocks to Infrastructure:**
|
||||
|
||||
| Building Block | Container / Process | Port |
|
||||
| --------------- | ---------------------------- | --------------- |
|
||||
| Nginx | `nginx` | 80/443 (public) |
|
||||
| Coolify Server | `coolify` | — |
|
||||
| Flask frontend | `frontend` | 12016 |
|
||||
| FastAPI backend | `backend` | 12015 |
|
||||
| DuckDB | File on host (`data/app.db`) | — |
|
||||
|
||||
## Infrastructure Level 2
|
||||
|
||||
### Coolify with Nixpacks (Production)
|
||||
|
||||
Both services are deployed as separate Nixpacks resources in Coolify, which results in two separate containers running on the same host. The database is a file on the host filesystem, mounted as a volume in the backend container.
|
||||
|
||||
#### Frontend
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph Coolify Server
|
||||
direction TB
|
||||
subgraph AI Frontend
|
||||
AI_Frontend[AI Frontend\nNixpacks\nBase Dir: /frontend]
|
||||
end
|
||||
end
|
||||
Users[Users / Internet] -->|HTTPS| AI_Frontend
|
||||
```
|
||||
|
||||
#### Backend
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph Coolify Server
|
||||
direction TB
|
||||
subgraph AI Backend
|
||||
AI_Backend[AI Backend\nNixpacks\nBase Dir: /backend]
|
||||
db[(DuckDB Database\nVolume: /app/data)]
|
||||
AI_Backend --> db
|
||||
end
|
||||
end
|
||||
Frontend[Frontend Container] -->|BACKEND_URL:12015| AI_Backend
|
||||
```
|
||||
# 7. Deployment View
|
||||
|
||||
Describes:
|
||||
|
||||
1. Technical infrastructure used to execute your system, with infrastructure elements like geographical locations, environments, computers, processors, channels and net topologies.
|
||||
2. Mapping of (software) building blocks to that infrastructure elements.
|
||||
|
||||
**See**: [Coolify Deployment Guide](./deployment/coolify.md) for detailed instructions.
|
||||
|
||||
## Infrastructure Level 1
|
||||
|
||||
Hosted on a single VM running docker containers, deployed via Coolify with Nixpacks to 192.168.88.18 for production.
|
||||
|
||||
Containers run behind nginx at 192.168.88.11 which handles TLS termination and reverse proxying to the frontend on port 12016 and backend on port 12015. The database is a file on the host filesystem at `data/app.db` accessed by the backend service.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Users[Users / Internet]
|
||||
Nginx[nginx reverse proxy\nTLS termination]
|
||||
Users -->|HTTPS| Nginx
|
||||
|
||||
subgraph Coolify Server
|
||||
direction TB
|
||||
subgraph AI Frontend
|
||||
AI_Frontend[AI Frontend\nFlask\nServes HTML/CSS/JS UI]
|
||||
end
|
||||
subgraph AI Backend
|
||||
AI_Backend[AI Backend\nFastAPI\nCommunicates with openrouter.ai API]
|
||||
db[(DuckDB Database\nFile: data/app.db)]
|
||||
AI_Backend --> db
|
||||
end
|
||||
AI_Frontend -->|BACKEND_URL:12015| AI_Backend
|
||||
end
|
||||
Nginx -->|12016| AI_Frontend
|
||||
```
|
||||
|
||||
**Motivation:** All three components run as Docker containers for simplicity and low operational overhead.
|
||||
|
||||
**Quality and/or Performance Features:** The frontend and backend are stateless; DuckDB persists data on the host filesystem.
|
||||
|
||||
**Mapping of Building Blocks to Infrastructure:**
|
||||
|
||||
| Building Block | Container / Process | Port |
|
||||
| --------------- | ---------------------------- | --------------- |
|
||||
| Nginx | `nginx` | 80/443 (public) |
|
||||
| Coolify Server | `coolify` | — |
|
||||
| Flask frontend | `frontend` | 12016 |
|
||||
| FastAPI backend | `backend` | 12015 |
|
||||
| DuckDB | File on host (`data/app.db`) | — |
|
||||
|
||||
## Infrastructure Level 2
|
||||
|
||||
### Coolify with Nixpacks (Production)
|
||||
|
||||
Both services are deployed as separate Nixpacks resources in Coolify, which results in two separate containers running on the same host. The database is a file on the host filesystem, mounted as a volume in the backend container.
|
||||
|
||||
#### Frontend
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph Coolify Server
|
||||
direction TB
|
||||
subgraph AI Frontend
|
||||
AI_Frontend[AI Frontend\nNixpacks\nBase Dir: /frontend]
|
||||
end
|
||||
end
|
||||
Users[Users / Internet] -->|HTTPS| AI_Frontend
|
||||
```
|
||||
|
||||
#### Backend
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph Coolify Server
|
||||
direction TB
|
||||
subgraph AI Backend
|
||||
AI_Backend[AI Backend\nNixpacks\nBase Dir: /backend]
|
||||
db[(DuckDB Database\nVolume: /app/data)]
|
||||
AI_Backend --> db
|
||||
end
|
||||
end
|
||||
Frontend[Frontend Container] -->|BACKEND_URL:12015| AI_Backend
|
||||
```
|
||||
@@ -1,35 +1,35 @@
|
||||
# 8. Cross-cutting Concepts
|
||||
|
||||
Describes crosscutting concepts (practices, patterns, regulations or solution ideas). Such concepts are often related to multiple building blocks. They may include many different topics such as domain models, architecture patterns, rules for using specific technology, security, logging, and error handling.
|
||||
|
||||
> Pick **only** the most-needed topics for your system.
|
||||
|
||||
## OpenRouter API Integration
|
||||
|
||||
see [docs/8.1-openrouter.md](./8.1-openrouter.md) for details on how the backend integrates with OpenRouter for multi-modal AI generation, including image and video generation flows.
|
||||
|
||||
## DuckDB Concurrency and Storage
|
||||
|
||||
See [docs/8.2-duckdb.md](./8.2-duckdb.md) for details on how the backend handles concurrent access to DuckDB and manages the database file on the host filesystem.
|
||||
|
||||
## Security
|
||||
|
||||
- All API endpoints (except `/auth/login`) require a valid JWT in the `Authorization: Bearer` header.
|
||||
- HTTPS enforced in production via reverse proxy (nginx or Caddy).
|
||||
- Passwords stored as bcrypt hashes.
|
||||
|
||||
## Logging
|
||||
|
||||
- Structured JSON logs from FastAPI via Python `logging` + `structlog`.
|
||||
- OpenTelemetry traces exported for observability.
|
||||
- Log level configurable via environment variable `LOG_LEVEL`.
|
||||
|
||||
## Error Handling
|
||||
|
||||
- All API errors return a unified JSON shape: `{ "error": "<code>", "message": "<description>" }`.
|
||||
- HTTP status codes follow REST conventions (400, 401, 403, 404, 422, 500).
|
||||
|
||||
## Configuration
|
||||
|
||||
- All secrets (API keys, DB path, JWT secret) loaded from environment variables or `.env` file.
|
||||
- No secrets committed to source control.
|
||||
# 8. Cross-cutting Concepts
|
||||
|
||||
Describes crosscutting concepts (practices, patterns, regulations or solution ideas). Such concepts are often related to multiple building blocks. They may include many different topics such as domain models, architecture patterns, rules for using specific technology, security, logging, and error handling.
|
||||
|
||||
> Pick **only** the most-needed topics for your system.
|
||||
|
||||
## OpenRouter API Integration
|
||||
|
||||
see [docs/8.1-openrouter.md](./8.1-openrouter.md) for details on how the backend integrates with OpenRouter for multi-modal AI generation, including image and video generation flows.
|
||||
|
||||
## DuckDB Concurrency and Storage
|
||||
|
||||
See [docs/8.2-duckdb.md](./8.2-duckdb.md) for details on how the backend handles concurrent access to DuckDB and manages the database file on the host filesystem.
|
||||
|
||||
## Security
|
||||
|
||||
- All API endpoints (except `/auth/login`) require a valid JWT in the `Authorization: Bearer` header.
|
||||
- HTTPS enforced in production via reverse proxy (nginx or Caddy).
|
||||
- Passwords stored as bcrypt hashes.
|
||||
|
||||
## Logging
|
||||
|
||||
- Structured JSON logs from FastAPI via Python `logging` + `structlog`.
|
||||
- OpenTelemetry traces exported for observability.
|
||||
- Log level configurable via environment variable `LOG_LEVEL`.
|
||||
|
||||
## Error Handling
|
||||
|
||||
- All API errors return a unified JSON shape: `{ "error": "<code>", "message": "<description>" }`.
|
||||
- HTTP status codes follow REST conventions (400, 401, 403, 404, 422, 500).
|
||||
|
||||
## Configuration
|
||||
|
||||
- All secrets (API keys, DB path, JWT secret) loaded from environment variables or `.env` file.
|
||||
- No secrets committed to source control.
|
||||
+31
-31
@@ -1,31 +1,31 @@
|
||||
# OpenRouter API Integration
|
||||
|
||||
## Text Generation
|
||||
|
||||
> [!warning]
|
||||
> TODO: Add more details on how the backend integrates with OpenRouter for text generation, including chat completions and single-prompt generation flows.
|
||||
|
||||
## Image Generation
|
||||
|
||||
Image generation uses two different OpenRouter endpoints depending on the model:
|
||||
|
||||
- **Legacy endpoint** (`/images/generations`): Used by DALL-E 3 and similar models. Returns `data[].url` and `data[].b64_json`.
|
||||
- **Chat completions** (`/chat/completions` with `modalities: ["image"]`): Used by FLUX.2 Klein 4B and GPT-5 Image Mini. Returns `choices[0].message.images[].image_url.url` as base64 data URLs.
|
||||
|
||||
The router auto-detects the model type and routes accordingly. Image configuration (`aspect_ratio`, `image_size`) is passed via `image_config` for chat-based models.
|
||||
|
||||
## Video Generation
|
||||
|
||||
Video generation uses OpenRouter's `/api/v1/videos` endpoint with a **submit-and-poll** pattern orchestrated by a background worker:
|
||||
|
||||
1. User submits a video request via `POST /generate/video` (or `/generate/video/from-image`)
|
||||
2. Backend inserts a row into `generated_videos` with `status: "queued"` and returns immediately
|
||||
3. Background worker (`video_worker.py`) picks up queued jobs every 15 seconds:
|
||||
- Calls `POST /api/v1/videos` with `model`, `prompt`, `aspect_ratio`, `resolution`, `duration`
|
||||
- Receives `{"id": "job_id", "polling_url": "https://..."}` and updates DB to `status: "processing"`
|
||||
- Polls `GET polling_url` every 15 seconds until `status` is `"completed"` or `"failed"`
|
||||
- Updates DB with final status, `video_url`, and any `error` message
|
||||
4. Frontend polls `GET /generate/video/{db_id}/status` every 5 seconds to show live updates
|
||||
5. Completed response includes `video_url` — the video is displayed in a `<video>` element
|
||||
|
||||
Supported models: `openai/sora-2-pro`, `google/veo-3.1-fast`. Both text-to-video and image-to-video use the same `/api/v1/videos` endpoint (image-to-video includes `frame_images` with `first_frame` in the request body).
|
||||
# OpenRouter API Integration
|
||||
|
||||
## Text Generation
|
||||
|
||||
> [!warning]
|
||||
> TODO: Add more details on how the backend integrates with OpenRouter for text generation, including chat completions and single-prompt generation flows.
|
||||
|
||||
## Image Generation
|
||||
|
||||
Image generation uses two different OpenRouter endpoints depending on the model:
|
||||
|
||||
- **Legacy endpoint** (`/images/generations`): Used by DALL-E 3 and similar models. Returns `data[].url` and `data[].b64_json`.
|
||||
- **Chat completions** (`/chat/completions` with `modalities: ["image"]`): Used by FLUX.2 Klein 4B and GPT-5 Image Mini. Returns `choices[0].message.images[].image_url.url` as base64 data URLs.
|
||||
|
||||
The router auto-detects the model type and routes accordingly. Image configuration (`aspect_ratio`, `image_size`) is passed via `image_config` for chat-based models.
|
||||
|
||||
## Video Generation
|
||||
|
||||
Video generation uses OpenRouter's `/api/v1/videos` endpoint with a **submit-and-poll** pattern orchestrated by a background worker:
|
||||
|
||||
1. User submits a video request via `POST /generate/video` (or `/generate/video/from-image`)
|
||||
2. Backend inserts a row into `generated_videos` with `status: "queued"` and returns immediately
|
||||
3. Background worker (`video_worker.py`) picks up queued jobs every 15 seconds:
|
||||
- Calls `POST /api/v1/videos` with `model`, `prompt`, `aspect_ratio`, `resolution`, `duration`
|
||||
- Receives `{"id": "job_id", "polling_url": "https://..."}` and updates DB to `status: "processing"`
|
||||
- Polls `GET polling_url` every 15 seconds until `status` is `"completed"` or `"failed"`
|
||||
- Updates DB with final status, `video_url`, and any `error` message
|
||||
4. Frontend polls `GET /generate/video/{db_id}/status` every 5 seconds to show live updates
|
||||
5. Completed response includes `video_url` — the video is displayed in a `<video>` element
|
||||
|
||||
Supported models: `openai/sora-2-pro`, `google/veo-3.1-fast`. Both text-to-video and image-to-video use the same `/api/v1/videos` endpoint (image-to-video includes `frame_images` with `first_frame` in the request body).
|
||||
+46
-46
@@ -1,46 +1,46 @@
|
||||
# DuckDB Concurrency and Storage
|
||||
|
||||
## Single Writer Per Process
|
||||
|
||||
DuckDB allows only one process to open the database file in read-write mode at a time. The FastAPI backend must be run with a single worker (`uvicorn --workers 1`). Running multiple workers against the same DuckDB file will cause startup errors.
|
||||
|
||||
## asyncio.Lock for Writes
|
||||
|
||||
All database write operations (`INSERT`, `UPDATE`, `DELETE`) in the FastAPI async context are wrapped in a single `asyncio.Lock` (`get_write_lock()` from `backend/app/db.py`). This prevents concurrent coroutines from issuing overlapping writes within the single process, which would otherwise raise DuckDB optimistic concurrency errors.
|
||||
|
||||
Read operations (`SELECT`) do not require the lock — DuckDB's MVCC provides consistent read snapshots.
|
||||
|
||||
## Schema
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id UUID DEFAULT uuid() PRIMARY KEY,
|
||||
email VARCHAR NOT NULL UNIQUE,
|
||||
password_hash VARCHAR NOT NULL,
|
||||
role VARCHAR DEFAULT 'user',
|
||||
created_at TIMESTAMP DEFAULT now(),
|
||||
updated_at TIMESTAMP DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE refresh_tokens (
|
||||
jti UUID DEFAULT uuid() PRIMARY KEY,
|
||||
user_id UUID NOT NULL, -- soft FK to users.id
|
||||
issued_at TIMESTAMP DEFAULT now(),
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
revoked BOOLEAN DEFAULT false
|
||||
);
|
||||
```
|
||||
|
||||
> The `REFERENCES users(id)` foreign key is intentionally omitted from `refresh_tokens`. DuckDB fires FK checks on `UPDATE` of the parent table (including email changes), causing false constraint violations. Referential integrity is enforced manually: deleting a user also deletes their refresh tokens in the same write transaction.
|
||||
|
||||
## Access Tokens
|
||||
|
||||
Access tokens are **stateless** JWTs — not stored in the database. They are validated by signature and expiry claim only. The short TTL (15 minutes) limits the blast radius if a token is leaked.
|
||||
|
||||
## Refresh Tokens
|
||||
|
||||
Refresh tokens store a JTI (JWT ID) UUID in the `refresh_tokens` table. On each use the old JTI is revoked and a new one issued (rotation). On logout the JTI is immediately revoked. Expired and revoked tokens can be purged via `POST /admin/tokens/purge`.
|
||||
|
||||
## Future: AI Generation History
|
||||
|
||||
AI generation metadata (model, prompt, cost, result URLs) can be stored as JSON columns in a future `generation_history` table in DuckDB, enabling per-user analytics and usage dashboards at zero extra infrastructure cost.
|
||||
# DuckDB Concurrency and Storage
|
||||
|
||||
## Single Writer Per Process
|
||||
|
||||
DuckDB allows only one process to open the database file in read-write mode at a time. The FastAPI backend must be run with a single worker (`uvicorn --workers 1`). Running multiple workers against the same DuckDB file will cause startup errors.
|
||||
|
||||
## asyncio.Lock for Writes
|
||||
|
||||
All database write operations (`INSERT`, `UPDATE`, `DELETE`) in the FastAPI async context are wrapped in a single `asyncio.Lock` (`get_write_lock()` from `backend/app/db.py`). This prevents concurrent coroutines from issuing overlapping writes within the single process, which would otherwise raise DuckDB optimistic concurrency errors.
|
||||
|
||||
Read operations (`SELECT`) do not require the lock — DuckDB's MVCC provides consistent read snapshots.
|
||||
|
||||
## Schema
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id UUID DEFAULT uuid() PRIMARY KEY,
|
||||
email VARCHAR NOT NULL UNIQUE,
|
||||
password_hash VARCHAR NOT NULL,
|
||||
role VARCHAR DEFAULT 'user',
|
||||
created_at TIMESTAMP DEFAULT now(),
|
||||
updated_at TIMESTAMP DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE refresh_tokens (
|
||||
jti UUID DEFAULT uuid() PRIMARY KEY,
|
||||
user_id UUID NOT NULL, -- soft FK to users.id
|
||||
issued_at TIMESTAMP DEFAULT now(),
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
revoked BOOLEAN DEFAULT false
|
||||
);
|
||||
```
|
||||
|
||||
> The `REFERENCES users(id)` foreign key is intentionally omitted from `refresh_tokens`. DuckDB fires FK checks on `UPDATE` of the parent table (including email changes), causing false constraint violations. Referential integrity is enforced manually: deleting a user also deletes their refresh tokens in the same write transaction.
|
||||
|
||||
## Access Tokens
|
||||
|
||||
Access tokens are **stateless** JWTs — not stored in the database. They are validated by signature and expiry claim only. The short TTL (15 minutes) limits the blast radius if a token is leaked.
|
||||
|
||||
## Refresh Tokens
|
||||
|
||||
Refresh tokens store a JTI (JWT ID) UUID in the `refresh_tokens` table. On each use the old JTI is revoked and a new one issued (rotation). On logout the JTI is immediately revoked. Expired and revoked tokens can be purged via `POST /admin/tokens/purge`.
|
||||
|
||||
## Future: AI Generation History
|
||||
|
||||
AI generation metadata (model, prompt, cost, result URLs) can be stored as JSON columns in a future `generation_history` table in DuckDB, enabling per-user analytics and usage dashboards at zero extra infrastructure cost.
|
||||
+113
-113
@@ -1,113 +1,113 @@
|
||||
# 9. Architecture Decisions
|
||||
|
||||
Important, expensive, large scale or risky architecture decisions including rationales. With "decisions" we mean selecting one alternative based on given criteria.
|
||||
|
||||
Refer to section 4 (Solution Strategy) where the most important decisions are already captured. Avoid redundancy.
|
||||
|
||||
> Consider using [ADRs (Architecture Decision Records)](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions) for every important decision.
|
||||
|
||||
## ADR-001: Use DuckDB as the embedded database
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** The application needs persistent storage for user data. A full RDBMS (PostgreSQL, MySQL) would require a separate server process and adds operational complexity.
|
||||
|
||||
**Decision:** Use DuckDB as an embedded, file-based database accessed in-process by the FastAPI backend.
|
||||
|
||||
**Consequences:** No separate DB server needed. Limited to single-writer access patterns. Suitable for the expected load.
|
||||
|
||||
---
|
||||
|
||||
## ADR-002: Use FastAPI for the backend
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** The backend needs async performance for concurrent AI generation requests and automatic OpenAPI documentation.
|
||||
|
||||
**Decision:** Use FastAPI with uvicorn as the ASGI server.
|
||||
|
||||
**Consequences:** Async endpoints enable high concurrency. Auto-generated OpenAPI docs simplify frontend integration and testing.
|
||||
|
||||
---
|
||||
|
||||
## ADR-003: Use Flask for the frontend
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** A lightweight server-side rendering layer is needed for the UI with minimal frontend complexity.
|
||||
|
||||
**Decision:** Use Flask with Jinja2 templates to serve HTML pages.
|
||||
|
||||
**Consequences:** Simple, familiar framework. No JavaScript build toolchain required. Frontend calls FastAPI over HTTP.
|
||||
|
||||
---
|
||||
|
||||
## ADR-004: Serialize DuckDB writes with asyncio.Lock
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** FastAPI runs async coroutines concurrently in a single process. DuckDB's optimistic concurrency model raises errors when multiple coroutines issue writes simultaneously to the same connection.
|
||||
|
||||
**Decision:** All write operations (`INSERT`, `UPDATE`, `DELETE`) acquire a single process-wide `asyncio.Lock` before executing. The lock is released immediately after the statement completes.
|
||||
|
||||
**Consequences:** Writes are serialised within the process, eliminating concurrency errors. Read performance is unaffected. Throughput is acceptable for the expected user load. If write throughput becomes a bottleneck in future, migrating to PostgreSQL is the preferred path.
|
||||
|
||||
---
|
||||
|
||||
## ADR-005: Use OpenRouter as the unified AI provider gateway
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** The application needs access to multiple AI model providers (OpenAI, Anthropic, Stability AI, Runway, etc.) for text, image, and video generation.
|
||||
|
||||
**Decision:** Route all AI generation requests through the [OpenRouter](https://openrouter.ai) API, which exposes an OpenAI-compatible REST interface for hundreds of models.
|
||||
|
||||
**Consequences:** Single API key and base URL for all model providers. Model switching requires only a change to the `model` field in the request payload. If OpenRouter is unavailable, all generation endpoints return `502 Bad Gateway`. Pricing and rate limits are governed by OpenRouter's policies per model.
|
||||
|
||||
---
|
||||
|
||||
## ADR-006: Use submit-and-poll pattern for video generation
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** OpenRouter's video generation models (Sora 2 Pro, Veo 3.1 Fast) do not return video URLs immediately. Video generation is a long-running operation (typically 30-120 seconds) that requires polling.
|
||||
|
||||
**Decision:** Use the `/api/v1/videos` endpoint with a two-step pattern: (1) `POST` to submit the job and receive a `polling_url`, (2) `GET` the `polling_url` every 5 seconds until `status` is `"completed"` or `"failed"`. The Flask frontend proxies polling requests via `GET /generate/video/status?polling_url=...` and the frontend JavaScript polls this endpoint automatically.
|
||||
|
||||
**Consequences:** The video generation endpoint returns immediately with `status: "queued"` and a `polling_url`. The frontend displays a "Processing..." message and polls for updates. When complete, the video is displayed in a `<video>` element. This adds complexity to the frontend but is necessary for long-running operations. If OpenRouter's polling endpoint is unavailable, the frontend shows an error after a timeout.
|
||||
|
||||
---
|
||||
|
||||
## ADR-007: Auto-detect image generation model type
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** OpenRouter supports image generation through two different endpoints: the legacy `/images/generations` endpoint (DALL-E 3) and the chat completions endpoint with `modalities: ["image"]` (FLUX.2 Klein 4B, GPT-5 Image Mini). These endpoints have different request/response formats.
|
||||
|
||||
**Decision:** The `/generate/image` router auto-detects the model type by checking if the model slug contains `"flux"` or `"gpt-5-image-mini"`. If so, it routes to `/chat/completions` with `modalities: ["image"]` and `image_config` (aspect_ratio, image_size). Otherwise, it uses `/images/generations` with `size` and `n`.
|
||||
|
||||
**Consequences:** Users can specify any image generation model in the form without needing to know which endpoint it uses. The router handles the routing transparently. Adding new image models requires only updating the detection logic if they use a different endpoint.
|
||||
|
||||
---
|
||||
|
||||
## ADR-008: Flask session-based auth with role caching
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** The Flask frontend needs to know the user's authentication state and role for route protection (`@login_required`, `@admin_required`) without making an extra API call on every request.
|
||||
|
||||
**Decision:** Store the JWT access token, refresh token, user email, and user role in the Flask server-side session cookie after login. The `@login_required` decorator checks for `access_token` in the session. The `@admin_required` decorator checks `session["user_role"] == "admin"`. This avoids an extra API call to `/users/me` on every request.
|
||||
|
||||
**Consequences:** The user role is cached in the session and may become stale if an admin changes a user's role while the user is logged in. The user must log out and log back in to see the updated role. This is acceptable for the expected usage pattern. The session cookie is signed (Flask's default) to prevent tampering.
|
||||
|
||||
---
|
||||
|
||||
## ADR-009: Separate generation pages in frontend
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** The original `/generate` page handled text, image, and video generation in a single form, which became unwieldy as more generation types were added.
|
||||
|
||||
**Decision:** Create separate Flask routes and Jinja2 templates for each generation type: `/generate/text`, `/generate/image`, `/generate/video`. The `/generate` route redirects to `/generate/text`. The navigation bar includes a "Generate" dropdown with links to each sub-page. The video page uses tabs for text-to-video and image-to-video.
|
||||
|
||||
**Consequences:** Each generation type has its own URL, making it bookmarkable and shareable. The navigation is clearer with a dropdown menu. Adding new generation types (e.g., audio) follows the same pattern. The `/generate` redirect provides a sensible default entry point.
|
||||
# 9. Architecture Decisions
|
||||
|
||||
Important, expensive, large scale or risky architecture decisions including rationales. With "decisions" we mean selecting one alternative based on given criteria.
|
||||
|
||||
Refer to section 4 (Solution Strategy) where the most important decisions are already captured. Avoid redundancy.
|
||||
|
||||
> Consider using [ADRs (Architecture Decision Records)](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions) for every important decision.
|
||||
|
||||
## ADR-001: Use DuckDB as the embedded database
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** The application needs persistent storage for user data. A full RDBMS (PostgreSQL, MySQL) would require a separate server process and adds operational complexity.
|
||||
|
||||
**Decision:** Use DuckDB as an embedded, file-based database accessed in-process by the FastAPI backend.
|
||||
|
||||
**Consequences:** No separate DB server needed. Limited to single-writer access patterns. Suitable for the expected load.
|
||||
|
||||
---
|
||||
|
||||
## ADR-002: Use FastAPI for the backend
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** The backend needs async performance for concurrent AI generation requests and automatic OpenAPI documentation.
|
||||
|
||||
**Decision:** Use FastAPI with uvicorn as the ASGI server.
|
||||
|
||||
**Consequences:** Async endpoints enable high concurrency. Auto-generated OpenAPI docs simplify frontend integration and testing.
|
||||
|
||||
---
|
||||
|
||||
## ADR-003: Use Flask for the frontend
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** A lightweight server-side rendering layer is needed for the UI with minimal frontend complexity.
|
||||
|
||||
**Decision:** Use Flask with Jinja2 templates to serve HTML pages.
|
||||
|
||||
**Consequences:** Simple, familiar framework. No JavaScript build toolchain required. Frontend calls FastAPI over HTTP.
|
||||
|
||||
---
|
||||
|
||||
## ADR-004: Serialize DuckDB writes with asyncio.Lock
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** FastAPI runs async coroutines concurrently in a single process. DuckDB's optimistic concurrency model raises errors when multiple coroutines issue writes simultaneously to the same connection.
|
||||
|
||||
**Decision:** All write operations (`INSERT`, `UPDATE`, `DELETE`) acquire a single process-wide `asyncio.Lock` before executing. The lock is released immediately after the statement completes.
|
||||
|
||||
**Consequences:** Writes are serialised within the process, eliminating concurrency errors. Read performance is unaffected. Throughput is acceptable for the expected user load. If write throughput becomes a bottleneck in future, migrating to PostgreSQL is the preferred path.
|
||||
|
||||
---
|
||||
|
||||
## ADR-005: Use OpenRouter as the unified AI provider gateway
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** The application needs access to multiple AI model providers (OpenAI, Anthropic, Stability AI, Runway, etc.) for text, image, and video generation.
|
||||
|
||||
**Decision:** Route all AI generation requests through the [OpenRouter](https://openrouter.ai) API, which exposes an OpenAI-compatible REST interface for hundreds of models.
|
||||
|
||||
**Consequences:** Single API key and base URL for all model providers. Model switching requires only a change to the `model` field in the request payload. If OpenRouter is unavailable, all generation endpoints return `502 Bad Gateway`. Pricing and rate limits are governed by OpenRouter's policies per model.
|
||||
|
||||
---
|
||||
|
||||
## ADR-006: Use submit-and-poll pattern for video generation
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** OpenRouter's video generation models (Sora 2 Pro, Veo 3.1 Fast) do not return video URLs immediately. Video generation is a long-running operation (typically 30-120 seconds) that requires polling.
|
||||
|
||||
**Decision:** Use the `/api/v1/videos` endpoint with a two-step pattern: (1) `POST` to submit the job and receive a `polling_url`, (2) `GET` the `polling_url` every 5 seconds until `status` is `"completed"` or `"failed"`. The Flask frontend proxies polling requests via `GET /generate/video/status?polling_url=...` and the frontend JavaScript polls this endpoint automatically.
|
||||
|
||||
**Consequences:** The video generation endpoint returns immediately with `status: "queued"` and a `polling_url`. The frontend displays a "Processing..." message and polls for updates. When complete, the video is displayed in a `<video>` element. This adds complexity to the frontend but is necessary for long-running operations. If OpenRouter's polling endpoint is unavailable, the frontend shows an error after a timeout.
|
||||
|
||||
---
|
||||
|
||||
## ADR-007: Auto-detect image generation model type
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** OpenRouter supports image generation through two different endpoints: the legacy `/images/generations` endpoint (DALL-E 3) and the chat completions endpoint with `modalities: ["image"]` (FLUX.2 Klein 4B, GPT-5 Image Mini). These endpoints have different request/response formats.
|
||||
|
||||
**Decision:** The `/generate/image` router auto-detects the model type by checking if the model slug contains `"flux"` or `"gpt-5-image-mini"`. If so, it routes to `/chat/completions` with `modalities: ["image"]` and `image_config` (aspect_ratio, image_size). Otherwise, it uses `/images/generations` with `size` and `n`.
|
||||
|
||||
**Consequences:** Users can specify any image generation model in the form without needing to know which endpoint it uses. The router handles the routing transparently. Adding new image models requires only updating the detection logic if they use a different endpoint.
|
||||
|
||||
---
|
||||
|
||||
## ADR-008: Flask session-based auth with role caching
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** The Flask frontend needs to know the user's authentication state and role for route protection (`@login_required`, `@admin_required`) without making an extra API call on every request.
|
||||
|
||||
**Decision:** Store the JWT access token, refresh token, user email, and user role in the Flask server-side session cookie after login. The `@login_required` decorator checks for `access_token` in the session. The `@admin_required` decorator checks `session["user_role"] == "admin"`. This avoids an extra API call to `/users/me` on every request.
|
||||
|
||||
**Consequences:** The user role is cached in the session and may become stale if an admin changes a user's role while the user is logged in. The user must log out and log back in to see the updated role. This is acceptable for the expected usage pattern. The session cookie is signed (Flask's default) to prevent tampering.
|
||||
|
||||
---
|
||||
|
||||
## ADR-009: Separate generation pages in frontend
|
||||
|
||||
**Status:** accepted
|
||||
|
||||
**Context:** The original `/generate` page handled text, image, and video generation in a single form, which became unwieldy as more generation types were added.
|
||||
|
||||
**Decision:** Create separate Flask routes and Jinja2 templates for each generation type: `/generate/text`, `/generate/image`, `/generate/video`. The `/generate` route redirects to `/generate/text`. The navigation bar includes a "Generate" dropdown with links to each sub-page. The video page uses tabs for text-to-video and image-to-video.
|
||||
|
||||
**Consequences:** Each generation type has its own URL, making it bookmarkable and shareable. The navigation is clearer with a dropdown menu. Adding new generation types (e.g., audio) follows the same pattern. The `/generate` redirect provides a sensible default entry point.
|
||||
@@ -1,21 +1,21 @@
|
||||
# 10. Quality Requirements
|
||||
|
||||
This section contains all relevant quality requirements. The most important ones are already described in section 1.2 (Quality Goals), and should only be referenced here.
|
||||
|
||||
## Quality Requirements Overview
|
||||
|
||||
| Category | Quality Requirement |
|
||||
| --------------- | ----------------------------------------------------------------------- |
|
||||
| Availability | Service available ≥ 99.9% of the time |
|
||||
| Performance | Generation endpoints respond within 200 ms (excluding AI model latency) |
|
||||
| Security | All user data encrypted at rest and in transit; JWT-protected endpoints |
|
||||
| Maintainability | Test coverage > 80% on `app/`; CI pipeline enforced |
|
||||
| Portability | Runs on Windows and Linux without modification |
|
||||
|
||||
## Quality Scenarios
|
||||
|
||||
| Scenario ID | Source | Stimulus | Environment | Artifact | Response | Response Measure |
|
||||
| ----------- | --------- | ------------------------------- | ----------- | ------------ | ------------------------------- | ----------------------------------------- |
|
||||
| QS-01 | End user | Submits text generation request | Production | AI Service | Returns generated text | < 200 ms (excluding model latency) |
|
||||
| QS-02 | Attacker | Sends request without valid JWT | Production | Auth Service | Returns 401 Unauthorized | 100% of unauthenticated requests rejected |
|
||||
| QS-03 | Developer | Adds new API endpoint | Development | FastAPI app | Tests pass, coverage maintained | CI pipeline green |
|
||||
# 10. Quality Requirements
|
||||
|
||||
This section contains all relevant quality requirements. The most important ones are already described in section 1.2 (Quality Goals), and should only be referenced here.
|
||||
|
||||
## Quality Requirements Overview
|
||||
|
||||
| Category | Quality Requirement |
|
||||
| --------------- | ----------------------------------------------------------------------- |
|
||||
| Availability | Service available ≥ 99.9% of the time |
|
||||
| Performance | Generation endpoints respond within 200 ms (excluding AI model latency) |
|
||||
| Security | All user data encrypted at rest and in transit; JWT-protected endpoints |
|
||||
| Maintainability | Test coverage > 80% on `app/`; CI pipeline enforced |
|
||||
| Portability | Runs on Windows and Linux without modification |
|
||||
|
||||
## Quality Scenarios
|
||||
|
||||
| Scenario ID | Source | Stimulus | Environment | Artifact | Response | Response Measure |
|
||||
| ----------- | --------- | ------------------------------- | ----------- | ------------ | ------------------------------- | ----------------------------------------- |
|
||||
| QS-01 | End user | Submits text generation request | Production | AI Service | Returns generated text | < 200 ms (excluding model latency) |
|
||||
| QS-02 | Attacker | Sends request without valid JWT | Production | Auth Service | Returns 401 Unauthorized | 100% of unauthenticated requests rejected |
|
||||
| QS-03 | Developer | Adds new API endpoint | Development | FastAPI app | Tests pass, coverage maintained | CI pipeline green |
|
||||
@@ -1,12 +1,12 @@
|
||||
# 11. Risks and Technical Debt
|
||||
|
||||
A list of identified technical risks or technical debts, ordered by priority.
|
||||
|
||||
> "Risk management is project management for grown-ups." — Tim Lister, Atlantic Systems Guild.
|
||||
|
||||
| Priority | Risk / Technical Debt | Probability | Impact | Suggested Measures |
|
||||
| -------- | ---------------------------------------------- | ----------- | ------ | ------------------------------------------------------------------------ |
|
||||
| 1 | Dependency on openrouter.ai availability | medium | high | Add fallback models; implement retry logic with exponential backoff |
|
||||
| 2 | DuckDB schema changes break existing data | low | high | Version migrations; backup strategy before upgrades |
|
||||
| 3 | Single-process DuckDB limits write concurrency | low | medium | Monitor load; consider migration to PostgreSQL if needed |
|
||||
| 4 | JWT secret leak | low | high | Rotate secrets via environment variables; never commit to source control |
|
||||
# 11. Risks and Technical Debt
|
||||
|
||||
A list of identified technical risks or technical debts, ordered by priority.
|
||||
|
||||
> "Risk management is project management for grown-ups." — Tim Lister, Atlantic Systems Guild.
|
||||
|
||||
| Priority | Risk / Technical Debt | Probability | Impact | Suggested Measures |
|
||||
| -------- | ---------------------------------------------- | ----------- | ------ | ------------------------------------------------------------------------ |
|
||||
| 1 | Dependency on openrouter.ai availability | medium | high | Add fallback models; implement retry logic with exponential backoff |
|
||||
| 2 | DuckDB schema changes break existing data | low | high | Version migrations; backup strategy before upgrades |
|
||||
| 3 | Single-process DuckDB limits write concurrency | low | medium | Monitor load; consider migration to PostgreSQL if needed |
|
||||
| 4 | JWT secret leak | low | high | Rotate secrets via environment variables; never commit to source control |
|
||||
+15
-15
@@ -1,15 +1,15 @@
|
||||
# 12. Glossary
|
||||
|
||||
The most important domain and technical terms that stakeholders use when discussing the system.
|
||||
|
||||
| Term | Definition |
|
||||
| ------------- | --------------------------------------------------------------------------------------- |
|
||||
| AI | Artificial Intelligence — machine learning models that generate text, images, or video |
|
||||
| API | Application Programming Interface — HTTP endpoints exposed by the FastAPI backend |
|
||||
| JWT | JSON Web Token — signed token used for stateless authentication |
|
||||
| DuckDB | Embedded analytical database used for persistent storage |
|
||||
| FastAPI | Python async web framework used for the backend |
|
||||
| Flask | Python web framework used for the frontend |
|
||||
| openrouter.ai | Third-party API aggregator providing access to multiple AI model providers |
|
||||
| ADR | Architecture Decision Record — a document capturing an important architectural decision |
|
||||
| HTTPS | Hypertext Transfer Protocol Secure — encrypted HTTP communication |
|
||||
# 12. Glossary
|
||||
|
||||
The most important domain and technical terms that stakeholders use when discussing the system.
|
||||
|
||||
| Term | Definition |
|
||||
| ------------- | --------------------------------------------------------------------------------------- |
|
||||
| AI | Artificial Intelligence — machine learning models that generate text, images, or video |
|
||||
| API | Application Programming Interface — HTTP endpoints exposed by the FastAPI backend |
|
||||
| JWT | JSON Web Token — signed token used for stateless authentication |
|
||||
| DuckDB | Embedded analytical database used for persistent storage |
|
||||
| FastAPI | Python async web framework used for the backend |
|
||||
| Flask | Python web framework used for the frontend |
|
||||
| openrouter.ai | Third-party API aggregator providing access to multiple AI model providers |
|
||||
| ADR | Architecture Decision Record — a document capturing an important architectural decision |
|
||||
| HTTPS | Hypertext Transfer Protocol Secure — encrypted HTTP communication |
|
||||
+19
-19
@@ -1,19 +1,19 @@
|
||||
# Testing Strategy
|
||||
|
||||
The FastAPI backend will be tested using a combination of unit and integration tests.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
- Use `pytest` with `pytest-asyncio` for async endpoint handlers.
|
||||
- Mock external dependencies such as the DuckDB connection and the openrouter.ai API.
|
||||
|
||||
## Integration Tests
|
||||
|
||||
- Use `pytest-flask` to spin up the FastAPI app in a test client.
|
||||
- Test full request/response cycles for authentication, user CRUD, and AI generation endpoints.
|
||||
- Use an in‑memory DuckDB instance for database tests.
|
||||
|
||||
## Test Coverage
|
||||
|
||||
- Aim for >80 % coverage on the `app/` package.
|
||||
- Generate coverage reports with `pytest-cov`.
|
||||
# Testing Strategy
|
||||
|
||||
The FastAPI backend will be tested using a combination of unit and integration tests.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
- Use `pytest` with `pytest-asyncio` for async endpoint handlers.
|
||||
- Mock external dependencies such as the DuckDB connection and the openrouter.ai API.
|
||||
|
||||
## Integration Tests
|
||||
|
||||
- Use `pytest-flask` to spin up the FastAPI app in a test client.
|
||||
- Test full request/response cycles for authentication, user CRUD, and AI generation endpoints.
|
||||
- Use an in‑memory DuckDB instance for database tests.
|
||||
|
||||
## Test Coverage
|
||||
|
||||
- Aim for >80 % coverage on the `app/` package.
|
||||
- Generate coverage reports with `pytest-cov`.
|
||||
+175
-175
@@ -1,175 +1,175 @@
|
||||
# Coolify Deployment Guide
|
||||
|
||||
This guide covers deploying `ai.allucanget.biz` using [Coolify](https://coolify.io) with Nixpacks from the repository `https://git.allucanget.biz/allucanget/ai.allucanget.biz.git`.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The application consists of two Python services:
|
||||
|
||||
| Service | Framework | Port | Description |
|
||||
| -------- | ----------------- | ----- | ------------------------------------------ |
|
||||
| Backend | FastAPI + uvicorn | 12015 | REST API, auth, AI generation, DuckDB |
|
||||
| Frontend | Flask + gunicorn | 12016 | SSR web UI, session auth, proxy to backend |
|
||||
|
||||
Coolify's built-in reverse proxy routes traffic:
|
||||
|
||||
- `/api/*` → Backend (port 12015)
|
||||
- `/` → Frontend (port 12016)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A Coolify instance (self-hosted or Cloud)
|
||||
- Git repository pushed to `https://git.allucanget.biz/allucanget/ai.allucanget.biz.git`
|
||||
- Domain configured to point to your Coolify server
|
||||
|
||||
## Step 1: Create Backend Service
|
||||
|
||||
1. In Coolify, click **Add Resource** → **Deploy a new resource** → **Git**
|
||||
2. Connect your Git repository (`git.allucanget.biz`)
|
||||
3. Select the `ai.allucanget.biz` repository
|
||||
4. Choose the `main` branch
|
||||
5. Set **Build Pack** to `nixpacks`
|
||||
6. Set **Base Directory** to `/backend` - this tells Nixpacks to look in the `backend/` subdirectory for `requirements.txt` and the Python application
|
||||
7. Set **Ports Exposed** to `12015`
|
||||
8. Set **Start Command** to:
|
||||
|
||||
```txt
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 12015
|
||||
```
|
||||
|
||||
9. Click **Create Resource**
|
||||
|
||||
> **Important:** Nixpacks copies the **contents** of the Base Directory to `/app/` in the container. When Base Directory is `/backend`, the `backend/` folder wrapper is removed — only `app/`, `tests/`, and `requirements.txt` are copied. Therefore the start command uses `app.main:app` (not `backend.app.main:app`).
|
||||
|
||||
### Backend Environment Variables
|
||||
|
||||
Add these as **Runtime** environment variables in Coolify:
|
||||
|
||||
| Variable | Description | Example |
|
||||
| -------------------- | ------------------------------------ | ------------------------------------ |
|
||||
| `OPENROUTER_API_KEY` | OpenRouter API key for AI generation | `sk-or-v1-...` |
|
||||
| `JWT_SECRET` | Secret key for JWT token signing | Generate with `openssl rand -hex 32` |
|
||||
| `APP_URL` | Public URL of the backend | `https://api.ai.allucanget.biz` |
|
||||
| `APP_NAME` | Application name | `All You Can GET AI` |
|
||||
| `CORS_ORIGINS` | Comma-separated allowed origins | `https://ai.allucanget.biz` |
|
||||
|
||||
## Step 2: Create Frontend Service
|
||||
|
||||
1. In Coolify, click **Add Resource** → **Deploy a new resource** → **Git**
|
||||
2. Select the same repository
|
||||
3. Choose the `main` branch
|
||||
4. Set **Build Pack** to `nixpacks`
|
||||
5. Set **Base Directory** to `/frontend` - this tells Nixpacks to look in the `frontend/` subdirectory for `requirements.txt` and the Python application
|
||||
6. Set **Ports Exposed** to `12016`
|
||||
7. Set **Start Command** to:
|
||||
|
||||
```txt
|
||||
gunicorn app.main:app --bind 0.0.0.0:12016 --workers 2 --timeout 120
|
||||
```
|
||||
|
||||
8. Click **Create Resource**
|
||||
|
||||
> **Note:** Nixpacks will automatically detect and install only the production dependencies from `requirements.txt`.
|
||||
> **Important:** Nixpacks copies the **contents** of the Base Directory to `/app/` in the container. When Base Directory is `/frontend`, the `frontend/` folder wrapper is removed — only `app/`, `tests/`, and `requirements.txt` are copied. Therefore the start command uses `app.main:app` (not `frontend.app.main:app`).
|
||||
|
||||
### Frontend Environment Variables
|
||||
|
||||
Add these as **Runtime** environment variables in Coolify:
|
||||
|
||||
| Variable | Description | Example |
|
||||
| ------------------ | ----------------------------------------- | --------------------------------------------------------------- |
|
||||
| `FLASK_SECRET_KEY` | Flask session cookie signing key | Generate with `openssl rand -hex 32` |
|
||||
| `BACKEND_URL` | Internal URL to reach the backend service | `http://localhost:12015` (or use Coolify's internal networking) |
|
||||
|
||||
## Step 3: Configure Reverse Proxy
|
||||
|
||||
Coolify provides a built-in reverse proxy. Configure routing rules:
|
||||
|
||||
### Backend Proxy Rules
|
||||
|
||||
- **Domain**: `api.ai.allucanget.biz` (or subdomain of your choice)
|
||||
- **Port**: `12015`
|
||||
- **Path**: `/api/*` → forward to backend
|
||||
|
||||
### Frontend Proxy Rules
|
||||
|
||||
- **Domain**: `ai.allucanget.biz`
|
||||
- **Port**: `12016`
|
||||
- **Path**: `/` → forward to frontend
|
||||
|
||||
## Step 4: SSL/TLS
|
||||
|
||||
Enable HTTPS in Coolify for both services:
|
||||
|
||||
1. Go to each service's settings
|
||||
2. Enable **Auto HTTPS** (Let's Encrypt)
|
||||
3. Configure domain names
|
||||
4. Coolify automatically handles certificate renewal
|
||||
|
||||
## Step 5: Persistent Storage (Optional)
|
||||
|
||||
If you want to persist DuckDB data:
|
||||
|
||||
1. In Coolify, go to the **Backend** service
|
||||
2. Navigate to **Persistent Storage**
|
||||
3. Add a volume mount:
|
||||
- **Host Path**: `/data` (or any path on the host)
|
||||
- **Container Path**: `/app/data`
|
||||
- **Type**: `Bind Mount` or `Volume`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Backend healthcheck stays unhealthy
|
||||
|
||||
- Check backend logs in Coolify
|
||||
- Verify `OPENROUTER_API_KEY` and `JWT_SECRET` are set
|
||||
- Verify volume mount at `/app/data` is writable
|
||||
|
||||
### Backend won't start
|
||||
|
||||
- Check that `OPENROUTER_API_KEY` is set
|
||||
- Verify `JWT_SECRET` is a sufficiently long random string
|
||||
- Check logs in Coolify's **Logs** tab
|
||||
|
||||
### Frontend can't reach backend
|
||||
|
||||
- Ensure `BACKEND_URL` points to the correct internal URL
|
||||
- If both services are on the same Coolify server, use `http://localhost:12015`
|
||||
- Check that the backend service is running and healthy
|
||||
|
||||
### CORS errors
|
||||
|
||||
- Set `CORS_ORIGINS` to include your frontend domain
|
||||
- Example: `https://ai.allucanget.biz`
|
||||
|
||||
### Nixpacks build fails
|
||||
|
||||
- Verify the base directory is correct (`/backend` or `/frontend`)
|
||||
- Check that `requirements.txt` exists in the base directory
|
||||
- Review build logs in Coolify
|
||||
|
||||
## Environment Variable Summary
|
||||
|
||||
All required environment variables:
|
||||
|
||||
| Variable | Service | Required |
|
||||
| -------------------- | -------- | ------------------------------------- |
|
||||
| `OPENROUTER_API_KEY` | Backend | Yes |
|
||||
| `JWT_SECRET` | Backend | Yes |
|
||||
| `APP_URL` | Backend | Yes |
|
||||
| `APP_NAME` | Backend | No (defaults to "All You Can GET AI") |
|
||||
| `CORS_ORIGINS` | Backend | Yes |
|
||||
| `FLASK_SECRET_KEY` | Frontend | Yes |
|
||||
| `BACKEND_URL` | Frontend | Yes |
|
||||
|
||||
## Deployment Checklist
|
||||
|
||||
- [ ] Repository pushed to Git
|
||||
- [ ] Backend service created with correct base directory (`/backend`)
|
||||
- [ ] Backend environment variables configured
|
||||
- [ ] Frontend service created with correct base directory (`/frontend`)
|
||||
- [ ] Frontend environment variables configured
|
||||
- [ ] SSL certificates enabled
|
||||
- [ ] Domain names configured
|
||||
- [ ] Health checks passing
|
||||
- [ ] Logs reviewed for errors
|
||||
# Coolify Deployment Guide
|
||||
|
||||
This guide covers deploying `ai.allucanget.biz` using [Coolify](https://coolify.io) with Nixpacks from the repository `https://git.allucanget.biz/allucanget/ai.allucanget.biz.git`.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The application consists of two Python services:
|
||||
|
||||
| Service | Framework | Port | Description |
|
||||
| -------- | ----------------- | ----- | ------------------------------------------ |
|
||||
| Backend | FastAPI + uvicorn | 12015 | REST API, auth, AI generation, DuckDB |
|
||||
| Frontend | Flask + gunicorn | 12016 | SSR web UI, session auth, proxy to backend |
|
||||
|
||||
Coolify's built-in reverse proxy routes traffic:
|
||||
|
||||
- `/api/*` → Backend (port 12015)
|
||||
- `/` → Frontend (port 12016)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A Coolify instance (self-hosted or Cloud)
|
||||
- Git repository pushed to `https://git.allucanget.biz/allucanget/ai.allucanget.biz.git`
|
||||
- Domain configured to point to your Coolify server
|
||||
|
||||
## Step 1: Create Backend Service
|
||||
|
||||
1. In Coolify, click **Add Resource** → **Deploy a new resource** → **Git**
|
||||
2. Connect your Git repository (`git.allucanget.biz`)
|
||||
3. Select the `ai.allucanget.biz` repository
|
||||
4. Choose the `main` branch
|
||||
5. Set **Build Pack** to `nixpacks`
|
||||
6. Set **Base Directory** to `/backend` - this tells Nixpacks to look in the `backend/` subdirectory for `requirements.txt` and the Python application
|
||||
7. Set **Ports Exposed** to `12015`
|
||||
8. Set **Start Command** to:
|
||||
|
||||
```txt
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 12015
|
||||
```
|
||||
|
||||
9. Click **Create Resource**
|
||||
|
||||
> **Important:** Nixpacks copies the **contents** of the Base Directory to `/app/` in the container. When Base Directory is `/backend`, the `backend/` folder wrapper is removed — only `app/`, `tests/`, and `requirements.txt` are copied. Therefore the start command uses `app.main:app` (not `backend.app.main:app`).
|
||||
|
||||
### Backend Environment Variables
|
||||
|
||||
Add these as **Runtime** environment variables in Coolify:
|
||||
|
||||
| Variable | Description | Example |
|
||||
| -------------------- | ------------------------------------ | ------------------------------------ |
|
||||
| `OPENROUTER_API_KEY` | OpenRouter API key for AI generation | `sk-or-v1-...` |
|
||||
| `JWT_SECRET` | Secret key for JWT token signing | Generate with `openssl rand -hex 32` |
|
||||
| `APP_URL` | Public URL of the backend | `https://api.ai.allucanget.biz` |
|
||||
| `APP_NAME` | Application name | `All You Can GET AI` |
|
||||
| `CORS_ORIGINS` | Comma-separated allowed origins | `https://ai.allucanget.biz` |
|
||||
|
||||
## Step 2: Create Frontend Service
|
||||
|
||||
1. In Coolify, click **Add Resource** → **Deploy a new resource** → **Git**
|
||||
2. Select the same repository
|
||||
3. Choose the `main` branch
|
||||
4. Set **Build Pack** to `nixpacks`
|
||||
5. Set **Base Directory** to `/frontend` - this tells Nixpacks to look in the `frontend/` subdirectory for `requirements.txt` and the Python application
|
||||
6. Set **Ports Exposed** to `12016`
|
||||
7. Set **Start Command** to:
|
||||
|
||||
```txt
|
||||
gunicorn app.main:app --bind 0.0.0.0:12016 --workers 2 --timeout 120
|
||||
```
|
||||
|
||||
8. Click **Create Resource**
|
||||
|
||||
> **Note:** Nixpacks will automatically detect and install only the production dependencies from `requirements.txt`.
|
||||
> **Important:** Nixpacks copies the **contents** of the Base Directory to `/app/` in the container. When Base Directory is `/frontend`, the `frontend/` folder wrapper is removed — only `app/`, `tests/`, and `requirements.txt` are copied. Therefore the start command uses `app.main:app` (not `frontend.app.main:app`).
|
||||
|
||||
### Frontend Environment Variables
|
||||
|
||||
Add these as **Runtime** environment variables in Coolify:
|
||||
|
||||
| Variable | Description | Example |
|
||||
| ------------------ | ----------------------------------------- | --------------------------------------------------------------- |
|
||||
| `FLASK_SECRET_KEY` | Flask session cookie signing key | Generate with `openssl rand -hex 32` |
|
||||
| `BACKEND_URL` | Internal URL to reach the backend service | `http://localhost:12015` (or use Coolify's internal networking) |
|
||||
|
||||
## Step 3: Configure Reverse Proxy
|
||||
|
||||
Coolify provides a built-in reverse proxy. Configure routing rules:
|
||||
|
||||
### Backend Proxy Rules
|
||||
|
||||
- **Domain**: `api.ai.allucanget.biz` (or subdomain of your choice)
|
||||
- **Port**: `12015`
|
||||
- **Path**: `/api/*` → forward to backend
|
||||
|
||||
### Frontend Proxy Rules
|
||||
|
||||
- **Domain**: `ai.allucanget.biz`
|
||||
- **Port**: `12016`
|
||||
- **Path**: `/` → forward to frontend
|
||||
|
||||
## Step 4: SSL/TLS
|
||||
|
||||
Enable HTTPS in Coolify for both services:
|
||||
|
||||
1. Go to each service's settings
|
||||
2. Enable **Auto HTTPS** (Let's Encrypt)
|
||||
3. Configure domain names
|
||||
4. Coolify automatically handles certificate renewal
|
||||
|
||||
## Step 5: Persistent Storage (Optional)
|
||||
|
||||
If you want to persist DuckDB data:
|
||||
|
||||
1. In Coolify, go to the **Backend** service
|
||||
2. Navigate to **Persistent Storage**
|
||||
3. Add a volume mount:
|
||||
- **Host Path**: `/data` (or any path on the host)
|
||||
- **Container Path**: `/app/data`
|
||||
- **Type**: `Bind Mount` or `Volume`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Backend healthcheck stays unhealthy
|
||||
|
||||
- Check backend logs in Coolify
|
||||
- Verify `OPENROUTER_API_KEY` and `JWT_SECRET` are set
|
||||
- Verify volume mount at `/app/data` is writable
|
||||
|
||||
### Backend won't start
|
||||
|
||||
- Check that `OPENROUTER_API_KEY` is set
|
||||
- Verify `JWT_SECRET` is a sufficiently long random string
|
||||
- Check logs in Coolify's **Logs** tab
|
||||
|
||||
### Frontend can't reach backend
|
||||
|
||||
- Ensure `BACKEND_URL` points to the correct internal URL
|
||||
- If both services are on the same Coolify server, use `http://localhost:12015`
|
||||
- Check that the backend service is running and healthy
|
||||
|
||||
### CORS errors
|
||||
|
||||
- Set `CORS_ORIGINS` to include your frontend domain
|
||||
- Example: `https://ai.allucanget.biz`
|
||||
|
||||
### Nixpacks build fails
|
||||
|
||||
- Verify the base directory is correct (`/backend` or `/frontend`)
|
||||
- Check that `requirements.txt` exists in the base directory
|
||||
- Review build logs in Coolify
|
||||
|
||||
## Environment Variable Summary
|
||||
|
||||
All required environment variables:
|
||||
|
||||
| Variable | Service | Required |
|
||||
| -------------------- | -------- | ------------------------------------- |
|
||||
| `OPENROUTER_API_KEY` | Backend | Yes |
|
||||
| `JWT_SECRET` | Backend | Yes |
|
||||
| `APP_URL` | Backend | Yes |
|
||||
| `APP_NAME` | Backend | No (defaults to "All You Can GET AI") |
|
||||
| `CORS_ORIGINS` | Backend | Yes |
|
||||
| `FLASK_SECRET_KEY` | Frontend | Yes |
|
||||
| `BACKEND_URL` | Frontend | Yes |
|
||||
|
||||
## Deployment Checklist
|
||||
|
||||
- [ ] Repository pushed to Git
|
||||
- [ ] Backend service created with correct base directory (`/backend`)
|
||||
- [ ] Backend environment variables configured
|
||||
- [ ] Frontend service created with correct base directory (`/frontend`)
|
||||
- [ ] Frontend environment variables configured
|
||||
- [ ] SSL certificates enabled
|
||||
- [ ] Domain names configured
|
||||
- [ ] Health checks passing
|
||||
- [ ] Logs reviewed for errors
|
||||
Reference in new issue
Block a user