chore: align sqlite control plane architecture
- replace default Convex control-plane wording with Rust SQLite control-plane across architecture, AGENTS, Reasonix, and design docs - retire root Convex functions source and deploy script into recycle while keeping explicit cloud/compat/sync-replica boundaries - add control-plane migration guard/docs and keep CodeGraph refreshed after the SQLite control-plane cutover
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ControlPlaneError {
|
||||
NotFound(String),
|
||||
Conflict(String),
|
||||
InvalidInput(String),
|
||||
Storage(String),
|
||||
Unauthorized(String),
|
||||
SessionExpired(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for ControlPlaneError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ControlPlaneError::NotFound(msg) => write!(f, "NotFound: {msg}"),
|
||||
ControlPlaneError::Conflict(msg) => write!(f, "Conflict: {msg}"),
|
||||
ControlPlaneError::InvalidInput(msg) => write!(f, "InvalidInput: {msg}"),
|
||||
ControlPlaneError::Storage(msg) => write!(f, "Storage: {msg}"),
|
||||
ControlPlaneError::Unauthorized(msg) => write!(f, "Unauthorized: {msg}"),
|
||||
ControlPlaneError::SessionExpired(msg) => write!(f, "SessionExpired: {msg}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ControlPlaneError {}
|
||||
|
||||
impl From<rusqlite::Error> for ControlPlaneError {
|
||||
fn from(e: rusqlite::Error) -> Self {
|
||||
ControlPlaneError::Storage(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for ControlPlaneError {
|
||||
fn from(e: serde_json::Error) -> Self {
|
||||
ControlPlaneError::Storage(format!("JSON error: {e}"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
pub mod error;
|
||||
pub mod migrations;
|
||||
pub mod model;
|
||||
pub mod sqlite;
|
||||
pub mod store;
|
||||
|
||||
pub use error::ControlPlaneError;
|
||||
pub use migrations::run_migrations;
|
||||
pub use model::*;
|
||||
pub use sqlite::SqliteControlPlaneStore;
|
||||
pub use store::*;
|
||||
@@ -0,0 +1,109 @@
|
||||
//! SQLite schema migrations for the MNote control-plane database.
|
||||
//!
|
||||
//! Each migration is a named step that runs inside a transaction.
|
||||
//! The `migrations` table tracks which steps have been applied.
|
||||
|
||||
use rusqlite::{Connection, Result as SqlResult};
|
||||
|
||||
const MIGRATIONS: &[(&str, &str)] = &[
|
||||
(
|
||||
"v1-initial-schema",
|
||||
include_str!("../migrations/001-initial-schema.sql"),
|
||||
),
|
||||
(
|
||||
"v2-ai-runtime-store",
|
||||
include_str!("../migrations/002-ai-runtime-store.sql"),
|
||||
),
|
||||
];
|
||||
|
||||
/// Create the `_migrations` meta-table if it does not exist.
|
||||
fn ensure_meta_table(conn: &Connection) -> SqlResult<()> {
|
||||
conn.execute_batch(
|
||||
"CREATE TABLE IF NOT EXISTS _migrations (
|
||||
name TEXT PRIMARY KEY,
|
||||
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);",
|
||||
)
|
||||
}
|
||||
|
||||
/// Run all pending migrations inside a single transaction.
|
||||
pub fn run_migrations(conn: &Connection) -> SqlResult<()> {
|
||||
ensure_meta_table(conn)?;
|
||||
|
||||
let already_applied: Vec<String> = conn
|
||||
.prepare("SELECT name FROM _migrations")?
|
||||
.query_map([], |row| row.get(0))?
|
||||
.filter_map(|r| r.ok())
|
||||
.collect();
|
||||
|
||||
for (name, sql) in MIGRATIONS {
|
||||
if already_applied.contains(&name.to_string()) {
|
||||
continue;
|
||||
}
|
||||
tracing::info!("Running migration: {name}");
|
||||
conn.execute_batch(sql)?;
|
||||
conn.execute("INSERT INTO _migrations (name) VALUES (?1)", [name])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Create a fresh in-memory SQLite database and run migrations.
|
||||
fn fresh_db() -> Connection {
|
||||
let conn = Connection::open_in_memory().expect("open in-memory db");
|
||||
conn.execute_batch(
|
||||
"PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL; PRAGMA foreign_keys=ON;",
|
||||
)
|
||||
.expect("pragmas");
|
||||
conn
|
||||
}
|
||||
|
||||
fn table_exists(conn: &Connection, name: &str) -> bool {
|
||||
conn.query_row(
|
||||
"SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?1",
|
||||
[name],
|
||||
|row| row.get::<_, i64>(0),
|
||||
)
|
||||
.unwrap_or(0)
|
||||
> 0
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn migration_is_idempotent() {
|
||||
let conn = fresh_db();
|
||||
run_migrations(&conn).expect("first run");
|
||||
run_migrations(&conn).expect("second run (idempotent)");
|
||||
|
||||
// All expected tables exist
|
||||
for table in &[
|
||||
"users",
|
||||
"auth_identities",
|
||||
"auth_sessions",
|
||||
"workspaces",
|
||||
"workspace_members",
|
||||
"directory_grants",
|
||||
"share_links",
|
||||
"sync_state",
|
||||
"ai_policies",
|
||||
"audit_log",
|
||||
"outbox_events",
|
||||
"legacy_id_map",
|
||||
"ai_runtime_runs",
|
||||
"ai_runtime_events",
|
||||
] {
|
||||
assert!(table_exists(&conn, table), "table {table} should exist");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn migration_runs_fresh_db_ok() {
|
||||
let conn = fresh_db();
|
||||
run_migrations(&conn).expect("fresh migration ok");
|
||||
assert!(table_exists(&conn, "users"));
|
||||
assert!(table_exists(&conn, "_migrations"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
//! SQLite 控制面 Phase 1 的最小数据模型。
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
pub type EntityId = String;
|
||||
pub type Timestamp = String;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct UserRecord {
|
||||
pub id: EntityId,
|
||||
pub email: Option<String>,
|
||||
pub username: String,
|
||||
pub display_name: String,
|
||||
pub role: String,
|
||||
pub status: String,
|
||||
pub created_at: Timestamp,
|
||||
pub updated_at: Timestamp,
|
||||
pub revision: i64,
|
||||
}
|
||||
|
||||
pub fn session_token_hash(raw_token: &str) -> String {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(raw_token.as_bytes());
|
||||
hex::encode(hasher.finalize())
|
||||
}
|
||||
|
||||
pub fn password_hash_v1(password: &str) -> String {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(b"mnote-password-v1:");
|
||||
hasher.update(password.as_bytes());
|
||||
format!("sha256-v1:{}", hex::encode(hasher.finalize()))
|
||||
}
|
||||
|
||||
pub fn share_token_hash_v1(token: &str) -> String {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(b"mnote-share-token-v1:");
|
||||
hasher.update(token.as_bytes());
|
||||
format!("sha256-v1:{}", hex::encode(hasher.finalize()))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct UpsertUserInput {
|
||||
pub id: Option<EntityId>,
|
||||
pub email: Option<String>,
|
||||
pub username: String,
|
||||
pub display_name: String,
|
||||
pub role: Option<String>,
|
||||
pub password_hash: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct CreatePasswordIdentityInput {
|
||||
pub user_id: EntityId,
|
||||
pub email: Option<String>,
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AuthenticatePasswordInput {
|
||||
pub account: String,
|
||||
pub password: String,
|
||||
pub session_id: Option<EntityId>,
|
||||
pub token_hash: String,
|
||||
pub user_agent: Option<String>,
|
||||
pub ip_hash: Option<String>,
|
||||
pub expires_at: Option<Timestamp>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AuthSessionRecord {
|
||||
pub id: EntityId,
|
||||
pub user_id: EntityId,
|
||||
pub token_hash: String,
|
||||
pub user_agent: Option<String>,
|
||||
pub ip_hash: Option<String>,
|
||||
pub created_at: Timestamp,
|
||||
pub expires_at: Timestamp,
|
||||
pub revoked_at: Option<Timestamp>,
|
||||
pub last_seen_at: Timestamp,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct CreateSessionInput {
|
||||
pub id: Option<EntityId>,
|
||||
pub user_id: EntityId,
|
||||
pub token_hash: String,
|
||||
pub user_agent: Option<String>,
|
||||
pub ip_hash: Option<String>,
|
||||
pub expires_at: Option<Timestamp>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ResolvedAuthSession {
|
||||
pub session: AuthSessionRecord,
|
||||
pub user: UserRecord,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct WorkspaceRecord {
|
||||
pub id: EntityId,
|
||||
pub owner_user_id: EntityId,
|
||||
pub name: String,
|
||||
pub kind: String,
|
||||
pub root_uri: String,
|
||||
pub root_path: String,
|
||||
pub source_kind: String,
|
||||
pub status: String,
|
||||
pub created_at: Timestamp,
|
||||
pub updated_at: Timestamp,
|
||||
pub revision: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct DirectoryGrantRecord {
|
||||
pub id: EntityId,
|
||||
pub user_id: EntityId,
|
||||
pub workspace_id: Option<EntityId>,
|
||||
pub root_uri: String,
|
||||
pub root_path: String,
|
||||
pub permission: String,
|
||||
pub recursive: bool,
|
||||
pub capabilities_json: String,
|
||||
pub source: String,
|
||||
pub status: String,
|
||||
pub created_by: Option<EntityId>,
|
||||
pub created_at: Timestamp,
|
||||
pub updated_at: Timestamp,
|
||||
pub revision: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct DirectoryGrantLookup {
|
||||
pub grant_id: Option<EntityId>,
|
||||
pub user_id: Option<EntityId>,
|
||||
pub root_uri: Option<String>,
|
||||
pub include_revoked: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct DirectoryGrantInput {
|
||||
pub user_id: EntityId,
|
||||
pub workspace_id: Option<EntityId>,
|
||||
pub root_uri: String,
|
||||
pub root_path: String,
|
||||
pub permission: String,
|
||||
pub recursive: bool,
|
||||
pub capabilities: Vec<String>,
|
||||
pub source: String,
|
||||
pub created_by: Option<EntityId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ResolvedAccess {
|
||||
pub user_id: EntityId,
|
||||
pub root_uri: String,
|
||||
pub permission: String,
|
||||
pub grant_ids: Vec<EntityId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct OutboxEventRecord {
|
||||
pub id: EntityId,
|
||||
pub topic: String,
|
||||
pub event_type: String,
|
||||
pub payload_json: String,
|
||||
pub created_at: Timestamp,
|
||||
pub delivered_at: Option<Timestamp>,
|
||||
pub attempts: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct OutboxEventInput {
|
||||
pub topic: String,
|
||||
pub event_type: String,
|
||||
pub payload_json: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AiPolicyRecord {
|
||||
pub id: EntityId,
|
||||
pub user_id: Option<EntityId>,
|
||||
pub workspace_id: Option<EntityId>,
|
||||
pub allowed_roots_json: String,
|
||||
pub model_policy_json: String,
|
||||
pub quota_json: String,
|
||||
pub created_at: Timestamp,
|
||||
pub updated_at: Timestamp,
|
||||
pub revision: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct UpsertAiPolicyInput {
|
||||
pub id: Option<EntityId>,
|
||||
pub user_id: Option<EntityId>,
|
||||
pub workspace_id: Option<EntityId>,
|
||||
pub allowed_roots_json: String,
|
||||
pub model_policy_json: String,
|
||||
pub quota_json: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct SyncStateRecord {
|
||||
pub id: EntityId,
|
||||
pub workspace_id: EntityId,
|
||||
pub remote_kind: String,
|
||||
pub remote_id: Option<String>,
|
||||
pub cursor: Option<String>,
|
||||
pub last_synced_at: Option<Timestamp>,
|
||||
pub status: String,
|
||||
pub error_json: Option<String>,
|
||||
pub updated_at: Timestamp,
|
||||
pub revision: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct UpsertSyncStateInput {
|
||||
pub id: Option<EntityId>,
|
||||
pub workspace_id: EntityId,
|
||||
pub remote_kind: String,
|
||||
pub remote_id: Option<String>,
|
||||
pub cursor: Option<String>,
|
||||
pub last_synced_at: Option<Timestamp>,
|
||||
pub status: String,
|
||||
pub error_json: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AiRuntimeRunRecord {
|
||||
pub id: EntityId,
|
||||
pub user_id: EntityId,
|
||||
pub workspace_id: Option<EntityId>,
|
||||
pub document_id: Option<String>,
|
||||
pub session_id: EntityId,
|
||||
pub run_id: EntityId,
|
||||
pub title: Option<String>,
|
||||
pub profile: String,
|
||||
pub acp_runtime: String,
|
||||
pub trace_id: Option<String>,
|
||||
pub status: String,
|
||||
pub runtime_json: String,
|
||||
pub payload_json: String,
|
||||
pub deleted_at: Option<Timestamp>,
|
||||
pub created_at: Timestamp,
|
||||
pub updated_at: Timestamp,
|
||||
pub revision: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct UpsertAiRuntimeRunInput {
|
||||
pub id: Option<EntityId>,
|
||||
pub user_id: EntityId,
|
||||
pub workspace_id: Option<EntityId>,
|
||||
pub document_id: Option<String>,
|
||||
pub session_id: EntityId,
|
||||
pub run_id: EntityId,
|
||||
pub title: Option<String>,
|
||||
pub profile: String,
|
||||
pub acp_runtime: String,
|
||||
pub trace_id: Option<String>,
|
||||
pub status: String,
|
||||
pub runtime_json: String,
|
||||
pub payload_json: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AiRuntimeEventRecord {
|
||||
pub id: EntityId,
|
||||
pub user_id: EntityId,
|
||||
pub workspace_id: Option<EntityId>,
|
||||
pub document_id: Option<String>,
|
||||
pub session_id: EntityId,
|
||||
pub run_id: EntityId,
|
||||
pub profile: String,
|
||||
pub acp_runtime: String,
|
||||
pub event_type: String,
|
||||
pub payload_json: String,
|
||||
pub created_at: Timestamp,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AppendAiRuntimeEventInput {
|
||||
pub id: Option<EntityId>,
|
||||
pub user_id: EntityId,
|
||||
pub workspace_id: Option<EntityId>,
|
||||
pub document_id: Option<String>,
|
||||
pub session_id: EntityId,
|
||||
pub run_id: EntityId,
|
||||
pub profile: String,
|
||||
pub acp_runtime: String,
|
||||
pub event_type: String,
|
||||
pub payload_json: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ShareLinkRecord {
|
||||
pub id: EntityId,
|
||||
pub workspace_id: EntityId,
|
||||
pub resource_kind: String,
|
||||
pub resource_id: String,
|
||||
pub token_hash: String,
|
||||
pub permission: String,
|
||||
pub created_by: EntityId,
|
||||
pub expires_at: Option<Timestamp>,
|
||||
pub revoked_at: Option<Timestamp>,
|
||||
pub created_at: Timestamp,
|
||||
pub updated_at: Timestamp,
|
||||
pub revision: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct CreateShareLinkInput {
|
||||
pub id: Option<EntityId>,
|
||||
pub workspace_id: EntityId,
|
||||
pub resource_kind: String,
|
||||
pub resource_id: String,
|
||||
pub token: Option<String>,
|
||||
pub permission: String,
|
||||
pub created_by: EntityId,
|
||||
pub expires_at: Option<Timestamp>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct CreatedShareLink {
|
||||
pub link: ShareLinkRecord,
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AuditLogRecord {
|
||||
pub id: EntityId,
|
||||
pub actor_user_id: Option<EntityId>,
|
||||
pub action: String,
|
||||
pub target_kind: String,
|
||||
pub target_id: Option<EntityId>,
|
||||
pub metadata_json: String,
|
||||
pub created_at: Timestamp,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AppendAuditInput {
|
||||
pub actor_user_id: Option<EntityId>,
|
||||
pub action: String,
|
||||
pub target_kind: String,
|
||||
pub target_id: Option<EntityId>,
|
||||
pub metadata_json: String,
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,172 @@
|
||||
//! 控制面 store trait。
|
||||
|
||||
use crate::error::ControlPlaneError;
|
||||
use crate::model::{
|
||||
AiPolicyRecord, AiRuntimeEventRecord, AiRuntimeRunRecord, AppendAiRuntimeEventInput,
|
||||
AppendAuditInput, AuditLogRecord, AuthSessionRecord, AuthenticatePasswordInput,
|
||||
CreatePasswordIdentityInput, CreateSessionInput, CreateShareLinkInput, CreatedShareLink,
|
||||
DirectoryGrantInput, DirectoryGrantLookup, DirectoryGrantRecord, OutboxEventInput,
|
||||
OutboxEventRecord, ResolvedAccess, ResolvedAuthSession, ShareLinkRecord, SyncStateRecord,
|
||||
UpsertAiPolicyInput, UpsertAiRuntimeRunInput, UpsertSyncStateInput, UpsertUserInput,
|
||||
UserRecord, WorkspaceRecord,
|
||||
};
|
||||
|
||||
pub trait ControlPlaneStore: Send + Sync {
|
||||
fn upsert_user(&self, input: UpsertUserInput) -> Result<UserRecord, ControlPlaneError>;
|
||||
|
||||
fn create_password_identity(
|
||||
&self,
|
||||
input: CreatePasswordIdentityInput,
|
||||
) -> Result<(), ControlPlaneError>;
|
||||
|
||||
fn authenticate_password(
|
||||
&self,
|
||||
input: AuthenticatePasswordInput,
|
||||
) -> Result<ResolvedAuthSession, ControlPlaneError>;
|
||||
|
||||
fn create_session(
|
||||
&self,
|
||||
input: CreateSessionInput,
|
||||
) -> Result<AuthSessionRecord, ControlPlaneError>;
|
||||
|
||||
fn get_session_by_token_hash(
|
||||
&self,
|
||||
token_hash: &str,
|
||||
) -> Result<Option<ResolvedAuthSession>, ControlPlaneError>;
|
||||
|
||||
fn revoke_session(&self, session_id: &str) -> Result<(), ControlPlaneError>;
|
||||
|
||||
fn ensure_default_workspace(
|
||||
&self,
|
||||
actor_id: &str,
|
||||
) -> Result<WorkspaceRecord, ControlPlaneError>;
|
||||
|
||||
fn grant_directory_access(
|
||||
&self,
|
||||
input: DirectoryGrantInput,
|
||||
) -> Result<DirectoryGrantRecord, ControlPlaneError>;
|
||||
|
||||
fn list_directory_grants(&self) -> Result<Vec<DirectoryGrantRecord>, ControlPlaneError>;
|
||||
|
||||
fn list_directory_grants_for_actor(
|
||||
&self,
|
||||
actor_id: &str,
|
||||
) -> Result<Vec<DirectoryGrantRecord>, ControlPlaneError>;
|
||||
|
||||
fn find_directory_grants(
|
||||
&self,
|
||||
lookup: DirectoryGrantLookup,
|
||||
) -> Result<Vec<DirectoryGrantRecord>, ControlPlaneError>;
|
||||
|
||||
fn revoke_directory_grant(
|
||||
&self,
|
||||
grant_id: &str,
|
||||
expected_revision: Option<i64>,
|
||||
) -> Result<(), ControlPlaneError>;
|
||||
|
||||
fn resolve_access(
|
||||
&self,
|
||||
actor_id: &str,
|
||||
root_uri: &str,
|
||||
) -> Result<ResolvedAccess, ControlPlaneError>;
|
||||
|
||||
fn create_share_link(
|
||||
&self,
|
||||
input: CreateShareLinkInput,
|
||||
) -> Result<CreatedShareLink, ControlPlaneError>;
|
||||
|
||||
fn list_share_links(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
) -> Result<Vec<ShareLinkRecord>, ControlPlaneError>;
|
||||
|
||||
fn resolve_share_link(
|
||||
&self,
|
||||
token_hash: &str,
|
||||
) -> Result<Option<ShareLinkRecord>, ControlPlaneError>;
|
||||
|
||||
fn revoke_share_link(&self, link_id: &str) -> Result<(), ControlPlaneError>;
|
||||
|
||||
fn append_audit(&self, input: AppendAuditInput) -> Result<(), ControlPlaneError>;
|
||||
|
||||
fn list_audit_log(&self, limit: usize) -> Result<Vec<AuditLogRecord>, ControlPlaneError>;
|
||||
|
||||
fn append_outbox(
|
||||
&self,
|
||||
input: OutboxEventInput,
|
||||
) -> Result<OutboxEventRecord, ControlPlaneError>;
|
||||
|
||||
fn drain_outbox(&self, limit: usize) -> Result<Vec<OutboxEventRecord>, ControlPlaneError>;
|
||||
|
||||
fn mark_outbox_delivered(&self, event_id: &str) -> Result<(), ControlPlaneError>;
|
||||
|
||||
fn upsert_ai_policy(
|
||||
&self,
|
||||
input: UpsertAiPolicyInput,
|
||||
) -> Result<AiPolicyRecord, ControlPlaneError>;
|
||||
|
||||
fn get_ai_policy(
|
||||
&self,
|
||||
actor_id: &str,
|
||||
workspace_id: Option<&str>,
|
||||
) -> Result<Option<AiPolicyRecord>, ControlPlaneError>;
|
||||
|
||||
fn upsert_sync_state(
|
||||
&self,
|
||||
input: UpsertSyncStateInput,
|
||||
) -> Result<SyncStateRecord, ControlPlaneError>;
|
||||
|
||||
fn get_sync_state(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
remote_kind: &str,
|
||||
) -> Result<Option<SyncStateRecord>, ControlPlaneError>;
|
||||
|
||||
fn upsert_ai_runtime_run(
|
||||
&self,
|
||||
input: UpsertAiRuntimeRunInput,
|
||||
) -> Result<AiRuntimeRunRecord, ControlPlaneError>;
|
||||
|
||||
fn list_ai_runtime_runs(
|
||||
&self,
|
||||
user_id: &str,
|
||||
workspace_id: Option<&str>,
|
||||
document_id: Option<&str>,
|
||||
session_id: Option<&str>,
|
||||
limit: usize,
|
||||
) -> Result<Vec<AiRuntimeRunRecord>, ControlPlaneError>;
|
||||
|
||||
fn append_ai_runtime_event(
|
||||
&self,
|
||||
input: AppendAiRuntimeEventInput,
|
||||
) -> Result<AiRuntimeEventRecord, ControlPlaneError>;
|
||||
|
||||
fn list_ai_runtime_events(
|
||||
&self,
|
||||
user_id: &str,
|
||||
run_id: &str,
|
||||
limit: usize,
|
||||
) -> Result<Vec<AiRuntimeEventRecord>, ControlPlaneError>;
|
||||
|
||||
fn rename_ai_runtime_session(
|
||||
&self,
|
||||
user_id: &str,
|
||||
session_id: &str,
|
||||
workspace_id: Option<&str>,
|
||||
title: &str,
|
||||
) -> Result<Vec<AiRuntimeRunRecord>, ControlPlaneError>;
|
||||
|
||||
fn auto_title_ai_runtime_session(
|
||||
&self,
|
||||
user_id: &str,
|
||||
session_id: &str,
|
||||
workspace_id: Option<&str>,
|
||||
) -> Result<Option<AiRuntimeRunRecord>, ControlPlaneError>;
|
||||
|
||||
fn delete_ai_runtime_session(
|
||||
&self,
|
||||
user_id: &str,
|
||||
session_id: &str,
|
||||
workspace_id: Option<&str>,
|
||||
) -> Result<usize, ControlPlaneError>;
|
||||
}
|
||||
Reference in New Issue
Block a user