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.

This commit is contained in:
2026-05-23 10:13:00 +02:00
parent 3218aed0a9
commit 547778bba5
5 changed files with 280 additions and 53 deletions
+126 -29
View File
@@ -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()` |