600 lines
19 KiB
Rust
600 lines
19 KiB
Rust
use sqlx::{PgPool, query};
|
|
|
|
pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
|
query(
|
|
"create table if not exists workspaces (
|
|
id text primary key,
|
|
slug text not null unique,
|
|
display_name text not null,
|
|
status text not null,
|
|
settings_json jsonb not null default '{}'::jsonb,
|
|
created_at timestamptz not null,
|
|
updated_at timestamptz not null
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists users (
|
|
id text primary key,
|
|
email text not null unique,
|
|
display_name text not null,
|
|
password_hash text null,
|
|
status text not null,
|
|
created_at timestamptz not null
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query("alter table users add column if not exists password_hash text null")
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"insert into users (
|
|
id,
|
|
email,
|
|
display_name,
|
|
status,
|
|
created_at
|
|
) values (
|
|
'user_default_owner',
|
|
'owner@crank.local',
|
|
'Workspace Owner',
|
|
'active',
|
|
now()
|
|
)
|
|
on conflict (id) do nothing",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists memberships (
|
|
workspace_id text not null references workspaces(id) on delete cascade,
|
|
user_id text not null references users(id) on delete cascade,
|
|
role text not null,
|
|
created_at timestamptz not null,
|
|
primary key (workspace_id, user_id)
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists user_sessions (
|
|
id text primary key,
|
|
user_id text not null references users(id) on delete cascade,
|
|
current_workspace_id text null references workspaces(id) on delete set null,
|
|
secret_hash text not null,
|
|
status text not null,
|
|
expires_at timestamptz not null,
|
|
last_seen_at timestamptz null,
|
|
created_at timestamptz not null
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"alter table user_sessions
|
|
add column if not exists current_workspace_id text null references workspaces(id) on delete set null",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"insert into workspaces (
|
|
id,
|
|
slug,
|
|
display_name,
|
|
status,
|
|
settings_json,
|
|
created_at,
|
|
updated_at
|
|
) values (
|
|
'ws_default',
|
|
'default',
|
|
'Default Workspace',
|
|
'active',
|
|
'{}'::jsonb,
|
|
now(),
|
|
now()
|
|
)
|
|
on conflict (id) do nothing",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"insert into memberships (
|
|
workspace_id,
|
|
user_id,
|
|
role,
|
|
created_at
|
|
) values (
|
|
'ws_default',
|
|
'user_default_owner',
|
|
'owner',
|
|
now()
|
|
)
|
|
on conflict (workspace_id, user_id) do nothing",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists invitation_tokens (
|
|
id text primary key,
|
|
workspace_id text not null references workspaces(id) on delete cascade,
|
|
email text not null,
|
|
role text not null,
|
|
status text not null,
|
|
token_hash text not null,
|
|
expires_at timestamptz not null,
|
|
created_at timestamptz not null
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists platform_api_keys (
|
|
id text primary key,
|
|
workspace_id text not null references workspaces(id) on delete cascade,
|
|
agent_id text null,
|
|
name text not null,
|
|
prefix text not null,
|
|
secret_hash text not null,
|
|
scopes_json jsonb not null,
|
|
status text not null,
|
|
created_at timestamptz not null,
|
|
last_used_at timestamptz null,
|
|
revoked_at timestamptz null
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create unique index if not exists platform_api_keys_workspace_name_idx on platform_api_keys(workspace_id, name)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
query("alter table platform_api_keys add column if not exists agent_id text null")
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"insert into workspaces (
|
|
id,
|
|
slug,
|
|
display_name,
|
|
status,
|
|
settings_json,
|
|
created_at,
|
|
updated_at
|
|
) values (
|
|
'ws_default',
|
|
'default',
|
|
'Default Workspace',
|
|
'active',
|
|
'{}'::jsonb,
|
|
now(),
|
|
now()
|
|
)
|
|
on conflict (id) do nothing",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists operations (
|
|
id text primary key,
|
|
workspace_id text null references workspaces(id) on delete cascade,
|
|
name text not null,
|
|
display_name text not null,
|
|
category text not null default 'general',
|
|
protocol text not null,
|
|
security_level text not null default 'standard',
|
|
status text not null,
|
|
current_draft_version integer not null default 1,
|
|
latest_published_version integer null,
|
|
created_at timestamptz not null,
|
|
updated_at timestamptz not null,
|
|
published_at timestamptz null
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query("alter table operations add column if not exists workspace_id text null references workspaces(id) on delete cascade")
|
|
.execute(pool)
|
|
.await?;
|
|
query(
|
|
"alter table operations add column if not exists category text not null default 'general'",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
query(
|
|
"alter table operations add column if not exists security_level text not null default 'standard'",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
query("update operations set workspace_id = 'ws_default' where workspace_id is null")
|
|
.execute(pool)
|
|
.await?;
|
|
query("alter table operations alter column workspace_id set not null")
|
|
.execute(pool)
|
|
.await?;
|
|
query("alter table operations drop constraint if exists operations_name_key")
|
|
.execute(pool)
|
|
.await?;
|
|
query(
|
|
"create unique index if not exists operations_workspace_name_idx on operations(workspace_id, name)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists operation_versions (
|
|
operation_id text not null references operations(id) on delete cascade,
|
|
version integer not null,
|
|
status text not null,
|
|
target_json jsonb not null,
|
|
input_schema_json jsonb not null,
|
|
output_schema_json jsonb not null,
|
|
input_mapping_json jsonb not null,
|
|
output_mapping_json jsonb not null,
|
|
execution_config_json jsonb not null,
|
|
tool_description_json jsonb not null,
|
|
samples_json jsonb null,
|
|
generated_draft_json jsonb null,
|
|
config_export_json jsonb null,
|
|
change_note text null,
|
|
created_at timestamptz not null,
|
|
created_by text null,
|
|
primary key (operation_id, version)
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists published_operations (
|
|
operation_id text primary key references operations(id) on delete cascade,
|
|
version integer not null,
|
|
published_at timestamptz not null,
|
|
published_by text null,
|
|
foreign key (operation_id, version) references operation_versions(operation_id, version) on delete cascade
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists operation_samples (
|
|
id text primary key,
|
|
operation_id text not null references operations(id) on delete cascade,
|
|
version integer not null,
|
|
sample_kind text not null,
|
|
storage_ref text not null,
|
|
content_type text not null,
|
|
file_name text null,
|
|
created_at timestamptz not null,
|
|
foreign key (operation_id, version) references operation_versions(operation_id, version) on delete cascade
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists descriptors (
|
|
id text primary key,
|
|
operation_id text null references operations(id) on delete cascade,
|
|
version integer null,
|
|
descriptor_kind text not null,
|
|
storage_ref text not null,
|
|
source_name text null,
|
|
package_index_json jsonb null,
|
|
created_at timestamptz not null,
|
|
foreign key (operation_id, version) references operation_versions(operation_id, version) on delete cascade
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists auth_profiles (
|
|
id text primary key,
|
|
workspace_id text null references workspaces(id) on delete cascade,
|
|
name text not null,
|
|
kind text not null,
|
|
config_json jsonb not null,
|
|
created_at timestamptz not null,
|
|
updated_at timestamptz not null
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query("alter table auth_profiles add column if not exists workspace_id text null references workspaces(id) on delete cascade")
|
|
.execute(pool)
|
|
.await?;
|
|
query("update auth_profiles set workspace_id = 'ws_default' where workspace_id is null")
|
|
.execute(pool)
|
|
.await?;
|
|
query("alter table auth_profiles alter column workspace_id set not null")
|
|
.execute(pool)
|
|
.await?;
|
|
query("alter table auth_profiles drop constraint if exists auth_profiles_name_key")
|
|
.execute(pool)
|
|
.await?;
|
|
query(
|
|
"create unique index if not exists auth_profiles_workspace_name_idx on auth_profiles(workspace_id, name)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists secrets (
|
|
id text primary key,
|
|
workspace_id text not null references workspaces(id) on delete cascade,
|
|
name text not null,
|
|
kind text not null,
|
|
status text not null,
|
|
current_version integer not null,
|
|
last_used_at timestamptz null,
|
|
created_at timestamptz not null,
|
|
updated_at timestamptz not null
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create unique index if not exists secrets_workspace_name_idx on secrets(workspace_id, name)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists secret_versions (
|
|
secret_id text not null references secrets(id) on delete cascade,
|
|
version integer not null,
|
|
ciphertext text not null,
|
|
key_version text not null,
|
|
created_at timestamptz not null,
|
|
created_by text null references users(id) on delete set null,
|
|
primary key (secret_id, version)
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists yaml_import_jobs (
|
|
id text primary key,
|
|
source_sample_id text null references operation_samples(id) on delete set null,
|
|
status text not null,
|
|
format_version text not null,
|
|
mode text not null,
|
|
result_operation_id text null references operations(id) on delete set null,
|
|
result_version integer null,
|
|
error_text text null,
|
|
created_at timestamptz not null,
|
|
finished_at timestamptz null
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists agents (
|
|
id text primary key,
|
|
workspace_id text not null references workspaces(id) on delete cascade,
|
|
slug text not null,
|
|
display_name text not null,
|
|
description text not null,
|
|
status text not null,
|
|
current_draft_version integer not null default 1,
|
|
latest_published_version integer null,
|
|
created_at timestamptz not null,
|
|
updated_at timestamptz not null,
|
|
published_at timestamptz null
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create unique index if not exists agents_workspace_slug_idx on agents(workspace_id, slug)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists agent_versions (
|
|
agent_id text not null references agents(id) on delete cascade,
|
|
version integer not null,
|
|
status text not null,
|
|
instructions_json jsonb not null,
|
|
tool_selection_policy_json jsonb not null,
|
|
created_at timestamptz not null,
|
|
primary key (agent_id, version)
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists agent_operation_bindings (
|
|
agent_id text not null references agents(id) on delete cascade,
|
|
agent_version integer not null,
|
|
operation_id text not null references operations(id) on delete cascade,
|
|
operation_version integer not null,
|
|
tool_name text not null,
|
|
tool_title text not null,
|
|
tool_description_override text null,
|
|
enabled boolean not null default true,
|
|
foreign key (agent_id, agent_version) references agent_versions(agent_id, version) on delete cascade,
|
|
foreign key (operation_id, operation_version) references operation_versions(operation_id, version) on delete cascade
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create unique index if not exists agent_bindings_tool_name_idx on agent_operation_bindings(agent_id, agent_version, tool_name)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists published_agents (
|
|
agent_id text primary key references agents(id) on delete cascade,
|
|
version integer not null,
|
|
published_at timestamptz not null,
|
|
published_by text null,
|
|
foreign key (agent_id, version) references agent_versions(agent_id, version) on delete cascade
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists invocation_logs (
|
|
id text primary key,
|
|
workspace_id text not null references workspaces(id) on delete cascade,
|
|
agent_id text null references agents(id) on delete set null,
|
|
operation_id text not null references operations(id) on delete cascade,
|
|
source text not null,
|
|
level text not null,
|
|
status text not null,
|
|
tool_name text not null,
|
|
message text not null,
|
|
request_id text null,
|
|
status_code integer null,
|
|
duration_ms bigint not null,
|
|
error_kind text null,
|
|
request_preview_json jsonb not null,
|
|
response_preview_json jsonb not null,
|
|
created_at timestamptz not null
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create index if not exists invocation_logs_workspace_created_idx on invocation_logs(workspace_id, created_at desc)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create index if not exists invocation_logs_workspace_operation_created_idx on invocation_logs(workspace_id, operation_id, created_at desc)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create index if not exists invocation_logs_workspace_agent_created_idx on invocation_logs(workspace_id, agent_id, created_at desc)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists usage_rollups (
|
|
workspace_id text not null references workspaces(id) on delete cascade,
|
|
agent_id text null references agents(id) on delete cascade,
|
|
operation_id text null references operations(id) on delete cascade,
|
|
period text not null,
|
|
calls_total bigint not null,
|
|
calls_ok bigint not null,
|
|
calls_error bigint not null,
|
|
p50_ms bigint not null,
|
|
p95_ms bigint not null,
|
|
p99_ms bigint not null,
|
|
updated_at timestamptz not null
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists stream_sessions (
|
|
id text primary key,
|
|
workspace_id text not null references workspaces(id) on delete cascade,
|
|
agent_id text null references agents(id) on delete set null,
|
|
operation_id text not null references operations(id) on delete cascade,
|
|
protocol text not null,
|
|
mode text not null,
|
|
status text not null,
|
|
cursor_json jsonb null,
|
|
state_json jsonb not null,
|
|
expires_at timestamptz not null,
|
|
last_poll_at timestamptz null,
|
|
created_at timestamptz not null,
|
|
closed_at timestamptz null
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create index if not exists stream_sessions_workspace_status_idx
|
|
on stream_sessions(workspace_id, status, created_at desc)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create index if not exists stream_sessions_expires_at_idx
|
|
on stream_sessions(expires_at)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create table if not exists async_jobs (
|
|
id text primary key,
|
|
workspace_id text not null references workspaces(id) on delete cascade,
|
|
agent_id text null references agents(id) on delete set null,
|
|
operation_id text not null references operations(id) on delete cascade,
|
|
status text not null,
|
|
progress_json jsonb not null,
|
|
result_json jsonb null,
|
|
error_json jsonb null,
|
|
expires_at timestamptz null,
|
|
last_poll_at timestamptz null,
|
|
created_at timestamptz not null,
|
|
updated_at timestamptz not null,
|
|
finished_at timestamptz null
|
|
)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query("alter table async_jobs add column if not exists last_poll_at timestamptz null")
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create index if not exists async_jobs_workspace_status_idx
|
|
on async_jobs(workspace_id, status, updated_at desc)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
query(
|
|
"create index if not exists async_jobs_expires_at_idx
|
|
on async_jobs(expires_at)",
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|