admin: add service seam builder
This commit is contained in:
@@ -155,4 +155,5 @@ Progress:
|
|||||||
- Phase 0 / task `0.11`: добавлены `RuntimeExecutorBuilder` и `community_default()`, а default community runtime wiring вынесен из `RuntimeExecutor` в отдельный builder layer
|
- Phase 0 / task `0.11`: добавлены `RuntimeExecutorBuilder` и `community_default()`, а default community runtime wiring вынесен из `RuntimeExecutor` в отдельный builder layer
|
||||||
- Phase 0 / task `0.12`: `apps/admin-api` и `apps/mcp-server` переведены на `community_default().with_limits(...).with_response_cache(...).build()` вместо прямой сборки runtime через `RuntimeExecutor::with_limits(...)`
|
- Phase 0 / task `0.12`: `apps/admin-api` и `apps/mcp-server` переведены на `community_default().with_limits(...).with_response_cache(...).build()` вместо прямой сборки runtime через `RuntimeExecutor::with_limits(...)`
|
||||||
- Phase 0 / task `0.13`: введены `RegistryExtension`, `ExtensionMigration` и `apply_extension_migrations(...)` в `crank-registry` как отдельный public seam для additive private migrations
|
- Phase 0 / task `0.13`: введены `RegistryExtension`, `ExtensionMigration` и `apply_extension_migrations(...)` в `crank-registry` как отдельный public seam для additive private migrations
|
||||||
|
- Phase 0 / task `0.14`: добавлены `AdminServiceBuilder`, seam slots (`identity_provider`, `policy_engine`, `audit_sink`, `token_issuer`, `capability_profile`) и community defaults для них; `apps/admin-api/src/main.rs` переведен на builder
|
||||||
- backward-compatible alias `CommunityMachineCredentialVerifier` сохранен, поведение Community не изменено
|
- backward-compatible alias `CommunityMachineCredentialVerifier` сохранен, поведение Community не изменено
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use tracing::info;
|
|||||||
use crate::{
|
use crate::{
|
||||||
app::build_app,
|
app::build_app,
|
||||||
auth::{AuthSettings, BootstrapAdminConfig},
|
auth::{AuthSettings, BootstrapAdminConfig},
|
||||||
service::AdminService,
|
service::AdminServiceBuilder,
|
||||||
state::AppState,
|
state::AppState,
|
||||||
};
|
};
|
||||||
use crank_runtime::{
|
use crank_runtime::{
|
||||||
@@ -70,13 +70,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
.with_limits(runtime_limits)
|
.with_limits(runtime_limits)
|
||||||
.with_response_cache(cache_stores.response.clone())
|
.with_response_cache(cache_stores.response.clone())
|
||||||
.build();
|
.build();
|
||||||
let service = AdminService::new_with_runtime(
|
let service = AdminServiceBuilder::new(
|
||||||
registry,
|
registry,
|
||||||
storage_root,
|
storage_root,
|
||||||
auth_settings,
|
auth_settings,
|
||||||
secret_crypto,
|
secret_crypto,
|
||||||
runtime,
|
runtime,
|
||||||
);
|
)
|
||||||
|
.build();
|
||||||
service.bootstrap_admin_user().await?;
|
service.bootstrap_admin_user().await?;
|
||||||
if env_flag("CRANK_DEMO_SEED") {
|
if env_flag("CRANK_DEMO_SEED") {
|
||||||
service.seed_demo_assets().await?;
|
service.seed_demo_assets().await?;
|
||||||
|
|||||||
+130
-15
@@ -1,19 +1,21 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
|
use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
|
||||||
use crank_core::{
|
use crank_core::{
|
||||||
Agent, AgentId, AgentOperationBinding, AgentStatus, AgentVersion, AggregationMode,
|
Agent, AgentId, AgentOperationBinding, AgentStatus, AgentVersion, AggregationMode,
|
||||||
AsyncJobHandle, AuthConfig, AuthKind, AuthProfile, AuthProfileId, CapabilityProfile,
|
AsyncJobHandle, AuditSink, AuthConfig, AuthKind, AuthProfile, AuthProfileId, CapabilityProfile,
|
||||||
ConfigExport, EditionCapabilities, ExecutionMode, ExportMode, GeneratedDraft,
|
CommunityCapabilityProfile, ConfigExport, EditionCapabilities, ExecutionMode, ExportMode,
|
||||||
GeneratedDraftStatus, InvitationId, InvitationStatus, InvitationToken, InvocationLevel,
|
GeneratedDraft, GeneratedDraftStatus, IdentityProvider, InvitationId, InvitationStatus,
|
||||||
InvocationLog, InvocationLogId, InvocationSource, InvocationStatus, IssueAgentTokenRequest,
|
InvitationToken, InvocationLevel, InvocationLog, InvocationLogId, InvocationSource,
|
||||||
IssueOneTimeAgentTokenRequest, IssuedAgentTokenResponse, JobStatus, MachineAccessMode,
|
InvocationStatus, IssueAgentTokenRequest, IssueOneTimeAgentTokenRequest,
|
||||||
MembershipRole, OperationId, OperationSecurityLevel, OperationStatus, PlatformApiKey,
|
IssuedAgentTokenResponse, JobStatus, MachineAccessMode, MachineTokenIssuer, MembershipRole,
|
||||||
PlatformApiKeyId, PlatformApiKeyScope, PlatformApiKeyStatus, ProductEdition, Protocol,
|
NoMachineTokenIssuer, NoopAuditSink, OperationId, OperationSecurityLevel, OperationStatus,
|
||||||
ResponseCachePolicy, SampleId, Samples, Secret, SecretId, SecretKind, SecretStatus,
|
OwnerOnlyPolicyEngine, PlatformApiKey, PlatformApiKeyId, PlatformApiKeyScope,
|
||||||
StreamSession, StreamStatus, Target, TransportBehavior, UsagePeriod, UserId, UserSessionId,
|
PlatformApiKeyStatus, PolicyEngine, ProductEdition, Protocol, ResponseCachePolicy, SampleId,
|
||||||
Workspace, WorkspaceId, WorkspaceStatus,
|
Samples, Secret, SecretId, SecretKind, SecretStatus, StreamSession, StreamStatus, Target,
|
||||||
|
TransportBehavior, UsagePeriod, UserId, UserSessionId, Workspace, WorkspaceId, WorkspaceStatus,
|
||||||
};
|
};
|
||||||
use crank_mapping::{JsonPathRoot, MappingSet, infer_mapping_from_samples};
|
use crank_mapping::{JsonPathRoot, MappingSet, infer_mapping_from_samples};
|
||||||
use crank_registry::{
|
use crank_registry::{
|
||||||
@@ -57,6 +59,24 @@ pub struct AdminService {
|
|||||||
storage: LocalArtifactStorage,
|
storage: LocalArtifactStorage,
|
||||||
auth_settings: AuthSettings,
|
auth_settings: AuthSettings,
|
||||||
secret_crypto: SecretCrypto,
|
secret_crypto: SecretCrypto,
|
||||||
|
identity_provider: Option<Arc<dyn IdentityProvider>>,
|
||||||
|
policy_engine: Arc<dyn PolicyEngine>,
|
||||||
|
audit_sink: Arc<dyn AuditSink>,
|
||||||
|
token_issuer: Arc<dyn MachineTokenIssuer>,
|
||||||
|
capability_profile: Arc<dyn CapabilityProfile>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AdminServiceBuilder {
|
||||||
|
registry: PostgresRegistry,
|
||||||
|
storage_root: PathBuf,
|
||||||
|
auth_settings: AuthSettings,
|
||||||
|
secret_crypto: SecretCrypto,
|
||||||
|
runtime: RuntimeExecutor,
|
||||||
|
identity_provider: Option<Arc<dyn IdentityProvider>>,
|
||||||
|
policy_engine: Option<Arc<dyn PolicyEngine>>,
|
||||||
|
audit_sink: Option<Arc<dyn AuditSink>>,
|
||||||
|
token_issuer: Option<Arc<dyn MachineTokenIssuer>>,
|
||||||
|
capability_profile: Option<Arc<dyn CapabilityProfile>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
@@ -654,19 +674,114 @@ impl AdminService {
|
|||||||
secret_crypto: SecretCrypto,
|
secret_crypto: SecretCrypto,
|
||||||
runtime: RuntimeExecutor,
|
runtime: RuntimeExecutor,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
AdminServiceBuilder::new(
|
||||||
registry,
|
registry,
|
||||||
runtime,
|
storage_root,
|
||||||
storage: LocalArtifactStorage::new(storage_root),
|
|
||||||
auth_settings,
|
auth_settings,
|
||||||
secret_crypto,
|
secret_crypto,
|
||||||
}
|
runtime,
|
||||||
|
)
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn auth_settings(&self) -> &AuthSettings {
|
pub fn auth_settings(&self) -> &AuthSettings {
|
||||||
&self.auth_settings
|
&self.auth_settings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn identity_provider(&self) -> Option<&Arc<dyn IdentityProvider>> {
|
||||||
|
self.identity_provider.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn policy_engine(&self) -> &Arc<dyn PolicyEngine> {
|
||||||
|
&self.policy_engine
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn audit_sink(&self) -> &Arc<dyn AuditSink> {
|
||||||
|
&self.audit_sink
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn token_issuer(&self) -> &Arc<dyn MachineTokenIssuer> {
|
||||||
|
&self.token_issuer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn capability_profile(&self) -> &Arc<dyn CapabilityProfile> {
|
||||||
|
&self.capability_profile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AdminServiceBuilder {
|
||||||
|
pub fn new(
|
||||||
|
registry: PostgresRegistry,
|
||||||
|
storage_root: PathBuf,
|
||||||
|
auth_settings: AuthSettings,
|
||||||
|
secret_crypto: SecretCrypto,
|
||||||
|
runtime: RuntimeExecutor,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
registry,
|
||||||
|
storage_root,
|
||||||
|
auth_settings,
|
||||||
|
secret_crypto,
|
||||||
|
runtime,
|
||||||
|
identity_provider: None,
|
||||||
|
policy_engine: None,
|
||||||
|
audit_sink: None,
|
||||||
|
token_issuer: None,
|
||||||
|
capability_profile: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_identity_provider(mut self, provider: Arc<dyn IdentityProvider>) -> Self {
|
||||||
|
self.identity_provider = Some(provider);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_policy_engine(mut self, policy_engine: Arc<dyn PolicyEngine>) -> Self {
|
||||||
|
self.policy_engine = Some(policy_engine);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_audit_sink(mut self, audit_sink: Arc<dyn AuditSink>) -> Self {
|
||||||
|
self.audit_sink = Some(audit_sink);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_token_issuer(mut self, token_issuer: Arc<dyn MachineTokenIssuer>) -> Self {
|
||||||
|
self.token_issuer = Some(token_issuer);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_capability_profile(
|
||||||
|
mut self,
|
||||||
|
capability_profile: Arc<dyn CapabilityProfile>,
|
||||||
|
) -> Self {
|
||||||
|
self.capability_profile = Some(capability_profile);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> AdminService {
|
||||||
|
AdminService {
|
||||||
|
registry: self.registry,
|
||||||
|
runtime: self.runtime,
|
||||||
|
storage: LocalArtifactStorage::new(self.storage_root),
|
||||||
|
auth_settings: self.auth_settings,
|
||||||
|
secret_crypto: self.secret_crypto,
|
||||||
|
identity_provider: self.identity_provider,
|
||||||
|
policy_engine: self
|
||||||
|
.policy_engine
|
||||||
|
.unwrap_or_else(|| Arc::new(OwnerOnlyPolicyEngine)),
|
||||||
|
audit_sink: self.audit_sink.unwrap_or_else(|| Arc::new(NoopAuditSink)),
|
||||||
|
token_issuer: self
|
||||||
|
.token_issuer
|
||||||
|
.unwrap_or_else(|| Arc::new(NoMachineTokenIssuer)),
|
||||||
|
capability_profile: self
|
||||||
|
.capability_profile
|
||||||
|
.unwrap_or_else(|| Arc::new(CommunityCapabilityProfile)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AdminService {
|
||||||
pub async fn bootstrap_admin_user(&self) -> Result<(), ApiError> {
|
pub async fn bootstrap_admin_user(&self) -> Result<(), ApiError> {
|
||||||
let password_hash = hash_password(
|
let password_hash = hash_password(
|
||||||
&self.auth_settings.bootstrap_admin.password,
|
&self.auth_settings.bootstrap_admin.password,
|
||||||
@@ -1496,7 +1611,7 @@ impl AdminService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_capabilities(&self) -> EditionCapabilities {
|
pub async fn get_capabilities(&self) -> EditionCapabilities {
|
||||||
crank_core::CommunityCapabilityProfile.capabilities()
|
self.capability_profile().capabilities()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn issue_agent_token(
|
pub async fn issue_agent_token(
|
||||||
|
|||||||
Reference in New Issue
Block a user