this repo has no description
1use crate::api::repo::record::utils::create_signed_commit; 2use crate::auth::BearerAuth; 3use crate::delegation::{self, DelegationActionType}; 4use crate::oauth::db as oauth_db; 5use crate::state::{AppState, RateLimitKind}; 6use crate::util::extract_client_ip; 7use crate::validation::is_valid_did; 8use axum::{ 9 Json, 10 extract::{Query, State}, 11 http::{HeaderMap, StatusCode}, 12 response::{IntoResponse, Response}, 13}; 14use jacquard::types::{integer::LimitedU32, string::Tid}; 15use jacquard_repo::{mst::Mst, storage::BlockStore}; 16use serde::{Deserialize, Serialize}; 17use serde_json::json; 18use std::sync::Arc; 19use tracing::{error, info, warn}; 20 21#[derive(Debug, Serialize)] 22#[serde(rename_all = "camelCase")] 23pub struct ControllerInfo { 24 pub did: String, 25 pub handle: String, 26 pub granted_scopes: String, 27 pub granted_at: chrono::DateTime<chrono::Utc>, 28 pub is_active: bool, 29} 30 31#[derive(Debug, Serialize)] 32pub struct ListControllersResponse { 33 pub controllers: Vec<ControllerInfo>, 34} 35 36pub async fn list_controllers(State(state): State<AppState>, auth: BearerAuth) -> Response { 37 let controllers = match delegation::get_delegations_for_account(&state.db, &auth.0.did).await { 38 Ok(c) => c, 39 Err(e) => { 40 tracing::error!("Failed to list controllers: {:?}", e); 41 return ( 42 StatusCode::INTERNAL_SERVER_ERROR, 43 Json(serde_json::json!({ 44 "error": "ServerError", 45 "message": "Failed to list controllers" 46 })), 47 ) 48 .into_response(); 49 } 50 }; 51 52 Json(ListControllersResponse { 53 controllers: controllers 54 .into_iter() 55 .map(|c| ControllerInfo { 56 did: c.did, 57 handle: c.handle, 58 granted_scopes: c.granted_scopes, 59 granted_at: c.granted_at, 60 is_active: c.is_active, 61 }) 62 .collect(), 63 }) 64 .into_response() 65} 66 67#[derive(Debug, Deserialize)] 68pub struct AddControllerInput { 69 pub controller_did: String, 70 pub granted_scopes: String, 71} 72 73pub async fn add_controller( 74 State(state): State<AppState>, 75 auth: BearerAuth, 76 Json(input): Json<AddControllerInput>, 77) -> Response { 78 if !is_valid_did(&input.controller_did) { 79 return ( 80 StatusCode::BAD_REQUEST, 81 Json(serde_json::json!({ 82 "error": "InvalidRequest", 83 "message": "Invalid DID format" 84 })), 85 ) 86 .into_response(); 87 } 88 89 if let Err(e) = delegation::scopes::validate_delegation_scopes(&input.granted_scopes) { 90 return ( 91 StatusCode::BAD_REQUEST, 92 Json(serde_json::json!({ 93 "error": "InvalidScopes", 94 "message": e 95 })), 96 ) 97 .into_response(); 98 } 99 100 let controller_exists: bool = sqlx::query_scalar!( 101 r#"SELECT EXISTS(SELECT 1 FROM users WHERE did = $1) as "exists!""#, 102 input.controller_did 103 ) 104 .fetch_one(&state.db) 105 .await 106 .unwrap_or(false); 107 108 if !controller_exists { 109 return ( 110 StatusCode::NOT_FOUND, 111 Json(serde_json::json!({ 112 "error": "ControllerNotFound", 113 "message": "Controller account not found" 114 })), 115 ) 116 .into_response(); 117 } 118 119 match delegation::controls_any_accounts(&state.db, &auth.0.did).await { 120 Ok(true) => { 121 return ( 122 StatusCode::BAD_REQUEST, 123 Json(serde_json::json!({ 124 "error": "InvalidDelegation", 125 "message": "Cannot add controllers to an account that controls other accounts" 126 })), 127 ) 128 .into_response(); 129 } 130 Err(e) => { 131 tracing::error!("Failed to check delegation status: {:?}", e); 132 return ( 133 StatusCode::INTERNAL_SERVER_ERROR, 134 Json(serde_json::json!({ 135 "error": "ServerError", 136 "message": "Failed to verify delegation status" 137 })), 138 ) 139 .into_response(); 140 } 141 Ok(false) => {} 142 } 143 144 match delegation::has_any_controllers(&state.db, &input.controller_did).await { 145 Ok(true) => { 146 return ( 147 StatusCode::BAD_REQUEST, 148 Json(serde_json::json!({ 149 "error": "InvalidDelegation", 150 "message": "Cannot add a controlled account as a controller" 151 })), 152 ) 153 .into_response(); 154 } 155 Err(e) => { 156 tracing::error!("Failed to check controller status: {:?}", e); 157 return ( 158 StatusCode::INTERNAL_SERVER_ERROR, 159 Json(serde_json::json!({ 160 "error": "ServerError", 161 "message": "Failed to verify controller status" 162 })), 163 ) 164 .into_response(); 165 } 166 Ok(false) => {} 167 } 168 169 match delegation::create_delegation( 170 &state.db, 171 &auth.0.did, 172 &input.controller_did, 173 &input.granted_scopes, 174 &auth.0.did, 175 ) 176 .await 177 { 178 Ok(_) => { 179 let _ = delegation::log_delegation_action( 180 &state.db, 181 &auth.0.did, 182 &auth.0.did, 183 Some(&input.controller_did), 184 DelegationActionType::GrantCreated, 185 Some(serde_json::json!({ 186 "granted_scopes": input.granted_scopes 187 })), 188 None, 189 None, 190 ) 191 .await; 192 193 ( 194 StatusCode::OK, 195 Json(serde_json::json!({ 196 "success": true 197 })), 198 ) 199 .into_response() 200 } 201 Err(e) => { 202 tracing::error!("Failed to add controller: {:?}", e); 203 ( 204 StatusCode::INTERNAL_SERVER_ERROR, 205 Json(serde_json::json!({ 206 "error": "ServerError", 207 "message": "Failed to add controller" 208 })), 209 ) 210 .into_response() 211 } 212 } 213} 214 215#[derive(Debug, Deserialize)] 216pub struct RemoveControllerInput { 217 pub controller_did: String, 218} 219 220pub async fn remove_controller( 221 State(state): State<AppState>, 222 auth: BearerAuth, 223 Json(input): Json<RemoveControllerInput>, 224) -> Response { 225 if !is_valid_did(&input.controller_did) { 226 return ( 227 StatusCode::BAD_REQUEST, 228 Json(serde_json::json!({ 229 "error": "InvalidRequest", 230 "message": "Invalid DID format" 231 })), 232 ) 233 .into_response(); 234 } 235 236 match delegation::revoke_delegation(&state.db, &auth.0.did, &input.controller_did, &auth.0.did) 237 .await 238 { 239 Ok(true) => { 240 let revoked_app_passwords = sqlx::query_scalar!( 241 r#"DELETE FROM app_passwords 242 WHERE user_id = (SELECT id FROM users WHERE did = $1) 243 AND created_by_controller_did = $2 244 RETURNING id"#, 245 auth.0.did, 246 input.controller_did 247 ) 248 .fetch_all(&state.db) 249 .await 250 .map(|r| r.len()) 251 .unwrap_or(0); 252 253 let revoked_oauth_tokens = oauth_db::revoke_tokens_for_controller( 254 &state.db, 255 &auth.0.did, 256 &input.controller_did, 257 ) 258 .await 259 .unwrap_or(0); 260 261 let _ = delegation::log_delegation_action( 262 &state.db, 263 &auth.0.did, 264 &auth.0.did, 265 Some(&input.controller_did), 266 DelegationActionType::GrantRevoked, 267 Some(serde_json::json!({ 268 "revoked_app_passwords": revoked_app_passwords, 269 "revoked_oauth_tokens": revoked_oauth_tokens 270 })), 271 None, 272 None, 273 ) 274 .await; 275 276 ( 277 StatusCode::OK, 278 Json(serde_json::json!({ 279 "success": true 280 })), 281 ) 282 .into_response() 283 } 284 Ok(false) => ( 285 StatusCode::NOT_FOUND, 286 Json(serde_json::json!({ 287 "error": "DelegationNotFound", 288 "message": "No active delegation found for this controller" 289 })), 290 ) 291 .into_response(), 292 Err(e) => { 293 tracing::error!("Failed to remove controller: {:?}", e); 294 ( 295 StatusCode::INTERNAL_SERVER_ERROR, 296 Json(serde_json::json!({ 297 "error": "ServerError", 298 "message": "Failed to remove controller" 299 })), 300 ) 301 .into_response() 302 } 303 } 304} 305 306#[derive(Debug, Deserialize)] 307pub struct UpdateControllerScopesInput { 308 pub controller_did: String, 309 pub granted_scopes: String, 310} 311 312pub async fn update_controller_scopes( 313 State(state): State<AppState>, 314 auth: BearerAuth, 315 Json(input): Json<UpdateControllerScopesInput>, 316) -> Response { 317 if !is_valid_did(&input.controller_did) { 318 return ( 319 StatusCode::BAD_REQUEST, 320 Json(serde_json::json!({ 321 "error": "InvalidRequest", 322 "message": "Invalid DID format" 323 })), 324 ) 325 .into_response(); 326 } 327 328 if let Err(e) = delegation::scopes::validate_delegation_scopes(&input.granted_scopes) { 329 return ( 330 StatusCode::BAD_REQUEST, 331 Json(serde_json::json!({ 332 "error": "InvalidScopes", 333 "message": e 334 })), 335 ) 336 .into_response(); 337 } 338 339 match delegation::update_delegation_scopes( 340 &state.db, 341 &auth.0.did, 342 &input.controller_did, 343 &input.granted_scopes, 344 ) 345 .await 346 { 347 Ok(true) => { 348 let _ = delegation::log_delegation_action( 349 &state.db, 350 &auth.0.did, 351 &auth.0.did, 352 Some(&input.controller_did), 353 DelegationActionType::ScopesModified, 354 Some(serde_json::json!({ 355 "new_scopes": input.granted_scopes 356 })), 357 None, 358 None, 359 ) 360 .await; 361 362 ( 363 StatusCode::OK, 364 Json(serde_json::json!({ 365 "success": true 366 })), 367 ) 368 .into_response() 369 } 370 Ok(false) => ( 371 StatusCode::NOT_FOUND, 372 Json(serde_json::json!({ 373 "error": "DelegationNotFound", 374 "message": "No active delegation found for this controller" 375 })), 376 ) 377 .into_response(), 378 Err(e) => { 379 tracing::error!("Failed to update controller scopes: {:?}", e); 380 ( 381 StatusCode::INTERNAL_SERVER_ERROR, 382 Json(serde_json::json!({ 383 "error": "ServerError", 384 "message": "Failed to update controller scopes" 385 })), 386 ) 387 .into_response() 388 } 389 } 390} 391 392#[derive(Debug, Serialize)] 393#[serde(rename_all = "camelCase")] 394pub struct DelegatedAccountInfo { 395 pub did: String, 396 pub handle: String, 397 pub granted_scopes: String, 398 pub granted_at: chrono::DateTime<chrono::Utc>, 399} 400 401#[derive(Debug, Serialize)] 402pub struct ListControlledAccountsResponse { 403 pub accounts: Vec<DelegatedAccountInfo>, 404} 405 406pub async fn list_controlled_accounts(State(state): State<AppState>, auth: BearerAuth) -> Response { 407 let accounts = match delegation::get_accounts_controlled_by(&state.db, &auth.0.did).await { 408 Ok(a) => a, 409 Err(e) => { 410 tracing::error!("Failed to list controlled accounts: {:?}", e); 411 return ( 412 StatusCode::INTERNAL_SERVER_ERROR, 413 Json(serde_json::json!({ 414 "error": "ServerError", 415 "message": "Failed to list controlled accounts" 416 })), 417 ) 418 .into_response(); 419 } 420 }; 421 422 Json(ListControlledAccountsResponse { 423 accounts: accounts 424 .into_iter() 425 .map(|a| DelegatedAccountInfo { 426 did: a.did, 427 handle: a.handle, 428 granted_scopes: a.granted_scopes, 429 granted_at: a.granted_at, 430 }) 431 .collect(), 432 }) 433 .into_response() 434} 435 436#[derive(Debug, Deserialize)] 437pub struct AuditLogParams { 438 #[serde(default = "default_limit")] 439 pub limit: i64, 440 #[serde(default)] 441 pub offset: i64, 442} 443 444fn default_limit() -> i64 { 445 50 446} 447 448#[derive(Debug, Serialize)] 449#[serde(rename_all = "camelCase")] 450pub struct AuditLogEntry { 451 pub id: String, 452 pub delegated_did: String, 453 pub actor_did: String, 454 pub controller_did: Option<String>, 455 pub action_type: String, 456 pub action_details: Option<serde_json::Value>, 457 pub created_at: chrono::DateTime<chrono::Utc>, 458} 459 460#[derive(Debug, Serialize)] 461pub struct GetAuditLogResponse { 462 pub entries: Vec<AuditLogEntry>, 463 pub total: i64, 464} 465 466pub async fn get_audit_log( 467 State(state): State<AppState>, 468 auth: BearerAuth, 469 Query(params): Query<AuditLogParams>, 470) -> Response { 471 let limit = params.limit.min(100).max(1); 472 let offset = params.offset.max(0); 473 474 let entries = 475 match delegation::audit::get_audit_log_for_account(&state.db, &auth.0.did, limit, offset) 476 .await 477 { 478 Ok(e) => e, 479 Err(e) => { 480 tracing::error!("Failed to get audit log: {:?}", e); 481 return ( 482 StatusCode::INTERNAL_SERVER_ERROR, 483 Json(serde_json::json!({ 484 "error": "ServerError", 485 "message": "Failed to get audit log" 486 })), 487 ) 488 .into_response(); 489 } 490 }; 491 492 let total = match delegation::audit::count_audit_log_entries(&state.db, &auth.0.did).await { 493 Ok(t) => t, 494 Err(_) => 0, 495 }; 496 497 Json(GetAuditLogResponse { 498 entries: entries 499 .into_iter() 500 .map(|e| AuditLogEntry { 501 id: e.id.to_string(), 502 delegated_did: e.delegated_did, 503 actor_did: e.actor_did, 504 controller_did: e.controller_did, 505 action_type: format!("{:?}", e.action_type), 506 action_details: e.action_details, 507 created_at: e.created_at, 508 }) 509 .collect(), 510 total, 511 }) 512 .into_response() 513} 514 515#[derive(Debug, Serialize)] 516pub struct ScopePresetInfo { 517 pub name: &'static str, 518 pub label: &'static str, 519 pub description: &'static str, 520 pub scopes: &'static str, 521} 522 523#[derive(Debug, Serialize)] 524pub struct GetScopePresetsResponse { 525 pub presets: Vec<ScopePresetInfo>, 526} 527 528pub async fn get_scope_presets() -> Response { 529 Json(GetScopePresetsResponse { 530 presets: delegation::SCOPE_PRESETS 531 .iter() 532 .map(|p| ScopePresetInfo { 533 name: p.name, 534 label: p.label, 535 description: p.description, 536 scopes: p.scopes, 537 }) 538 .collect(), 539 }) 540 .into_response() 541} 542 543#[derive(Debug, Deserialize)] 544#[serde(rename_all = "camelCase")] 545pub struct CreateDelegatedAccountInput { 546 pub handle: String, 547 pub email: Option<String>, 548 pub controller_scopes: String, 549 pub invite_code: Option<String>, 550} 551 552#[derive(Debug, Serialize)] 553#[serde(rename_all = "camelCase")] 554pub struct CreateDelegatedAccountResponse { 555 pub did: String, 556 pub handle: String, 557} 558 559pub async fn create_delegated_account( 560 State(state): State<AppState>, 561 headers: HeaderMap, 562 auth: BearerAuth, 563 Json(input): Json<CreateDelegatedAccountInput>, 564) -> Response { 565 let client_ip = extract_client_ip(&headers); 566 if !state 567 .check_rate_limit(RateLimitKind::AccountCreation, &client_ip) 568 .await 569 { 570 warn!(ip = %client_ip, "Delegated account creation rate limit exceeded"); 571 return ( 572 StatusCode::TOO_MANY_REQUESTS, 573 Json(json!({ 574 "error": "RateLimitExceeded", 575 "message": "Too many account creation attempts. Please try again later." 576 })), 577 ) 578 .into_response(); 579 } 580 581 if let Err(e) = delegation::scopes::validate_delegation_scopes(&input.controller_scopes) { 582 return ( 583 StatusCode::BAD_REQUEST, 584 Json(json!({ 585 "error": "InvalidScopes", 586 "message": e 587 })), 588 ) 589 .into_response(); 590 } 591 592 match delegation::has_any_controllers(&state.db, &auth.0.did).await { 593 Ok(true) => { 594 return ( 595 StatusCode::BAD_REQUEST, 596 Json(json!({ 597 "error": "InvalidDelegation", 598 "message": "Cannot create delegated accounts from a controlled account" 599 })), 600 ) 601 .into_response(); 602 } 603 Err(e) => { 604 tracing::error!("Failed to check controller status: {:?}", e); 605 return ( 606 StatusCode::INTERNAL_SERVER_ERROR, 607 Json(json!({ 608 "error": "ServerError", 609 "message": "Failed to verify controller status" 610 })), 611 ) 612 .into_response(); 613 } 614 Ok(false) => {} 615 } 616 617 let hostname = std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string()); 618 let pds_suffix = format!(".{}", hostname); 619 620 let handle = if !input.handle.contains('.') || input.handle.ends_with(&pds_suffix) { 621 let handle_to_validate = if input.handle.ends_with(&pds_suffix) { 622 input 623 .handle 624 .strip_suffix(&pds_suffix) 625 .unwrap_or(&input.handle) 626 } else { 627 &input.handle 628 }; 629 match crate::api::validation::validate_short_handle(handle_to_validate) { 630 Ok(h) => format!("{}.{}", h, hostname), 631 Err(e) => { 632 return ( 633 StatusCode::BAD_REQUEST, 634 Json(json!({"error": "InvalidHandle", "message": e.to_string()})), 635 ) 636 .into_response(); 637 } 638 } 639 } else { 640 input.handle.to_lowercase() 641 }; 642 643 let email = input 644 .email 645 .as_ref() 646 .map(|e| e.trim().to_string()) 647 .filter(|e| !e.is_empty()); 648 if let Some(ref email) = email 649 && !crate::api::validation::is_valid_email(email) 650 { 651 return ( 652 StatusCode::BAD_REQUEST, 653 Json(json!({"error": "InvalidEmail", "message": "Invalid email format"})), 654 ) 655 .into_response(); 656 } 657 658 if let Some(ref code) = input.invite_code { 659 let valid = sqlx::query_scalar!( 660 "SELECT available_uses > 0 AND NOT disabled FROM invite_codes WHERE code = $1", 661 code 662 ) 663 .fetch_optional(&state.db) 664 .await 665 .ok() 666 .flatten() 667 .unwrap_or(Some(false)); 668 669 if valid != Some(true) { 670 return ( 671 StatusCode::BAD_REQUEST, 672 Json(json!({"error": "InvalidInviteCode", "message": "Invalid or expired invite code"})), 673 ) 674 .into_response(); 675 } 676 } else { 677 let invite_required = std::env::var("INVITE_CODE_REQUIRED") 678 .map(|v| v == "true" || v == "1") 679 .unwrap_or(false); 680 if invite_required { 681 return ( 682 StatusCode::BAD_REQUEST, 683 Json(json!({"error": "InviteCodeRequired", "message": "An invite code is required to create an account"})), 684 ) 685 .into_response(); 686 } 687 } 688 689 use k256::ecdsa::SigningKey; 690 use rand::rngs::OsRng; 691 692 let pds_endpoint = format!("https://{}", hostname); 693 let secret_key = k256::SecretKey::random(&mut OsRng); 694 let secret_key_bytes = secret_key.to_bytes().to_vec(); 695 696 let signing_key = match SigningKey::from_slice(&secret_key_bytes) { 697 Ok(k) => k, 698 Err(e) => { 699 error!("Error creating signing key: {:?}", e); 700 return ( 701 StatusCode::INTERNAL_SERVER_ERROR, 702 Json(json!({"error": "InternalError"})), 703 ) 704 .into_response(); 705 } 706 }; 707 708 let rotation_key = std::env::var("PLC_ROTATION_KEY") 709 .unwrap_or_else(|_| crate::plc::signing_key_to_did_key(&signing_key)); 710 711 let genesis_result = match crate::plc::create_genesis_operation( 712 &signing_key, 713 &rotation_key, 714 &handle, 715 &pds_endpoint, 716 ) { 717 Ok(r) => r, 718 Err(e) => { 719 error!("Error creating PLC genesis operation: {:?}", e); 720 return ( 721 StatusCode::INTERNAL_SERVER_ERROR, 722 Json( 723 json!({"error": "InternalError", "message": "Failed to create PLC operation"}), 724 ), 725 ) 726 .into_response(); 727 } 728 }; 729 730 let plc_client = crate::plc::PlcClient::new(None); 731 if let Err(e) = plc_client 732 .send_operation(&genesis_result.did, &genesis_result.signed_operation) 733 .await 734 { 735 error!("Failed to submit PLC genesis operation: {:?}", e); 736 return ( 737 StatusCode::BAD_GATEWAY, 738 Json(json!({ 739 "error": "UpstreamError", 740 "message": format!("Failed to register DID with PLC directory: {}", e) 741 })), 742 ) 743 .into_response(); 744 } 745 746 let did = genesis_result.did; 747 info!(did = %did, handle = %handle, controller = %auth.0.did, "Created DID for delegated account"); 748 749 let mut tx = match state.db.begin().await { 750 Ok(tx) => tx, 751 Err(e) => { 752 error!("Error starting transaction: {:?}", e); 753 return ( 754 StatusCode::INTERNAL_SERVER_ERROR, 755 Json(json!({"error": "InternalError"})), 756 ) 757 .into_response(); 758 } 759 }; 760 761 let user_insert: Result<(uuid::Uuid,), _> = sqlx::query_as( 762 r#"INSERT INTO users ( 763 handle, email, did, password_hash, password_required, 764 account_type, preferred_comms_channel 765 ) VALUES ($1, $2, $3, NULL, FALSE, 'delegated'::account_type, 'email'::comms_channel) RETURNING id"#, 766 ) 767 .bind(&handle) 768 .bind(&email) 769 .bind(&did) 770 .fetch_one(&mut *tx) 771 .await; 772 773 let user_id = match user_insert { 774 Ok((id,)) => id, 775 Err(e) => { 776 if let Some(db_err) = e.as_database_error() 777 && db_err.code().as_deref() == Some("23505") 778 { 779 let constraint = db_err.constraint().unwrap_or(""); 780 if constraint.contains("handle") { 781 return ( 782 StatusCode::BAD_REQUEST, 783 Json(json!({"error": "HandleNotAvailable", "message": "Handle already taken"})), 784 ) 785 .into_response(); 786 } else if constraint.contains("email") { 787 return ( 788 StatusCode::BAD_REQUEST, 789 Json( 790 json!({"error": "InvalidEmail", "message": "Email already registered"}), 791 ), 792 ) 793 .into_response(); 794 } 795 } 796 error!("Error inserting user: {:?}", e); 797 return ( 798 StatusCode::INTERNAL_SERVER_ERROR, 799 Json(json!({"error": "InternalError"})), 800 ) 801 .into_response(); 802 } 803 }; 804 805 let encrypted_key_bytes = match crate::config::encrypt_key(&secret_key_bytes) { 806 Ok(bytes) => bytes, 807 Err(e) => { 808 error!("Error encrypting signing key: {:?}", e); 809 return ( 810 StatusCode::INTERNAL_SERVER_ERROR, 811 Json(json!({"error": "InternalError"})), 812 ) 813 .into_response(); 814 } 815 }; 816 817 if let Err(e) = sqlx::query!( 818 "INSERT INTO user_keys (user_id, key_bytes, encryption_version, encrypted_at) VALUES ($1, $2, $3, NOW())", 819 user_id, 820 &encrypted_key_bytes[..], 821 crate::config::ENCRYPTION_VERSION 822 ) 823 .execute(&mut *tx) 824 .await 825 { 826 error!("Error inserting user key: {:?}", e); 827 return ( 828 StatusCode::INTERNAL_SERVER_ERROR, 829 Json(json!({"error": "InternalError"})), 830 ) 831 .into_response(); 832 } 833 834 if let Err(e) = sqlx::query!( 835 r#"INSERT INTO account_delegations (delegated_did, controller_did, granted_scopes, granted_by) 836 VALUES ($1, $2, $3, $4)"#, 837 did, 838 auth.0.did, 839 input.controller_scopes, 840 auth.0.did 841 ) 842 .execute(&mut *tx) 843 .await 844 { 845 error!("Error creating initial delegation: {:?}", e); 846 return ( 847 StatusCode::INTERNAL_SERVER_ERROR, 848 Json(json!({"error": "InternalError"})), 849 ) 850 .into_response(); 851 } 852 853 let mst = Mst::new(Arc::new(state.block_store.clone())); 854 let mst_root = match mst.persist().await { 855 Ok(c) => c, 856 Err(e) => { 857 error!("Error persisting MST: {:?}", e); 858 return ( 859 StatusCode::INTERNAL_SERVER_ERROR, 860 Json(json!({"error": "InternalError"})), 861 ) 862 .into_response(); 863 } 864 }; 865 let rev = Tid::now(LimitedU32::MIN); 866 let (commit_bytes, _sig) = 867 match create_signed_commit(&did, mst_root, rev.as_ref(), None, &signing_key) { 868 Ok(result) => result, 869 Err(e) => { 870 error!("Error creating genesis commit: {:?}", e); 871 return ( 872 StatusCode::INTERNAL_SERVER_ERROR, 873 Json(json!({"error": "InternalError"})), 874 ) 875 .into_response(); 876 } 877 }; 878 let commit_cid: cid::Cid = match state.block_store.put(&commit_bytes).await { 879 Ok(c) => c, 880 Err(e) => { 881 error!("Error saving genesis commit: {:?}", e); 882 return ( 883 StatusCode::INTERNAL_SERVER_ERROR, 884 Json(json!({"error": "InternalError"})), 885 ) 886 .into_response(); 887 } 888 }; 889 let commit_cid_str = commit_cid.to_string(); 890 if let Err(e) = sqlx::query!( 891 "INSERT INTO repos (user_id, repo_root_cid) VALUES ($1, $2)", 892 user_id, 893 commit_cid_str 894 ) 895 .execute(&mut *tx) 896 .await 897 { 898 error!("Error inserting repo: {:?}", e); 899 return ( 900 StatusCode::INTERNAL_SERVER_ERROR, 901 Json(json!({"error": "InternalError"})), 902 ) 903 .into_response(); 904 } 905 906 if let Some(ref code) = input.invite_code { 907 let _ = sqlx::query!( 908 "UPDATE invite_codes SET available_uses = available_uses - 1 WHERE code = $1", 909 code 910 ) 911 .execute(&mut *tx) 912 .await; 913 914 let _ = sqlx::query!( 915 "INSERT INTO invite_code_uses (code, used_by_user) VALUES ($1, $2)", 916 code, 917 user_id 918 ) 919 .execute(&mut *tx) 920 .await; 921 } 922 923 if let Err(e) = tx.commit().await { 924 error!("Error committing transaction: {:?}", e); 925 return ( 926 StatusCode::INTERNAL_SERVER_ERROR, 927 Json(json!({"error": "InternalError"})), 928 ) 929 .into_response(); 930 } 931 932 if let Err(e) = 933 crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle)).await 934 { 935 warn!("Failed to sequence identity event for {}: {}", did, e); 936 } 937 if let Err(e) = crate::api::repo::record::sequence_account_event(&state, &did, true, None).await 938 { 939 warn!("Failed to sequence account event for {}: {}", did, e); 940 } 941 942 let profile_record = json!({ 943 "$type": "app.bsky.actor.profile", 944 "displayName": handle 945 }); 946 if let Err(e) = crate::api::repo::record::create_record_internal( 947 &state, 948 &did, 949 "app.bsky.actor.profile", 950 "self", 951 &profile_record, 952 ) 953 .await 954 { 955 warn!("Failed to create default profile for {}: {}", did, e); 956 } 957 958 let _ = delegation::log_delegation_action( 959 &state.db, 960 &did, 961 &auth.0.did, 962 Some(&auth.0.did), 963 DelegationActionType::GrantCreated, 964 Some(json!({ 965 "account_created": true, 966 "granted_scopes": input.controller_scopes 967 })), 968 None, 969 None, 970 ) 971 .await; 972 973 info!(did = %did, handle = %handle, controller = %auth.0.did, "Delegated account created"); 974 975 Json(CreateDelegatedAccountResponse { did, handle }).into_response() 976}