forked from wrenn/wrenn
Switch database IDs from TEXT to native UUID
Consolidate 16 migrations into one with UUID columns for all entity
IDs. TEXT is kept only for polymorphic fields (audit_logs.actor_id,
resource_id) and template names. The id package now generates UUIDs
via google/uuid, with Format*/Parse* helpers for the prefixed wire
format (sb-{uuid}, usr-{uuid}, etc.). Auth context, services, and
handlers pass pgtype.UUID internally; conversion to/from prefixed
strings happens at API and RPC boundaries. Adds PlatformTeamID
(all-zeros UUID) for shared resources.
This commit is contained in:
@ -1,25 +1,236 @@
|
||||
-- +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,
|
||||
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;
|
||||
@ -1,17 +0,0 @@
|
||||
-- +goose Up
|
||||
|
||||
ALTER TABLE teams ADD COLUMN slug TEXT;
|
||||
ALTER TABLE teams ADD COLUMN deleted_at TIMESTAMPTZ;
|
||||
|
||||
-- Backfill slugs for existing teams using MD5 of their ID.
|
||||
-- MD5 returns 32 hex chars; take chars 1-6 and 7-12 to form a 6-6 slug.
|
||||
UPDATE teams SET slug = LEFT(MD5(id), 6) || '-' || SUBSTRING(MD5(id), 7, 6);
|
||||
|
||||
ALTER TABLE teams ALTER COLUMN slug SET NOT NULL;
|
||||
CREATE UNIQUE INDEX idx_teams_slug ON teams(slug);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP INDEX idx_teams_slug;
|
||||
ALTER TABLE teams DROP COLUMN deleted_at;
|
||||
ALTER TABLE teams DROP COLUMN slug;
|
||||
@ -1,5 +0,0 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE users ADD COLUMN name TEXT NOT NULL DEFAULT '';
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE users DROP COLUMN name;
|
||||
@ -1,19 +0,0 @@
|
||||
-- +goose Up
|
||||
|
||||
-- Refresh tokens for host agent JWT rotation.
|
||||
-- Hosts exchange a refresh token for a new short-lived JWT + new refresh token (rotation).
|
||||
-- Refresh tokens expire after 60 days; hosts must re-register with a new one-time token after that.
|
||||
CREATE TABLE host_refresh_tokens (
|
||||
id TEXT PRIMARY KEY,
|
||||
host_id TEXT NOT NULL REFERENCES hosts(id) ON DELETE CASCADE,
|
||||
token_hash TEXT NOT NULL UNIQUE, -- SHA-256 hex of the opaque token
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
revoked_at TIMESTAMPTZ -- NULL = active; set on rotation or host delete
|
||||
);
|
||||
|
||||
CREATE INDEX idx_host_refresh_tokens_host ON host_refresh_tokens(host_id);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP TABLE host_refresh_tokens;
|
||||
@ -1,28 +0,0 @@
|
||||
-- +goose Up
|
||||
|
||||
CREATE TABLE audit_logs (
|
||||
id TEXT PRIMARY KEY,
|
||||
team_id TEXT NOT NULL,
|
||||
actor_type TEXT NOT NULL, -- 'user', 'api_key', 'system'
|
||||
actor_id TEXT, -- user_id or api_key_id; NULL for system
|
||||
actor_name TEXT, -- display name snapshotted at write time; NULL for system
|
||||
resource_type TEXT NOT NULL, -- 'sandbox', 'snapshot', 'team', 'api_key', 'member', 'host'
|
||||
resource_id TEXT, -- primary ID of the affected resource; NULL when not applicable
|
||||
action TEXT NOT NULL, -- 'create', 'pause', 'resume', 'destroy', 'delete', 'rename',
|
||||
-- 'revoke', 'add', 'remove', 'leave', 'role_update',
|
||||
-- 'marked_down', 'marked_up'
|
||||
scope TEXT NOT NULL, -- 'team' or 'admin'
|
||||
status TEXT NOT NULL, -- 'success', 'info', 'warning', 'error'
|
||||
metadata JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Primary access pattern: team feed sorted newest-first with cursor pagination.
|
||||
CREATE INDEX idx_audit_logs_team_time ON audit_logs (team_id, created_at DESC);
|
||||
|
||||
-- Secondary index: filtered by resource_type and action within a team.
|
||||
CREATE INDEX idx_audit_logs_team_resource ON audit_logs (team_id, resource_type, action, created_at DESC);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP TABLE audit_logs;
|
||||
@ -1,18 +0,0 @@
|
||||
-- +goose Up
|
||||
|
||||
CREATE TABLE sandbox_metrics_snapshots (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
team_id TEXT NOT NULL,
|
||||
sampled_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
running_count INTEGER NOT NULL,
|
||||
vcpus_reserved INTEGER NOT NULL,
|
||||
memory_mb_reserved INTEGER NOT NULL
|
||||
);
|
||||
|
||||
-- All queries filter on team_id first then range-scan sampled_at.
|
||||
CREATE INDEX idx_metrics_snapshots_team_time
|
||||
ON sandbox_metrics_snapshots (team_id, sampled_at DESC);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP TABLE sandbox_metrics_snapshots;
|
||||
@ -1,16 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE sandbox_metric_points (
|
||||
sandbox_id TEXT 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);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS sandbox_metric_points;
|
||||
@ -1,25 +0,0 @@
|
||||
-- +goose Up
|
||||
|
||||
CREATE TABLE template_builds (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
base_template TEXT NOT NULL DEFAULT 'minimal',
|
||||
recipe JSONB NOT NULL DEFAULT '[]',
|
||||
healthcheck TEXT,
|
||||
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,
|
||||
sandbox_id TEXT,
|
||||
host_id TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP TABLE template_builds;
|
||||
Reference in New Issue
Block a user