Files
crank/apps/admin-api/src/dto.rs
T
bsodfather bc03c33387
CI / Rust Checks (push) Failing after 4m6s
CI / UI Checks (push) Has been skipped
CI / Frontend E2E (push) Has been skipped
CI / Community Image Smoke (push) Has been skipped
CI / Deploy (push) Has been skipped
fix(openapi): harden story 2.1 production lifecycle
2026-08-29 00:48:22 +03:00

736 lines
19 KiB
Rust

use crank_core::{
AgentId, AgentStatus, ApprovalRequestStatus, AuthConfig, AuthKind, ExecutionMode, ExportMode,
GeneratedDraft, InvocationLevel, InvocationSource, InvocationStatus, OperationAvailability,
OperationSecurityLevel, OperationStatus, OperationVersionState, PlatformApiKeyKind,
PlatformApiKeyScope, Protocol, SecretKind, Target, ToolSelectionPolicy, UsagePeriod,
WizardState, WorkspaceId, WorkspaceStatus,
};
use crank_mapping::MappingSet;
use crank_registry::{
InvocationLogRecord, PlatformApiKeyRecord, RegistryOperation, UsageAgentBreakdown,
UsageOperationBreakdown, UsageOutcomeGroup, UsageSummary, UsageTimelinePoint,
WorkspaceMembershipRecord, WorkspaceRecord,
};
use crank_schema::Schema;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Clone, Debug, Deserialize)]
pub struct LoginPayload {
pub email: String,
pub password: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct CompleteBootstrapPayload {
pub token: String,
pub password: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct BootstrapStatusResponse {
pub bootstrap_required: bool,
}
#[derive(Clone, Debug, Serialize)]
pub struct SessionResponse {
pub user: crank_core::User,
pub memberships: Vec<WorkspaceMembershipRecord>,
pub current_workspace_id: Option<String>,
pub csrf_token: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct UpdateProfilePayload {
pub display_name: String,
pub email: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ChangePasswordPayload {
pub current_password: String,
pub new_password: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct UpdateCurrentWorkspacePayload {
pub workspace_id: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct OperationPayload {
pub name: String,
pub display_name: String,
#[serde(default = "default_operation_category")]
pub category: String,
pub protocol: Protocol,
#[serde(default)]
pub security_level: OperationSecurityLevel,
pub target: Target,
pub input_schema: Schema,
pub output_schema: Schema,
pub input_mapping: MappingSet,
pub output_mapping: MappingSet,
pub execution_config: crank_core::ExecutionConfig,
pub tool_description: crank_core::ToolDescription,
#[serde(default)]
pub wizard_state: Option<WizardState>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct NewVersionPayload {
#[serde(flatten)]
pub operation: OperationPayload,
#[serde(default)]
pub change_note: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct PublishPayload {
pub version: u32,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TestRunPayload {
pub version: u32,
pub input: Value,
#[serde(default)]
pub confirmation_token: Option<String>,
#[serde(default)]
pub locale: Option<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct TestRunResult {
pub ok: bool,
pub mode: ExecutionMode,
pub tested_version: u32,
pub request_id: String,
pub trace_id: String,
pub request_preview: Value,
pub response_preview: Value,
pub errors: Vec<Value>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct AuthProfilePayload {
pub name: String,
pub kind: AuthKind,
pub config: AuthConfig,
}
#[derive(Clone, Debug, Deserialize)]
pub struct UpstreamPayload {
pub name: String,
pub base_url: String,
#[serde(default)]
pub static_headers: Value,
#[serde(default)]
pub auth_profile_id: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct SecretPayload {
pub name: String,
pub kind: SecretKind,
pub value: Value,
}
#[derive(Clone, Debug, Deserialize)]
pub struct RotateSecretPayload {
pub value: Value,
}
#[derive(Clone, Debug, Deserialize)]
pub struct WorkspacePayload {
pub slug: String,
pub display_name: String,
#[serde(default)]
pub settings: Value,
}
#[derive(Clone, Debug, Deserialize)]
pub struct UpdateWorkspacePayload {
pub slug: Option<String>,
pub display_name: Option<String>,
pub status: Option<WorkspaceStatus>,
pub settings: Option<Value>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct AgentPayload {
pub slug: String,
pub display_name: String,
pub description: String,
#[serde(default)]
pub instructions: Value,
#[serde(default)]
pub tool_selection_policy: ToolSelectionPolicy,
}
#[derive(Clone, Debug, Deserialize)]
pub struct UpdateAgentPayload {
pub slug: String,
pub display_name: String,
pub description: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct AgentBindingPayload {
pub operation_id: String,
pub operation_version: u32,
pub tool_name: String,
pub tool_title: String,
pub tool_description_override: Option<String>,
#[serde(default = "default_enabled")]
pub enabled: bool,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ToolSearchPreviewPayload {
pub query: String,
#[serde(default)]
pub group_ids: Vec<String>,
pub bindings: Vec<AgentBindingPayload>,
pub tool_selection_policy: ToolSelectionPolicy,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
pub enum AgentCatalogPayload {
Bindings(Vec<AgentBindingPayload>),
Config {
bindings: Vec<AgentBindingPayload>,
tool_selection_policy: ToolSelectionPolicy,
},
}
impl AgentCatalogPayload {
pub fn into_parts(self) -> (Vec<AgentBindingPayload>, Option<ToolSelectionPolicy>) {
match self {
Self::Bindings(bindings) => (bindings, None),
Self::Config {
bindings,
tool_selection_policy,
} => (bindings, Some(tool_selection_policy)),
}
}
}
impl From<Vec<AgentBindingPayload>> for AgentCatalogPayload {
fn from(bindings: Vec<AgentBindingPayload>) -> Self {
Self::Bindings(bindings)
}
}
#[derive(Clone, Debug, Serialize)]
pub struct CreatedAgentResponse {
pub agent_id: String,
pub workspace_id: String,
pub version: u32,
pub status: AgentStatus,
}
#[derive(Clone, Debug, Serialize)]
pub struct PublishAgentResponse {
pub agent_id: String,
pub workspace_id: String,
pub published_version: u32,
pub published_at: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct AgentSummaryView {
pub id: String,
pub workspace_id: String,
pub slug: String,
pub display_name: String,
pub description: String,
pub status: AgentStatus,
pub current_draft_version: u32,
pub latest_published_version: Option<u32>,
pub catalog_revision: i64,
pub created_at: String,
pub updated_at: String,
pub published_at: Option<String>,
pub operation_count: usize,
pub operation_ids: Vec<String>,
pub tool_selection_policy: ToolSelectionPolicy,
pub key_count: usize,
pub calls_today: u64,
pub mcp_endpoint: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct AgentMutationResult {
pub agent_id: String,
pub workspace_id: String,
pub updated_at: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct PlatformApiKeyPayload {
pub name: String,
#[serde(default = "default_platform_api_key_kind")]
pub key_kind: PlatformApiKeyKind,
pub scopes: Vec<PlatformApiKeyScope>,
#[serde(default)]
pub expires_at: Option<String>,
#[serde(default)]
pub allowed_origins: Vec<String>,
}
fn default_platform_api_key_kind() -> PlatformApiKeyKind {
PlatformApiKeyKind::McpClient
}
#[derive(Clone, Debug, Serialize)]
pub struct CreatedPlatformApiKeyResponse {
pub api_key: PlatformApiKeyRecord,
pub secret: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub connection: Option<EphemeralMcpConnection>,
}
#[derive(Clone, Debug, Serialize)]
pub struct EphemeralMcpClientConfig {
pub client: String,
pub config: Value,
}
#[derive(Clone, Debug, Serialize)]
pub struct EphemeralMcpConnection {
pub endpoint: String,
pub clients: Vec<EphemeralMcpClientConfig>,
pub secret_display: &'static str,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct OnboardingEventPayload {
pub event: String,
pub idempotency_key: String,
pub expected_revision: i64,
#[serde(default)]
pub completed_steps: Option<Value>,
}
#[derive(Clone, Debug, Serialize)]
pub struct OnboardingStepView {
pub id: crank_core::OnboardingStepId,
pub completed: bool,
pub status: String,
pub action_code: String,
pub reason_code: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct OnboardingFirstCallEvidence {
pub log_id: String,
pub agent_id: String,
pub key_id: String,
pub operation_id: String,
pub operation_version: u32,
pub tool_name: String,
pub occurred_at: String,
pub request_id: Option<String>,
pub trace_id: Option<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct OnboardingResponse {
pub schema_version: u16,
pub workspace_id: String,
pub revision: i64,
pub status: String,
pub completed: bool,
pub eligible_since: Option<String>,
pub steps: Vec<OnboardingStepView>,
pub operation_id: Option<String>,
pub operation_version: Option<u32>,
pub agent_id: Option<String>,
pub catalog_revision: Option<i64>,
pub platform_api_key_id: Option<String>,
pub mcp_endpoint: Option<String>,
pub first_call: Option<OnboardingFirstCallEvidence>,
}
#[derive(Clone, Debug, Serialize)]
pub struct OnboardingEventResponse {
pub accepted: bool,
#[serde(flatten)]
pub onboarding: OnboardingResponse,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ResetOnboardingSelectionPayload {
pub expected_revision: i64,
}
#[derive(Clone, Debug, Serialize)]
pub struct ResetOnboardingSelectionResponse {
pub selection_reset: bool,
#[serde(flatten)]
pub onboarding: OnboardingResponse,
}
#[derive(Clone, Debug, Serialize)]
pub struct WorkspaceCatalogSnapshotResponse {
pub kind: String,
pub format_version: String,
pub restorable: bool,
pub included: Vec<String>,
pub excluded: Vec<String>,
pub workspace: WorkspaceRecord,
pub operations: Vec<OperationSummaryView>,
pub agents: Vec<AgentSummaryView>,
pub platform_api_keys: Vec<PlatformApiKeyRecord>,
pub exported_at: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct LogsQuery {
pub level: Option<InvocationLevel>,
pub status: Option<InvocationStatus>,
pub outcome_group: Option<UsageOutcomeGroup>,
pub search: Option<String>,
pub source: Option<InvocationSource>,
pub operation_id: Option<String>,
pub agent_id: Option<String>,
pub period: Option<UsagePeriod>,
pub created_after: Option<String>,
pub created_before: Option<String>,
pub cursor: Option<String>,
pub limit: Option<u32>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ApprovalsQuery {
pub status: Option<ApprovalRequestStatus>,
pub limit: Option<u32>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ApprovalDecisionPayload {
pub approve: String,
#[serde(default)]
pub note: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct UsageRequestQuery {
pub period: Option<UsagePeriod>,
pub source: Option<InvocationSource>,
pub created_after: Option<String>,
pub created_before: Option<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct UsageOverviewResponse {
pub summary: UsageSummary,
pub timeline: Vec<UsageTimelinePoint>,
pub operations: Vec<UsageOperationBreakdown>,
pub agents: Vec<UsageAgentBreakdown>,
pub outcomes: Vec<crank_registry::UsageOutcomeBreakdown>,
}
#[derive(Clone, Debug, Serialize)]
pub struct LogsListResponse {
pub items: Vec<InvocationLogRecord>,
pub next_cursor: Option<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct ProtocolCapabilityView {
pub protocol: Protocol,
pub supports_execution_modes: Vec<ExecutionMode>,
pub supports_transport_behaviors: Vec<String>,
pub supports_auth_kinds: Vec<String>,
pub supports_upload_artifacts: Vec<String>,
pub supports_cursor_path: bool,
pub supports_done_path: bool,
pub supports_aggregation_mode: Vec<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct GenerateDraftPayload {
#[serde(default)]
pub sources: Vec<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct DraftGenerationResult {
pub generated_draft: GeneratedDraft,
pub input_schema: Schema,
pub output_schema: Schema,
pub input_mapping: MappingSet,
pub output_mapping: MappingSet,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ImportQuery {
#[serde(default)]
pub mode: ImportMode,
}
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ImportMode {
#[default]
Create,
Upsert,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ExportQuery {
pub version: Option<u32>,
#[serde(default = "default_export_mode")]
pub mode: ExportMode,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct YamlOperationDocument {
pub format_version: String,
pub kind: String,
pub operation: PortableOperation,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct PortableOperation {
pub name: String,
pub display_name: String,
#[serde(default = "default_operation_category")]
pub category: String,
pub protocol: Protocol,
#[serde(default)]
pub security_level: OperationSecurityLevel,
pub target: Target,
pub input_schema: Schema,
pub output_schema: Schema,
pub input_mapping: MappingSet,
pub output_mapping: MappingSet,
pub execution_config: crank_core::ExecutionConfig,
pub tool_description: crank_core::ToolDescription,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct LegacyYamlOperationDocument {
pub format_version: String,
pub kind: String,
pub operation: RegistryOperation,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum OpenApiUploadLocale {
En,
Ru,
}
#[derive(Clone, Debug)]
pub struct OpenApiUpload {
pub bytes: Vec<u8>,
pub mime_type: String,
pub locale: OpenApiUploadLocale,
}
#[derive(Clone, Debug, Serialize)]
pub struct OpenApiImportPreviewResponse {
pub job_id: String,
pub expires_at: String,
pub preview: crank_import::rest::ImportPreview,
}
#[derive(Clone, Debug, Deserialize)]
pub struct OpenApiImportCreatePayload {
pub selected_operation_keys: Vec<String>,
#[serde(default)]
pub server_url: Option<String>,
#[serde(default = "default_openapi_conflict_mode")]
pub conflict_mode: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct OpenApiImportCreateResponse {
pub created: Vec<OpenApiImportCreatedOperation>,
pub skipped: Vec<OpenApiImportSkippedOperation>,
pub findings: Vec<crank_import::rest::ImportFinding>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub struct OpenApiImportCreatedOperation {
pub operation_key: String,
pub operation_id: String,
pub name: String,
pub version: u32,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub struct OpenApiImportSkippedOperation {
pub operation_key: String,
pub name: String,
pub reason: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct CreatedOperationResponse {
pub operation_id: String,
pub workspace_id: String,
pub version: u32,
pub status: OperationStatus,
pub updated_at: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct PublishResponse {
pub operation_id: String,
pub workspace_id: String,
pub published_version: u32,
pub published_at: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct UpdateOperationPayload {
pub display_name: String,
#[serde(default = "default_operation_category")]
pub category: String,
#[serde(default)]
pub security_level: OperationSecurityLevel,
pub target: Target,
pub input_schema: Schema,
pub output_schema: Schema,
pub input_mapping: MappingSet,
pub output_mapping: MappingSet,
pub execution_config: crank_core::ExecutionConfig,
pub tool_description: crank_core::ToolDescription,
#[serde(default)]
pub wizard_state: Option<WizardState>,
}
#[derive(Clone, Debug, Serialize)]
pub struct OperationUsageSummaryView {
pub calls_today: u64,
pub error_rate_pct: f64,
pub avg_latency_ms: u64,
}
#[derive(Clone, Debug, Serialize)]
pub struct OperationAgentRefView {
pub agent_id: String,
pub agent_slug: String,
pub display_name: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct OperationSummaryView {
pub id: String,
pub workspace_id: String,
pub name: String,
pub display_name: String,
pub category: String,
pub protocol: Protocol,
pub security_level: OperationSecurityLevel,
pub target_url: String,
pub target_action: String,
pub status: OperationStatus,
pub current_draft_version: u32,
pub latest_published_version: Option<u32>,
pub can_delete: bool,
pub created_at: String,
pub updated_at: String,
pub published_at: Option<String>,
pub usage_summary: OperationUsageSummaryView,
pub agent_refs: Vec<OperationAgentRefView>,
}
#[derive(Clone, Debug, Serialize)]
pub struct OperationDetailView {
pub id: String,
pub workspace_id: String,
pub name: String,
pub display_name: String,
pub category: String,
pub protocol: Protocol,
pub security_level: OperationSecurityLevel,
pub status: OperationStatus,
pub availability: OperationAvailability,
pub current_draft_version: u32,
pub latest_published_version: Option<u32>,
pub created_at: String,
pub updated_at: String,
pub published_at: Option<String>,
pub draft_version_ref: VersionRef,
pub published_version_ref: Option<VersionRef>,
pub agent_refs: Vec<OperationAgentRefView>,
}
#[derive(Clone, Debug, Serialize)]
pub struct VersionRef {
pub version: u32,
pub status: OperationVersionState,
}
#[derive(Clone, Debug, Serialize)]
pub struct OperationMutationResult {
pub operation_id: String,
pub workspace_id: String,
pub version: u32,
pub status: OperationStatus,
pub updated_at: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct ImportResponse {
pub operation_id: String,
pub workspace_id: String,
pub version: u32,
pub import_mode: ImportMode,
pub warnings: Vec<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct DescriptorUploadResponse {
pub descriptor_id: String,
pub version: u32,
}
fn default_operation_category() -> String {
"general".to_owned()
}
fn default_openapi_conflict_mode() -> String {
"rename".to_owned()
}
fn default_export_mode() -> ExportMode {
ExportMode::Portable
}
fn default_enabled() -> bool {
true
}
pub(crate) struct InvocationRecordRequest<'a> {
pub workspace_id: &'a WorkspaceId,
pub agent_id: Option<&'a AgentId>,
pub operation: &'a RegistryOperation,
pub request_id: Option<&'a str>,
pub trace_id: Option<&'a str>,
pub source: InvocationSource,
pub level: InvocationLevel,
pub status: InvocationStatus,
pub message: String,
pub status_code: Option<u16>,
pub error_kind: Option<String>,
pub execution_stage: Option<crank_core::ExecutionStage>,
pub execution_error_code: Option<crank_core::ExecutionErrorCode>,
pub retryability: Option<crank_core::Retryability>,
pub outcome_certainty: Option<crank_core::OutcomeCertainty>,
pub duration_ms: u64,
pub request_preview: Value,
pub response_preview: Value,
}