# AGENTS.md ## Purpose This file is a quick handover for future agents working on `D:\www\Nutrio`. It describes what the project is, what is already implemented, and what still needs work. ## Project Summary - Project name: `Nutrio` - Goal: meal planning + nutrition tracking + daily diary totals. - Backend: PHP (`tpsoft/apilite`, `tpsoft/dbmodel`) - Frontend: Vue 3 + Vite + TypeScript - Monorepo layout: - `backend/` - `frontend/` ## Current State (as of 2026-02-13) - `README.md` already contains product specification in Slovak and English. - Backend DB migrations exist in `backend/src/Maintenance.php` up to version `7`. - Backend API methods are implemented in `backend/src/API.php`. - Frontend auth page is now implemented: - `frontend/src/App.vue` renders router view. - `frontend/src/router/index.ts` maps `/` to `frontend/src/views/AuthView.vue`. - `AuthView` serves as login + registration entry (single form, email + password). - Successful login stores `token` and `user_email` in `localStorage`. - Frontend i18n is wired: - setup in `frontend/src/i18n/index.ts` - locale files in `frontend/src/locales/{sk,cs,en,es,de}.ts` - language switcher updates locale dynamically. - Frontend theme system is implemented: - light/dark mode toggle in auth page - design tokens in `frontend/src/assets/css/style.css` (`:root` variables). - App logo is served from `frontend/public/Nutrio.png` (copied from `doc/Nutrio.png`). - Font Awesome is installed and registered globally in `frontend/src/main.ts`. - `frontend/src/BackendAPI.js` is generated via `backend/scripts/buildTypeScript.php` and should not be edited manually. - `frontend/src/BackendAPI.d.ts` provides TS declarations for generated `BackendAPI.js`. - `backend/data.json` contains sample meal data (not currently wired into DB/API flow). ## Backend Architecture - Entry point: `backend/public/API.php` - Bootstrap and DB init: `backend/src/Init.php` - Configuration: `backend/config/Configuration.php` - Supports `mysql` and `sqlite` - Loads override config files from `backend/config/*.php` (except the base file itself) - Migration logic: `backend/src/Maintenance.php` - API implementation: `backend/src/API.php` - DB table models: `backend/src/Models/*.php` ## Database Schema Snapshot Migration creates these tables: - `options` (`key`, `value`) for internal settings, including DB version. - `users` (`user_id`, `email`, `password_hash`, `token`, `token_expires`, `created_at`). - `ingredients`: - `ingredient_id`, `user_id`, `name` - per-100g values: `protein_g_100`, `carbs_g_100`, `sugar_g_100`, `fat_g_100`, `fiber_g_100`, `kcal_100` - `meals`: - `meal_id`, `user_id`, `name`, `meal_type` (`breakfast|lunch|dinner`) - `meal_items`: - `meal_item_id`, `meal_id`, `ingredient_id`, `grams`, `position` - `diary_days`: - `diary_day_id`, `user_id`, `day_date` - unique: `(user_id, day_date)` - `diary_entries`: - `diary_entry_id`, `diary_day_id`, `meal_type`, `meal_id` - unique: `(diary_day_id, meal_type)` ## API Surface (Implemented) All actions are invoked through `backend/public/API.php` with `?action=`. - Utility: - `health` - Auth / Users: - `userRegistration(email, password)` - `userLogin(email, password)` - `userLogout(token)` - `userDelete(email, password)` - Ingredients: - `ingredientList(token, query = "", include_global = true)` - `ingredientGet(token, ingredient_id)` - `ingredientCreate(token, name, protein_g_100, carbs_g_100, sugar_g_100, fat_g_100, fiber_g_100 = 0, kcal_100 = 0)` - `ingredientUpdate(token, ingredient_id, name, protein_g_100, carbs_g_100, sugar_g_100, fat_g_100, fiber_g_100 = 0, kcal_100 = 0)` - `ingredientDelete(token, ingredient_id)` - Meals: - `mealList(token, meal_type = "", with_items = false, with_totals = false)` - `mealGet(token, meal_id, with_items = true, with_totals = true)` - `mealCreate(token, name, meal_type)` - `mealUpdate(token, meal_id, name, meal_type)` - `mealDelete(token, meal_id)` - Meal items: - `mealItemList(token, meal_id, with_calculated = true)` - `mealItemAdd(token, meal_id, ingredient_id, grams, position = 1)` - `mealItemUpdate(token, meal_item_id, ingredient_id, grams, position)` - `mealItemDelete(token, meal_item_id)` - `mealItemReorder(token, meal_id, ordered_item_ids)` - Calculations: - `mealTotals(token, meal_id)` - Diary: - `diaryDayGet(token, day_date, with_totals = true)` - `diaryDaySetMeal(token, day_date, meal_type, meal_id)` - `diaryDayUnsetMeal(token, day_date, meal_type)` - `diaryRange(token, date_from, date_to)` ## Behavior and Validation Rules - `meal_type` must be one of: `breakfast`, `lunch`, `dinner`. - Date params must use format `YYYY-MM-DD`. - `grams` must be `> 0`. - Nutrition input values are validated as non-negative. - If `kcal_100` is `0`, API computes kcal by formula: - `protein*4 + carbs*4 + fat*9` - User-bound actions now require `token` and resolve `user_id` from it at method start. - Token validation path: - `Users::getUserIDbyToken(token)` -> `Users::verifyToken(user_id, token)` - valid token refreshes `token_expires` - expired token clears `token` and `token_expires` to `NULL` - Ownership checks are enforced by resolved `user_id`: - meals and meal items must belong to the user - ingredients can be user-owned or global (`user_id = null`) for read/select - Registration/login generate and store user token in DB. - `userLogout(token)` invalidates session by setting `token` and `token_expires` to `NULL`. - `userLogin(email, password)` now auto-registers user when: - email is valid - user does not exist - then proceeds with normal login/token generation flow - `userLogin` response now includes `auto_registered` flag (`true|false`). ## Known Pitfalls and Notes - The historical FK issue in `meal_items` should reference `meals(meal_id)`. Current file already uses the correct FK. - If someone ran migrations before FK fix, old MySQL state may still be broken. In that case reset affected table(s) or rebuild DB from clean state. - Some comments in `Maintenance.php` show encoding artifacts, but SQL structure is valid. - Basic token auth is implemented, but token is still passed as plain API parameter. - For `array` parameters (for example `ordered_item_ids`), APIlite expects JSON in request payload. - APIlite wraps responses with a nested `data` object. Keep this in mind on frontend parsing. - `frontend/src/BackendAPI.js` is generated output; regenerate when backend API changes, do not patch manually. - In vue-i18n locale strings, `@` must be escaped as `{'@'}` to avoid "Invalid linked format" errors. ## Local Runbook Backend: - `cd backend` - `composer install` - configure DB via `backend/config/*.php` override - serve `backend/public` through web server/PHP runtime - first API hit triggers `Maintenance->database()` migration flow - when backend API signature changes, regenerate frontend API client: - `php scripts/buildTypeScript.php` Frontend: - `cd frontend` - `npm install` - `npm run dev` - optional checks: `npm run type-check`, `npm run build`, `npm run lint` ## Product Behavior Target (what to build next) - Harden auth (token transport/header strategy, token revoke strategy, brute-force/rate-limits). - Build remaining frontend screens for ingredients, meals, meal item editor, diary day, diary range. - Connect frontend to implemented backend actions. - Add API tests for validation, ownership checks, and totals calculation consistency. - Add pagination/filter strategy where list endpoints grow. ## Practical Conventions - Keep IDs as `BIGINT UNSIGNED` and use `*_id` naming consistently. - Keep MySQL + SQLite compatibility in SQL where possible (project supports both). - When changing schema, always bump DB version in `Maintenance.php` with forward-only migration steps. - Keep API action names stable unless frontend is updated at the same time.