registry: add extension migration seam
This commit is contained in:
@@ -148,4 +148,5 @@ Progress:
|
||||
- Phase 0 / task `0.5`: введены `PolicyEngine`, `SessionActor`, `PolicyAction`, `PolicyScope`, `PolicyDecision` и default `OwnerOnlyPolicyEngine` в public seam `crank_core::ext::access`
|
||||
- Phase 0 / task `0.6`: введены `AuditSink`, `NoopAuditSink`, `AuditEventId` и базовые audit-типы в public seam `crank_core::ext::audit`
|
||||
- Phase 0 / task `0.7`: введены `CapabilityProfile` и `CommunityCapabilityProfile`, а `admin-api` capability payload теперь собирается через public seam вместо локального literal
|
||||
- Phase 0 / task `0.13`: введены `RegistryExtension`, `ExtensionMigration` и `apply_extension_migrations(...)` в `crank-registry` как отдельный public seam для additive private migrations
|
||||
- backward-compatible alias `CommunityMachineCredentialVerifier` сохранен, поведение Community не изменено
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use sqlx::{PgPool, query};
|
||||
|
||||
use crate::RegistryError;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct ExtensionMigration {
|
||||
pub version: u32,
|
||||
pub sql: &'static str,
|
||||
}
|
||||
|
||||
pub trait RegistryExtension: Send + Sync {
|
||||
fn name(&self) -> &str;
|
||||
fn migrations(&self) -> &[ExtensionMigration];
|
||||
}
|
||||
|
||||
pub async fn apply_extension_migrations(
|
||||
pool: &PgPool,
|
||||
extensions: &[Arc<dyn RegistryExtension>],
|
||||
) -> Result<(), RegistryError> {
|
||||
query(
|
||||
"create table if not exists __crank_ext_migrations (
|
||||
extension_name text not null,
|
||||
version integer not null,
|
||||
applied_at timestamptz not null default now(),
|
||||
primary key (extension_name, version)
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
for extension in extensions {
|
||||
for migration in extension.migrations() {
|
||||
let already_applied = query(
|
||||
"select 1
|
||||
from __crank_ext_migrations
|
||||
where extension_name = $1 and version = $2",
|
||||
)
|
||||
.bind(extension.name())
|
||||
.bind(i32::try_from(migration.version).map_err(|_| {
|
||||
RegistryError::InvalidNumericValue {
|
||||
field: "extension_migration.version",
|
||||
value: migration.version as i64,
|
||||
}
|
||||
})?)
|
||||
.fetch_optional(pool)
|
||||
.await?
|
||||
.is_some();
|
||||
|
||||
if already_applied {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut tx = pool.begin().await?;
|
||||
|
||||
query(migration.sql).execute(&mut *tx).await?;
|
||||
query(
|
||||
"insert into __crank_ext_migrations (extension_name, version)
|
||||
values ($1, $2)",
|
||||
)
|
||||
.bind(extension.name())
|
||||
.bind(i32::try_from(migration.version).map_err(|_| {
|
||||
RegistryError::InvalidNumericValue {
|
||||
field: "extension_migration.version",
|
||||
value: migration.version as i64,
|
||||
}
|
||||
})?)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
mod error;
|
||||
mod ext;
|
||||
mod migrations;
|
||||
mod model;
|
||||
mod postgres;
|
||||
|
||||
pub use error::RegistryError;
|
||||
pub use ext::{ExtensionMigration, RegistryExtension, apply_extension_migrations};
|
||||
pub use model::{
|
||||
AgentSummary, AgentVersionRecord, AsyncJobFilter, AsyncJobRecord, AuthUserRecord,
|
||||
CreateAgentRequest, CreateAsyncJobRequest, CreateInvitationRequest, CreateInvocationLogRequest,
|
||||
|
||||
Reference in New Issue
Block a user