From ab387b9b3dbe535464eea502d9374aa5fff5e5d6 Mon Sep 17 00:00:00 2001 From: Till JS Date: Tue, 31 Mar 2026 16:52:25 +0200 Subject: [PATCH] chore: remove all NestJS backend references, replace with Hono/Bun MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Delete nestjs-backend.md guideline (replaced by hono-server.md) - Delete Dockerfile.nestjs-base and Dockerfile.nestjs templates - Delete stale BACKEND_ARCHITECTURE.md doc (NestJS-era, obsolete) - Update CLAUDE.md, GUIDELINES.md, authentication.md to Hono/Bun first - Update all app CLAUDE.md files: backend/ → server/, NestJS → Hono+Bun - Update all app package.json files: @*/backend → @*/server - Update docs: LOCAL_DEVELOPMENT, PORT_SCHEMA, ENVIRONMENT_VARIABLES, DATABASE_MIGRATIONS, MAC_MINI_SERVER, PROJECT_OVERVIEW - Update scripts: generate-env.mjs, setup-databases.sh, build-app.sh - Update CI/CD: cd-macmini.yml backend → server paths - Update Astro docs site: @chat/backend → @chat/server Co-Authored-By: Claude Opus 4.6 --- .claude/GUIDELINES.md | 17 +- .claude/guidelines/authentication.md | 113 +-- .claude/guidelines/code-style.md | 7 +- .claude/guidelines/hono-server.md | 137 +++ .claude/guidelines/nestjs-backend.md | 659 ------------ .claude/guidelines/testing.md | 2 +- .github/workflows/cd-macmini.yml | 18 - CLAUDE.md | 144 +-- apps/calendar/CLAUDE.md | 23 +- apps/calendar/package.json | 8 +- apps/chat/CLAUDE.md | 27 +- apps/contacts/CLAUDE.md | 7 +- apps/contacts/package.json | 8 +- apps/context/CLAUDE.md | 6 +- apps/context/package.json | 2 +- .../docs/development/database-migrations.mdx | 20 +- .../docs/development/local-development.mdx | 2 +- .../src/content/docs/development/testing.mdx | 6 +- .../getting-started/project-structure.mdx | 2 +- apps/docs/src/content/docs/projects/chat.mdx | 18 +- apps/mukke/CLAUDE.md | 14 +- apps/nutriphi/CLAUDE.md | 8 +- apps/nutriphi/package.json | 14 +- apps/picture/CLAUDE.md | 4 +- apps/planta/CLAUDE.md | 6 +- apps/planta/package.json | 8 +- apps/storage/CLAUDE.md | 2 +- apps/todo/CLAUDE.md | 6 +- apps/todo/package.json | 8 +- apps/traces/CLAUDE.md | 4 +- docker/Dockerfile.nestjs-base | 58 -- docker/templates/Dockerfile.nestjs | 89 -- docs/BACKEND_ARCHITECTURE.md | 945 ------------------ docs/DATABASE_MIGRATIONS.md | 4 +- docs/ENVIRONMENT_VARIABLES.md | 42 +- docs/LOCAL_DEVELOPMENT.md | 296 ++++-- docs/MAC_MINI_SERVER.md | 7 +- docs/PORT_SCHEMA.md | 40 +- docs/PROJECT_OVERVIEW.md | 2 +- scripts/generate-env.mjs | 108 +- scripts/mac-mini/build-app.sh | 5 - scripts/setup-databases.sh | 53 +- scripts/validate-dockerfiles.mjs | 47 +- 43 files changed, 598 insertions(+), 2398 deletions(-) create mode 100644 .claude/guidelines/hono-server.md delete mode 100644 .claude/guidelines/nestjs-backend.md delete mode 100644 docker/Dockerfile.nestjs-base delete mode 100644 docker/templates/Dockerfile.nestjs delete mode 100644 docs/BACKEND_ARCHITECTURE.md diff --git a/.claude/GUIDELINES.md b/.claude/GUIDELINES.md index 1c0ccbaa0..68daf8570 100644 --- a/.claude/GUIDELINES.md +++ b/.claude/GUIDELINES.md @@ -9,7 +9,7 @@ This directory contains comprehensive guidelines for working in the Mana Univers | [Code Style](./guidelines/code-style.md) | Formatting, naming conventions, linting rules | | [Database](./guidelines/database.md) | Drizzle ORM patterns, schema design, migrations | | [Testing](./guidelines/testing.md) | Jest/Vitest patterns, mock factories, coverage | -| [NestJS Backend](./guidelines/nestjs-backend.md) | Controllers, services, DTOs, modules | +| [Hono Server](./guidelines/hono-server.md) | Compute servers (Hono + Bun) | | [Error Handling](./guidelines/error-handling.md) | Go-style errors, error codes, Result types | | [SvelteKit Web](./guidelines/sveltekit-web.md) | Svelte 5 runes, stores, routing | | [Expo Mobile](./guidelines/expo-mobile.md) | React Native, NativeWind, navigation | @@ -44,7 +44,7 @@ This directory contains comprehensive guidelines for working in the Mana Univers |-------|------------|-------| | **Package Manager** | pnpm 9.15+ | Workspace monorepo | | **Build System** | Turborepo | Parallel task execution | -| **Backend** | NestJS 10-11 | TypeScript, Drizzle ORM | +| **Server** | Hono + Bun | TypeScript, Drizzle ORM | | **Web** | SvelteKit 2 + Svelte 5 | Runes mode only | | **Mobile** | Expo SDK 52-54 | React Native, NativeWind | | **Database** | PostgreSQL | Via Drizzle ORM | @@ -62,15 +62,15 @@ manacore-monorepo/ ├── apps/ # Product applications │ └── {project}/ │ ├── apps/ -│ │ ├── backend/ # NestJS API +│ │ ├── server/ # Hono/Bun compute server │ │ ├── web/ # SvelteKit web │ │ ├── mobile/ # Expo app │ │ └── landing/ # Astro landing │ └── packages/ # Project-specific shared ├── packages/ # Monorepo-wide shared │ ├── shared-errors/ # Error codes & Result types -│ ├── shared-nestjs-auth/ # NestJS auth guards │ ├── shared-auth/ # Client auth service +│ ├── local-store/ # Local-first data layer (Dexie.js + sync) │ └── ... ├── services/ # Standalone microservices │ └── mana-core-auth/ # Central auth service @@ -114,7 +114,8 @@ See [Error Handling](./guidelines/error-handling.md) for complete details. # Development pnpm install # Install dependencies pnpm {project}:dev # Start project (all apps) -pnpm dev:{project}:backend # Start just backend +pnpm dev:{project}:server # Start just Hono server +pnpm dev:{project}:local # Start sync + server + web (no auth) pnpm dev:{project}:web # Start just web # Quality @@ -122,7 +123,7 @@ pnpm type-check # TypeScript validation pnpm format # Format code pnpm test # Run tests -# Database -pnpm {project}:db:push # Push schema changes -pnpm {project}:db:studio # Open Drizzle Studio +# Database (for apps with Drizzle in server) +pnpm --filter @{project}/server db:push # Push schema changes +pnpm --filter @{project}/server db:studio # Open Drizzle Studio ``` diff --git a/.claude/guidelines/authentication.md b/.claude/guidelines/authentication.md index 483706136..cf0bcc184 100644 --- a/.claude/guidelines/authentication.md +++ b/.claude/guidelines/authentication.md @@ -8,8 +8,8 @@ All authentication is handled by **Mana Core Auth**, a centralized authenticatio ``` ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐ -│ Web/Mobile │────>│ Backend API │────>│ mana-core-auth │ -│ Client │ │ (NestJS) │ │ (port 3001) │ +│ Web/Mobile │────>│ Compute Server │────>│ mana-auth │ +│ Client │ │ (Hono/Bun) │ │ (port 3001) │ └─────────────────┘ └─────────────────┘ └──────────────────┘ │ │ │ │ 1. Login │ │ @@ -85,101 +85,48 @@ Always use `text` type for `user_id` columns in all database schemas. | Package | Purpose | Use Case | |---------|---------|----------| -| `@manacore/shared-nestjs-auth` | NestJS guards/decorators | Backend APIs | -| `@mana-core/nestjs-integration` | Auth + Credits integration | Backends with credits | +| `@manacore/shared-hono` | Hono auth middleware + helpers | All compute servers (Hono/Bun) | | `@manacore/shared-auth` | Client auth service | Web/Mobile apps | +| `@mana-core/nestjs-integration` | Auth + Credits for NestJS | `@arcade/backend` only | -## Backend Integration +## Server Integration (Hono/Bun) -### Option 1: Simple Auth Only - -Use `@manacore/shared-nestjs-auth` for JWT validation: +All compute servers use `@manacore/shared-hono`: ```typescript -// app.module.ts -import { Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; +import { Hono } from 'hono'; +import { authMiddleware, healthRoute, errorHandler, notFoundHandler } from '@manacore/shared-hono'; -@Module({ - imports: [ - ConfigModule.forRoot({ isGlobal: true }), - // No auth module needed - guards handle it - ], -}) -export class AppModule {} +const app = new Hono(); +app.onError(errorHandler); +app.notFound(notFoundHandler); +app.route('/health', healthRoute('my-server')); + +// Protect all /api/* routes +app.use('/api/*', authMiddleware()); + +// Access user in route handlers +app.get('/api/v1/data', (c) => { + const userId = c.get('userId'); // Better Auth user ID (not UUID) + const email = c.get('userEmail'); + return c.json({ userId }); +}); ``` -```typescript -// file.controller.ts -import { Controller, Get, UseGuards } from '@nestjs/common'; -import { JwtAuthGuard, CurrentUser, CurrentUserData } from '@manacore/shared-nestjs-auth'; +### NestJS (arcade only) -@Controller('files') -@UseGuards(JwtAuthGuard) // Apply to all routes -export class FileController { - @Get() - async listFiles(@CurrentUser() user: CurrentUserData) { - // user.userId, user.email, user.role available - return this.fileService.findAll(user.userId); - } -} -``` - -### Option 2: Auth + Credits - -Use `@mana-core/nestjs-integration` for full integration: +`@arcade/backend` still uses NestJS with `@mana-core/nestjs-integration`: ```typescript -// app.module.ts -import { Module } from '@nestjs/common'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { ManaCoreModule } from '@mana-core/nestjs-integration'; - -@Module({ - imports: [ - ConfigModule.forRoot({ isGlobal: true }), - ManaCoreModule.forRootAsync({ - imports: [ConfigModule], - useFactory: (config: ConfigService) => ({ - appId: config.get('APP_ID'), - serviceKey: config.get('MANA_CORE_SERVICE_KEY'), - debug: config.get('NODE_ENV') === 'development', - }), - inject: [ConfigService], - }), - ], -}) -export class AppModule {} -``` - -```typescript -// generation.controller.ts -import { Controller, Post, UseGuards, Body } from '@nestjs/common'; import { AuthGuard } from '@mana-core/nestjs-integration/guards'; import { CurrentUser } from '@mana-core/nestjs-integration/decorators'; -import { CreditClientService } from '@mana-core/nestjs-integration'; -@Controller('generations') +@Controller('api') @UseGuards(AuthGuard) -export class GenerationController { - constructor(private creditClient: CreditClientService) {} - - @Post() - async generate(@CurrentUser() user: any, @Body() dto: GenerateDto) { - // Check and consume credits - const result = await this.creditClient.consumeCredits( - user.sub, - 'ai_generation', - 10, - 'AI image generation' - ); - - if (!result.ok) { - throw new AppException(result.error); - } - - // Proceed with generation - return this.generationService.generate(user.sub, dto); +export class ApiController { + @Get('data') + getData(@CurrentUser() user: any) { + return this.service.findAll(user.sub); } } ``` @@ -187,7 +134,7 @@ export class GenerationController { ## Environment Variables ```env -# Required for all backends +# Required for all servers MANA_CORE_AUTH_URL=http://localhost:3001 # Development bypass (optional) diff --git a/.claude/guidelines/code-style.md b/.claude/guidelines/code-style.md index 0c4a62085..96c64f848 100644 --- a/.claude/guidelines/code-style.md +++ b/.claude/guidelines/code-style.md @@ -236,13 +236,12 @@ All SvelteKit web apps use **Phosphor icons** via `@manacore/shared-icons` (re-e - **Ideal**: 10-25 lines - Extract complex logic into helper functions -### Module Structure (NestJS) +### Module Structure (Hono/Bun Server) ``` feature/ -├── feature.controller.ts # HTTP layer +├── feature.routes.ts # HTTP routes ├── feature.service.ts # Business logic -├── feature.module.ts # DI configuration ├── feature.spec.ts # Tests └── dto/ ├── create-feature.dto.ts @@ -340,5 +339,5 @@ pnpm format pnpm format:check # Format specific project -pnpm --filter @chat/backend format +pnpm --filter @chat/server format ``` diff --git a/.claude/guidelines/hono-server.md b/.claude/guidelines/hono-server.md new file mode 100644 index 000000000..00e6fbf23 --- /dev/null +++ b/.claude/guidelines/hono-server.md @@ -0,0 +1,137 @@ +# Hono Server Guidelines + +## Overview + +All app compute servers use Hono + Bun with a lightweight architecture. Servers handle only what can't run client-side: file uploads (S3), AI calls, RRULE expansion, external API integration, etc. All CRUD is handled client-side via local-first (Dexie.js + mana-sync). + +## Project Structure + +``` +apps/{project}/apps/server/ +├── src/ +│ ├── index.ts # Entry point + route mounting +│ ├── routes/ # Route handlers +│ │ ├── {feature}.ts +│ │ └── admin.ts +│ ├── lib/ # Shared utilities +│ └── db/ # Drizzle schema (if needed) +│ ├── schema/ +│ ├── connection.ts +│ └── drizzle.config.ts +├── package.json +└── tsconfig.json +``` + +## Entry Point (index.ts) + +```typescript +import { Hono } from 'hono'; +import { cors } from 'hono/cors'; +import { authMiddleware, healthRoute, errorHandler, notFoundHandler } from '@manacore/shared-hono'; + +const PORT = parseInt(process.env.PORT || '3031', 10); +const CORS_ORIGINS = (process.env.CORS_ORIGINS || 'http://localhost:5173').split(','); + +const app = new Hono(); + +// Standard middleware stack +app.onError(errorHandler); +app.notFound(notFoundHandler); +app.use('*', cors({ origin: CORS_ORIGINS, credentials: true })); +app.route('/health', healthRoute('my-server')); +app.use('/api/*', authMiddleware()); + +// Routes +app.route('/api/v1/compute', computeRoutes); + +export default { port: PORT, fetch: app.fetch }; +``` + +## Shared Hono Package + +Use `@manacore/shared-hono` for consistent middleware across all servers: + +```typescript +import { + authMiddleware, // JWT validation via mana-auth JWKS + healthRoute, // Standard /health endpoint + errorHandler, // Global error handler with logging + notFoundHandler, // 404 handler +} from '@manacore/shared-hono'; +``` + +## Route Handlers + +```typescript +// src/routes/upload.ts +import { Hono } from 'hono'; + +export const uploadRoutes = new Hono(); + +uploadRoutes.post('/avatar', async (c) => { + const userId = c.get('userId'); // Set by authMiddleware + const formData = await c.req.formData(); + const file = formData.get('file') as File | null; + + if (!file) return c.json({ error: 'No file' }, 400); + if (file.size > 5 * 1024 * 1024) return c.json({ error: 'Max 5MB' }, 400); + + // ... handle upload + return c.json({ url: result.url }, 201); +}); +``` + +## Database (Optional) + +Only servers that need their own database use Drizzle. Most apps rely on mana-sync for data persistence. + +**Servers with Drizzle:** chat, todo, moodlit, context, planta, presi, traces, uload, wisekeep, news + +**Servers without Drizzle (mana-sync only):** calendar, contacts, manadeck, mukke, nutriphi, picture, questions, storage + +## Running Servers + +```bash +# Development (with watch) +cd apps/{project}/apps/server && bun run --watch src/index.ts + +# Via root scripts +pnpm dev:{project}:server # Just the server +pnpm dev:{project}:local # sync + server + web (no auth needed) +pnpm dev:{project}:full # auth + sync + server + web +``` + +## When to Add a Server + +Add a Hono server when the app needs: + +- **File operations**: S3 uploads, image processing +- **AI/LLM calls**: Gemini, OpenAI, etc. (API keys can't be client-side) +- **External APIs**: Google Calendar sync, vCard parsing, etc. +- **Heavy compute**: RRULE expansion, PDF generation, etc. +- **Admin endpoints**: GDPR compliance, data export + +Do NOT add a server for pure CRUD — use local-first + mana-sync instead. + +## Environment Variables + +Servers read `.env` from their directory (Bun auto-loads it): + +```env +PORT=3031 +CORS_ORIGINS=http://localhost:5173,http://localhost:5188 +MANA_CORE_AUTH_URL=http://localhost:3001 +DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/myapp # if Drizzle +``` + +## Key Differences from Old NestJS Pattern + +| Aspect | Old (NestJS) | New (Hono + Bun) | +| ----------- | ----------------------- | ------------------------------------- | +| Runtime | Node.js | Bun | +| Framework | NestJS (decorators, DI) | Hono (functional, minimal) | +| CRUD | Server handles all CRUD | Client-side (local-first + mana-sync) | +| Server role | Full backend | Compute-only endpoints | +| Auth | NestJS guards | `@manacore/shared-hono` middleware | +| Startup | `nest start --watch` | `bun run --watch src/index.ts` | +| Config | `ConfigModule` + DI | `process.env` directly | diff --git a/.claude/guidelines/nestjs-backend.md b/.claude/guidelines/nestjs-backend.md deleted file mode 100644 index b00490882..000000000 --- a/.claude/guidelines/nestjs-backend.md +++ /dev/null @@ -1,659 +0,0 @@ -# NestJS Backend Guidelines - -## Overview - -All backend services use NestJS with a consistent architecture. This guide covers controllers, services, DTOs, modules, and integration with the error handling system. - -## Project Structure - -``` -apps/{project}/apps/backend/ -├── src/ -│ ├── main.ts # Bootstrap -│ ├── app.module.ts # Root module -│ ├── db/ -│ │ ├── schema/ # Drizzle schemas -│ │ ├── connection.ts # DB singleton -│ │ ├── database.module.ts # NestJS module -│ │ └── migrations/ # Migration files -│ ├── common/ -│ │ ├── filters/ # Exception filters -│ │ ├── guards/ # Custom guards -│ │ └── decorators/ # Custom decorators -│ ├── health/ -│ │ ├── health.controller.ts -│ │ └── health.module.ts -│ └── {feature}/ -│ ├── {feature}.controller.ts -│ ├── {feature}.service.ts -│ ├── {feature}.module.ts -│ ├── {feature}.spec.ts -│ └── dto/ -│ ├── create-{feature}.dto.ts -│ └── update-{feature}.dto.ts -├── test/ -│ ├── jest-e2e.json -│ └── app.e2e-spec.ts -├── drizzle.config.ts -├── nest-cli.json -├── package.json -└── tsconfig.json -``` - -## Bootstrap (main.ts) - -```typescript -// src/main.ts -import { NestFactory } from '@nestjs/core'; -import { ValidationPipe, Logger } from '@nestjs/common'; -import { AppModule } from './app.module'; -import { AppExceptionFilter } from './common/filters/app-exception.filter'; - -async function bootstrap() { - const app = await NestFactory.create(AppModule); - const logger = new Logger('Bootstrap'); - - // CORS - const corsOrigins = process.env.CORS_ORIGINS?.split(',').map((o) => o.trim()) || [ - 'http://localhost:3000', - 'http://localhost:5173', - 'http://localhost:8081', - ]; - - app.enableCors({ - origin: corsOrigins, - methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], - credentials: true, - }); - - // Global validation pipe - app.useGlobalPipes( - new ValidationPipe({ - whitelist: true, // Strip unknown properties - forbidNonWhitelisted: true, // Reject unknown properties - transform: true, // Auto-transform types - transformOptions: { - enableImplicitConversion: true, - }, - }) - ); - - // Global exception filter - app.useGlobalFilters(new AppExceptionFilter()); - - // API prefix - app.setGlobalPrefix('api/v1'); - - const port = process.env.PORT || 3000; - await app.listen(port); - logger.log(`Application running on http://localhost:${port}`); -} - -bootstrap(); -``` - -## App Module - -```typescript -// src/app.module.ts -import { Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; -import { DatabaseModule } from './db/database.module'; -import { HealthModule } from './health/health.module'; -import { FileModule } from './file/file.module'; -import { FolderModule } from './folder/folder.module'; - -@Module({ - imports: [ - ConfigModule.forRoot({ - isGlobal: true, - envFilePath: '.env', - }), - DatabaseModule, - HealthModule, - FileModule, - FolderModule, - ], -}) -export class AppModule {} -``` - -## Controllers - -### Basic Pattern - -```typescript -// src/file/file.controller.ts -import { - Controller, - Get, - Post, - Patch, - Delete, - Param, - Body, - Query, - UseGuards, - ParseUUIDPipe, -} from '@nestjs/common'; -import { JwtAuthGuard, CurrentUser, CurrentUserData } from '@manacore/shared-nestjs-auth'; -import { AppException } from '@manacore/shared-errors'; -import { FileService } from './file.service'; -import { CreateFileDto, UpdateFileDto, QueryFilesDto } from './dto'; - -@Controller('files') -@UseGuards(JwtAuthGuard) // Apply to all routes in controller -export class FileController { - constructor(private readonly fileService: FileService) {} - - @Get() - async list(@CurrentUser() user: CurrentUserData, @Query() query: QueryFilesDto) { - const result = await this.fileService.findAll(user.userId, query); - if (!result.ok) throw new AppException(result.error); - return { files: result.data }; - } - - @Get(':id') - async getById(@Param('id', ParseUUIDPipe) id: string, @CurrentUser() user: CurrentUserData) { - const result = await this.fileService.findById(id, user.userId); - if (!result.ok) throw new AppException(result.error); - return { file: result.data }; - } - - @Post() - async create(@Body() dto: CreateFileDto, @CurrentUser() user: CurrentUserData) { - const result = await this.fileService.create(user.userId, dto); - if (!result.ok) throw new AppException(result.error); - return { file: result.data }; - } - - @Patch(':id') - async update( - @Param('id', ParseUUIDPipe) id: string, - @Body() dto: UpdateFileDto, - @CurrentUser() user: CurrentUserData - ) { - const result = await this.fileService.update(id, user.userId, dto); - if (!result.ok) throw new AppException(result.error); - return { file: result.data }; - } - - @Delete(':id') - async delete(@Param('id', ParseUUIDPipe) id: string, @CurrentUser() user: CurrentUserData) { - const result = await this.fileService.delete(id, user.userId); - if (!result.ok) throw new AppException(result.error); - return { success: true }; - } -} -``` - -### Public Endpoints (No Auth) - -```typescript -@Controller('public') -export class PublicController { - @Get('shares/:token') // No @UseGuards - public access - async getSharedItem(@Param('token') token: string) { - const result = await this.shareService.findByToken(token); - if (!result.ok) throw new AppException(result.error); - return { item: result.data }; - } -} -``` - -## Services - -### Basic Pattern with Result Types - -```typescript -// src/file/file.service.ts -import { Injectable, Inject, Logger } from '@nestjs/common'; -import { Result, ok, err, ErrorCode } from '@manacore/shared-errors'; -import { DATABASE_CONNECTION, Database } from '../db/database.module'; -import { files, File, NewFile } from '../db/schema'; -import { eq, and, desc } from 'drizzle-orm'; -import { CreateFileDto, UpdateFileDto, QueryFilesDto } from './dto'; - -@Injectable() -export class FileService { - private readonly logger = new Logger(FileService.name); - - constructor(@Inject(DATABASE_CONNECTION) private db: Database) {} - - async findAll(userId: string, query: QueryFilesDto): Promise> { - try { - const conditions = [eq(files.userId, userId), eq(files.isDeleted, false)]; - - if (query.folderId) { - conditions.push(eq(files.parentFolderId, query.folderId)); - } - - const result = await this.db - .select() - .from(files) - .where(and(...conditions)) - .orderBy(desc(files.createdAt)) - .limit(query.limit ?? 50) - .offset(query.offset ?? 0); - - return ok(result); - } catch (error) { - this.logger.error('Failed to fetch files', { userId, error: error.message }); - return err(ErrorCode.DATABASE_ERROR, 'Failed to fetch files'); - } - } - - async findById(id: string, userId: string): Promise> { - try { - const [file] = await this.db - .select() - .from(files) - .where(and(eq(files.id, id), eq(files.userId, userId), eq(files.isDeleted, false))); - - if (!file) { - return err(ErrorCode.FILE_NOT_FOUND, `File ${id} not found`); - } - - return ok(file); - } catch (error) { - this.logger.error('Failed to fetch file', { id, userId, error: error.message }); - return err(ErrorCode.DATABASE_ERROR, 'Failed to fetch file'); - } - } - - async create(userId: string, dto: CreateFileDto): Promise> { - // Validation - if (!dto.name?.trim()) { - return err(ErrorCode.MISSING_REQUIRED_FIELD, 'File name is required'); - } - - try { - const newFile: NewFile = { - userId, - name: dto.name.trim(), - originalName: dto.originalName, - mimeType: dto.mimeType, - size: dto.size, - storagePath: dto.storagePath, - storageKey: dto.storageKey, - parentFolderId: dto.folderId ?? null, - }; - - const [created] = await this.db.insert(files).values(newFile).returning(); - return ok(created); - } catch (error) { - if (error.code === '23505') { - return err(ErrorCode.DUPLICATE_ENTRY, 'A file with this name already exists'); - } - this.logger.error('Failed to create file', { userId, error: error.message }); - return err(ErrorCode.DATABASE_ERROR, 'Failed to create file'); - } - } - - async update(id: string, userId: string, dto: UpdateFileDto): Promise> { - // Check ownership first - const existingResult = await this.findById(id, userId); - if (!existingResult.ok) return existingResult; - - try { - const [updated] = await this.db - .update(files) - .set({ - ...(dto.name && { name: dto.name.trim() }), - ...(dto.parentFolderId !== undefined && { parentFolderId: dto.parentFolderId }), - updatedAt: new Date(), - }) - .where(eq(files.id, id)) - .returning(); - - return ok(updated); - } catch (error) { - this.logger.error('Failed to update file', { id, error: error.message }); - return err(ErrorCode.DATABASE_ERROR, 'Failed to update file'); - } - } - - async delete(id: string, userId: string): Promise> { - // Check ownership first - const existingResult = await this.findById(id, userId); - if (!existingResult.ok) return existingResult; - - try { - await this.db - .update(files) - .set({ isDeleted: true, deletedAt: new Date() }) - .where(eq(files.id, id)); - - return ok(undefined); - } catch (error) { - this.logger.error('Failed to delete file', { id, error: error.message }); - return err(ErrorCode.DATABASE_ERROR, 'Failed to delete file'); - } - } -} -``` - -### Service with External Dependencies - -```typescript -@Injectable() -export class UploadService { - private readonly logger = new Logger(UploadService.name); - - constructor( - @Inject(DATABASE_CONNECTION) private db: Database, - private readonly storageService: StorageService, - private readonly fileService: FileService - ) {} - - async uploadFile( - userId: string, - file: Express.Multer.File, - folderId?: string - ): Promise> { - // 1. Upload to storage - const storageResult = await this.storageService.upload( - generateStorageKey(userId, file.originalname), - file.buffer, - { contentType: file.mimetype } - ); - - if (!storageResult.ok) { - return err(ErrorCode.UPLOAD_FAILED, 'Failed to upload file to storage'); - } - - // 2. Create database record - const createResult = await this.fileService.create(userId, { - name: file.originalname, - originalName: file.originalname, - mimeType: file.mimetype, - size: file.size, - storagePath: storageResult.data.path, - storageKey: storageResult.data.key, - folderId, - }); - - if (!createResult.ok) { - // Cleanup on failure - await this.storageService.delete(storageResult.data.key); - return createResult; - } - - return createResult; - } -} -``` - -## DTOs - -### Create DTO - -```typescript -// src/file/dto/create-file.dto.ts -import { IsString, IsOptional, IsNumber, IsUUID, MaxLength, Min } from 'class-validator'; - -export class CreateFileDto { - @IsString() - @MaxLength(500) - name: string; - - @IsOptional() - @IsString() - @MaxLength(500) - originalName?: string; - - @IsString() - @MaxLength(255) - mimeType: string; - - @IsNumber() - @Min(0) - size: number; - - @IsString() - @MaxLength(1000) - storagePath: string; - - @IsString() - @MaxLength(500) - storageKey: string; - - @IsOptional() - @IsUUID() - folderId?: string; -} -``` - -### Update DTO (Partial) - -```typescript -// src/file/dto/update-file.dto.ts -import { IsString, IsOptional, IsUUID, MaxLength } from 'class-validator'; - -export class UpdateFileDto { - @IsOptional() - @IsString() - @MaxLength(500) - name?: string; - - @IsOptional() - @IsUUID() - parentFolderId?: string | null; -} -``` - -### Query DTO - -```typescript -// src/file/dto/query-files.dto.ts -import { IsOptional, IsUUID, IsNumber, Min, Max } from 'class-validator'; -import { Transform } from 'class-transformer'; - -export class QueryFilesDto { - @IsOptional() - @IsUUID() - folderId?: string; - - @IsOptional() - @Transform(({ value }) => parseInt(value, 10)) - @IsNumber() - @Min(1) - @Max(100) - limit?: number = 50; - - @IsOptional() - @Transform(({ value }) => parseInt(value, 10)) - @IsNumber() - @Min(0) - offset?: number = 0; -} -``` - -### DTO Index - -```typescript -// src/file/dto/index.ts -export * from './create-file.dto'; -export * from './update-file.dto'; -export * from './query-files.dto'; -``` - -## Modules - -```typescript -// src/file/file.module.ts -import { Module } from '@nestjs/common'; -import { FileController } from './file.controller'; -import { FileService } from './file.service'; -import { UploadService } from './upload.service'; -import { StorageModule } from '../storage/storage.module'; - -@Module({ - imports: [StorageModule], - controllers: [FileController], - providers: [FileService, UploadService], - exports: [FileService], // Export for use in other modules -}) -export class FileModule {} -``` - -## Exception Filter - -```typescript -// src/common/filters/app-exception.filter.ts -import { ExceptionFilter, Catch, ArgumentsHost, HttpStatus, Logger } from '@nestjs/common'; -import { Response } from 'express'; -import { AppException, ERROR_STATUS_MAP, ErrorCode } from '@manacore/shared-errors'; - -@Catch(AppException) -export class AppExceptionFilter implements ExceptionFilter { - private readonly logger = new Logger(AppExceptionFilter.name); - - catch(exception: AppException, host: ArgumentsHost) { - const ctx = host.switchToHttp(); - const response = ctx.getResponse(); - - const status = ERROR_STATUS_MAP[exception.error.code] ?? HttpStatus.INTERNAL_SERVER_ERROR; - - // Log server errors - if (status >= 500) { - this.logger.error('Server error', { - code: exception.error.code, - message: exception.error.message, - details: exception.error.details, - }); - } - - response.status(status).json({ - ok: false, - error: { - code: exception.error.code, - message: exception.error.message, - ...(process.env.NODE_ENV === 'development' && { - details: exception.error.details, - }), - }, - }); - } -} -``` - -## File Upload - -```typescript -// src/file/file.controller.ts -import { UseInterceptors, UploadedFile, ParseFilePipe, MaxFileSizeValidator } from '@nestjs/common'; -import { FileInterceptor } from '@nestjs/platform-express'; - -@Controller('files') -@UseGuards(JwtAuthGuard) -export class FileController { - @Post('upload') - @UseInterceptors(FileInterceptor('file')) - async uploadFile( - @UploadedFile( - new ParseFilePipe({ - validators: [ - new MaxFileSizeValidator({ maxSize: 100 * 1024 * 1024 }), // 100MB - ], - }) - ) - file: Express.Multer.File, - @Query('folderId') folderId: string | undefined, - @CurrentUser() user: CurrentUserData - ) { - const result = await this.uploadService.uploadFile(user.userId, file, folderId); - if (!result.ok) throw new AppException(result.error); - return { file: result.data }; - } -} -``` - -## Health Check - -```typescript -// src/health/health.controller.ts -import { Controller, Get } from '@nestjs/common'; -import { Inject } from '@nestjs/common'; -import { DATABASE_CONNECTION, Database } from '../db/database.module'; -import { sql } from 'drizzle-orm'; - -@Controller('health') -export class HealthController { - constructor(@Inject(DATABASE_CONNECTION) private db: Database) {} - - @Get() - async check() { - try { - await this.db.execute(sql`SELECT 1`); - return { - status: 'ok', - timestamp: new Date().toISOString(), - database: 'connected', - }; - } catch (error) { - return { - status: 'error', - timestamp: new Date().toISOString(), - database: 'disconnected', - }; - } - } -} -``` - -## API Response Format - -### Success Responses - -```typescript -// Single resource -{ file: { id: '...', name: '...', ... } } - -// Multiple resources -{ files: [...] } - -// With pagination -{ files: [...], total: 100, page: 1, limit: 20 } - -// Action success -{ success: true } - -// Action with data -{ success: true, message: 'File moved', file: {...} } -``` - -### Error Responses - -```typescript -{ - ok: false, - error: { - code: 'ERR_4003', - message: 'File not found' - } -} -``` - -## Environment Variables - -```env -# Required -NODE_ENV=development -PORT=3016 -DATABASE_URL=postgresql://user:pass@localhost:5432/db -MANA_CORE_AUTH_URL=http://localhost:3001 - -# CORS -CORS_ORIGINS=http://localhost:5173,http://localhost:3000 - -# Storage -S3_ENDPOINT=http://localhost:9000 -S3_REGION=us-east-1 -S3_ACCESS_KEY=minioadmin -S3_SECRET_KEY=minioadmin - -# Optional - Development bypass -DEV_BYPASS_AUTH=true -DEV_USER_ID=dev-user-123 -``` diff --git a/.claude/guidelines/testing.md b/.claude/guidelines/testing.md index e0fa64d99..efb51e0f9 100644 --- a/.claude/guidelines/testing.md +++ b/.claude/guidelines/testing.md @@ -546,7 +546,7 @@ pnpm test pnpm test:cov # Run specific project -pnpm --filter @storage/backend test +pnpm --filter @storage/server test # Run in watch mode pnpm test:watch diff --git a/.github/workflows/cd-macmini.yml b/.github/workflows/cd-macmini.yml index cd3d000b9..e710e7edf 100644 --- a/.github/workflows/cd-macmini.yml +++ b/.github/workflows/cd-macmini.yml @@ -259,16 +259,6 @@ jobs: SERVICES="${{ steps.services.outputs.services }}" DEPLOY_ALL="${{ steps.services.outputs.deploy-all }}" - # Check if any backend service is being deployed - NEEDS_BASE=false - if [ "$DEPLOY_ALL" == "true" ]; then - NEEDS_BASE=true - else - for svc in $SERVICES; do - case "$svc" in *-backend) NEEDS_BASE=true; break ;; esac - done - fi - NEEDS_WEB_BASE=false if [ "$DEPLOY_ALL" == "true" ]; then NEEDS_WEB_BASE=true @@ -278,14 +268,6 @@ jobs: done fi - if [ "$NEEDS_BASE" == "true" ]; then - echo "=== Building shared NestJS base image ===" - docker build -f docker/Dockerfile.nestjs-base -t nestjs-base:local . 2>&1 | tail -5 - echo "NestJS base image built" - else - echo "No backends to deploy, skipping NestJS base image" - fi - if [ "$NEEDS_WEB_BASE" == "true" ]; then echo "=== Building shared SvelteKit base image ===" docker build -f docker/Dockerfile.sveltekit-base -t sveltekit-base:local . 2>&1 | tail -5 diff --git a/CLAUDE.md b/CLAUDE.md index 75f8adcad..3211c6ddd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Monorepo Overview -This is a pnpm workspace monorepo containing multiple product applications with shared packages. All projects use Supabase for database/auth and follow similar architectural patterns. +This is a pnpm workspace monorepo containing multiple product applications with shared packages. All projects use a local-first architecture (Dexie.js + mana-sync) with Hono/Bun compute servers and follow consistent patterns. **Package Manager:** pnpm 9.15.0 (use `pnpm` for all commands) **Build System:** Turborepo @@ -20,7 +20,7 @@ For comprehensive guidelines on code patterns and conventions, see the `.claude/ | [`.claude/guidelines/code-style.md`](.claude/guidelines/code-style.md) | Formatting, naming, linting | | [`.claude/guidelines/database.md`](.claude/guidelines/database.md) | Drizzle ORM, schema patterns | | [`.claude/guidelines/testing.md`](.claude/guidelines/testing.md) | Jest/Vitest, mock factories | -| [`.claude/guidelines/nestjs-backend.md`](.claude/guidelines/nestjs-backend.md) | Controllers, services, DTOs | +| [`.claude/guidelines/hono-server.md`](.claude/guidelines/hono-server.md) | Hono/Bun compute servers | | [`.claude/guidelines/error-handling.md`](.claude/guidelines/error-handling.md) | Go-style Result types, error codes | | [`.claude/guidelines/sveltekit-web.md`](.claude/guidelines/sveltekit-web.md) | Svelte 5 runes, stores | | [`.claude/guidelines/expo-mobile.md`](.claude/guidelines/expo-mobile.md) | React Native, NativeWind | @@ -123,8 +123,9 @@ pnpm run contacts:dev # Start specific app within project pnpm run dev:chat:mobile # Just mobile app -pnpm run dev:chat:backend # Just NestJS backend -pnpm run dev:chat:app # Web + backend together +pnpm run dev:chat:server # Just Hono/Bun server +pnpm run dev:chat:local # sync + server + web (no auth needed) +pnpm run dev:chat:app # Server + web together # Build & quality pnpm run build @@ -143,7 +144,7 @@ manacore-monorepo/ ├── apps/ # Active SaaS product applications │ ├── chat/ │ │ ├── apps/ -│ │ │ ├── backend/ # NestJS API +│ │ │ ├── server/ # Hono/Bun compute server │ │ │ ├── mobile/ # Expo React Native app │ │ │ ├── web/ # SvelteKit web app │ │ │ └── landing/ # Astro marketing page @@ -193,7 +194,7 @@ manacore-monorepo/ ``` apps/{project}/ ├── apps/ -│ ├── backend/ # NestJS API (when present) +│ ├── server/ # Hono/Bun compute server (when present) │ ├── mobile/ # Expo React Native app │ ├── web/ # SvelteKit web app │ └── landing/ # Astro marketing page @@ -259,11 +260,11 @@ Parent workspace packages (e.g., `apps/chat/package.json`, `apps/zitare/package. - Tailwind CSS - Static site generation -**Backends (NestJS):** +**Compute Servers (Hono + Bun):** -- NestJS 10-11 -- TypeScript -- Supabase integration +- Hono 4.x + Bun runtime +- TypeScript, Drizzle ORM (where needed) +- `@manacore/shared-hono` for auth middleware ### Authentication Architecture @@ -271,8 +272,8 @@ All projects use **mana-core-auth** as the central authentication service: ``` ┌─────────────┐ ┌─────────────┐ ┌────────────────┐ -│ Client │────>│ Backend │────>│ mana-core-auth │ -│ (Web/Mobile)│ │ (NestJS) │ │ (port 3001) │ +│ Client │────>│ Server │────>│ mana-auth │ +│ (Web/Mobile)│ │ (Hono/Bun) │ │ (port 3001) │ └─────────────┘ └─────────────┘ └────────────────┘ │ │ │ │ Bearer token │ POST /validate │ @@ -287,82 +288,36 @@ All projects use **mana-core-auth** as the central authentication service: | Component | Purpose | | ------------------------------- | -------------------------------------------------- | -| `services/mana-core-auth` | Central auth service (Better Auth + EdDSA JWT) | -| `@manacore/shared-nestjs-auth` | Shared NestJS guards/decorators for JWT validation | -| `@mana-core/nestjs-integration` | Extended NestJS module with auth + credits | +| `services/mana-auth` | Central auth service (Better Auth + EdDSA JWT) | +| `@manacore/shared-hono` | Shared Hono middleware for JWT validation | | `@manacore/shared-auth` | Client-side auth for web/mobile apps | -#### NestJS Backend Integration +#### Hono Server Auth Integration -**Option 1: Simple auth only** - Use `@manacore/shared-nestjs-auth`: +All compute servers use `@manacore/shared-hono` for auth: ```typescript -// In your controller -import { JwtAuthGuard, CurrentUser, CurrentUserData } from '@manacore/shared-nestjs-auth'; +import { authMiddleware, healthRoute, errorHandler, notFoundHandler } from '@manacore/shared-hono'; -@Controller('api') -@UseGuards(JwtAuthGuard) -export class MyController { - @Get('profile') - getProfile(@CurrentUser() user: CurrentUserData) { - return { userId: user.userId, email: user.email }; - } -} -``` +const app = new Hono(); +app.onError(errorHandler); +app.notFound(notFoundHandler); +app.route('/health', healthRoute('my-server')); +app.use('/api/*', authMiddleware()); -**Option 2: Auth + Credits** - Use `@mana-core/nestjs-integration`: - -```typescript -// app.module.ts -import { ManaCoreModule } from '@mana-core/nestjs-integration'; - -@Module({ - imports: [ - ManaCoreModule.forRootAsync({ - imports: [ConfigModule], - useFactory: (config: ConfigService) => ({ - appId: config.get('APP_ID'), - serviceKey: config.get('MANA_CORE_SERVICE_KEY'), - debug: config.get('NODE_ENV') === 'development', - }), - inject: [ConfigService], - }), - ], -}) -export class AppModule {} - -// In controller -import { AuthGuard } from '@mana-core/nestjs-integration/guards'; -import { CurrentUser } from '@mana-core/nestjs-integration/decorators'; -import { CreditClientService } from '@mana-core/nestjs-integration'; - -@Controller('api') -@UseGuards(AuthGuard) -export class ApiController { - constructor(private creditClient: CreditClientService) {} - - @Post('generate') - async generate(@CurrentUser() user: any) { - await this.creditClient.consumeCredits(user.sub, 'generation', 10, 'AI generation'); - // ... do work - } -} +// In route handlers, get user from context: +app.get('/api/v1/data', (c) => { + const userId = c.get('userId'); + // ... +}); ``` #### Required Environment Variables ```env -# All backends need this +# All servers need this MANA_CORE_AUTH_URL=http://localhost:3001 - -# For development bypass (optional) -NODE_ENV=development -DEV_BYPASS_AUTH=true -DEV_USER_ID=your-test-user-id - -# For credit operations (optional) -MANA_CORE_SERVICE_KEY=your-service-key -APP_ID=your-app-id +CORS_ORIGINS=http://localhost:5173 ``` #### JWT Token Structure (EdDSA) @@ -382,11 +337,11 @@ APP_ID=your-app-id #### Testing Auth Integration ```bash -# 1. Start mana-core-auth +# 1. Start mana-auth pnpm dev:auth -# 2. Start a backend (e.g., Zitare) -pnpm dev:zitare:backend +# 2. Start an app locally +pnpm dev:contacts:local # 3. Get a token TOKEN=$(curl -s -X POST http://localhost:3001/api/v1/auth/login \ @@ -394,20 +349,10 @@ TOKEN=$(curl -s -X POST http://localhost:3001/api/v1/auth/login \ -d '{"email": "test@example.com", "password": "password"}' | jq -r '.accessToken') # 4. Call protected endpoint -curl http://localhost:3007/api/favorites \ +curl http://localhost:3033/api/v1/import/vcard \ -H "Authorization: Bearer $TOKEN" ``` -#### Integrated Backends - -| Backend | Package | Port | -| -------- | ------------------------------- | ---- | -| Chat | `@mana-core/nestjs-integration` | 3002 | -| Picture | `@manacore/shared-nestjs-auth` | 3006 | -| Zitare | `@manacore/shared-nestjs-auth` | 3007 | -| Presi | Custom (same pattern) | 3008 | -| ManaDeck | `@mana-core/nestjs-integration` | 3009 | - #### Adding a New App to SSO When adding a new app that should participate in cross-app SSO, update **all three** locations: @@ -585,7 +530,7 @@ CACHE_SEARCH_TTL=3600 CACHE_EXTRACT_TTL=86400 ``` -#### Usage in Backend +#### Usage in Server ```typescript // Direct fetch @@ -674,8 +619,8 @@ Logged in: App → IndexedDB → UI → SyncEngine → mana-sync (Go) → Postg pnpm dev:sync # Go sync server (port 3050) pnpm dev:sync:build # Compile Go binary pnpm dev:todo:server # Hono/Bun compute server (port 3019) -pnpm dev:todo:local # Web + sync + Hono (no auth/NestJS needed) -pnpm dev:todo:full # Everything incl. auth + NestJS legacy +pnpm dev:todo:local # Web + sync + server (no auth needed) +pnpm dev:todo:full # Everything incl. auth + DB setup ``` ### Adding Local-First to a New App @@ -696,8 +641,7 @@ Full migration plan: `.claude/plans/local-first-architecture-migration.md` | Package | Purpose | | ------------------------------- | ----------------------------------------------- | | `@manacore/local-store` | Local-first data layer (Dexie.js + sync engine) | -| `@manacore/shared-nestjs-auth` | NestJS JWT validation guards via mana-core-auth | -| `@mana-core/nestjs-integration` | NestJS module with auth guards + credit client | +| `@manacore/shared-hono` | Shared Hono middleware (auth, health, errors) | | `@manacore/shared-auth` | Client-side auth service for web/mobile apps | | `@manacore/shared-storage` | S3-compatible storage (MinIO) | | `@manacore/shared-types` | Common TypeScript types | @@ -756,7 +700,7 @@ pnpm docker:up | `contacts-storage` | Contacts | Contact avatars/files | | `storage-storage` | Storage | Cloud drive files | -### Usage in Backend +### Usage in Server ```typescript import { createPictureStorage, generateUserFileKey, getContentType } from '@manacore/shared-storage'; @@ -944,7 +888,7 @@ docker compose -f docker-compose.macmini.yml logs -f # View logs All apps build on shared base images to reduce build time and memory usage: - **`sveltekit-base:local`** (`docker/Dockerfile.sveltekit-base`) — All shared packages for SvelteKit web apps -- **`nestjs-base:local`** (`docker/Dockerfile.nestjs-base`) — All shared packages for NestJS backends +- **`hono-server`** (`docker/Dockerfile.hono-server`) — Hono/Bun compute server template Rebuild base images after shared package changes: `./scripts/mac-mini/build-app.sh --base` @@ -1010,7 +954,7 @@ The script reads `.env.development` and generates platform-specific `.env` files - **Expo mobile**: `EXPO_PUBLIC_*` prefix - **SvelteKit web**: `PUBLIC_*` prefix -- **NestJS backend**: No prefix +- **Hono/Bun server**: No prefix ### Key Files @@ -1041,12 +985,12 @@ PUBLIC_SUPABASE_URL=... PUBLIC_SUPABASE_ANON_KEY=... ``` -**Backend (NestJS):** +**Server (Hono/Bun):** ``` -SUPABASE_URL=... -SUPABASE_SERVICE_ROLE_KEY=... PORT=... +DATABASE_URL=... +MANA_CORE_AUTH_URL=... ``` ## Project-Specific Documentation diff --git a/apps/calendar/CLAUDE.md b/apps/calendar/CLAUDE.md index c7be7f680..a91593642 100644 --- a/apps/calendar/CLAUDE.md +++ b/apps/calendar/CLAUDE.md @@ -6,7 +6,7 @@ | App | Port | URL | |-----|------|-----| -| Backend | 3014 | http://localhost:3014 | +| Server | 3014 | http://localhost:3014 | | Web App | 5179 | http://localhost:5179 | | Landing Page | 4322 | http://localhost:4322 | | Mobile | 8081 | Expo Go | @@ -16,7 +16,7 @@ ``` apps/calendar/ ├── apps/ -│ ├── backend/ # NestJS API server (@calendar/backend) +│ ├── server/ # Hono/Bun compute server (@calendar/server) │ │ └── src/ │ │ ├── main.ts │ │ ├── app.module.ts @@ -114,11 +114,12 @@ apps/calendar/ pnpm calendar:dev # Run all calendar apps # Einzelne Apps starten -pnpm dev:calendar:backend # Start backend server (port 3014) +pnpm dev:calendar:server # Start server (port 3014) pnpm dev:calendar:web # Start web app (port 5179) pnpm dev:calendar:landing # Start landing page (port 4322) pnpm dev:calendar:mobile # Start mobile app [TODO] -pnpm dev:calendar:app # Start web + backend together +pnpm dev:calendar:app # Start web + server together +pnpm dev:calendar:local # Start web + sync (no auth needed) # Datenbank pnpm calendar:db:push # Push schema to database @@ -126,7 +127,7 @@ pnpm calendar:db:studio # Open Drizzle Studio pnpm calendar:db:seed # Seed initial data ``` -### Backend (apps/calendar/apps/backend) +### Server (apps/calendar/apps/server) ```bash pnpm dev # Start with hot reload @@ -157,7 +158,7 @@ pnpm preview # Preview build | Layer | Technology | |-------|------------| -| **Backend** | NestJS 10, Drizzle ORM, PostgreSQL | +| **Server** | Hono + Bun, Drizzle ORM, PostgreSQL | | **Web** | SvelteKit 2.x, Svelte 5 (runes mode), Tailwind CSS 4 | | **Landing** | Astro 5.x, Tailwind CSS | | **Mobile** | React Native 0.81 + Expo SDK 54, NativeWind [TODO] | @@ -224,7 +225,7 @@ eventsStore.updateEvent(id, data) eventsStore.deleteEvent(id) ``` -### Backend API Endpoints +### Server API Endpoints #### Health @@ -400,7 +401,7 @@ FREQ=WEEKLY;UNTIL=20241231T235959Z # Wöchentlich bis Ende 2024 ## Environment Variables -### Backend (.env) +### Server (.env) ```env NODE_ENV=development @@ -544,11 +545,11 @@ pnpm calendar:db:push ### 2. Apps starten ```bash -# Backend + Web zusammen +# Server + Web zusammen pnpm dev:calendar:app # Oder einzeln: -pnpm dev:calendar:backend # Terminal 1 +pnpm dev:calendar:server # Terminal 1 pnpm dev:calendar:web # Terminal 2 pnpm dev:calendar:landing # Terminal 3 (optional) ``` @@ -653,7 +654,7 @@ pnpm --filter @calendar/web test:e2e 1. **Authentication**: Nutzt Mana Core Auth (JWT im Authorization Header) 2. **Database**: PostgreSQL mit Drizzle ORM (Port 5432) -3. **Port**: Backend läuft auf Port 3014 +3. **Port**: Server läuft auf Port 3014 4. **Recurrence**: Verwendet RFC 5545 RRULE Format 5. **i18n**: 5 Sprachen unterstützt (DE, EN, FR, ES, IT) 6. **Theme**: Ocean-Theme (Blautöne) als Standard diff --git a/apps/calendar/package.json b/apps/calendar/package.json index 42c4fb57b..3319cc668 100644 --- a/apps/calendar/package.json +++ b/apps/calendar/package.json @@ -5,13 +5,13 @@ "description": "Calendar App - Personal and Shared Calendars with CalDAV/iCal Sync", "scripts": { "dev": "pnpm run --filter=@calendar/* --parallel dev", - "dev:backend": "pnpm --filter @calendar/backend dev", + "dev:server": "pnpm --filter @calendar/server dev", "dev:web": "pnpm --filter @calendar/web dev", "dev:landing": "pnpm --filter @calendar/landing dev", "dev:mobile": "pnpm --filter @calendar/mobile dev", - "db:push": "pnpm --filter @calendar/backend db:push", - "db:studio": "pnpm --filter @calendar/backend db:studio", - "db:seed": "pnpm --filter @calendar/backend db:seed" + "db:push": "pnpm --filter @calendar/server db:push", + "db:studio": "pnpm --filter @calendar/server db:studio", + "db:seed": "pnpm --filter @calendar/server db:seed" }, "devDependencies": { "typescript": "^5.9.3" diff --git a/apps/chat/CLAUDE.md b/apps/chat/CLAUDE.md index f4e2f4909..bde59f5cd 100644 --- a/apps/chat/CLAUDE.md +++ b/apps/chat/CLAUDE.md @@ -5,7 +5,7 @@ ``` apps/chat/ ├── apps/ -│ ├── backend/ # NestJS API server (@chat/backend) +│ ├── server/ # Hono/Bun compute server (@chat/server) │ ├── landing/ # Astro marketing landing page (@chat/landing) │ ├── web/ # SvelteKit web application (@chat/web) │ └── mobile/ # Expo/React Native mobile app (@chat/mobile) @@ -23,8 +23,9 @@ pnpm chat:dev # Run all chat apps pnpm dev:chat:mobile # Start mobile app pnpm dev:chat:web # Start web app pnpm dev:chat:landing # Start landing page -pnpm dev:chat:backend # Start backend server -pnpm dev:chat:full # Start backend + web + auth together +pnpm dev:chat:server # Start server +pnpm dev:chat:local # Start web + sync (no auth needed) +pnpm dev:chat:full # Start server + web + auth together ``` ### Mobile App (chat/apps/mobile) @@ -38,7 +39,7 @@ pnpm build:preview # Build preview version pnpm build:prod # Build production version ``` -### Backend (apps/chat/apps/backend) +### Server (apps/chat/apps/server) ```bash pnpm start:dev # Start with hot reload @@ -70,13 +71,13 @@ pnpm preview # Preview production build - **Mobile**: React Native 0.76.7 + Expo SDK 52, NativeWind, Expo Router - **Web**: SvelteKit 2.x, Svelte 5, Tailwind CSS 4 - **Landing**: Astro 5.16, Tailwind CSS -- **Backend**: NestJS 10, OpenRouter AI + mana-llm (local), Drizzle ORM, PostgreSQL +- **Server**: Hono + Bun, OpenRouter AI + mana-llm (local), Drizzle ORM, PostgreSQL - **Auth**: Mana Core Auth (JWT) - **Types**: TypeScript 5.x ## Architecture -### Backend API Endpoints +### Server API Endpoints | Endpoint | Method | Description | | --------------------------------- | ------ | --------------------------- | @@ -91,7 +92,7 @@ pnpm preview # Preview production build ### Environment Variables -#### Backend (.env) +#### Server (.env) ```env # Cloud AI models via OpenRouter (optional if using only local models) @@ -162,13 +163,13 @@ PUBLIC_BACKEND_URL=http://localhost:3002 ```bash # Add new models to existing database -pnpm --filter @chat/backend db:add-local-models +pnpm --filter @chat/server db:add-local-models ``` ## Quick Start 1. **Get OpenRouter API key** at https://openrouter.ai/keys -2. **Create `.env`** in `apps/chat/apps/backend/`: +2. **Create `.env`** in `apps/chat/apps/server/`: ```env OPENROUTER_API_KEY=sk-or-v1-xxx DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/chat @@ -182,13 +183,13 @@ pnpm --filter @chat/backend db:add-local-models ``` 4. **Seed database** (first time only): ```bash - pnpm --filter @chat/backend db:push - pnpm --filter @chat/backend db:seed + pnpm --filter @chat/server db:push + pnpm --filter @chat/server db:seed ``` ## Important Notes -1. **Security**: API keys are stored in the backend only - never in client apps +1. **Security**: API keys are stored in the server only - never in client apps 2. **Authentication**: Uses Mana Core Auth (JWT tokens) 3. **Database**: PostgreSQL with Drizzle ORM (uses shared Docker container) -4. **Deployment**: Backend runs on port 3002 +4. **Deployment**: Server runs on port 3002 diff --git a/apps/contacts/CLAUDE.md b/apps/contacts/CLAUDE.md index dfc6e9e6b..836edff69 100644 --- a/apps/contacts/CLAUDE.md +++ b/apps/contacts/CLAUDE.md @@ -5,7 +5,7 @@ ``` apps/contacts/ ├── apps/ -│ ├── backend/ # NestJS API server (@contacts/backend) - Port 3015 +│ ├── server/ # Hono/Bun compute server (@contacts/server) - Port 3015 │ ├── landing/ # Astro marketing landing page (@contacts/landing) │ ├── web/ # SvelteKit web application (@contacts/web) - Port 5184 │ └── mobile/ # Expo/React Native mobile app (@contacts/mobile) @@ -23,8 +23,9 @@ pnpm contacts:dev # Run all contacts apps pnpm dev:contacts:mobile # Start mobile app pnpm dev:contacts:web # Start web app pnpm dev:contacts:landing # Start landing page -pnpm dev:contacts:backend # Start backend server -pnpm dev:contacts:app # Start web + backend together +pnpm dev:contacts:server # Start server +pnpm dev:contacts:app # Start web + server together +pnpm dev:contacts:local # Start web + sync (no auth needed) ``` ### Mobile App (apps/contacts/apps/mobile) diff --git a/apps/contacts/package.json b/apps/contacts/package.json index 837687b14..6bd060717 100644 --- a/apps/contacts/package.json +++ b/apps/contacts/package.json @@ -5,13 +5,13 @@ "description": "Contacts App - Contact Management with Manacore Integration", "scripts": { "dev": "pnpm run --filter=@contacts/* --parallel dev", - "dev:backend": "pnpm --filter @contacts/backend dev", + "dev:server": "pnpm --filter @contacts/server dev", "dev:web": "pnpm --filter @contacts/web dev", "dev:landing": "pnpm --filter @contacts/landing dev", "dev:mobile": "pnpm --filter @contacts/mobile dev", - "db:push": "pnpm --filter @contacts/backend db:push", - "db:studio": "pnpm --filter @contacts/backend db:studio", - "db:seed": "pnpm --filter @contacts/backend db:seed" + "db:push": "pnpm --filter @contacts/server db:push", + "db:studio": "pnpm --filter @contacts/server db:studio", + "db:seed": "pnpm --filter @contacts/server db:seed" }, "devDependencies": { "typescript": "^5.9.3" diff --git a/apps/context/CLAUDE.md b/apps/context/CLAUDE.md index c20f89bc3..91510eb4d 100644 --- a/apps/context/CLAUDE.md +++ b/apps/context/CLAUDE.md @@ -13,7 +13,7 @@ AI-powered document management and context system for knowledge organization. ``` apps/context/ ├── apps/ -│ ├── backend/ # NestJS API server (@context/backend) +│ ├── backend/ # Hono/Bun compute server (@context/server) │ │ └── src/ │ │ ├── main.ts │ │ ├── app.module.ts @@ -45,7 +45,7 @@ apps/context/ ```bash # From monorepo root pnpm dev:context:full # Start auth + backend + web (with DB setup) -pnpm dev:context:backend # Start backend only (port 3020) +pnpm dev:context:server # Start backend only (port 3020) pnpm dev:context:web # Start web only (port 5192) pnpm dev:context:app # Start web + backend together pnpm dev:context:mobile # Start mobile app @@ -61,7 +61,7 @@ pnpm setup:db:context # Create DB + push schema | Layer | Technology | |-------|------------| -| **Backend** | NestJS 10, Drizzle ORM, PostgreSQL | +| **Backend** | Hono + Bun, Drizzle ORM, PostgreSQL | | **Web** | SvelteKit 2.x, Svelte 5 (runes mode), Tailwind CSS 4 | | **Mobile** | React Native 0.76 + Expo SDK 52, NativeWind | | **Auth** | Mana Core Auth (JWT) | diff --git a/apps/context/package.json b/apps/context/package.json index b550210d2..162f63ad2 100644 --- a/apps/context/package.json +++ b/apps/context/package.json @@ -5,7 +5,7 @@ "scripts": { "dev": "turbo run dev", "dev:web": "pnpm --filter @context/web dev", - "dev:backend": "pnpm --filter @context/backend dev", + "dev:server": "pnpm --filter @context/server dev", "dev:mobile": "pnpm --filter @context/mobile dev" } } diff --git a/apps/docs/src/content/docs/development/database-migrations.mdx b/apps/docs/src/content/docs/development/database-migrations.mdx index 42d17e67f..7fa011c78 100644 --- a/apps/docs/src/content/docs/development/database-migrations.mdx +++ b/apps/docs/src/content/docs/development/database-migrations.mdx @@ -25,16 +25,16 @@ Each backend service has its own database and schema: ```bash # Push schema changes (development) -pnpm --filter @chat/backend db:push +pnpm --filter @chat/server db:push # Generate migration files -pnpm --filter @chat/backend db:generate +pnpm --filter @chat/server db:generate # Run migrations -pnpm --filter @chat/backend db:migrate +pnpm --filter @chat/server db:migrate # Open Drizzle Studio (database GUI) -pnpm --filter @chat/backend db:studio +pnpm --filter @chat/server db:studio ``` ## Development Workflow @@ -60,13 +60,13 @@ For local development, use `db:push` to quickly sync schema changes: 2. **Push changes** ```bash - pnpm --filter @chat/backend db:push + pnpm --filter @chat/server db:push ``` 3. **Verify in Studio** ```bash - pnpm --filter @chat/backend db:studio + pnpm --filter @chat/server db:studio ``` @@ -82,7 +82,7 @@ For production, use proper migration files: 1. **Generate migration** ```bash - pnpm --filter @chat/backend db:generate + pnpm --filter @chat/server db:generate ``` This creates a timestamped SQL file in `drizzle/migrations/`. @@ -99,7 +99,7 @@ For production, use proper migration files: 3. **Run migration** ```bash - pnpm --filter @chat/backend db:migrate + pnpm --filter @chat/server db:migrate ``` @@ -241,7 +241,7 @@ export class UsersService { Schema not pushed: ```bash -pnpm --filter @chat/backend db:push --force +pnpm --filter @chat/server db:push --force ``` ### Migration conflicts @@ -253,7 +253,7 @@ Reset and regenerate: rm -rf apps/chat/apps/backend/drizzle/migrations/* # Regenerate -pnpm --filter @chat/backend db:generate +pnpm --filter @chat/server db:generate ``` ### Connection refused diff --git a/apps/docs/src/content/docs/development/local-development.mdx b/apps/docs/src/content/docs/development/local-development.mdx index fd9837854..0087e742b 100644 --- a/apps/docs/src/content/docs/development/local-development.mdx +++ b/apps/docs/src/content/docs/development/local-development.mdx @@ -139,7 +139,7 @@ If you see errors about missing tables/columns: ```bash # Push the latest schema -pnpm --filter @chat/backend db:push --force +pnpm --filter @chat/server db:push --force ``` ### Port already in use diff --git a/apps/docs/src/content/docs/development/testing.mdx b/apps/docs/src/content/docs/development/testing.mdx index 8a2e40225..579b5dc88 100644 --- a/apps/docs/src/content/docs/development/testing.mdx +++ b/apps/docs/src/content/docs/development/testing.mdx @@ -16,13 +16,13 @@ Manacore uses Jest for backend testing and Vitest for frontend testing. pnpm test # Run tests for specific project -pnpm --filter @chat/backend test +pnpm --filter @chat/server test # Run tests in watch mode -pnpm --filter @chat/backend test:watch +pnpm --filter @chat/server test:watch # Run with coverage -pnpm --filter @chat/backend test:cov +pnpm --filter @chat/server test:cov ``` ## Test Structure diff --git a/apps/docs/src/content/docs/getting-started/project-structure.mdx b/apps/docs/src/content/docs/getting-started/project-structure.mdx index c60995d2a..0528ace2d 100644 --- a/apps/docs/src/content/docs/getting-started/project-structure.mdx +++ b/apps/docs/src/content/docs/getting-started/project-structure.mdx @@ -124,7 +124,7 @@ Turborepo handles task orchestration with: | Type | Pattern | Example | |------|---------|---------| -| App package | `@{project}/{app}` | `@chat/backend` | +| App package | `@{project}/{app}` | `@chat/server` | | Shared package | `@manacore/shared-{name}` | `@manacore/shared-auth` | | Service | `@mana-{name}/service` | `@mana-search/service` | diff --git a/apps/docs/src/content/docs/projects/chat.mdx b/apps/docs/src/content/docs/projects/chat.mdx index 788576f3c..574c48de2 100644 --- a/apps/docs/src/content/docs/projects/chat.mdx +++ b/apps/docs/src/content/docs/projects/chat.mdx @@ -15,7 +15,7 @@ AI chat application supporting multiple language models including local (Ollama) | Component | Technology | Port | |-----------|------------|------| -| Backend | NestJS | 3002 | +| Server | Hono/Bun | 3002 | | Web | SvelteKit | 5173 | | Mobile | Expo | - | | Landing | Astro | - | @@ -27,7 +27,7 @@ AI chat application supporting multiple language models including local (Ollama) pnpm dev:chat:full # Or individual components -pnpm dev:chat:backend +pnpm dev:chat:server pnpm dev:chat:web pnpm dev:chat:mobile ``` @@ -36,8 +36,8 @@ pnpm dev:chat:mobile ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ -│ Client │────>│ Backend │────>│ AI Models │ -│ (Web/Mobile)│ │ (NestJS) │ │ Ollama/API │ +│ Client │────>│ Server │────>│ AI Models │ +│ (Web/Mobile)│ │ (Hono/Bun) │ │ Ollama/API │ └─────────────┘ └──────┬──────┘ └─────────────┘ │ ▼ @@ -147,21 +147,21 @@ export const messages = pgTable('messages', { First time setup requires seeding AI models: ```bash -pnpm --filter @chat/backend db:push -pnpm --filter @chat/backend db:seed +pnpm --filter @chat/server db:push +pnpm --filter @chat/server db:seed ``` ### Run Tests ```bash -pnpm --filter @chat/backend test -pnpm --filter @chat/backend test:e2e +pnpm --filter @chat/server test +pnpm --filter @chat/server test:e2e ``` ### Open Database GUI ```bash -pnpm --filter @chat/backend db:studio +pnpm --filter @chat/server db:studio ``` ## Links diff --git a/apps/mukke/CLAUDE.md b/apps/mukke/CLAUDE.md index 0a7f3fea9..fac60dfe7 100644 --- a/apps/mukke/CLAUDE.md +++ b/apps/mukke/CLAUDE.md @@ -7,7 +7,7 @@ Mukke is a web application for managing your music library, playing tracks, and ``` apps/mukke/ ├── apps/ -│ ├── backend/ # NestJS API (port 3010) +│ ├── backend/ # Hono/Bun server (port 3010) │ ├── web/ # SvelteKit app (port 5180) │ └── landing/ # Astro marketing page ├── packages/ @@ -23,7 +23,7 @@ pnpm dev:mukke:full # Or start components individually pnpm docker:up # Start PostgreSQL, Redis, MinIO -pnpm --filter @mukke/backend dev # Backend on port 3010 +pnpm --filter @mukke/server dev # Server on port 3010 pnpm --filter @mukke/web dev # Web on port 5180 pnpm --filter @mukke/landing dev # Landing page ``` @@ -150,7 +150,7 @@ Playback uses HTML5 Audio (browser-native codec support). Upload accepts any `au | Waveform | wavesurfer.js 7.x | | BPM Detection | Web Audio API (peak detection) | | Metadata | music-metadata (server-side) | -| Backend | NestJS 10, Drizzle ORM | +| Backend | Hono + Bun, Drizzle ORM | | Database | PostgreSQL | | Storage | MinIO (S3-compatible) | | Auth | mana-core-auth | @@ -178,14 +178,14 @@ PUBLIC_BACKEND_URL=http://localhost:3010 ```bash # Database -pnpm --filter @mukke/backend db:push # Push schema -pnpm --filter @mukke/backend db:studio # Open Drizzle Studio +pnpm --filter @mukke/server db:push # Push schema +pnpm --filter @mukke/server db:studio # Open Drizzle Studio # Type checking -pnpm --filter @mukke/backend type-check +pnpm --filter @mukke/server type-check pnpm --filter @mukke/web type-check # Build -pnpm --filter @mukke/backend build +pnpm --filter @mukke/server build pnpm --filter @mukke/web build ``` diff --git a/apps/nutriphi/CLAUDE.md b/apps/nutriphi/CLAUDE.md index d2f992e95..cbf60cdc4 100644 --- a/apps/nutriphi/CLAUDE.md +++ b/apps/nutriphi/CLAUDE.md @@ -15,7 +15,7 @@ ``` apps/nutriphi/ ├── apps/ -│ ├── backend/ # NestJS API server (@nutriphi/backend) +│ ├── backend/ # Hono/Bun compute server (@nutriphi/server) │ │ └── src/ │ │ ├── main.ts │ │ ├── app.module.ts @@ -71,7 +71,7 @@ apps/nutriphi/ pnpm nutriphi:dev # Individual apps -pnpm dev:nutriphi:backend # Backend (port 3015) +pnpm dev:nutriphi:server # Backend (port 3015) pnpm dev:nutriphi:web # Web app (port 5180) pnpm dev:nutriphi:landing # Landing page (port 4323) pnpm dev:nutriphi:app # Web + backend together @@ -108,7 +108,7 @@ pnpm build # Build for production | Layer | Technology | |-------|------------| -| **Backend** | NestJS 10, Drizzle ORM, PostgreSQL | +| **Backend** | Hono + Bun, Drizzle ORM, PostgreSQL | | **AI** | Google Gemini 2.5 Flash | | **Web** | SvelteKit 2.x, Svelte 5 (runes mode), Tailwind CSS 4 | | **Landing** | Astro 5.x, Tailwind CSS | @@ -306,7 +306,7 @@ GEMINI_API_KEY=your-gemini-api-key pnpm dev:nutriphi:app # Or individually: -pnpm dev:nutriphi:backend # Terminal 1 +pnpm dev:nutriphi:server # Terminal 1 pnpm dev:nutriphi:web # Terminal 2 pnpm dev:nutriphi:landing # Terminal 3 ``` diff --git a/apps/nutriphi/package.json b/apps/nutriphi/package.json index aad7bd86f..6fccd6007 100644 --- a/apps/nutriphi/package.json +++ b/apps/nutriphi/package.json @@ -5,17 +5,17 @@ "description": "NutriPhi - AI-powered nutrition tracking with photo analysis", "scripts": { "dev": "pnpm run --filter=@nutriphi/* --parallel dev", - "dev:backend": "pnpm --filter @nutriphi/backend dev", + "dev:server": "pnpm --filter @nutriphi/server dev", "dev:web": "pnpm --filter @nutriphi/web dev", "dev:landing": "pnpm --filter @nutriphi/landing dev", - "db:push": "pnpm --filter @nutriphi/backend db:push", - "db:studio": "pnpm --filter @nutriphi/backend db:studio", - "db:seed": "pnpm --filter @nutriphi/backend db:seed", - "test": "pnpm --filter @nutriphi/backend test && pnpm --filter @nutriphi/shared test && pnpm --filter @nutriphi/web test", - "test:backend": "pnpm --filter @nutriphi/backend test", + "db:push": "pnpm --filter @nutriphi/server db:push", + "db:studio": "pnpm --filter @nutriphi/server db:studio", + "db:seed": "pnpm --filter @nutriphi/server db:seed", + "test": "pnpm --filter @nutriphi/server test && pnpm --filter @nutriphi/shared test && pnpm --filter @nutriphi/web test", + "test:backend": "pnpm --filter @nutriphi/server test", "test:web": "pnpm --filter @nutriphi/web test", "test:shared": "pnpm --filter @nutriphi/shared test", - "test:cov": "pnpm --filter @nutriphi/backend test:cov" + "test:cov": "pnpm --filter @nutriphi/server test:cov" }, "devDependencies": { "typescript": "^5.9.3" diff --git a/apps/picture/CLAUDE.md b/apps/picture/CLAUDE.md index 918932504..b0a05fdb9 100644 --- a/apps/picture/CLAUDE.md +++ b/apps/picture/CLAUDE.md @@ -7,7 +7,7 @@ AI image generation app using Replicate API with freemium credit system. ``` apps/picture/ ├── apps/ -│ ├── backend/ # NestJS API (port 3006) +│ ├── backend/ # Hono/Bun server (port 3006) │ ├── mobile/ # Expo React Native app │ ├── web/ # SvelteKit web app │ └── landing/ # Astro marketing page @@ -21,7 +21,7 @@ apps/picture/ pnpm dev:picture:full # Start backend + web + auto DB setup # Individual apps -pnpm --filter @picture/backend dev # Backend only (port 3006) +pnpm --filter @picture/server dev # Backend only (port 3006) pnpm --filter @picture/web dev # Web only pnpm --filter @picture/mobile dev # Mobile only ``` diff --git a/apps/planta/CLAUDE.md b/apps/planta/CLAUDE.md index 95c02ed44..15e36697d 100644 --- a/apps/planta/CLAUDE.md +++ b/apps/planta/CLAUDE.md @@ -5,7 +5,7 @@ ``` apps/planta/ ├── apps/ -│ ├── backend/ # NestJS API server (@planta/backend) +│ ├── backend/ # Hono/Bun compute server (@planta/server) │ └── web/ # SvelteKit web application (@planta/web) ├── packages/ │ └── shared/ # Shared types, utils (@planta/shared) @@ -19,7 +19,7 @@ apps/planta/ ```bash pnpm planta:dev # Run all planta apps pnpm dev:planta:web # Start web app -pnpm dev:planta:backend # Start backend server +pnpm dev:planta:server # Start backend server pnpm dev:planta:app # Start web + backend together pnpm dev:planta:full # Start auth + backend + web with DB setup ``` @@ -45,7 +45,7 @@ pnpm preview # Preview production build ## Technology Stack - **Web**: SvelteKit 2.x, Svelte 5 (runes mode), Tailwind CSS -- **Backend**: NestJS 10, Drizzle ORM, PostgreSQL +- **Backend**: Hono + Bun, Drizzle ORM, PostgreSQL - **AI**: Google Gemini Vision for plant analysis - **Storage**: MinIO (S3-compatible) - **Auth**: Mana Core Auth (JWT) diff --git a/apps/planta/package.json b/apps/planta/package.json index b0907134d..0f7731adb 100644 --- a/apps/planta/package.json +++ b/apps/planta/package.json @@ -5,11 +5,11 @@ "description": "Planta - Plant Documentation & Care App", "scripts": { "dev": "turbo run dev", - "dev:backend": "pnpm --filter @planta/backend dev", + "dev:server": "pnpm --filter @planta/server dev", "dev:web": "pnpm --filter @planta/web dev", - "db:push": "pnpm --filter @planta/backend db:push", - "db:studio": "pnpm --filter @planta/backend db:studio", - "db:seed": "pnpm --filter @planta/backend db:seed" + "db:push": "pnpm --filter @planta/server db:push", + "db:studio": "pnpm --filter @planta/server db:studio", + "db:seed": "pnpm --filter @planta/server db:seed" }, "devDependencies": { "typescript": "^5.9.3" diff --git a/apps/storage/CLAUDE.md b/apps/storage/CLAUDE.md index f82f9ed82..10ce22bad 100644 --- a/apps/storage/CLAUDE.md +++ b/apps/storage/CLAUDE.md @@ -5,7 +5,7 @@ ``` apps/storage/ ├── apps/ -│ ├── backend/ # NestJS API server (@storage/backend) - Port 3016 +│ ├── backend/ # Hono/Bun compute server (@storage/server) - Port 3016 │ ├── landing/ # Astro marketing landing page (@storage/landing) │ └── web/ # SvelteKit web application (@storage/web) - Port 5185 ├── packages/ diff --git a/apps/todo/CLAUDE.md b/apps/todo/CLAUDE.md index 75d3baec1..b66693d3c 100644 --- a/apps/todo/CLAUDE.md +++ b/apps/todo/CLAUDE.md @@ -15,7 +15,7 @@ ``` apps/todo/ ├── apps/ -│ ├── backend/ # NestJS API server (@todo/backend) +│ ├── backend/ # Hono/Bun compute server (@todo/server) │ ├── web/ # SvelteKit web application (@todo/web) │ └── landing/ # Astro marketing landing page (@todo/landing) ├── packages/ @@ -75,7 +75,7 @@ pnpm preview # Preview build | Layer | Technology | |-------|------------| -| **Backend** | NestJS 10, Drizzle ORM, PostgreSQL | +| **Backend** | Hono + Bun, Drizzle ORM, PostgreSQL | | **Web** | SvelteKit 2.x, Svelte 5 (runes mode), Tailwind CSS 4 | | **Landing** | Astro 5.x, Tailwind CSS | | **Auth** | Mana Core Auth (JWT) | @@ -300,7 +300,7 @@ Priority: explicit duration in text > history estimate > default fallback > none ```bash # Unit tests -pnpm --filter @todo/backend test +pnpm --filter @todo/server test pnpm --filter @todo/web test # E2E tests diff --git a/apps/todo/package.json b/apps/todo/package.json index 5791c8496..6fc70b8b4 100644 --- a/apps/todo/package.json +++ b/apps/todo/package.json @@ -5,12 +5,12 @@ "description": "Todo App - Task Management for ManaCore Ecosystem", "scripts": { "dev": "pnpm run --filter=@todo/* --parallel dev", - "dev:backend": "pnpm --filter @todo/backend dev", + "dev:server": "pnpm --filter @todo/server dev", "dev:web": "pnpm --filter @todo/web dev", "dev:landing": "pnpm --filter @todo/landing dev", - "db:push": "pnpm --filter @todo/backend db:push", - "db:studio": "pnpm --filter @todo/backend db:studio", - "db:seed": "pnpm --filter @todo/backend db:seed" + "db:push": "pnpm --filter @todo/server db:push", + "db:studio": "pnpm --filter @todo/server db:studio", + "db:seed": "pnpm --filter @todo/server db:seed" }, "devDependencies": { "typescript": "^5.9.3" diff --git a/apps/traces/CLAUDE.md b/apps/traces/CLAUDE.md index bd403dc20..411d7a4d0 100644 --- a/apps/traces/CLAUDE.md +++ b/apps/traces/CLAUDE.md @@ -8,7 +8,7 @@ GPS tracking app with AI city guides. Location tracking runs locally via AsyncSt apps/traces/ ├── package.json # Orchestrator (name: traces) ├── apps/ -│ ├── backend/ # @traces/backend (NestJS, Port 3026) +│ ├── backend/ # @traces/server (NestJS, Port 3026) │ │ └── src/ │ │ ├── main.ts │ │ ├── app.module.ts @@ -31,7 +31,7 @@ apps/traces/ ```bash # Development pnpm dev:traces:mobile # Start Expo app -pnpm dev:traces:backend # Start NestJS backend +pnpm dev:traces:server # Start NestJS backend pnpm dev:traces:full # Start auth + backend + mobile # Database diff --git a/docker/Dockerfile.nestjs-base b/docker/Dockerfile.nestjs-base deleted file mode 100644 index 8d690e624..000000000 --- a/docker/Dockerfile.nestjs-base +++ /dev/null @@ -1,58 +0,0 @@ -# syntax=docker/dockerfile:1 -# Shared builder base for all NestJS backends -# Contains pre-built shared packages and dependencies -# -# Usage in backend Dockerfiles: -# FROM nestjs-base:local AS builder -# COPY apps/myapp/apps/backend ./apps/myapp/apps/backend -# RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store pnpm install --frozen-lockfile --ignore-scripts -# WORKDIR /app/apps/myapp/apps/backend -# RUN pnpm build - -FROM node:20-alpine - -# Install pnpm -RUN corepack enable && corepack prepare pnpm@9.15.0 --activate - -WORKDIR /app - -# Copy root workspace files -COPY pnpm-workspace.yaml ./ -COPY package.json ./ -COPY pnpm-lock.yaml ./ -COPY patches ./patches - -# Copy ALL shared packages used by NestJS backends -COPY packages/credit-operations ./packages/credit-operations -COPY packages/mana-core-nestjs-integration ./packages/mana-core-nestjs-integration -COPY packages/manadeck-database ./packages/manadeck-database -COPY packages/shared-drizzle-config ./packages/shared-drizzle-config -COPY packages/shared-errors ./packages/shared-errors -COPY packages/shared-error-tracking ./packages/shared-error-tracking -COPY packages/shared-nestjs-auth ./packages/shared-nestjs-auth -COPY packages/shared-nestjs-health ./packages/shared-nestjs-health -COPY packages/shared-nestjs-metrics ./packages/shared-nestjs-metrics -COPY packages/shared-nestjs-setup ./packages/shared-nestjs-setup -COPY packages/shared-llm ./packages/shared-llm -COPY packages/shared-storage ./packages/shared-storage -COPY packages/shared-tsconfig ./packages/shared-tsconfig - -# Install dependencies -RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \ - pnpm install --frozen-lockfile --ignore-scripts - -# Build all shared packages (in dependency order) -RUN cd packages/shared-errors && pnpm build \ - && cd /app/packages/shared-nestjs-auth && pnpm build \ - && cd /app/packages/shared-nestjs-health && pnpm build \ - && cd /app/packages/shared-nestjs-metrics && pnpm build \ - && cd /app/packages/shared-nestjs-setup && pnpm build \ - && cd /app/packages/shared-error-tracking && pnpm build \ - && cd /app/packages/shared-storage && pnpm build \ - && cd /app/packages/shared-llm && pnpm build \ - && cd /app/packages/shared-drizzle-config && pnpm build 2>/dev/null || true \ - && cd /app/packages/credit-operations && pnpm build \ - && cd /app/packages/mana-core-nestjs-integration && pnpm build \ - && cd /app/packages/manadeck-database && pnpm build 2>/dev/null || true - -WORKDIR /app diff --git a/docker/templates/Dockerfile.nestjs b/docker/templates/Dockerfile.nestjs deleted file mode 100644 index 79bc90dcb..000000000 --- a/docker/templates/Dockerfile.nestjs +++ /dev/null @@ -1,89 +0,0 @@ -# syntax=docker/dockerfile:1 -# Multi-stage Dockerfile for NestJS backend services -# This is a template - copy and customize for each backend service - -# ============================================ -# Build Stage -# ============================================ -FROM node:20-alpine AS builder - -# Install pnpm -RUN corepack enable && corepack prepare pnpm@9.15.0 --activate - -WORKDIR /app - -# Copy workspace files -COPY pnpm-workspace.yaml ./ -COPY package.json ./ -COPY pnpm-lock.yaml ./ - -# Copy all shared packages (adjust based on dependencies) -COPY packages/ ./packages/ - -# Copy the specific backend service -# CUSTOMIZE THIS: Replace with your service path -# Example: COPY apps/chat/apps/backend ./apps/chat/apps/backend -ARG SERVICE_PATH -COPY ${SERVICE_PATH} ./${SERVICE_PATH} - -# Install all dependencies (including devDependencies for build) -RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store pnpm install --frozen-lockfile - -# Build shared packages first -RUN pnpm run build:packages - -# Build the backend service -WORKDIR /app/${SERVICE_PATH} -RUN pnpm build - -# ============================================ -# Production Stage -# ============================================ -FROM node:20-alpine AS production - -# Install pnpm and system dependencies -RUN corepack enable && corepack prepare pnpm@9.15.0 --activate \ - && apk add --no-cache \ - postgresql-client \ - curl \ - wget - -WORKDIR /app - -# Copy workspace files -COPY --from=builder /app/pnpm-workspace.yaml ./ -COPY --from=builder /app/package.json ./ -COPY --from=builder /app/pnpm-lock.yaml ./ - -# Copy built packages and service -COPY --from=builder /app/packages ./packages -ARG SERVICE_PATH -COPY --from=builder /app/${SERVICE_PATH} ./${SERVICE_PATH} - -# Install production dependencies only -RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store pnpm install --prod --frozen-lockfile - -# Create non-root user -RUN addgroup -g 1001 -S nodejs && \ - adduser -S nestjs -u 1001 - -# Change ownership -RUN chown -R nestjs:nodejs /app - -# Switch to non-root user -USER nestjs - -# Set working directory to service -WORKDIR /app/${SERVICE_PATH} - -# Expose port (customize per service) -ARG PORT=3000 -EXPOSE ${PORT} - -# Health check (customize endpoint per service) -ARG HEALTH_PATH=/health -HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost:${PORT}${HEALTH_PATH} || exit 1 - -# Start the application -CMD ["node", "dist/main.js"] diff --git a/docs/BACKEND_ARCHITECTURE.md b/docs/BACKEND_ARCHITECTURE.md deleted file mode 100644 index 5b1e14cc2..000000000 --- a/docs/BACKEND_ARCHITECTURE.md +++ /dev/null @@ -1,945 +0,0 @@ -# Backend-Architektur im Manacore Monorepo - -Diese Dokumentation beschreibt die Backend-Implementierungen aller Projekte im Manacore Monorepo. - -## Übersicht - -Das Monorepo enthält 6 Hauptprojekte mit unterschiedlichen Backend-Architekturen: - -| Projekt | Backend-Typ | Datenbank | Status | -|---------|-------------|-----------|--------| -| **Maerchenzauber** | NestJS v10 | Supabase (PostgreSQL) | Aktiv | -| **Manadeck** | NestJS v11 | PostgreSQL + Drizzle ORM | Aktiv | -| **Uload** | NestJS v11 | PostgreSQL + Drizzle ORM | Aktiv | -| **Picture** | Kein Backend | - | Frontend-only | -| **Memoro** | Kein Backend | - | Frontend-only | -| **Manacore** | Kein Backend (extern) | - | Externes Backend | - ---- - -## 1. Maerchenzauber - -**Pfad:** `/maerchenzauber/apps/backend` - -**Zweck:** KI-gestützte Kindergeschichten-Generierung mit benutzerdefinierten Charakteren. - -### Technologie-Stack - -- **Framework:** NestJS 10.0.0 -- **Datenbank:** Supabase (PostgreSQL) -- **ORM:** `@supabase/supabase-js` v2.81.1 -- **AI-Services:** Azure OpenAI, Google Gemini, Replicate - -### Architektur - -``` -apps/backend/ -├── src/ -│ ├── character/ # Charakter-Modul -│ │ ├── character.controller.ts -│ │ ├── character.service.ts -│ │ └── character.repository.ts -│ ├── story/ # Story-Modul -│ │ ├── story.controller.ts -│ │ ├── story.service.ts -│ │ └── pipelines/ # Story-Generierung-Pipelines -│ ├── core/ # Kern-Services -│ │ └── services/ -│ │ └── prompting.service.ts -│ ├── settings/ # Benutzereinstellungen -│ ├── health/ # Health-Checks -│ └── feedback/ # Feedback-Modul -``` - -### Datenbank-Schema - -**Tabellen:** -- `characters` - Benutzercharaktere -- `stories` - Generierte Geschichten -- `story_collections` - Sammlungen von Geschichten -- `user_settings` - Benutzereinstellungen - -**Sicherheit:** Row-Level Security (RLS) für Datenzugriffskontrolle - -### Authentifizierung - -Mana Core Integration via `@mana-core/nestjs-integration`: - -```typescript -// Beispiel: Geschützter Endpoint -@UseGuards(AuthGuard) -@Get('characters') -async getCharacters(@CurrentUser() user: User) { - return this.characterService.findByUser(user.id); -} -``` - -**Auth-Endpoint:** `https://mana-core-middleware-111768794939.europe-west3.run.app` - -### AI-Services - -| Service | Verwendung | API | -|---------|------------|-----| -| Azure OpenAI (GPT-4) | Story-Generierung | `MAERCHENZAUBER_AZURE_OPENAI_ENDPOINT` | -| Google Gemini | Charakter-Generierung | `GOOGLE_GEMINI_API_KEY` | -| Replicate (Flux) | Bildgenerierung | `REPLICATE_API_TOKEN` | - -### File Storage - -- **Provider:** Supabase Storage -- **Bucket:** `maerchenzauber` -- **Verwendung:** Charakter- und Story-Bilder - -### Deployment - -- **Plattform:** Google Cloud Run -- **Region:** europe-west3 -- **URL:** `https://storyteller-backend-111768794939.europe-west3.run.app` -- **Port:** 3002 (Development) - ---- - -## 2. Manadeck - -**Pfad:** `/manadeck/apps/backend` - -**Zweck:** KI-gestützte Lernkarten-Generierung (Flashcards, Quizzes, Mixed). - -### Technologie-Stack - -- **Framework:** NestJS 11.0.1 -- **Datenbank:** PostgreSQL 16 -- **ORM:** Drizzle ORM -- **AI-Service:** Google Gemini API - -### Architektur - -``` -apps/backend/ -├── src/ -│ ├── api.controller.ts # Haupt-API-Endpoints -│ ├── public.controller.ts # Öffentliche Endpoints -│ ├── health.controller.ts # Health-Checks -│ ├── ai.service.ts # AI-Generierung -│ └── repositories/ -│ ├── deck.repository.ts -│ ├── card.repository.ts -│ ├── user-stats.repository.ts -│ └── deck-template.repository.ts -``` - -### Datenbank-Package - -Das Datenbank-Schema ist in einem separaten Package ausgelagert: - -**Pfad:** `/packages/manadeck-database` - -```typescript -// Verwendung im Backend -import { db, schema } from '@manacore/manadeck-database'; - -const decks = await db.query.decks.findMany({ - where: eq(schema.decks.userId, userId) -}); -``` - -**Drizzle-Konfiguration:** -```typescript -// drizzle.config.ts -export default { - schema: './src/schema/*', - out: './migrations', - driver: 'pg', - dbCredentials: { - connectionString: process.env.DATABASE_URL - } -}; -``` - -### Authentifizierung - -```typescript -import { AuthGuard, CurrentUser } from '@mana-core/nestjs-integration'; - -@Controller('api') -@UseGuards(AuthGuard) -export class ApiController { - @Post('decks') - async createDeck(@CurrentUser() user: User, @Body() dto: CreateDeckDto) { - // Credit-Prüfung und Deck-Erstellung - } -} -``` - -### Credit-System - -Integration mit Mana Core Credit Service: - -```typescript -import { CreditClientService } from '@mana-core/nestjs-integration'; - -@Injectable() -export class AiService { - constructor(private creditClient: CreditClientService) {} - - async generateDeck(userId: string, input: GenerateInput) { - // 1. Credit-Balance prüfen - const hasCredits = await this.creditClient.checkBalance(userId, 'DECK_CREATION'); - - // 2. Deck generieren - const deck = await this.generateWithGemini(input); - - // 3. Credits abziehen - await this.creditClient.deduct(userId, 'DECK_CREATION'); - - return deck; - } -} -``` - -### AI-Generierung - -**Unterstützte Kartentypen:** -- `text` - Textbasierte Karten -- `flashcard` - Klassische Lernkarten -- `quiz` - Multiple-Choice Quiz -- `mixed` - Gemischte Inhalte - -**Schwierigkeitsgrade:** -- `beginner` -- `intermediate` -- `advanced` - -### Docker-Setup - -```yaml -# docker-compose.yml (Lokale Entwicklung) -services: - postgres: - image: postgres:16 - ports: - - "5433:5432" - environment: - POSTGRES_DB: manadeck - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - - pgadmin: - image: dpage/pgadmin4 - ports: - - "5050:80" -``` - -### Deployment - -- **Docker Image:** Multi-stage Build (Node 18-alpine) -- **Port:** 8080 -- **Health-Check:** `/health` - ---- - -## 3. Uload - -**Pfad:** `/uload/apps/backend` - -**Zweck:** URL-Shortener mit Link-Analytics. - -### Technologie-Stack - -- **Framework:** NestJS 11.0.1 -- **Datenbank:** PostgreSQL 16 -- **ORM:** Drizzle ORM -- **Cache:** Redis (optional) - -### Architektur - -``` -uload/apps/backend/ -├── src/ -│ ├── main.ts -│ ├── app.module.ts -│ ├── config/ -│ │ └── validation.schema.ts -│ ├── controllers/ -│ │ ├── redirect.controller.ts # GET /:code (public redirect) -│ │ ├── links.controller.ts # CRUD /api/links -│ │ ├── analytics.controller.ts # GET /api/analytics -│ │ └── health.controller.ts -│ ├── services/ -│ │ ├── links.service.ts -│ │ ├── redirect.service.ts -│ │ └── analytics.service.ts -│ └── database/ -│ ├── database.module.ts -│ └── repositories/ -│ ├── link.repository.ts -│ └── click.repository.ts -├── Dockerfile -└── package.json -``` - -### Datenbank-Package - -**Pfad:** `/packages/uload-database` - -```typescript -// Verwendung im Backend -import { db, links, clicks, eq, desc } from '@manacore/uload-database'; - -const userLinks = await db.query.links.findMany({ - where: eq(links.userId, userId), - orderBy: desc(links.createdAt) -}); -``` - -### API Endpoints - -| Endpoint | Method | Auth | Beschreibung | -|----------|--------|------|--------------| -| `/:code` | GET | Public | Redirect zu Original-URL | -| `/api/links` | GET | Protected | Liste aller Links | -| `/api/links` | POST | Protected | Link erstellen | -| `/api/links/:id` | GET | Protected | Link Details | -| `/api/links/:id` | PATCH | Protected | Link aktualisieren | -| `/api/links/:id` | DELETE | Protected | Link löschen | -| `/api/analytics/:linkId` | GET | Protected | Link-Statistiken | -| `/health` | GET | Public | Health Check | - -### Authentifizierung - -Mana Core Integration via `@mana-core/nestjs-integration`: - -```typescript -import { AuthGuard, CurrentUser } from '@mana-core/nestjs-integration'; - -@Controller('api/links') -@UseGuards(AuthGuard) -export class LinksController { - @Get() - async getLinks(@CurrentUser() user: any) { - return this.linksService.getLinks(user.sub); - } -} -``` - -### Deployment - -- **Docker Image:** Multi-stage Build (Node 20-alpine) -- **Port:** 3003 -- **Health-Check:** `/health` - ---- - -## 4. Picture - -**Pfad:** `/picture` - -**Zweck:** Bild- und Medienverwaltung. - -### Architektur - -**Kein dediziertes Backend.** Picture verwendet: - -- SvelteKit Server-Routes für Backend-Logik -- Mana Core für Authentifizierung -- Shared Packages aus `/packages` - -``` -picture/ -├── apps/ -│ ├── mobile/ # React Native Expo -│ ├── web/ # SvelteKit -│ └── landing/ # Astro -└── packages/ - ├── design-tokens/ # Design System - ├── mobile-ui/ # Mobile UI Components - └── shared/ # Utilities -``` - ---- - -## 5. Memoro - -**Pfad:** `/memoro` - -**Zweck:** Legacy-Content und Memory-Preservation. - -### Architektur - -**Kein dediziertes Backend.** Memoro verwendet: - -- SvelteKit Server-Routes -- Mana Core für Authentifizierung -- Supabase (Legacy-Konfiguration vorhanden) - -``` -memoro/ -├── apps/ -│ ├── mobile/ -│ ├── web/ -│ └── landing/ -└── supabase/ # Legacy Supabase Config -``` - ---- - -## 6. Manacore - -**Pfad:** `/manacore` - -**Zweck:** Core-Authentifizierung und Credit-System. - -### Architektur - -Das Manacore-Backend ist **extern gehostet** und nicht Teil des Monorepos: - -- **URL:** `https://mana-core-middleware-111768794939.europe-west3.run.app` -- **Integration:** Via `@mana-core/nestjs-integration` Package - -``` -manacore/ -├── apps/ -│ ├── mobile/ # Auth-Flow UI -│ ├── web/ # Dashboard -│ └── landing/ # Marketing -``` - ---- - -## Shared Packages für Backend - -### @manacore/manadeck-database - -PostgreSQL-Datenbankschema für Manadeck. - -``` -packages/manadeck-database/ -├── src/ -│ ├── schema/ # Drizzle Schema -│ ├── client.ts # DB Client -│ └── index.ts # Exports -├── drizzle.config.ts -└── docker-compose.yml -``` - -### @manacore/uload-database - -PostgreSQL-Datenbankschema für Uload URL-Shortener. - -``` -packages/uload-database/ -├── src/ -│ ├── schema/ -│ │ ├── users.ts -│ │ ├── links.ts -│ │ ├── clicks.ts -│ │ ├── tags.ts -│ │ ├── workspaces.ts -│ │ ├── accounts.ts -│ │ └── relations.ts -│ ├── client.ts # DB Client -│ └── index.ts # Exports -├── drizzle.config.ts -└── docker-compose.yml -``` - -### @mana-core/nestjs-integration - -Externe Dependency für Backend-Integration: - -```typescript -// Installation via git -"@mana-core/nestjs-integration": "git+https://github.com/mana-core/nestjs-integration.git" -``` - -**Bereitgestellte Features:** -- `AuthGuard` - JWT-Authentifizierung -- `@CurrentUser()` - User-Context Decorator -- `CreditClientService` - Credit-Operationen -- Konfigurationsmodule - ---- - -## Authentifizierungs-Pattern - -Alle Projekte nutzen zentrale **Mana Core Authentifizierung**: - -``` -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ Frontend │────▶│ Project Backend │────▶│ Mana Core │ -│ (Web/Mobile) │ │ (NestJS/etc.) │ │ Middleware │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ - │ │ │ - │ │ │ - ▼ ▼ ▼ - shared-auth AuthGuard JWT Validation - shared-auth-ui @CurrentUser Credit Service - shared-auth-stores CreditClient User Management -``` - -### Frontend-Integration - -```typescript -// Shared Auth Store (Svelte) -import { authStore } from '@manacore/shared-auth-stores'; - -// Login -await authStore.login(email, password); - -// Token für API-Requests -const token = authStore.getAccessToken(); -``` - -### Backend-Integration - -```typescript -// NestJS Module Setup -@Module({ - imports: [ - ManaCoreModule.forRoot({ - serviceKey: process.env.MANA_CORE_SERVICE_KEY, - baseUrl: process.env.MANA_CORE_URL, - }), - ], -}) -export class AppModule {} -``` - ---- - -## Datenbank-Migrationen - -### Manadeck (Drizzle) - -```bash -# Migration generieren -pnpm --filter @manacore/manadeck-database drizzle-kit generate - -# Migration ausführen -pnpm --filter @manacore/manadeck-database drizzle-kit push -``` - -### Maerchenzauber (Supabase) - -```bash -# Supabase CLI -supabase migration new -supabase db push -``` - ---- - -## Umgebungsvariablen - -### Maerchenzauber Backend - -```env -# Supabase -SUPABASE_URL= -SUPABASE_SERVICE_ROLE_KEY= - -# Mana Core -MANA_CORE_URL=https://mana-core-middleware-111768794939.europe-west3.run.app -MANA_CORE_SERVICE_KEY= - -# AI Services -MAERCHENZAUBER_AZURE_OPENAI_ENDPOINT= -AZURE_OPENAI_API_KEY= -GOOGLE_GEMINI_API_KEY= -REPLICATE_API_TOKEN= -``` - -### Manadeck Backend - -```env -# Database -DATABASE_URL=postgresql://postgres:postgres@localhost:5433/manadeck - -# Mana Core -MANA_CORE_URL= -MANA_CORE_SERVICE_KEY= - -# AI -GOOGLE_GEMINI_API_KEY= - -# Server -PORT=8080 -``` - -### Uload - -```env -# Database -DATABASE_URL=postgresql://... - -# Redis -REDIS_URL=redis://localhost:6379 - -# PocketBase -POCKETBASE_URL= -``` - ---- - -## Lokale Entwicklung - -### Maerchenzauber Backend - -```bash -cd maerchenzauber/apps/backend -pnpm install -pnpm run start:dev -# Läuft auf Port 3002 -``` - -### Manadeck Backend - -```bash -# 1. Datenbank starten -cd packages/manadeck-database -docker-compose up -d - -# 2. Backend starten -cd manadeck/apps/backend -pnpm install -pnpm run start:dev -# Läuft auf Port 8080 -``` - -### Uload - -```bash -cd uload -docker-compose up -d # PostgreSQL + Redis -pnpm install -pnpm run dev -``` - ---- - -## Zusammenfassung - -Das Manacore Monorepo verwendet verschiedene Backend-Strategien: - -1. **Full Backend (NestJS):** Maerchenzauber, Manadeck - Für komplexe Geschäftslogik und AI-Integration -2. **Embedded Database (PocketBase):** Uload - Für einfache CRUD-Operationen -3. **Frontend-only:** Picture, Memoro - Server-Routes in SvelteKit -4. **External Backend:** Manacore - Zentrale Auth/Credit-Services - -Alle Projekte teilen sich: -- Gemeinsame Authentifizierung via Mana Core -- Shared Packages für UI, Auth, Types -- Einheitliches Deployment-Pattern (Docker + Cloud Run) - ---- - -## Vereinheitlichungs-Roadmap - -### Aktuelle Fragmentierung - -| Aspekt | Maerchenzauber | Manadeck | Uload | -|--------|----------------|----------|-------| -| Framework | NestJS v10 | NestJS v11 | PocketBase | -| Datenbank | Supabase | PostgreSQL | PocketBase + PG | -| ORM | @supabase/js | Drizzle | Drizzle | -| Auth | Mana Core | Mana Core | PocketBase + Mana Core | - ---- - -### Strategie 1: Shared NestJS Backend Package - -**Ziel:** Ein gemeinsames `@manacore/shared-backend` Package mit wiederverwendbaren Modulen. - -``` -packages/shared-backend/ -├── src/ -│ ├── auth/ -│ │ ├── auth.module.ts -│ │ ├── auth.guard.ts -│ │ └── current-user.decorator.ts -│ ├── database/ -│ │ ├── database.module.ts -│ │ ├── drizzle.provider.ts -│ │ └── base.repository.ts -│ ├── health/ -│ │ └── health.module.ts -│ ├── credits/ -│ │ └── credits.module.ts -│ └── common/ -│ ├── filters/ -│ ├── interceptors/ -│ └── pipes/ -``` - -**Vorteile:** -- Einheitliche Auth-Integration -- Wiederverwendbare Module -- Konsistente Error-Handling - -**Aufwand:** Mittel - ---- - -### Strategie 2: Einheitliche Datenbank-Strategie - -#### Option A: Alles auf Drizzle + PostgreSQL (Empfohlen) - -```typescript -// packages/shared-database/src/base-schema.ts -export const baseColumns = { - id: uuid('id').primaryKey().defaultRandom(), - createdAt: timestamp('created_at').defaultNow(), - updatedAt: timestamp('updated_at').defaultNow(), - userId: text('user_id').notNull(), -}; - -// Projekt-spezifische Erweiterung -// maerchenzauber/database/schema/characters.ts -import { baseColumns } from '@manacore/shared-database'; - -export const characters = pgTable('characters', { - ...baseColumns, - name: text('name').notNull(), - traits: jsonb('traits'), -}); -``` - -**Migration von Supabase:** -- Supabase ist PostgreSQL → Schema kann übernommen werden -- RLS-Policies in Application-Layer verschieben -- Storage → S3/Cloudflare R2 - -#### Option B: Alles auf Supabase - -```typescript -export const createProjectClient = (project: 'maerchenzauber' | 'manadeck' | 'uload') => { - return createClient( - process.env[`${project.toUpperCase()}_SUPABASE_URL`], - process.env[`${project.toUpperCase()}_SUPABASE_KEY`] - ); -}; -``` - -**Vorteile Supabase:** -- Eingebaute Auth (optional nutzbar) -- Storage inklusive -- Realtime-Subscriptions -- Edge Functions möglich - -**Nachteile Supabase:** -- Vendor Lock-in -- Weniger Kontrolle über Schema - -**Empfehlung:** Drizzle + PostgreSQL wegen Type-Safety, moderner API und keinem Vendor Lock-in. - ---- - -### Strategie 3: Einheitliche Monorepo Backend Struktur - -**Ziel-Architektur:** - -``` -packages/ -├── shared-backend/ # Gemeinsame NestJS Module -│ ├── auth/ -│ ├── database/ -│ ├── health/ -│ └── credits/ -├── shared-database/ # Drizzle Basis-Schema -│ ├── base-schema.ts -│ ├── migrations/ -│ └── client.ts -├── maerchenzauber-database/ # Projekt-Schema -├── manadeck-database/ # ✓ Existiert bereits -└── uload-database/ # Neu - -apps/ -├── maerchenzauber-backend/ # Nutzt shared-backend -├── manadeck-backend/ # Nutzt shared-backend -└── uload-backend/ # Neues NestJS Backend (ersetzt PocketBase) -``` - ---- - -### Strategie 4: Shared Backend als Service-Layer - -**Ziel:** Gemeinsamer Service-Layer, projekt-spezifische Controller. - -```typescript -// packages/shared-backend/src/services/ai.service.ts -@Injectable() -export class BaseAiService { - constructor( - private gemini: GeminiClient, - private credits: CreditService, - ) {} - - protected async generateWithCredits( - userId: string, - operation: string, - generator: () => Promise - ): Promise { - await this.credits.check(userId, operation); - const result = await generator(); - await this.credits.deduct(userId, operation); - return result; - } -} - -// maerchenzauber/backend/src/story/story.service.ts -@Injectable() -export class StoryService extends BaseAiService { - async generateStory(userId: string, input: StoryInput) { - return this.generateWithCredits(userId, 'STORY_GENERATION', async () => { - // Projekt-spezifische Logik - }); - } -} -``` - ---- - -### Strategie 5: API-Gateway Pattern (Optional) - -**Ziel:** Ein zentrales Gateway vor allen Backends. - -``` - ┌─────────────────┐ - │ API Gateway │ - │ (Kong/Traefik) │ - └────────┬────────┘ - │ - ┌────────────────────┼────────────────────┐ - │ │ │ - ▼ ▼ ▼ -┌───────────────┐ ┌───────────────┐ ┌───────────────┐ -│ Maerchenzauber│ │ Manadeck │ │ Uload │ -│ Backend │ │ Backend │ │ Backend │ -└───────────────┘ └───────────────┘ └───────────────┘ -``` - -**Vorteile:** -- Zentrale Auth-Validierung -- Rate Limiting -- Request Logging -- Einheitliche API-Struktur - -**Aufwand:** Hoch - Empfohlen erst bei Skalierungsbedarf - ---- - -### Empfohlene Implementierungsreihenfolge - -#### Phase 1: Shared Backend Package - -**Priorität:** Hoch -**Aufwand:** 2-3 Wochen - -Neues Package `packages/shared-backend/` mit: -- Auth Module (wraps @mana-core/nestjs-integration) -- Health Module -- Credits Module -- Base Repository Pattern -- Common Decorators, Guards, Filters - -#### Phase 2: Datenbank-Vereinheitlichung - -**Priorität:** Hoch -**Aufwand:** 3-4 Wochen - -1. `packages/shared-database/` mit Drizzle Basis-Schema erstellen -2. Maerchenzauber von Supabase auf Drizzle migrieren -3. Uload PocketBase durch PostgreSQL + Drizzle ersetzen - -#### Phase 3: Uload Backend Neubau (Optional) - -**Priorität:** Mittel -**Aufwand:** 2-3 Wochen - -PocketBase → NestJS Migration: -- Konsistenz mit anderen Projekten -- Bessere Integration mit Mana Core -- Einheitliches Deployment - ---- - -### Optionen-Vergleich - -| Option | Aufwand | Benefit | Empfehlung | -|--------|---------|---------|------------| -| Shared Backend Package | Mittel | Hoch | ✅ Priorität 1 | -| Drizzle überall | Mittel-Hoch | Hoch | ✅ Priorität 2 | -| Uload auf NestJS | Hoch | Mittel | ⚡ Optional | -| API Gateway | Sehr Hoch | Mittel | ⏳ Später | - ---- - -### Quick Wins (sofort umsetzbar) - -1. **NestJS Version angleichen** → Alle auf v11 -2. **Einheitliche Health-Endpoints** → `/health`, `/health/ready` -3. **Gemeinsame ESLint/Prettier Config** → `@manacore/eslint-config-backend` -4. **Einheitliche Error-Response-Struktur:** - -```typescript -// Einheitliches Error-Format für alle Backends -interface ApiError { - statusCode: number; - error: string; - message: string; - timestamp: string; - path: string; -} -``` - -5. **Einheitliche Logging-Struktur:** - -```typescript -// packages/shared-backend/src/logging/logger.service.ts -@Injectable() -export class AppLogger { - log(context: string, message: string, meta?: Record) { - console.log(JSON.stringify({ - level: 'info', - context, - message, - timestamp: new Date().toISOString(), - ...meta, - })); - } -} -``` - ---- - -### Ziel-Architektur nach Vereinheitlichung - -``` -┌─────────────────────────────────────────────────────────────┐ -│ Shared Packages │ -├─────────────────┬─────────────────┬─────────────────────────┤ -│ shared-backend │ shared-database │ shared-types │ -│ - AuthModule │ - baseColumns │ - ApiError │ -│ - HealthModule │ - drizzleClient │ - User │ -│ - CreditsModule │ - migrations │ - CreditOperation │ -│ - BaseRepo │ │ │ -└────────┬────────┴────────┬────────┴────────┬────────────────┘ - │ │ │ - ▼ ▼ ▼ -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ Maerchenzauber │ │ Manadeck │ │ Uload │ -│ Backend │ │ Backend │ │ Backend │ -├─────────────────┤ ├─────────────────┤ ├─────────────────┤ -│ NestJS v11 │ │ NestJS v11 │ │ NestJS v11 │ -│ PostgreSQL │ │ PostgreSQL │ │ PostgreSQL │ -│ Drizzle ORM │ │ Drizzle ORM │ │ Drizzle ORM │ -│ Port: 3002 │ │ Port: 8080 │ │ Port: 3003 │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ - │ │ │ - └─────────────────┼─────────────────┘ - ▼ - ┌─────────────────────┐ - │ Mana Core │ - │ (Auth + Credits) │ - └─────────────────────┘ -``` diff --git a/docs/DATABASE_MIGRATIONS.md b/docs/DATABASE_MIGRATIONS.md index 775d05ee1..08cd872ce 100644 --- a/docs/DATABASE_MIGRATIONS.md +++ b/docs/DATABASE_MIGRATIONS.md @@ -246,8 +246,8 @@ pnpm --filter mana-core-auth db:generate pnpm --filter mana-core-auth db:migrate # chat-backend -pnpm --filter @chat/backend db:push -pnpm --filter @chat/backend db:migrate +pnpm --filter @chat/server db:push +pnpm --filter @chat/server db:migrate ``` --- diff --git a/docs/ENVIRONMENT_VARIABLES.md b/docs/ENVIRONMENT_VARIABLES.md index 515de41ec..1a443035c 100644 --- a/docs/ENVIRONMENT_VARIABLES.md +++ b/docs/ENVIRONMENT_VARIABLES.md @@ -32,7 +32,7 @@ The generator reads `.env.development` and creates app-specific `.env` files wit |----------|--------|---------| | Expo (mobile) | `EXPO_PUBLIC_` | `EXPO_PUBLIC_SUPABASE_URL` | | SvelteKit (web) | `PUBLIC_` | `PUBLIC_SUPABASE_URL` | -| NestJS (backend) | None | `SUPABASE_URL` | +| Hono/Bun (server) | None | `DATABASE_URL` | ## File Locations @@ -40,19 +40,17 @@ The generator reads `.env.development` and creates app-specific `.env` files wit - **`.env.development`** - Single source of truth, committed to git ### Generated Files (gitignored) -- `services/mana-core-auth/.env` -- `apps/chat/apps/backend/.env` +- `services/mana-auth/.env` +- `apps/chat/apps/server/.env` - `apps/chat/apps/mobile/.env` - `apps/chat/apps/web/.env` -- `apps/maerchenzauber/apps/backend/.env` -- `apps/maerchenzauber/apps/mobile/.env` -- `apps/maerchenzauber/apps/web/.env` - `apps/manacore/apps/mobile/.env` - `apps/manacore/apps/web/.env` -- `apps/memoro/apps/mobile/.env` -- `apps/memoro/apps/web/.env` -- `apps/manadeck/apps/backend/.env` +- `apps/manadeck/apps/server/.env` - `apps/manadeck/apps/web/.env` +- `apps/*/apps/server/.env` (all apps with compute servers) +- `apps/*/apps/web/.env` (all web apps) +- `apps/*/apps/mobile/.env` (all mobile apps) ## Variable Reference @@ -98,28 +96,6 @@ The generator reads `.env.development` and creates app-specific `.env` files wit | `CHAT_SUPABASE_URL` | Supabase project URL | - | | `CHAT_SUPABASE_ANON_KEY` | Supabase anonymous key | - | -### Maerchenzauber Project - -| Variable | Description | Default | -|----------|-------------|---------| -| `MAERCHENZAUBER_BACKEND_PORT` | Backend service port | `3003` | -| `MAERCHENZAUBER_APP_ID` | Mana Core app ID | - | -| `MAERCHENZAUBER_SUPABASE_URL` | Supabase project URL | - | -| `MAERCHENZAUBER_SUPABASE_ANON_KEY` | Supabase anonymous key | - | -| `MAERCHENZAUBER_JWT_SECRET` | JWT secret for Supabase | - | -| `MAERCHENZAUBER_AZURE_OPENAI_KEY` | Azure OpenAI key | - | -| `MAERCHENZAUBER_AZURE_OPENAI_ENDPOINT` | Azure OpenAI endpoint | - | -| `MAERCHENZAUBER_REPLICATE_API_KEY` | Replicate API key (images) | - | - -### Memoro Project - -| Variable | Description | -|----------|-------------| -| `MEMORO_SUPABASE_URL` | Supabase project URL | -| `MEMORO_SUPABASE_ANON_KEY` | Supabase anonymous key | -| `MEMORO_MIDDLEWARE_API_URL` | Middleware API URL | -| `MEMORO_APPID` | Mana Core app ID | - ### Manacore Project | Variable | Description | @@ -168,9 +144,9 @@ Edit `scripts/generate-env.mjs` and add your app config: }, }, { - path: 'apps/my-project/apps/backend/.env', + path: 'apps/my-project/apps/server/.env', vars: { - // For NestJS, no prefix needed + // For Hono/Bun servers, no prefix needed API_KEY: (env) => env.MY_NEW_PROJECT_API_KEY, API_URL: (env) => env.MY_NEW_PROJECT_URL, }, diff --git a/docs/LOCAL_DEVELOPMENT.md b/docs/LOCAL_DEVELOPMENT.md index 1647adc02..b4117c09c 100644 --- a/docs/LOCAL_DEVELOPMENT.md +++ b/docs/LOCAL_DEVELOPMENT.md @@ -1,35 +1,94 @@ # Local Development Guide -This guide explains how to set up and run applications locally with automatic database setup. +This guide explains how to set up and run applications locally. All apps use a **local-first architecture** with IndexedDB (Dexie.js) for reads/writes and mana-sync for background synchronization. Server-side compute (AI, file operations, RRULE expansion, etc.) is handled by lightweight **Hono/Bun servers**. ## Quick Start -For any project with a backend, use the `dev:*:full` command: +### Fastest: `dev:*:local` (recommended for daily development) + +No auth service needed. Starts mana-sync + server (if any) + web: ```bash -pnpm dev:chat:full # Start chat with auth + database setup -pnpm dev:zitare:full # Start zitare with auth + database setup -pnpm dev:contacts:full # Start contacts with auth + database setup -# ... etc +pnpm dev:todo:local # sync + server + web +pnpm dev:chat:local # sync + server + web +pnpm dev:zitare:local # sync + web (no server needed) +pnpm dev:clock:local # sync + web (no server needed) +``` + +Guest mode works out of the box -- data lives in IndexedDB, no login required. + +### Full stack: `dev:*:full` (with auth + database setup) + +Use this when you need authentication, cross-device sync, or to test the full production flow: + +```bash +pnpm dev:todo:full # DB setup + auth + sync + server + web +pnpm dev:chat:full # DB setup + auth + sync + server + web +pnpm dev:zitare:full # auth + sync + web +pnpm dev:calendar:full # DB setup + auth + sync + server + web ``` These commands automatically: -1. Create the database if it doesn't exist -2. Push the latest schema (Drizzle `db:push`) -3. Start the auth service (mana-core-auth) -4. Start the backend and web app with colored output +1. Create the database if it doesn't exist (for apps with Drizzle schemas) +2. Push the latest schema (`drizzle-kit push --force`) +3. Start the auth service (mana-auth) +4. Start mana-sync (Go WebSocket server) +5. Start the Hono/Bun server (if the app has one) +6. Start the web app with colored output -## Available Full Dev Commands +## Available Dev Commands -| Command | Database | Backend Port | Web Port | -|---------|----------|--------------|----------| -| `pnpm dev:chat:full` | chat | 3002 | 5173 | -| `pnpm dev:zitare:full` | zitare | 3007 | 5177 | -| `pnpm dev:contacts:full` | contacts | 3015 | 5184 | -| `pnpm dev:calendar:full` | calendar | 3014 | 5179 | -| `pnpm dev:clock:full` | clock | 3017 | 5187 | -| `pnpm dev:todo:full` | todo | 3018 | 5188 | -| `pnpm dev:picture:full` | picture | 3006 | 5175 | +### Apps with Hono/Bun Servers + +These apps have server-side compute and support both `local` and `full` modes: + +| App | Server Port | Web Port | `local` | `full` | +|-----|-------------|----------|---------|--------| +| Todo | 3019 | 5188 | Yes | Yes | +| Chat | 3002 | 5174 | Yes | Yes | +| Calendar | 3003 | 5179 | Yes | Yes | +| Contacts | 3004 | 5184 | Yes | Yes | +| Picture | 3006 | 5175 | Yes | Yes | +| ManaDeck | 3009 | 5176 | Yes | Yes | +| Mukke | 3010 | 5180 | Yes | Yes | +| Questions | 3011 | 5111 | Yes | Yes | +| Storage | 3016 | 5185 | Yes | Yes | +| Context | 3020 | 5192 | Yes | Yes | +| Planta | 3022 | 5191 | Yes | Yes | +| NutriPhi | 3023 | 5180 | Yes | Yes | +| Traces | 3026 | mobile | Yes | Yes | +| Presi | 3008 | 5178 | Yes | Yes | +| uLoad | 3070 | 5173 | Yes | Yes | +| News | 3071 | 5173 | Yes | Yes | +| WiseKeep | 3072 | 5173 | Yes | Yes | +| Moodlit | 3073 | 5173 | Yes | Yes | + +### Apps without Servers (sync + web only) + +These apps use only IndexedDB + mana-sync, no server-side compute: + +| App | Web Port | `local` | `full` | +|-----|----------|---------|--------| +| Zitare | 5107 | Yes | Yes | +| Clock | 5187 | Yes | Yes | +| SkilltTree | 5195 | Yes | Yes | +| Photos | 5189 | Yes | Yes | +| CityCorners | 5196 | Yes | Yes | +| Inventar | 5190 | Yes | Yes | +| Times | 5197 | Yes | Yes | +| Calc | 5198 | Yes | Yes | +| ManaVoxel | 5028 | Yes | Yes | + +### Infrastructure Ports + +| Service | Port | Description | +|---------|------|-------------| +| mana-auth | 3001 | Central auth (Better Auth + EdDSA JWT) | +| mana-sync | 3050 | Data sync (Go, WebSocket, field-level LWW) | +| PostgreSQL | 5432 | Database | +| Redis | 6379 | Cache | +| MinIO API | 9000 | S3-compatible storage | +| MinIO Console | 9001 | Storage web UI | ## Prerequisites @@ -39,41 +98,65 @@ Before running any `dev:*:full` command: # 1. Start Docker infrastructure (PostgreSQL, Redis, MinIO) pnpm docker:up -# 2. Generate environment files (runs automatically on pnpm install) +# 2. Build mana-sync (first time only, or after Go changes) +pnpm dev:sync:build + +# 3. Generate environment files (runs automatically on pnpm install) pnpm setup:env ``` +For `dev:*:local`, only mana-sync needs to be built. No Docker required unless your server uses a database. + ## Database Setup Commands ### Individual Service Setup ```bash -pnpm setup:db:auth # Setup mana-core-auth database + schema -pnpm setup:db:chat # Setup chat database + schema -pnpm setup:db:zitare # Setup zitare database + schema -pnpm setup:db:contacts # Setup contacts database + schema -pnpm setup:db:calendar # Setup calendar database + schema -pnpm setup:db:clock # Setup clock database + schema -pnpm setup:db:todo # Setup todo database + schema -pnpm setup:db:picture # Setup picture database + schema +pnpm setup:db:auth # Setup mana-auth database + schema +pnpm setup:db:chat # Setup chat database + schema +pnpm setup:db:todo # Setup todo database + schema +pnpm setup:db:contacts # Setup contacts database + schema +pnpm setup:db:calendar # Setup calendar database + schema +pnpm setup:db:picture # Setup picture database + schema +pnpm setup:db:uload # Setup uload database + schema +pnpm setup:db:context # Setup context database + schema +pnpm setup:db:storage # Setup storage database + schema +pnpm setup:db:mukke # Setup mukke database + schema +pnpm setup:db:planta # Setup planta database + schema +pnpm setup:db:nutriphi # Setup nutriphi database + schema +pnpm setup:db:questions # Setup questions database + schema +pnpm setup:db:traces # Setup traces database + schema ``` ### Setup All Databases ```bash -pnpm setup:db # Creates ALL databases and pushes ALL schemas +pnpm setup:db # Creates ALL databases and pushes ALL schemas ``` This is useful when setting up a fresh environment or after pulling new schema changes. ## How It Works +### Architecture + +``` +Guest: App -> IndexedDB (Dexie.js) -> UI (no sync) +Logged in: App -> IndexedDB -> UI -> SyncEngine -> mana-sync (Go) -> PostgreSQL + <- WebSocket push <- +``` + +**mana-sync** (Go, port 3050) handles WebSocket-based sync with field-level last-write-wins conflict resolution. All CRUD goes through IndexedDB first, making the UI instant regardless of network. + +**Hono/Bun servers** handle operations that cannot run client-side: +- AI/LLM calls (chat, questions, picture generation) +- File operations (storage, media processing) +- RRULE expansion and reminder scheduling (todo, calendar) +- Server-side data processing (news extraction, transcription) + ### Docker Init Script -On first `pnpm docker:up`, the PostgreSQL container runs `docker/init-db/01-create-databases.sql` which creates all databases: - -- manacore, chat, zitare, contacts, calendar, clock, todo, manadeck -- storage, mail, moodlit, finance, inventory, techbase, voxel_lava, figgos +On first `pnpm docker:up`, the PostgreSQL container runs `docker/init-db/01-create-databases.sql` which creates all databases. ### Setup Script @@ -84,6 +167,16 @@ The `scripts/setup-databases.sh` script: The `--force` flag auto-approves schema changes without interactive prompts. +### What each command starts + +| Command pattern | What it runs | +|-----------------|-------------| +| `dev:*:local` | mana-sync + server (if any) + web | +| `dev:*:full` | DB setup + mana-auth + mana-sync + server (if any) + web | +| `dev:*:server` | Just the Hono/Bun server (`bun run --watch src/index.ts`) | +| `dev:*:web` | Just the SvelteKit web app | +| `dev:*:app` | Server + web (no sync, no auth) | + ## Troubleshooting ### Database doesn't exist @@ -103,17 +196,26 @@ PGPASSWORD=devpassword psql -h localhost -U manacore -d postgres -c "CREATE DATA If you see errors about missing tables/columns: ```bash -# Push the latest schema -pnpm --filter @chat/backend db:push --force +# Push the latest schema from the server package +pnpm --filter @chat/server db:push --force +``` + +### mana-sync not built + +If you see `./server: No such file or directory`: + +```bash +pnpm dev:sync:build ``` ### Port already in use -If auth (port 3001) is already running: +If auth (port 3001) or sync (port 3050) is already running: ```bash # Check what's using the port lsof -i :3001 +lsof -i :3050 # Kill the process if needed kill @@ -134,23 +236,6 @@ pnpm docker:up pnpm setup:db ``` -## Apps Without Full Commands - -Some apps don't have backends or don't use Drizzle: - -| App | Reason | -|-----|--------| -| manacore | No backend (uses other services) | -| manadeck | Backend exists but no db:push | -| bauntown, context, maerchenzauber, memoro, news, nutriphi, presi, quote, reader, storage, wisekeep | No backends | - -For these, use the regular dev commands: - -```bash -pnpm dev:manacore:web -pnpm dev:manadeck:app -``` - ## Adding a New Application ### Step 1: Create Project Structure @@ -160,7 +245,7 @@ Create the standard project structure under `apps/`: ``` apps/newproject/ ├── apps/ -│ ├── backend/ # NestJS API (if needed) +│ ├── server/ # Hono/Bun server (if needed for compute) │ ├── mobile/ # Expo React Native app │ ├── web/ # SvelteKit web app │ └── landing/ # Astro marketing page @@ -170,9 +255,24 @@ apps/newproject/ └── CLAUDE.md # Project documentation ``` -### Step 2: Configure Backend Database (if applicable) +### Step 2: Set Up Local-First Data Layer -If your backend uses Drizzle ORM: +1. Create `apps/newproject/apps/web/src/lib/data/local-store.ts` with `createLocalStore()` +2. Create `apps/newproject/apps/web/src/lib/data/guest-seed.ts` for onboarding data +3. Use `collection.getAll()` / `collection.insert()` in stores (not API calls) +4. In layout: `await store.initialize()`, `store.startSync()` on login + +### Step 3: Create Hono/Bun Server (if needed) + +Only needed if your app requires server-side compute (AI, file ops, etc.). + +Create `apps/newproject/apps/server/` with: +- `src/index.ts` -- Hono app entry point +- `src/config.ts` -- Port and env config +- `src/routes/health.ts` -- Health check endpoint +- `package.json` with `"name": "@newproject/server"` + +### Step 4: Configure Database (if the server uses Drizzle) 1. **Add database to Docker init** (`docker/init-db/01-create-databases.sql`): ```sql @@ -181,84 +281,59 @@ If your backend uses Drizzle ORM: ``` 2. **Add to setup script** (`scripts/setup-databases.sh`): - - In the `setup_service()` function, add a new case: ```bash newproject) create_db_if_not_exists "newproject" - push_schema "@newproject/backend" "newproject" + push_schema "@newproject/server" "newproject" ;; ``` - Also add to the `ALL_DATABASES` array: - ```bash - ALL_DATABASES=( - ... - "newproject" - ) - ``` - - And to the services loop at the bottom: - ```bash - for service in auth chat ... newproject; do - ``` - 3. **Add DATABASE_URL to `.env.development`**: ```env NEWPROJECT_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/newproject ``` -4. **Update `scripts/generate-env.mjs`** to generate the backend `.env` file. +4. **Update `scripts/generate-env.mjs`** to generate the server `.env` file. -### Step 3: Add Package.json Scripts +### Step 5: Add Package.json Scripts Add to root `package.json`: ```json { "scripts": { - // Project-level dev (all apps) - "newproject:dev": "turbo run dev --filter=newproject...", - - // Individual app commands "dev:newproject:web": "pnpm --filter @newproject/web dev", - "dev:newproject:mobile": "pnpm --filter @newproject/mobile dev", - "dev:newproject:backend": "pnpm --filter @newproject/backend dev", - "dev:newproject:landing": "pnpm --filter @newproject/landing dev", - "dev:newproject:app": "turbo run dev --filter=@newproject/web --filter=@newproject/backend", - - // Full dev with auto database setup - "dev:newproject:full": "./scripts/setup-databases.sh newproject && ./scripts/setup-databases.sh auth && concurrently -n auth,backend,web -c blue,green,cyan \"pnpm dev:auth\" \"pnpm dev:newproject:backend\" \"pnpm dev:newproject:web\"", - - // Database shortcuts - "newproject:db:push": "pnpm --filter @newproject/backend db:push", - "newproject:db:studio": "pnpm --filter @newproject/backend db:studio", - - // Setup shortcut + "dev:newproject:server": "cd apps/newproject/apps/server && bun run --watch src/index.ts", + "dev:newproject:local": "concurrently -n sync,server,web -c magenta,yellow,cyan \"pnpm dev:sync\" \"pnpm dev:newproject:server\" \"pnpm dev:newproject:web\"", + "dev:newproject:full": "./scripts/setup-databases.sh newproject && ./scripts/setup-databases.sh auth && concurrently -n auth,sync,server,web -c blue,magenta,yellow,cyan \"pnpm dev:auth\" \"pnpm dev:sync\" \"pnpm dev:newproject:server\" \"pnpm dev:newproject:web\"", "setup:db:newproject": "./scripts/setup-databases.sh newproject" } } ``` -### Step 4: Create Project CLAUDE.md +For apps **without** a server (sync + web only): -Create `apps/newproject/CLAUDE.md` with: -- Project overview -- Structure diagram -- Available commands -- API endpoints (if backend) -- Environment variables -- Tech stack details +```json +{ + "scripts": { + "dev:newproject:web": "pnpm --filter @newproject/web dev", + "dev:newproject:local": "concurrently -n sync,web -c magenta,cyan \"pnpm dev:sync\" \"pnpm dev:newproject:web\"", + "dev:newproject:full": "concurrently -n auth,sync,web -c blue,magenta,cyan \"pnpm dev:auth\" \"pnpm dev:sync\" \"pnpm dev:newproject:web\"" + } +} +``` -See existing projects like `apps/chat/CLAUDE.md` for reference. +### Step 6: Create Project CLAUDE.md -### Step 5: Test the Setup +Create `apps/newproject/CLAUDE.md` with project overview, structure, commands, and API endpoints. + +### Step 7: Test the Setup ```bash -# Create database and push schema -pnpm setup:db:newproject +# Quick start (no auth needed) +pnpm dev:newproject:local -# Start with full dev command +# Full stack (with auth + DB setup) pnpm dev:newproject:full ``` @@ -266,10 +341,13 @@ pnpm dev:newproject:full - [ ] Create project structure under `apps/newproject/` - [ ] Add `pnpm-workspace.yaml` in project root -- [ ] Add database to `docker/init-db/01-create-databases.sql` -- [ ] Add service to `scripts/setup-databases.sh` -- [ ] Add DATABASE_URL to `.env.development` +- [ ] Set up local-store with Dexie.js collections +- [ ] Create guest seed data +- [ ] Add Hono/Bun server (if compute needed) +- [ ] Add database to `docker/init-db/01-create-databases.sql` (if using Drizzle) +- [ ] Add service to `scripts/setup-databases.sh` (if using Drizzle) +- [ ] Add DATABASE_URL to `.env.development` (if using Drizzle) - [ ] Update `scripts/generate-env.mjs` for env generation - [ ] Add scripts to root `package.json` - [ ] Create `CLAUDE.md` with project documentation -- [ ] Test with `pnpm dev:newproject:full` +- [ ] Test with `pnpm dev:newproject:local` diff --git a/docs/MAC_MINI_SERVER.md b/docs/MAC_MINI_SERVER.md index 0140e154f..c04a76f54 100644 --- a/docs/MAC_MINI_SERVER.md +++ b/docs/MAC_MINI_SERVER.md @@ -454,16 +454,15 @@ git pull ### Docker Base Images -Alle Apps werden auf vorgebauten Base Images aufgebaut, um Build-Zeit und Memory-Verbrauch zu reduzieren: +Alle Web-Apps werden auf einem vorgebauten Base Image aufgebaut, um Build-Zeit und Memory-Verbrauch zu reduzieren. Backend-Server verwenden `docker/Dockerfile.hono-server` (Hono + Bun) direkt. | Base Image | Dockerfile | Verwendet von | |------------|-----------|---------------| | `sveltekit-base:local` | `docker/Dockerfile.sveltekit-base` | Alle SvelteKit Web-Apps | -| `nestjs-base:local` | `docker/Dockerfile.nestjs-base` | Alle NestJS Backends | -Die Base Images enthalten alle Shared Packages (`packages/`) vorinstalliert und vorgebaut. App-Dockerfiles müssen nur noch ihren app-spezifischen Code kopieren. +Das Base Image enthaelt alle Shared Packages (`packages/`) vorinstalliert und vorgebaut. App-Dockerfiles muessen nur noch ihren app-spezifischen Code kopieren. -**Base Images neu bauen** wenn sich Shared Packages ändern: +**Base Image neu bauen** wenn sich Shared Packages aendern: ```bash ./scripts/mac-mini/build-app.sh --base diff --git a/docs/PORT_SCHEMA.md b/docs/PORT_SCHEMA.md index bd017f020..bb3171980 100644 --- a/docs/PORT_SCHEMA.md +++ b/docs/PORT_SCHEMA.md @@ -46,23 +46,23 @@ Canonical port assignments for all ManaCore services. This is the single source | 3024 | mana-voice-bot | Python | Voice-to-voice assistant | | 3025-3029 | *(reserved)* | | | -## 3030-3059: App Backends +## 3030-3059: App Compute Servers Only apps that need server-side compute (AI, external APIs, file operations). Pure CRUD apps use mana-sync directly. | Port | Service | Runtime | Description | |------|---------|---------|-------------| -| 3030 | chat-backend | NestJS | AI chat, streaming, spaces | +| 3030 | chat-server | Hono/Bun | AI chat, streaming, spaces | | 3031 | todo-server | Hono/Bun | RRULE expansion, reminders | -| 3032 | calendar-backend | NestJS | CalDAV sync, Google Calendar, notifications | -| 3033 | contacts-backend | NestJS | Google Contacts, vCard import/export | -| 3034 | storage-backend | NestJS | S3 file ops, versioning, shares | -| 3035 | picture-backend | NestJS | Replicate AI, generation orchestration | -| 3036 | manadeck-backend | NestJS | AI card generation | -| 3037 | mukke-backend | NestJS | Audio processing, BPM, ID3 tags | -| 3038 | nutriphi-backend | NestJS | Gemini meal analysis | -| 3039 | planta-backend | NestJS | Gemini plant analysis | +| 3032 | calendar-server | Hono/Bun | CalDAV sync, Google Calendar, notifications | +| 3033 | contacts-server | Hono/Bun | Google Contacts, vCard import/export | +| 3034 | storage-server | Hono/Bun | S3 file ops, versioning, shares | +| 3035 | picture-server | Hono/Bun | Replicate AI, generation orchestration | +| 3036 | manadeck-server | Hono/Bun | AI card generation | +| 3037 | mukke-server | Hono/Bun | Audio processing, BPM, ID3 tags | +| 3038 | nutriphi-server | Hono/Bun | Gemini meal analysis | +| 3039 | planta-server | Hono/Bun | Gemini plant analysis | | 3040 | presi-server | Hono/Bun | Share links | | 3041-3059 | *(reserved)* | | | @@ -78,28 +78,28 @@ Pure CRUD apps use mana-sync directly. ## 5000-5059: Web Frontends (SvelteKit) -| Port | Service | Corresponds to Backend | +| Port | Service | Corresponds to Server | |------|---------|----------------------| | 5000 | mana-web | Hub/Dashboard | -| 5010 | chat-web | 3030 chat-backend | +| 5010 | chat-web | 3030 chat-server | | 5011 | todo-web | 3031 todo-server | -| 5012 | calendar-web | 3032 calendar-backend | +| 5012 | calendar-web | 3032 calendar-server | | 5013 | clock-web | *(local-first only)* | -| 5014 | contacts-web | 3033 contacts-backend | -| 5015 | storage-web | 3034 storage-backend | +| 5014 | contacts-web | 3033 contacts-server | +| 5015 | storage-web | 3034 storage-server | | 5016 | presi-web | 3040 presi-server | -| 5017 | nutriphi-web | 3038 nutriphi-backend | +| 5017 | nutriphi-web | 3038 nutriphi-server | | 5018 | zitare-web | *(local-first only)* | | 5019 | photos-web | *(local-first + mana-media)* | | 5020 | skilltree-web | *(local-first only)* | -| 5021 | picture-web | 3035 picture-backend | +| 5021 | picture-web | 3035 picture-server | | 5022 | citycorners-web | *(local-first only)* | -| 5023 | manadeck-web | 3036 manadeck-backend | -| 5024 | mukke-web | 3037 mukke-backend | +| 5023 | manadeck-web | 3036 manadeck-server | +| 5024 | mukke-web | 3037 mukke-server | | 5025 | inventar-web | *(local-first only)* | | 5026 | context-web | *(local-first only)* | | 5027 | questions-web | *(local-first only)* | -| 5028 | planta-web | 3039 planta-backend | +| 5028 | planta-web | 3039 planta-server | | 5029 | moodlit-web | *(future)* | | 5030-5049 | *(reserved)* | | diff --git a/docs/PROJECT_OVERVIEW.md b/docs/PROJECT_OVERVIEW.md index 62debd486..50b9ec0ca 100644 --- a/docs/PROJECT_OVERVIEW.md +++ b/docs/PROJECT_OVERVIEW.md @@ -412,7 +412,7 @@ nutriphi/ │ ├── mobile/ # Expo React Native App (@nutriphi/mobile) │ ├── web/ # SvelteKit Web App (@nutriphi/web) │ └── landing/ # Astro Landing Page (@nutriphi/landing) -├── backend/ # NestJS API Server (@nutriphi/backend) +├── server/ # Hono/Bun server (@nutriphi/server) ``` #### API Endpoints diff --git a/scripts/generate-env.mjs b/scripts/generate-env.mjs index 54f71fd57..ead468187 100644 --- a/scripts/generate-env.mjs +++ b/scripts/generate-env.mjs @@ -70,9 +70,9 @@ const APP_CONFIGS = [ }, }, - // Chat Backend + // Chat Server (Hono/Bun) { - path: 'apps/chat/apps/backend/.env', + path: 'apps/chat/apps/server/.env', vars: { NODE_ENV: () => 'development', PORT: (env) => env.CHAT_BACKEND_PORT || '3002', @@ -130,9 +130,9 @@ const APP_CONFIGS = [ }, }, - // Manadeck Backend + // Manadeck Server (Hono/Bun) { - path: 'apps/manadeck/apps/backend/.env', + path: 'apps/manadeck/apps/server/.env', vars: { NODE_ENV: () => 'development', PORT: (env) => env.MANADECK_BACKEND_PORT || '3004', @@ -154,9 +154,9 @@ const APP_CONFIGS = [ }, }, - // Picture Backend (NestJS) + // Picture Server (Hono/Bun) { - path: 'apps/picture/apps/backend/.env', + path: 'apps/picture/apps/server/.env', vars: { NODE_ENV: () => 'development', PORT: (env) => env.PICTURE_BACKEND_PORT || '3006', @@ -204,9 +204,9 @@ const APP_CONFIGS = [ }, }, - // Nutriphi Backend (NestJS) + // Nutriphi Server (Hono/Bun) { - path: 'apps/nutriphi/apps/backend/.env', + path: 'apps/nutriphi/apps/server/.env', vars: { NODE_ENV: () => 'development', PORT: (env) => env.NUTRIPHI_BACKEND_PORT || '3002', @@ -282,9 +282,9 @@ const APP_CONFIGS = [ }, }, - // Mana Games Backend (NestJS) + // Arcade Backend (NestJS) { - path: 'games/mana-games/apps/backend/.env', + path: 'games/arcade/apps/backend/.env', vars: { NODE_ENV: () => 'development', PORT: (env) => env.MANA_GAMES_BACKEND_PORT || '3011', @@ -304,17 +304,17 @@ const APP_CONFIGS = [ }, }, - // Mana Games Web (Astro) + // Arcade Web (Astro) { - path: 'games/mana-games/apps/web/.env', + path: 'games/arcade/apps/web/.env', vars: { PUBLIC_BACKEND_URL: (env) => `http://localhost:${env.MANA_GAMES_BACKEND_PORT || '3011'}`, }, }, - // Context Backend (NestJS) + // Context Server (Hono/Bun) { - path: 'apps/context/apps/backend/.env', + path: 'apps/context/apps/server/.env', vars: { NODE_ENV: () => 'development', PORT: (env) => env.CONTEXT_BACKEND_PORT || '3020', @@ -338,9 +338,9 @@ const APP_CONFIGS = [ }, }, - // Calendar Backend (NestJS) + // Calendar Server (Hono/Bun) { - path: 'apps/calendar/apps/backend/.env', + path: 'apps/calendar/apps/server/.env', vars: { NODE_ENV: () => 'development', PORT: (env) => env.CALENDAR_BACKEND_PORT || '3014', @@ -379,9 +379,9 @@ const APP_CONFIGS = [ }, }, - // Contacts Backend (NestJS) + // Contacts Server (Hono/Bun) { - path: 'apps/contacts/apps/backend/.env', + path: 'apps/contacts/apps/server/.env', vars: { NODE_ENV: () => 'development', PORT: (env) => env.CONTACTS_BACKEND_PORT || '3015', @@ -422,9 +422,9 @@ const APP_CONFIGS = [ }, }, - // Storage Backend (NestJS) + // Storage Server (Hono/Bun) { - path: 'apps/storage/apps/backend/.env', + path: 'apps/storage/apps/server/.env', vars: { NODE_ENV: () => 'development', PORT: (env) => env.STORAGE_BACKEND_PORT || '3016', @@ -467,9 +467,9 @@ const APP_CONFIGS = [ }, }, - // Todo Backend (NestJS) + // Todo Server (Hono/Bun) { - path: 'apps/todo/apps/backend/.env', + path: 'apps/todo/apps/server/.env', vars: { NODE_ENV: () => 'development', PORT: (env) => env.TODO_BACKEND_PORT || '3018', @@ -492,9 +492,9 @@ const APP_CONFIGS = [ }, }, - // Moodlit Backend (NestJS) + // Moodlit Server (Hono/Bun) { - path: 'apps/moodlit/apps/backend/.env', + path: 'apps/moodlit/apps/server/.env', vars: { NODE_ENV: () => 'development', PORT: (env) => env.MOODLIT_BACKEND_PORT || '3012', @@ -524,37 +524,7 @@ const APP_CONFIGS = [ }, }, - // Finance Backend (NestJS) - { - path: 'apps/finance/apps/backend/.env', - vars: { - NODE_ENV: () => 'development', - PORT: (env) => env.FINANCE_BACKEND_PORT || '3019', - DATABASE_URL: (env) => env.FINANCE_DATABASE_URL, - MANA_CORE_AUTH_URL: (env) => env.MANA_CORE_AUTH_URL, - DEV_BYPASS_AUTH: () => 'true', - DEV_USER_ID: (env) => env.DEV_USER_ID || '00000000-0000-0000-0000-000000000000', - CORS_ORIGINS: (env) => env.CORS_ORIGINS, - }, - }, - - // Finance Mobile (Expo) - { - path: 'apps/finance/apps/mobile/.env', - vars: { - EXPO_PUBLIC_BACKEND_URL: (env) => `http://localhost:${env.FINANCE_BACKEND_PORT || '3019'}`, - EXPO_PUBLIC_MANA_CORE_AUTH_URL: (env) => env.MANA_CORE_AUTH_URL, - }, - }, - - // Finance Web (SvelteKit) - { - path: 'apps/finance/apps/web/.env', - vars: { - PUBLIC_BACKEND_URL: (env) => `http://localhost:${env.FINANCE_BACKEND_PORT || '3019'}`, - PUBLIC_MANA_CORE_AUTH_URL: (env) => env.MANA_CORE_AUTH_URL, - }, - }, + // Finance: REMOVED // Worldream Web (SvelteKit) { @@ -576,29 +546,11 @@ const APP_CONFIGS = [ }, }, - // TechBase Backend (NestJS) - { - path: 'apps/techbase/apps/backend/.env', - vars: { - NODE_ENV: () => 'development', - PORT: (env) => env.TECHBASE_BACKEND_PORT || '3021', - DATABASE_URL: (env) => env.TECHBASE_DATABASE_URL, - MANA_CORE_AUTH_URL: (env) => env.MANA_CORE_AUTH_URL, - CORS_ORIGINS: () => 'http://localhost:4321,http://localhost:5173', - }, - }, + // TechBase: REMOVED - // TechBase Web (Astro) + // Traces Server (Hono/Bun) { - path: 'apps/techbase/apps/web/.env', - vars: { - PUBLIC_BACKEND_URL: (env) => `http://localhost:${env.TECHBASE_BACKEND_PORT || '3021'}`, - }, - }, - - // Traces Backend (NestJS) - { - path: 'apps/traces/apps/backend/.env', + path: 'apps/traces/apps/server/.env', vars: { NODE_ENV: () => 'development', PORT: (env) => env.TRACES_BACKEND_PORT || '3026', @@ -657,9 +609,9 @@ const APP_CONFIGS = [ }, }, - // Mukke Backend (NestJS) + // Mukke Server (Hono/Bun) { - path: 'apps/mukke/apps/backend/.env', + path: 'apps/mukke/apps/server/.env', vars: { NODE_ENV: () => 'development', PORT: (env) => env.MUKKE_BACKEND_PORT || '3010', diff --git a/scripts/mac-mini/build-app.sh b/scripts/mac-mini/build-app.sh index f3726a8cc..6b9f8c972 100755 --- a/scripts/mac-mini/build-app.sh +++ b/scripts/mac-mini/build-app.sh @@ -111,11 +111,6 @@ build_base_images() { $DOCKER build -f "$PROJECT_ROOT/docker/Dockerfile.sveltekit-base" -t sveltekit-base:local "$PROJECT_ROOT" echo "sveltekit-base:local built." echo "" - - echo "=== Building nestjs-base image ===" - $DOCKER build -f "$PROJECT_ROOT/docker/Dockerfile.nestjs-base" -t nestjs-base:local "$PROJECT_ROOT" - echo "nestjs-base:local built." - echo "" } build_services() { diff --git a/scripts/setup-databases.sh b/scripts/setup-databases.sh index 11222159d..78e56822a 100755 --- a/scripts/setup-databases.sh +++ b/scripts/setup-databases.sh @@ -66,13 +66,8 @@ ALL_DATABASES=( "manadeck" "storage" "presi" - "mail" "moodlit" - "finance" "inventory" - "techbase" - "voxel_lava" - "figgos" "planta" "nutriphi" "photos" @@ -108,7 +103,7 @@ setup_service() { ;; chat) create_db_if_not_exists "chat" - push_schema "@chat/backend" "chat" + push_schema "@chat/server" "chat" ;; zitare) create_db_if_not_exists "zitare" @@ -116,11 +111,11 @@ setup_service() { ;; contacts) create_db_if_not_exists "contacts" - push_schema "@contacts/backend" "contacts" + # Schema managed by mana-sync (local-first) ;; calendar) create_db_if_not_exists "calendar" - push_schema "@calendar/backend" "calendar" + # Schema managed by mana-sync (local-first) ;; clock) create_db_if_not_exists "clock" @@ -128,47 +123,31 @@ setup_service() { ;; todo) create_db_if_not_exists "todo" - push_schema "@todo/backend" "todo" + push_schema "@todo/server" "todo" ;; manadeck) create_db_if_not_exists "manadeck" - push_schema "@manadeck/backend" "manadeck" - ;; - mail) - create_db_if_not_exists "mail" - push_schema "@mail/backend" "mail" + # Schema managed by mana-sync (local-first) ;; moodlit) create_db_if_not_exists "moodlit" - push_schema "@moodlit/backend" "moodlit" + push_schema "@moodlit/server" "moodlit" ;; picture) create_db_if_not_exists "picture" - push_schema "@picture/backend" "picture" + # Schema managed by mana-sync (local-first) ;; photos) create_db_if_not_exists "photos" # Schema managed by mana-sync (backend removed) ;; - finance) - create_db_if_not_exists "finance" - push_schema "@finance/backend" "finance" - ;; - voxel-lava) - create_db_if_not_exists "voxel_lava" - push_schema "@voxel-lava/backend" "voxel-lava" - ;; - figgos) - create_db_if_not_exists "figgos" - push_schema "@figgos/backend" "figgos" - ;; planta) create_db_if_not_exists "planta" - push_schema "@planta/backend" "planta" + push_schema "@planta/server" "planta" ;; nutriphi) create_db_if_not_exists "nutriphi" - push_schema "@nutriphi/backend" "nutriphi" + # Schema managed by mana-sync (local-first) ;; presi) create_db_if_not_exists "presi" @@ -176,7 +155,7 @@ setup_service() { ;; storage) create_db_if_not_exists "storage" - push_schema "@storage/backend" "storage" + # Schema managed by mana-sync (local-first) ;; projectdoc) create_db_if_not_exists "projectdoc" @@ -196,7 +175,7 @@ setup_service() { ;; questions) create_db_if_not_exists "questions" - push_schema "@questions/backend" "questions" + # Schema managed by mana-sync (local-first) ;; skilltree) create_db_if_not_exists "skilltree" @@ -204,15 +183,15 @@ setup_service() { ;; mukke) create_db_if_not_exists "mukke" - push_schema "@mukke/backend" "mukke" + push_schema "@mukke/server" "mukke" ;; traces) create_db_if_not_exists "traces" - push_schema "@traces/backend" "traces" + push_schema "@traces/server" "traces" ;; context) create_db_if_not_exists "context" - push_schema "@context/backend" "context" + push_schema "@context/server" "context" ;; citycorners) create_db_if_not_exists "citycorners" @@ -224,7 +203,7 @@ setup_service() { ;; *) echo -e "${RED}Unknown service: $service${NC}" - echo "Available services: auth, chat, zitare, contacts, calendar, clock, todo, manadeck, mail, moodlit, picture, photos, finance, voxel-lava, figgos, planta, nutriphi, presi, storage, projectdoc, zitare_bot, todo_bot, nutriphi_bot, questions, skilltree, mukke, traces, context, citycorners, uload" + echo "Available services: auth, chat, zitare, contacts, calendar, clock, todo, manadeck, moodlit, picture, photos, planta, nutriphi, presi, storage, projectdoc, zitare_bot, todo_bot, nutriphi_bot, questions, skilltree, mukke, traces, context, citycorners, uload" exit 1 ;; esac @@ -248,7 +227,7 @@ echo -e "\n${GREEN}Step 2: Pushing schemas${NC}" echo "--------------------------------------" # Push schemas for all known services -for service in auth chat zitare contacts calendar clock todo manadeck picture photos mail moodlit finance voxel-lava figgos planta nutriphi presi storage questions skilltree mukke traces context citycorners; do +for service in auth chat zitare contacts calendar clock todo manadeck picture photos moodlit planta nutriphi presi storage questions skilltree mukke traces context citycorners; do setup_service "$service" 2>/dev/null || true done diff --git a/scripts/validate-dockerfiles.mjs b/scripts/validate-dockerfiles.mjs index 120166123..42ae85343 100755 --- a/scripts/validate-dockerfiles.mjs +++ b/scripts/validate-dockerfiles.mjs @@ -90,12 +90,6 @@ function getWorkspaceDeps(pkgJsonPath) { return deps; } -// Check if a Dockerfile uses nestjs-base:local as its base image -function usesNestjsBase(dockerfilePath) { - const content = readFileSync(dockerfilePath, 'utf8'); - return content.includes('FROM nestjs-base:local'); -} - // Check if a Dockerfile is a non-monorepo build (standalone, no workspace COPY needed) function isStandaloneBuild(dockerfilePath) { const content = readFileSync(dockerfilePath, 'utf8'); @@ -133,22 +127,6 @@ function getDockerfileCopyPaths(dockerfilePath) { return { copyPaths, hasPatchesCopy }; } -// Get packages pre-built in nestjs-base image -function getNestjsBasePackages() { - const baseDockerfile = join(ROOT, 'docker', 'Dockerfile.nestjs-base'); - if (!existsSync(baseDockerfile)) return new Set(); - const content = readFileSync(baseDockerfile, 'utf8'); - const paths = new Set(); - for (const line of content.split('\n')) { - const trimmed = line.trim(); - const copyMatch = trimmed.match(/^COPY\s+(packages\/\S+)/); - if (copyMatch) { - paths.add(copyMatch[1]); - } - } - return paths; -} - // Extract @scope/package imports from a source file function extractImports(filePath) { if (!existsSync(filePath)) return []; @@ -173,13 +151,7 @@ function extractImports(filePath) { // Validate a single Dockerfile and return result function validateDockerfile(dockerfilePath, pkgJsonPath, relPath, packageMap, opts = {}) { - const { - isNestjsBase = false, - nestjsBasePackagePaths = new Set(), - checkImports = false, - appDir = null, - checkPatches = false, - } = opts; + const { checkImports = false, appDir = null, checkPatches = false } = opts; if (!existsSync(pkgJsonPath)) { return { @@ -203,14 +175,6 @@ function validateDockerfile(dockerfilePath, pkgJsonPath, relPath, packageMap, op continue; } - // For nestjs-base backends, packages/* are pre-built in the base image - if (isNestjsBase && dirPath.startsWith('packages/')) { - const isCoveredByBase = [...nestjsBasePackagePaths].some( - (bp) => bp === dirPath || dirPath.startsWith(bp + '/') || bp.startsWith(dirPath) - ); - if (isCoveredByBase) continue; - } - // Check if any COPY path matches or is a parent directory of this package const found = [...copyPaths].some( (cp) => cp === dirPath || dirPath.startsWith(cp + '/') || cp.startsWith(dirPath) @@ -221,7 +185,7 @@ function validateDockerfile(dockerfilePath, pkgJsonPath, relPath, packageMap, op } // Check patches (only for web apps that need them) - if (checkPatches && !isNestjsBase && !hasPatchesCopy) { + if (checkPatches && !hasPatchesCopy) { errors.push('MISSING: patches/ directory → add: COPY patches/ ./patches/'); } @@ -281,7 +245,6 @@ function main() { const servicesDir = join(ROOT, 'services'); let hasErrors = false; const results = []; - const nestjsBasePackagePaths = getNestjsBasePackages(); // Find all app directories const appDirs = readdirSync(appsDir, { withFileTypes: true }) @@ -317,12 +280,8 @@ function main() { const pkgJsonPath = join(appsDir, appName, 'apps', 'backend', 'package.json'); const relPath = `apps/${appName}/apps/backend/Dockerfile`; - const isNestjsBase = usesNestjsBase(dockerfilePath); - const result = validateDockerfile(dockerfilePath, pkgJsonPath, relPath, packageMap, { - isNestjsBase, - nestjsBasePackagePaths, - }); + const result = validateDockerfile(dockerfilePath, pkgJsonPath, relPath, packageMap); if (result.errors.length > 0) hasErrors = true; results.push(result); printResult(result);