managarten/apps/maerchenzauber/MANA_CORE_ARCHITECTURE.md
Wuesteon d36b321d9d style: auto-format codebase with Prettier
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)
2025-11-27 18:33:16 +01:00

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

  1. 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,
        });
      }
    };
    
  2. 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;
    }
    
  3. 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

  1. 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
  2. Credit Management

    • Balance tracking per user
    • Pre-flight validation
    • Transaction recording
    • Operation type tracking
    • App-level tracking
    • Metadata support
  3. 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

  1. Application Logic

    • Define operation types
    • Set credit costs
    • Implement business logic
    • Handle errors
  2. Frontend Integration

    • Token storage
    • API client with auto-refresh
    • Error handling UI
    • Purchase flow (if needed)
  3. 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

  1. Always validate credits BEFORE expensive operations
  2. Consume credits AFTER successful operations
  3. Handle InsufficientCreditsException gracefully
  4. Log all credit transactions for audit

Next Steps

After understanding the architecture:

  1. Review the Integration Guide: See MANA_CORE_INTEGRATION_GUIDE.md
  2. Follow the Checklist: Use MANA_CORE_INTEGRATION_CHECKLIST.md
  3. Study the Code: Examine Storyteller's implementation
  4. Test Thoroughly: Ensure all flows work correctly
  5. Monitor in Production: Track usage and errors

Questions?