feat: major update with network graphs, themes, todo extensions, and more

## New Features

### Network Graph Visualization (Contacts, Calendar, Todo)
- D3.js force simulation for physics-based layout
- Zoom & pan with mouse/touchpad
- Keyboard shortcuts: +/- zoom, 0 reset, Esc deselect, / search, F focus
- Filtering by tags, company/location/project, connection strength
- Shared components in @manacore/shared-ui

### Central Tags API (mana-core-auth)
- CRUD endpoints for tags
- Schema: tags table with userId, name, color, app
- Shared tag components in @manacore/shared-ui

### Custom Themes System
- Theme editor with live preview and color picker
- Community theme gallery
- Theme sharing (public, unlisted, private)
- Backend API in mana-core-auth

### Todo App Extensions
- Glass-pill design for task input and items
- Settings page with 20+ preferences
- Task edit modal with inline editing
- Statistics page with visualizations
- PWA support with offline capabilities
- Multiple kanban boards

### Contacts App Features
- Duplicate detection
- Photo upload
- Batch operations
- Enhanced favorites page with multiple view modes
- Alphabet view improvements
- Search modal

### Help System
- @manacore/shared-help-content
- @manacore/shared-help-ui
- @manacore/shared-help-types

### Other Features
- Themes page for all apps
- Referral system frontend
- CommandBar (global search)
- Skeleton loaders
- Settings page improvements

## Bug Fixes
- Network graph simulation initialization
- Database schema TEXT for user_id columns (Better Auth compatibility)
- Various styling fixes

## Documentation
- Daily report for 2025-12-10
- CI/CD deployment guide

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2025-12-10 02:37:46 +01:00
parent e84371aa94
commit ee42b6cc76
381 changed files with 39284 additions and 6275 deletions

View file

@ -80,7 +80,7 @@ The manacore-monorepo uses a comprehensive CI/CD pipeline with the following fea
- **Docker Hub**: For image storage (or alternative registry)
- **Supabase**: For database services
- **Azure**: For OpenAI services
- **Hetzner + Docker Compose**: For hosting (recommended)
- **Hetzner/Coolify**: For hosting (recommended)
### GitHub Secrets

View file

@ -745,20 +745,20 @@ pnpm docker:down
---
### 2. Production Orchestration (Docker Compose)
### 2. Production Orchestration (Coolify)
**Production Configuration:** `docker-compose.production.yml`
**Coolify Configuration:** `.coolify/docker-compose.prod.yml`
```yaml
version: '3.9'
# Production Docker Compose Deployment
# With:
# - Automatic SSL (Certbot/Let's Encrypt)
# Production Docker Compose for Coolify Deployment
# Coolify will handle:
# - Automatic SSL (Let's Encrypt)
# - Health check monitoring
# - Auto-restart on failure
# - Log aggregation
# - Resource limits
# - Nginx reverse proxy
services:
chat-backend:
@ -785,18 +785,19 @@ services:
retries: 3
start_period: 40s
labels:
- "com.manacore.project=chat"
- "com.manacore.service=backend"
- "com.manacore.port=3002"
- "com.manacore.domain=api-chat.manacore.app"
- "coolify.managed=true"
- "coolify.project=chat"
- "coolify.service=backend"
- "coolify.port=3002"
- "coolify.domain=api-chat.manacore.app"
```
**Docker Compose Deployment Strategy:**
**Coolify Deployment Strategy:**
1. **Per-project services**: Each project (chat, picture, etc.) deployed as separate service stack
2. **Shared infrastructure**: PostgreSQL and Redis in dedicated compose file
3. **Manual scaling**: Scale with `docker compose up --scale service=N`
4. **Blue-green deployments**: Scripted zero-downtime deployment via Nginx
1. **Per-project services**: Each project (chat, maerchenzauber, etc.) deployed as separate Coolify application
2. **Resource pools**: Shared PostgreSQL and Redis as Coolify resources
3. **Auto-scaling**: Configure horizontal scaling based on CPU/memory
4. **Blue-green deployments**: Coolify's native zero-downtime deployment
---
@ -966,7 +967,7 @@ k8s/
│ │
│ [Development] → [Staging] → [Production] │
│ ↓ ↓ ↓ │
│ Local Docker Docker Docker/K8s
│ Local Docker Coolify Coolify/K8s
│ 127.0.0.1 staging.* app domains │
│ Hot reload Manual test Blue-green │
│ No SSL Let's Encrypt Let's Encrypt │
@ -1041,7 +1042,7 @@ k8s/
│ BLUE-GREEN DEPLOYMENT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ [Load Balancer / Nginx Proxy]
│ [Load Balancer / Coolify Proxy]
│ ↓ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ BLUE (Live) │ │ GREEN (Standby) │ │
@ -1065,7 +1066,7 @@ k8s/
```bash
# Instant rollback by switching traffic back to BLUE
./scripts/switch-deployment.sh blue
coolify switch-deployment blue
# Or with Kubernetes
kubectl set image deployment/chat-backend chat-backend=registry.manacore.app/chat-backend:v1.5.2
@ -1245,8 +1246,8 @@ Development → Staging → Production
# Manual trigger (after staging validation)
kubectl exec -it chat-backend-pod -- pnpm migration:run
# Or automated (via deploy script)
./scripts/deploy/deploy-hetzner.sh chat-backend --run-migrations
# Or automated (Coolify)
coolify deploy chat-backend --run-migrations
```
**Migration Safety Rules:**
@ -1418,7 +1419,7 @@ async updateCredits(userId: string, amount: number) {
│ - https://docs.manacore.app → API documentation │
│ │
│ All domains: │
│ - SSL via Let's Encrypt (Certbot auto-provision) │
│ - SSL via Let's Encrypt (Coolify auto-provision) │
│ - HTTP/2 enabled │
│ - HSTS headers (max-age=31536000) │
│ - Cloudflare DNS (with proxy for DDoS protection) │
@ -1431,7 +1432,7 @@ async updateCredits(userId: string, amount: number) {
```
Type Name Target Proxy
─────────────────────────────────────────────────────────────────────
A chat.manacore.app 185.230.123.45 (Server IP) Yes
A chat.manacore.app 185.230.123.45 (Coolify IP) Yes
A app-chat.manacore.app 185.230.123.45 Yes
A api-chat.manacore.app 185.230.123.45 No*
CNAME *.manacore.app manacore.app Yes
@ -1443,14 +1444,16 @@ CNAME *.manacore.app manacore.app Yes
### 2. SSL/TLS Certificate Management
**Automatic SSL (Certbot):**
**Coolify Automatic SSL:**
```bash
# Install certbot
apt-get install certbot python3-certbot-nginx
# Configure auto-renewal
systemctl enable certbot.timer
```yaml
# .coolify/settings.yml
ssl:
provider: letsencrypt
email: devops@manacore.app
staging: false # Use production Let's Encrypt
auto_renew: true
renewal_days_before: 30
```
**Manual SSL (Certbot):**
@ -1510,7 +1513,7 @@ server {
- **Simplicity**: Each backend has its own domain
- **Low traffic volume**: Gateway overhead not justified yet
- **Independent scaling**: Services scale independently
- **Nginx routing**: Reverse proxy handles routing
- **Coolify routing**: Built-in reverse proxy handles routing
**Future API Gateway (Kong/Traefik) - When to Adopt:**
@ -1701,13 +1704,13 @@ location ~* \.(html)$ {
**Secret Management:**
- **Development:** `.env.development` (committed to git)
- **Staging/Production:** Environment files or Kubernetes secrets
- **Staging/Production:** Coolify secrets UI or Kubernetes secrets
```bash
# Docker Compose secret injection via .env files
# /opt/manacore/.env.production
AZURE_OPENAI_API_KEY=secret123
DATABASE_URL=postgresql://...
# Coolify secret injection
coolify env set chat-backend \
AZURE_OPENAI_API_KEY=secret123 \
DATABASE_URL=postgresql://...
```
**Kubernetes Secrets:**
@ -2160,14 +2163,14 @@ jobs:
url: https://staging-chat.manacore.app
steps:
- name: Deploy to Staging
- name: Deploy to Coolify (Staging)
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.STAGING_HOST }}
username: ${{ secrets.STAGING_SSH_USER }}
key: ${{ secrets.STAGING_SSH_KEY }}
host: ${{ secrets.COOLIFY_STAGING_HOST }}
username: ${{ secrets.COOLIFY_SSH_USER }}
key: ${{ secrets.COOLIFY_SSH_KEY }}
script: |
cd /opt/manacore/chat-staging
cd /var/lib/coolify/apps/chat-staging
docker compose pull
docker compose up -d --force-recreate
docker compose exec -T chat-backend pnpm migration:run
@ -2189,14 +2192,14 @@ jobs:
url: https://chat.manacore.app
steps:
- name: Deploy to Production
- name: Deploy to Coolify (Production)
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.PRODUCTION_HOST }}
username: ${{ secrets.PRODUCTION_SSH_USER }}
key: ${{ secrets.PRODUCTION_SSH_KEY }}
host: ${{ secrets.COOLIFY_PROD_HOST }}
username: ${{ secrets.COOLIFY_SSH_USER }}
key: ${{ secrets.COOLIFY_SSH_KEY }}
script: |
cd /opt/manacore/chat-production
cd /var/lib/coolify/apps/chat-production
# Blue-green deployment: Deploy to green environment
docker compose -f docker-compose.green.yml pull
@ -2211,8 +2214,8 @@ jobs:
# Health check green environment
curl -f http://localhost:3002/api/health || exit 1
# Switch traffic to green (update Nginx routing)
./scripts/switch-deployment.sh chat green
# Switch traffic to green (update Coolify routing)
coolify switch-deployment chat green
# Keep blue running for 1 hour (rollback window)
# Decommission blue after validation
@ -2353,9 +2356,8 @@ curl -f https://api-chat.manacore.app/api/health
```bash
# 1. Provision new server (same specs)
# 2. Install Docker + Docker Compose
curl -fsSL https://get.docker.com | bash
apt-get update && apt-get install -y docker-compose-plugin
# 2. Install Docker + Coolify
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
# 3. Clone repository
git clone https://github.com/manacore/manacore-monorepo.git
@ -2575,7 +2577,7 @@ services:
### 3. Secrets Management
**Current:** Docker Compose environment files (encrypted at rest)
**Current:** Coolify environment variables UI (encrypted at rest)
**Future:** HashiCorp Vault or AWS Secrets Manager
@ -2689,7 +2691,7 @@ Permissions-Policy: geolocation=(), microphone=(), camera=()
- [ ] Set up GitHub Actions workflows (per project)
- [ ] Configure Docker image registry (GitHub Container Registry)
- [ ] Implement automated testing in CI
- [ ] Set up staging environment with Docker Compose
- [ ] Set up staging environment on Coolify
- [ ] Implement blue-green deployment scripts
### Phase 3: Production Deployment (Week 5-6)

View file

@ -27,7 +27,7 @@
│ (Nginx/Static) │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ Landing Servers │ │ Docker/K8s LB
│ Landing Servers │ │ Coolify/K8s LB
│ - chat.app │ │ (Load Balancer) │
│ - picture.app │ └────────┬─────────┘
│ - memoro.app │ │
@ -181,7 +181,7 @@ TOTAL BUILD TIME:
┌─────────────────────────────────┐
│ Cloudflare / Nginx Proxy
│ Cloudflare / Coolify Proxy
│ - DDoS Protection │
│ - SSL Termination │
│ - Rate Limiting │
@ -237,7 +237,7 @@ NETWORK SECURITY RULES:
SSL/TLS CONFIGURATION:
Certificate Provider: Let's Encrypt (Certbot auto-provision)
Certificate Provider: Let's Encrypt (Coolify auto-provision)
Protocols: TLSv1.2, TLSv1.3
Cipher Suites: HIGH:!aNULL:!MD5:!3DES
HSTS: max-age=31536000; includeSubDomains; preload
@ -270,7 +270,7 @@ SSL/TLS CONFIGURATION:
┌───────────────────────────┐
Nginx Reverse Proxy
Coolify Reverse Proxy
│ - SSL termination │
│ - Route to container │
│ - Health check │
@ -400,7 +400,7 @@ CACHING STRATEGY:
```
┌────────────────────────────────────────────────────────────────────────────────────────┐
│ CI/CD DEPLOYMENT PIPELINE │
│ (GitHub Actions → Docker Compose)
│ (GitHub Actions → Coolify)
└────────────────────────────────────────────────────────────────────────────────────────┘
[Developer]
@ -464,7 +464,7 @@ CACHING STRATEGY:
┌───────────────────────────┐
│ Deploy to Staging │
│ ┌─────────────────────┐ │
│ │ SSH to server │ │
│ │ SSH to Coolify │ │
│ │ docker compose pull │ │
│ │ docker compose up │ │
│ │ pnpm migration:run │ │
@ -604,7 +604,7 @@ DEPLOYMENT TIMELINE:
ROLLBACK PROCEDURE (if needed):
1. Detect issue (error spike, customer reports)
2. Run: ./scripts/switch-deployment.sh chat blue
2. Run: coolify switch-deployment chat blue
3. Traffic reverts to BLUE (v1.5.2) in <30 seconds
4. Investigate issue in GREEN (offline)
5. Fix and redeploy when ready
@ -881,8 +881,8 @@ SECURITY LEVELS:
DEPLOYMENT STAGES:
Development - Local Docker Compose
Staging - Docker Compose (staging server)
Production - Docker Compose (production server)
Staging - Coolify (separate server)
Production - Coolify (production server)
ABBREVIATIONS:
RTO - Recovery Time Objective

View file

@ -29,24 +29,21 @@
- [ ] GitHub account (for CI/CD)
- [ ] Supabase projects created (one per product)
### Step 1: Set up Docker & Docker Compose
### Step 1: Set up Docker Compose
```bash
# SSH into server
ssh root@your-server-ip
# Install Docker
curl -fsSL https://get.docker.com | bash
# Install Docker Compose plugin
apt-get update && apt-get install -y docker-compose-plugin
# Set up Docker Compose (automated installer)
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
# Verify installation
docker --version
docker compose version
coolify --version
# Set up deployment directory
mkdir -p /opt/manacore && cd /opt/manacore
# Access Docker Compose configuration
# Navigate to: http://your-server-ip:8000
# Create admin account
```
### Step 2: Configure DNS
@ -147,7 +144,7 @@ curl -X POST http://localhost:3001/api/auth/register \
}'
```
### Step 7: Configure SSL (Let's Encrypt)
### Step 7: Configure SSL (Coolify Auto)
In Docker Compose configuration:
@ -285,8 +282,8 @@ docker compose --profile picture up -d
# Step 7: Run database migrations
docker compose exec picture-backend pnpm migration:run
# Step 8: Configure Nginx routing
# In Nginx configuration:
# Step 8: Configure Coolify routing
# In Docker Compose configuration:
# - Add new application: picture-backend
# - Domain: api-picture.manacore.app
# - Port: 3005
@ -367,9 +364,9 @@ curl -f http://localhost:3012/api/health
# Step 9: Smoke tests on green
./scripts/smoke-test.sh http://localhost:3012
# Step 10: Switch traffic to green (Nginx)
# Update Nginx configuration to point to green:
./scripts/switch-deployment.sh chat green
# Step 10: Switch traffic to green (Coolify)
# In Docker Compose configuration or via API:
coolify switch-deployment chat green
# Or manually update Nginx:
sudo nano /etc/nginx/sites-available/api-chat.manacore.app
@ -565,7 +562,7 @@ export async function up(db) {
# (If blue environment still running)
# Switch traffic back to blue
./scripts/switch-deployment.sh chat blue
coolify switch-deployment chat blue
# Or manually update Nginx:
sudo nano /etc/nginx/sites-available/api-chat.manacore.app
@ -826,8 +823,8 @@ openssl s_client -connect api-chat.manacore.app:443 -servername api-chat.manacor
# Manually renew certificate
sudo certbot renew --force-renewal
# Or check certbot logs:
cat /var/log/letsencrypt/letsencrypt.log | tail -50
# Or via Coolify:
coolify ssl renew api-chat.manacore.app
# Verification:
curl -I https://api-chat.manacore.app
@ -1006,7 +1003,7 @@ aws s3 cp env-backup-$(date +%Y%m%d).tar.gz.gpg \
# Scenario: Complete server failure, restore to new server
# Step 1: Provision new server
# Install Docker, Docker Compose, dependencies
# Install Docker, Coolify, dependencies
# Step 2: Clone repository
git clone https://github.com/manacore/manacore-monorepo.git

709
docs/SELF-HOSTING-GUIDE.md Normal file
View file

@ -0,0 +1,709 @@
# Self-Hosting Guide - Manacore Monorepo
Komplette Anleitung zum Hosten aller Projekte auf eigener Infrastruktur (VPS).
## Projektübersicht
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ MANACORE MONOREPO │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ MAERCHENZAUBER│ │ MANACORE │ │ MANADECK │ │ MEMORO │ │
│ │ (Storyteller)│ │ (Auth Hub) │ │ (Deck App) │ │ (Voice App) │ │
│ ├──────────────┤ ├──────────────┤ ├──────────────┤ ├──────────────┤ │
│ │ • Web │ │ • Web │ │ • Web │ │ • Web │ │
│ │ • Mobile │ │ • Mobile │ │ • Mobile │ │ • Mobile │ │
│ │ • Landing │ │ • Landing │ │ • Landing │ │ • Landing │ │
│ │ • Backend │ │ │ │ • Backend │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ PICTURE │ │ ULOAD │ │
│ │ (Canvas App) │ │(URL Shortener)│ │
│ ├──────────────┤ ├──────────────┤ │
│ │ • Web │ │ • Web │ │
│ │ • Mobile │ │ │ │
│ │ • Landing │ │ │ │
│ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Technologie-Stack pro Projekt
| Projekt | Web App | Landing | Backend | Mobile | Datenbank |
| ------------------ | --------- | ------- | ------- | ------ | ------------------ |
| **Maerchenzauber** | SvelteKit | Astro | NestJS | Expo | Supabase |
| **Manacore** | SvelteKit | Astro | - | Expo | Supabase |
| **Manadeck** | SvelteKit | Astro | NestJS | Expo | PostgreSQL |
| **Memoro** | SvelteKit | Astro | - | Expo | Supabase |
| **Picture** | SvelteKit | Astro | - | Expo | Supabase |
| **uLoad** | SvelteKit | - | - | - | PostgreSQL + Redis |
---
## Deployment-Optionen im Überblick
### Option A: Single VPS mit Coolify (Empfohlen für Start)
- **Kosten:** ~€15-30/Monat
- **Komplexität:** Niedrig
- **Skalierung:** Begrenzt
### Option B: Multi-VPS mit Coolify
- **Kosten:** ~€50-100/Monat
- **Komplexität:** Mittel
- **Skalierung:** Gut
### Option C: Kubernetes (K3s)
- **Kosten:** ~€30-80/Monat
- **Komplexität:** Hoch
- **Skalierung:** Sehr gut
### Option D: Hybrid (Self-Hosted + Managed)
- **Kosten:** ~€20-50/Monat + Supabase
- **Komplexität:** Niedrig-Mittel
- **Skalierung:** Flexibel
---
# Option A: Single VPS mit Coolify
Die einfachste Lösung für den Start. Alle Services auf einem Server.
## Architektur
```
┌─────────────────────────────────────────────────────────────────┐
│ Hetzner VPS (CX31+) │
│ 4 vCPU, 8GB RAM, 80GB │
├─────────────────────────────────────────────────────────────────┤
│ COOLIFY │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ TRAEFIK │ │
│ │ (Reverse Proxy + SSL) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │ │ │ │
│ ┌──────┴──────┐ ┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐ │
│ │ Web Apps │ │ Backends │ │ Databases │ │ Landing │ │
│ │ (Node.js) │ │ (NestJS) │ │ (PG+Redis)│ │ (Astro) │ │
│ │ :3000-3005 │ │ :4000-4001│ │ :5432,6379│ │ :8080+ │ │
│ └─────────────┘ └───────────┘ └───────────┘ └───────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
## Ressourcen-Anforderungen
| Komponente | RAM | CPU | Disk |
| ----------------- | -------- | ------ | --------- |
| PostgreSQL | 1GB | 0.5 | 10GB |
| Redis | 256MB | 0.2 | 1GB |
| Coolify | 512MB | 0.3 | 5GB |
| Traefik | 128MB | 0.1 | - |
| Pro Web App | 256MB | 0.3 | - |
| Pro Backend | 512MB | 0.5 | - |
| Pro Landing | 64MB | 0.1 | - |
| **Gesamt (alle)** | **~6GB** | **~4** | **~30GB** |
**Empfohlener Server:** Hetzner CX31 (4 vCPU, 8GB RAM, 80GB) - €8.98/Monat
## Schritt-für-Schritt Setup
### 1. VPS bestellen und Coolify installieren
```bash
# SSH zum Server
ssh root@YOUR-IP
# System updaten
apt update && apt upgrade -y
# Coolify installieren
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
```
### 2. Datenbank-Services erstellen
**PostgreSQL:**
```
Name: shared-postgres
Version: 16-alpine
Databases: manacore, manadeck, uload
```
**Redis:**
```
Name: shared-redis
Version: 7-alpine
```
### 3. Projekte deployen
#### Deployment-Reihenfolge (wichtig!)
1. **Datenbanken** (PostgreSQL, Redis)
2. **Backends** (Maerchenzauber, Manadeck)
3. **Web Apps** (alle)
4. **Landing Pages** (alle)
#### Konfiguration pro Projekt
**Alle SvelteKit Web Apps:**
```
Base Directory: /
Dockerfile: {projekt}/apps/web/Dockerfile # Falls vorhanden
oder
Build Pack: Nixpacks
Build Command: cd {projekt}/apps/web && pnpm build
Start Command: cd {projekt}/apps/web && node build
Port: 3000
```
**Alle Astro Landing Pages:**
```
Build Pack: Static
Base Directory: {projekt}/apps/landing
Build Command: pnpm build
Publish Directory: dist
```
**NestJS Backends:**
```
Base Directory: /
Dockerfile: {projekt}/apps/backend/Dockerfile
oder
Dockerfile: {projekt}/backend/Dockerfile
Port: 4000
```
### 4. Domain-Mapping
| Service | Domain | Port |
| ---------------------- | --------------------- | ---- |
| uload-web | ulo.ad | 3000 |
| maerchenzauber-web | app.maerchenzauber.de | 3001 |
| maerchenzauber-landing | maerchenzauber.de | 8080 |
| maerchenzauber-backend | api.maerchenzauber.de | 4000 |
| manacore-web | app.manacore.io | 3002 |
| manacore-landing | manacore.io | 8081 |
| manadeck-web | app.manadeck.de | 3003 |
| manadeck-landing | manadeck.de | 8082 |
| manadeck-backend | api.manadeck.de | 4001 |
| memoro-web | app.memoro.ai | 3004 |
| memoro-landing | memoro.ai | 8083 |
| picture-web | app.picture.io | 3005 |
| picture-landing | picture.io | 8084 |
---
# Option B: Multi-VPS mit Coolify
Bessere Isolation und Skalierung durch mehrere Server.
## Architektur
```
┌─────────────────┐
│ DNS / CDN │
│ (Cloudflare) │
└────────┬────────┘
┌───────────────────────────┼───────────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ VPS 1 │ │ VPS 2 │ │ VPS 3 │
│ (Apps) │ │ (Backends) │ │ (Databases) │
│ CX21 │ │ CX21 │ │ CX31 │
├───────────────┤ ├───────────────┤ ├───────────────┤
│ • Web Apps │ ◄─────► │ • NestJS APIs │ ◄─────► │ • PostgreSQL │
│ • Landing │ │ • Workers │ │ • Redis │
│ Pages │ │ │ │ • Backups │
└───────────────┘ └───────────────┘ └───────────────┘
```
## Server-Aufteilung
### VPS 1: Frontend (CX21 - €4.49/Monat)
- Alle SvelteKit Web Apps
- Alle Astro Landing Pages
- Traefik Reverse Proxy
### VPS 2: Backends (CX21 - €4.49/Monat)
- Maerchenzauber NestJS Backend
- Manadeck NestJS Backend
- Background Workers
### VPS 3: Datenbanken (CX31 - €8.98/Monat)
- PostgreSQL (shared)
- Redis (shared)
- Automated Backups
**Gesamtkosten:** ~€18/Monat
## Einrichtung
### VPS 3 (Datenbanken) zuerst
```bash
# Coolify installieren
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
# PostgreSQL mit externem Zugriff
# In Coolify: Network → Enable External Access
```
### VPS 2 (Backends)
```bash
# Coolify installieren
# Backends deployen mit DATABASE_URL zu VPS 3
```
### VPS 1 (Frontends)
```bash
# Coolify installieren
# Web Apps deployen mit API_URL zu VPS 2
```
## Netzwerk-Sicherheit
```bash
# Auf VPS 3 (Datenbanken): Nur VPS 1+2 erlauben
ufw allow from VPS1-IP to any port 5432
ufw allow from VPS2-IP to any port 5432
ufw allow from VPS1-IP to any port 6379
ufw allow from VPS2-IP to any port 6379
ufw deny 5432
ufw deny 6379
```
---
# Option C: Kubernetes mit K3s
Für maximale Skalierung und Automatisierung.
## Architektur
```
┌─────────────────────────────────────────────────────────────────┐
│ K3s Cluster │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ INGRESS (Traefik) │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────┼───────────────────────────────┐ │
│ │ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Namespace │ │ Namespace │ │ Namespace │ │ │
│ │ │ uload │ │ maerchen- │ │ manadeck │ │ │
│ │ │ │ │ zauber │ │ │ │ │
│ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │ │
│ │ │ │ web:3 │ │ │ │ web:2 │ │ │ │ web:2 │ │ │ │
│ │ │ │ replicas│ │ │ │ backend │ │ │ │ backend │ │ │ │
│ │ │ └─────────┘ │ │ │ landing │ │ │ │ landing │ │ │ │
│ │ └─────────────┘ │ └─────────┘ │ │ └─────────┘ │ │ │
│ │ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ Shared Services │ │ │
│ │ │ PostgreSQL (StatefulSet) │ Redis (StatefulSet) │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ Node 1 (CX21) Node 2 (CX21) Node 3 (CX21) │
└─────────────────────────────────────────────────────────────────┘
```
## K3s Setup
### Master Node installieren
```bash
# Auf Node 1
curl -sfL https://get.k3s.io | sh -
# Token für Worker holen
cat /var/lib/rancher/k3s/server/node-token
```
### Worker Nodes hinzufügen
```bash
# Auf Node 2 und 3
curl -sfL https://get.k3s.io | K3S_URL=https://NODE1-IP:6443 K3S_TOKEN=TOKEN sh -
```
### Helm Charts deployen
```yaml
# values-uload.yaml
replicaCount: 2
image:
repository: ghcr.io/your-org/uload-web
tag: latest
service:
port: 3000
ingress:
enabled: true
hosts:
- host: ulo.ad
paths:
- path: /
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
```
```bash
helm install uload ./charts/sveltekit -f values-uload.yaml
```
## Vorteile K8s
- Auto-Scaling bei Last
- Rolling Updates ohne Downtime
- Self-Healing bei Ausfällen
- Resource Limits pro App
## Nachteile K8s
- Höhere Komplexität
- Mehr Overhead (RAM für K8s selbst)
- Lernkurve
---
# Option D: Hybrid (Self-Hosted + Managed)
Kombination aus Self-Hosting und Managed Services für beste Balance.
## Architektur
```
┌─────────────────────────────────────────────────────────────────┐
│ MANAGED SERVICES │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ SUPABASE │ │ CLOUDFLARE │ │ VERCEL/ │ │
│ │ (Database) │ │ (CDN) │ │ NETLIFY │ │
│ │ PostgreSQL │ │ DNS, Cache │ │ (Landing) │ │
│ │ Auth, Store │ │ DDoS Prot. │ │ Static │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
└─────────┼──────────────────┼──────────────────┼──────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ SELF-HOSTED (VPS) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Hetzner CX21 │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │ Web Apps │ │ Backends │ │ uLoad │ │ │
│ │ │ (SvelteKit)│ │ (NestJS) │ │ + DB │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
## Was wo hosten?
### Managed Services (empfohlen)
| Service | Anbieter | Kosten | Grund |
| ------------- | -------------- | ---------- | ------------------------- |
| Datenbank | Supabase | Free-$25/M | Auth + Realtime inklusive |
| Landing Pages | Vercel/Netlify | Free | CDN + Edge |
| CDN | Cloudflare | Free | DDoS + Caching |
| Email | Resend | Free-$20/M | Deliverability |
| Payments | Stripe | % per Tx | Compliance |
### Self-Hosted (VPS)
| Service | Grund |
| -------- | -------------------------------------- |
| Web Apps | Volle Kontrolle, günstiger bei Traffic |
| Backends | Custom Code, API Keys |
| uLoad | Komplett eigene Infra gewünscht |
| Redis | Falls benötigt |
## Setup
### 1. Supabase Projekt erstellen
Für: Maerchenzauber, Manacore, Memoro, Picture
```bash
# Supabase CLI
supabase init
supabase db push
```
### 2. Landing Pages auf Vercel
```bash
# In jedem Landing-Projekt
cd maerchenzauber/apps/landing
vercel deploy --prod
```
### 3. VPS für Web Apps + Backends
```bash
# Coolify auf Hetzner CX21
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
# Apps deployen mit Supabase URLs
```
## Kosten-Vergleich
| Komponente | Full Self-Hosted | Hybrid |
| ---------- | ---------------- | ------------------ |
| VPS | €9-18/Monat | €4.50/Monat |
| Supabase | - | Free-€25/Monat |
| Vercel | - | Free |
| Cloudflare | - | Free |
| **Gesamt** | **€9-18/Monat** | **€4.50-30/Monat** |
---
# Dockerfiles für alle Projekte
## SvelteKit Web Apps (Template)
Erstelle für jedes Projekt ohne Dockerfile:
```dockerfile
# {projekt}/apps/web/Dockerfile
FROM node:20-alpine AS builder
RUN corepack enable && corepack prepare pnpm@9.15.0 --activate
WORKDIR /app
# Monorepo files
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
COPY {projekt}/apps/web/ ./{projekt}/apps/web/
COPY packages/ ./packages/
# Install and build
RUN pnpm install --filter @{projekt}/web... --shamefully-hoist
WORKDIR /app/{projekt}/apps/web
RUN pnpm build
# Runner
FROM node:20-alpine
RUN adduser -D sveltekit
WORKDIR /app
COPY --from=builder /app/{projekt}/apps/web/build ./build
COPY --from=builder /app/{projekt}/apps/web/package.json ./
COPY --from=builder /app/node_modules ./node_modules
USER sveltekit
ENV NODE_ENV=production PORT=3000
EXPOSE 3000
CMD ["node", "build"]
```
## Astro Landing Pages
```dockerfile
# {projekt}/apps/landing/Dockerfile
FROM node:20-alpine AS builder
RUN corepack enable && corepack prepare pnpm@9.15.0 --activate
WORKDIR /app
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
COPY {projekt}/apps/landing/ ./{projekt}/apps/landing/
COPY packages/ ./packages/
RUN pnpm install --filter @{projekt}/landing... --shamefully-hoist
WORKDIR /app/{projekt}/apps/landing
RUN pnpm build
# Nginx for static files
FROM nginx:alpine
COPY --from=builder /app/{projekt}/apps/landing/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
## NestJS Backends
Bereits vorhanden in:
- `maerchenzauber/apps/backend/Dockerfile`
- `manadeck/backend/Dockerfile`
---
# Environment Variables
## Gemeinsame Variablen (alle Projekte)
```env
NODE_ENV=production
```
## Supabase-basierte Projekte
```env
# Maerchenzauber, Manacore, Memoro, Picture
PUBLIC_SUPABASE_URL=https://xxx.supabase.co
PUBLIC_SUPABASE_ANON_KEY=eyJxx...
SUPABASE_SERVICE_ROLE_KEY=eyJxx... # Nur Backend
```
## PostgreSQL-basierte Projekte
```env
# Manadeck, uLoad
DATABASE_URL=postgresql://user:pass@host:5432/db
```
## Projekt-spezifische Variablen
### Maerchenzauber Backend
```env
AZURE_OPENAI_ENDPOINT=https://xxx.openai.azure.com
AZURE_OPENAI_API_KEY=xxx
GOOGLE_GEMINI_API_KEY=xxx
REPLICATE_API_TOKEN=xxx
GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json
```
### Manadeck Backend
```env
GOOGLE_GEMINI_API_KEY=xxx
```
### uLoad
```env
REDIS_URL=redis://localhost:6379
STRIPE_SECRET_KEY=sk_live_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
RESEND_API_KEY=re_xxx
R2_ACCESS_KEY_ID=xxx
R2_SECRET_ACCESS_KEY=xxx
R2_BUCKET_NAME=xxx
R2_ENDPOINT=https://xxx.r2.cloudflarestorage.com
AUTH_SECRET=xxx
```
---
# Checkliste: Komplettes Self-Hosting
## Infrastruktur
- [ ] VPS bestellt (Hetzner CX21/CX31)
- [ ] SSH-Zugang eingerichtet
- [ ] Coolify installiert
- [ ] Firewall konfiguriert
## Datenbanken
- [ ] PostgreSQL läuft
- [ ] Redis läuft (falls benötigt)
- [ ] Backups eingerichtet
- [ ] Connection Strings notiert
## Projekte (für jedes)
- [ ] Dockerfile erstellt/geprüft
- [ ] Environment Variables gesetzt
- [ ] Domain konfiguriert
- [ ] SSL-Zertifikat aktiv
- [ ] Health-Check funktioniert
## DNS (für jede Domain)
- [ ] A-Record auf Server-IP
- [ ] www CNAME (optional)
- [ ] Propagation geprüft
## Monitoring
- [ ] Logs erreichbar
- [ ] Alerting eingerichtet (optional)
- [ ] Uptime-Monitoring (optional)
## Backups
- [ ] Datenbank-Backup automatisiert
- [ ] Backup-Test durchgeführt
- [ ] Offsite-Backup (optional)
---
# Empfehlung
## Für den Start: Option D (Hybrid)
1. **Supabase** für Datenbank + Auth (Free Tier)
2. **Vercel/Netlify** für Landing Pages (Free)
3. **Hetzner CX21** für Web Apps + Backends (€4.50/Monat)
4. **Cloudflare** für DNS + CDN (Free)
**Vorteile:**
- Schneller Start
- Geringe Kosten
- Managed Auth & Realtime
- Einfache Skalierung später
## Für Wachstum: Option B (Multi-VPS)
Wenn Traffic steigt:
1. Datenbanken auf eigenen VPS migrieren
2. Frontend/Backend trennen
3. Load Balancing hinzufügen
## Für Enterprise: Option C (Kubernetes)
Wenn benötigt:
- Auto-Scaling
- Zero-Downtime Deployments
- Multi-Region
---
# Support & Links
- **Coolify Docs:** https://coolify.io/docs
- **Hetzner:** https://www.hetzner.com/cloud
- **Supabase:** https://supabase.com/docs
- **K3s:** https://k3s.io
- **Traefik:** https://doc.traefik.io/traefik/

451
docs/ULOAD-DEPLOYMENT.md Normal file
View file

@ -0,0 +1,451 @@
# uload Deployment Guide
Schritt-für-Schritt Anleitung zum Deployment von uload mit Coolify auf Hetzner VPS.
## Architektur
```
┌─────────────────────────────────────────────────────────────────┐
│ Hetzner VPS │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Coolify │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ │
│ │ │ uload │ │ PostgreSQL │ │ Redis │ │ │
│ │ │ (Node) │ │ (16) │ │ (7) │ │ │
│ │ │ :3000 │ │ :5432 │ │ :6379 │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────────┘ │ │
│ │ │ │ │ │ │
│ │ └────────────────┴──────────────────┘ │ │
│ │ Traefik (SSL/Proxy) │ │
│ │ :80 / :443 │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
https://ulo.ad
```
---
## Voraussetzungen
- [ ] Hetzner VPS (mindestens CX21: 2 vCPU, 4GB RAM, 40GB SSD)
- [ ] Domain mit DNS-Zugang (z.B. ulo.ad)
- [ ] GitHub Account mit Zugriff auf das Repository
- [ ] Accounts für externe Services:
- Resend (Email)
- Stripe (Payments)
- Cloudflare R2 (Storage)
---
## Schritt 1: Hetzner VPS einrichten
### 1.1 Server erstellen
1. Gehe zu [Hetzner Cloud Console](https://console.hetzner.cloud)
2. Erstelle neues Projekt oder wähle bestehendes
3. Klicke **Add Server**
4. Wähle:
- **Location:** Falkenstein oder Nürnberg (DE)
- **Image:** Ubuntu 22.04
- **Type:** CX21 (2 vCPU, 4GB RAM) oder größer
- **SSH Key:** Füge deinen öffentlichen SSH-Key hinzu
5. Klicke **Create & Buy Now**
6. Notiere die **IP-Adresse**
### 1.2 Mit Server verbinden
```bash
ssh root@DEINE-SERVER-IP
```
### 1.3 System updaten
```bash
apt update && apt upgrade -y
```
---
## Schritt 2: Coolify installieren
### 2.1 Installation
```bash
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
```
Die Installation dauert ca. 2-5 Minuten.
### 2.2 Coolify öffnen
1. Öffne im Browser: `http://DEINE-SERVER-IP:8000`
2. Erstelle Admin-Account (E-Mail + Passwort)
3. Wähle **Self-hosted** als Instance Type
4. Der Server "localhost" wird automatisch hinzugefügt
---
## Schritt 3: PostgreSQL Datenbank erstellen
### 3.1 In Coolify
1. Klicke **+ New Resource**
2. Wähle **Database**
3. Wähle **PostgreSQL**
4. Konfiguriere:
- **Name:** `uload-postgres`
- **Version:** `16-alpine`
- **Database Name:** `uload`
- **Database User:** `uload`
- **Password:** (automatisch generiert oder eigenes)
5. Klicke **Start**
### 3.2 Connection String notieren
Nach dem Start findest du unter **Connect** die Internal URL:
```
postgresql://uload:PASSWORT@uload-postgres:5432/uload
```
**Wichtig:** Kopiere diese URL - du brauchst sie später!
---
## Schritt 4: Redis erstellen (optional, aber empfohlen)
### 4.1 In Coolify
1. Klicke **+ New Resource**
2. Wähle **Database**
3. Wähle **Redis**
4. Konfiguriere:
- **Name:** `uload-redis`
- **Version:** `7-alpine`
5. Klicke **Start**
### 4.2 Connection String notieren
```
redis://uload-redis:6379
```
---
## Schritt 5: GitHub Repository verbinden
### 5.1 GitHub App erstellen
1. In Coolify: Gehe zu **Sources** (linke Sidebar)
2. Klicke **+ Add**
3. Wähle **GitHub App**
4. Klicke **Register GitHub App**
5. Du wirst zu GitHub weitergeleitet
6. Gib der App einen Namen (z.B. "coolify-uload")
7. Klicke **Create GitHub App**
8. Installiere die App für dein Repository
### 5.2 Repository-Zugriff gewähren
1. Wähle **Only select repositories**
2. Wähle `manacore-monorepo`
3. Klicke **Install**
---
## Schritt 6: uload Application erstellen
### 6.1 Neue Application
1. Klicke **+ New Resource**
2. Wähle **Application**
3. Wähle deine **GitHub App** als Source
4. Wähle das Repository `manacore-monorepo`
5. Wähle Branch: `main`
### 6.2 Build-Konfiguration (WICHTIG!)
Da uload Teil eines Monorepos ist, muss die Build-Konfiguration genau so sein:
| Einstellung | Wert |
| ----------------------- | -------------------------- |
| **Base Directory** | `/` (leer lassen oder `/`) |
| **Build Pack** | Dockerfile |
| **Dockerfile Location** | `uload/Dockerfile` |
| **Port Exposes** | `3000` |
**Warum `/` als Base Directory?**
Das Dockerfile benötigt Zugriff auf:
- `uload/apps/web/` (die App)
- `packages/shared-*` (gemeinsame Packages)
- `pnpm-workspace.yaml` und `pnpm-lock.yaml` (Workspace-Config)
---
## Schritt 7: Environment Variables setzen
### 7.1 In Coolify
Gehe zu deiner Application → **Environment Variables** → **Add Variable**
### 7.2 Erforderliche Variablen
```env
# === APP ===
NODE_ENV=production
PORT=3000
HOST=0.0.0.0
ORIGIN=https://ulo.ad
# === DATABASE ===
# Von Schritt 3.2 - PostgreSQL Internal URL
DATABASE_URL=postgresql://uload:DEIN-PASSWORT@uload-postgres:5432/uload
# === REDIS (optional) ===
# Von Schritt 4.2
REDIS_URL=redis://uload-redis:6379
# === AUTH ===
# Generiere mit: openssl rand -base64 32
AUTH_SECRET=GENERIERE-EINEN-SICHEREN-STRING-HIER
# === EMAIL (Resend) ===
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
# === PAYMENTS (Stripe) ===
STRIPE_SECRET_KEY=sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxxxx
# === STORAGE (Cloudflare R2) ===
R2_ACCESS_KEY_ID=xxxxxxxxxxxxxxxxxxxxxxxxxx
R2_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
R2_BUCKET_NAME=uload-uploads
R2_ENDPOINT=https://xxxxxxxxxx.r2.cloudflarestorage.com
```
### 7.3 AUTH_SECRET generieren
Auf deinem lokalen Rechner:
```bash
openssl rand -base64 32
```
Kopiere das Ergebnis als `AUTH_SECRET`.
---
## Schritt 8: Domain konfigurieren
### 8.1 DNS-Einträge setzen
Bei deinem DNS-Provider (z.B. Cloudflare, Namecheap):
| Type | Name | Value | TTL |
| ---- | ---- | --------------- | ---- |
| A | @ | DEINE-SERVER-IP | 3600 |
| A | www | DEINE-SERVER-IP | 3600 |
### 8.2 Domain in Coolify hinzufügen
1. Gehe zu deiner Application → **Settings**
2. Unter **Domains** klicke **+ Add**
3. Gib ein: `ulo.ad`
4. Aktiviere: **Generate SSL Certificate** (Let's Encrypt)
5. Optional: Füge auch `www.ulo.ad` hinzu mit Redirect
### 8.3 Warten
DNS-Änderungen können 5-30 Minuten dauern. SSL-Zertifikate werden automatisch erstellt.
---
## Schritt 9: Deployment starten
### 9.1 Erster Deploy
1. Gehe zu deiner Application
2. Klicke **Deploy**
3. Warte auf den Build (ca. 3-5 Minuten)
### 9.2 Build-Logs überwachen
Klicke auf das laufende Deployment um die Logs zu sehen.
**Erfolgreicher Build zeigt:**
```
✔ done
Listening on http://0.0.0.0:3000
```
---
## Schritt 10: Datenbank-Migration
### 10.1 Nach erstem Deployment
Die Datenbank-Tabellen müssen erstellt werden:
1. In Coolify: Gehe zu deiner Application → **Terminal**
2. Oder via SSH:
```bash
# Container-Name finden
docker ps | grep uload
# In Container gehen
docker exec -it CONTAINER-NAME sh
# Migration ausführen
npx drizzle-kit push
```
### 10.2 Alternative: Pre-Deploy Command
In Coolify → Application → **Settings****Pre-Deploy Command**:
```bash
cd /app && npx drizzle-kit push
```
---
## Schritt 11: Verifizieren
### 11.1 Health Check
```bash
curl https://ulo.ad/api/health
```
Erwartete Antwort:
```json
{ "status": "ok", "timestamp": "2025-11-25T12:00:00.000Z", "uptime": 123.45 }
```
### 11.2 Website öffnen
Öffne `https://ulo.ad` im Browser.
---
## Automatische Deployments
### Webhook (Standard)
Coolify erstellt automatisch einen GitHub Webhook. Bei jedem Push auf `main` wird automatisch deployed.
### Manuelles Deployment
In Coolify: Application → **Redeploy**
---
## Wartung & Monitoring
### Logs anzeigen
**In Coolify:**
Application → **Logs**
**Via SSH:**
```bash
docker logs -f $(docker ps -qf "name=uload")
```
### Container neustarten
In Coolify: Application → **Restart**
### Datenbank Backup
```bash
# Manuelles Backup
docker exec uload-postgres pg_dump -U uload uload > backup_$(date +%Y%m%d).sql
# Backup wiederherstellen
cat backup_20251125.sql | docker exec -i uload-postgres psql -U uload uload
```
---
## Troubleshooting
### Build schlägt fehl
| Problem | Lösung |
| -------------------------- | ---------------------------------------- |
| "Cannot find package" | Prüfe Base Directory (muss `/` sein) |
| "pnpm-lock.yaml not found" | Prüfe dass pnpm-lock.yaml im Repo ist |
| Timeout beim Build | Erhöhe Build-Timeout in Coolify Settings |
### Container startet nicht
| Problem | Lösung |
| ---------------------------- | ----------------------------------------- |
| "Missing API key" | Prüfe RESEND_API_KEY Environment Variable |
| "Cannot connect to database" | Prüfe DATABASE_URL (Internal URL!) |
| Port already in use | Prüfe ob alter Container noch läuft |
### SSL-Zertifikat Fehler
1. Prüfe DNS-Einträge (A-Record auf Server-IP)
2. Warte 5-10 Minuten
3. In Coolify: Domain löschen und neu hinzufügen
4. Prüfe ob Port 80 erreichbar ist (Firewall)
### Datenbank-Verbindung fehlgeschlagen
1. Prüfe ob PostgreSQL-Container läuft
2. Verwende **Internal URL** (nicht External!)
3. Teste Verbindung:
```bash
docker exec -it uload-postgres psql -U uload -d uload -c "SELECT 1"
```
---
## Checkliste Production-Ready
- [ ] Hetzner VPS erstellt und SSH funktioniert
- [ ] Coolify installiert und Admin-Account erstellt
- [ ] PostgreSQL läuft und CONNECTION_STRING notiert
- [ ] Redis läuft (optional)
- [ ] GitHub Repository verbunden
- [ ] Application mit korrektem Dockerfile-Pfad erstellt
- [ ] Alle Environment Variables gesetzt
- [ ] AUTH_SECRET generiert (min. 32 Zeichen)
- [ ] DNS A-Records konfiguriert
- [ ] Domain in Coolify hinzugefügt
- [ ] SSL-Zertifikat aktiv
- [ ] Erster Deploy erfolgreich
- [ ] Datenbank-Migration ausgeführt
- [ ] Health-Check funktioniert (`/api/health`)
- [ ] Website erreichbar
---
## Dateien im Repository
| Datei | Beschreibung |
| ---------------------------------- | ------------------------ |
| `uload/Dockerfile` | Multi-Stage Docker Build |
| `uload/docker-compose.yml` | Lokale Entwicklung |
| `uload/docker-compose.coolify.yml` | Coolify Deployment |
| `uload/docker-compose.prod.yml` | Standalone Production |
---
## Support
Bei Problemen:
1. Coolify Logs prüfen
2. Container Logs prüfen (`docker logs`)
3. GitHub Issues: https://github.com/anthropics/claude-code/issues

View file

@ -0,0 +1,383 @@
# Central Command Bar
Die zentrale Command Bar bietet eine einheitliche Schnellsuche und Navigation über alle Manacore-Apps hinweg. Sie wird mit `Cmd/Ctrl+K` aktiviert und bietet Suche, Quick Actions und Tastatur-Navigation.
## Architektur
```
┌─────────────────────────────────────────────────────────────┐
@manacore/shared-ui │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ CommandBar.svelte │ │
│ │ - Suche mit Debounce (150ms) │ │
│ │ - Quick Actions │ │
│ │ - Tastatur-Navigation │ │
│ │ - Ergebnis-Anzeige mit Avataren │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌────────────────────┼────────────────────┐
│ │ │
┌────▼────┐ ┌─────▼────┐ ┌─────▼────┐
│ Todo │ │ Calendar │ │ Contacts │
│ │ │ │ │ │
│ Sucht │ │ Sucht │ │ Sucht │
│ Tasks │ │ Events │ │ Kontakte │
└─────────┘ └──────────┘ └──────────┘
```
## Package
| Package | Beschreibung |
|---------|--------------|
| `@manacore/shared-ui` | CommandBar Svelte-Komponente + TypeScript-Typen |
## Keyboard Shortcut
| Shortcut | Aktion |
|----------|--------|
| `Cmd/Ctrl+K` | Command Bar öffnen |
| `↑↓` | Navigation durch Ergebnisse |
| `Enter` | Auswahl bestätigen |
| `Escape` | Schließen |
## TypeScript Interfaces
### CommandBarItem
Ein Suchergebnis:
```typescript
interface CommandBarItem {
id: string; // Eindeutige ID
title: string; // Haupttext (z.B. Name, Titel)
subtitle?: string; // Untertitel (z.B. Datum, E-Mail)
icon?: string; // Icon-Name
imageUrl?: string; // Avatar/Bild URL
isFavorite?: boolean; // Favorit-Markierung (zeigt Herz-Icon)
}
```
### QuickAction
Eine Schnellaktion (ohne Suche):
```typescript
interface QuickAction {
id: string; // Eindeutige ID
label: string; // Anzeigetext
icon: string; // Icon-Name
href?: string; // Link-Ziel (optional)
shortcut?: string; // Tastenkürzel-Anzeige
onclick?: () => void; // Click-Handler (alternativ zu href)
}
```
### Unterstützte Icons
Die CommandBar unterstützt folgende eingebaute Icons:
| Icon-Name | Beschreibung |
|-----------|--------------|
| `plus` | Plus-Zeichen (Neu erstellen) |
| `heart` | Herz (Favoriten) |
| `tag` | Tag/Label |
| `upload` | Upload (Import) |
| `calendar` | Kalender |
| `clock` | Uhr |
| `check` | Häkchen |
| `settings` | Zahnrad (Einstellungen) |
| `list` | Liste |
## Komponenten-Props
```typescript
interface Props {
open: boolean; // Sichtbarkeit
onClose: () => void; // Schließen-Handler
onSearch: (query: string) => Promise<CommandBarItem[]>; // Such-Funktion
onSelect: (item: CommandBarItem) => void; // Auswahl-Handler
quickActions?: QuickAction[]; // Schnellaktionen
placeholder?: string; // Suchfeld-Placeholder
emptyText?: string; // Text bei leeren Ergebnissen
searchingText?: string; // Text während Suche
}
```
## Nutzung
### Basis-Beispiel
```svelte
<script lang="ts">
import { goto } from '$app/navigation';
import { CommandBar } from '@manacore/shared-ui';
import type { CommandBarItem, QuickAction } from '@manacore/shared-ui';
let commandBarOpen = $state(false);
// Quick Actions definieren
const quickActions: QuickAction[] = [
{ id: 'new', label: 'Neu erstellen', icon: 'plus', href: '/new', shortcut: 'N' },
{ id: 'settings', label: 'Einstellungen', icon: 'settings', href: '/settings' },
];
// Such-Funktion implementieren
async function handleSearch(query: string): Promise<CommandBarItem[]> {
const results = await api.search(query);
return results.map((item) => ({
id: item.id,
title: item.name,
subtitle: item.description,
}));
}
// Auswahl verarbeiten
function handleSelect(item: CommandBarItem) {
goto(`/item/${item.id}`);
}
// Keyboard Shortcut registrieren
function handleKeydown(event: KeyboardEvent) {
if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
event.preventDefault();
commandBarOpen = true;
}
}
</script>
<svelte:window onkeydown={handleKeydown} />
<CommandBar
bind:open={commandBarOpen}
onClose={() => (commandBarOpen = false)}
onSearch={handleSearch}
onSelect={handleSelect}
{quickActions}
placeholder="Suchen..."
emptyText="Keine Ergebnisse"
searchingText="Suche..."
/>
```
## App-spezifische Implementierungen
### Todo App
```typescript
// Quick Actions
const quickActions: QuickAction[] = [
{ id: 'new', label: 'Neue Aufgabe erstellen', icon: 'plus', href: '/task/new', shortcut: 'N' },
{ id: 'kanban', label: 'Kanban-Board', icon: 'list', href: '/kanban' },
{ id: 'stats', label: 'Statistiken', icon: 'chart', href: '/statistics' },
{ id: 'settings', label: 'Einstellungen', icon: 'settings', href: '/settings' },
];
// Suche: Tasks durchsuchen
async function handleSearch(query: string): Promise<CommandBarItem[]> {
const tasks = await getTasks({ search: query });
return tasks.slice(0, 10).map((task) => ({
id: task.id,
title: task.title,
subtitle: task.isCompleted
? '✓ Erledigt'
: task.dueDate
? new Date(task.dueDate).toLocaleDateString('de-DE')
: 'Kein Datum',
}));
}
// Auswahl: Zu Task navigieren
function handleSelect(item: CommandBarItem) {
goto(`/task/${item.id}`);
}
```
### Calendar App
```typescript
// Quick Actions
const quickActions: QuickAction[] = [
{ id: 'new', label: 'Neuen Termin erstellen', icon: 'plus', href: '/event/new', shortcut: 'N' },
{ id: 'today', label: 'Zu Heute springen', icon: 'calendar', onclick: () => viewStore.goToToday() },
{ id: 'agenda', label: 'Agenda anzeigen', icon: 'list', href: '/agenda' },
{ id: 'settings', label: 'Einstellungen', icon: 'settings', href: '/settings' },
];
// Suche: Events durchsuchen
async function handleSearch(query: string): Promise<CommandBarItem[]> {
const result = await searchEvents(query);
if (result.error || !result.data) return [];
return result.data.slice(0, 10).map((event) => ({
id: event.id,
title: event.title,
subtitle: format(new Date(event.startTime), 'dd. MMM yyyy, HH:mm', { locale: de }),
}));
}
// Auswahl: Zu Event navigieren
function handleSelect(item: CommandBarItem) {
goto(`/event/${item.id}`);
}
```
### Contacts App
```typescript
// Quick Actions
const quickActions: QuickAction[] = [
{ id: 'new', label: 'Neuen Kontakt erstellen', icon: 'plus', href: '/contacts/new', shortcut: 'N' },
{ id: 'favorites', label: 'Favoriten anzeigen', icon: 'heart', href: '/favorites' },
{ id: 'tags', label: 'Tags verwalten', icon: 'tag', href: '/tags' },
{ id: 'import', label: 'Kontakte importieren', icon: 'upload', href: '/data?tab=import' },
];
// Suche: Kontakte durchsuchen
async function handleSearch(query: string): Promise<CommandBarItem[]> {
const response = await contactsApi.list({ search: query, limit: 10 });
return (response.contacts || []).map((contact) => ({
id: contact.id,
title: contact.displayName ||
[contact.firstName, contact.lastName].filter(Boolean).join(' ') ||
contact.email || 'Unbekannt',
subtitle: contact.company || contact.email,
imageUrl: contact.photoUrl,
isFavorite: contact.isFavorite,
}));
}
// Auswahl: Kontakt-Modal öffnen
function handleSelect(item: CommandBarItem) {
goto(`/contacts/${item.id}`);
}
```
## Funktionen
### Suche
- **Debounce:** 150ms Verzögerung für Performance
- **Loading-State:** Spinner während der Suche
- **Empty State:** Konfigurierbare Meldung bei keinen Ergebnissen
- **Limit:** Ergebnisse werden typischerweise auf 10 begrenzt
### Quick Actions
Wenn kein Suchtext eingegeben ist, werden Quick Actions angezeigt:
- Navigation mit Pfeiltasten
- Ausführung mit Enter
- Keyboard-Shortcuts werden rechts angezeigt
### Ergebnis-Anzeige
- **Avatar:** Bild oder Initialen
- **Titel:** Haupttext
- **Untertitel:** Zusatzinfo (grau)
- **Favorit:** Herz-Icon wenn `isFavorite: true`
- **Hover:** Visuelles Feedback bei Maus-Over
### Keyboard Navigation
| Taste | Aktion |
|-------|--------|
| `↑` / `↓` | Durch Ergebnisse navigieren |
| `Enter` | Ausgewähltes Element öffnen |
| `Escape` | Command Bar schließen |
## Styling
Die Command Bar verwendet ein dunkles Theme mit CSS-Variablen:
```css
.command-modal {
background: #1a1a1a;
border: 1px solid #333;
border-radius: 12px;
max-width: 560px;
}
.command-result.selected {
background: #2a2a2a;
}
.result-avatar {
background: #3b82f6; /* Primary Color */
}
.result-favorite {
color: #ef4444; /* Rot für Herz */
}
```
### Animationen
- **Fade In:** Backdrop erscheint mit 0.15s
- **Slide In:** Modal gleitet von oben mit 0.2s
- **Loading Spinner:** Rotation Animation
## Integration in Layout
Typischerweise wird die CommandBar im App-Layout integriert:
```svelte
<!-- src/routes/(app)/+layout.svelte -->
<script lang="ts">
import { CommandBar } from '@manacore/shared-ui';
let commandBarOpen = $state(false);
function handleKeydown(event: KeyboardEvent) {
// Cmd/Ctrl+K funktioniert auch in Input-Feldern
if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
event.preventDefault();
commandBarOpen = true;
}
}
</script>
<svelte:window onkeydown={handleKeydown} />
<!-- Haupt-Layout -->
<PillNavigation ... />
<main>
{@render children()}
</main>
<!-- Command Bar (global) -->
<CommandBar
bind:open={commandBarOpen}
onClose={() => (commandBarOpen = false)}
onSearch={handleSearch}
onSelect={handleSelect}
{quickActions}
/>
```
## Dateien
### @manacore/shared-ui
| Datei | Beschreibung |
|-------|--------------|
| `src/command-bar/CommandBar.svelte` | Hauptkomponente |
| `src/command-bar/index.ts` | Exports |
| `src/index.ts` | Package-Export |
## Vorteile
- **Einheitliche UX:** Gleiche Interaktion in allen Apps
- **Schnelle Navigation:** Sofortiger Zugriff auf häufige Aktionen
- **Tastatur-fokussiert:** Optimiert für Power-User
- **Flexibel:** App-spezifische Such-Logik und Quick Actions
- **Responsive:** Funktioniert auf Desktop und Mobile
## Best Practices
1. **Such-Performance:** Limit auf 10 Ergebnisse setzen
2. **Quick Actions:** Maximal 4-5 Aktionen für Übersichtlichkeit
3. **Sinnvolle Shortcuts:** Mnemonische Kürzel (N=Neu, S=Settings)
4. **Gute Subtitles:** Zusätzliche Info für eindeutige Identifikation
5. **Keyboard First:** Cmd+K sollte immer funktionieren, auch in Input-Feldern

View file

@ -0,0 +1,603 @@
# Central Help System
Das zentrale Help-System bietet eine einheitliche Hilfeseite für alle Manacore-Apps. Es unterstützt mehrsprachige Inhalte, Volltextsuche, und die Kombination von zentralen und app-spezifischen Inhalten.
## Architektur
```
┌─────────────────────────────────────────────────────────────┐
│ Zentrale Help-Inhalte │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ FAQ (allgemein)│ │ Features │ │ Changelog │ │
│ │ - Account │ │ - Theming │ │ - v1.5.0 │ │
│ │ - Billing │ │ - Tags │ │ - v1.4.0 │ │
│ │ - Privacy │ │ - Sync │ │ ... │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
└─────────────────────────────────────────────────────────────┘
mergeContent()
┌─────────────────────┼─────────────────────┐
│ │ │
┌────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ Todo │ │ Calendar │ │ Contacts │
│ │ │ │ │ │
│ + App- │ │ + App- │ │ + App- │
│ spezif. │ │ spezif. │ │ spezif. │
│ FAQ │ │ Shortcuts │ │ Features │
└──────────┘ └───────────┘ └───────────┘
```
## Packages
| Package | Beschreibung |
|---------|--------------|
| `@manacore/shared-help-types` | TypeScript-Typen und Zod-Schemas |
| `@manacore/shared-help-content` | Content-Loader, Parser, Merger, Suche |
| `@manacore/shared-help-ui` | Svelte 5 UI-Komponenten |
| `@manacore/shared-help-mobile` | React Native Komponenten |
## Content-Typen
### FAQ (Frequently Asked Questions)
```typescript
interface FAQItem {
id: string;
language: 'de' | 'en' | 'fr' | 'it' | 'es';
question: string;
answer: string;
category: 'general' | 'account' | 'billing' | 'features' | 'technical' | 'privacy';
featured?: boolean;
tags?: string[];
relatedFaqs?: string[];
order?: number;
appSpecific?: boolean;
apps?: string[];
}
```
### Features
```typescript
interface FeatureItem {
id: string;
language: SupportedLanguage;
title: string;
description: string;
content: string;
icon?: string;
category: 'getting-started' | 'core' | 'advanced' | 'integration';
available?: boolean;
comingSoon?: boolean;
highlights?: string[];
learnMoreUrl?: string;
}
```
### Keyboard Shortcuts
```typescript
interface ShortcutsItem {
id: string;
language: SupportedLanguage;
category: 'navigation' | 'editing' | 'general' | 'app-specific';
title?: string;
shortcuts: Array<{
shortcut: string; // z.B. "Ctrl+S", "⌘+K"
action: string;
description?: string;
}>;
}
```
### Getting Started Guides
```typescript
interface GettingStartedItem {
id: string;
language: SupportedLanguage;
title: string;
description: string;
content: string;
difficulty: 'beginner' | 'intermediate' | 'advanced';
estimatedTime?: string;
prerequisites?: string[];
steps?: Array<{
title: string;
content: string;
duration?: string;
}>;
}
```
### Changelog
```typescript
interface ChangelogItem {
id: string;
language: SupportedLanguage;
version: string;
title: string;
releaseDate: Date;
type: 'major' | 'minor' | 'patch' | 'beta';
summary?: string;
content: string;
highlighted?: boolean;
changes?: {
features?: Array<{ title: string; description?: string }>;
improvements?: Array<{ title: string; description?: string }>;
bugfixes?: Array<{ title: string; description?: string }>;
};
platforms?: string[];
}
```
### Contact Info
```typescript
interface ContactInfo {
id: string;
language: SupportedLanguage;
title: string;
content: string;
supportEmail?: string;
supportUrl?: string;
discordUrl?: string;
twitterUrl?: string;
documentationUrl?: string;
responseTime?: string;
}
```
## Unterstützte Sprachen
```typescript
type SupportedLanguage = 'en' | 'de' | 'fr' | 'it' | 'es';
```
## Content-Struktur
### HelpContent Objekt
```typescript
interface HelpContent {
faq: FAQItem[];
features: FeatureItem[];
shortcuts: ShortcutsItem[];
gettingStarted: GettingStartedItem[];
changelog: ChangelogItem[];
contact: ContactInfo | null;
}
```
## Content Laden und Mergen
### Content Merger
Der Merger kombiniert zentrale und app-spezifische Inhalte:
```typescript
import { mergeContent, createEmptyContent } from '@manacore/shared-help-content';
// Zentrale Inhalte (für alle Apps)
const centralContent: HelpContent = {
faq: [
{ id: 'account-login', question: 'Wie melde ich mich an?', ... },
{ id: 'billing-cancel', question: 'Wie kündige ich?', ... },
],
features: [...],
// ...
};
// App-spezifische Inhalte
const appContent: Partial<HelpContent> = {
faq: [
{ id: 'todo-recurring', question: 'Wie erstelle ich wiederkehrende Tasks?', ... },
],
shortcuts: [
{ category: 'app-specific', shortcuts: [...] },
],
};
// Zusammenführen
const content = mergeContent(centralContent, appContent, {
appId: 'todo',
locale: 'de',
overrideById: true, // App-Content ersetzt zentralen mit gleicher ID
});
```
### Filterung
Inhalte werden automatisch gefiltert nach:
- **Sprache:** Nur Inhalte der aktuellen Locale
- **App:** `appSpecific: true` Inhalte nur wenn `apps` die aktuelle App enthält
- **Reihenfolge:** Sortiert nach `order` Property
## Suche
### Such-Index erstellen
```typescript
import { buildSearchIndex, search, createSearcher } from '@manacore/shared-help-content';
// Index erstellen
const index = buildSearchIndex(content, {
titleWeight: 2.0,
contentWeight: 1.0,
tagsWeight: 1.5,
threshold: 0.3,
minMatchCharLength: 2,
});
// Suchen
const results = search(index, 'wiederkehrend', {
limit: 10,
threshold: 0.4,
types: ['faq', 'guide'], // Optional: Nur bestimmte Typen
});
```
### SearchResult
```typescript
interface SearchResult {
id: string;
type: 'faq' | 'feature' | 'guide' | 'changelog';
title: string;
excerpt: string;
score: number;
highlight?: string;
item: FAQItem | FeatureItem | GettingStartedItem | ChangelogItem;
}
```
## UI-Komponenten
### HelpPage (Hauptkomponente)
Vollständige Hilfeseite mit allen Sektionen:
```svelte
<script>
import { HelpPage } from '@manacore/shared-help-ui';
import { helpContent, translations } from '$lib/help';
</script>
<HelpPage
content={helpContent}
appName="Todo"
appId="todo"
translations={translations}
searchEnabled={true}
showFAQ={true}
showFeatures={true}
showShortcuts={true}
showGettingStarted={true}
showChangelog={true}
showContact={true}
defaultSection="faq"
showBackButton={true}
onBack={() => goto('/')}
onSectionChange={(section) => console.log(section)}
/>
```
### Einzelne Sektionen
```svelte
<script>
import {
FAQSection,
FeaturesOverview,
KeyboardShortcuts,
GettingStartedGuide,
ChangelogSection,
ContactSection,
HelpSearch,
} from '@manacore/shared-help-ui';
</script>
<!-- FAQ mit Kategorien -->
<FAQSection
items={content.faq}
translations={translations}
showCategories={true}
maxItems={10}
expandFirst={true}
/>
<!-- Features-Übersicht -->
<FeaturesOverview
items={content.features}
translations={translations}
/>
<!-- Keyboard Shortcuts -->
<KeyboardShortcuts
items={content.shortcuts}
translations={translations}
/>
<!-- Getting Started Guides -->
<GettingStartedGuide
items={content.gettingStarted}
translations={translations}
/>
<!-- Changelog -->
<ChangelogSection
items={content.changelog}
translations={translations}
maxItems={5}
/>
<!-- Kontakt -->
<ContactSection
contact={content.contact}
translations={translations}
/>
<!-- Suche -->
<HelpSearch
content={content}
translations={translations}
placeholder="Hilfe durchsuchen..."
onResultSelect={(result) => navigateToResult(result)}
/>
```
## Übersetzungen
### HelpPageTranslations Interface
```typescript
interface HelpPageTranslations {
title: string;
subtitle?: string;
searchPlaceholder: string;
sections: {
faq: string;
features: string;
shortcuts: string;
gettingStarted: string;
changelog: string;
contact: string;
};
search: {
noResults: string;
resultsCount: string;
searching: string;
};
faq: {
noItems: string;
categories: {
general: string;
account: string;
billing: string;
features: string;
technical: string;
privacy: string;
};
};
features: {
noItems: string;
comingSoon: string;
learnMore: string;
};
shortcuts: {
noItems: string;
};
gettingStarted: {
noItems: string;
estimatedTime: string;
difficulty: {
beginner: string;
intermediate: string;
advanced: string;
};
};
changelog: {
noItems: string;
types: {
major: string;
minor: string;
patch: string;
beta: string;
};
};
contact: {
noInfo: string;
email: string;
responseTime: string;
};
common: {
back: string;
showMore: string;
showLess: string;
};
}
```
### Beispiel (Deutsch)
```typescript
const translations: HelpPageTranslations = {
title: 'Hilfe',
subtitle: 'Finde Antworten und lerne die App kennen',
searchPlaceholder: 'Suche in der Hilfe...',
sections: {
faq: 'Häufige Fragen',
features: 'Funktionen',
shortcuts: 'Tastaturkürzel',
gettingStarted: 'Erste Schritte',
changelog: 'Neuigkeiten',
contact: 'Kontakt',
},
search: {
noResults: 'Keine Ergebnisse gefunden',
resultsCount: '{count} Ergebnisse',
searching: 'Suche...',
},
faq: {
noItems: 'Keine FAQ-Einträge vorhanden',
categories: {
general: 'Allgemein',
account: 'Konto',
billing: 'Abrechnung',
features: 'Funktionen',
technical: 'Technisch',
privacy: 'Datenschutz',
},
},
// ... weitere
};
```
## Dateien
### @manacore/shared-help-types
| Datei | Beschreibung |
|-------|--------------|
| `src/content.ts` | Content-Typ Definitionen |
| `src/schemas.ts` | Zod-Validierungsschemas |
| `src/search.ts` | Such-bezogene Typen |
### @manacore/shared-help-content
| Datei | Beschreibung |
|-------|--------------|
| `src/parser.ts` | Markdown-Parser |
| `src/loader.ts` | Content-Loader für verschiedene Formate |
| `src/merger.ts` | Content-Merger (zentral + app-spezifisch) |
| `src/search.ts` | Volltextsuche mit Fuse.js |
### @manacore/shared-help-ui
| Datei | Beschreibung |
|-------|--------------|
| `src/pages/HelpPage.svelte` | Vollständige Hilfeseite |
| `src/components/FAQSection.svelte` | FAQ-Bereich |
| `src/components/FAQItem.svelte` | Einzelner FAQ-Eintrag |
| `src/components/FeaturesOverview.svelte` | Feature-Übersicht |
| `src/components/FeatureCard.svelte` | Feature-Karte |
| `src/components/KeyboardShortcuts.svelte` | Tastaturkürzel |
| `src/components/GettingStartedGuide.svelte` | Erste-Schritte-Guide |
| `src/components/ChangelogSection.svelte` | Changelog-Bereich |
| `src/components/ChangelogEntry.svelte` | Einzelner Changelog-Eintrag |
| `src/components/ContactSection.svelte` | Kontakt-Bereich |
| `src/components/HelpSearch.svelte` | Suchkomponente |
## Integration in eine App
### 1. Dependencies
```json
{
"dependencies": {
"@manacore/shared-help-types": "workspace:*",
"@manacore/shared-help-content": "workspace:*",
"@manacore/shared-help-ui": "workspace:*"
}
}
```
### 2. Content erstellen
```typescript
// src/lib/help/content.ts
import type { HelpContent } from '@manacore/shared-help-types';
export const appHelpContent: Partial<HelpContent> = {
faq: [
{
id: 'calendar-recurring',
language: 'de',
question: 'Wie erstelle ich wiederkehrende Termine?',
answer: 'Öffne einen Termin und aktiviere die Wiederholung...',
category: 'features',
appSpecific: true,
apps: ['calendar'],
},
],
shortcuts: [
{
id: 'calendar-shortcuts',
language: 'de',
category: 'app-specific',
title: 'Kalender-Shortcuts',
shortcuts: [
{ shortcut: 'N', action: 'Neuer Termin' },
{ shortcut: 'T', action: 'Zu Heute springen' },
{ shortcut: 'W', action: 'Wochenansicht' },
],
},
],
};
```
### 3. Hilfeseite erstellen
```svelte
<!-- src/routes/(app)/help/+page.svelte -->
<script lang="ts">
import { HelpPage } from '@manacore/shared-help-ui';
import { mergeContent } from '@manacore/shared-help-content';
import { centralContent } from '$lib/help/central';
import { appHelpContent } from '$lib/help/content';
import { translations } from '$lib/help/translations';
import { goto } from '$app/navigation';
const content = mergeContent(centralContent, appHelpContent, {
appId: 'calendar',
locale: 'de',
});
</script>
<HelpPage
{content}
appName="Calendar"
appId="calendar"
{translations}
searchEnabled={true}
onBack={() => goto('/')}
/>
```
## App-spezifische Inhalte
### Markierung
Inhalte können als app-spezifisch markiert werden:
```typescript
{
id: 'todo-labels',
appSpecific: true,
apps: ['todo', 'calendar'], // Nur in diesen Apps sichtbar
// ...
}
```
### Override by ID
Wenn `overrideById: true` (Standard), ersetzt app-spezifischer Content zentralen Content mit gleicher ID:
```typescript
// Zentral
{ id: 'feature-tags', title: 'Tags allgemein', ... }
// App-spezifisch
{ id: 'feature-tags', title: 'Tags in Todo', ... } // Ersetzt zentralen Content
```
## Vorteile
- **Wiederverwendbarkeit:** Zentrale FAQs (Account, Billing) nur einmal schreiben
- **Konsistenz:** Einheitliches Look & Feel der Hilfeseite
- **Mehrsprachigkeit:** 5 Sprachen unterstützt
- **Suche:** Integrierte Volltextsuche
- **Flexibilität:** App-spezifische Inhalte können hinzugefügt werden
- **Typ-Sicherheit:** Vollständige TypeScript-Typen und Zod-Validierung

View file

@ -0,0 +1,90 @@
# Central Services
Dieses Verzeichnis dokumentiert zentrale Services, die von allen Manacore-Apps gemeinsam genutzt werden. Diese Services laufen in `mana-core-auth` und bieten einheitliche APIs.
## Übersicht
| Service | Beschreibung | Dokumentation |
|---------|--------------|---------------|
| **Tags** | Einheitliche Tags/Labels für Todo, Calendar, Contacts | [TAGS.md](./TAGS.md) |
| **Theming** | Theme-Varianten, Dark Mode, Accessibility, Custom Themes | [THEMING.md](./THEMING.md) |
| **Help** | Zentrale Hilfeseite mit FAQ, Features, Shortcuts, Changelog | [HELP.md](./HELP.md) |
| **Command Bar** | Globale Schnellsuche und Navigation (Cmd/Ctrl+K) | [COMMAND-BAR.md](./COMMAND-BAR.md) |
## Architektur-Prinzipien
### Zentralisierung
Bestimmte Daten und Funktionen werden zentral in `mana-core-auth` verwaltet:
- **User-bezogen:** Jeder Service speichert Daten pro User (`userId`)
- **App-übergreifend:** Daten sind in allen Apps verfügbar
- **API-basiert:** Zugriff erfolgt über REST-APIs
### Soft References
Da die Apps ihre eigenen Datenbanken haben, können keine Foreign Keys zu mana-core-auth erstellt werden:
```
Todo-DB mana-core-auth-DB
┌─────────────────┐ ┌─────────────────┐
│ task_to_tags │ │ tags │
│ │ │ │
│ tag_id ─ ─ ─ ─ ─│─ ─ ─ ─ ▶│ id │
│ (keine FK) │ │ │
└─────────────────┘ └─────────────────┘
```
**Konsequenz:** Apps müssen ungültige IDs beim Laden ignorieren.
### Shared Packages
Für jeden zentralen Service gibt es ein entsprechendes Client-Package:
| Service | Package |
|---------|---------|
| Tags | `@manacore/shared-tags` |
Diese Packages enthalten:
- TypeScript Types
- API Client Klasse
- Helper-Funktionen
## Lokale Entwicklung
### Alle zentralen Services starten
```bash
# Infrastruktur
pnpm docker:up
# Auth-Service (enthält alle zentralen APIs)
pnpm dev:auth
```
### Datenbank-Schema pushen
```bash
cd services/mana-core-auth
pnpm db:push
```
## Shared Packages
| Package | Beschreibung |
|---------|--------------|
| `@manacore/shared-tags` | Tags Client für zentrale Tags API |
| `@manacore/shared-theme` | Theme Store, A11y Store, User Settings |
| `@manacore/shared-theme-ui` | Svelte UI-Komponenten für Theming |
| `@manacore/shared-help-types` | TypeScript-Typen für Help-Inhalte |
| `@manacore/shared-help-content` | Content-Loader, Parser, Merger, Suche |
| `@manacore/shared-help-ui` | Svelte UI-Komponenten für Hilfeseite |
| `@manacore/shared-help-mobile` | React Native Komponenten für Hilfe |
## Hinzufügen neuer zentraler Services
1. **Schema erstellen:** `services/mana-core-auth/src/db/schema/<name>.schema.ts`
2. **Module erstellen:** `services/mana-core-auth/src/<name>/`
3. **In app.module.ts registrieren**
4. **Shared Package erstellen:** `packages/shared-<name>/`
5. **Dokumentation schreiben:** `docs/central-services/<NAME>.md`

View file

@ -0,0 +1,248 @@
# Central Tags API
Das zentrale Tags-System ermöglicht einheitliche Tags/Labels über alle Manacore-Apps hinweg. Ein Tag, der in Todo erstellt wird, ist automatisch auch in Calendar und Contacts verfügbar.
## Architektur
```
┌─────────────────────────────────────────────────────────────┐
│ mana-core-auth │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ tags Tabelle (zentral) │ │
│ │ - id, userId, name, color, icon, createdAt │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ GET /api/v1/tags │ POST/PUT/DELETE │
└────────────────────────────┼────────────────────────────────┘
┌────────────────────┼────────────────────┐
│ │ │
┌────▼────┐ ┌─────▼────┐ ┌─────▼────┐
│ Todo │ │ Calendar │ │ Contacts │
│ │ │ │ │ │
│ task_ │ │ event_ │ │ contact_ │
│ to_tags │ │ to_tags │ │ to_tags │
│ (tagId) │ │ (tagId) │ │ (tagId) │
└─────────┘ └──────────┘ └──────────┘
```
## API Endpoints
Alle Endpoints sind unter `http://localhost:3001/api/v1/tags` verfügbar und erfordern einen Bearer Token.
| Methode | Endpoint | Beschreibung |
|---------|----------|--------------|
| `GET` | `/tags` | Alle Tags des Users abrufen |
| `GET` | `/tags/:id` | Einzelnes Tag abrufen |
| `GET` | `/tags/by-ids?ids=id1,id2` | Mehrere Tags per ID abrufen |
| `POST` | `/tags` | Neues Tag erstellen |
| `POST` | `/tags/defaults` | Default-Tags erstellen |
| `PUT` | `/tags/:id` | Tag aktualisieren |
| `DELETE` | `/tags/:id` | Tag löschen |
## Tag-Objekt
```typescript
interface Tag {
id: string; // UUID
userId: string; // User-ID (aus JWT)
name: string; // Tag-Name (max 100 Zeichen)
color: string; // Hex-Farbe (#3B82F6)
icon?: string | null; // Optionales Phosphor-Icon
createdAt: Date;
updatedAt: Date;
}
```
## Default-Tags
Beim Aufruf von `POST /tags/defaults` werden folgende Standard-Tags erstellt:
| Name | Farbe | Hex |
|------|-------|-----|
| Arbeit | Blau | `#3B82F6` |
| Persönlich | Grün | `#10B981` |
| Familie | Pink | `#EC4899` |
| Wichtig | Rot | `#EF4444` |
## Client-Nutzung
### Shared Package
Das `@manacore/shared-tags` Package stellt einen Client bereit:
```typescript
import { createTagsClient } from '@manacore/shared-tags';
const tagsClient = createTagsClient({
authUrl: 'http://localhost:3001',
getToken: async () => authStore.getAccessToken(),
});
// Alle Tags laden
const tags = await tagsClient.getAll();
// Tag erstellen
const newTag = await tagsClient.create({
name: 'Meeting',
color: '#8B5CF6',
});
// Tags per IDs laden
const selectedTags = await tagsClient.getByIds(['id1', 'id2']);
// Tag aktualisieren
await tagsClient.update('tag-id', { color: '#22C55E' });
// Tag löschen
await tagsClient.delete('tag-id');
// Default-Tags erstellen
await tagsClient.createDefaults();
```
### In App-Stores
Die Apps nutzen den Client in ihren Stores:
**Todo (labels.svelte.ts):**
```typescript
import { createTagsClient, type Tag } from '@manacore/shared-tags';
// Label = Tag (Alias für Abwärtskompatibilität)
export type Label = Tag;
// Client lazy initialisieren
const client = createTagsClient({ ... });
export const labelsStore = {
async fetchLabels() {
const labels = await client.getAll();
// ...
}
};
```
**Calendar (event-tags.ts):**
```typescript
export type EventTag = Tag;
```
**Contacts (contacts.ts):**
```typescript
export type ContactTag = Tag;
```
## Junction Tables
Jede App behält ihre eigene Junction-Table für die Zuordnung:
### Todo: `task_to_tags`
```sql
CREATE TABLE task_to_tags (
task_id UUID REFERENCES tasks(id) ON DELETE CASCADE,
tag_id UUID NOT NULL, -- Soft reference zu mana-core-auth.tags
PRIMARY KEY (task_id, tag_id)
);
```
### Calendar: `event_to_tags`
```sql
CREATE TABLE event_to_tags (
event_id UUID REFERENCES events(id) ON DELETE CASCADE,
tag_id UUID NOT NULL, -- Soft reference zu mana-core-auth.tags
PRIMARY KEY (event_id, tag_id)
);
```
### Contacts: `contact_to_tags`
```sql
CREATE TABLE contact_to_tags (
contact_id UUID REFERENCES contacts(id) ON DELETE CASCADE,
tag_id UUID NOT NULL, -- Soft reference zu mana-core-auth.tags
PRIMARY KEY (contact_id, tag_id)
);
```
**Hinweis:** Da die Tags in einer anderen Datenbank liegen, sind keine Foreign Key Constraints möglich. Die Apps validieren Tag-IDs beim Laden und ignorieren ungültige IDs.
## Entwicklung & Testing
### Alle drei Apps gleichzeitig starten
```bash
pnpm dev:tags-test
```
Dieser Befehl:
1. Richtet alle Datenbanken ein (todo, calendar, contacts, auth)
2. Startet alle Services mit farbcodierten Logs
### Manuelles API-Testing
```bash
# Token holen
TOKEN=$(curl -s -X POST http://localhost:3001/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com", "password": "password"}' | jq -r '.accessToken')
# Tags abrufen
curl http://localhost:3001/api/v1/tags \
-H "Authorization: Bearer $TOKEN"
# Tag erstellen
curl -X POST http://localhost:3001/api/v1/tags \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Projekt X", "color": "#F59E0B"}'
# Default-Tags erstellen
curl -X POST http://localhost:3001/api/v1/tags/defaults \
-H "Authorization: Bearer $TOKEN"
```
## Dateien
### Backend (mana-core-auth)
| Datei | Beschreibung |
|-------|--------------|
| `src/db/schema/tags.schema.ts` | Drizzle-Schema für tags-Tabelle |
| `src/tags/tags.module.ts` | NestJS Module |
| `src/tags/tags.controller.ts` | REST Controller |
| `src/tags/tags.service.ts` | Business Logic |
| `src/tags/dto/create-tag.dto.ts` | DTO für Tag-Erstellung |
| `src/tags/dto/update-tag.dto.ts` | DTO für Tag-Update |
### Shared Package
| Datei | Beschreibung |
|-------|--------------|
| `packages/shared-tags/src/types.ts` | TypeScript Interfaces |
| `packages/shared-tags/src/client.ts` | TagsClient Klasse |
| `packages/shared-tags/src/index.ts` | Exports |
### Frontend-Integrationen
| App | API-Client | Store |
|-----|------------|-------|
| Todo | `src/lib/api/labels.ts` | `src/lib/stores/labels.svelte.ts` |
| Calendar | `src/lib/api/event-tags.ts` | `src/lib/stores/event-tags.svelte.ts` |
| Contacts | `src/lib/api/contacts.ts` (tagsApi) | - |
## Migration von lokalen Tags
Wenn eine App vorher eigene Tags hatte:
1. **Daten exportieren:** Bestehende Tags aus der lokalen Tabelle exportieren
2. **Tags erstellen:** Per API in mana-core-auth erstellen
3. **IDs mappen:** Alte Tag-IDs auf neue IDs mappen
4. **Junction Tables aktualisieren:** Tag-IDs in Junction-Tables ersetzen
5. **Lokale Tabelle löschen:** Alte Tags-Tabelle entfernen
## Vorteile
- **Konsistenz:** Ein Tag "Arbeit" ist überall gleich
- **Einheitliche Farben:** Tags sehen in allen Apps identisch aus
- **Weniger Duplikation:** Code und Daten werden geteilt
- **Cross-App Features:** Möglich (z.B. "Zeige alles mit Tag X")

View file

@ -0,0 +1,497 @@
# Central Theming System
Das zentrale Theming-System ermöglicht einheitliches Aussehen und Benutzereinstellungen über alle Manacore-Apps hinweg. Es besteht aus mehreren Schichten: Theme-Varianten, Light/Dark-Modus, Accessibility-Einstellungen und Custom Themes.
## Architektur
```
┌─────────────────────────────────────────────────────────────┐
│ mana-core-auth │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ user_settings (JSON-Feld) │ │
│ │ - theme: { mode, colorScheme, pinnedThemes } │ │
│ │ - nav: { desktopPosition, sidebarCollapsed } │ │
│ │ - locale: "de" │ │
│ │ - general: { startPages, sounds, etc. } │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ custom_themes Tabelle (Community Themes) │ │
│ │ - lightColors, darkColors, author, downloads, etc. │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌────────────────────┼────────────────────┐
│ │ │
┌────▼────┐ ┌─────▼────┐ ┌─────▼────┐
│ Todo │ │ Calendar │ │ Contacts │
│ │ │ │ │ │
│ shared- │ │ shared- │ │ shared- │
│ theme │ │ theme │ │ theme │
└─────────┘ └──────────┘ └──────────┘
```
## Packages
| Package | Beschreibung |
|---------|--------------|
| `@manacore/shared-theme` | Theme Store, Types, Utilities, Konstanten |
| `@manacore/shared-theme-ui` | Svelte UI-Komponenten (ThemeSelector, ThemePage, etc.) |
## Theme-Varianten
Es gibt 8 vordefinierte Theme-Varianten:
### Standard-Varianten (PillNav)
| Name | Farbe | Icon | Hue |
|------|-------|------|-----|
| `lume` | Gold ✨ | sparkle | 47 |
| `nature` | Grün 🌿 | leaf | 122 |
| `stone` | Blau-Grau 🪨 | hexagon | 200 |
| `ocean` | Blau 🌊 | waves | 199 |
### Erweiterte Varianten (Themes-Seite)
| Name | Farbe | Icon | Hue |
|------|-------|------|-----|
| `sunset` | Coral/Orange 🌅 | sun | 15 |
| `midnight` | Violett 🌙 | moon | 260 |
| `rose` | Pink 🌹 | flower | 340 |
| `lavender` | Lavendel 💜 | sparkle | 270 |
## Theme-Modus
```typescript
type ThemeMode = 'light' | 'dark' | 'system';
```
- **light**: Heller Modus
- **dark**: Dunkler Modus
- **system**: Folgt der System-Einstellung
## Color Tokens
Jede Theme-Variante definiert diese HSL-Farbwerte für Light und Dark:
```typescript
interface ThemeColors {
primary: string; // Hauptfarbe
primaryForeground: string; // Text auf Primary
secondary: string; // Sekundärfarbe
secondaryForeground: string;
background: string; // Seitenhintergrund
foreground: string; // Haupttext
surface: string; // Karten-Hintergrund
surfaceHover: string; // Hover-Zustand
surfaceElevated: string; // Modals, Dropdowns
muted: string; // Deaktivierte Elemente
mutedForeground: string;
border: string; // Rahmen
borderStrong: string; // Starke Rahmen
error: string; // Fehler-Rot
success: string; // Erfolg-Grün
warning: string; // Warnung-Orange
input: string; // Input-Hintergrund
ring: string; // Focus-Ring
}
```
### HSL-Format
Farben werden als HSL-Strings ohne `hsl()` Wrapper gespeichert:
```typescript
// Format: "H S% L%"
const gold = '47 95% 58%';
const darkBlue = '199 100% 18%';
// CSS-Verwendung
--color-primary: 47 95% 58%;
background-color: hsl(var(--color-primary));
```
## Store-Nutzung
### Theme Store
```typescript
import { createThemeStore } from '@manacore/shared-theme';
// Store erstellen
export const theme = createThemeStore({
appId: 'calendar',
defaultMode: 'system',
defaultVariant: 'ocean',
});
// In Komponente initialisieren
onMount(() => {
const cleanup = theme.initialize();
return cleanup;
});
// Zugriff auf State
theme.mode // 'light' | 'dark' | 'system'
theme.variant // 'ocean' | 'nature' | ...
theme.effectiveMode // 'light' | 'dark' (aufgelöst)
theme.isDark // boolean
// Aktionen
theme.setMode('dark');
theme.setVariant('nature');
theme.toggleMode(); // Light ↔ Dark
theme.cycleMode(); // Light → Dark → System → Light
```
### App-spezifische Primary Color
```typescript
export const theme = createThemeStore({
appId: 'memoro',
primaryColor: {
light: '47 95% 58%', // Gold
dark: '47 95% 58%',
},
});
```
### User Settings Store (Server-Sync)
```typescript
import { createUserSettingsStore } from '@manacore/shared-theme';
export const userSettings = createUserSettingsStore({
appId: 'calendar',
authUrl: 'http://localhost:3001',
getAccessToken: () => authStore.getAccessToken(),
});
// Laden
await userSettings.load();
// Zugriff
userSettings.theme.mode // Theme-Modus
userSettings.theme.colorScheme // Variante
userSettings.nav.desktopPosition // 'top' | 'bottom'
userSettings.locale // 'de'
userSettings.general.soundsEnabled
// Aktualisieren (speichert auf Server)
await userSettings.updateGlobal({
theme: { mode: 'dark' }
});
// App-spezifische Überschreibung
await userSettings.updateAppOverride({
theme: { colorScheme: 'nature' }
});
```
## Accessibility (A11y)
### A11y Store
```typescript
import { createA11yStore } from '@manacore/shared-theme';
export const a11y = createA11yStore({ appId: 'calendar' });
// State
a11y.contrast // 'normal' | 'high'
a11y.colorblind // 'none' | 'deuteranopia' | 'protanopia' | 'monochrome'
a11y.reduceMotion // boolean
// Aktionen
a11y.setContrast('high');
a11y.setColorblind('deuteranopia');
a11y.setReduceMotion(true);
a11y.resetAll();
```
### A11y-Optionen
**Kontrast:**
- `normal`: Standard (WCAG AA 4.5:1)
- `high`: Erhöhter Kontrast (WCAG AAA 7:1)
**Farbenblindheit:**
- `none`: Keine Anpassung
- `deuteranopia`: Grün-Blindheit (~6% der Männer)
- `protanopia`: Rot-Blindheit (~1% der Männer)
- `monochrome`: Graustufen
**Reduzierte Bewegung:**
- Respektiert `prefers-reduced-motion`
- Kann manuell überschrieben werden
## Custom Themes
### Custom Themes Store
```typescript
import { createCustomThemesStore } from '@manacore/shared-theme';
export const customThemes = createCustomThemesStore({
authUrl: 'http://localhost:3001',
getAccessToken: () => authStore.getAccessToken(),
});
// Eigene Themes laden
await customThemes.loadCustomThemes();
// Theme erstellen
const newTheme = await customThemes.createTheme({
name: 'Mein Theme',
emoji: '🎨',
lightColors: { primary: '200 80% 50%', ... },
darkColors: { primary: '200 70% 60%', ... },
});
// Community Themes durchsuchen
await customThemes.browseCommunity({
sort: 'popular',
search: 'dark',
});
// Theme herunterladen
await customThemes.downloadTheme(themeId);
// Theme anwenden
customThemes.applyCustomTheme(theme);
```
### Theme Editor
Der Theme Editor erlaubt das visuelle Erstellen von Themes:
**Hauptfarben (immer sichtbar):**
- Primary, Background, Surface, Foreground
- Error, Success, Warning
**Erweiterte Farben (zugeklappt):**
- PrimaryForeground, Secondary, SecondaryForeground
- SurfaceHover, SurfaceElevated, Muted, MutedForeground
- Border, BorderStrong, Input, Ring
## UI-Komponenten
### ThemePage
Vollständige Themes-Seite mit allen Optionen:
```svelte
<script>
import { ThemePage } from '@manacore/shared-theme-ui';
import { theme, a11y } from '$lib/stores';
</script>
<ThemePage
themeStore={theme}
a11yStore={a11y}
showAccessibility={true}
showPinnedThemes={true}
/>
```
### ThemeSelector
Dropdown zur Theme-Auswahl:
```svelte
<script>
import { ThemeSelector } from '@manacore/shared-theme-ui';
import { theme } from '$lib/stores';
</script>
<ThemeSelector themeStore={theme} />
```
### ThemeModeSelector
Umschalter für Light/Dark/System:
```svelte
<script>
import { ThemeModeSelector } from '@manacore/shared-theme-ui';
import { theme } from '$lib/stores';
</script>
<ThemeModeSelector themeStore={theme} />
```
### ThemeToggle
Einfacher Dark/Light Toggle:
```svelte
<script>
import { ThemeToggle } from '@manacore/shared-theme-ui';
import { theme } from '$lib/stores';
</script>
<ThemeToggle themeStore={theme} />
```
### A11ySettings
Vollständige Accessibility-Einstellungen:
```svelte
<script>
import { A11ySettings } from '@manacore/shared-theme-ui';
import { a11y } from '$lib/stores';
</script>
<A11ySettings store={a11y} />
```
## CSS-Integration
### Tailwind CSS
Die Themes werden als CSS-Variablen angewendet:
```css
:root {
--color-primary: 199 98% 45%;
--color-background: 199 100% 97%;
--color-foreground: 199 100% 18%;
/* ... weitere Tokens */
}
.dark {
--color-primary: 199 98% 48%;
--color-background: 200 25% 7%;
--color-foreground: 0 0% 100%;
}
```
In Tailwind:
```html
<div class="bg-background text-foreground">
<button class="bg-primary text-primary-foreground">
Klick mich
</button>
</div>
```
### tailwind.config.js
```javascript
theme: {
extend: {
colors: {
background: 'hsl(var(--color-background))',
foreground: 'hsl(var(--color-foreground))',
primary: {
DEFAULT: 'hsl(var(--color-primary))',
foreground: 'hsl(var(--color-primary-foreground))',
},
// ... weitere
}
}
}
```
## Dateien
### @manacore/shared-theme
| Datei | Beschreibung |
|-------|--------------|
| `src/types.ts` | Alle TypeScript Interfaces |
| `src/constants.ts` | Theme-Definitionen (8 Varianten) |
| `src/store.svelte.ts` | Theme Store Factory |
| `src/a11y-store.svelte.ts` | Accessibility Store |
| `src/a11y-constants.ts` | A11y Konstanten |
| `src/a11y-utils.ts` | A11y Helper |
| `src/user-settings-store.svelte.ts` | Server-Sync Store |
| `src/custom-themes-store.svelte.ts` | Custom Themes Store |
| `src/utils.ts` | Theme Utilities |
| `src/app-routes.ts` | Start-Page Konfiguration |
### @manacore/shared-theme-ui
| Datei | Beschreibung |
|-------|--------------|
| `src/ThemeSelector.svelte` | Theme-Dropdown |
| `src/ThemeModeSelector.svelte` | Light/Dark/System Selector |
| `src/ThemeToggle.svelte` | Einfacher Toggle |
| `src/components/ThemeCard.svelte` | Theme-Vorschau Karte |
| `src/components/ThemeGrid.svelte` | Grid von Theme-Karten |
| `src/components/A11ySettings.svelte` | A11y Einstellungen |
| `src/components/editor/` | Theme Editor Komponenten |
| `src/components/community/` | Community Themes Komponenten |
| `src/pages/ThemePage.svelte` | Vollständige Themes-Seite |
| `src/pages/ThemeEditorPage.svelte` | Theme Editor Seite |
| `src/pages/CommunityThemesPage.svelte` | Community Themes |
## Integration in eine App
### 1. Dependencies installieren
```json
{
"dependencies": {
"@manacore/shared-theme": "workspace:*",
"@manacore/shared-theme-ui": "workspace:*"
}
}
```
### 2. Store erstellen
```typescript
// src/lib/stores/theme.ts
import { createThemeStore, createA11yStore } from '@manacore/shared-theme';
export const theme = createThemeStore({
appId: 'myapp',
defaultVariant: 'ocean',
});
export const a11y = createA11yStore({
appId: 'myapp',
});
```
### 3. Im Root-Layout initialisieren
```svelte
<!-- src/routes/+layout.svelte -->
<script>
import { onMount } from 'svelte';
import { theme, a11y } from '$lib/stores/theme';
onMount(() => {
const cleanupTheme = theme.initialize();
const cleanupA11y = a11y.initialize();
return () => {
cleanupTheme();
cleanupA11y();
};
});
</script>
<slot />
```
### 4. Themes-Seite hinzufügen
```svelte
<!-- src/routes/(app)/themes/+page.svelte -->
<script>
import { ThemePage } from '@manacore/shared-theme-ui';
import { theme, a11y } from '$lib/stores/theme';
</script>
<ThemePage themeStore={theme} a11yStore={a11y} />
```
## Vorteile
- **Konsistenz:** Alle Apps sehen einheitlich aus
- **User Experience:** Theme-Einstellungen werden gespeichert
- **Accessibility:** Barrierefreiheit ist eingebaut
- **Anpassbarkeit:** Nutzer können eigene Themes erstellen
- **Community:** Themes können geteilt werden

View file

@ -0,0 +1,347 @@
# Tagesbericht 10. Dezember 2025
**Autor:** Till JS
Übersicht aller Änderungen der letzten 20 Stunden, nach Priorität sortiert.
---
## Priorität 1: Neue Features
### Network Graph Visualisierung (Contacts, Calendar, Todo)
Interaktive Netzwerk-Graphen zur Visualisierung von Verbindungen zwischen Elementen basierend auf gemeinsamen Tags.
**Commits:**
- `feat(contacts): add interactive network graph visualization`
- `feat(calendar,todo): add network graph visualization for tags`
- `feat(ui): centralize network graph components in shared-ui`
- `feat(network): add quick wins - keyboard shortcuts, strength filter, better tooltips`
- `feat(contacts): improve network graph UX with zoom-independent labels and larger nodes`
- `fix(network): initialize D3 simulation on page load`
**Features:**
- D3.js Force-Simulation für physikbasiertes Layout
- Zoom & Pan mit Maus/Touchpad
- Keyboard Shortcuts: `+/-` Zoom, `0` Reset, `Esc` Deselect, `/` Suche, `F` Fokus
- Filterung nach Tags, Firma/Ort/Projekt, Verbindungsstärke
- Hover-Tooltips mit Verbindungsdetails
- Klick auf Node öffnet Detail-Sidebar (Contacts) oder Info-Panel (Calendar/Todo)
- Doppelklick navigiert zur Detail-Seite
- Shared Components in `@manacore/shared-ui`
---
### Zentrales Tags API (mana-core-auth)
Neues zentrales Tag-Management über den Auth-Service für app-übergreifende Tags.
**Commits:**
- `feat(auth): add central Tags API in mana-core-auth`
- `feat(ui,contacts,todo): centralize tag components and add label management`
**Features:**
- CRUD-Endpoints für Tags in mana-core-auth
- Schema: `tags` Tabelle mit userId, name, color, app
- Shared Tag-Komponenten in `@manacore/shared-ui`
- Label-Management in Todo App
---
### Todo App - Umfangreiche Erweiterungen
Massive Erweiterung der Todo App mit vielen neuen Features.
**Commits:**
- `feat(todo): redesign task input and items with glass-pill style`
- `feat(todo): add comprehensive settings page with 20+ preferences`
- `feat(todo): add task edit modal and fix task loading`
- `feat(todo): add task metadata fields and mana page`
- `feat(todo): add statistics page with visualizations`
- `feat(todo): add PWA support with offline capabilities`
- `feat(todo): add multiple kanban boards with task editing features`
**Features:**
- **Glass-Pill Design**: Neues modernes UI für Task-Input und Items
- **Settings Page**: 20+ Einstellungen für Personalisierung
- **Task Edit Modal**: Inline-Bearbeitung von Tasks
- **Metadata Fields**: Erweiterte Task-Eigenschaften
- **Mana Page**: Neue Seite für Gamification/Belohnungen
- **Statistics Page**: Visualisierungen mit Charts
- **PWA Support**: Offline-Fähigkeit, Service Worker, Install-Prompt
- **Multiple Kanban Boards**: Mehrere Boards pro User
---
### Contacts App - Erweiterte Funktionen
**Commits:**
- `feat(contacts): add duplicate detection, photo upload, and batch operations`
- `feat(contacts): add enhanced favorites page with multiple view modes`
- `feat(contacts): improve alphabet view UX and make it default`
- `feat(contacts): add SearchModal component and help content`
- `feat(contacts): add archive link to settings page`
**Features:**
- **Duplikat-Erkennung**: Automatische Erkennung ähnlicher Kontakte
- **Foto-Upload**: Kontaktbilder hochladen
- **Batch-Operationen**: Mehrere Kontakte gleichzeitig bearbeiten
- **Favoriten-Seite**: Grid/List/Cards Ansichtsmodi
- **Alphabet-Ansicht**: Verbesserte UX, jetzt Standard
- **Search Modal**: Schnellsuche mit Tastaturkürzel
---
### Themes Page (Contacts, Todo, Calendar)
Neue Themes-Seite für Farbschema-Auswahl in allen Apps.
**Commits:**
- `feat(themes): add themes page to contacts, todo, and calendar apps`
**Features:**
- Auswahl aus vordefinierten Farbthemen
- Live-Preview
- Persistierung der Auswahl
---
### Referral System Frontend
Integration des Empfehlungssystems in die Web-Apps.
**Commits:**
- `feat(referral): integrate referral system frontend`
**Features:**
- Referral-Code Anzeige
- Einladungs-Links
- Belohnungs-Tracking
---
### Help System
Zentralisiertes Hilfesystem mit Shared Packages.
**Commits:**
- `feat(help): add centralized help system with shared packages`
**Features:**
- `@manacore/shared-help-content`: Hilfetexte
- `@manacore/shared-help-ui`: UI-Komponenten
- `@manacore/shared-help-types`: TypeScript-Typen
---
### CommandBar (Global Search)
Globale Suche über alle Apps hinweg.
**Commits:**
- `feat(shared-ui): add global CommandBar component with search across apps`
**Features:**
- `Cmd+K` / `Ctrl+K` öffnet CommandBar
- Suche über alle Inhalte
- Tastaturnavigation
---
## Priorität 2: UI/UX Verbesserungen
### Skeleton Loaders
Ladezustands-Anzeigen für bessere UX.
**Commits:**
- `feat(ui): add comprehensive skeleton loaders for contacts and todo apps`
- `feat(ui): add skeleton loaders for calendar and clock apps`
**Features:**
- SkeletonAvatar, SkeletonCard, SkeletonGrid, SkeletonList, SkeletonRow
- Konsistente Lade-Animation
- App-spezifische Skeleton-Komponenten
---
### Settings Page Verbesserungen
**Commits:**
- `feat(ui): add table of contents and sticky pill headers to settings page`
- `fix(settings): unify global settings across all web apps`
- `fix(settings): complete global settings unification for remaining apps`
**Features:**
- Inhaltsverzeichnis mit Sprungmarken
- Sticky Section Headers
- Einheitliche Settings-Struktur über alle Apps
---
### PillNavigation Icons
**Commits:**
- `feat(ui): add Phosphor Icons to PillNavigation`
**Features:**
- Phosphor Icons statt Text-Labels
- Bessere visuelle Unterscheidung
---
### Settings Components Refactoring
**Commits:**
- `refactor(shared-ui): convert SettingsSelect from CSS to Tailwind classes`
- `refactor(shared-ui): convert settings components from scoped CSS to Tailwind`
**Features:**
- Migration von Scoped CSS zu Tailwind
- Bessere Konsistenz und Wartbarkeit
---
## Priorität 3: Refactoring & Code Quality
### Groups → Tags Konsolidierung
**Commits:**
- `refactor(contacts): consolidate groups into tags feature`
- `fix(contacts): remove groups store dependency from data page`
**Änderungen:**
- Groups-Feature entfernt
- Funktionalität in Tags integriert
- Weniger Code-Duplikation
---
### Calendar Tags Integration
**Commits:**
- `feat(calendar): add full tags integration with colors`
**Features:**
- Farbige Tags für Events
- Filterung nach Tags
---
## Priorität 4: Bugfixes
### Database Schema Fix
**Commits:**
- `fix(todo): use TEXT for user_id columns (Better Auth compatibility)`
- `fix(db): use TEXT for user_id columns across entire codebase`
**Problem:** Better Auth generiert User-IDs, die nicht UUID-kompatibel sind.
**Lösung:** Alle `user_id` Spalten von UUID auf TEXT umgestellt.
---
### Contacts Settings Page
**Commits:**
- `fix(contacts): fix settings page styling and add Tailwind source directives`
**Problem:** Styling-Probleme auf der Settings-Seite.
**Lösung:** Tailwind source directives hinzugefügt.
---
### Network Graph Simulation
**Commits:**
- `fix(network): initialize D3 simulation on page load`
**Problem:** Network-Graph zeigte keine Nodes an.
**Lösung:** D3 Simulation wird jetzt korrekt beim Laden initialisiert.
---
## Priorität 5: DevOps & CI/CD
### Deployment Guide
**Commits:**
- `docs(cicd): add comprehensive deployment guide with CI/CD architecture`
**Dokumentation:**
- CI/CD Pipeline Architektur
- Deployment-Prozesse
- Staging vs. Production
---
### CI/CD Verbesserungen
**Commits:**
- `fix(ci): prevent container name conflict in staging deployment`
- `feat(ci): add database migrations step to tagged staging deployments`
**Änderungen:**
- Container-Namenskonflikte behoben
- Automatische DB-Migrationen bei Tagged Deployments
---
## Statistik
| Kategorie | Anzahl |
| ---------------------- | ------ |
| Neue Features | 15 |
| UI/UX Verbesserungen | 8 |
| Refactoring | 5 |
| Bugfixes | 5 |
| DevOps/CI | 3 |
| Dokumentation | 3 |
**Betroffene Apps:**
- Todo (7 große Features)
- Contacts (6 große Features)
- Calendar (2 Features)
- Clock (1 Feature)
- Shared UI (8 Komponenten)
- mana-core-auth (1 API)
---
_Autor: Till JS | Generiert am 10.12.2025_