Commit graph

14 commits

Author SHA1 Message Date
ba075276b1 Audit pass: cookie auth migration, route guards, GP% clamp, CSV export, OCR transaction safety, N+1 fix, and cleanup
- Migrated all 435 frontend fetch calls from Authorization Bearer header to credentials: 'include' (cookie auth)
- Removed ?token= from all file/image URLs (browser history exposure)
- Added ProtectedRoute wrapper to all capability-gated routes in App.tsx
- OCR background task: added transaction boundary, improved error handling and status rollback
- DuplicateDetector: wrapped in non-fatal try/except so crashes don't abort invoice processing
- File upload: commit DB row before writing to disk to prevent orphaned files
- GP% clamped to 100% in GPReport (credit notes can inflate above 100%)
- Added CSV export to GPReport (suppliers, daily data, allowances breakdown)
- Backend file-serving endpoints: cookie auth with ?token= fallback for backward compatibility
- DATA_DIR: moved from hardcoded /app/data to os.getenv in invoices.py and recipes.py
- N+1 fix in list_recipes: batch-loads latest cost snapshot in 1 query (was N)
- Zero-yield sub-recipe: logs warning instead of silently zeroing cost contribution
- Budget spend rate input: rejects negative values
- GPReport allowances toggle: persisted to localStorage across page loads
- DB pool_size/max_overflow: configurable via DB_POOL_SIZE/DB_MAX_OVERFLOW env vars
- Fixed SyntaxWarning from \\d in invoices.py docstring

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 10:04:20 +00:00
6f6e16c88f Replace in-app Access Control with JWT cap enforcement
The in-app page-restriction system (admin-only toggles in Settings) was
nav-hiding only and duplicated functionality already covered by JWT caps
in the main stack auth service. All 9 pages in the restriction list were
already gated in Layout.tsx by existing caps.

Backend: add router-level requireCap() to enforce caps at the API layer:
- reports.py: Depends(require_cap("view"))
- logbook.py: Depends(require_cap("logbook"))
- search.py: Depends(require_cap("invoices"))

Frontend: remove the Access Control settings section entirely:
- Drop pageRestrictions query, restrictedPages/accessSaveMessage state,
  savePageRestrictionsMutation, isSectionAccessible helper
- Remove 'access' from SettingsSection type and sidebarItems
- Strip restrictPath from all sidebar items (no longer needed)

Access management is now fully centralised in the main stack auth service.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 23:17:22 +00:00
1564a66283 Wire Azure OCR to central settings toggle; remove redundant Users section
- settings/src/integrations/schema.js: rename azure from 'Azure AD' to
  'Azure Document Intelligence', swap fields to endpoint + api_key
- Add use_global_azure column (migration + model)
- global_settings_service: add azure to check_global_status and apply_global_overrides
- api/settings.py: expose use_global_azure in response/update; apply overrides
  in test_azure_connection before credential check
- Settings.tsx: add 'Use credentials from main stack settings' toggle for
  Azure OCR section (endpoint/key disabled when on, test button enabled when
  global is configured); remove Users section (managed centrally via auth
  service), clean up UserData interface, users query and mutations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 22:58:32 +00:00
d7898ba897 Add 'use main stack settings' toggle for third-party integration credentials
Each of Newbook, Resos, SambaPOS, SMTP, and Nextcloud now has a checkbox at the
top of its credentials block. When enabled, the app reads auth credentials from
the central stack settings service (SETTINGS_URL + STACK_INTERNAL_SECRET) and
the local auth fields are grayed out. App-specific fields (base path, GL codes,
keywords, sync intervals, etc.) remain editable regardless.

Backend: new use_global_* columns on kitchen_settings, migration, global_settings_service
with apply_global_overrides() for in-memory credential injection, GET /api/settings/global-status
endpoint, and apply_global_overrides() called in test-connection endpoints and FileArchivalService.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 21:57:10 +00:00
742bc91b3b Fix PDF viewing: cookie auth fallback + .mjs MIME type
- /file and /pdf endpoints: make token optional, fall back to hnf_session
  cookie auth when no valid JWT token provided. Kitchen frontend passes the
  literal '__session__' compat shim so token-only auth always 401'd.
- Add Request injection so cookie-based fallback path can read hnf_session.
- nginx: add types block mapping .mjs → text/javascript so pdf.worker.min.mjs
  passes browser strict MIME check for ES module scripts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 21:34:51 +00:00
cd9e7dbfef Fix invoice file 500 and pdf.worker MIME error
- Remove stale import of non-existent require_cap_from_token from
  get_invoice_file endpoint — caused ImportError → 500 on every PDF load
- Fix pdf.js worker path from '/pdf.worker.min.mjs' to '/kitchen/pdf.worker.min.mjs'
  in Review.tsx and SearchDefinitions.tsx — worker was being fetched from domain
  root instead of under the /kitchen/ base, causing MIME type rejection

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 21:28:55 +00:00
9cd1683572 Fix invoicestatus enum case mismatch in invoice list filter
Frontend sends lowercase status (e.g. 'confirmed'), but the DB enum values
are uppercase (CONFIRMED). Uppercase the parameter before comparison.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 20:05:27 +00:00
bc874ac9ff Fix migration transaction poisoning: ADD COLUMN IF NOT EXISTS
Migrations used try/except around ADD COLUMN inside a single engine.begin()
block. When a 'column already exists' error was caught, asyncpg left the
transaction in aborted state, causing all subsequent DDL in the block to fail
with InFailedSQLTransactionError. Replace with IF NOT EXISTS to prevent the
error entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 19:53:26 +00:00
e7e1fda9f6 Fix 413 upload limit + add /api/kds/settings to kitchen backend
- nginx client_max_body_size raised to 800m (was 20m) to allow 577MB backup
  upload; proxy_read_timeout raised to 600s for long restore operations
- Add api/kds_settings.py: GET+PATCH /api/kds/settings served by kitchen
  backend (kitchen Settings page configures KDS timers/GraphQL/course order;
  all config lives in kitchen_settings so kitchen owns these endpoints)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 17:47:28 +00:00
14903e80a6 Seed kitchens id=1 on startup to satisfy kitchen_settings FK
kitchen_settings has a FK to kitchens.id — on a fresh kitchen_db the
kitchens table is empty, so any restore attempt fails with a FK violation.
Idempotent INSERT ON CONFLICT DO NOTHING runs after create_all on every boot.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 16:38:47 +00:00
ecd63fc65f Add get_current_user_from_token to auth.py; fix missing import in 3 api files
?token= query-param endpoints (PO/invoice print previews, recipe image exports,
backup download) call get_current_user_from_token which was not ported from the
original archive auth module. Added the function and fixed the missing import in
backup.py, ingredients.py, and invoices.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 12:34:01 +00:00
907243c2cd Restore api/migrations/models/services source files accidentally removed in gitignore commit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 12:16:28 +00:00
c8fad1cf36 Add .gitignore, remove __pycache__ from tracking
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 12:16:04 +00:00
8d688b459d Initial kitchen scaffold — Phase 1 kitchen port (build-verified 2026-07-11)
FastAPI backend (Python 3.11, MSSQL ODBC for SambaPOS, Azure DI OCR),
kitchen_db on central PG. React/TS/Vite frontend with navy sidebar layout.

Backend: auth.py (APP_SLUG=kitchen, SimpleNamespace — archive routes use
.kitchen_id/.is_admin without modification), main.py (51 migrations, scheduler,
internal router for KDS bookings feed), api/internal.py, full archive API
(31 routers: invoices, recipes, menus, sambapos, resos, newbook, disputes,
purchase_orders, etc.), models, migrations, OCR pipeline.
kitchen_id pinned to 1 (B1 — single hotel).

Frontend: AuthGate (app=kitchen, token shim for archive compat — B5b pending),
Layout (navy sidebar, 6 sections, Lucide icons, teal --app-primary),
App.tsx (Outlet pattern, UploadApp outside Layout), index.css (full :root block).
strict: false — archive components have type issues; build clean.

Note: 45 archive components call fetch('/api/...') without /kitchen/ prefix
(B5b). Runtime 404s; deferred until after initial testing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 12:15:39 +00:00