Skip to content

GitLab

  • Projects
  • Groups
  • Snippets
  • Help
    • Loading...
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in
B
broccoli-detector-D
  • Project overview
    • Project overview
    • Details
    • Activity
    • Releases
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 0
    • Issues 0
    • List
    • Boards
    • Labels
    • Service Desk
    • Milestones
  • Merge Requests 0
    • Merge Requests 0
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Operations
    • Operations
    • Incidents
    • Environments
  • Packages & Registries
    • Packages & Registries
    • Package Registry
  • Analytics
    • Analytics
    • CI/CD
    • Repository
    • Value Stream
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Members
    • Members
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • Manol Draganov
  • broccoli-detector-D
  • Merge Requests
  • !1

Merged
Created Jun 02, 2026 by Manol Draganov@manol.draganovMaintainer

Production-harden BroccoliDetect: security, reliability, frontend resilience, tests & docs

  • Overview 28
  • Commits 69
  • Changes 48

Summary

Takes the BroccoliDetect proof-of-concept from a working prototype to a production-ready app. It hardens the internet-facing backend, makes the frontend resilient and accessible, adds a frontend test suite, tightens the Docker/Render setup, and rewrites the documentation. Local-dev behaviour is unchanged by default (auth stays off unless API_KEY is set).

This is a wide change (43 commits, ~44 files) because the PoC needed work in almost every layer. The history is atomic and each commit message stands on its own, so it reads best commit-by-commit. main hasn't moved since this branch was cut, so it merges as a clean fast-forward (no conflicts).

Why

The PoC ran, but it wasn't safe to expose or easy to hand over: no auth or rate limiting, model inference blocking the event loop, print-based logging, no tests, scattered configuration, and a thin README. This MR closes those gaps.

What changed

Security (backend + nginx)

  • Optional API-key auth on /api/detect — constant-time compare, enforced only when API_KEY is set.
  • Per-IP sliding-window rate limiting → 429 with Retry-After.
  • Layered upload defence: request-body cap (413), streamed file-size cap, decompression-bomb pixel cap, real-image decode check, and a saved filename derived from the decoded format (never the user-supplied name).
  • Optional SHA-256 weights integrity check before torch.load.
  • Hardened CORS allowlist; nginx security headers + gzip + long-cache for fingerprinted assets; non-root container; /docs hidden in production; background upload-retention sweep.

Backend reliability & correctness

  • Run YOLO inference/annotation off the event loop in a threadpool, with a lock around the non-thread-safe model.
  • Real 503 when the model isn't loaded (no more silent failure); new /api/ready probe; /api/health returns 503 when degraded.
  • Division-by-zero guard in the mm/pixel math; camera-height validation (100–5000 mm).
  • Request correlation IDs (X-Request-ID) + a global error handler.

Backend architecture & code quality

  • Single source of truth in app/config.py; repeated literals pulled into constants.
  • Thin routes + small single-purpose services; extracted detection_filters; dependency injection via FastAPI Depends (testable).
  • Structured logging replaces print; one structured log line per completed detection.
  • Unified confidence default (0.40) and raised the API floor (0.10).

Frontend resilience, UX & accessibility

  • All backend calls centralised in src/api/client.js (checks response.ok, surfaces the request-id in errors).
  • ErrorBoundary + defensive rendering against corrupt storage / malformed payloads.
  • Cancellable uploads + 60 s watchdog/timeout; revoke preview blob URLs (memory-leak fix).
  • Dark mode with no first-paint flash; last detection persists across reload via sessionStorage.
  • Keyboard-accessible drop zone and crown rows; light-mode readability fix on the model info panel.

Testing

  • New Vitest + Testing Library suite: API client, settings clamping, error boundary, defensive rendering, keyboard a11y, single-sourced constants, dark-mode FOUC prevention, and the upload abort/timeout flow.

DevOps / build / deploy

  • .env.example + docker-compose reads .env; .dockerignore for both build contexts.
  • Arch-neutral PyTorch wheels (builds on Apple Silicon and Intel); pinned frontend deps + npm ci; weights baked into the compose image.
  • Backend HEALTHCHECK, frontend gated on backend readiness, CPU/memory limits, documented single-process Uvicorn choice, and render.yaml deploy notes.

Docs

  • Full README.md rewrite (architecture diagrams, config table, API reference, coding principles, deploy notes) + docs/size-estimation.md deep dive.

API / behaviour changes (for reviewers)

  • /api/detect: new optional form fields conf_threshold and aspect_ratio_filter; the response now also includes conf_threshold, aspect_ratio_filter, and num_filtered. May now return 401 (only when API_KEY is set), 413, or 429.
  • /api/health now returns 503 when the model isn't loaded; new /api/ready endpoint.
  • New environment variables (see .env.example). Defaults keep auth disabled, so existing local-dev flows are unchanged.

How to verify

  • App: cp .env.example .env && docker compose up --build → http://localhost:8080
  • Frontend tests: cd frontend && npm ci && npm test

Follow-ups (not in this MR)

  • Backend pytest suite (the service layer is structured for it).
  • Shared store (e.g. Redis) for rate-limit/retention state if running multi-instance.
  • Use real RGB-D depth instead of the fixed-height assumption.
Assignee
Assign to
Reviewer
Request review from
None
Milestone
None
Assign milestone
Time tracking
Source branch: development