mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:41:09 +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>
30 KiB
30 KiB
Mana Core Architecture in Storyteller
This document explains the architecture and data flow of the Mana Core integration in the Storyteller project.
System Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Frontend Layer │
│ (React Native + Expo) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Auth Service │ │ API Client │ │Token Manager │ │
│ │ │ │ │ │ │ │
│ │ • Sign In │ │ • fetchWith │ │ • getValid │ │
│ │ • Sign Up │ │ Auth() │ │ Token() │ │
│ │ • Sign Out │ │ • Auto │ │ • refresh │ │
│ │ • Device │ │ Refresh │ │ Token() │ │
│ │ Info │ │ • Error │ │ • Token │ │
│ │ │ │ Handling │ │ Storage │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
└─────────┼──────────────────┼──────────────────┼────────────────┘
│ │ │
│ HTTP/HTTPS │ Bearer Token │ Refresh
│ Requests │ in Headers │ Flow
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ Backend Layer │
│ (NestJS + TypeScript) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Mana Core NestJS Integration │ │
│ ├───────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ AuthGuard │ │@CurrentUser()│ │ Credit │ │ │
│ │ │ │ │ │ │ Client │ │ │
│ │ │ • Validate │ │ • Extract │ │ Service │ │ │
│ │ │ JWT │ │ User ID │ │ │ │ │
│ │ │ • Check │ │ • Extract │ │ • validate │ │ │
│ │ │ Expiry │ │ Email │ │ Credits() │ │ │
│ │ │ • Inject │ │ • Extract │ │ • consume │ │ │
│ │ │ User │ │ Role │ │ Credits() │ │ │
│ │ │ │ │ │ │ • get │ │ │
│ │ │ │ │ │ │ Balance() │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────┼──────────────────────────────────┐ │
│ │ Application Controllers & Services │ │
│ ├────────────────────────┼──────────────────────────────────┤ │
│ │ │ │ │
│ │ CharacterController │ StoryController │ │
│ │ • generateCharacter │ • createStory │ │
│ │ • getCharacters │ • getStories │ │
│ │ • updateCharacter │ • updateStory │ │
│ │ • deleteCharacter │ • deleteStory │ │
│ │ │ │ │
│ │ SettingsController │ CreatorsController │ │
│ │ • getUserSettings │ • getCreators │ │
│ │ • updateSettings │ • getLanguages │ │
│ │ │ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
└───────────────────────────┼─────────────────────────────────────┘
│
│ API Requests
│ (Auth Validation,
│ Credit Operations)
▼
┌─────────────────────────────────────────────────────────────────┐
│ Mana Core Service │
│ (Authentication & Credits) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ Authentication │ │ Credit Management │ │
│ │ │ │ │ │
│ │ • User Management │ │ • Balance Tracking │ │
│ │ • Token Generation │ │ • Transaction Log │ │
│ │ • Token Validation │ │ • Operation Types │ │
│ │ • Token Refresh │ │ • App-Level Track │ │
│ │ • Device Management │ │ • Space Credits │ │
│ │ • OAuth Providers │ │ • Billing History │ │
│ │ │ │ │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Authentication Flow
Sign-In Flow
┌────────┐ ┌────────┐ ┌────────┐
│ Mobile │ │Backend │ │ Mana │
│ App │ │ (Nest) │ │ Core │
└───┬────┘ └───┬────┘ └───┬────┘
│ │ │
│ 1. Sign In Request │ │
│ POST /auth/signin │ │
│ { email, password, │ │
│ deviceInfo } │ │
├────────────────────────────►│ │
│ │ │
│ │ 2. Forward to Mana │
│ │ POST /auth/signin │
│ ├────────────────────────────►│
│ │ │
│ │ │ 3. Validate
│ │ │ Credentials
│ │ │
│ │ 4. Return Tokens │
│ │ { appToken, refreshToken, │
│ │ user, device } │
│ │◄────────────────────────────┤
│ │ │
│ 5. Return to Client │ │
│ { appToken, refreshToken } │ │
│◄────────────────────────────┤ │
│ │ │
│ 6. Store Tokens │ │
│ in SecureStorage │ │
│ │ │
Token Refresh Flow
┌────────┐ ┌────────┐ ┌────────┐
│ Mobile │ │Backend │ │ Mana │
│ App │ │ (Nest) │ │ Core │
└───┬────┘ └───┬────┘ └───┬────┘
│ │ │
│ 1. API Request │ │
│ with Expired Token │ │
├────────────────────────────►│ │
│ │ │
│ │ 2. Validate Token │
│ │ (Expired!) │
│ │ │
│ 3. 401 Unauthorized │ │
│◄────────────────────────────┤ │
│ │ │
│ 4. Token Refresh Request │ │
│ POST /auth/refresh │ │
│ { refreshToken, deviceInfo }│ │
├────────────────────────────►│ │
│ │ │
│ │ 5. Forward to Mana │
│ ├────────────────────────────►│
│ │ │
│ │ │ 6. Validate
│ │ │ Refresh
│ │ │ Token
│ │ │
│ │ 7. New Tokens │
│ │◄────────────────────────────┤
│ │ │
│ 8. Return New Tokens │ │
│◄────────────────────────────┤ │
│ │ │
│ 9. Store New Tokens │ │
│ │ │
│ 10. Retry Original Request │ │
│ with New Token │ │
├────────────────────────────►│ │
│ │ │
│ 11. Success Response │ │
│◄────────────────────────────┤ │
Protected Route Flow
With AuthGuard
┌────────┐ ┌────────┐ ┌────────┐
│ Mobile │ │Backend │ │ Mana │
│ App │ │ (Nest) │ │ Core │
└───┬────┘ └───┬────┘ └───┬────┘
│ │ │
│ 1. GET /character │ │
│ Authorization: Bearer │ │
│ eyJhbGc... │ │
├────────────────────────────►│ │
│ │ │
│ │ 2. AuthGuard │
│ │ intercepts │
│ │ │
│ │ 3. Validate Token │
│ ├────────────────────────────►│
│ │ │
│ │ 4. Token Valid │
│ │ + User Payload │
│ │◄────────────────────────────┤
│ │ │
│ │ 5. Inject user into │
│ │ request object │
│ │ │
│ │ 6. Execute Controller │
│ │ Method │
│ │ @CurrentUser() │
│ │ extracts user │
│ │ │
│ 7. Response with Data │ │
│◄────────────────────────────┤ │
│ │ │
Credit Management Flow
Character Creation (20 Credits)
┌────────┐ ┌────────┐ ┌────────┐
│ Mobile │ │Backend │ │ Mana │
│ App │ │ (Nest) │ │ Core │
└───┬────┘ └───┬────┘ └───┬────┘
│ │ │
│ 1. Create Character Request │ │
│ POST /character/generate │ │
│ { name, description } │ │
├────────────────────────────►│ │
│ │ │
│ │ 2. Pre-flight Check │
│ │ validateCredits() │
│ │ userId, "character_ │
│ │ creation", 20 │
│ ├────────────────────────────►│
│ │ │
│ │ │ 3. Check
│ │ │ Balance
│ │ │
│ │ 4. Validation Result │
│ │ { hasCredits: true, │
│ │ availableCredits: 100 } │
│ │◄────────────────────────────┤
│ │ │
│ │ 5. If hasCredits = false │
│ │ Return error │
│◄────────────────────────────┤ │
│ │ │
│ 6. Show "Buy Credits" Modal │ │
│ │ │
│ │ 7. If hasCredits = true │
│ │ Proceed with creation │
│ │ • Generate images │
│ │ • Store in database │
│ │ │
│ │ 8. Consume Credits │
│ │ consumeCredits() │
│ │ userId, "character_ │
│ │ creation", 20, │
│ │ "Created: Cat" │
│ ├────────────────────────────►│
│ │ │
│ │ │ 9. Deduct
│ │ │ Credits
│ │ │
│ │ 10. Transaction Receipt │
│ │ { transactionId, │
│ │ remainingBalance: 80 } │
│ │◄────────────────────────────┤
│ │ │
│ 11. Success Response │ │
│ { data: { characterId, │ │
│ images, ... } } │ │
│◄────────────────────────────┤ │
│ │ │
Story Creation (100 Credits)
Same flow as character creation, but with:
- operationType: "story_creation"
- amount: 100
- More expensive due to:
* Story text generation
* Multiple image generations (10 pages)
* Translation to German
Credit Operation Types in Storyteller
// Defined in Storyteller
type StorytellerOperations =
| 'character_creation' // 20 credits
| 'story_creation'; // 100 credits
// Credit Costs
const CREDIT_COSTS = {
character_creation: 20, // 3 image variants
story_creation: 100, // 10-page story with images + translation
};
Data Flow: Complete Example
Creating a Story with Credits
-
Frontend: User fills story form
const createStory = async () => { const response = await fetchWithAuth('/story', { method: 'POST', body: JSON.stringify({ characters: [characterId], storyDescription: 'A magical forest adventure', authorId: 'author-1', illustratorId: 'illustrator-1', }), }); const data = await response.json(); if (data.error === 'insufficient_credits') { navigation.navigate('PurchaseCredits', { required: 100, available: data.availableCredits, }); } }; -
Backend Controller: Story creation endpoint
@Post() @UseGuards(AuthGuard) async createStory( @Body() dto: CreateStoryDto, @CurrentUser() user: JwtPayload, ) { // Pre-flight check const validation = await this.creditClient.validateCredits( user.sub, 'story_creation', 100, ); if (!validation.hasCredits) { throw new BadRequestException({ error: 'insufficient_credits', requiredCredits: 100, availableCredits: validation.availableCredits, }); } // Create story const result = await this.storyService.createStory({ userId: user.sub, characterId: dto.characters[0], storyDescription: dto.storyDescription, }); // Consume credits await this.creditClient.consumeCredits( user.sub, 'story_creation', 100, `Created story: ${result.storyData.title}`, { storyId: result.storyData.id }, ); return result; } -
Mana Core: Processes credit operations
- Validates user has sufficient credits
- Deducts credits from user's balance
- Records transaction in ledger
- Returns transaction ID and new balance
- Tracks operation by app ID
Security Architecture
Token Security
┌─────────────────────────────────────────────────────────┐
│ Token Structure │
├─────────────────────────────────────────────────────────┤
│ │
│ Access Token (appToken): │
│ • JWT format │
│ • Short-lived (15 minutes) │
│ • Contains: { sub, email, role, exp, iat } │
│ • Used for API authentication │
│ • Stored in SecureStorage (mobile) │
│ │
│ Refresh Token: │
│ • Long-lived (30 days) │
│ • Used to get new access token │
│ • Single-use (rotated on refresh) │
│ • Device-specific │
│ • Stored in SecureStorage (mobile) │
│ │
└─────────────────────────────────────────────────────────┘
AuthGuard Protection
// All routes under @UseGuards(AuthGuard) are protected:
✅ Validates JWT signature
✅ Checks token expiration
✅ Extracts user payload
✅ Injects user into request
✅ Returns 401 if invalid
// Usage:
@Controller('character')
@UseGuards(AuthGuard) // ← Protects all routes in controller
export class CharacterController {
@Get()
getCharacters(@CurrentUser() user: JwtPayload) {
// user is automatically available here
}
}
Database Integration (Supabase RLS)
Row Level Security with JWT
Storyteller uses Supabase with Row Level Security (RLS). The @UserToken() decorator extracts the raw JWT for RLS:
@Get()
async getCharacters(
@CurrentUser() user: JwtPayload, // Validated user
@UserToken() token: string, // Raw JWT for RLS
) {
// Pass token to Supabase client
const characters = await this.supabase
.from('characters')
.select('*')
.eq('user_id', user.sub)
.setAuth(token); // RLS enforces user_id match
return { data: characters };
}
Why Both Decorators?
@CurrentUser(): Validated user data for business logic@UserToken(): Raw JWT for database RLS enforcement
Environment Configuration
Backend .env
# Mana Core (Required)
MANA_SERVICE_URL=https://mana-core-middleware-111768794939.europe-west3.run.app
APP_ID=8d2f5ddb-e251-4b3b-8802-84022a7ac77f
MANA_SUPABASE_SECRET_KEY=your-service-key
# Application
NODE_ENV=development
PORT=3002
# Database (Supabase)
MAERCHENZAUBER_SUPABASE_URL=your-supabase-url
MAERCHENZAUBER_SUPABASE_ANON_KEY=your-anon-key
Frontend .env
# Backend URL
EXPO_PUBLIC_STORYTELLER_BACKEND_URL=http://localhost:3002
# For production:
# EXPO_PUBLIC_STORYTELLER_BACKEND_URL=https://your-api.com
Key Takeaways
What Mana Core Provides
-
Authentication System
- ✅ Email/password sign-in/sign-up
- ✅ Google OAuth integration
- ✅ Apple Sign-in integration
- ✅ JWT token generation and validation
- ✅ Token refresh mechanism
- ✅ Multi-device management
-
Credit Management
- ✅ Balance tracking per user
- ✅ Pre-flight validation
- ✅ Transaction recording
- ✅ Operation type tracking
- ✅ App-level tracking
- ✅ Metadata support
-
Developer Experience
- ✅ Simple module configuration
- ✅ Guards for route protection
- ✅ Decorators for data extraction
- ✅ TypeScript support
- ✅ Error handling utilities
- ✅ Debug logging
What You Need to Implement
-
Application Logic
- Define operation types
- Set credit costs
- Implement business logic
- Handle errors
-
Frontend Integration
- Token storage
- API client with auto-refresh
- Error handling UI
- Purchase flow (if needed)
-
Testing
- Unit tests with mocked services
- Integration tests
- End-to-end flows
Performance Considerations
Token Caching
- Frontend caches tokens in SecureStorage
- Token validation is fast (local check + remote if needed)
- Refresh only when needed (5-minute buffer)
Credit Operations
- Pre-flight validation is lightweight
- Consumption happens after success
- No blocking during operations
Best Practices
- Always validate credits BEFORE expensive operations
- Consume credits AFTER successful operations
- Handle
InsufficientCreditsExceptiongracefully - Log all credit transactions for audit
Next Steps
After understanding the architecture:
- Review the Integration Guide: See
MANA_CORE_INTEGRATION_GUIDE.md - Follow the Checklist: Use
MANA_CORE_INTEGRATION_CHECKLIST.md - Study the Code: Examine Storyteller's implementation
- Test Thoroughly: Ensure all flows work correctly
- Monitor in Production: Track usage and errors
Questions?
- Full Guide:
MANA_CORE_INTEGRATION_GUIDE.md - Checklist:
MANA_CORE_INTEGRATION_CHECKLIST.md - Mana Docs: https://docs.mana-core.com
- GitHub: https://github.com/Memo-2023/mana-core-nestjs-package