Phase 3: env masking, image updates, port conflicts, resources, templates (0.3.0)
Backend:
- update_service: registry manifest digest check (Docker Hub/ghcr/lscr/private
v2 token auth) vs local RepoDigests; in-memory cache + background loop
- port_service: parse compose ports, check /proc/net/tcp[6] + docker bindings
- template_service + bundled templates (jellyfin/vaultwarden/uptime-kuma/
paperless-ngx/gitea) with {{VAR}} placeholders; custom templates in DB
- compose_edit set_resources (deploy.resources.limits/reservations)
- routers: images, ports, templates, editor/set-resources
- Template model; background update task wired into lifespan
Frontend:
- EnvEditor (table + raw, sensitive masking, quick-insert)
- Images page + UpdateBadge + dashboard 'updates available' banner
- PortConflictDialog pre-deploy check on Deploy
- ResourcePanel (CPU/RAM sliders) as editor Limits tab
- Templates page with per-variable instantiate form
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b553c1b861
commit
22d9864436
@@ -30,4 +30,17 @@ export const editorApi = {
|
||||
value,
|
||||
})
|
||||
.then((r) => r.data.yaml),
|
||||
setResources: (
|
||||
yaml: string,
|
||||
service: string,
|
||||
res: {
|
||||
cpus?: number | null;
|
||||
memory?: string | null;
|
||||
cpus_reserve?: number | null;
|
||||
memory_reserve?: string | null;
|
||||
}
|
||||
) =>
|
||||
api
|
||||
.post<{ yaml: string }>("/api/editor/set-resources", { yaml, service, ...res })
|
||||
.then((r) => r.data.yaml),
|
||||
};
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import api from "./client";
|
||||
|
||||
export interface UpdateStatus {
|
||||
image: string;
|
||||
update_available: boolean;
|
||||
current_digest: string | null;
|
||||
remote_digest: string | null;
|
||||
checked_at: number;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
export interface ImageRow {
|
||||
id: string;
|
||||
tag: string;
|
||||
size: number;
|
||||
created: string;
|
||||
stacks: string[];
|
||||
update: UpdateStatus | null;
|
||||
}
|
||||
|
||||
export const imagesApi = {
|
||||
list: () => api.get<ImageRow[]>("/api/images").then((r) => r.data),
|
||||
updates: () =>
|
||||
api.get<Record<string, UpdateStatus>>("/api/images/updates").then((r) => r.data),
|
||||
check: () =>
|
||||
api.post<Record<string, UpdateStatus>>("/api/images/check").then((r) => r.data),
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import api from "./client";
|
||||
|
||||
export interface PortConflict {
|
||||
port: number;
|
||||
protocol: string;
|
||||
service: string | null;
|
||||
used_by: string;
|
||||
}
|
||||
|
||||
export const portsApi = {
|
||||
conflicts: (yaml: string, ignore_stack?: string) =>
|
||||
api
|
||||
.post<{ conflicts: PortConflict[] }>("/api/ports/conflicts", {
|
||||
yaml,
|
||||
ignore_stack,
|
||||
})
|
||||
.then((r) => r.data.conflicts),
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
import api from "./client";
|
||||
|
||||
export interface TemplateVariable {
|
||||
name: string;
|
||||
description: string;
|
||||
default: string;
|
||||
}
|
||||
|
||||
export interface TemplateSummary {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
tags: string[];
|
||||
gpu?: string | null;
|
||||
source: "bundled" | "custom";
|
||||
}
|
||||
|
||||
export interface TemplateDetail extends TemplateSummary {
|
||||
yaml: string;
|
||||
variables: TemplateVariable[];
|
||||
}
|
||||
|
||||
export const templatesApi = {
|
||||
list: () => api.get<TemplateSummary[]>("/api/templates").then((r) => r.data),
|
||||
get: (id: string) =>
|
||||
api.get<TemplateDetail>(`/api/templates/${id}`).then((r) => r.data),
|
||||
instantiate: (id: string, name: string, values: Record<string, string>) =>
|
||||
api
|
||||
.post<{ id: string; name: string }>(`/api/templates/${id}/instantiate`, {
|
||||
name,
|
||||
values,
|
||||
})
|
||||
.then((r) => r.data),
|
||||
save: (body: { name: string; description?: string; tags: string[]; yaml: string }) =>
|
||||
api.post("/api/templates", body).then((r) => r.data),
|
||||
remove: (slug: string) =>
|
||||
api.delete(`/api/templates/custom/${slug}`).then((r) => r.data),
|
||||
};
|
||||
Reference in New Issue
Block a user