跳到正文
GitHub·· 7 天前AI 评分12

Nutrilens 开源饮食记录应用:AI 识别服务与主服务隔离部署

Wolfi-OwO/nutrilens

AI 导读

Nutrilens 将应用拆分为 apps/api(Node.js/TypeScript、Express、PostgreSQL,负责用户、认证、饮食计划与餐食记录)和 apps/ai-server(Python/FastAPI、ONNX Runtime,仅做食物照片推理、不存任何用户数据)两个可独立部署的服务。

正文

Why it is built this way

Nutrilens is deliberately split into two independently deployable services (see ADR-0001):

  • apps/api — the primary application server (Node.js/TypeScript, Express, PostgreSQL). Owns users, authentication, diet plans, meal logs, and all persistent data.
  • apps/ai-server — a standalone AI-detection service (Python/FastAPI, ONNX Runtime). Owns nothing but inference: it receives a food photo, returns a structured prediction, and holds no user data of its own — no database, no persisted images, no logs containing image content (see apps/ai-server/API.md).

The API server is the only caller of the AI server, over an internal network segment the AI server cannot be reached from publicly. A vulnerability or a compromised dependency in the inference stack (a much larger, faster-moving set of ML packages) is contained to a service that has no database credentials, no user PII, and no session state. This mirrors the separation used across this account's projects for stack isolation (see cli-image-upscaler for the same principle applied to a single binary's AI extras).

A food photo goes to apps/ai-server — a real food-recognition model (see ADR-0002) behind a timeout/retry/circuit-breaker policy (see ADR-0003), so an AI-server outage degrades to manual meal logging rather than blocking the user.

apps/api (serving the built apps/frontend from the same container) and apps/ai-server run behind Caddy on a Contabo VPS, via docker-compose.prod.yml at /opt/nutrilens. That replaced the original Azure PaaS hosting in the 2026-09 migration; only the image push to the shared Azure Container Registry (globalcr01) survives from that pipeline (see .github/workflows/release.yml).

ui-prototype/ is retired — a static, hardcoded-data walkthrough kept only as a historical reference for the original design pass; every page it mocked is now a real, backend-wired page under apps/frontend. Still ahead: observability (M7), a hardening pass (M8), and an admin dashboard (M9) — none of it required for the app to work, all of it scoped as post-v0.0.1 follow-up. See the issue tracker and milestones for sequencing, and organizational/ for use cases, activity diagrams, requirements, and ADRs.

Tech stack

Component Stack
Frontend React, Vite, TypeScript, Tailwind CSS, React Router, TanStack Query
API server Node.js, TypeScript, Express, PostgreSQL, zod
AI server Python, FastAPI, ONNX Runtime (food-recognition model)
Infra Docker per service, Contabo VPS (Caddy + docker-compose.prod.yml), shared ACR for image pushes

apps/api's HTTP API is RESTful at Richardson Maturity Model level 3 — correct verbs/status codes, _links (HATEOAS) on every JSON representation, and a GET /api hypermedia entry point. See the full API reference.

Getting started

cp .env.example .env
docker compose up

Brings up Postgres, apps/api (migrated and listening on :8080), and apps/ai-server (internal-only, no published port — reachable from apps/api as http://ai-server:8000) together. Each service also has its own docker-compose.yml for developing it in isolation (apps/api/docker-compose.yml, apps/ai-server/docker-compose.yml).

apps/frontend isn't containerized yet — run it separately:

cp apps/frontend/.env.example apps/frontend/.env
npm run dev --workspace=@nutrilens/frontend

Project structure

organizational/   Use cases, activity diagrams, requirements, ADRs, deploy docs
ui-prototype/     Retired — historical, hardcoded-data prototype, no backend calls
apps/frontend/    Production frontend (React/Vite/TypeScript)
apps/api/         Main application server (Node.js/TypeScript)
apps/ai-server/   Isolated AI-detection service (Python/FastAPI)
e2e/              End-to-end + accessibility tests (Playwright), run against
                  the real docker-compose stack — not one app on its own

Contributing

See CONTRIBUTING.md. main is protected — all changes land through a pull request from a branch created off an issue, gated on CI (lint, typecheck, tests, coverage thresholds), an end-to-end + accessibility suite against the real running stack, CodeQL, Trivy, and secret scanning all passing.

Security

See SECURITY.md for how to report a vulnerability.

License

MIT

来源:GitHub · github.com