1use docbox_core::{
2 database::{DbErr, models::tenant::Tenant, sqlx::types::Uuid},
3 storage::StorageLayerError,
4 tenant::tenant_options_ext::TenantOptionsExt,
5};
6use docbox_management_interface::{
7 CheckRootOutput, CreateTenantInput, CreateTenantOutput, DeleteTenantInput, DeleteTenantOutput,
8 DocboxManagementInterface, DocboxServiceError, FailedTenantMigration, GetTenantInput,
9 GetTenantOutput, GetTenantPendingMigrationsInput, GetTenantPendingMigrationsOutput,
10 GetTenantsInput, GetTenantsOutput, ManagedTenant, ManagementError, ManagementTenantTarget,
11 MigrateTenantIAMInput, MigrateTenantIAMOutput, MigrateTenantInput, MigrateTenantOutput,
12 PendingMigration, SetTenantAllowedCorsOriginsInput, TenantMigrationService, async_trait,
13 error::DynServiceError,
14};
15use thiserror::Error;
16
17use crate::{
18 config::ServerConfigData,
19 server::ManagedServer,
20 tenant::{
21 MigrateTenantsOutcome, TenantTarget,
22 create_tenant::CreateTenantConfig,
23 delete_tenant::{DeleteTenant, DeleteTenantOptions},
24 migrate_tenants::MigrateTenantsConfig,
25 migrate_tenants_search::MigrateTenantsSearchConfig,
26 migrate_tenants_storage::MigrateTenantsStorageConfig,
27 },
28};
29
30pub struct ManagedServerInterface {
31 pub server: ManagedServer,
32 pub config: ServerConfigData,
33}
34
35#[derive(Debug, Error)]
36#[error(transparent)]
37pub struct ManagementDbErr(DbErr);
38
39impl DocboxServiceError for ManagementDbErr {}
40
41#[derive(Debug, Error)]
42#[error(transparent)]
43pub struct ManagementStorageLayerErr(StorageLayerError);
44
45impl DocboxServiceError for ManagementStorageLayerErr {}
46
47#[derive(Debug, Error)]
48#[error("tenant not found")]
49pub struct TenantNotFoundError;
50
51impl DocboxServiceError for TenantNotFoundError {}
52
53impl DocboxServiceError for crate::root::initialize::InitializeError {}
54
55impl DocboxServiceError for crate::root::migrate_root::MigrateRootError {}
56
57impl DocboxServiceError for crate::tenant::flush_tenant_cache::FlushTenantCacheError {}
58
59impl DocboxServiceError for crate::tenant::delete_tenant::DeleteTenantError {}
60
61impl DocboxServiceError for crate::tenant::create_tenant::CreateTenantError {}
62
63impl DocboxServiceError for crate::tenant::migrate_tenants::MigrateTenantsError {}
64
65impl DocboxServiceError for crate::tenant::migrate_tenants_search::MigrateTenantsSearchError {}
66
67impl DocboxServiceError for crate::tenant::migrate_tenants_storage::MigrateTenantsStorageError {}
68
69impl DocboxServiceError for crate::tenant::migrate_tenant_secret_to_iam::MigrateIAMError {}
70
71impl DocboxServiceError
72 for crate::tenant::get_pending_tenant_search_migrations::GetPendingTenantMigrationsError
73{
74}
75
76impl DocboxServiceError
77 for crate::tenant::get_pending_tenant_storage_migrations::GetPendingTenantMigrationsError
78{
79}
80
81impl From<TenantNotFoundError> for ManagementError {
82 fn from(value: TenantNotFoundError) -> Self {
83 ManagementError::Service(DynServiceError::from(value))
84 }
85}
86
87impl From<ManagementDbErr> for ManagementError {
88 fn from(value: ManagementDbErr) -> Self {
89 ManagementError::Service(DynServiceError::from(value))
90 }
91}
92
93impl From<ManagementStorageLayerErr> for ManagementError {
94 fn from(value: ManagementStorageLayerErr) -> Self {
95 ManagementError::Service(DynServiceError::from(value))
96 }
97}
98
99fn map_managed_tenant(tenant: Tenant) -> ManagedTenant {
100 ManagedTenant {
101 env: tenant.env,
102 id: tenant.id,
103 name: tenant.name,
104 db_name: tenant.db_name,
105 db_secret_name: tenant.db_secret_name,
106 db_iam_user_name: tenant.db_iam_user_name,
107 s3_name: tenant.s3_name,
108 os_index_name: tenant.os_index_name,
109 event_queue_url: tenant.event_queue_url,
110 }
111}
112
113#[async_trait]
114impl DocboxManagementInterface for ManagedServerInterface {
115 async fn check_root(&self) -> Result<CheckRootOutput, ManagementError> {
116 let initialized = crate::root::initialize::is_initialized(&self.server.db_provider)
117 .await
118 .map_err(ManagementDbErr)
119 .map_err(DynServiceError::from)?;
120
121 Ok(CheckRootOutput { initialized })
122 }
123
124 async fn create_root(&self) -> Result<(), ManagementError> {
125 if self.config.database.root_iam {
126 crate::root::initialize::initialize_iam(&self.server.db_provider)
127 .await
128 .map_err(DynServiceError::from)?;
129 } else if let Some(root_secret_name) = self.config.database.root_secret_name.as_ref() {
130 crate::root::initialize::initialize(
131 &self.server.db_provider,
132 &self.server.secrets,
133 root_secret_name,
134 )
135 .await
136 .map_err(DynServiceError::from)?;
137 }
138
139 Ok(())
140 }
141
142 async fn create_tenant(
143 &self,
144 input: CreateTenantInput,
145 ) -> Result<CreateTenantOutput, ManagementError> {
146 let tenant = crate::tenant::create_tenant::create_tenant(
147 &self.server.db_provider,
148 &self.server.search,
149 &self.server.storage,
150 &self.server.secrets,
151 CreateTenantConfig {
152 id: input.id,
153 name: input.name,
154 env: input.env,
155 db_name: input.db_name,
156 db_role_name: input.db_role_name,
157 db_secret_name: input.db_secret_name,
158 db_iam_user: input.db_iam_user,
159 storage_bucket_name: input.storage_bucket_name,
160 storage_cors_origins: input.storage_cors_origins,
161 storage_s3_queue_arn: input.storage_s3_queue_arn,
162 search_index_name: input.search_index_name,
163 event_queue_url: input.event_queue_url,
164 },
165 )
166 .await
167 .map_err(DynServiceError::from)?;
168
169 Ok(CreateTenantOutput {
170 tenant: map_managed_tenant(tenant),
171 })
172 }
173
174 async fn get_tenant(&self, input: GetTenantInput) -> Result<GetTenantOutput, ManagementError> {
175 let tenant = crate::tenant::get_tenant::get_tenant(
176 &self.server.db_provider,
177 &input.env,
178 input.tenant_id,
179 )
180 .await
181 .map_err(ManagementDbErr)
182 .map_err(DynServiceError::from)?;
183
184 Ok(GetTenantOutput {
185 tenant: tenant.map(map_managed_tenant),
186 })
187 }
188
189 async fn delete_tenant(
190 &self,
191 input: DeleteTenantInput,
192 ) -> Result<DeleteTenantOutput, ManagementError> {
193 let tenant = crate::tenant::get_tenant::get_tenant(
194 &self.server.db_provider,
195 &input.env,
196 input.tenant_id,
197 )
198 .await
199 .map_err(ManagementDbErr)?
200 .ok_or(TenantNotFoundError)?;
201
202 self.server.db_cache.close_tenant_pool(&tenant).await;
205
206 self.flush_tenant_cache().await?;
208
209 crate::tenant::delete_tenant::delete_tenant(
210 &self.server.db_provider,
211 &self.server.search,
212 &self.server.storage,
213 &self.server.events,
214 &self.server.secrets,
215 DeleteTenant {
216 env: input.env,
217 tenant_id: input.tenant_id,
218 options: DeleteTenantOptions {
219 delete_contents: input.options.delete_contents,
220 delete_database: input.options.delete_database,
221 delete_search: input.options.delete_search,
222 delete_storage: input.options.delete_storage,
223 permanently_delete_secret: input.options.permanently_delete_secret,
224 },
225 },
226 )
227 .await
228 .map_err(DynServiceError::from)?;
229
230 Ok(DeleteTenantOutput {})
231 }
232
233 async fn get_tenants(
234 &self,
235 input: GetTenantsInput,
236 ) -> Result<GetTenantsOutput, ManagementError> {
237 let mut tenants = crate::tenant::get_tenants::get_tenants(&self.server.db_provider)
238 .await
239 .map_err(ManagementDbErr)?;
240
241 if let Some(env) = input.env {
242 tenants.retain(|tenant| tenant.env.eq(&env));
243 }
244
245 Ok(GetTenantsOutput {
246 tenants: tenants.into_iter().map(map_managed_tenant).collect(),
247 })
248 }
249
250 async fn set_tenant_allowed_cors_origins(
251 &self,
252 input: SetTenantAllowedCorsOriginsInput,
253 ) -> Result<(), ManagementError> {
254 let tenant = crate::tenant::get_tenant::get_tenant(
255 &self.server.db_provider,
256 &input.env,
257 input.tenant_id,
258 )
259 .await
260 .map_err(ManagementDbErr)?
261 .ok_or(TenantNotFoundError)?;
262
263 let storage = self
264 .server
265 .storage
266 .create_layer(tenant.storage_layer_options());
267
268 storage
269 .set_bucket_cors_origins(input.origins)
270 .await
271 .map_err(|err| DynServiceError::from(ManagementStorageLayerErr(err)))?;
272
273 Ok(())
274 }
275
276 async fn migrate_root(&self) -> Result<(), ManagementError> {
277 crate::root::migrate_root::migrate_root(&self.server.db_provider, None)
278 .await
279 .map_err(DynServiceError::from)?;
280
281 Ok(())
282 }
283
284 async fn migrate_tenant(
285 &self,
286 input: MigrateTenantInput,
287 ) -> Result<MigrateTenantOutput, ManagementError> {
288 let target = ApplyServiceMigrationTarget {
289 env: input.env,
290 tenant_id: input.tenant_id,
291 name: input.name,
292 skip_failed: input.skip_failed,
293 };
294
295 if let Some(service) = input.service {
296 return self.apply_service_migration(service, &target).await;
297 }
298
299 let database_outcome = self
300 .apply_service_migration(TenantMigrationService::Database, &target)
301 .await?;
302
303 let search_outcome = self
304 .apply_service_migration(TenantMigrationService::Search, &target)
305 .await?;
306
307 let storage_outcome = self
308 .apply_service_migration(TenantMigrationService::Storage, &target)
309 .await?;
310
311 let applied_tenants: Vec<ManagementTenantTarget> = database_outcome
312 .applied_tenants
313 .into_iter()
314 .chain(search_outcome.applied_tenants)
315 .chain(storage_outcome.applied_tenants)
316 .collect();
317
318 let failed_tenants: Vec<FailedTenantMigration> = database_outcome
319 .failed_tenants
320 .into_iter()
321 .chain(search_outcome.failed_tenants)
322 .chain(storage_outcome.failed_tenants)
323 .collect();
324
325 Ok(MigrateTenantOutput {
326 applied_tenants,
327 failed_tenants,
328 })
329 }
330
331 async fn migrate_tenant_iam(
332 &self,
333 input: MigrateTenantIAMInput,
334 ) -> Result<MigrateTenantIAMOutput, ManagementError> {
335 let mut tenants = crate::tenant::get_tenants::get_tenants(&self.server.db_provider)
336 .await
337 .map_err(ManagementDbErr)?;
338
339 tenants.retain(|tenant| {
340 tenant.env.eq(&input.env) && input.tenant_id.is_none_or(|id| tenant.id.eq(&id))
341 });
342
343 let mut migrated_tenants = Vec::new();
344
345 for mut tenant in tenants {
346 if tenant.db_iam_user_name.is_some() {
347 tracing::debug!(?tenant, "skipping tenant with iam user name already set");
348 continue;
349 }
350
351 crate::tenant::migrate_tenant_secret_to_iam::migrate_tenant_secret_to_iam(
352 &self.server.db_provider,
353 &self.server.secrets,
354 &mut tenant,
355 )
356 .await
357 .map_err(DynServiceError::from)?;
358
359 migrated_tenants.push(ManagementTenantTarget {
360 id: tenant.id,
361 name: tenant.name,
362 env: tenant.env,
363 });
364 }
365
366 Ok(MigrateTenantIAMOutput {
367 applied_tenants: migrated_tenants,
368 })
369 }
370
371 async fn get_pending_root_migrations(&self) -> Result<Vec<String>, ManagementError> {
372 let pending_migrations =
373 crate::root::get_pending_root_migrations::get_pending_root_migrations(
374 &self.server.db_provider,
375 )
376 .await
377 .map_err(ManagementDbErr)?;
378 Ok(pending_migrations)
379 }
380
381 async fn get_tenant_pending_migrations(
382 &self,
383 input: GetTenantPendingMigrationsInput,
384 ) -> Result<GetTenantPendingMigrationsOutput, ManagementError> {
385 let tenant = crate::tenant::get_tenant::get_tenant(
386 &self.server.db_provider,
387 &input.env,
388 input.tenant_id,
389 )
390 .await
391 .map_err(ManagementDbErr)?
392 .ok_or(TenantNotFoundError)?;
393
394 if let Some(service) = input.service {
395 let migrations = self.get_service_migrations(service, &tenant).await?;
396 return Ok(GetTenantPendingMigrationsOutput { migrations });
397 }
398
399 let database_migrations = self
400 .get_service_migrations(TenantMigrationService::Database, &tenant)
401 .await?;
402 let search_migrations = self
403 .get_service_migrations(TenantMigrationService::Search, &tenant)
404 .await?;
405 let storage_migrations = self
406 .get_service_migrations(TenantMigrationService::Storage, &tenant)
407 .await?;
408
409 let migrations = database_migrations
410 .into_iter()
411 .chain(search_migrations)
412 .chain(storage_migrations)
413 .collect();
414
415 Ok(GetTenantPendingMigrationsOutput { migrations })
416 }
417
418 async fn flush_tenant_cache(&self) -> Result<(), ManagementError> {
419 crate::tenant::flush_tenant_cache::flush_tenant_cache(&self.config.api)
420 .await
421 .map_err(DynServiceError::from)?;
422
423 Ok(())
424 }
425}
426
427struct ApplyServiceMigrationTarget {
428 env: Option<String>,
429 tenant_id: Option<Uuid>,
430 name: Option<String>,
431 skip_failed: bool,
432}
433
434fn map_tenant_target(tenant: TenantTarget) -> ManagementTenantTarget {
435 ManagementTenantTarget {
436 env: tenant.env,
437 id: tenant.tenant_id,
438 name: tenant.name,
439 }
440}
441
442fn map_migrate_tenants_outcome(outcome: MigrateTenantsOutcome) -> MigrateTenantOutput {
443 MigrateTenantOutput {
444 applied_tenants: outcome
445 .applied_tenants
446 .into_iter()
447 .map(map_tenant_target)
448 .collect(),
449 failed_tenants: outcome
450 .failed_tenants
451 .into_iter()
452 .map(|(error, target)| FailedTenantMigration {
453 error,
454 target: map_tenant_target(target),
455 })
456 .collect(),
457 }
458}
459
460impl ManagedServerInterface {
461 async fn apply_service_migration(
462 &self,
463 service: TenantMigrationService,
464 target: &ApplyServiceMigrationTarget,
465 ) -> Result<MigrateTenantOutput, ManagementError> {
466 match service {
467 TenantMigrationService::Database => {
468 let outcome = crate::tenant::migrate_tenants::migrate_tenants(
469 &self.server.db_provider,
470 MigrateTenantsConfig {
471 env: target.env.clone(),
472 tenant_id: target.tenant_id,
473 skip_failed: target.skip_failed,
474 target_migration_name: target.name.clone(),
475 },
476 )
477 .await
478 .map_err(DynServiceError::from)?;
479
480 Ok(map_migrate_tenants_outcome(outcome))
481 }
482 TenantMigrationService::Search => {
483 let outcome = crate::tenant::migrate_tenants_search::migrate_tenants_search(
484 &self.server.db_provider,
485 &self.server.search,
486 MigrateTenantsSearchConfig {
487 env: target.env.clone(),
488 tenant_id: target.tenant_id,
489 skip_failed: target.skip_failed,
490 target_migration_name: target.name.clone(),
491 },
492 )
493 .await
494 .map_err(DynServiceError::from)?;
495
496 Ok(map_migrate_tenants_outcome(outcome))
497 }
498 TenantMigrationService::Storage => {
499 let outcome = crate::tenant::migrate_tenants_storage::migrate_tenants_storage(
500 &self.server.db_provider,
501 &self.server.storage,
502 MigrateTenantsStorageConfig {
503 env: target.env.clone(),
504 tenant_id: target.tenant_id,
505 skip_failed: target.skip_failed,
506 target_migration_name: target.name.clone(),
507 },
508 )
509 .await
510 .map_err(DynServiceError::from)?;
511
512 Ok(map_migrate_tenants_outcome(outcome))
513 }
514 }
515 }
516
517 async fn get_service_migrations(
518 &self,
519 service: TenantMigrationService,
520 tenant: &Tenant,
521 ) -> Result<Vec<PendingMigration>, ManagementError> {
522 match service {
523 TenantMigrationService::Database => {
524 let pending_migrations =
525 crate::tenant::get_pending_tenant_migrations::get_pending_tenant_migrations(
526 &self.server.db_provider,
527 tenant,
528 )
529 .await
530 .map_err(ManagementDbErr)?;
531
532 Ok(pending_migrations
533 .into_iter()
534 .map(|name| PendingMigration { name, service })
535 .collect())
536 }
537 TenantMigrationService::Search => {
538 let pending_migrations = crate::tenant::get_pending_tenant_storage_migrations::get_pending_tenant_storage_migrations(
539 &self.server.db_provider,
540 &self.server.storage,
541 tenant,
542 )
543 .await
544 .map_err(DynServiceError::from)?;
545
546 Ok(pending_migrations
547 .into_iter()
548 .map(|name| PendingMigration { name, service })
549 .collect())
550 }
551 TenantMigrationService::Storage => {
552 let pending_migrations = crate::tenant::get_pending_tenant_search_migrations::get_pending_tenant_search_migrations(
553 &self.server.db_provider,
554 &self.server.search,
555 tenant,
556 )
557 .await .map_err(DynServiceError::from)?;
558
559 Ok(pending_migrations
560 .into_iter()
561 .map(|name| PendingMigration { name, service })
562 .collect())
563 }
564 }
565 }
566}