refactor(workload): finalize containers index + post-review hardening
Wraps up the workload refactor with the fixes that came out of the multi-agent code review (see docs/plans/workload-refactor.md "What actually shipped"). Backend: - store.ReconcileContainer: separate write path so the 30s reconciler tick no longer overwrites deployer-owned fields (subdomain, proxy_route_id, npm_proxy_id, image_tag). - Container.stage_id column + index; ListProxyRoutes / ListContainersByStageID join via stage_id (survives stage rename), with legacy fallback to (project_id, role=stage_name). - Reconciler: workload-existence check (rejects forged tinyforge.workload.id labels), skips inventing project-kind rows, child-context cancel before wg.Wait() on shutdown. - Transactional CRUD across projects / stacks / static_sites: parent UPDATE and workload sync land in one transaction so secret rotations are durable. - Webhook routing reads exclusively through workloads.webhook_secret; legacy GetProjectByWebhookSecret / GetStaticSiteByWebhookSecret fallback removed. - store.GetStackByComposeProjectName + indexed lookup (no more full-table stack scan per compose container per tick). - store.ListMissingSweepRows: filtered query for the missing-sweep. - /api/instances/* handlers verify (workload_id, role) match URL (project_id, stage_name) before mutating — closes the cross-project hijack the security review flagged. - extra_json no longer referenced from Go (column kept on disk for now). Frontend: - WorkloadContainers.svelte: generic detail-page panel reusable by stack and site detail pages. - Containers page polish: client-side kind/state filters over an unfiltered fetch, URL-synced filters, race-safe loads via sequence number, EN+RU i18n, sidebar counter via navCounts.containers. Misc: - scripts/dev-server.sh: tolerate empty netstat grep result. - .gitignore: ignore docker-watcher binaries, .claude/worktrees/, .facts-sync.json.
This commit is contained in:
+20
-17
@@ -365,24 +365,27 @@ type Workload struct {
|
||||
// Replaces the project-specific Instance table after migration. Subdomain/
|
||||
// proxy fields are hoisted as first-class columns because ListProxyRoutes,
|
||||
// stale detection, and dashboard queries filter on them frequently.
|
||||
//
|
||||
// StageID is populated by the deployer for project containers so ListProxyRoutes
|
||||
// survives stage renames; it stays empty for stack and site rows.
|
||||
type Container struct {
|
||||
ID string `json:"id"`
|
||||
WorkloadID string `json:"workload_id"`
|
||||
WorkloadKind string `json:"workload_kind"` // denormalized for filtered queries
|
||||
Role string `json:"role"` // stage name (project), service name (stack), '' (site)
|
||||
ContainerID string `json:"container_id"` // Docker container ID; '' between create+start
|
||||
ImageRef string `json:"image_ref"` // "image:tag" as scheduled
|
||||
ImageTag string `json:"image_tag"` // just the tag, for ListProxyRoutes
|
||||
Host string `json:"host"`
|
||||
State string `json:"state"` // running | stopped | failed | removing | missing
|
||||
Port int `json:"port"`
|
||||
Subdomain string `json:"subdomain"`
|
||||
ProxyRouteID string `json:"proxy_route_id"`
|
||||
NpmProxyID int `json:"npm_proxy_id"`
|
||||
LastSeenAt string `json:"last_seen_at"`
|
||||
ExtraJSON string `json:"extra_json"` // {} default; reserved for kind-specific forward-compat
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
ID string `json:"id"`
|
||||
WorkloadID string `json:"workload_id"`
|
||||
WorkloadKind string `json:"workload_kind"` // denormalized for filtered queries
|
||||
Role string `json:"role"` // stage name (project), service name (stack), '' (site)
|
||||
StageID string `json:"stage_id"` // project containers only; '' otherwise
|
||||
ContainerID string `json:"container_id"` // Docker container ID; '' between create+start
|
||||
ImageRef string `json:"image_ref"` // "image:tag" as scheduled
|
||||
ImageTag string `json:"image_tag"` // just the tag, for ListProxyRoutes
|
||||
Host string `json:"host"`
|
||||
State string `json:"state"` // running | stopped | failed | removing | missing
|
||||
Port int `json:"port"`
|
||||
Subdomain string `json:"subdomain"`
|
||||
ProxyRouteID string `json:"proxy_route_id"`
|
||||
NpmProxyID int `json:"npm_proxy_id"`
|
||||
LastSeenAt string `json:"last_seen_at"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
// App is an optional grouping of workloads (e.g., "my-saas" = web project + worker stack + redis stack).
|
||||
|
||||
Reference in New Issue
Block a user