Fix nutrition upload crash, persist runtime writes, serve API on its own domain
/api/upload/nutrition called json.dumps() in a module that never imported json, so every request to it raised NameError, was swallowed by the broad except, and came back as "500 Database import failed". Import json. Persist the three directories the app writes to at runtime. Products added through the UI are appended to data/seed_catalogs/*.json and retrained models are written to app/intelligence/artifacts/*.joblib; both live inside the image, so a redeploy silently discarded them. The paths now come from settings (DATA_DIR / SEED_CATALOG_DIR / MODEL_ARTIFACTS_DIR) so a volume can be mounted on them, and catalog_engine.save_catalog resolves against DATA_DIR instead of a working-directory-relative "data/", which landed somewhere different depending on where the process was started from. Mounting those volumes would otherwise have made things worse: Docker seeds a named volume from the image on first use, but a bind mount starts empty and just hides what the image shipped. A bind mount on /app/data would have left the API with no seed catalogs, so the next product added would write a JSON file containing only that product. The image now keeps pristine copies at /app/.bundled, and restore_bundled_assets() tops up whatever a freshly mounted directory is missing at startup without overwriting anything already there. Configure CORS for the split-domain deployment: the React app is served from catalogue.nearle.ai.in and calls the API on mcp.catalogue.nearle.ai.in, so the frontend origin has to be in API_CORS_ORIGINS. A wrong list fails only in the browser while the server logs a healthy 200, so the effective origins are now logged at startup with a warning when they are localhost-only. Fix FRONTEND_DIST, which looked for a sibling "frontend/" directory that is actually named "catalogue_frontend/", so the single-port unified-serving branch could never activate even with a build sitting next to it. Rebuild the Dockerfile on the frontend's multi-stage pattern: dependencies resolve into a venv in a build stage, the runtime stage copies only that. Adds PYTHONUNBUFFERED so startup errors reach Dokploy's log pane, a liveness HEALTHCHECK (/api/health answers 200 even when Postgres is down, so a database blip cannot restart-loop the container), and an overridable PORT. The CMD execs uvicorn so SIGTERM reaches it rather than the sh wrapper. Add "from __future__ import annotations" to ollama_service and image_search, which used PEP 604 unions in runtime-evaluated signatures and so could not be imported below Python 3.10. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
37
README.md
37
README.md
@@ -69,6 +69,43 @@ permission check; `user` holds the product/store/inventory permissions.
|
||||
`AUTH_ENABLED=false` disables all of it for local work — never in a deployment.
|
||||
See the Authentication section of `../DEPLOYMENT.md` for the full endpoint map.
|
||||
|
||||
## Persistence: the two volumes a deployment needs
|
||||
|
||||
Most state lives in Postgres, but three things are written to the filesystem,
|
||||
and in a container those live inside the image - so a redeploy rebuilds the
|
||||
image and silently discards them:
|
||||
|
||||
| Path | Written by |
|
||||
|---|---|
|
||||
| `/app/data/seed_catalogs` | `POST /api/user/products/add`, `/upload-file` - every product added through the UI is appended to the brand's JSON |
|
||||
| `/app/app/intelligence/artifacts` | the training endpoints - every retrained `*.joblib` model |
|
||||
| `/app/data` | catalogs saved by the ingestion pipeline |
|
||||
|
||||
Mount a volume on each (the first is inside the third, so two mounts cover all
|
||||
three):
|
||||
|
||||
```
|
||||
/app/data
|
||||
/app/app/intelligence/artifacts
|
||||
```
|
||||
|
||||
In Dokploy, add both under the service's **Volumes**. Named volume or bind
|
||||
mount, either is fine - `docker compose --profile full up -d` shows the same
|
||||
two mounts as named volumes.
|
||||
|
||||
Bind mounts normally break this pattern, because they start empty and hide the
|
||||
seed catalogs and pre-trained models the image ships with. They are safe here:
|
||||
the image keeps read-only copies at `/app/.bundled`, and on startup
|
||||
`app/infrastructure/persistence.py` copies in whatever the mounted directory is
|
||||
missing. It never overwrites an existing file, so a user-added product always
|
||||
survives the next redeploy rather than being reverted to the bundled catalog.
|
||||
|
||||
If you leave the volumes off, the API still runs and logs a warning naming the
|
||||
directories that will be lost.
|
||||
|
||||
Override the locations with `DATA_DIR`, `SEED_CATALOG_DIR` and
|
||||
`MODEL_ARTIFACTS_DIR` if the writable data belongs somewhere else.
|
||||
|
||||
## Pulling the local LLM (one-time)
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user