mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-17 22:09:39 +02:00
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:
parent
e84371aa94
commit
ee42b6cc76
381 changed files with 39284 additions and 6275 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
709
docs/SELF-HOSTING-GUIDE.md
Normal 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
451
docs/ULOAD-DEPLOYMENT.md
Normal 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
|
||||
383
docs/central-services/COMMAND-BAR.md
Normal file
383
docs/central-services/COMMAND-BAR.md
Normal 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
|
||||
603
docs/central-services/HELP.md
Normal file
603
docs/central-services/HELP.md
Normal 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
|
||||
90
docs/central-services/README.md
Normal file
90
docs/central-services/README.md
Normal 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`
|
||||
248
docs/central-services/TAGS.md
Normal file
248
docs/central-services/TAGS.md
Normal 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")
|
||||
497
docs/central-services/THEMING.md
Normal file
497
docs/central-services/THEMING.md
Normal 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
|
||||
347
docs/daily-reports/2025-12-10.md
Normal file
347
docs/daily-reports/2025-12-10.md
Normal 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_
|
||||
Loading…
Add table
Add a link
Reference in a new issue