Remove Gemini AI PR review workflow and enhance CSRF protection in admin actions
This commit is contained in:
@@ -1,324 +0,0 @@
|
||||
---
|
||||
name: Coolify React CMS Stack
|
||||
overview: "Empfohlener Standard-Stack: Next.js (React) + Payload CMS (self-hosted) + PostgreSQL, alles als Docker-Images über GitHub → Coolify. Cursor/MCP für die Entwicklung, GitHub Actions für Qualitätssicherung vor dem Deploy."
|
||||
todos:
|
||||
- id: scaffold-monorepo
|
||||
content: "Greenfield-Monorepo anlegen: apps/web (Next standalone), apps/cms (Payload), packages/shared-types"
|
||||
status: pending
|
||||
- id: docker-coolify
|
||||
content: "Dockerfiles, docker-compose.yml, docs/COOLIFY.md (3 Services: Postgres, CMS, Web) nach FlixCooks-Muster"
|
||||
status: pending
|
||||
- id: cms-content-model
|
||||
content: Payload Collections + Webhooks für On-Demand Revalidation; Env-Beispiele in .env.example
|
||||
status: pending
|
||||
- id: github-ci
|
||||
content: "GitHub Actions: lint, typecheck, build, docker build smoke; branch protection auf main"
|
||||
status: pending
|
||||
- id: mcp-agents
|
||||
content: .cursor/mcp.json + .agents/brain.md mit Stack-, Env- und Deploy-Regeln für KI
|
||||
status: pending
|
||||
- id: animation-baseline
|
||||
content: "GSAP/Lenis/Framer-Baseline-Komponenten und Regel: Overlays stoppen Lenis"
|
||||
status: pending
|
||||
isProject: false
|
||||
---
|
||||
|
||||
# Tech-Stack & Workflow: React-Sites, CMS, Docker, Coolify
|
||||
|
||||
## Empfehlung (Default)
|
||||
|
||||
Du bist unsicher bei CMS und Frontend – hier ist ein **bewährtes Default**, das zu deinen Vorgaben passt (React, Animationen, Docker, Coolify, KI/MCP, CI):
|
||||
|
||||
| Schicht | Technologie | Warum |
|
||||
|---------|-------------|--------|
|
||||
| **Frontend** | [Next.js 15](https://nextjs.org) (App Router, TypeScript) | SEO/SSR/ISR, React-Ökosystem, `output: 'standalone'` für ein schlankes Production-Docker-Image |
|
||||
| **Animationen** | GSAP (+ ScrollTrigger), Framer Motion, Lenis | GSAP für Scroll/Timeline-Premium-Feel; Framer für UI-Micro-Interactions; Lenis kennst du bereits aus FlixCooks |
|
||||
| **CMS** | [Payload CMS 3](https://payloadcms.com) (eigener Container) | TypeScript, Postgres-native, Admin-UI out of the box, Docker-freundlich, passt zu Coolify wie dein aktuelles Postgres-Setup |
|
||||
| **Datenbank** | PostgreSQL 16 (Coolify Database Service) | Eine Instanz, getrennte DBs/User für CMS vs. App optional |
|
||||
| **Runtime / Deploy** | Docker + [Coolify](https://coolify.io) | Git-Webhook → Build → Traefik/HTTPS; du hast das Muster schon in [docs/COOLIFY.md](docs/COOLIFY.md) |
|
||||
| **Lokale Dev** | `docker compose` (Web + CMS + Postgres) | Parität zu Production, wie [docker-compose.yml](docker-compose.yml) bei FlixCooks |
|
||||
| **KI-Entwicklung** | Cursor + MCP-Server | Repo-Kontext, GitHub, Docs, optional DB |
|
||||
| **CI** | GitHub Actions | Lint, Types, Build, Docker-Smoke, optional E2E; PR-Review wie [.github/workflows/gemini-pr-review.yml](.github/workflows/gemini-pr-review.yml) |
|
||||
|
||||
**Alternative Frontend:** Vite + React SPA + nginx-Image – maximal frei für reine Animation-Landingpages, aber schlechteres SEO und kein ISR ohne Extra-Aufwand. **Alternative CMS (weniger Ops):** Sanity/Contentful (Cloud) – nur Frontend-Container auf Coolify. **Alternative CMS (kein Backend):** Tina/Decap + Markdown im Repo – gut für Blogs, schwächer für Redakteur:innen ohne Git.
|
||||
|
||||
---
|
||||
|
||||
## Zielarchitektur auf Coolify
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph dev [Entwicklung]
|
||||
Cursor[Cursor + MCP]
|
||||
LocalCompose[docker compose]
|
||||
Cursor --> LocalCompose
|
||||
end
|
||||
|
||||
subgraph github [GitHub]
|
||||
Repo[Monorepo]
|
||||
GHA[GitHub Actions CI]
|
||||
Repo --> GHA
|
||||
end
|
||||
|
||||
subgraph coolify [Coolify Server]
|
||||
PG[(PostgreSQL)]
|
||||
CMS[Payload CMS Container]
|
||||
WEB[Next.js Container]
|
||||
Traefik[Traefik HTTPS]
|
||||
PG --> CMS
|
||||
CMS -->|REST/GraphQL| WEB
|
||||
Traefik --> WEB
|
||||
Traefik --> CMS
|
||||
end
|
||||
|
||||
dev -->|push main| Repo
|
||||
GHA -->|grüner Build| Repo
|
||||
Repo -->|Webhook Deploy| coolify
|
||||
```
|
||||
|
||||
**Drei Coolify-Ressourcen** (analog zu deinem FlixCooks-Setup: Postgres + App):
|
||||
|
||||
1. **PostgreSQL** – internal URL, nicht öffentlich
|
||||
2. **CMS-App** – Dockerfile aus `apps/cms`, Port z. B. 3001, Env: `DATABASE_URL`, `PAYLOAD_SECRET`
|
||||
3. **Web-App** – Dockerfile aus `apps/web`, Port 3000, Env: `CMS_URL` (internal), `REVALIDATE_SECRET` für On-Demand-ISR
|
||||
|
||||
Persistenz: Postgres-Volume (Inhalte), optional Volume für CMS-Uploads (`/app/media`).
|
||||
|
||||
---
|
||||
|
||||
## Repository-Struktur (Greenfield-Vorlage)
|
||||
|
||||
Ein Repo pro „Site-Familie“ oder Monorepo für mehrere Marken:
|
||||
|
||||
```
|
||||
my-site/
|
||||
├── apps/
|
||||
│ ├── web/ # Next.js
|
||||
│ │ ├── Dockerfile
|
||||
│ │ ├── src/
|
||||
│ │ └── next.config.ts # output: 'standalone'
|
||||
│ └── cms/ # Payload
|
||||
│ ├── Dockerfile
|
||||
│ └── payload.config.ts
|
||||
├── packages/
|
||||
│ └── shared-types/ # optional: gemeinsame TS-Typen CMS ↔ Web
|
||||
├── docker-compose.yml # lokaler Prod-Parität-Stack
|
||||
├── docker-compose.dev.yml # nur Postgres (wie bei FlixCooks)
|
||||
├── .github/workflows/
|
||||
│ ├── ci.yml
|
||||
│ └── gemini-pr-review.yml # optional, aus FlixCooks übernehmen
|
||||
├── .cursor/
|
||||
│ └── mcp.json # MCP-Server für das Team
|
||||
├── .agents/
|
||||
│ ├── brain.md # Architektur für KI (Pattern aus FlixCooks)
|
||||
│ └── rules/AGENT.md
|
||||
└── docs/
|
||||
└── COOLIFY.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Frontend-Stack (React + Animationen)
|
||||
|
||||
**Kern:**
|
||||
|
||||
- **Next.js App Router** – Seiten in `app/`, Server Components für CMS-Daten, Client Components nur für Animation/Interaktion
|
||||
- **TypeScript** – strikt; Typen aus Payload generieren (`payload generate:types`)
|
||||
- **Styling** – CSS Modules oder Tailwind (nur wenn du es willst; FlixCooks bleibt bei Vanilla CSS – für neue React-Sites ist Tailwind optional, nicht Pflicht)
|
||||
|
||||
**Animation-Toolkit:**
|
||||
|
||||
| Tool | Einsatz |
|
||||
|------|---------|
|
||||
| **GSAP + ScrollTrigger** | Hero-Sequences, pinned Sections, komplexe Timelines |
|
||||
| **Framer Motion** | Hover, Page-Transitions, modale UI |
|
||||
| **Lenis** | Smooth Scroll (wie FlixCooks: bei Overlays `lenis.stop()`) |
|
||||
| **(optional) @react-three/fiber** | 3D-Hero nur wenn nötig |
|
||||
|
||||
**CMS-Anbindung im Web:**
|
||||
|
||||
- **Build-Zeit (SSG):** `generateStaticParams` + Fetch von Payload REST für Marketing-Seiten
|
||||
- **On-Demand Revalidation:** Payload-Webhook → `POST /api/revalidate?secret=...` in Next.js (Inhalt ändert sich ohne Full-Redeploy)
|
||||
- **Preview:** Draft-Modus mit Payload Preview-URL + Next `draftMode()`
|
||||
|
||||
Env im Web-Container (Coolify):
|
||||
|
||||
- `CMS_URL=http://payload-service:3001` (internal hostname)
|
||||
- `REVALIDATE_SECRET`, `NEXT_PUBLIC_SITE_URL`
|
||||
|
||||
---
|
||||
|
||||
## CMS-Stack (Payload auf Coolify)
|
||||
|
||||
**Warum Payload als Default:** Self-hosted, eine Postgres-URL, Admin unter `/admin`, Collections/Blocks für Seitenmodule, Media-Uploads, Webhooks – alles containerisierbar.
|
||||
|
||||
**Coolify Env (CMS):**
|
||||
|
||||
- `DATABASE_URL` – internal Postgres URL (gleiches Muster wie [docs/COOLIFY.md](docs/COOLIFY.md) Zeilen 33–43)
|
||||
- `PAYLOAD_SECRET` – langer Zufallswert (nur Secrets, nie ins Repo)
|
||||
- `NEXT_PUBLIC_SERVER_URL` – öffentliche CMS-URL (für Admin-Assets)
|
||||
|
||||
**Erstes Deployment:** analog FlixCooks `RUN_DB_SEED` – einmalig Migration/Seed, danach Flag entfernen.
|
||||
|
||||
**Sicherheit:** CMS-Admin nur über HTTPS; CORS auf Web-Domain beschränken; API-Keys für Preview/Revalidate nur als Secrets.
|
||||
|
||||
---
|
||||
|
||||
## Docker-Images
|
||||
|
||||
### Web (`apps/web/Dockerfile`) – Next standalone
|
||||
|
||||
Mehrstufig: `node:22-alpine` → `npm ci` → `npm run build` → Runtime nur `.next/standalone` + `static` + `public`.
|
||||
|
||||
- `EXPOSE 3000`
|
||||
- `HEALTHCHECK` auf `/api/health` (kleine Route: `{ "status": "ok" }`)
|
||||
- Coolify: Port **3000**, Health Path `/api/health`
|
||||
|
||||
### CMS (`apps/cms/Dockerfile`)
|
||||
|
||||
Payload-Official-Pattern oder Node-Image mit `npm run build && npm run start`.
|
||||
|
||||
- `HEALTHCHECK` auf CMS-Health-Endpoint
|
||||
- Volume für `/app/media` (Uploads überleben Redeploy)
|
||||
|
||||
### Lokales Parität-Compose
|
||||
|
||||
Orientierung an deinem bestehenden [docker-compose.yml](docker-compose.yml):
|
||||
|
||||
- `postgres` mit `healthcheck`
|
||||
- `cms` `depends_on: postgres: service_healthy`
|
||||
- `web` `depends_on: cms` + `DATABASE_URL` nur wenn Web eigene DB braucht (meist nicht – nur CMS nutzt DB)
|
||||
|
||||
Entrypoint-Pattern von [docker/entrypoint.sh](docker/entrypoint.sh) übernehmen: **DB warten → Migration → dann Prozess starten**.
|
||||
|
||||
---
|
||||
|
||||
## Coolify-Workflow (End-to-End)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Dev as Developer
|
||||
participant GH as GitHub
|
||||
participant GHA as GitHub Actions
|
||||
participant CF as Coolify
|
||||
participant Web as Next Container
|
||||
participant CMS as Payload Container
|
||||
|
||||
Dev->>GH: push feature branch
|
||||
Dev->>GH: open PR
|
||||
GHA->>GHA: lint typecheck build docker
|
||||
GHA-->>Dev: PR checks green
|
||||
Dev->>GH: merge to main
|
||||
GH->>CF: webhook deploy
|
||||
CF->>CF: build CMS image
|
||||
CF->>CF: build Web image
|
||||
CF->>Web: rolling update
|
||||
CMS->>Web: optional revalidate webhook
|
||||
```
|
||||
|
||||
**Coolify-Konfiguration pro App:**
|
||||
|
||||
| Setting | Web | CMS |
|
||||
|---------|-----|-----|
|
||||
| Build Pack | Dockerfile | Dockerfile |
|
||||
| Branch | `main` | `main` |
|
||||
| Port | 3000 | 3001 |
|
||||
| Health | `/api/health` | `/api/health` oder Payload-Default |
|
||||
| Secrets | `REVALIDATE_SECRET`, `CMS_URL` | `DATABASE_URL`, `PAYLOAD_SECRET` |
|
||||
|
||||
**Checkliste Erstdeploy** (aus [docs/COOLIFY.md](docs/COOLIFY.md) übertragbar):
|
||||
|
||||
1. Postgres healthy, internal URL notieren
|
||||
2. CMS deployen, Admin anlegen, Collections seeden
|
||||
3. Web deployen mit internal `CMS_URL`
|
||||
4. Domain + HTTPS (Traefik)
|
||||
5. Webhook Payload → Next Revalidate testen
|
||||
6. `RUN_DB_SEED` / Migration-Flags wieder aus
|
||||
|
||||
---
|
||||
|
||||
## GitHub Actions (CI vor Coolify)
|
||||
|
||||
**Workflow `ci.yml`** (bei jedem PR + push auf `main`):
|
||||
|
||||
1. **checkout**
|
||||
2. **Node 22** + Cache (`apps/web`, `apps/cms`)
|
||||
3. **Parallel jobs oder Matrix:**
|
||||
- `npm run lint` (ESLint)
|
||||
- `npm run typecheck` (`tsc --noEmit`)
|
||||
- `npm run build` (Web + CMS)
|
||||
4. **Docker build test** (ohne Push):
|
||||
- `docker build -f apps/web/Dockerfile apps/web`
|
||||
- `docker build -f apps/cms/Dockerfile apps/cms`
|
||||
5. **(optional) Playwright** gegen `docker compose up` – Smoke: Startseite, eine CMS-Seite, Health endpoints
|
||||
6. **(optional) PR Review** – bestehendes Gemini-Workflow aus FlixCooks wiederverwenden
|
||||
|
||||
**Branch-Schutz:** `main` nur mit grünen Required Checks mergebar.
|
||||
|
||||
Coolify deployt **nach** Merge – CI blockiert kaputte Images, Coolify baut das echte Production-Image (oder du pushst zu GHCR – für den Start reicht Coolify-eigener Build).
|
||||
|
||||
---
|
||||
|
||||
## KI-gestützte Entwicklung mit MCP
|
||||
|
||||
**Cursor `mcp.json` (Team-Standard):**
|
||||
|
||||
| MCP | Zweck |
|
||||
|-----|--------|
|
||||
| **GitHub** | Issues, PRs, Actions-Logs aus dem Chat |
|
||||
| **Context7** (oder Fetch) | Aktuelle Next.js / Payload / GSAP-Docs |
|
||||
| **Postgres** (optional, nur Dev) | Content/Debugging – nie Production-Credentials im Repo |
|
||||
| **Filesystem** | Standard in Cursor |
|
||||
|
||||
**Projekt-Wissen für Agenten** (aus FlixCooks übernehmen):
|
||||
|
||||
- [.agents/brain.md](.agents/brain.md) – Architektur, Env-Regeln, Coolify-Hosts
|
||||
- [.agents/rules/AGENT.md](.agents/rules/AGENT.md) – Commit/PR/TODO-Konventionen
|
||||
- `docs/COOLIFY.md` – Deploy-Runbook pro Projekt
|
||||
|
||||
**Typischer KI-Workflow:**
|
||||
|
||||
1. Ticket/Issue in GitHub (MCP)
|
||||
2. Feature-Branch; Agent ändert `apps/web` + Payload-Collection
|
||||
3. `docker compose up` lokal; Agent nutzt Health-URLs
|
||||
4. PR → CI grün → Gemini-Review optional
|
||||
5. Merge → Coolify
|
||||
|
||||
---
|
||||
|
||||
## Entwickler-Alltag (Kurzablauf)
|
||||
|
||||
1. `cp .env.example .env` – lokale URLs
|
||||
2. `docker compose -f docker-compose.dev.yml up -d` (nur Postgres) **oder** volles `docker compose up`
|
||||
3. `npm run dev` in `apps/web` und `apps/cms` (schneller Hot Reload) **oder** alles in Containern
|
||||
4. In Payload Inhalte pflegen → Webhook triggert Revalidate
|
||||
5. `git push` → PR → CI → merge → Coolify rebuild
|
||||
|
||||
---
|
||||
|
||||
## Bezug zu FlixCooks (dieses Repo)
|
||||
|
||||
FlixCooks ist heute **Vanilla PHP + Postgres + eingebautes `admin.php`-CMS** – kein React. Das ist ein **paralleler Stack**, kein Widerspruch:
|
||||
|
||||
| Aspekt | FlixCooks (aktuell) | Neuer React-Stack |
|
||||
|--------|---------------------|-------------------|
|
||||
| Frontend | PHP-Templates | Next.js + React |
|
||||
| CMS | `admin.php` | Payload (Container) |
|
||||
| DB | Postgres | Postgres |
|
||||
| Deploy | [Dockerfile](Dockerfile) + [COOLIFY.md](docs/COOLIFY.md) | gleiches Muster, zwei App-Services |
|
||||
| Animationen | Lenis, Vanilla CSS | Lenis + GSAP + Framer |
|
||||
|
||||
Du kannst FlixCooks auf Coolify weiterbetreiben und **neue Projekte** im Monorepo-Template starten. Eine spätere Migration FlixCooks → Next wäre ein separates Projekt (Content-Export aus Postgres/JSON → Payload-Collections).
|
||||
|
||||
---
|
||||
|
||||
## Nächste konkrete Schritte (nach Plan-Freigabe)
|
||||
|
||||
1. **Greenfield-Repo** aus der Struktur oben scaffolden (oder `create-payload-app` + Next in Monorepo).
|
||||
2. **Minimale Collections** in Payload: `pages`, `recipes` (oder `projects`), `siteSettings`, Media.
|
||||
3. **Dockerfiles + compose** + `docs/COOLIFY.md` vom FlixCooks-Muster kopieren/adaptieren.
|
||||
4. **GitHub Actions `ci.yml`** anlegen.
|
||||
5. **Coolify:** Postgres → CMS → Web → Webhook Revalidate testen.
|
||||
6. **`.cursor/mcp.json` + `.agents/brain.md`** für das neue Repo.
|
||||
|
||||
Wenn du willst, kann im nächsten Schritt ein **konkretes Starter-Repo** (Dateien + minimale Hero-Animation + eine Payload-Collection) direkt in einem neuen Ordner oder Branch angelegt werden.
|
||||
@@ -1,27 +0,0 @@
|
||||
name: "Gemini AI PR Review"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
review:
|
||||
name: "Code Review"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Gemini AI Review
|
||||
uses: petarzarkov/gemini-code-review-action@v1.0.4 # Pinned version for stability
|
||||
env:
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
model: gemini-2.5-flash-lite # Cheapest Gemini model - $0.10/1M input, $0.40/1M output
|
||||
# You can add custom prompt instructions here if needed:
|
||||
# prompt: "You are a senior software engineer. Review this PR..."
|
||||
@@ -196,6 +196,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save_
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delete') {
|
||||
if (!hash_equals($_SESSION['csrf_token'] ?? '', $_POST['token'] ?? '')) {
|
||||
$errors[] = 'Invalid form token, please retry.';
|
||||
} else {
|
||||
$slugDel = trim($_POST['slug'] ?? '');
|
||||
if ($slugDel !== '' && delete_recipe($slugDel)) {
|
||||
$message = 'Recipe deleted.';
|
||||
@@ -205,7 +208,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delet
|
||||
}
|
||||
$editing = null;
|
||||
$editIndex = null;
|
||||
}
|
||||
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save') {
|
||||
if (!hash_equals($_SESSION['csrf_token'] ?? '', $_POST['token'] ?? '')) {
|
||||
$errors[] = 'Invalid form token, please retry.';
|
||||
} else {
|
||||
// helpers
|
||||
$parse_csv = fn($text) => array_values(array_filter(array_map('trim', explode(',', $text ?? '')), 'strlen'));
|
||||
$parse_lines = fn($text) => array_values(array_filter(array_map('trim', preg_split('/\\r?\\n/', $text ?? '')), 'strlen'));
|
||||
@@ -344,6 +351,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delet
|
||||
$message = 'Could not save to database.';
|
||||
}
|
||||
}
|
||||
} // end CSRF else
|
||||
}
|
||||
?>
|
||||
<!doctype html>
|
||||
@@ -527,6 +535,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delet
|
||||
<a class="button" href="/admin.php?edit=<?php echo urlencode($r['slug']); ?>" style="padding:8px 12px;">Edit</a>
|
||||
<form method="post" style="margin:0;" onsubmit="return confirm('Delete this recipe?');">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="token" value="<?php echo e($_SESSION['csrf_token']); ?>">
|
||||
<input type="hidden" name="slug" value="<?php echo e($r['slug']); ?>">
|
||||
<button type="submit" class="button" style="padding:8px 12px;">Delete</button>
|
||||
</form>
|
||||
@@ -538,6 +547,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delet
|
||||
|
||||
<form class="admin-form" method="post">
|
||||
<input type="hidden" name="action" value="save">
|
||||
<input type="hidden" name="token" value="<?php echo e($_SESSION['csrf_token']); ?>">
|
||||
<?php if ($editing): ?>
|
||||
<input type="hidden" name="slug_original" value="<?php echo e($editing['slug']); ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ services:
|
||||
- "8080:80"
|
||||
environment:
|
||||
DATABASE_URL: postgresql://flixcooks:flixcooks_prod@postgres:5432/flixcooks
|
||||
FLIXCOOKS_ADMIN_KEY: ${FLIXCOOKS_ADMIN_KEY:-change-me-in-production}
|
||||
FLIXCOOKS_ADMIN_KEY: ${FLIXCOOKS_ADMIN_KEY:-}
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
Reference in New Issue
Block a user