mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-25 20:34:37 +02:00
Projects included: - maerchenzauber (NestJS backend + Expo mobile + SvelteKit web + Astro landing) - manacore (Expo mobile + SvelteKit web + Astro landing) - manadeck (NestJS backend + Expo mobile + SvelteKit web) - memoro (Expo mobile + SvelteKit web + Astro landing) This commit preserves the current state before monorepo restructuring. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
432 lines
No EOL
15 KiB
Markdown
432 lines
No EOL
15 KiB
Markdown
# Märchenzauber Database Documentation
|
|
|
|
## Project Information
|
|
- **Project Name**: Maerchenzauber
|
|
- **Project ID**: dyywxrmonxoiojsjmymc
|
|
- **Region**: eu-central-1
|
|
- **Status**: ACTIVE_HEALTHY
|
|
- **Database Host**: db.dyywxrmonxoiojsjmymc.supabase.co
|
|
- **Postgres Version**: 15.8.1.079
|
|
- **Created**: 2025-04-29T21:43:53.3429Z
|
|
|
|
## Database Schema Overview
|
|
|
|
The Märchenzauber database is designed to support a storytelling application with character creation and story generation capabilities. It uses a custom JWT authentication system alongside standard Supabase auth.
|
|
|
|
## Tables
|
|
|
|
### 1. `characters`
|
|
Stores user-created characters for storytelling.
|
|
|
|
| Column | Type | Default | Nullable | Description |
|
|
|--------|------|---------|----------|-------------|
|
|
| `id` | uuid | uuid_generate_v4() | NO | Primary key |
|
|
| `user_id` | text | - | NO | User identifier (custom JWT) |
|
|
| `name` | varchar | - | NO | Character name |
|
|
| `original_description` | text | - | NO | Original character description |
|
|
| `character_description_prompt` | text | - | NO | AI prompt for character |
|
|
| `image_url` | text | - | NO | Character image URL |
|
|
| `source_image_url` | text | - | YES | Original source image |
|
|
| `is_animal` | boolean | false | YES | Whether character is an animal |
|
|
| `animal_type` | varchar | 'unspecified animal' | YES | Type of animal if applicable |
|
|
| `images_data` | jsonb | '[]'::jsonb | YES | JSON array of image data |
|
|
| `created_at` | timestamptz | now() | NO | Creation timestamp |
|
|
| `updated_at` | timestamptz | now() | NO | Last update timestamp |
|
|
|
|
**Indexes:**
|
|
- `characters_pkey` (PRIMARY KEY on id)
|
|
- `characters_user_id_id_key` (UNIQUE on user_id, id)
|
|
- `idx_characters_user_id` (on user_id)
|
|
- `idx_characters_images_data` (GIN index on images_data)
|
|
|
|
**Relationships:**
|
|
- Referenced by `stories.character_id`
|
|
- Referenced by `errors.character_id`
|
|
|
|
### 2. `stories`
|
|
Stores generated stories with their associated characters and content.
|
|
|
|
| Column | Type | Default | Nullable | Description |
|
|
|--------|------|---------|----------|-------------|
|
|
| `id` | uuid | uuid_generate_v4() | NO | Primary key |
|
|
| `user_id` | text | - | NO | User identifier (custom JWT) |
|
|
| `title` | varchar | - | YES | Story title |
|
|
| `story_prompt` | text | - | NO | Prompt used to generate story |
|
|
| `story_text` | text | - | YES | Generated story content |
|
|
| `is_animal_story` | boolean | false | YES | Whether it's an animal story |
|
|
| `animal_type` | varchar | 'unspecified animal' | YES | Type of animal if applicable |
|
|
| `character_id` | uuid | - | YES | Reference to character |
|
|
| `character_name` | varchar | - | YES | Character name (denormalized) |
|
|
| `used_settings` | jsonb | - | YES | Settings used for generation |
|
|
| `characters_data` | jsonb | '[]'::jsonb | YES | JSON array of character data |
|
|
| `pages_data` | jsonb | '[]'::jsonb | YES | JSON array of page data |
|
|
| `created_at` | timestamptz | now() | NO | Creation timestamp |
|
|
| `updated_at` | timestamptz | now() | NO | Last update timestamp |
|
|
|
|
**Indexes:**
|
|
- `stories_pkey` (PRIMARY KEY on id)
|
|
- `stories_user_id_id_key` (UNIQUE on user_id, id)
|
|
- `idx_stories_user_id` (on user_id)
|
|
- `idx_stories_character_id` (on character_id)
|
|
- `idx_stories_characters_data` (GIN index on characters_data)
|
|
- `idx_stories_pages_data` (GIN index on pages_data)
|
|
|
|
**Foreign Keys:**
|
|
- `character_id` → `characters.id`
|
|
|
|
### 3. `creators`
|
|
Stores information about story and illustration creators (admin-managed).
|
|
|
|
| Column | Type | Default | Nullable | Description |
|
|
|--------|------|---------|----------|-------------|
|
|
| `id` | uuid | uuid_generate_v4() | NO | Primary key |
|
|
| `creator_id` | varchar | - | NO | Unique creator identifier |
|
|
| `name` | varchar | - | NO | Creator name |
|
|
| `type` | varchar | - | NO | 'illustrator' or 'author' |
|
|
| `system_prompt` | text | - | NO | AI system prompt |
|
|
| `description` | text | - | NO | Creator description |
|
|
| `profile_picture` | text | - | YES | Profile picture URL |
|
|
| `extra_prompt_beginning` | text | - | YES | Additional prompt prefix |
|
|
| `extra_prompt_end` | text | - | YES | Additional prompt suffix |
|
|
| `created_at` | timestamptz | now() | NO | Creation timestamp |
|
|
| `updated_at` | timestamptz | now() | NO | Last update timestamp |
|
|
|
|
**Indexes:**
|
|
- `creators_pkey` (PRIMARY KEY on id)
|
|
- `creators_creator_id_key` (UNIQUE on creator_id)
|
|
|
|
**Constraints:**
|
|
- CHECK: `type` must be either 'illustrator' or 'author'
|
|
|
|
### 4. `errors`
|
|
Logs errors that occur during character creation or story generation.
|
|
|
|
| Column | Type | Default | Nullable | Description |
|
|
|--------|------|---------|----------|-------------|
|
|
| `id` | uuid | uuid_generate_v4() | NO | Primary key |
|
|
| `user_id` | text | - | NO | User identifier |
|
|
| `story_id` | uuid | - | YES | Related story ID |
|
|
| `character_id` | uuid | - | YES | Related character ID |
|
|
| `error_type` | varchar | - | NO | Type of error |
|
|
| `error_details` | jsonb | - | NO | Detailed error information |
|
|
| `created_at` | timestamptz | now() | NO | Error timestamp |
|
|
|
|
**Indexes:**
|
|
- `errors_pkey` (PRIMARY KEY on id)
|
|
- `idx_errors_user_id` (on user_id)
|
|
- `idx_errors_story_id` (on story_id)
|
|
- `idx_errors_character_id` (on character_id)
|
|
|
|
**Foreign Keys:**
|
|
- `story_id` → `stories.id`
|
|
- `character_id` → `characters.id`
|
|
|
|
### 5. `story_logbooks`
|
|
Comprehensive logs of the story creation process including all AI prompts, responses, and timing data.
|
|
|
|
| Column | Type | Default | Nullable | Description |
|
|
|--------|------|---------|----------|-------------|
|
|
| `id` | uuid | gen_random_uuid() | NO | Primary key |
|
|
| `story_id` | uuid | - | NO | Related story ID |
|
|
| `user_id` | uuid | - | NO | User identifier |
|
|
| `character_ids` | uuid[] | '{}' | YES | Array of character IDs used in story |
|
|
| `start_time` | timestamptz | - | NO | Story creation start time |
|
|
| `end_time` | timestamptz | - | YES | Story creation end time |
|
|
| `total_duration_ms` | integer | - | YES | Total duration in milliseconds |
|
|
| `success` | boolean | false | YES | Whether creation was successful |
|
|
| `environment` | text | 'production' | YES | Environment (development/production) |
|
|
| `entries` | jsonb | '[]'::jsonb | NO | Array of log entries with prompts/responses |
|
|
| `metadata` | jsonb | '{}'::jsonb | NO | Additional metadata (characters, settings, etc.) |
|
|
| `summary` | text | - | YES | Human-readable summary of the creation process |
|
|
| `created_at` | timestamptz | now() | YES | Log creation timestamp |
|
|
| `updated_at` | timestamptz | now() | YES | Last update timestamp |
|
|
|
|
**Indexes:**
|
|
- `story_logbooks_pkey` (PRIMARY KEY on id)
|
|
- `idx_story_logbooks_story_id` (on story_id)
|
|
- `idx_story_logbooks_user_id` (on user_id)
|
|
- `idx_story_logbooks_character_ids` (GIN index on character_ids)
|
|
- `idx_story_logbooks_created_at` (on created_at DESC)
|
|
- `idx_story_logbooks_success` (on success)
|
|
|
|
**JSONB Structure:**
|
|
|
|
**entries** array contains objects with:
|
|
```json
|
|
{
|
|
"timestamp": "ISO 8601 timestamp",
|
|
"step": "step_name",
|
|
"type": "prompt|response|error|info|character|illustration",
|
|
"data": {}, // Context-specific data
|
|
"duration": 1234 // Optional, in milliseconds
|
|
}
|
|
```
|
|
|
|
**metadata** object contains:
|
|
```json
|
|
{
|
|
"storyTitle": "string",
|
|
"characters": [{
|
|
"id": "uuid",
|
|
"name": "string",
|
|
"description": "string",
|
|
"isAnimal": boolean,
|
|
"animalType": "string"
|
|
}],
|
|
"storyDescription": "string",
|
|
"language": "string",
|
|
"authorId": "uuid",
|
|
"illustratorId": "uuid",
|
|
"pageCount": number,
|
|
"imageCount": number,
|
|
"success": boolean,
|
|
"errors": []
|
|
}
|
|
```
|
|
|
|
## Database Functions
|
|
|
|
### Authentication & Authorization Functions
|
|
|
|
1. **`auth_is_admin()`**
|
|
- Returns: `boolean`
|
|
- Checks if current user has admin privileges
|
|
|
|
2. **`authenticate_custom_jwt(jwt_token text)`**
|
|
- Returns: `json`
|
|
- Validates and authenticates custom JWT tokens
|
|
|
|
3. **`current_user_id()`**
|
|
- Returns: `text`
|
|
- Gets current user ID from auth context or JWT claims
|
|
|
|
4. **`current_user_uuid()`**
|
|
- Returns: `uuid`
|
|
- Gets current user UUID from auth context
|
|
|
|
5. **`user_owns_resource(resource_user_id text)`**
|
|
- Returns: `boolean`
|
|
- Checks if current user owns a specific resource
|
|
|
|
### Utility Functions
|
|
|
|
6. **`debug_jwt_context()`**
|
|
- Returns: `json`
|
|
- Debugging function to inspect JWT context
|
|
|
|
7. **`get_current_user_context()`**
|
|
- Returns: `json`
|
|
- Gets complete current user context information
|
|
|
|
8. **`set_request_user_id(user_id uuid/text)`**
|
|
- Returns: `void`
|
|
- Sets the request user ID (overloaded for uuid and text)
|
|
|
|
### Data Operation Functions
|
|
|
|
9. **`execute_as_user(p_token text, p_operation text, p_params jsonb)`**
|
|
- Returns: `jsonb`
|
|
- Executes operations on behalf of a user with proper authentication
|
|
|
|
10. **`create_test_character(p_user_id text, p_name text, p_description text, p_prompt text, p_image_url text)`**
|
|
- Returns: `jsonb`
|
|
- Creates a test character (likely for testing purposes)
|
|
|
|
## Row Level Security (RLS) Policies
|
|
|
|
### Characters Table
|
|
- **Users can view own characters**: SELECT where `user_id` matches current user
|
|
- **Users can insert own characters**: INSERT where `user_id` matches current user
|
|
- **Users can update own characters**: UPDATE where `user_id` matches current user
|
|
- **Users can delete own characters**: DELETE where `user_id` matches current user
|
|
- **Service role full access**: Full access for service_role
|
|
|
|
### Stories Table
|
|
- **Users can view own stories**: SELECT where `user_id` matches current user
|
|
- **Users can insert own stories**: INSERT where `user_id` matches current user
|
|
- **Users can update own stories**: UPDATE where `user_id` matches current user
|
|
- **Users can delete own stories**: DELETE where `user_id` matches current user
|
|
- **Service role full access**: Full access for service_role
|
|
|
|
### Creators Table
|
|
- **All users can view creators**: SELECT for all authenticated users
|
|
- **Admins can insert creators**: INSERT only for admins
|
|
- **Admins can update creators**: UPDATE only for admins
|
|
- **Admins can delete creators**: DELETE only for admins
|
|
- **Service role full access**: Full access for service_role
|
|
|
|
### Errors Table
|
|
- **Users can insert errors**: Any authenticated user can log errors
|
|
- **Admins can view errors**: SELECT only for admins
|
|
- **Admins can update errors**: UPDATE only for admins
|
|
- **Admins can delete errors**: DELETE only for admins
|
|
- **Service role full access**: Full access for service_role
|
|
|
|
### Story Logbooks Table
|
|
- **Service role full access**: Full access for service_role (backend handles auth via Mana Core)
|
|
|
|
## Enabled Extensions
|
|
|
|
### Core Extensions
|
|
- `uuid-ossp` (v1.1): UUID generation
|
|
- `pgcrypto` (v1.3): Cryptographic functions
|
|
- `pg_stat_statements` (v1.10): Query performance tracking
|
|
- `pgjwt` (v0.2.0): JWT handling for PostgreSQL
|
|
|
|
### GraphQL & Vault
|
|
- `pg_graphql` (v1.5.11): GraphQL support
|
|
- `supabase_vault` (v0.3.1): Supabase Vault for secrets management
|
|
|
|
## Migration History
|
|
|
|
The database has undergone 43 migrations, primarily focused on:
|
|
1. Custom JWT authentication implementation
|
|
2. RLS policy refinements for security
|
|
3. Storage integration with custom auth
|
|
4. Performance optimizations
|
|
5. Security enhancements
|
|
|
|
Key migration themes:
|
|
- Initial custom JWT verification setup (20250501)
|
|
- RLS policies for JWT authentication (20250501)
|
|
- Auth helper functions implementation (20250705)
|
|
- Storage RLS policies with custom JWT support (20250705)
|
|
- Performance and security optimizations (20250706-20250712)
|
|
- Story logbooks table for comprehensive creation tracking (20250910)
|
|
|
|
## Security Advisories
|
|
|
|
### Warnings
|
|
1. **Function Search Path Mutable** (SECURITY)
|
|
- Function `public.execute_as_user` has a mutable search_path
|
|
- [Remediation Guide](https://supabase.com/docs/guides/database/database-linter?lint=0011_function_search_path_mutable)
|
|
|
|
2. **Auth OTP Long Expiry** (SECURITY)
|
|
- OTP expiry is set to more than an hour
|
|
- Recommendation: Set to less than an hour
|
|
- [Security Best Practices](https://supabase.com/docs/guides/platform/going-into-prod#security)
|
|
|
|
### Performance Issues
|
|
1. **RLS Performance** (PERFORMANCE)
|
|
- Multiple RLS policies re-evaluate `current_setting()` or `auth.<function>()` for each row
|
|
- Affected tables: `stories`, `characters`, `errors`
|
|
- Solution: Replace `auth.<function>()` with `(select auth.<function>())`
|
|
- [Performance Guide](https://supabase.com/docs/guides/database/postgres/row-level-security#call-functions-with-select)
|
|
|
|
2. **Unused Indexes** (INFO)
|
|
- Several indexes have never been used and could be removed:
|
|
- `idx_errors_user_id`
|
|
- `idx_errors_story_id`
|
|
- `idx_characters_images_data`
|
|
- `idx_stories_pages_data`
|
|
- `idx_stories_characters_data`
|
|
|
|
## TypeScript Types
|
|
|
|
The database includes generated TypeScript types for all tables and functions, providing type-safe access from client applications. Key types include:
|
|
|
|
```typescript
|
|
// Table row types
|
|
type Character = {
|
|
id: string
|
|
user_id: string
|
|
name: string
|
|
original_description: string
|
|
character_description_prompt: string
|
|
image_url: string
|
|
source_image_url: string | null
|
|
is_animal: boolean | null
|
|
animal_type: string | null
|
|
images_data: Json | null
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
type Story = {
|
|
id: string
|
|
user_id: string
|
|
title: string | null
|
|
story_prompt: string
|
|
story_text: string | null
|
|
is_animal_story: boolean | null
|
|
animal_type: string | null
|
|
character_id: string | null
|
|
character_name: string | null
|
|
used_settings: Json | null
|
|
characters_data: Json | null
|
|
pages_data: Json | null
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
type Creator = {
|
|
id: string
|
|
creator_id: string
|
|
name: string
|
|
type: string
|
|
system_prompt: string
|
|
description: string
|
|
profile_picture: string | null
|
|
extra_prompt_beginning: string | null
|
|
extra_prompt_end: string | null
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
type Error = {
|
|
id: string
|
|
user_id: string
|
|
story_id: string | null
|
|
character_id: string | null
|
|
error_type: string
|
|
error_details: Json
|
|
created_at: string
|
|
}
|
|
|
|
type StoryLogbook = {
|
|
id: string
|
|
story_id: string
|
|
user_id: string
|
|
character_ids: string[] | null
|
|
start_time: string
|
|
end_time: string | null
|
|
total_duration_ms: number | null
|
|
success: boolean | null
|
|
environment: string | null
|
|
entries: Json
|
|
metadata: Json
|
|
summary: string | null
|
|
created_at: string | null
|
|
updated_at: string | null
|
|
}
|
|
```
|
|
|
|
## Development Recommendations
|
|
|
|
### Immediate Actions Required
|
|
1. **Fix Function Security**: Update `execute_as_user` function to set search_path
|
|
2. **Reduce OTP Expiry**: Configure OTP to expire within 1 hour
|
|
3. **Optimize RLS Policies**: Update policies to use subqueries for better performance
|
|
|
|
### Performance Optimizations
|
|
1. **Review Unused Indexes**: Consider removing unused GIN indexes on JSONB columns
|
|
2. **RLS Query Optimization**: Implement suggested RLS performance improvements
|
|
|
|
### Best Practices
|
|
1. **Use TypeScript Types**: Leverage generated types for type-safe database access
|
|
2. **Monitor Query Performance**: Use `pg_stat_statements` to track slow queries
|
|
3. **Regular Security Audits**: Run security advisors regularly after schema changes
|
|
4. **Test with Custom JWT**: Ensure all features work with custom JWT authentication
|
|
|
|
## Authentication Architecture
|
|
|
|
The system supports dual authentication methods:
|
|
1. **Standard Supabase Auth**: Using `auth.uid()`
|
|
2. **Custom JWT**: Using JWT claims from request headers
|
|
|
|
All RLS policies use `COALESCE` to support both authentication methods, prioritizing:
|
|
1. Supabase auth UID
|
|
2. Custom JWT claims (`sub` field)
|
|
3. Fallback to `current_user_id()` function
|
|
|
|
This architecture allows flexibility in authentication while maintaining security through consistent RLS policies. |