docs: vollständige README für neue Sessions / Modell-Wechsel
Dokumentiert den aktuellen Code-Stand: was die App tut, Tech-Stack, Verzeichnisstruktur, Dev-Setup (Ports 7173/7100/55432), DB-Tabellen, tRPC-Routermap, Authentik vs Dev-Auto-Login, Frontend-Architektur, Brand & Voice, Production-Deployment, Konventionen-Verweise und Quick-Wins fürs nächste Modell. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
95beb99cd4
commit
4879556905
1 file changed
+415
-26
@@ -1,44 +1,433 @@
|
||||
# ToolCTRL everythingbutwork
|
||||
# everythingbutwork
|
||||
|
||||
Internes Web-Tool zur täglichen Übersicht über alle Projekte und Bereiche. Ergänzt **Plane** (Tickets) und **Outline** (Doku) als drittes Werkzeug gemäß ToolCTRL Projektmanagement-Leitfaden v3.0.
|
||||
Internes Web-Tool von **ToolCTRL** für den täglichen Überblick über alle Projekte und Bereiche. Es ist das dritte Werkzeug neben **Plane** (Tickets) und **Outline** (Doku) und operationalisiert den ToolCTRL Projektmanagement-Leitfaden v3.0.
|
||||
|
||||
## Stack
|
||||
> Falls du in dieses Repo neu reinrutschst (neue Session, anderes Modell): lies **`CONVENTIONS.md`** vor jedem Edit, und schau in **`_bmad-output/planning-artifacts/`** für den vollen PRD/Architektur/UX-/Stories-Kontext.
|
||||
|
||||
- **Frontend:** Vite + React 18 + TypeScript + Tailwind (kommt in Story 1.5)
|
||||
- **Backend:** Fastify + tRPC v11 + Drizzle (kommt in Story 1.4 + 1.2)
|
||||
- **DB:** Postgres 16+ (kommt in Story 1.2)
|
||||
- **Auth:** Authentik OIDC (kommt in Story 1.3)
|
||||
- **Hosting:** Docker + Caddy auf interner ToolCTRL-Infrastruktur
|
||||
---
|
||||
|
||||
## Was die App tut
|
||||
|
||||
| Bereich | Feature |
|
||||
|---|---|
|
||||
| **Puls** (`/puls`) | Unternehmens-Übersicht: Bereichs-Ampelstatus, „Heute fehlt"-Liste, bereichsübergreifende Blocker, Pflicht-Minimum-Übersicht, Jourfixe-Hinweis, „Heute im Team" (ToDos), „Zeit im Team heute" (Live-Tracking), Vortags-Feed |
|
||||
| **Bereich** (`/bereich`, `/bereich/<slug>`) | Tab-Switcher über Entwicklung / Vertrieb / Marketing — pro Bereich Projekt-Liste (Kundenprojekte / Interne Projekte) mit Pflicht-Minimum-Indicator |
|
||||
| **Projekt** (`/projekt/:id`) | Detail: Verlinkungen (Outline-Steckbrief, Plane-Board, Risikoliste), Pflicht-Minimum-Status, Meilenstein-Checkliste, DoD, Projekt-Abschluss (P1) |
|
||||
| **Heute** (`/heute`) | Tagesrhythmus: Lock-in mit ToDo-Liste, Auto-Carryover offener ToDos vom Vortag, ToDo-Editor, Zeiterfassungs-Timeline mit Edit/Delete/Nachtrag |
|
||||
| **Reflexion** (`/reflexion`) | Abend-Reflexion: ToDos abhaken, Freitext, Ampel + Trend, optional Blocker. Editierbar bis 00:00 Berlin |
|
||||
| **Archiv** (`/archiv`) | Abgeschlossene Projekte, gruppiert nach Bereich |
|
||||
|
||||
**Querschnittsfunktionen**
|
||||
|
||||
- **Zeiterfassung**: Persistente Timer-Pill in der TopNav (Live-Sekundenzähler), Quick-Picker mit ToDos + Kategorien + Freitext, retroaktiver Nachtrag-Dialog, Bindung an ToDo optional. Personenübergreifend sichtbar auf dem Puls.
|
||||
- **Blocker**: Inline in der Reflexion oder über `n`-Shortcut bzw. den globalen „Neuer Blocker"-Dialog. Auflösung nur durch den Projekt-P1.
|
||||
- **Pflicht-Minimum-Check** (Leitfaden §9): 6-Punkte-Checkliste pro Projekt, einer davon (Status-Ampel aktuell) wird stündlich per Cron aus den Reflexions-Zeitstempeln aktualisiert.
|
||||
- **Streak**: aufeinanderfolgende Werktage mit voller Reflexion. Flammen-Pill rechts oben.
|
||||
- **Keyboard-Shortcuts**: `g p/b/h/a` Navigation, `g e/v/m` direkt in Entwicklung/Vertrieb/Marketing, `n` Neuer Blocker, `?` Cheatsheet.
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Layer | Wahl |
|
||||
|---|---|
|
||||
| Sprache | **TypeScript** strict, ESM überall |
|
||||
| Repo-Layout | **pnpm**-Monorepo (`pnpm-workspace.yaml`) |
|
||||
| Node | **22+** |
|
||||
| Frontend | **Vite 5 + React 18 + Tailwind 4 + React Router 7 + React Query 5 + Sonner** (Toasts) + **lucide-react** (Icons) |
|
||||
| Backend | **Fastify 5 + tRPC v11** (`@trpc/server/adapters/fastify`) |
|
||||
| ORM | **Drizzle 0.38** + **drizzle-kit** |
|
||||
| Datenbank | **Postgres 16** |
|
||||
| Auth | **Authentik OIDC** via `openid-client v6` (Auth Code + PKCE), Session-Cookies HttpOnly+SameSite=Lax, Sessions in Postgres |
|
||||
| Validation | **Zod 3** (geteilt zwischen FE und BE via `@cockpit/shared`) |
|
||||
| Logging | **pino** (Fastify-built-in) |
|
||||
| Styling | Tailwind 4 + CSS-Custom-Properties · Hausschrift **Space Grotesk** (selbst gehostet) · Keycap-Button-Stil |
|
||||
| Tests | **Vitest** (Unit + tRPC) |
|
||||
| Reverse-Proxy | **Caddy 2** (siehe `Caddyfile`) |
|
||||
| CI | **Gitea Actions** (`.gitea/workflows/ci.yml`) |
|
||||
| Deployment | **Docker Compose** (`docker-compose.yml`) auf ToolCTRL-interner Infra |
|
||||
|
||||
⚠️ **Namespace-Hinweis**: Die internen pnpm-Packages heißen `@cockpit/*` (Historie), die App selbst heißt **everythingbutwork**. Nicht umbenennen — das würde unzählige Imports brechen ohne Mehrwert.
|
||||
|
||||
---
|
||||
|
||||
## Verzeichnisstruktur
|
||||
|
||||
```
|
||||
.
|
||||
├── apps/
|
||||
│ ├── web/ # Vite + React SPA
|
||||
│ └── api/ # Fastify Backend
|
||||
│ ├── web/ Vite + React SPA (Port 7173)
|
||||
│ │ ├── public/ Brand-Assets (Logo SVG, Fonts, Favicon)
|
||||
│ │ ├── src/
|
||||
│ │ │ ├── components/
|
||||
│ │ │ │ ├── blocker/ BlockerDialog
|
||||
│ │ │ │ ├── layout/ AppShell, TopNav
|
||||
│ │ │ │ ├── time/ TimerPill, TimelineSection, TeamTimeSection
|
||||
│ │ │ │ └── ui/ Button, Card, KeyCaret, SkeletonCard, ToolCtrlLogo
|
||||
│ │ │ ├── hooks/ useCurrentUser
|
||||
│ │ │ ├── lib/ router, trpc, trpc-provider, voice,
|
||||
│ │ │ │ format-duration, time-categories
|
||||
│ │ │ ├── routes/ puls, bereich, projekt, heute, reflexion,
|
||||
│ │ │ │ archiv, login, not-found
|
||||
│ │ │ └── styles/globals.css
|
||||
│ │ ├── index.html
|
||||
│ │ └── vite.config.ts
|
||||
│ └── api/ Fastify Backend (Port 7100)
|
||||
│ ├── crontab System-Cron-Definitionen (Containerseitig)
|
||||
│ └── src/
|
||||
│ ├── auth/ oidc, routes (login/callback/logout/dev-login),
|
||||
│ │ session, user, middleware
|
||||
│ ├── cron/routes.ts /internal/cron/* (Bearer-Token geschützt)
|
||||
│ ├── lib/ werktag (Mo-Fr Berlin), ampel, pflicht-minimum, logger
|
||||
│ ├── routers/ _app, user, area, project, deal, gtm,
|
||||
│ │ campaign, blocker, day-entry, time, puls
|
||||
│ ├── env.ts Zod-validierte Env-Vars
|
||||
│ ├── db.ts Singleton-Drizzle-Client
|
||||
│ ├── trpc.ts Context, protectedProcedure, withAudit
|
||||
│ ├── types.ts Fastify-Augmentation für req.cockpitUser
|
||||
│ └── server.ts Fastify-Bootstrap + Healthcheck
|
||||
├── packages/
|
||||
│ ├── db/ # Drizzle-Schema + Migrationen
|
||||
│ └── shared/ # Zod-Schemas + Konstanten + Typen
|
||||
├── _bmad-output/ # Planungs-Artefakte (PRD, Architektur, UX, Epics)
|
||||
└── CONVENTIONS.md # Bindende Code-Konventionen (Pflicht-Lesen)
|
||||
│ ├── db/ Drizzle-Schema + Migrationen
|
||||
│ │ ├── src/{schema,client,seed,index}.ts
|
||||
│ │ ├── migrations/ 0000_init … 0006_time_entry
|
||||
│ │ └── drizzle.config.ts
|
||||
│ └── shared/ Zod-Schemas + Konstanten + Types
|
||||
│ └── src/schemas/ day-entry, project, deal, campaign, time-entry
|
||||
├── _bmad-output/planning-artifacts/ PRD, Architektur, UX-Spec, Epics, Decision-Log
|
||||
├── docs/operations.md Ops-Guide (Authentik-Setup, Backup, Restore)
|
||||
├── scripts/backup.sh pg_dump-Skript (im Postgres-Container)
|
||||
├── docker-compose.yml Produktion (web + api + postgres + caddy)
|
||||
├── docker-compose.dev.yml Nur Postgres für lokale Entwicklung
|
||||
├── Caddyfile Reverse-Proxy-Config
|
||||
├── CONVENTIONS.md Bindende Code-Konventionen
|
||||
├── .gitea/workflows/ci.yml Gitea Actions
|
||||
├── .env.example Env-Template
|
||||
├── eslint.config.mjs Flat-Config (ESLint 9)
|
||||
├── tsconfig.base.json Shared TS-Config
|
||||
└── pnpm-workspace.yaml
|
||||
```
|
||||
|
||||
## Loslegen
|
||||
---
|
||||
|
||||
## Lokal loslegen
|
||||
|
||||
### Vorbereitung (einmalig)
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm --filter web dev # Frontend auf http://localhost:5173
|
||||
pnpm --filter api dev # Backend auf http://localhost:3000
|
||||
pnpm typecheck # Alle Workspaces typecheken
|
||||
pnpm lint # ESLint
|
||||
pnpm format # Prettier
|
||||
# Repo & Tooling
|
||||
pnpm install # 4 Workspaces installieren
|
||||
cp .env.example .env # Defaults reichen für lokale Entwicklung
|
||||
```
|
||||
|
||||
### Dev-Stack starten
|
||||
|
||||
```bash
|
||||
# 1) Postgres im Docker-Container (Port 55432, NICHT 5432!)
|
||||
docker compose -f docker-compose.dev.yml up -d postgres
|
||||
|
||||
# 2) Migration anwenden (Schema initialisieren)
|
||||
DATABASE_URL='postgres://cockpit:cockpit_dev@localhost:55432/cockpit' \
|
||||
pnpm --filter @cockpit/db migrate:up
|
||||
|
||||
# 3) Optional: Seed mit Test-Areas (Entwicklung/Vertrieb/Marketing/Allgemein)
|
||||
DATABASE_URL='postgres://cockpit:cockpit_dev@localhost:55432/cockpit' \
|
||||
pnpm --filter @cockpit/db run seed
|
||||
|
||||
# 4) API + Web parallel starten (zwei Terminals)
|
||||
pnpm --filter @cockpit/api dev # → http://localhost:7100
|
||||
pnpm --filter web dev # → http://localhost:7173
|
||||
```
|
||||
|
||||
### Browser öffnen
|
||||
|
||||
**http://localhost:7173**
|
||||
|
||||
Im Dev-Modus läuft ein **Auto-Login** als Admin-User `dejan@tool-ctrl.de`. Authentik ist nicht erforderlich; der Redirect zu `/api/auth/dev-login` setzt sofort eine Session.
|
||||
|
||||
### Wichtige Ports
|
||||
|
||||
| Service | Port | Quelle |
|
||||
|---|---|---|
|
||||
| Vite Dev-Server | **7173** | `apps/web/vite.config.ts` |
|
||||
| Fastify API | **7100** | `.env` (`PORT`) |
|
||||
| Postgres (Dev) | **55432** | `docker-compose.dev.yml` (5432 würde mit Mac-System-Postgres kollidieren) |
|
||||
|
||||
### Häufig genutzte Scripts
|
||||
|
||||
```bash
|
||||
pnpm typecheck # alle Workspaces parallel
|
||||
pnpm lint # ESLint (flat config)
|
||||
pnpm format # Prettier write
|
||||
pnpm test # Vitest (--passWithNoTests für leere)
|
||||
pnpm --filter @cockpit/db studio # Drizzle Studio (Browser-DB-Inspector)
|
||||
pnpm --filter @cockpit/db migrate:generate --name=<name> # neue Migration
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Datenbank
|
||||
|
||||
Postgres 16, Drizzle als ORM und Migrator. **Schema-Single-Source-of-Truth**: `packages/db/src/schema.ts`.
|
||||
|
||||
### Tabellen (aktueller Stand nach Migration 0006)
|
||||
|
||||
| Tabelle | Zweck |
|
||||
|---|---|
|
||||
| `user` | Mitarbeiter (`authentik_sub` als Identitäts-Anker), `is_admin`-Flag |
|
||||
| `session` | Server-Side Sessions (Postgres-persistent), Browser kennt nur Cookie-`id` |
|
||||
| `audit_event` | Audit-Trail für jede mutierende Operation |
|
||||
| `area` | Bereich (entwicklung / vertrieb / marketing / allgemein) |
|
||||
| `project` | Projekt mit Cockpit-Sicht-Feldern (Outline-/Plane-URLs, Pflicht-Minimum-State JSONB, etc.) |
|
||||
| `milestone` | Cockpit-Sicht-Meilenstein pro Projekt |
|
||||
| `day_entry` | Tageseintrag (begun / full / absent), `published_at` für Vortrags-Veröffentlichung |
|
||||
| `todo` | Tagesplan-Item, optional an Bezug gebunden, `carried_over`-Flag |
|
||||
| `absence_day` | Urlaub/Krank/Sonstiges — zählt nicht gegen Streak |
|
||||
| `blocker` | Bereichsübergreifendes Hindernis |
|
||||
| `deal`, `gtm_step`, `campaign`, `lead_handover` | Im Schema vorhanden (Phase-2-Vorbereitung), **kein UI** mehr |
|
||||
| `time_entry` | Zeiterfassungs-Block, optional an ToDo gebunden, Partial-Unique-Index erzwingt max. einen aktiven Timer pro User |
|
||||
|
||||
Migrationen liegen in `packages/db/migrations/*.sql` und werden über `drizzle-kit migrate` angewendet. Schema-Änderungen brauchen **immer eine neue Migration** — kein Schema-Drift.
|
||||
|
||||
---
|
||||
|
||||
## API-Surface
|
||||
|
||||
### REST-Routen (Fastify)
|
||||
|
||||
| Methode | Pfad | Zweck |
|
||||
|---|---|---|
|
||||
| `GET` | `/api/health` | DB- und Authentik-Reachability-Check |
|
||||
| `GET` | `/api/me` | Eingeloggter User (oder 401) |
|
||||
| `GET` | `/api/auth/login` | Startet OIDC-Flow (Redirect zu Authentik) |
|
||||
| `GET` | `/api/auth/callback` | Code-Exchange + Session-Create |
|
||||
| `POST` | `/api/auth/logout` | Session löschen + Cookie clearen |
|
||||
| `GET\|POST` | `/api/auth/dev-login` | **Nur Dev-Modus**: Auto-Login als Dejan |
|
||||
| `POST` | `/trpc/<procedurePath>` | tRPC-Endpunkt (Batch + Stream) |
|
||||
| `POST` | `/internal/cron/<job>` | Bearer-Token-geschützt: publish-day-entries, recompute-streaks, recompute-pflicht-minimum-5, pg-dump-backup |
|
||||
|
||||
### tRPC-Router (`apps/api/src/routers/_app.ts`)
|
||||
|
||||
```
|
||||
health publicProcedure
|
||||
user me, list, markAbsent
|
||||
area list
|
||||
project list, byAreaSlug, byId (mit area), create, archive,
|
||||
setMilestoneStatus, listArchived
|
||||
deal list, kpis, create, updateStage (kein UI, Phase 2)
|
||||
gtm list, create, setStatus (kein UI, Phase 2)
|
||||
campaign list, create, funnel, crosslink (kein UI, Phase 2)
|
||||
blocker listOpen, byProject, create, resolve
|
||||
dayEntry today, todayTeam, pendingCarryOver, checkIn,
|
||||
updateTodos, submitReflection, feed, todayPresence
|
||||
time now, start, stop, listToday, update, upsertManual,
|
||||
delete, teamToday
|
||||
puls overview
|
||||
```
|
||||
|
||||
Frontend importiert den Typ via `import type { AppRouter } from '@cockpit/api/router'` (siehe `apps/web/src/lib/trpc.ts`).
|
||||
|
||||
### Procedure-Typen
|
||||
|
||||
- `publicProcedure` — kein Auth-Check
|
||||
- `protectedProcedure` — wirft `UNAUTHORIZED` wenn `ctx.user === null`
|
||||
|
||||
Hinweis: `dayEntryVisibleProcedure` und `projectOwnerProcedure` waren ursprünglich geplant (siehe Architektur-Spec), sind aber implizit in den Routern erfüllt (z. B. `blocker.resolve` prüft P1 manuell).
|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
### Produktion: Authentik OIDC
|
||||
|
||||
Der Code ist vollständig vorhanden — was fehlt sind die Authentik-seitigen Daten:
|
||||
|
||||
1. Authentik-Admin legt Provider + Application an (siehe `docs/operations.md` § Authentik-Setup).
|
||||
2. Client-ID/Secret/Issuer-URL fließen in `.env.production`:
|
||||
```
|
||||
AUTHENTIK_ISSUER=https://auth.tool-ctrl.de/application/o/everythingbutwork/
|
||||
AUTHENTIK_CLIENT_ID=<von Authentik>
|
||||
AUTHENTIK_CLIENT_SECRET=<von Authentik>
|
||||
AUTHENTIK_REDIRECT_URI=https://everythingbutwork.tool-ctrl.de/api/auth/callback
|
||||
```
|
||||
3. Flow: `openid-client v6`, Authorization Code + PKCE, Discovery via `/.well-known/openid-configuration`. Code: `apps/api/src/auth/oidc.ts`.
|
||||
|
||||
**Bekannte offene Stelle (OQ-1)**: Der `groups`-Claim wird aus dem Token gelesen, aber **noch nicht** auf `user.default_area_id` gemappt. Heißt: neue User landen ohne Default-Bereich. Mapping kann später in `auth/user.ts:upsertUserFromOidc` ergänzt werden.
|
||||
|
||||
### Dev: Auto-Login
|
||||
|
||||
Im `NODE_ENV=development` ist `/api/auth/dev-login` aktiv. Die Frontend-Login-Route (`/login`) redirected dort automatisch hin, sodass man nie eine Login-Seite sieht. Logik:
|
||||
|
||||
- Sucht den User mit `email = 'dejan@tool-ctrl.de'`.
|
||||
- Existiert er nicht, wird er via `upsertUserFromOidc` mit `sub='local-dejan'` angelegt.
|
||||
- Session wird gesetzt, Redirect auf `/puls`.
|
||||
|
||||
In Production ist diese Route nicht registriert, weil die `if (env.NODE_ENV === 'development')`-Guard in `apps/api/src/auth/routes.ts` dann false ist.
|
||||
|
||||
### Session-Modell
|
||||
|
||||
- Postgres-Tabelle `session` (id, user_id, expires_at, ip_address, user_agent, created_at)
|
||||
- Cookie: `cockpit_session`, HttpOnly + SameSite=Lax + (`Secure` in Prod)
|
||||
- TTL: 24h (Env: `SESSION_TTL_HOURS`)
|
||||
- Stop-condition: jedes Request läuft durch `auth/middleware.ts` → `getSessionWithUser(sessionId)` → setzt `req.cockpitUser`
|
||||
|
||||
---
|
||||
|
||||
## Frontend-Architektur
|
||||
|
||||
### Routing
|
||||
|
||||
React Router v7 (`createBrowserRouter`) — alle Routen sind **Lazy** (Code-Split per Route). Definition in `apps/web/src/lib/router.tsx`.
|
||||
|
||||
Login-Route ist außerhalb der `AppShell` (`/login`). Alle übrigen Routen unter `/` rendern die `AppShell` (TopNav + Main + Outlet + BlockerDialog).
|
||||
|
||||
### State
|
||||
|
||||
- **Server-State**: ausschließlich React Query via tRPC (`createTRPCReact<AppRouter>`). Konfig in `apps/web/src/lib/trpc-provider.tsx` (30 s staleTime, ein Retry, zentrales onError → Toast).
|
||||
- **Auth-State**: React Query `user.me` (Hook `useCurrentUser`).
|
||||
- **Lokaler UI-State**: `useState` in der Komponente. Kein globaler Store.
|
||||
|
||||
### Brand & Voice
|
||||
|
||||
- **Logo**: ToolCTRL-Emblem (`apps/web/public/toolctrl-emblem.svg`) + Wordmark `everythingbutwork`.
|
||||
- **Hausschrift**: Space Grotesk (selbst gehostet in `apps/web/public/fonts/`).
|
||||
- **Buttons**: Keycap-Stil (Gradient + Druckeffekt + Tastenkante) — siehe `apps/web/src/styles/globals.css` § Keycap-Buttons.
|
||||
- **Mikrocopy**: zentral in `apps/web/src/lib/voice.ts`. Tonalität locker/direkt/deutsch (mit zwei englischen Lehnwörtern: „Lock in" und „Locked in" statt „Einstempeln/Eingestempelt").
|
||||
- **Ampel-Farben**: `green=#15803d/#dcfce7`, `amber=#a16207/#fef3c7`, `red=#b91c1c/#fee2e2` (UX-Spec § Visual Design Foundation).
|
||||
|
||||
### Keyboard-Shortcuts
|
||||
|
||||
In `apps/web/src/components/layout/AppShell.tsx`:
|
||||
|
||||
```
|
||||
g p Puls
|
||||
g b Bereich (Default Entwicklung)
|
||||
g e Entwicklung
|
||||
g v Vertrieb
|
||||
g m Marketing
|
||||
g h Heute
|
||||
g a Archiv
|
||||
n Neuer Blocker
|
||||
? Hilfe-Cheatsheet
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Geteilte Logik (`packages/shared`)
|
||||
|
||||
Zod-Schemas + zugehörige TS-Types werden über `@cockpit/shared` re-exportiert. Beispiele:
|
||||
|
||||
```ts
|
||||
import { DayEntrySchemas, TimeEntrySchemas, ProjectSchemas } from '@cockpit/shared';
|
||||
|
||||
// Im Backend (tRPC):
|
||||
checkIn: protectedProcedure.input(DayEntrySchemas.checkInInput).mutation(...)
|
||||
|
||||
// Im Frontend (React Hook Form):
|
||||
const resolver = zodResolver(TimeEntrySchemas.manualTimeEntryInput);
|
||||
```
|
||||
|
||||
Konstanten (`MAX_TODO_TEXT_LENGTH`, `WERKTAGE`, `JOURFIXE_DAY`, `REFLEXION_REMINDER_HOUR`) in `packages/shared/src/constants.ts`.
|
||||
|
||||
---
|
||||
|
||||
## Produktion / Deployment
|
||||
|
||||
### Docker Compose
|
||||
|
||||
`docker-compose.yml` definiert vier Services:
|
||||
|
||||
| Service | Image | Rolle |
|
||||
|---|---|---|
|
||||
| `postgres` | `postgres:16-alpine` | DB, persistentes Volume `cockpit_postgres_data`, Backups in `cockpit_backups` |
|
||||
| `api` | Build aus `apps/api/Dockerfile` | Fastify auf 3000, hängt an Postgres-Health |
|
||||
| `web` | Build aus `apps/web/Dockerfile` | Vite-Build wird in `cockpit_web_dist`-Volume gelegt |
|
||||
| `caddy` | `caddy:2-alpine` | Reverse-Proxy (80+443), TLS via Let's Encrypt, Caddyfile mountet |
|
||||
|
||||
Env-Variablen werden über `.env.production` injiziert. Required: `POSTGRES_PASSWORD`, `SESSION_COOKIE_SECRET` (mind. 32 Z.), `CRON_BEARER_TOKEN`, `AUTHENTIK_*`.
|
||||
|
||||
### Caddyfile
|
||||
|
||||
- `cockpit.tool-ctrl.de` als Host (per `${COCKPIT_HOST}` overridbar) — wird beim nächsten Deploy auf `everythingbutwork.tool-ctrl.de` gesetzt
|
||||
- `/internal/*` wird **extern geblockt** (403) — Cron-Endpunkte nur internes Netz
|
||||
- `/api/*` und `/trpc/*` reverse-proxy zu `api:3000`
|
||||
- Statischer Frontend-Content aus `/srv`
|
||||
- Standard-Security-Header (HSTS, X-Content-Type-Options, Referrer-Policy, Permissions-Policy)
|
||||
|
||||
### Cron im Postgres-Container
|
||||
|
||||
`scripts/backup.sh` läuft als System-Cron im Postgres-Container: tägliches `pg_dump` mit 14-Tage-Retention + Heartbeat an `/internal/cron/pg-dump-backup` für Audit.
|
||||
|
||||
### CI
|
||||
|
||||
`.gitea/workflows/ci.yml`:
|
||||
|
||||
```
|
||||
quality → install, typecheck, lint, format:check, test
|
||||
build → install, web build, api build (needs quality)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Konventionen — Pflicht-Lesen vor Code-Edits
|
||||
|
||||
**`CONVENTIONS.md`** im Repo-Root. Zusammengefasst die wichtigsten Punkte:
|
||||
|
||||
- **DB**: Singular `snake_case` Tabellen, `id` als UUID, FK `<entity>_id`, Enums als `text` + check.
|
||||
- **tRPC**: Procedure-Naming `<feature>.<verbNoun>` (camelCase). Inputs **immer** Zod aus `@cockpit/shared`, niemals inline.
|
||||
- **Errors**: `TRPCError` mit **deutschen** menschenlesbaren Messages. Frontend zeigt sie via zentralem onError-Toast.
|
||||
- **State**: Server-State ausschließlich React Query — niemals `useState` für Server-Loading.
|
||||
- **Werktage/Zeit**: Alle Datumslogik im Backend (`apps/api/src/lib/werktag.ts`). Server-Zeit `Europe/Berlin`.
|
||||
- **Sichtbarkeit**: Heutige ToDos und Zeit sind **teamweit sichtbar** (Änderung gegenüber Original-PRD). Vortags-Reflexionen werden via Cron publiziert.
|
||||
- **No-Gos**: kein Kanban, kein Steckbrief-Editor, kein Plane-Issue-Spiegel — die Wahrheit dieser Dinge lebt in Plane/Outline.
|
||||
|
||||
---
|
||||
|
||||
## Was bewusst NICHT gebaut ist (Phase 2)
|
||||
|
||||
- **Plane-/Outline-API-Sync** — Cockpit verlinkt nur, hängt sich nichts ab
|
||||
- **Kanban-Pipeline / Lead-Funnel-UI** — Schemas existieren, aber UI raus
|
||||
- **Reportings / Wochen-/Monatsauswertungen** — bewusst minimal
|
||||
- **Reaktionen / Kommentare** auf Tagesupdates
|
||||
- **Mobile schreibbar** — Desktop-first, mobil nur lesbar
|
||||
- **Volltextsuche** über Updates
|
||||
- **Kimai-Sync** — Kimai läuft parallel ohne Bezug (eigener Pfad)
|
||||
- **Loki/Grafana, Sentry** — pino → stdout reicht erstmal
|
||||
- **Authentik-Group → Bereich-Mapping** (OQ-1, siehe Auth-Abschnitt)
|
||||
|
||||
---
|
||||
|
||||
## Planungs-Artefakte
|
||||
|
||||
- [PRD](./_bmad-output/planning-artifacts/prds/prd-projectmanagement-2026-05-25/prd.md) — 49 Functional Requirements, 16 NFRs
|
||||
- [Architektur](./_bmad-output/planning-artifacts/architecture.md) — Stack, Schnitt, Patterns
|
||||
- [UX-Spec](./_bmad-output/planning-artifacts/ux-design-specification.md) — Brand-Tokens, Komponenten, User Journeys
|
||||
- [Epics & Stories](./_bmad-output/planning-artifacts/epics.md) — 6 Epics, 32 Stories
|
||||
- [Konventionen](./CONVENTIONS.md) — Naming, Struktur, Formate, Errors
|
||||
Vollständige PRD/Architektur/UX/Stories im `_bmad-output/planning-artifacts/`-Ordner. Diese Artefakte sind teilweise historisch (z. B. Kanban war ursprünglich geplant, ist aber inzwischen aus dem UI raus) — der Code ist die aktuelle Quelle der Wahrheit. Die Artefakte sind nützlich für:
|
||||
|
||||
- Hintergrund zur Methodik (ToolCTRL-Leitfaden v3.0)
|
||||
- Entscheidungs-Log (`prds/prd-projectmanagement-2026-05-25/.decision-log.md`)
|
||||
- Ursprüngliche User Journeys
|
||||
- Anti-Ziele und Roadmap-Hinweise
|
||||
|
||||
| Datei | Inhalt |
|
||||
|---|---|
|
||||
| `prds/prd-projectmanagement-2026-05-25/prd.md` | PRD (49 FRs, 16 NFRs) |
|
||||
| `architecture.md` | Stack-Entscheidungen, Patterns |
|
||||
| `ux-design-specification.md` | Brand-Tokens, Komponenten, Voice & Tone |
|
||||
| `epics.md` | 6 Epics, 32 Stories |
|
||||
|
||||
---
|
||||
|
||||
## Repo & Remote
|
||||
|
||||
- **Default-Branch**: `main`
|
||||
- **Remote**: `ssh://git@git.tool-ctrl.de:2222/toolctrl-labs/everythingbutwork.git` (Gitea, non-standard SSH-Port)
|
||||
- **Gitignore**: `.env`, `node_modules`, `dist`, `_bmad/`-Tools (Bmad-Framework), `.claude/`, `ToolCTRL Brand Identity/` (215 MB Raw-Assets)
|
||||
|
||||
---
|
||||
|
||||
## Quick-Wins fürs nächste Modell / die nächste Session
|
||||
|
||||
1. **Sanity-Check vor jedem Edit**: `git status`, dann lies `CONVENTIONS.md`.
|
||||
2. **Schema ändern?** → neue Drizzle-Migration generieren (`pnpm --filter @cockpit/db migrate:generate --name=<...>`) und applien. NIEMALS direkt am DB-Snapshot rumdoktern.
|
||||
3. **Neuer tRPC-Endpoint?** → Zod-Schema in `packages/shared/src/schemas/<feature>.ts`, dann Procedure im passenden Router. Frontend importiert den Typ automatisch über `AppRouter`.
|
||||
4. **Englisch-Verbot in UI-Strings** — Ausnahmen: „Lock in", „Locked in" (vom Owner so gewollt), Fachbegriffe wie „Kanban" (im Leitfaden gesetzt).
|
||||
5. **Postgres-Port ist 55432, nicht 5432**. Ports sind aus Kollisionsgründen so gewählt: API 7100, Web 7173, DB 55432.
|
||||
6. **Dev-User**: `dejan@tool-ctrl.de` (Admin). Wird im Auto-Login angelegt falls nicht vorhanden.
|
||||
7. **Beim Bauen neuer Features**: schau wie ähnliche Features umgesetzt sind (z. B. Time-Tracking als Vorbild für strukturähnliche Domain-Slices: Schema → Shared-Zod → Router → Frontend-Komponente).
|
||||
Reference in new issue
Block a user