mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-18 04:49:40 +02:00
Move inactive projects out of active workspace: - bauntown (community website) - maerchenzauber (AI story generation) - memoro (voice memo app) - news (news aggregation) - nutriphi (nutrition tracking) - reader (reading app) - uload (URL shortener) - wisekeep (AI wisdom extraction) Update CLAUDE.md documentation: - Add presi to active projects - Document archived projects section - Update workspace configuration Archived apps can be re-activated by moving back to apps/ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
332 lines
9.5 KiB
Markdown
332 lines
9.5 KiB
Markdown
# System Characters
|
|
|
|
System characters are special read-only characters visible to all users in the Storyteller app. They provide a curated set of characters that users can use to create stories without needing to create their own characters first.
|
|
|
|
## Overview
|
|
|
|
System characters differ from regular user characters in several key ways:
|
|
|
|
- **Shared Visibility**: All users can see and use system characters
|
|
- **Read-Only**: Users cannot edit or delete system characters
|
|
- **No Sharing**: Share and edit buttons are hidden in the UI
|
|
- **Special User ID**: System characters belong to a special system user
|
|
|
|
## Technical Implementation
|
|
|
|
### System User ID
|
|
|
|
System characters use a special UUID to identify them:
|
|
|
|
```typescript
|
|
const SYSTEM_USER_ID = '00000000-0000-0000-0000-000000000000';
|
|
```
|
|
|
|
This UUID is used consistently across:
|
|
|
|
- Database records (`user_id` field)
|
|
- Backend validation logic
|
|
- Frontend UI conditional rendering
|
|
|
|
### Database Structure
|
|
|
|
System characters are stored in the same `characters` table as user characters, with `user_id` set to the system UUID:
|
|
|
|
```sql
|
|
INSERT INTO public.characters (
|
|
id,
|
|
user_id,
|
|
name,
|
|
user_description,
|
|
character_description,
|
|
character_description_prompt,
|
|
image_url,
|
|
is_animal,
|
|
animal_type,
|
|
archived,
|
|
created_at,
|
|
updated_at,
|
|
is_published,
|
|
sharing_preference,
|
|
images_data
|
|
) VALUES (
|
|
'11111111-1111-1111-1111-111111111111', -- Unique character ID
|
|
'00000000-0000-0000-0000-000000000000', -- System user ID
|
|
'Finia',
|
|
'Eine Mutter weibliche Füchsin...',
|
|
'A wise mother fox with red-golden fur...',
|
|
-- ... other fields
|
|
);
|
|
```
|
|
|
|
### Row Level Security (RLS)
|
|
|
|
The database uses RLS policies to allow all authenticated users to read system characters while protecting them from modification:
|
|
|
|
**Read Access Policy:**
|
|
|
|
```sql
|
|
CREATE POLICY "Users can view their own characters and system characters"
|
|
ON public.characters
|
|
FOR SELECT
|
|
USING (
|
|
user_id = current_setting('request.jwt.claims', true)::json->>'user_id'
|
|
OR user_id = '00000000-0000-0000-0000-000000000000'
|
|
);
|
|
```
|
|
|
|
**Write Protection:**
|
|
System characters are protected from updates and deletes by the existing ownership checks in the UPDATE and DELETE policies.
|
|
|
|
### Backend Validation
|
|
|
|
The backend validates access to characters in multiple places. Each validation point checks both ownership and system character status:
|
|
|
|
**Character Controller** (`backend/src/character/character.controller.ts`):
|
|
|
|
```typescript
|
|
const SYSTEM_USER_ID = '00000000-0000-0000-0000-000000000000';
|
|
const isSystemCharacter = character.user_id === SYSTEM_USER_ID;
|
|
const isOwnCharacter = character.user_id === user.sub;
|
|
|
|
if (!isSystemCharacter && !isOwnCharacter) {
|
|
return { error: 'Character not found or access denied' };
|
|
}
|
|
```
|
|
|
|
This pattern is applied in:
|
|
|
|
- `getCharacterById()` - GET /character/:id
|
|
- `updateCharacter()` - PUT /character/:id
|
|
- `deleteCharacter()` - DELETE /character/:id
|
|
|
|
**Story Creation Service** (`backend/src/story/services/story-creation.service.ts`):
|
|
|
|
```typescript
|
|
const SYSTEM_USER_ID = '00000000-0000-0000-0000-000000000000';
|
|
const isSystemCharacter = character.user_id === SYSTEM_USER_ID;
|
|
const isOwnCharacter = character.user_id === userId;
|
|
|
|
if (!isSystemCharacter && !isOwnCharacter) {
|
|
throw new Error('Character not found or does not belong to user');
|
|
}
|
|
```
|
|
|
|
### Frontend UI
|
|
|
|
The mobile app detects system characters and hides edit/share functionality:
|
|
|
|
**Character Detail Screen** (`mobile/app/character/[id].tsx`):
|
|
|
|
```typescript
|
|
// Check if this is a system character (read-only)
|
|
const SYSTEM_USER_ID = '00000000-0000-0000-0000-000000000000';
|
|
const isSystemCharacter = character?.user_id === SYSTEM_USER_ID;
|
|
|
|
// Hide edit/share buttons for system characters
|
|
{!isEditing && !isSystemCharacter && (
|
|
<ScrollView>
|
|
<ShareCharacterButton character={character} />
|
|
<TouchableOpacity onPress={() => setIsEditing(!isEditing)}>
|
|
<AntDesign name="edit" />
|
|
<Text>Bearbeiten</Text>
|
|
</TouchableOpacity>
|
|
</ScrollView>
|
|
)}
|
|
```
|
|
|
|
## Adding New System Characters
|
|
|
|
To add a new system character to the platform:
|
|
|
|
### 1. Prepare Character Assets
|
|
|
|
Upload the character image to Supabase Storage in the system user's folder:
|
|
|
|
```
|
|
bucket: user-uploads
|
|
path: 00000000-0000-0000-0000-000000000000/characters/[filename]
|
|
```
|
|
|
|
Example URL:
|
|
|
|
```
|
|
https://dyywxrmonxoiojsjmymc.supabase.co/storage/v1/object/public/user-uploads/00000000-0000-0000-0000-000000000000/characters/1762453771144-cmngxj-large.webp
|
|
```
|
|
|
|
### 2. Create Database Migration
|
|
|
|
Create a migration to insert the character record:
|
|
|
|
```sql
|
|
-- Add new system character
|
|
INSERT INTO public.characters (
|
|
id, -- Generate new UUID
|
|
user_id, -- Always '00000000-0000-0000-0000-000000000000'
|
|
name, -- Character name
|
|
user_description, -- User-facing description (German)
|
|
character_description, -- AI prompt description (English)
|
|
character_description_prompt, -- Enhanced prompt for consistency
|
|
image_url, -- Supabase Storage URL
|
|
is_animal, -- true/false
|
|
animal_type, -- Animal type if applicable
|
|
archived, -- false
|
|
created_at, -- NOW()
|
|
updated_at, -- NOW()
|
|
is_published, -- true
|
|
sharing_preference, -- 'public'
|
|
images_data -- JSON array of image variations
|
|
) VALUES (
|
|
gen_random_uuid(), -- Or specific UUID
|
|
'00000000-0000-0000-0000-000000000000',
|
|
'Character Name',
|
|
'Deutsche Beschreibung...',
|
|
'English AI description...',
|
|
'Detailed prompt for image generation...',
|
|
'https://...',
|
|
true,
|
|
'animal_type',
|
|
false,
|
|
NOW(),
|
|
NOW(),
|
|
true,
|
|
'public',
|
|
'[{"description": "...", "imageUrl": "..."}]'::jsonb
|
|
) ON CONFLICT (id) DO UPDATE SET
|
|
name = EXCLUDED.name,
|
|
user_description = EXCLUDED.user_description,
|
|
-- ... update other fields as needed
|
|
;
|
|
```
|
|
|
|
### 3. Apply Migration
|
|
|
|
Use Supabase MCP tools to apply the migration to both development and production databases:
|
|
|
|
```typescript
|
|
// Example using MCP tools
|
|
await mcp__supabase__apply_migration({
|
|
project_id: 'your-project-id',
|
|
name: 'add_[character_name]_system_character',
|
|
query: 'INSERT INTO ...',
|
|
});
|
|
```
|
|
|
|
## Current System Characters
|
|
|
|
### Finia - The Wise Fox
|
|
|
|
- **ID**: `11111111-1111-1111-1111-111111111111`
|
|
- **Type**: Female Fox (Füchsin)
|
|
- **Description**: A wise mother fox with red-golden fur and a caring nature
|
|
- **Purpose**: Default character for first-time users to create stories immediately
|
|
- **Languages**: German (user_description) and English (character_description)
|
|
|
|
**Image Location**:
|
|
|
|
```
|
|
https://dyywxrmonxoiojsjmymc.supabase.co/storage/v1/object/public/user-uploads/00000000-0000-0000-0000-000000000000/characters/1762453771144-cmngxj-large.webp
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### Character Selection
|
|
|
|
- Choose characters with broad appeal for diverse story types
|
|
- Ensure high-quality, consistent character images
|
|
- Provide both German and English descriptions
|
|
- Make characters suitable for children's stories
|
|
|
|
### Naming Conventions
|
|
|
|
- Use descriptive, memorable names
|
|
- Consider cultural appropriateness
|
|
- Ensure uniqueness from user-created characters
|
|
|
|
### Image Requirements
|
|
|
|
- High resolution (recommended: 1024x1024 or higher)
|
|
- Consistent art style across system characters
|
|
- Child-appropriate content
|
|
- Clear, recognizable character features
|
|
|
|
### Database Management
|
|
|
|
- Always use migrations for changes
|
|
- Test in development before production
|
|
- Include rollback procedures in migrations
|
|
- Document character purposes and target audiences
|
|
|
|
## Troubleshooting
|
|
|
|
### Character Not Visible to Users
|
|
|
|
**Check RLS Policies:**
|
|
|
|
```sql
|
|
-- Verify the SELECT policy exists
|
|
SELECT * FROM pg_policies
|
|
WHERE tablename = 'characters'
|
|
AND policyname LIKE '%system%';
|
|
```
|
|
|
|
**Verify User ID:**
|
|
|
|
```sql
|
|
-- Ensure character has correct system user_id
|
|
SELECT id, name, user_id
|
|
FROM characters
|
|
WHERE user_id = '00000000-0000-0000-0000-000000000000';
|
|
```
|
|
|
|
### Backend Rejecting System Characters
|
|
|
|
Ensure all character validation logic includes system character checks:
|
|
|
|
```typescript
|
|
const SYSTEM_USER_ID = '00000000-0000-0000-0000-000000000000';
|
|
const isSystemCharacter = character.user_id === SYSTEM_USER_ID;
|
|
const isOwnCharacter = character.user_id === user.sub;
|
|
|
|
if (!isSystemCharacter && !isOwnCharacter) {
|
|
// Reject access
|
|
}
|
|
```
|
|
|
|
Check these files:
|
|
|
|
- `backend/src/character/character.controller.ts` (GET, PUT, DELETE)
|
|
- `backend/src/story/services/story-creation.service.ts` (character validation)
|
|
|
|
### Edit/Share Buttons Still Showing
|
|
|
|
Verify frontend detection logic in `mobile/app/character/[id].tsx`:
|
|
|
|
```typescript
|
|
const SYSTEM_USER_ID = '00000000-0000-0000-0000-000000000000';
|
|
const isSystemCharacter = character?.user_id === SYSTEM_USER_ID;
|
|
```
|
|
|
|
Ensure the character object includes the `user_id` field from the API response.
|
|
|
|
## Related Files
|
|
|
|
### Backend
|
|
|
|
- `backend/src/character/character.controller.ts` - Character CRUD endpoints
|
|
- `backend/src/story/services/story-creation.service.ts` - Story creation validation
|
|
- `backend/src/core/services/supabase-data.service.ts` - Database operations
|
|
|
|
### Frontend
|
|
|
|
- `mobile/app/character/[id].tsx` - Character detail screen
|
|
- `mobile/src/utils/dataService.ts` - Character data service (removed hardcoded logic)
|
|
|
|
### Database
|
|
|
|
- Migration: `add_finnia_system_character_fixed`
|
|
- Migration: `cleanup_conflicting_character_policies`
|
|
|
|
### Documentation
|
|
|
|
- `CLAUDE.md` - Main project documentation
|
|
- This file - System characters documentation
|