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()` |
+5 -8
View File
@@ -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"
+84 -16
View File
@@ -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 projects [.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).
+26
View File
@@ -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:
+39
View File
@@ -0,0 +1,39 @@
<?php
/**
* Kurzer Verbindungstest zur lokalen Postgres-Instanz.
* Aufruf (im Projektroot):
* php scripts/db-check.php
*/
require_once dirname(__DIR__) . '/config.php';
require_once dirname(__DIR__) . '/helpers.php';
if (!extension_loaded('pdo_pgsql')) {
fwrite(STDERR, "PHP-Extension pdo_pgsql fehlt. z. B.: sudo apt install php-pgsql\n");
exit(1);
}
$url = getenv('DATABASE_URL');
if (!$url) {
fwrite(STDERR, "DATABASE_URL ist nicht gesetzt. In .env eintragen (siehe .env.example).\n");
exit(1);
}
$pdo = get_db_connection();
if (!$pdo) {
fwrite(STDERR, "Keine DB-Verbindung zu: {$url}\n");
fwrite(STDERR, "Prüfe: Docker läuft? docker compose -f docker-compose.dev.yml ps\n");
exit(1);
}
init_db();
$count = (int) $pdo->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";
}
}