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.
This commit is contained in:
Kunthawat Greethong
2026-08-31 13:05:21 +07:00
parent 89d04e795b
commit 8b84378fe1
165 changed files with 15884 additions and 4001 deletions

View File

@@ -13,6 +13,10 @@
"failed": "Failed",
"unknown": "Unknown",
"unknownError": "Unknown error",
"requestFailed": "Request failed. Please try again.",
"networkError": "Network error. Please check your connection.",
"requestTimeout": "The request timed out. Please try again.",
"unauthorized": "Your session has expired. Please sign in again.",
"none": "None",
"close": "Close",
"back": "Back",
@@ -23,7 +27,20 @@
"minutes": "minutes",
"rounds": "rounds",
"items": "items",
"files": "files"
"files": "files",
"notFound": "The requested resource was not found."
},
"auth": {
"eyebrow": "Secure workspace",
"title": "Sign in to CrowdSight",
"subtitle": "Use your company account to continue.",
"email": "Email",
"emailPlaceholder": "name@company.com",
"password": "Password",
"passwordPlaceholder": "Enter your password",
"signIn": "Sign in",
"signingIn": "Signing in...",
"signInFailed": "Sign-in failed. Please try again."
},
"meta": {
"title": "CrowdSight - Predict Everything",
@@ -404,12 +421,26 @@
"envRunning": "Environment is running and ready for Interview commands",
"envNotRunningShort": "Environment not running or closed",
"requireGraphIdAndQuery": "Please provide graph_id and query",
"internalError": "The server could not complete the request.",
"requestError": "The request could not be completed.",
"idempotencyRequired": "Please provide an Idempotency-Key header.",
"idempotencyInvalid": "The Idempotency-Key header is invalid.",
"idempotencyConflict": "This Idempotency-Key was already used for a different request.",
"idempotencyInProgress": "An identical request is already in progress.",
"invalidCredentials": "Invalid email or password.",
"tooManyAttempts": "Too many attempts. Please try again later.",
"organizationRequired": "Please specify organization_slug.",
"organizationNotFound": "Organization not found.",
"invalidEmail": "Please provide a valid email address.",
"invalidRole": "The requested role is not valid.",
"userExists": "A user with this email already exists.",
"initReportAgent": "Initializing Report Agent..."
},
"progress": {
"initGraphService": "Initializing graph build service...",
"textChunking": "Chunking text...",
"creatingZepGraph": "Creating Zep graph...",
"creatingGraph": "Creating graph...",
"settingOntology": "Setting ontology definition...",
"addingChunks": "Adding {count} text chunks...",
"waitingZepProcess": "Waiting for Zep to process data...",
@@ -432,6 +463,7 @@
"taskFailed": "Task failed",
"startPreparingEnv": "Preparing simulation environment...",
"connectingZepGraph": "Connecting to Zep graph...",
"connectingGraph": "Connecting to graph...",
"readingNodeData": "Reading node data...",
"readingComplete": "Done, {count} entities found",
"startGenerating": "Starting generation...",
@@ -688,5 +720,36 @@
"fiction_story": "Fiction & Stories",
"social_culture": "Social & Culture",
"fillPrompt": "Edit prompt before running"
},
"admin": {
"title": "User management",
"refresh": "Refresh",
"loading": "Loading users…",
"empty": "No users found.",
"colEmail": "Email",
"colRole": "Role",
"colStatus": "Status",
"colActions": "Actions",
"roleUser": "User",
"roleAdmin": "Admin",
"roleSuperAdmin": "Super admin",
"status_active": "Active",
"status_disabled": "Disabled",
"deactivate": "Disable",
"activate": "Activate"
},
"settings": {
"title": "Platform LLM settings",
"provider": "Provider",
"model": "Model",
"baseUrl": "Base URL",
"apiKey": "API key",
"apiKeyPlaceholder": "•••••••• (leave empty to keep current)",
"save": "Save settings",
"saved": "Settings saved.",
"error": "Could not save settings.",
"loading": "Loading settings…",
"noneStored": "No settings stored yet.",
"version": "Version"
}
}