197 lines
9.2 KiB
Markdown
197 lines
9.2 KiB
Markdown
# 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-14)
|
|
|
|
- `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 implemented:
|
|
- `frontend/src/App.vue` renders router view.
|
|
- `frontend/src/router/index.ts` maps `/` to `frontend/src/views/AuthView.vue` for guests.
|
|
- `AuthView` serves as login + registration entry (single form, email + password).
|
|
- Login now uses `frontend/src/stores/auth.ts` (`Pinia`) and redirects to authenticated app routes.
|
|
- `frontend/src/views/AuthView.vue` formatting now uses tab-based indentation.
|
|
- Frontend authenticated area is implemented:
|
|
- `frontend/src/views/AppLayout.vue` is shell layout.
|
|
- Desktop navigation: `frontend/src/components/navigation/AppSidebar.vue`.
|
|
- Mobile navigation: `frontend/src/components/navigation/AppBottomTabs.vue`.
|
|
- Top bar/title: `frontend/src/components/navigation/AppTopbar.vue`.
|
|
- Pages: `TodayView`, `MealsView`, `MealDetailView`, `IngredientsView`, `StatsView`, `SettingsView`.
|
|
- Route guard is active for `/app/*` (requires token), guest-only for `/`.
|
|
- Frontend i18n is wired and used across new UI:
|
|
- setup in `frontend/src/i18n/index.ts`
|
|
- locale files in `frontend/src/locales/{sk,cs,en,es,de}.json`
|
|
- language switcher updates locale dynamically.
|
|
- new app UI keys are added (`nav`, `pageTitles`, `mealTypes`, `common`, `nutrition`, `today`, `meals`, `ingredients`, `stats`, `settings`).
|
|
- 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`.
|
|
- Pinia is installed and configured in `frontend/src/main.ts` via `frontend/src/stores/index.ts`.
|
|
- Frontend domain/store structure exists:
|
|
- `frontend/src/types/domain.ts`
|
|
- `frontend/src/stores/{auth,ingredients,meals,diary}.ts`
|
|
- `frontend/src/utils/{nutrition,api,date}.ts`
|
|
- `frontend/src/BackendAPI.ts` is generated via `backend/scripts/buildTypeScript.php` and should not be edited manually.
|
|
- `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=<method_name>`.
|
|
|
|
- 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 response handling detail:
|
|
- raw API response is wrapped as `{ status, data }`
|
|
- generated `BackendAPI.ts` currently resolves `response.data` in `callPromise` for non-`__HELP__` actions
|
|
- frontend stores use `frontend/src/utils/api.ts` (`unwrapApiData`) to normalize both envelope and unwrapped runtime shapes
|
|
- `frontend/src/BackendAPI.ts` 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).
|
|
- Polish authenticated frontend UX (validation messages, delete confirmations, optimistic updates, better loading/error states).
|
|
- Add diary range screen/workflow on frontend (backend endpoint already exists).
|
|
- Add i18n coverage for any future UI additions and keep keys stable.
|
|
- 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.
|
|
- In source files, use tab characters for indentation (do not add space-based indentation).
|