forked from wrenn/wrenn
v0.0.1 (#8)
Co-authored-by: Tasnim Kabir Sadik <tksadik92@gmail.com> Reviewed-on: wrenn/sandbox#8
This commit is contained in:
@ -1,25 +1,237 @@
|
||||
-- +goose Up
|
||||
|
||||
CREATE TABLE sandboxes (
|
||||
id TEXT PRIMARY KEY,
|
||||
owner_id TEXT NOT NULL DEFAULT '',
|
||||
host_id TEXT NOT NULL DEFAULT 'default',
|
||||
template TEXT NOT NULL DEFAULT 'minimal',
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
vcpus INTEGER NOT NULL DEFAULT 1,
|
||||
memory_mb INTEGER NOT NULL DEFAULT 512,
|
||||
timeout_sec INTEGER NOT NULL DEFAULT 0,
|
||||
guest_ip TEXT NOT NULL DEFAULT '',
|
||||
host_ip TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
started_at TIMESTAMPTZ,
|
||||
last_active_at TIMESTAMPTZ,
|
||||
last_updated TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
-- teams
|
||||
CREATE TABLE teams (
|
||||
id UUID PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
is_byoc BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
deleted_at TIMESTAMPTZ
|
||||
);
|
||||
CREATE INDEX idx_teams_slug ON teams(slug);
|
||||
|
||||
-- users
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
is_admin BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- users_teams (junction)
|
||||
CREATE TABLE users_teams (
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
team_id UUID NOT NULL REFERENCES teams(id),
|
||||
is_default BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
role TEXT NOT NULL DEFAULT 'member',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (team_id, user_id)
|
||||
);
|
||||
CREATE INDEX idx_users_teams_user ON users_teams(user_id);
|
||||
|
||||
-- team_api_keys
|
||||
CREATE TABLE team_api_keys (
|
||||
id UUID PRIMARY KEY,
|
||||
team_id UUID NOT NULL REFERENCES teams(id),
|
||||
name TEXT NOT NULL,
|
||||
key_hash TEXT NOT NULL UNIQUE,
|
||||
key_prefix TEXT NOT NULL,
|
||||
created_by UUID NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
last_used TIMESTAMPTZ
|
||||
);
|
||||
CREATE INDEX idx_team_api_keys_team ON team_api_keys(team_id);
|
||||
|
||||
-- oauth_providers
|
||||
CREATE TABLE oauth_providers (
|
||||
provider TEXT NOT NULL,
|
||||
provider_id TEXT NOT NULL,
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
email TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (provider, provider_id)
|
||||
);
|
||||
CREATE INDEX idx_oauth_providers_user ON oauth_providers(user_id);
|
||||
|
||||
-- admin_permissions
|
||||
CREATE TABLE admin_permissions (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
permission TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (user_id, permission)
|
||||
);
|
||||
CREATE INDEX idx_admin_permissions_user ON admin_permissions(user_id);
|
||||
|
||||
-- hosts
|
||||
CREATE TABLE hosts (
|
||||
id UUID PRIMARY KEY,
|
||||
type TEXT NOT NULL DEFAULT 'regular',
|
||||
team_id UUID REFERENCES teams(id),
|
||||
provider TEXT NOT NULL DEFAULT '',
|
||||
availability_zone TEXT NOT NULL DEFAULT '',
|
||||
arch TEXT NOT NULL DEFAULT '',
|
||||
cpu_cores INTEGER NOT NULL DEFAULT 0,
|
||||
memory_mb INTEGER NOT NULL DEFAULT 0,
|
||||
disk_gb INTEGER NOT NULL DEFAULT 0,
|
||||
address TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
last_heartbeat_at TIMESTAMPTZ,
|
||||
metadata JSONB NOT NULL DEFAULT '{}',
|
||||
created_by UUID NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
cert_fingerprint TEXT NOT NULL DEFAULT '',
|
||||
mtls_enabled BOOLEAN NOT NULL DEFAULT FALSE
|
||||
);
|
||||
CREATE INDEX idx_hosts_type ON hosts(type);
|
||||
CREATE INDEX idx_hosts_team ON hosts(team_id);
|
||||
CREATE INDEX idx_hosts_status ON hosts(status);
|
||||
|
||||
-- host_tokens
|
||||
CREATE TABLE host_tokens (
|
||||
id UUID PRIMARY KEY,
|
||||
host_id UUID NOT NULL REFERENCES hosts(id) ON DELETE CASCADE,
|
||||
created_by UUID NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
used_at TIMESTAMPTZ
|
||||
);
|
||||
CREATE INDEX idx_host_tokens_host ON host_tokens(host_id);
|
||||
|
||||
-- host_tags
|
||||
CREATE TABLE host_tags (
|
||||
host_id UUID NOT NULL REFERENCES hosts(id) ON DELETE CASCADE,
|
||||
tag TEXT NOT NULL,
|
||||
PRIMARY KEY (host_id, tag)
|
||||
);
|
||||
CREATE INDEX idx_host_tags_tag ON host_tags(tag);
|
||||
|
||||
-- host_refresh_tokens
|
||||
CREATE TABLE host_refresh_tokens (
|
||||
id UUID PRIMARY KEY,
|
||||
host_id UUID NOT NULL REFERENCES hosts(id) ON DELETE CASCADE,
|
||||
token_hash TEXT NOT NULL UNIQUE,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
revoked_at TIMESTAMPTZ
|
||||
);
|
||||
CREATE INDEX idx_host_refresh_tokens_host ON host_refresh_tokens(host_id);
|
||||
|
||||
-- templates (TEXT primary key — not UUID)
|
||||
CREATE TABLE templates (
|
||||
name TEXT PRIMARY KEY,
|
||||
type TEXT NOT NULL DEFAULT 'base',
|
||||
vcpus INTEGER NOT NULL DEFAULT 1,
|
||||
memory_mb INTEGER NOT NULL DEFAULT 512,
|
||||
size_bytes BIGINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
team_id UUID NOT NULL
|
||||
);
|
||||
CREATE INDEX idx_templates_team ON templates(team_id);
|
||||
|
||||
-- sandboxes
|
||||
CREATE TABLE sandboxes (
|
||||
id UUID PRIMARY KEY,
|
||||
team_id UUID NOT NULL REFERENCES teams(id),
|
||||
host_id UUID NOT NULL,
|
||||
template TEXT NOT NULL DEFAULT 'minimal',
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
vcpus INTEGER NOT NULL DEFAULT 1,
|
||||
memory_mb INTEGER NOT NULL DEFAULT 512,
|
||||
timeout_sec INTEGER NOT NULL DEFAULT 300,
|
||||
disk_size_mb INTEGER NOT NULL DEFAULT 5120,
|
||||
guest_ip TEXT NOT NULL DEFAULT '',
|
||||
host_ip TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
started_at TIMESTAMPTZ,
|
||||
last_active_at TIMESTAMPTZ,
|
||||
last_updated TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX idx_sandboxes_status ON sandboxes(status);
|
||||
CREATE INDEX idx_sandboxes_host_status ON sandboxes(host_id, status);
|
||||
CREATE INDEX idx_sandboxes_team ON sandboxes(team_id);
|
||||
|
||||
-- audit_logs (id and team_id are UUID; actor_id and resource_id are TEXT for polymorphism)
|
||||
CREATE TABLE audit_logs (
|
||||
id UUID PRIMARY KEY,
|
||||
team_id UUID NOT NULL,
|
||||
actor_type TEXT NOT NULL,
|
||||
actor_id TEXT,
|
||||
actor_name TEXT NOT NULL DEFAULT '',
|
||||
resource_type TEXT NOT NULL,
|
||||
resource_id TEXT,
|
||||
action TEXT NOT NULL,
|
||||
scope TEXT NOT NULL DEFAULT 'team',
|
||||
status TEXT NOT NULL DEFAULT 'success',
|
||||
metadata JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX idx_audit_logs_team_time ON audit_logs(team_id, created_at DESC);
|
||||
CREATE INDEX idx_audit_logs_team_resource ON audit_logs(team_id, resource_type, created_at DESC);
|
||||
|
||||
-- sandbox_metrics_snapshots
|
||||
CREATE TABLE sandbox_metrics_snapshots (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
team_id UUID NOT NULL,
|
||||
sampled_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
running_count INTEGER NOT NULL DEFAULT 0,
|
||||
vcpus_reserved INTEGER NOT NULL DEFAULT 0,
|
||||
memory_mb_reserved INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX idx_metrics_snapshots_team_time ON sandbox_metrics_snapshots(team_id, sampled_at DESC);
|
||||
|
||||
-- sandbox_metric_points
|
||||
CREATE TABLE sandbox_metric_points (
|
||||
sandbox_id UUID NOT NULL,
|
||||
tier TEXT NOT NULL CHECK (tier IN ('10m', '2h', '24h')),
|
||||
ts BIGINT NOT NULL,
|
||||
cpu_pct FLOAT8 NOT NULL DEFAULT 0,
|
||||
mem_bytes BIGINT NOT NULL DEFAULT 0,
|
||||
disk_bytes BIGINT NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (sandbox_id, tier, ts)
|
||||
);
|
||||
CREATE INDEX idx_sandbox_metric_points_sandbox_tier ON sandbox_metric_points(sandbox_id, tier);
|
||||
|
||||
-- template_builds
|
||||
CREATE TABLE template_builds (
|
||||
id UUID PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
base_template TEXT NOT NULL,
|
||||
recipe JSONB NOT NULL DEFAULT '[]',
|
||||
healthcheck TEXT NOT NULL DEFAULT '',
|
||||
vcpus INTEGER NOT NULL DEFAULT 1,
|
||||
memory_mb INTEGER NOT NULL DEFAULT 512,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
current_step INTEGER NOT NULL DEFAULT 0,
|
||||
total_steps INTEGER NOT NULL DEFAULT 0,
|
||||
logs JSONB NOT NULL DEFAULT '[]',
|
||||
error TEXT NOT NULL DEFAULT '',
|
||||
sandbox_id UUID,
|
||||
host_id UUID,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP TABLE sandboxes;
|
||||
DROP TABLE IF EXISTS template_builds;
|
||||
DROP TABLE IF EXISTS sandbox_metric_points;
|
||||
DROP TABLE IF EXISTS sandbox_metrics_snapshots;
|
||||
DROP TABLE IF EXISTS audit_logs;
|
||||
DROP TABLE IF EXISTS sandboxes;
|
||||
DROP TABLE IF EXISTS templates;
|
||||
DROP TABLE IF EXISTS host_refresh_tokens;
|
||||
DROP TABLE IF EXISTS host_tags;
|
||||
DROP TABLE IF EXISTS host_tokens;
|
||||
DROP TABLE IF EXISTS hosts;
|
||||
DROP TABLE IF EXISTS admin_permissions;
|
||||
DROP TABLE IF EXISTS oauth_providers;
|
||||
DROP TABLE IF EXISTS team_api_keys;
|
||||
DROP TABLE IF EXISTS users_teams;
|
||||
DROP TABLE IF EXISTS users;
|
||||
DROP TABLE IF EXISTS teams;
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
-- +goose Up
|
||||
|
||||
CREATE TABLE templates (
|
||||
name TEXT PRIMARY KEY,
|
||||
type TEXT NOT NULL DEFAULT 'base', -- 'base' or 'snapshot'
|
||||
vcpus INTEGER,
|
||||
memory_mb INTEGER,
|
||||
size_bytes BIGINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP TABLE templates;
|
||||
@ -1,46 +0,0 @@
|
||||
-- +goose Up
|
||||
|
||||
CREATE TABLE users (
|
||||
id TEXT PRIMARY KEY,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE teams (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE users_teams (
|
||||
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
|
||||
is_default BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
role TEXT NOT NULL DEFAULT 'owner',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (team_id, user_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_users_teams_user ON users_teams(user_id);
|
||||
|
||||
CREATE TABLE team_api_keys (
|
||||
id TEXT PRIMARY KEY,
|
||||
team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
key_hash TEXT NOT NULL UNIQUE,
|
||||
key_prefix TEXT NOT NULL DEFAULT '',
|
||||
created_by TEXT NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
last_used TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX idx_team_api_keys_team ON team_api_keys(team_id);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP TABLE team_api_keys;
|
||||
DROP TABLE users_teams;
|
||||
DROP TABLE teams;
|
||||
DROP TABLE users;
|
||||
@ -1,31 +0,0 @@
|
||||
-- +goose Up
|
||||
|
||||
ALTER TABLE sandboxes
|
||||
ADD COLUMN team_id TEXT NOT NULL DEFAULT '';
|
||||
|
||||
UPDATE sandboxes SET team_id = owner_id WHERE owner_id != '';
|
||||
|
||||
ALTER TABLE sandboxes
|
||||
DROP COLUMN owner_id;
|
||||
|
||||
ALTER TABLE templates
|
||||
ADD COLUMN team_id TEXT NOT NULL DEFAULT '';
|
||||
|
||||
CREATE INDEX idx_sandboxes_team ON sandboxes(team_id);
|
||||
CREATE INDEX idx_templates_team ON templates(team_id);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
ALTER TABLE sandboxes
|
||||
ADD COLUMN owner_id TEXT NOT NULL DEFAULT '';
|
||||
|
||||
UPDATE sandboxes SET owner_id = team_id WHERE team_id != '';
|
||||
|
||||
ALTER TABLE sandboxes
|
||||
DROP COLUMN team_id;
|
||||
|
||||
ALTER TABLE templates
|
||||
DROP COLUMN team_id;
|
||||
|
||||
DROP INDEX IF EXISTS idx_sandboxes_team;
|
||||
DROP INDEX IF EXISTS idx_templates_team;
|
||||
@ -1,22 +0,0 @@
|
||||
-- +goose Up
|
||||
|
||||
ALTER TABLE users
|
||||
ALTER COLUMN password_hash DROP NOT NULL;
|
||||
|
||||
CREATE TABLE oauth_providers (
|
||||
provider TEXT NOT NULL,
|
||||
provider_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
email TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (provider, provider_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_oauth_providers_user ON oauth_providers(user_id);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP TABLE oauth_providers;
|
||||
|
||||
UPDATE users SET password_hash = '' WHERE password_hash IS NULL;
|
||||
ALTER TABLE users ALTER COLUMN password_hash SET NOT NULL;
|
||||
@ -1,21 +0,0 @@
|
||||
-- +goose Up
|
||||
|
||||
ALTER TABLE users
|
||||
ADD COLUMN is_admin BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
|
||||
CREATE TABLE admin_permissions (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
permission TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (user_id, permission)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_admin_permissions_user ON admin_permissions(user_id);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP TABLE admin_permissions;
|
||||
|
||||
ALTER TABLE users
|
||||
DROP COLUMN is_admin;
|
||||
@ -1,9 +0,0 @@
|
||||
-- +goose Up
|
||||
|
||||
ALTER TABLE teams
|
||||
ADD COLUMN is_byoc BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
|
||||
-- +goose Down
|
||||
|
||||
ALTER TABLE teams
|
||||
DROP COLUMN is_byoc;
|
||||
@ -1,47 +0,0 @@
|
||||
-- +goose Up
|
||||
|
||||
CREATE TABLE hosts (
|
||||
id TEXT PRIMARY KEY,
|
||||
type TEXT NOT NULL DEFAULT 'regular', -- 'regular' or 'byoc'
|
||||
team_id TEXT REFERENCES teams(id) ON DELETE SET NULL,
|
||||
provider TEXT,
|
||||
availability_zone TEXT,
|
||||
arch TEXT,
|
||||
cpu_cores INTEGER,
|
||||
memory_mb INTEGER,
|
||||
disk_gb INTEGER,
|
||||
address TEXT, -- ip:port of host agent
|
||||
status TEXT NOT NULL DEFAULT 'pending', -- 'pending', 'online', 'offline', 'draining'
|
||||
last_heartbeat_at TIMESTAMPTZ,
|
||||
metadata JSONB NOT NULL DEFAULT '{}',
|
||||
created_by TEXT NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE host_tokens (
|
||||
id TEXT PRIMARY KEY,
|
||||
host_id TEXT NOT NULL REFERENCES hosts(id) ON DELETE CASCADE,
|
||||
created_by TEXT NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
used_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE TABLE host_tags (
|
||||
host_id TEXT NOT NULL REFERENCES hosts(id) ON DELETE CASCADE,
|
||||
tag TEXT NOT NULL,
|
||||
PRIMARY KEY (host_id, tag)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_hosts_type ON hosts(type);
|
||||
CREATE INDEX idx_hosts_team ON hosts(team_id);
|
||||
CREATE INDEX idx_hosts_status ON hosts(status);
|
||||
CREATE INDEX idx_host_tokens_host ON host_tokens(host_id);
|
||||
CREATE INDEX idx_host_tags_tag ON host_tags(tag);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP TABLE host_tags;
|
||||
DROP TABLE host_tokens;
|
||||
DROP TABLE hosts;
|
||||
@ -1,11 +0,0 @@
|
||||
-- +goose Up
|
||||
|
||||
ALTER TABLE hosts
|
||||
ADD COLUMN cert_fingerprint TEXT,
|
||||
ADD COLUMN mtls_enabled BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
|
||||
-- +goose Down
|
||||
|
||||
ALTER TABLE hosts
|
||||
DROP COLUMN cert_fingerprint,
|
||||
DROP COLUMN mtls_enabled;
|
||||
82
db/migrations/20260328162803_template_uuid_pk.sql
Normal file
82
db/migrations/20260328162803_template_uuid_pk.sql
Normal file
@ -0,0 +1,82 @@
|
||||
-- +goose Up
|
||||
|
||||
-- 1. Add UUID id column to templates and make it the primary key.
|
||||
ALTER TABLE templates ADD COLUMN id UUID DEFAULT gen_random_uuid();
|
||||
UPDATE templates SET id = gen_random_uuid() WHERE id IS NULL;
|
||||
ALTER TABLE templates ALTER COLUMN id SET NOT NULL;
|
||||
ALTER TABLE templates DROP CONSTRAINT templates_pkey;
|
||||
ALTER TABLE templates ADD PRIMARY KEY (id);
|
||||
|
||||
-- 2. Name becomes a display field with team-scoped uniqueness.
|
||||
ALTER TABLE templates ADD CONSTRAINT uq_templates_team_name UNIQUE (team_id, name);
|
||||
|
||||
-- 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.
|
||||
-- +goose StatementBegin
|
||||
CREATE OR REPLACE FUNCTION check_global_template_name_collision()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF NEW.team_id != '00000000-0000-0000-0000-000000000000' THEN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM templates
|
||||
WHERE name = NEW.name
|
||||
AND team_id = '00000000-0000-0000-0000-000000000000'
|
||||
) THEN
|
||||
RAISE EXCEPTION 'template name "%" is reserved by a global template', NEW.name
|
||||
USING ERRCODE = 'unique_violation';
|
||||
END IF;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
-- +goose StatementEnd
|
||||
|
||||
CREATE TRIGGER trg_check_global_template_name
|
||||
BEFORE INSERT OR UPDATE ON templates
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION check_global_template_name_collision();
|
||||
|
||||
-- 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
|
||||
ADD COLUMN template_id UUID,
|
||||
ADD COLUMN team_id UUID;
|
||||
|
||||
-- 5. Add template UUID references to sandboxes.
|
||||
ALTER TABLE sandboxes
|
||||
ADD COLUMN template_id UUID,
|
||||
ADD COLUMN template_team_id UUID;
|
||||
|
||||
-- +goose Down
|
||||
|
||||
ALTER TABLE sandboxes
|
||||
DROP COLUMN IF EXISTS template_team_id,
|
||||
DROP COLUMN IF EXISTS template_id;
|
||||
|
||||
ALTER TABLE template_builds
|
||||
DROP COLUMN IF EXISTS team_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 FUNCTION IF EXISTS check_global_template_name_collision();
|
||||
|
||||
ALTER TABLE templates DROP CONSTRAINT IF EXISTS uq_templates_team_name;
|
||||
|
||||
ALTER TABLE templates DROP CONSTRAINT IF EXISTS templates_pkey;
|
||||
ALTER TABLE templates ADD PRIMARY KEY (name);
|
||||
ALTER TABLE templates DROP COLUMN IF EXISTS id;
|
||||
7
db/migrations/20260330112050_mtls_cert_expiry.sql
Normal file
7
db/migrations/20260330112050_mtls_cert_expiry.sql
Normal file
@ -0,0 +1,7 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE hosts DROP COLUMN mtls_enabled;
|
||||
ALTER TABLE hosts ADD COLUMN cert_expires_at TIMESTAMPTZ;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE hosts DROP COLUMN cert_expires_at;
|
||||
ALTER TABLE hosts ADD COLUMN mtls_enabled BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
11
db/migrations/20260330150223_build_options.sql
Normal file
11
db/migrations/20260330150223_build_options.sql
Normal file
@ -0,0 +1,11 @@
|
||||
-- +goose Up
|
||||
|
||||
-- Allow completed_at to be set when a build is cancelled.
|
||||
-- (The UpdateBuildStatus query is updated in sqlc; no schema change needed for that.)
|
||||
|
||||
-- Add skip_pre_post flag: when true, the pre-build and post-build command phases
|
||||
-- are skipped for this build.
|
||||
ALTER TABLE template_builds ADD COLUMN skip_pre_post BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE template_builds DROP COLUMN skip_pre_post;
|
||||
19
db/migrations/20260409103357_add_channels.sql
Normal file
19
db/migrations/20260409103357_add_channels.sql
Normal file
@ -0,0 +1,19 @@
|
||||
-- +goose Up
|
||||
|
||||
CREATE TABLE channels (
|
||||
id UUID PRIMARY KEY,
|
||||
team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
provider TEXT NOT NULL,
|
||||
config JSONB NOT NULL DEFAULT '{}',
|
||||
event_types TEXT[] NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (team_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_channels_team ON channels(team_id);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP TABLE IF EXISTS channels;
|
||||
@ -0,0 +1,14 @@
|
||||
-- name: InsertAuditLog :exec
|
||||
INSERT INTO audit_logs (id, team_id, actor_type, actor_id, actor_name, resource_type, resource_id, action, scope, status, metadata)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11);
|
||||
|
||||
-- name: ListAuditLogs :many
|
||||
SELECT * FROM audit_logs
|
||||
WHERE team_id = $1
|
||||
AND scope = ANY($2::text[])
|
||||
AND (cardinality($3::text[]) = 0 OR resource_type = ANY($3::text[]))
|
||||
AND (cardinality($4::text[]) = 0 OR action = ANY($4::text[]))
|
||||
AND ($5::timestamptz IS NULL OR created_at < $5
|
||||
OR (created_at = $5 AND id < $6))
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT $7;
|
||||
|
||||
29
db/queries/channels.sql
Normal file
29
db/queries/channels.sql
Normal file
@ -0,0 +1,29 @@
|
||||
-- name: InsertChannel :one
|
||||
INSERT INTO channels (id, team_id, name, provider, config, event_types)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING *;
|
||||
|
||||
-- name: ListChannelsByTeam :many
|
||||
SELECT * FROM channels WHERE team_id = $1 ORDER BY created_at DESC;
|
||||
|
||||
-- name: GetChannelByTeam :one
|
||||
SELECT * FROM channels WHERE id = $1 AND team_id = $2;
|
||||
|
||||
-- name: UpdateChannel :one
|
||||
UPDATE channels SET name = $3, event_types = $4, updated_at = NOW()
|
||||
WHERE id = $1 AND team_id = $2
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateChannelConfig :one
|
||||
UPDATE channels SET config = $3, updated_at = NOW()
|
||||
WHERE id = $1 AND team_id = $2
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteChannelByTeam :exec
|
||||
DELETE FROM channels WHERE id = $1 AND team_id = $2;
|
||||
|
||||
-- name: ListChannelsForEvent :many
|
||||
SELECT * FROM channels
|
||||
WHERE team_id = $1
|
||||
AND sqlc.arg(event_type)::text = ANY(event_types)
|
||||
ORDER BY created_at;
|
||||
19
db/queries/host_refresh_tokens.sql
Normal file
19
db/queries/host_refresh_tokens.sql
Normal file
@ -0,0 +1,19 @@
|
||||
-- name: InsertHostRefreshToken :one
|
||||
INSERT INTO host_refresh_tokens (id, host_id, token_hash, expires_at)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetHostRefreshTokenByHash :one
|
||||
SELECT * FROM host_refresh_tokens
|
||||
WHERE token_hash = $1 AND revoked_at IS NULL AND expires_at > NOW();
|
||||
|
||||
-- name: RevokeHostRefreshToken :exec
|
||||
UPDATE host_refresh_tokens SET revoked_at = NOW() WHERE id = $1;
|
||||
|
||||
-- name: RevokeHostRefreshTokensByHost :exec
|
||||
UPDATE host_refresh_tokens SET revoked_at = NOW()
|
||||
WHERE host_id = $1 AND revoked_at IS NULL;
|
||||
|
||||
-- name: DeleteExpiredHostRefreshTokens :exec
|
||||
DELETE FROM host_refresh_tokens
|
||||
WHERE expires_at < NOW() OR revoked_at IS NOT NULL;
|
||||
@ -20,16 +20,25 @@ SELECT * FROM hosts WHERE status = $1 ORDER BY created_at DESC;
|
||||
|
||||
-- name: RegisterHost :execrows
|
||||
UPDATE hosts
|
||||
SET arch = $2,
|
||||
cpu_cores = $3,
|
||||
memory_mb = $4,
|
||||
disk_gb = $5,
|
||||
address = $6,
|
||||
status = 'online',
|
||||
SET arch = $2,
|
||||
cpu_cores = $3,
|
||||
memory_mb = $4,
|
||||
disk_gb = $5,
|
||||
address = $6,
|
||||
cert_fingerprint = $7,
|
||||
cert_expires_at = $8,
|
||||
status = 'online',
|
||||
last_heartbeat_at = NOW(),
|
||||
updated_at = NOW()
|
||||
updated_at = NOW()
|
||||
WHERE id = $1 AND status = 'pending';
|
||||
|
||||
-- name: UpdateHostCert :exec
|
||||
UPDATE hosts
|
||||
SET cert_fingerprint = $2,
|
||||
cert_expires_at = $3,
|
||||
updated_at = NOW()
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: UpdateHostStatus :exec
|
||||
UPDATE hosts SET status = $2, updated_at = NOW() WHERE id = $1;
|
||||
|
||||
@ -67,3 +76,19 @@ SELECT * FROM host_tokens WHERE host_id = $1 ORDER BY created_at DESC;
|
||||
|
||||
-- name: GetHostByTeam :one
|
||||
SELECT * FROM hosts WHERE id = $1 AND team_id = $2;
|
||||
|
||||
-- name: ListActiveHosts :many
|
||||
-- Returns all hosts that have completed registration (not pending/offline).
|
||||
SELECT * FROM hosts WHERE status NOT IN ('pending', 'offline') ORDER BY created_at;
|
||||
|
||||
-- name: UpdateHostHeartbeatAndStatus :execrows
|
||||
-- Updates last_heartbeat_at and transitions unreachable hosts back to online.
|
||||
-- Returns 0 if no host was found (deleted), which the caller treats as 404.
|
||||
UPDATE hosts
|
||||
SET last_heartbeat_at = NOW(),
|
||||
status = CASE WHEN status = 'unreachable' THEN 'online' ELSE status END,
|
||||
updated_at = NOW()
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: MarkHostUnreachable :exec
|
||||
UPDATE hosts SET status = 'unreachable', updated_at = NOW() WHERE id = $1;
|
||||
|
||||
68
db/queries/metrics.sql
Normal file
68
db/queries/metrics.sql
Normal file
@ -0,0 +1,68 @@
|
||||
-- name: InsertMetricsSnapshot :exec
|
||||
INSERT INTO sandbox_metrics_snapshots (team_id, running_count, vcpus_reserved, memory_mb_reserved)
|
||||
VALUES ($1, $2, $3, $4);
|
||||
|
||||
-- name: GetLiveMetrics :one
|
||||
-- Reads directly from sandboxes for accurate real-time current values.
|
||||
-- CPU reserved = running + starting only (paused VMs release CPU).
|
||||
-- RAM reserved = running + starting + sum(ceil(each_paused/2)) (per-VM ceiling).
|
||||
SELECT
|
||||
(COUNT(*) FILTER (WHERE status IN ('running', 'starting')))::INTEGER AS running_count,
|
||||
(COALESCE(SUM(vcpus) FILTER (WHERE status IN ('running', 'starting')), 0))::INTEGER AS vcpus_reserved,
|
||||
(COALESCE(SUM(memory_mb) FILTER (WHERE status IN ('running', 'starting')), 0)
|
||||
+ COALESCE(SUM(CEIL(memory_mb::NUMERIC / 2)) FILTER (WHERE status = 'paused'), 0))::INTEGER AS memory_mb_reserved
|
||||
FROM sandboxes
|
||||
WHERE team_id = $1;
|
||||
|
||||
-- name: GetPeakMetrics :one
|
||||
SELECT
|
||||
COALESCE(MAX(running_count), 0)::INTEGER AS peak_running_count,
|
||||
COALESCE(MAX(vcpus_reserved), 0)::INTEGER AS peak_vcpus,
|
||||
COALESCE(MAX(memory_mb_reserved), 0)::INTEGER AS peak_memory_mb
|
||||
FROM sandbox_metrics_snapshots
|
||||
WHERE team_id = $1
|
||||
AND sampled_at > NOW() - INTERVAL '30 days';
|
||||
|
||||
-- name: PruneOldMetrics :exec
|
||||
DELETE FROM sandbox_metrics_snapshots
|
||||
WHERE sampled_at < NOW() - INTERVAL '60 days';
|
||||
|
||||
-- name: InsertSandboxMetricPoint :exec
|
||||
INSERT INTO sandbox_metric_points (sandbox_id, tier, ts, cpu_pct, mem_bytes, disk_bytes)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
ON CONFLICT (sandbox_id, tier, ts) DO NOTHING;
|
||||
|
||||
-- name: GetSandboxMetricPoints :many
|
||||
SELECT ts, cpu_pct, mem_bytes, disk_bytes
|
||||
FROM sandbox_metric_points
|
||||
WHERE sandbox_id = $1 AND tier = $2 AND ts >= $3
|
||||
ORDER BY ts ASC;
|
||||
|
||||
-- name: DeleteSandboxMetricPoints :exec
|
||||
DELETE FROM sandbox_metric_points
|
||||
WHERE sandbox_id = $1;
|
||||
|
||||
-- name: DeleteSandboxMetricPointsByTier :exec
|
||||
DELETE FROM sandbox_metric_points
|
||||
WHERE sandbox_id = $1 AND tier = $2;
|
||||
|
||||
-- name: PruneSandboxMetricPoints :exec
|
||||
-- Remove metric points older than 30 days for destroyed sandboxes.
|
||||
DELETE FROM sandbox_metric_points
|
||||
WHERE ts < EXTRACT(EPOCH FROM NOW() - INTERVAL '30 days')::BIGINT;
|
||||
|
||||
-- name: SampleSandboxMetrics :many
|
||||
-- Aggregates per-team resource usage from the live sandboxes table.
|
||||
-- Groups by all teams that have any sandbox row (including stopped) so that
|
||||
-- zero-value snapshots are recorded when all capsules are stopped, keeping the
|
||||
-- time-series charts continuous rather than trailing off into empty space.
|
||||
-- CPU reserved = running + starting only (paused VMs release CPU).
|
||||
-- RAM reserved = running + starting + sum(ceil(each_paused/2)) (per-VM ceiling).
|
||||
SELECT
|
||||
team_id,
|
||||
(COUNT(*) FILTER (WHERE status IN ('running', 'starting')))::INTEGER AS running_count,
|
||||
(COALESCE(SUM(vcpus) FILTER (WHERE status IN ('running', 'starting')), 0))::INTEGER AS vcpus_reserved,
|
||||
(COALESCE(SUM(memory_mb) FILTER (WHERE status IN ('running', 'starting')), 0)
|
||||
+ COALESCE(SUM(CEIL(memory_mb::NUMERIC / 2)) FILTER (WHERE status = 'paused'), 0))::INTEGER AS memory_mb_reserved
|
||||
FROM sandboxes
|
||||
GROUP BY team_id;
|
||||
@ -1,6 +1,6 @@
|
||||
-- name: InsertSandbox :one
|
||||
INSERT INTO sandboxes (id, team_id, host_id, template, status, vcpus, memory_mb, timeout_sec)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
INSERT INTO sandboxes (id, team_id, host_id, template, status, vcpus, memory_mb, timeout_sec, disk_size_mb, template_id, template_team_id)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetSandbox :one
|
||||
@ -9,6 +9,14 @@ SELECT * FROM sandboxes WHERE id = $1;
|
||||
-- name: GetSandboxByTeam :one
|
||||
SELECT * FROM sandboxes WHERE id = $1 AND team_id = $2;
|
||||
|
||||
-- name: GetSandboxProxyTarget :one
|
||||
-- Returns the sandbox status and its host's address in one query.
|
||||
-- Used by SandboxProxyWrapper to avoid two round-trips.
|
||||
SELECT s.status, h.address AS host_address
|
||||
FROM sandboxes s
|
||||
JOIN hosts h ON h.id = s.host_id
|
||||
WHERE s.id = $1 AND s.team_id = $2;
|
||||
|
||||
-- name: ListSandboxes :many
|
||||
SELECT * FROM sandboxes ORDER BY created_at DESC;
|
||||
|
||||
@ -50,4 +58,26 @@ WHERE id = $1;
|
||||
UPDATE sandboxes
|
||||
SET status = $2,
|
||||
last_updated = NOW()
|
||||
WHERE id = ANY($1::text[]);
|
||||
WHERE id = ANY($1::uuid[]);
|
||||
|
||||
-- name: ListActiveSandboxesByTeam :many
|
||||
SELECT * FROM sandboxes
|
||||
WHERE team_id = $1 AND status IN ('running', 'paused', 'starting')
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
-- name: MarkSandboxesMissingByHost :exec
|
||||
-- Called when the host monitor marks a host unreachable.
|
||||
-- Marks running/starting/pending sandboxes on that host as 'missing' so users see
|
||||
-- the sandbox is not currently reachable, without permanently losing the record.
|
||||
UPDATE sandboxes
|
||||
SET status = 'missing',
|
||||
last_updated = NOW()
|
||||
WHERE host_id = $1 AND status IN ('running', 'starting', 'pending');
|
||||
|
||||
-- name: BulkRestoreRunning :exec
|
||||
-- Called by the reconciler when a host comes back online and its sandboxes are
|
||||
-- confirmed alive. Restores only sandboxes that are in 'missing' state.
|
||||
UPDATE sandboxes
|
||||
SET status = 'running',
|
||||
last_updated = NOW()
|
||||
WHERE id = ANY($1::uuid[]) AND status = 'missing';
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
-- name: InsertTeam :one
|
||||
INSERT INTO teams (id, name)
|
||||
VALUES ($1, $2)
|
||||
INSERT INTO teams (id, name, slug)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetTeam :one
|
||||
@ -13,14 +13,43 @@ VALUES ($1, $2, $3, $4);
|
||||
-- name: GetDefaultTeamForUser :one
|
||||
SELECT t.* FROM teams t
|
||||
JOIN users_teams ut ON ut.team_id = t.id
|
||||
WHERE ut.user_id = $1 AND ut.is_default = TRUE
|
||||
WHERE ut.user_id = $1 AND ut.is_default = TRUE AND t.deleted_at IS NULL
|
||||
LIMIT 1;
|
||||
|
||||
-- name: SetTeamBYOC :exec
|
||||
UPDATE teams SET is_byoc = $2 WHERE id = $1;
|
||||
|
||||
-- name: GetBYOCTeams :many
|
||||
SELECT * FROM teams WHERE is_byoc = TRUE ORDER BY created_at;
|
||||
SELECT * FROM teams WHERE is_byoc = TRUE AND deleted_at IS NULL ORDER BY created_at;
|
||||
|
||||
-- name: GetTeamMembership :one
|
||||
SELECT * FROM users_teams WHERE user_id = $1 AND team_id = $2;
|
||||
|
||||
-- name: UpdateTeamName :exec
|
||||
UPDATE teams SET name = $2 WHERE id = $1 AND deleted_at IS NULL;
|
||||
|
||||
-- name: SoftDeleteTeam :exec
|
||||
UPDATE teams SET deleted_at = NOW() WHERE id = $1;
|
||||
|
||||
-- name: GetTeamBySlug :one
|
||||
SELECT * FROM teams WHERE slug = $1 AND deleted_at IS NULL;
|
||||
|
||||
-- name: GetTeamsForUser :many
|
||||
SELECT t.id, t.name, t.slug, t.is_byoc, t.created_at, t.deleted_at, ut.role
|
||||
FROM teams t
|
||||
JOIN users_teams ut ON ut.team_id = t.id
|
||||
WHERE ut.user_id = $1 AND t.deleted_at IS NULL
|
||||
ORDER BY ut.created_at;
|
||||
|
||||
-- name: GetTeamMembers :many
|
||||
SELECT u.id, u.name, u.email, ut.role, ut.created_at AS joined_at
|
||||
FROM users_teams ut
|
||||
JOIN users u ON u.id = ut.user_id
|
||||
WHERE ut.team_id = $1
|
||||
ORDER BY ut.created_at;
|
||||
|
||||
-- name: UpdateMemberRole :exec
|
||||
UPDATE users_teams SET role = $3 WHERE team_id = $1 AND user_id = $2;
|
||||
|
||||
-- name: DeleteTeamMember :exec
|
||||
DELETE FROM users_teams WHERE team_id = $1 AND user_id = $2;
|
||||
|
||||
33
db/queries/template_builds.sql
Normal file
33
db/queries/template_builds.sql
Normal file
@ -0,0 +1,33 @@
|
||||
-- name: InsertTemplateBuild :one
|
||||
INSERT INTO template_builds (id, name, base_template, recipe, healthcheck, vcpus, memory_mb, status, total_steps, template_id, team_id, skip_pre_post)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, 'pending', $8, $9, $10, $11)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetTemplateBuild :one
|
||||
SELECT * FROM template_builds WHERE id = $1;
|
||||
|
||||
-- name: ListTemplateBuilds :many
|
||||
SELECT * FROM template_builds ORDER BY created_at DESC;
|
||||
|
||||
-- name: UpdateBuildStatus :one
|
||||
UPDATE template_builds
|
||||
SET status = $2,
|
||||
started_at = CASE WHEN $2 = 'running' AND started_at IS NULL THEN NOW() ELSE started_at END,
|
||||
completed_at = CASE WHEN $2 IN ('success', 'failed', 'cancelled') THEN NOW() ELSE completed_at END
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateBuildProgress :exec
|
||||
UPDATE template_builds
|
||||
SET current_step = $2, logs = $3
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: UpdateBuildSandbox :exec
|
||||
UPDATE template_builds
|
||||
SET sandbox_id = $2, host_id = $3
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: UpdateBuildError :exec
|
||||
UPDATE template_builds
|
||||
SET error = $2, status = 'failed', completed_at = NOW()
|
||||
WHERE id = $1;
|
||||
@ -1,13 +1,22 @@
|
||||
-- name: InsertTemplate :one
|
||||
INSERT INTO templates (name, type, vcpus, memory_mb, size_bytes, team_id)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
INSERT INTO templates (id, name, type, vcpus, memory_mb, size_bytes, team_id)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetTemplate :one
|
||||
SELECT * FROM templates WHERE name = $1;
|
||||
SELECT * FROM templates WHERE id = $1;
|
||||
|
||||
-- name: GetTemplateByTeam :one
|
||||
SELECT * FROM templates WHERE name = $1 AND team_id = $2;
|
||||
-- Platform templates (team_id = 00000000-...) are visible to all teams.
|
||||
SELECT * FROM templates WHERE name = $1 AND (team_id = $2 OR team_id = '00000000-0000-0000-0000-000000000000');
|
||||
|
||||
-- name: GetTemplateByName :one
|
||||
-- Look up a template by team_id and name (exact team match, no global fallback).
|
||||
SELECT * FROM templates WHERE team_id = $1 AND name = $2;
|
||||
|
||||
-- name: GetPlatformTemplateByName :one
|
||||
-- Check if a global (platform) template exists with the given name.
|
||||
SELECT * FROM templates WHERE team_id = '00000000-0000-0000-0000-000000000000' AND name = $1;
|
||||
|
||||
-- name: ListTemplates :many
|
||||
SELECT * FROM templates ORDER BY created_at DESC;
|
||||
@ -16,13 +25,23 @@ SELECT * FROM templates ORDER BY created_at DESC;
|
||||
SELECT * FROM templates WHERE type = $1 ORDER BY created_at DESC;
|
||||
|
||||
-- name: ListTemplatesByTeam :many
|
||||
SELECT * FROM templates WHERE team_id = $1 ORDER BY created_at DESC;
|
||||
-- Platform templates are visible to all teams.
|
||||
SELECT * FROM templates WHERE (team_id = $1 OR team_id = '00000000-0000-0000-0000-000000000000') ORDER BY created_at DESC;
|
||||
|
||||
-- name: ListTemplatesByTeamAndType :many
|
||||
SELECT * FROM templates WHERE team_id = $1 AND type = $2 ORDER BY created_at DESC;
|
||||
-- Platform templates are visible to all teams.
|
||||
SELECT * FROM templates WHERE (team_id = $1 OR team_id = '00000000-0000-0000-0000-000000000000') AND type = $2 ORDER BY created_at DESC;
|
||||
|
||||
-- name: DeleteTemplate :exec
|
||||
DELETE FROM templates WHERE name = $1;
|
||||
DELETE FROM templates WHERE id = $1;
|
||||
|
||||
-- name: DeleteTemplateByTeam :exec
|
||||
DELETE FROM templates WHERE name = $1 AND team_id = $2;
|
||||
|
||||
-- name: DeleteTemplatesByTeam :exec
|
||||
-- Bulk delete all templates owned by a team (for team soft-delete cleanup).
|
||||
DELETE FROM templates WHERE team_id = $1;
|
||||
|
||||
-- name: ListTemplatesByTeamOnly :many
|
||||
-- List templates owned by a specific team (NOT including platform templates).
|
||||
SELECT * FROM templates WHERE team_id = $1 ORDER BY created_at DESC;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
-- name: InsertUser :one
|
||||
INSERT INTO users (id, email, password_hash)
|
||||
VALUES ($1, $2, $3)
|
||||
INSERT INTO users (id, email, password_hash, name)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetUserByEmail :one
|
||||
@ -10,8 +10,8 @@ SELECT * FROM users WHERE email = $1;
|
||||
SELECT * FROM users WHERE id = $1;
|
||||
|
||||
-- name: InsertUserOAuth :one
|
||||
INSERT INTO users (id, email)
|
||||
VALUES ($1, $2)
|
||||
INSERT INTO users (id, email, name)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING *;
|
||||
|
||||
-- name: SetUserAdmin :exec
|
||||
@ -34,3 +34,9 @@ SELECT * FROM admin_permissions WHERE user_id = $1 ORDER BY permission;
|
||||
SELECT EXISTS(
|
||||
SELECT 1 FROM admin_permissions WHERE user_id = $1 AND permission = $2
|
||||
) AS has_permission;
|
||||
|
||||
-- name: SearchUsersByEmailPrefix :many
|
||||
SELECT id, email FROM users WHERE email LIKE $1 || '%' ORDER BY email LIMIT 10;
|
||||
|
||||
-- name: UpdateUserName :exec
|
||||
UPDATE users SET name = $2, updated_at = NOW() WHERE id = $1;
|
||||
|
||||
Reference in New Issue
Block a user