# FlixCooks Project Brain This document summarizes architectural knowledge, conventions, and learnings for humans and AI agents working on this repo. ## 1. Project Architecture & Stack - **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:** 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:** `.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 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. 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()` |