fix: codebase audit fixes — indexes, daily_rate, anomaly key, CSRF, chat pruning, shopping_name

## v1.7.6

- DB: fix shopping_name Pi→Piadina, Grana→Formaggio, Prosciutto cotto→Affettato, Panna acida→Panna
- DB: composite indexes idx_transactions_type_date + idx_transactions_pid_type_undone (+ migration)
- PHP: daily_rate uses first_in→last_activity window (not first_in→now)
- PHP: anomaly dismiss key uses product_id+direction (stable, not product_id+round(expected))
- PHP: smart shopping — products exhausted within 14 days bypass token/family suppression
- PHP: chat pruning — DELETE messages beyond 200 after each chatSave()
- PHP: getStats() — 5 queries → 1 consolidated query with subselects
- PHP: bringCleanupObsolete — 300ms delay between bulk removals
- PHP: CSRF guard — POST write actions require X-EverShelf-Request:1 or Content-Type:application/json
- JS: api() — sends X-EverShelf-Request:1 on all POST requests
- JS: _opLog — prunes entries older than 30 days in addition to 200-entry cap
This commit is contained in:
dadaloop82
2026-05-10 11:26:10 +00:00
parent f65fb4365c
commit ed447d5811
6 changed files with 106 additions and 20 deletions
+16
View File
@@ -5,6 +5,22 @@ All notable changes to EverShelf will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.7.6] - 2026-05-10
### Fixed
- **`shopping_name` troncato (Piadina)** — Il prodotto "Piadine medie" aveva `shopping_name='Pi'` (troncato), non veniva aggruppato correttamente nella famiglia. Corretto in `Piadina`.
- **Family merges DB** — Grana Padano ora sotto `Formaggio` (era `Grana` singleton), Prosciutto cotto ora sotto `Affettato`, Panna acida ora sotto `Panna`.
- **`daily_rate` su periodo effettivo** — Il tasso di consumo giornaliero usava `first_in → now` come finestra, diluendo il rate con periodi in cui il prodotto era già esaurito (es. aglio esaurito a 34gg veniva calcolato su 60+). Ora usa `first_in → last_activity` (ultimo acquisto o ultimo uso), più preciso per le previsioni di riordino.
- **Anomaly dismiss key stabile** — La chiave di dismiss usava `product_id + round(expected)` che cambiava ad ogni nuova transazione, causando la ricomparsa delle anomalie già chiuse. Ora usa `product_id + direction` (phantom/missing/untracked) — stabile finché la direzione non cambia.
- **Smart shopping: prodotti esauriti < 14 giorni** — Prodotti terminati negli ultimi 14 giorni non vengono più soppressi dal check token-coverage o shopping_name-family: se li hai appena finiti, è probabile tu voglia ricomprarli indipendentemente dalla presenza di equivalenti in stock.
- **Chat pruning** — `chatSave()` ora esegue `DELETE` dei messaggi oltre i 200 più recenti dopo ogni salvataggio, evitando crescita illimitata della tabella `chat_messages`.
- **`getStats()` query consolidate** — Le 5 query separate (COUNT products, SUM inventory, COUNT locations, COUNT recent_in, COUNT recent_out) sono ora una sola query con subselect, riducendo i round-trip SQLite da 5 a 1.
- **Bring! cleanup rate-limiting** — Aggiunto `usleep(300ms)` tra le rimozioni multiple per evitare di sovraccaricare l'API Bring! in burst.
- **Indici compositi su `transactions`** — Aggiunti `idx_transactions_type_date(type, created_at)` (per `getStats`) e `idx_transactions_pid_type_undone(product_id, type, undone)` (per `smartShopping`), con migration automatica per DB esistenti.
### Security
- **CSRF protection** — Le action di scrittura (inventory_add, bring_add, product_save, ecc.) richiedono ora `X-EverShelf-Request: 1` oppure `Content-Type: application/json`. Il frontend `api()` invia sempre il header su POST. Questo previene attacchi CSRF cross-site tramite form HTML.
## [1.7.5] - 2026-05-10
### Added