mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:01:09 +02:00
Applied formatting to 1487+ files using pnpm format:write - TypeScript/JavaScript files - Svelte components - Astro pages - JSON configs - Markdown docs 13 files still need manual review (Astro JSX comments)
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