наблюдаемость: завершить базовый контур Community
Добавить структурированные журналы, метрики, трассировку и безопасный канал критических ошибок. Усилить границы рантайма, тесты, проверку зависимостей и сценарии развёртывания.
This commit is contained in:
@@ -1,6 +1,61 @@
|
||||
use sqlx::{PgPool, query};
|
||||
use sqlx::{PgPool, Postgres, Row, Transaction, query};
|
||||
|
||||
const CORE_MIGRATION_LOCK_ID: i64 = 0x43_52_41_4E_4B;
|
||||
const BASELINE_VERSION: i32 = 1;
|
||||
const BASELINE_CHECKSUM: &str = "crank-community-baseline-v1";
|
||||
|
||||
pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
let mut transaction = pool.begin().await?;
|
||||
query("select pg_advisory_xact_lock($1)")
|
||||
.bind(CORE_MIGRATION_LOCK_ID)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
query(
|
||||
"create table if not exists __crank_core_migrations (
|
||||
version integer primary key,
|
||||
description text not null,
|
||||
checksum text not null,
|
||||
applied_at timestamptz not null default now()
|
||||
)",
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
let applied = query(
|
||||
"select version, checksum
|
||||
from __crank_core_migrations
|
||||
order by version",
|
||||
)
|
||||
.fetch_all(&mut *transaction)
|
||||
.await?;
|
||||
for row in &applied {
|
||||
let version = row.try_get::<i32, _>("version")?;
|
||||
let checksum = row.try_get::<String, _>("checksum")?;
|
||||
if version != BASELINE_VERSION || checksum != BASELINE_CHECKSUM {
|
||||
return Err(sqlx::Error::Protocol(format!(
|
||||
"unsupported or modified core migration: version={version}, checksum={checksum}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
if applied.is_empty() {
|
||||
apply_baseline(&mut transaction).await?;
|
||||
query(
|
||||
"insert into __crank_core_migrations (version, description, checksum)
|
||||
values ($1, $2, $3)",
|
||||
)
|
||||
.bind(BASELINE_VERSION)
|
||||
.bind("community baseline")
|
||||
.bind(BASELINE_CHECKSUM)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
}
|
||||
|
||||
transaction.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn apply_baseline(transaction: &mut Transaction<'_, Postgres>) -> Result<(), sqlx::Error> {
|
||||
query(
|
||||
"create table if not exists workspaces (
|
||||
id text primary key,
|
||||
@@ -12,7 +67,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
updated_at timestamptz not null
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -25,11 +80,11 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
created_at timestamptz not null
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query("alter table users add column if not exists password_hash text null")
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -48,7 +103,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
)
|
||||
on conflict (id) do nothing",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -60,7 +115,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
primary key (workspace_id, user_id)
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -75,14 +130,14 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
created_at timestamptz not null
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.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)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -105,7 +160,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
)
|
||||
on conflict (id) do nothing",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -122,7 +177,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
)
|
||||
on conflict (workspace_id, user_id) do nothing",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -137,7 +192,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
created_at timestamptz not null
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -158,29 +213,29 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
allowed_origins_json jsonb not null default '[]'::jsonb
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
"create unique index if not exists platform_api_keys_workspace_name_idx on platform_api_keys(workspace_id, name)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query("alter table platform_api_keys add column if not exists agent_id text null")
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query(
|
||||
"alter table platform_api_keys add column if not exists key_kind text not null default 'mcp_client'",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query("alter table platform_api_keys add column if not exists expires_at timestamptz null")
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query(
|
||||
"alter table platform_api_keys add column if not exists allowed_origins_json jsonb not null default '[]'::jsonb",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -203,7 +258,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
)
|
||||
on conflict (id) do nothing",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -223,35 +278,35 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
published_at timestamptz null
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query("alter table operations add column if not exists workspace_id text null references workspaces(id) on delete cascade")
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query(
|
||||
"alter table operations add column if not exists category text not null default 'general'",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query(
|
||||
"alter table operations add column if not exists security_level text not null default 'standard'",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query("update operations set workspace_id = 'ws_default' where workspace_id is null")
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query("alter table operations alter column workspace_id set not null")
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query("alter table operations drop constraint if exists operations_name_key")
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query(
|
||||
"create unique index if not exists operations_workspace_name_idx on operations(workspace_id, name)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -276,11 +331,11 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
primary key (operation_id, version)
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query("alter table operation_versions add column if not exists wizard_state_json jsonb null")
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -292,7 +347,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
foreign key (operation_id, version) references operation_versions(operation_id, version) on delete cascade
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -308,7 +363,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
foreign key (operation_id, version) references operation_versions(operation_id, version) on delete cascade
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -324,7 +379,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
foreign key (operation_id, version) references operation_versions(operation_id, version) on delete cascade
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -338,25 +393,25 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
updated_at timestamptz not null
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query("alter table auth_profiles add column if not exists workspace_id text null references workspaces(id) on delete cascade")
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query("update auth_profiles set workspace_id = 'ws_default' where workspace_id is null")
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query("alter table auth_profiles alter column workspace_id set not null")
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query("alter table auth_profiles drop constraint if exists auth_profiles_name_key")
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query(
|
||||
"create unique index if not exists auth_profiles_workspace_name_idx on auth_profiles(workspace_id, name)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -371,17 +426,17 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
updated_at timestamptz not null
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query(
|
||||
"create unique index if not exists workspace_upstreams_workspace_name_idx on workspace_upstreams(workspace_id, name)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query(
|
||||
"create unique index if not exists workspace_upstreams_workspace_base_auth_idx on workspace_upstreams(workspace_id, base_url, coalesce(auth_profile_id, ''))",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query(
|
||||
"insert into workspace_upstreams (
|
||||
@@ -411,7 +466,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
and wu.name = 'Frankfurter'
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -427,13 +482,13 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
updated_at timestamptz not null
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
"create unique index if not exists secrets_workspace_name_idx on secrets(workspace_id, name)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -447,7 +502,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
primary key (secret_id, version)
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -464,7 +519,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
finished_at timestamptz null
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -483,7 +538,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
finished_at timestamptz null
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -501,13 +556,13 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
published_at timestamptz null
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
"create unique index if not exists agents_workspace_slug_idx on agents(workspace_id, slug)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -521,7 +576,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
primary key (agent_id, version)
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -538,13 +593,13 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
foreign key (operation_id, operation_version) references operation_versions(operation_id, version) on delete cascade
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.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)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -556,7 +611,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
foreign key (agent_id, version) references agent_versions(agent_id, version) on delete cascade
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -577,36 +632,30 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
decision_note text null
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query("alter table approval_requests drop column if exists confirmation_title")
|
||||
.execute(pool)
|
||||
.await?;
|
||||
query("alter table approval_requests drop column if exists confirmation_body")
|
||||
.execute(pool)
|
||||
.await?;
|
||||
query("alter table approval_requests add column if not exists execution_started_at timestamptz null")
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query("alter table approval_requests add column if not exists execution_attempts integer not null default 0")
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query("alter table approval_requests add column if not exists request_fingerprint text null")
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query(
|
||||
"create unique index if not exists approval_requests_pending_fingerprint_idx
|
||||
on approval_requests(agent_id, operation_id, operation_version, request_fingerprint)
|
||||
where status = 'pending' and request_fingerprint is not null",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
query(
|
||||
"create index if not exists approval_requests_agent_status_idx
|
||||
on approval_requests(workspace_id, agent_id, status, expires_at)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -629,25 +678,25 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
created_at timestamptz not null
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
"create index if not exists invocation_logs_workspace_created_idx on invocation_logs(workspace_id, created_at desc)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.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)
|
||||
.execute(&mut **transaction)
|
||||
.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)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
query(
|
||||
@@ -665,7 +714,7 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||
updated_at timestamptz not null
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user