refactor(workload): plugin architecture wave + apps UI + volume scopes
Completes the workload-first refactor's plugin layer:
- internal/workload/plugin/ — Source/Trigger plugin contract,
registry, types (Workload, DeploymentIntent, InboundEvent,
PublicFace). Self-registering init() pattern + blank-import
in cmd/server/main.go.
- Source plugins: image (blue-green with multi-face proxy routing),
compose, static. Trigger plugins: registry, git, manual.
- internal/deployer/dispatch.go — DispatchPlugin/Teardown/Reconcile
seam routing the legacy deployer through plugins.
- internal/api/workload_*.go — REST surface: workloads, env,
volumes, chain (parent/children), promote-from. hooks.go
serves /api/hooks/kinds/{kind}/schema for the wizard.
- internal/store: workload_env (encrypt-at-rest secrets) and
workload_volumes tables, keyed on workload_id.
- cmd/server/static_backend.go — phantom-row adapter delegating
the static source plugin to the legacy staticsite.Manager
(deleted at hard cutover once the static inline port lands).
- web/src/routes/apps/ — /apps list + /apps/new wizard +
/apps/[id] detail with kind-aware compose / image / static
forms (Advanced JSON toggle), env panel, volumes panel,
webhook panel, chain panel, manual deploy.
Volume scope generalization (v2 resolver):
- internal/volume.ResolveWorkloadPath (workload-keyed, sits
next to legacy ResolvePath). Honors all VolumeScope values:
absolute, ephemeral, instance, stage, project, project_named,
named. internal/workload/plugin/source/image/image.go
computeMounts wires settings + imageTag through. Coverage in
internal/volume/resolver_test.go (portable Linux/Windows via
t.TempDir).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,14 +6,19 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { onDestroy } from 'svelte';
|
||||
import { fetchContainerLogs, fetchStaticSiteLogs } from '$lib/api';
|
||||
import {
|
||||
fetchContainerLogs,
|
||||
fetchStaticSiteLogs,
|
||||
fetchWorkloadContainerLogs
|
||||
} from '$lib/api';
|
||||
import { getAuthToken } from '$lib/auth';
|
||||
import { t } from '$lib/i18n';
|
||||
import { IconLoader, IconX } from '$lib/components/icons';
|
||||
|
||||
export type LogSource =
|
||||
| { kind: 'instance'; projectId: string; stageId: string; instanceId: string }
|
||||
| { kind: 'site'; siteId: string };
|
||||
| { kind: 'site'; siteId: string }
|
||||
| { kind: 'workload'; workloadId: string; containerRowId: string };
|
||||
|
||||
interface Props {
|
||||
source: LogSource;
|
||||
@@ -58,6 +63,9 @@
|
||||
if (source.kind === 'instance') {
|
||||
return `/api/projects/${source.projectId}/stages/${source.stageId}/instances/${source.instanceId}/logs?follow=true&tail=0${tokenParam}`;
|
||||
}
|
||||
if (source.kind === 'workload') {
|
||||
return `/api/workloads/${source.workloadId}/containers/${source.containerRowId}/logs?follow=true&tail=0${tokenParam}`;
|
||||
}
|
||||
return `/api/sites/${source.siteId}/logs?follow=true&tail=0${tokenParam}`;
|
||||
}
|
||||
|
||||
@@ -65,6 +73,9 @@
|
||||
if (source.kind === 'instance') {
|
||||
return fetchContainerLogs(source.projectId, source.stageId, source.instanceId, tail);
|
||||
}
|
||||
if (source.kind === 'workload') {
|
||||
return fetchWorkloadContainerLogs(source.workloadId, source.containerRowId, tail);
|
||||
}
|
||||
return fetchStaticSiteLogs(source.siteId, tail);
|
||||
}
|
||||
|
||||
|
||||
+36
-3
@@ -536,11 +536,11 @@ export interface SystemStatsSample {
|
||||
|
||||
// ── Workload / Container / App ────────────────────────────────────
|
||||
|
||||
export type WorkloadKind = 'project' | 'stack' | 'site';
|
||||
export type WorkloadKind = 'project' | 'stack' | 'site' | 'plugin' | (string & {});
|
||||
|
||||
/**
|
||||
* Workload is the unifying primitive over Project / Stack / StaticSite.
|
||||
* Read-only at this layer — mutations go through the kind-specific endpoints.
|
||||
* Workload is the unifying primitive over Project / Stack / StaticSite,
|
||||
* plus plugin-native rows whose source/trigger kinds are populated.
|
||||
*/
|
||||
export interface Workload {
|
||||
id: string;
|
||||
@@ -548,12 +548,45 @@ export interface Workload {
|
||||
ref_id: string;
|
||||
name: string;
|
||||
app_id: string;
|
||||
source_kind: string;
|
||||
source_config: string;
|
||||
trigger_kind: string;
|
||||
trigger_config: string;
|
||||
public_faces: string;
|
||||
parent_workload_id: string;
|
||||
notification_url: string;
|
||||
webhook_require_signature: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface PublicFace {
|
||||
subdomain: string;
|
||||
domain: string;
|
||||
target_service: string;
|
||||
target_port: number;
|
||||
access_list_id: number;
|
||||
enable_ssl: boolean;
|
||||
}
|
||||
|
||||
export interface PluginWorkloadInput {
|
||||
name: string;
|
||||
group_id?: string;
|
||||
parent_workload_id?: string;
|
||||
source_kind: string;
|
||||
source_config: unknown;
|
||||
trigger_kind: string;
|
||||
trigger_config: unknown;
|
||||
public_faces?: PublicFace[];
|
||||
notification_url?: string;
|
||||
webhook_require_signature?: boolean;
|
||||
}
|
||||
|
||||
export interface HookKinds {
|
||||
sources: string[];
|
||||
triggers: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Canonical container states. The trailing `(string & {})` is the
|
||||
* "literal-friendly string" trick — it lets the union accept arbitrary
|
||||
|
||||
Reference in New Issue
Block a user