From 1122acd5a5b77f8144c0d559a070f8ae6993b106 Mon Sep 17 00:00:00 2001 From: LordSchmackes Date: Fri, 3 Jul 2026 10:46:54 +0200 Subject: [PATCH] Remove Gemini AI PR review workflow and enhance CSRF protection in admin actions --- .../coolify_react_cms_stack_ea69bc7d.plan.md | 324 ------------------ .github/workflows/gemini-pr-review.yml | 27 -- admin.php | 24 +- docker-compose.yml | 2 +- 4 files changed, 18 insertions(+), 359 deletions(-) delete mode 100644 .cursor/plans/coolify_react_cms_stack_ea69bc7d.plan.md delete mode 100644 .github/workflows/gemini-pr-review.yml diff --git a/.cursor/plans/coolify_react_cms_stack_ea69bc7d.plan.md b/.cursor/plans/coolify_react_cms_stack_ea69bc7d.plan.md deleted file mode 100644 index e0d7bc5..0000000 --- a/.cursor/plans/coolify_react_cms_stack_ea69bc7d.plan.md +++ /dev/null @@ -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. diff --git a/.github/workflows/gemini-pr-review.yml b/.github/workflows/gemini-pr-review.yml deleted file mode 100644 index 69f979b..0000000 --- a/.github/workflows/gemini-pr-review.yml +++ /dev/null @@ -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..." diff --git a/admin.php b/admin.php index 460db2a..eca5819 100644 --- a/admin.php +++ b/admin.php @@ -196,16 +196,23 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save_ } if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delete') { - $slugDel = trim($_POST['slug'] ?? ''); - if ($slugDel !== '' && delete_recipe($slugDel)) { - $message = 'Recipe deleted.'; - $allRecipes = load_recipes(); + if (!hash_equals($_SESSION['csrf_token'] ?? '', $_POST['token'] ?? '')) { + $errors[] = 'Invalid form token, please retry.'; } else { - $message = 'Could not delete recipe.'; + $slugDel = trim($_POST['slug'] ?? ''); + if ($slugDel !== '' && delete_recipe($slugDel)) { + $message = 'Recipe deleted.'; + $allRecipes = load_recipes(); + } else { + $message = 'Could not delete recipe.'; + } + $editing = null; + $editIndex = null; } - $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 } ?> @@ -527,6 +535,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delet Edit
+
@@ -538,6 +547,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delet
+ diff --git a/docker-compose.yml b/docker-compose.yml index 466dc9a..b38f67e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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