Commit Graph

4 Commits

Author SHA1 Message Date
Kunthawat Greethong
8953b8f066 fix: ensure Postgres driver installs in image & stop uv re-sync at runtime
Worker crash-loop root cause (from container log):
  sqlalchemy.exc.NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:postgres

Two compounding issues:
1. Dockerfile copied pyproject.toml/uv.lock, ran 'uv sync --frozen',
   then 'COPY backend ./backend' which OVERWROTE those dep files with the
   shipped versions. The two could differ, so every entrypoint 'uv run'
   detected drift and REBUILT/re-synced the project at container runtime
   (seen as repeated 'Building crowdsight-backend...' + 'Uninstalled N /
   Installed 1'), never installing the psycopg Postgres driver that the
   image's own lock actually lists.
2. Result: alembic upgrade head over a postgres DATABASE_URL crashed with
   NoSuchModuleError -> worker crash-loop.

Fix:
- Dockerfile: COPY backend (full source) BEFORE 'uv sync --frozen --no-dev',
  so the installed deps match the shipped pyproject.toml/uv.lock exactly.
- Use 'uv run --frozen' for alembic/gunicorn/worker so nothing re-syncs at
  runtime.
- entrypoint: fail fast with a clear message if DATABASE_URL is postgres
  but psycopg is missing (instead of a confusing alembic traceback).
Verified: entrypoint bash syntax ok; 'uv run --frozen ... import psycopg'
passes; psycopg present in git-tracked uv.lock + pyproject.
2026-08-31 18:56:18 +07:00
Kunthawat Greethong
42208c4f5a fix: run alembic migrations before starting services
Production image had no DB migration step, so a fresh container had an
empty database: the durable worker queried the 'jobs' table before it
existed and crash-looped with sqlalchemy OperationalError 'no such table:
jobs' (supervisor restart loop).

- Add backend/docker_entrypoint.sh: fail-fast if DATABASE_URL is unset,
  run 'alembic upgrade head' (idempotent), then exec supervisord.
- Dockerfile CMD now runs the entrypoint.
- supervisor: fix nodaemon typo, stream stdout/stderr to /dev/stdout +
  /dev/stderr so worker errors are visible in container logs, and give
  worker startsecs/startretries.

Verified locally: entrypoint bash syntax ok, alembic upgrade head
idempotent, jobs + 19 tables created, worker --once exits 0 after migrate
(previously exit 1 with no-such-table). Worker/schema tests 11 passed.
2026-08-31 15:16:59 +07:00
Kunthawat Greethong
8b84378fe1 feat: SaaS foundation for CrowdSight
Elevate MiroFish/CrowdSight from single-container dev to a SaaS foundation:

- Local memory backend (Zep-compatible): memory services/models, local graph
  builder + updater, AgentActivity seam, import-boundary isolation; Zep stays
  default, local is opt-in behind MEMORY_BACKEND. Semantic parity not yet proven.
- Durable product persistence: projects/simulations/reports schema (migration
  0007) + tenant/owner-scoped ProductRepository + dual-write + scoped_project
  read-first + ArtifactStore abstraction; durable JobQueue + worker.py.
- SaaS hardening: durable RateLimiter (wired to login), UsageService (LLM
  accounting), redacted AuditService, idempotency, CORS allowlist, safe API
  errors, single-use PasswordResetService + endpoints (covers invite-pending).
- Exactly 3 roles (super_admin/admin/user) with tenant authz policy.
- Admin UI: GET/POST/PATCH /api/admin/users + GET/PUT /api/admin/settings
  (super-admin only, encrypted/masked); AdminView.vue + SettingsView.vue with
  admin/super-admin route guards, th/en i18n.
- Production deploy topology: multi-stage Dockerfile (frontend build + gunicorn
  wsgi + nginx SPA-proxy + supervisord worker), backend/wsgi.py, gunicorn dep.

Backend 197 passed; frontend 10 tests + build green. ruff unavailable (gap).
No commit of credentials; secrets handled via env/.env.example.
Deferred: Zep semantic A/B parity, object storage cutover, mobile QA, EasyPanel
container build of deploy topology.
2026-08-31 13:05:21 +07:00
666ghj
0efd9352a0 feat(docker): add Docker support with Dockerfile, docker-compose.yml, and .dockerignore; update README for Docker deployment instructions 2026-01-22 14:35:41 +08:00