1
0
forked from wrenn/wrenn

Seed minimal template in DB and protect it from deletion

Insert a minimal template row (all-zeros UUID) so it appears in both
team and admin template listings. Guard delete endpoints to prevent
removal of the minimal template.
This commit is contained in:
2026-03-29 01:34:54 +06:00
parent 906cc42d13
commit 46d60fc5a5
3 changed files with 29 additions and 1 deletions

View File

@ -12,6 +12,7 @@ ALTER TABLE templates ADD CONSTRAINT uq_templates_team_name UNIQUE (team_id, nam
-- 3. Prevent team templates from using names that belong to global (platform) templates. -- 3. Prevent team templates from using names that belong to global (platform) templates.
-- A team template insert/update with a name matching any platform template is rejected. -- A team template insert/update with a name matching any platform template is rejected.
-- +goose StatementBegin
CREATE OR REPLACE FUNCTION check_global_template_name_collision() CREATE OR REPLACE FUNCTION check_global_template_name_collision()
RETURNS TRIGGER AS $$ RETURNS TRIGGER AS $$
BEGIN BEGIN
@ -28,13 +29,27 @@ BEGIN
RETURN NEW; RETURN NEW;
END; END;
$$ LANGUAGE plpgsql; $$ LANGUAGE plpgsql;
-- +goose StatementEnd
CREATE TRIGGER trg_check_global_template_name CREATE TRIGGER trg_check_global_template_name
BEFORE INSERT OR UPDATE ON templates BEFORE INSERT OR UPDATE ON templates
FOR EACH ROW FOR EACH ROW
EXECUTE FUNCTION check_global_template_name_collision(); EXECUTE FUNCTION check_global_template_name_collision();
-- 4. Add template UUID references to template_builds. -- 4. Seed the built-in "minimal" template so it appears in all listings.
-- Both id and team_id are the all-zeros UUID (platform sentinel).
INSERT INTO templates (id, name, type, vcpus, memory_mb, size_bytes, team_id)
VALUES (
'00000000-0000-0000-0000-000000000000',
'minimal',
'base',
1,
512,
0,
'00000000-0000-0000-0000-000000000000'
) ON CONFLICT DO NOTHING;
-- 5. Add template UUID references to template_builds.
ALTER TABLE template_builds ALTER TABLE template_builds
ADD COLUMN template_id UUID, ADD COLUMN template_id UUID,
ADD COLUMN team_id UUID; ADD COLUMN team_id UUID;
@ -54,6 +69,9 @@ ALTER TABLE template_builds
DROP COLUMN IF EXISTS team_id, DROP COLUMN IF EXISTS team_id,
DROP COLUMN IF EXISTS template_id; DROP COLUMN IF EXISTS template_id;
-- Remove the seeded minimal template.
DELETE FROM templates WHERE id = '00000000-0000-0000-0000-000000000000';
DROP TRIGGER IF EXISTS trg_check_global_template_name ON templates; DROP TRIGGER IF EXISTS trg_check_global_template_name ON templates;
DROP FUNCTION IF EXISTS check_global_template_name_collision(); DROP FUNCTION IF EXISTS check_global_template_name_collision();

View File

@ -12,6 +12,7 @@ import (
"git.omukk.dev/wrenn/sandbox/internal/db" "git.omukk.dev/wrenn/sandbox/internal/db"
"git.omukk.dev/wrenn/sandbox/internal/id" "git.omukk.dev/wrenn/sandbox/internal/id"
"git.omukk.dev/wrenn/sandbox/internal/layout"
"git.omukk.dev/wrenn/sandbox/internal/lifecycle" "git.omukk.dev/wrenn/sandbox/internal/lifecycle"
"git.omukk.dev/wrenn/sandbox/internal/service" "git.omukk.dev/wrenn/sandbox/internal/service"
"git.omukk.dev/wrenn/sandbox/internal/validate" "git.omukk.dev/wrenn/sandbox/internal/validate"
@ -221,6 +222,10 @@ func (h *buildHandler) DeleteTemplate(w http.ResponseWriter, r *http.Request) {
writeError(w, http.StatusNotFound, "not_found", "template not found") writeError(w, http.StatusNotFound, "not_found", "template not found")
return return
} }
if layout.IsMinimal(tmpl.TeamID, tmpl.ID) {
writeError(w, http.StatusForbidden, "forbidden", "the minimal template cannot be deleted")
return
}
// Broadcast delete to all online hosts. // Broadcast delete to all online hosts.
hosts, _ := h.db.ListActiveHosts(ctx) hosts, _ := h.db.ListActiveHosts(ctx)

View File

@ -17,6 +17,7 @@ import (
"git.omukk.dev/wrenn/sandbox/internal/auth" "git.omukk.dev/wrenn/sandbox/internal/auth"
"git.omukk.dev/wrenn/sandbox/internal/db" "git.omukk.dev/wrenn/sandbox/internal/db"
"git.omukk.dev/wrenn/sandbox/internal/id" "git.omukk.dev/wrenn/sandbox/internal/id"
"git.omukk.dev/wrenn/sandbox/internal/layout"
"git.omukk.dev/wrenn/sandbox/internal/lifecycle" "git.omukk.dev/wrenn/sandbox/internal/lifecycle"
"git.omukk.dev/wrenn/sandbox/internal/service" "git.omukk.dev/wrenn/sandbox/internal/service"
"git.omukk.dev/wrenn/sandbox/internal/validate" "git.omukk.dev/wrenn/sandbox/internal/validate"
@ -271,6 +272,10 @@ func (h *snapshotHandler) Delete(w http.ResponseWriter, r *http.Request) {
writeError(w, http.StatusForbidden, "forbidden", "platform templates cannot be deleted here") writeError(w, http.StatusForbidden, "forbidden", "platform templates cannot be deleted here")
return return
} }
if layout.IsMinimal(tmpl.TeamID, tmpl.ID) {
writeError(w, http.StatusForbidden, "forbidden", "the minimal template cannot be deleted")
return
}
if err := h.deleteSnapshotBroadcast(ctx, tmpl.TeamID, tmpl.ID); err != nil { if err := h.deleteSnapshotBroadcast(ctx, tmpl.TeamID, tmpl.ID); err != nil {
writeError(w, http.StatusInternalServerError, "agent_error", "failed to delete snapshot files") writeError(w, http.StatusInternalServerError, "agent_error", "failed to delete snapshot files")