From 547778bba53b9ed9d95a291e2f22f297d24a7aaf Mon Sep 17 00:00:00 2001 From: LordSchmackes Date: Sat, 23 May 2026 10:13:00 +0200 Subject: [PATCH] Enhance project documentation with updated architecture, PostgreSQL integration, and local development setup. Refine admin panel and GitHub Actions details. Remove Firebase configuration from `.env.example` and clarify database fallback mechanisms. --- .agents/brain.md | 155 +++++++++++++++++++++++++++++++++-------- .env.example | 13 ++-- README.md | 100 +++++++++++++++++++++----- docker-compose.dev.yml | 26 +++++++ scripts/db-check.php | 39 +++++++++++ 5 files changed, 280 insertions(+), 53 deletions(-) create mode 100644 docker-compose.dev.yml create mode 100644 scripts/db-check.php diff --git a/.agents/brain.md b/.agents/brain.md index 9d54165..a31ba54 100644 --- a/.agents/brain.md +++ b/.agents/brain.md @@ -1,41 +1,138 @@ # FlixCooks Project Brain -This document summarizes the architectural knowledge, conventions, and learnings accumulated during our session. +This document summarizes architectural knowledge, conventions, and learnings for humans and AI agents working on this repo. ## 1. Project Architecture & Stack -- **Backend:** Vanilla PHP. The project intentionally avoids heavy frameworks. -- **Database:** Flat-file JSON database (`data/recipes.json`). Data is loaded and saved via utility functions in `helpers.php`. -- **Admin Panel (`admin.php`):** Acts as a lightweight CMS. It uses simple textareas where each line maps to an array element (e.g., for `ingredients`, `steps`, `step_videos`, `step_timers`). This keeps the JSON structure clean and parsing straightforward. -- **Frontend:** Server-rendered PHP templates (`index.php`, `recipe.php`) with Vanilla JavaScript and Vanilla CSS. No Tailwind or heavy component libraries. +- **Backend:** Vanilla PHP. No heavy frameworks. +- **Database:** PostgreSQL (optional) via `DATABASE_URL` in `.env`, with automatic seed from and backup to `data/recipes.json`. All data access lives in `helpers.php`; connection logic in `config.php`. +- **Site settings (legal pages):** Flat-file `data/site.json` via `load_site_settings()` / `save_site_settings()` — not in Postgres. +- **Admin Panel (`admin.php`):** Lightweight CMS. Textareas use one line per array element (`ingredients`, `steps`, `step_videos`, `step_timers`). +- **Frontend:** Server-rendered PHP (`index.php`, `recipe.php`, …), Vanilla JS/CSS. Firebase compat SDKs in `partials/head.php` for auth, Firestore (newsletter/bookmarks where used). +- **Config:** `config.php` loads `.env`, exposes `get_firebase_config()`, `get_db_connection()`. Never commit `.env` (see `.gitignore`). ## 2. Design & Aesthetics -- **CSS:** Highly customized CSS with modern design tokens (e.g., `var(--ease-out-expo)`, `var(--surface-1)`). -- **Animations:** Employs sophisticated micro-animations, glassmorphism (`backdrop-filter: blur`), and dynamic layouts (e.g., `clip-path` for overlays). -- **Smooth Scrolling:** Uses **Lenis** (`LenisSmoothScroll`). - - *Crucial Rule:* Whenever a fullscreen overlay (like the Cooking Mode) is opened, `lenis.stop()` must be called to prevent background scrolling. When closed, call `lenis.start()`. -- **Preloader:** A custom `CapitoliumPreloader` runs on the homepage. It is cached in `sessionStorage('flixcooks_preloader_seen')` so it only fires once per browsing session. +- **CSS:** Custom properties (`var(--ease-out-expo)`, `var(--surface-1)`), glassmorphism, `FloemaLayoutGrid`. +- **Lenis:** Call `lenis.stop()` when opening fullscreen overlays (e.g. Cooking Mode); `lenis.start()` on close. +- **Preloader:** `CapitoliumPreloader` on homepage; once per session via `sessionStorage('flixcooks_preloader_seen')`. ## 3. Antigravity Agent Configuration -- **Workspace Rules:** Best placed in `.agents/rules/` (e.g., `AGENT.md`). To ensure they are always active, the frontmatter must include: - ```yaml - always_on: true - glob: "*" - ``` -- **Custom Skills:** Can be defined as JSON files in `.agents/skills/`. We successfully created `close_feature.json` to automate the Git workflow of checking out `main`, merging a feature branch, verifying functionality, and deleting the branch. +- **Workspace Rules:** `.agents/rules/` with `always_on: true` and `glob: "*"` in frontmatter. +- **Custom Skills:** `.agents/skills/` (e.g. `close_feature.json` for Git merge workflow). ## 4. GitHub Actions & CI/CD -- **Gemini Code Review Automation:** We integrated `petarzarkov/gemini-code-review-action` to automatically review PRs. - - **Secrets:** Must be passed using `env:` instead of `with:` (e.g., `GEMINI_API_KEY`, `GITHUB_TOKEN`), otherwise the action fails with unexpected input errors. NEVER hardcode API keys in workflow files. - - **Model Naming:** Google's `v1beta` API is very strict. `gemini-1.5-flash` often fails. You must use the fully-qualified name like `gemini-1.5-flash-latest` or `gemini-2.0-flash-lite`. - - **Pinning Versions:** Always pin GitHub Actions to a specific version tag (e.g., `@v1.0.4`) rather than `@latest` to prevent unexpected breaking changes. +- **Gemini PR review:** `petarzarkov/gemini-code-review-action`; secrets via `env:` not `with:`; pin action versions; use full model names (e.g. `gemini-2.0-flash-lite`). -## 5. Completed Milestones -- **Phase 3 (Nutrition):** Implemented. Recipes now store `calories`, `protein`, `carbs`, and `fat`. Admin panel handles inputs, and the UI displays them beautifully. -- **Phase 4 (Interactive Cooking Mode):** Implemented. Recipes now support step-by-step looping background videos and interactive timers (`step_videos`, `step_timers`). The UI utilizes a fullscreen overlay slider with Vanilla JS logic. -- **Phase 6 (Firestore Database Migration):** Migrated recipes database from `data/recipes.json` to Firebase Firestore. - - *Zero-Dependency REST API Read:* Server-side read requests in `helpers.php` use native PHP cURL to query the Firestore REST API `/documents/recipes`. Complex Firestore nested type maps are dynamically parsed into clean standard associative arrays using custom decoders. - - *Dynamic Local Fallback:* In case of rate limits, network failures, or missing `.env` config, all lookup functions automatically fail back to the local `recipes.json` flat-file, guaranteeing 100% database availability and site resilience. - - *Browser-Driven Seeding & Auto-Sync:* Admin seeding and real-time updates are executed client-side in `admin.php` via the authenticated Firebase Client SDK, keeping the local file and Cloud Firestore perfectly in sync without server-side OAuth2 keys. +--- -## 6. Next Steps -According to `TODO.md`, the next major feature block is completing the database seeding (by clicking "Seed Firestore" on `admin.php` in the browser) and verifying all recipe updates reflect in real-time. Afterwards, we can proceed to **Phase 5 (PWA & Offline Support)**. +## 5. PostgreSQL — Schema & Data Flow + +### Table `recipes` (only app table today) +Created by `init_db()` in `helpers.php` if missing: + +| Column | Type | Role | +|-------------|-------------|------| +| `slug` | `VARCHAR(255)` PRIMARY KEY | Stable recipe ID (URLs: `recipe.php?slug=…`) | +| `data` | `JSONB` NOT NULL | **Entire recipe document** (title, i18n, ingredients, steps, nutrition, …) | +| `created_at`| `TIMESTAMP` | Auto on insert | +| `updated_at`| `TIMESTAMP` | Set on `ON CONFLICT` update in `save_recipes()` | + +**Design choice:** Document-in-a-row (JSONB), not normalized columns. PHP already works with JSON arrays; avoids schema migrations for every new recipe field. PostgreSQL can still query inside JSON (`data->'i18n'->'en'->>'title'`). + +### Runtime flow +1. `get_db_connection()` in `config.php` parses `DATABASE_URL` → PDO `pgsql:` DSN. +2. If no URL or connection fails → `load_recipes_local()` reads `data/recipes.json` only. +3. If connected → `init_db()` once per request (static flag): `CREATE TABLE IF NOT EXISTS`, then if `COUNT(*) = 0` → seed all rows from `data/recipes.json`. +4. `load_recipes()` / `load_recipe_by_slug()` decode `data` JSONB to PHP arrays. +5. `save_recipes()` (admin): upsert all recipes in Postgres **and** write `data/recipes.json` as backup. + +### `config.php` functions +- `load_env()` — parses `.env` into `getenv()` / `$_ENV` / `$_SERVER`. +- `get_firebase_config()` — returns Firebase web config array from `FIREBASE_*` env vars. **Required** by `partials/head.php` and `admin.php`. Was accidentally removed in postgres commit `e7f35d7`; restored (undefined function caused HTTP 500). +- `get_db_connection()` — returns `PDO` or `null`; logs failures, does not throw. + +--- + +## 6. Local Development — Docker Postgres + +### Files +- `docker-compose.dev.yml` — Postgres 16 Alpine, container `flixcooks-postgres-dev`, port `5432`. +- `.env.example` — template including local `DATABASE_URL`. +- `scripts/db-check.php` — CLI: connect, `init_db()`, print recipe count + sample slugs/titles. + +### Docker credentials (dev only) +``` +POSTGRES_USER=flixcooks +POSTGRES_PASSWORD=flixcooks_dev +POSTGRES_DB=flixcooks_dev +DATABASE_URL="postgresql://flixcooks:flixcooks_dev@127.0.0.1:5432/flixcooks_dev" +``` + +### Commands +```bash +docker compose -f docker-compose.dev.yml up -d # start +docker compose -f docker-compose.dev.yml ps # health +docker compose -f docker-compose.dev.yml down # stop (data kept) +docker compose -f docker-compose.dev.yml down -v # stop + wipe volume → re-seed on next hit + +docker exec -it flixcooks-postgres-dev psql -U flixcooks -d flixcooks_dev +# psql: \dt , \d recipes , SELECT slug FROM recipes; , \q +``` + +### PHP requirements (WSL/Linux) +- Extension **`php-pgsql`** (or `php8.5-pgsql`) required; without it: log `could not find driver`, fallback to JSON. +- Install interactively: `sudo apt install php8.5-pgsql` (sudo in non-interactive agent shells may timeout). +- Verify: `php -m | grep pgsql` → expect `pdo_pgsql`, `pgsql`. + +### App server +```bash +php -S localhost:8000 +php scripts/db-check.php # after .env + pgsql OK +``` + +### `.env` rules for agents +- Copy from `.env.example`; never commit `.env`. +- **Local:** use `127.0.0.1` Docker URL above. +- **Railway production:** `postgres.railway.internal` only works **inside** Railway network — not from local WSL. For local access to hosted DB use Railway **public** proxy URL from dashboard, or prefer Docker for dev. +- **Firebase:** `FIREBASE_API_KEY`, `FIREBASE_AUTH_DOMAIN`, `FIREBASE_PROJECT_ID`, `FIREBASE_STORAGE_BUCKET`, `FIREBASE_MESSAGING_SENDER_ID`, `FIREBASE_APP_ID` — empty values break client `firebase.initializeApp()` in browser. +- Omit `DATABASE_URL` entirely to force JSON-only mode (UI work without Postgres). + +### Environment separation (important) +- One database per environment (local Docker / staging / production). +- Never point a dev branch `.env` at production Postgres. +- Export/import between envs: `pg_dump` / `psql` when needed; document URLs in platform secrets (Railway variables), not in repo. + +--- + +## 7. Troubleshooting (known issues) + +| Symptom | Cause | Fix | +|--------|--------|-----| +| HTTP 500, `Call to undefined function get_firebase_config()` | Function missing from `config.php` | Ensure `get_firebase_config()` exists in `config.php` | +| Log: `could not find driver` | `php-pgsql` not installed | `sudo apt install php8.5-pgsql` | +| Log: connection failed, host `postgres.railway.internal` | Internal Railway hostname from local machine | Use Docker local URL or Railway public URL | +| Site loads, no recipes from DB | `DATABASE_URL` unset or DB empty and seed file missing | Set URL, ensure `data/recipes.json` exists, hit site or run `db-check.php` | +| Recipes work without Docker | Expected fallback | `load_recipes_local()` uses JSON when `get_db_connection()` is null | + +--- + +## 8. Completed Milestones +- **Phase 3 (Nutrition):** `calories`, `protein`, `carbs`, `fat` on recipes; admin + UI. +- **Phase 4 (Cooking Mode):** `step_videos`, `step_timers`; fullscreen overlay. +- **Phase 6 (Postgres):** `recipes` JSONB table; seed from JSON; admin dual-write. +- **Local dev Postgres:** `docker-compose.dev.yml`, README section, `scripts/db-check.php`, `.env.example` with `DATABASE_URL`. +- **Bugfix (May 2026):** Restored `get_firebase_config()` after postgres migration regression. +- **Local profile data:** Favorites/goals via `assets/fc-local.js` (`localStorage`). Newsletter may use `mailto:` or Firestore depending on page. + +## 9. Next Steps +See `.agents/TODO.md` (e.g. PWA & offline support). README `Local Postgres (Docker)` and `Postgres in diesem Projekt` sections mirror setup for humans. + +## 10. Key file map (data layer) +| File | Purpose | +|------|---------| +| `config.php` | `.env`, Firebase config, PDO | +| `helpers.php` | `init_db`, `load_recipes`, `save_recipes`, site settings | +| `data/recipes.json` | Seed + fallback + admin backup | +| `data/site.json` | Imprint/privacy settings | +| `docker-compose.dev.yml` | Local Postgres | +| `scripts/db-check.php` | Connection + seed smoke test | +| `partials/head.php` | Firebase init via `get_firebase_config()` | diff --git a/.env.example b/.env.example index 810ca3e..b1d9ce7 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,6 @@ -# FlixCooks Firebase Configuration -# Replace these placeholder values with your actual Firebase project settings. +# FlixCooks – lokale Entwicklung (.env wird nicht committed) +# Kopieren: cp .env.example .env -FIREBASE_API_KEY="AIzaSyYourApiKeyHere" -FIREBASE_AUTH_DOMAIN="flixcooks-your-project-id.firebaseapp.com" -FIREBASE_PROJECT_ID="flixcooks-your-project-id" -FIREBASE_STORAGE_BUCKET="flixcooks-your-project-id.appspot.com" -FIREBASE_MESSAGING_SENDER_ID="123456789012" -FIREBASE_APP_ID="1:123456789012:web:abcdef123456" +# --- Postgres (docker-compose.dev.yml) --- +# Nur setzen, wenn du die lokale DB testen willst. Ohne DATABASE_URL → Fallback auf data/recipes.json +DATABASE_URL="postgresql://flixcooks:flixcooks_dev@127.0.0.1:5432/flixcooks_dev" diff --git a/README.md b/README.md index 9c2bb3b..6b39884 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,12 @@ The project is architected to remain extremely lightweight and fast, intentional - `index.php`: The atmospheric landing page showcasing featured recipe selections, introducing the brand, and housing the **Sleek Swipe Discovery Carousel**. - `recipe.php`: The immersive recipe detail page, featuring floating macro-nutrition widgets, interactive ingredients lists, and the fullscreen **Step-by-Step Cooking Mode**. - `admin.php`: A custom, lightweight CMS/admin dashboard allowing full CRUD capabilities over the recipe database, dynamic ingredient line parsing, cooking timers, and video URL associations. - - `login.php` & `register.php`: Fully responsive, glassmorphic auth portals powered by Firebase. + - `login.php`: Local profile page for dietary goals and saved favorites (browser storage). - **Support & Layouts**: - `partials/`: Contains modular templates (`head.php`, `header.php`, `footer.php`) to maintain a clean DRY structure. - `helpers.php`: Core PHP utilities containing data formatting helpers and the data access layer for the flat-file database. - `config.php`: Environment-independent configuration loader which reads runtime secrets from `.env`. - - `api/`: Lightweight, stateless backend endpoints supporting AJAX operations (e.g., newsletter subscriptions, bookmarks, recommendation queries). + - `assets/fc-local.js`: Browser-side storage for favorites and dietary goals. - `data/`: Houses `recipes.json`, our flat-file recipe database. --- @@ -36,13 +36,9 @@ FlixCooks uses a modern, carefully curated vanilla tech-stack focused on lightni ### ⚙️ Backend & Data - **Engine**: Vanilla PHP. -- **Database**: Flat-file JSON database (`data/recipes.json`), allowing lightning-quick load times and simple structural schemas without heavy overhead. +- **Database**: PostgreSQL (production/staging) with automatic seed from `data/recipes.json`. Without `DATABASE_URL`, the app falls back to the JSON file. - **Environment**: Custom `.env` variable parser integrated into PHP bootstrap. -### 🔒 Integrations & Cloud Services -- **Firebase Authentication**: Client and server-side synchronized user sessions for profile management. -- **Firebase Firestore**: Real-time database managing newsletter subscribers and user bookmarks/favorites lists. - --- ## 🚀 Local Development Setup @@ -51,7 +47,8 @@ Follow these simple steps to spin up the local development environment. ### Prerequisites Make sure you have the following installed on your local machine: -- **PHP** (v7.4 or higher recommended) +- **PHP** (8.x recommended) with the **pgsql** extension (`php-pgsql` on Linux/WSL) +- **Docker** (for local Postgres via `docker-compose.dev.yml`) - A modern web browser ### 1. Set Up Environment Variables @@ -59,14 +56,59 @@ Make sure you have the following installed on your local machine: ```bash cp .env.example .env ``` -2. Open `.env` and fill in your actual **Firebase project settings** (API keys, project identifier, authentication domain, etc.): - ```env - FIREBASE_API_KEY="AIzaSyYourApiKeyHere" - FIREBASE_AUTH_DOMAIN="flixcooks-your-project-id.firebaseapp.com" - ... - ``` +2. Open `.env` and optionally set **local Postgres** (see [Local Postgres (Docker)](#local-postgres-docker) below). -### 2. Start the Development Server +### 2. Local Postgres (Docker) + +Für DB-Integration auf einem Dev-Branch – getrennt von Production. + +```bash +# Container starten +docker compose -f docker-compose.dev.yml up -d + +# Warten bis healthy (einmalig prüfen) +docker compose -f docker-compose.dev.yml ps +``` + +In `.env` (Werte passen zu `docker-compose.dev.yml`): + +```env +DATABASE_URL="postgresql://flixcooks:flixcooks_dev@127.0.0.1:5432/flixcooks_dev" +``` + +**PHP-Extension (WSL/Ubuntu, einmalig):** + +```bash +sudo apt install php-pgsql +# oder passend zur Version: sudo apt install php8.5-pgsql +``` + +**Datenbank-Shell (zum Lernen / Inspizieren):** + +```bash +docker exec -it flixcooks-postgres-dev psql -U flixcooks -d flixcooks_dev +``` + +Nützliche SQL-Befehle in `psql`: + +```sql +\dt -- alle Tabellen +\d recipes -- Spalten der Tabelle recipes +SELECT slug, created_at FROM recipes; +SELECT slug, data->>'title' AS title FROM recipes, jsonb_to_record(data) AS x(title text); -- optional +\q -- beenden +``` + +**DB komplett leeren und neu seeden** (lädt wieder aus `data/recipes.json` beim nächsten Seitenaufruf): + +```bash +docker compose -f docker-compose.dev.yml down -v +docker compose -f docker-compose.dev.yml up -d +``` + +Details zum Schema und Ablauf: Abschnitt unten in dieser README und `helpers.php` → `init_db()`. + +### 3. Start the Development Server #### Option A: PHP Built-in Web Server (Recommended & Easiest) You do not need to install complex local servers like Apache or Nginx. Simply run the following command in the root folder of the project: @@ -78,7 +120,7 @@ Then, open your browser and navigate to: http://localhost:8000 ``` -#### Option B: Local Apache Environments (XAMPP / MAMP / WAMP) +#### Option B: Local Apache (XAMPP / MAMP / WAMP) If you prefer running a full local stack: 1. Move or link the project directory inside your local server's document root (e.g., `htdocs` or `www`). 2. Ensure URL rewriting is enabled (the included `.htaccess` file handles caching and custom redirections). @@ -86,6 +128,32 @@ If you prefer running a full local stack: --- +## 🗄️ Postgres in diesem Projekt (Kurzüberblick) + +FlixCooks nutzt **eine Tabelle** – kein klassisches „eine Spalte pro Rezeptfeld“-Schema: + +| Spalte | Typ | Bedeutung | +|-------------|------------|-----------| +| `slug` | `VARCHAR` | Eindeutige ID des Rezepts (URL: `/recipe.php?slug=...`) | +| `data` | `JSONB` | **Gesamtes** Rezept als JSON (Titel, Zutaten, Schritte, i18n, …) | +| `created_at`| `TIMESTAMP`| Erstellzeit | +| `updated_at`| `TIMESTAMP`| Letzte Änderung (wird beim Update gesetzt) | + +**Warum JSONB?** Das Rezept ist in PHP/JSON ohnehin ein Objekt. Statt 20+ SQL-Spalten zu pflegen, speichert ihr ein Dokument pro Zeile. PostgreSQL kann in `JSONB` trotzdem indexieren und abfragen (`data->>'title'`), wenn ihr später filtern wollt. + +**Ablauf beim ersten Aufruf mit leerer DB:** + +1. `config.php` liest `DATABASE_URL` → PDO-Verbindung. +2. `init_db()` in `helpers.php` erstellt `recipes`, falls nicht vorhanden. +3. Ist die Tabelle leer → Import aus `data/recipes.json`. +4. `load_recipes()` liest alle Zeilen, dekodiert `data` zurück zu PHP-Arrays. + +**Admin speichern:** `save_recipes()` schreibt nach Postgres **und** aktualisiert `data/recipes.json` als Backup. + +Für **Staging/Production** setzt du `DATABASE_URL` in der jeweiligen Hosting-Umgebung – nie Production-Daten in der lokalen Dev-DB mischen. + +--- + ## 📈 Development Tracking & Progress All current development tasks, features, and roadmaps are actively tracked and updated in the project’s [.agents/TODO.md](file:///.agents/TODO.md) file. Architectural learnings, conventions, and configuration updates are maintained in the central knowledge base: [.agents/brain.md](file:///.agents/brain.md). diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..4f1a2de --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,26 @@ +# Lokale Postgres-Instanz für Entwicklung (dev branch). +# Start: docker compose -f docker-compose.dev.yml up -d +# Stop: docker compose -f docker-compose.dev.yml down +# Reset: docker compose -f docker-compose.dev.yml down -v (löscht alle Daten!) + +services: + postgres: + image: postgres:16-alpine + container_name: flixcooks-postgres-dev + restart: unless-stopped + ports: + - "5432:5432" + environment: + POSTGRES_USER: flixcooks + POSTGRES_PASSWORD: flixcooks_dev + POSTGRES_DB: flixcooks_dev + volumes: + - flixcooks_pg_dev:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U flixcooks -d flixcooks_dev"] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + flixcooks_pg_dev: diff --git a/scripts/db-check.php b/scripts/db-check.php new file mode 100644 index 0000000..54f3e57 --- /dev/null +++ b/scripts/db-check.php @@ -0,0 +1,39 @@ +query('SELECT COUNT(*) FROM recipes')->fetchColumn(); +echo "Verbindung OK. Rezepte in DB: {$count}\n"; + +if ($count > 0) { + $stmt = $pdo->query("SELECT slug, data->'i18n'->'en'->>'title' AS title FROM recipes LIMIT 5"); + echo "\nBeispiel-Zeilen:\n"; + while ($row = $stmt->fetch()) { + echo " - {$row['slug']}: {$row['title']}\n"; + } +}