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.clamp(1, 100); 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 = delegation::audit::count_audit_log_entries(&state.db, &auth.0.did) 493 .await 494 .unwrap_or_default(); 495 496 Json(GetAuditLogResponse { 497 entries: entries 498 .into_iter() 499 .map(|e| AuditLogEntry { 500 id: e.id.to_string(), 501 delegated_did: e.delegated_did, 502 actor_did: e.actor_did, 503 controller_did: e.controller_did, 504 action_type: format!("{:?}", e.action_type), 505 action_details: e.action_details, 506 created_at: e.created_at, 507 }) 508 .collect(), 509 total, 510 }) 511 .into_response() 512} 513 514#[derive(Debug, Serialize)] 515pub struct ScopePresetInfo { 516 pub name: &'static str, 517 pub label: &'static str, 518 pub description: &'static str, 519 pub scopes: &'static str, 520} 521 522#[derive(Debug, Serialize)] 523pub struct GetScopePresetsResponse { 524 pub presets: Vec<ScopePresetInfo>, 525} 526 527pub async fn get_scope_presets() -> Response { 528 Json(GetScopePresetsResponse { 529 presets: delegation::SCOPE_PRESETS 530 .iter() 531 .map(|p| ScopePresetInfo { 532 name: p.name, 533 label: p.label, 534 description: p.description, 535 scopes: p.scopes, 536 }) 537 .collect(), 538 }) 539 .into_response() 540} 541 542#[derive(Debug, Deserialize)] 543#[serde(rename_all = "camelCase")] 544pub struct CreateDelegatedAccountInput { 545 pub handle: String, 546 pub email: Option<String>, 547 pub controller_scopes: String, 548 pub invite_code: Option<String>, 549} 550 551#[derive(Debug, Serialize)] 552#[serde(rename_all = "camelCase")] 553pub struct CreateDelegatedAccountResponse { 554 pub did: String, 555 pub handle: String, 556} 557 558pub async fn create_delegated_account( 559 State(state): State<AppState>, 560 headers: HeaderMap, 561 auth: BearerAuth, 562 Json(input): Json<CreateDelegatedAccountInput>, 563) -> Response { 564 let client_ip = extract_client_ip(&headers); 565 if !state 566 .check_rate_limit(RateLimitKind::AccountCreation, &client_ip) 567 .await 568 { 569 warn!(ip = %client_ip, "Delegated account creation rate limit exceeded"); 570 return ( 571 StatusCode::TOO_MANY_REQUESTS, 572 Json(json!({ 573 "error": "RateLimitExceeded", 574 "message": "Too many account creation attempts. Please try again later." 575 })), 576 ) 577 .into_response(); 578 } 579 580 if let Err(e) = delegation::scopes::validate_delegation_scopes(&input.controller_scopes) { 581 return ( 582 StatusCode::BAD_REQUEST, 583 Json(json!({ 584 "error": "InvalidScopes", 585 "message": e 586 })), 587 ) 588 .into_response(); 589 } 590 591 match delegation::has_any_controllers(&state.db, &auth.0.did).await { 592 Ok(true) => { 593 return ( 594 StatusCode::BAD_REQUEST, 595 Json(json!({ 596 "error": "InvalidDelegation", 597 "message": "Cannot create delegated accounts from a controlled account" 598 })), 599 ) 600 .into_response(); 601 } 602 Err(e) => { 603 tracing::error!("Failed to check controller status: {:?}", e); 604 return ( 605 StatusCode::INTERNAL_SERVER_ERROR, 606 Json(json!({ 607 "error": "ServerError", 608 "message": "Failed to verify controller status" 609 })), 610 ) 611 .into_response(); 612 } 613 Ok(false) => {} 614 } 615 616 let hostname = std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string()); 617 let pds_suffix = format!(".{}", hostname); 618 619 let handle = if !input.handle.contains('.') || input.handle.ends_with(&pds_suffix) { 620 let handle_to_validate = if input.handle.ends_with(&pds_suffix) { 621 input 622 .handle 623 .strip_suffix(&pds_suffix) 624 .unwrap_or(&input.handle) 625 } else { 626 &input.handle 627 }; 628 match crate::api::validation::validate_short_handle(handle_to_validate) { 629 Ok(h) => format!("{}.{}", h, hostname), 630 Err(e) => { 631 return ( 632 StatusCode::BAD_REQUEST, 633 Json(json!({"error": "InvalidHandle", "message": e.to_string()})), 634 ) 635 .into_response(); 636 } 637 } 638 } else { 639 input.handle.to_lowercase() 640 }; 641 642 let email = input 643 .email 644 .as_ref() 645 .map(|e| e.trim().to_string()) 646 .filter(|e| !e.is_empty()); 647 if let Some(ref email) = email 648 && !crate::api::validation::is_valid_email(email) 649 { 650 return ( 651 StatusCode::BAD_REQUEST, 652 Json(json!({"error": "InvalidEmail", "message": "Invalid email format"})), 653 ) 654 .into_response(); 655 } 656 657 if let Some(ref code) = input.invite_code { 658 let valid = sqlx::query_scalar!( 659 "SELECT available_uses > 0 AND NOT disabled FROM invite_codes WHERE code = $1", 660 code 661 ) 662 .fetch_optional(&state.db) 663 .await 664 .ok() 665 .flatten() 666 .unwrap_or(Some(false)); 667 668 if valid != Some(true) { 669 return ( 670 StatusCode::BAD_REQUEST, 671 Json(json!({"error": "InvalidInviteCode", "message": "Invalid or expired invite code"})), 672 ) 673 .into_response(); 674 } 675 } else { 676 let invite_required = std::env::var("INVITE_CODE_REQUIRED") 677 .map(|v| v == "true" || v == "1") 678 .unwrap_or(false); 679 if invite_required { 680 return ( 681 StatusCode::BAD_REQUEST, 682 Json(json!({"error": "InviteCodeRequired", "message": "An invite code is required to create an account"})), 683 ) 684 .into_response(); 685 } 686 } 687 688 use k256::ecdsa::SigningKey; 689 use rand::rngs::OsRng; 690 691 let pds_endpoint = format!("https://{}", hostname); 692 let secret_key = k256::SecretKey::random(&mut OsRng); 693 let secret_key_bytes = secret_key.to_bytes().to_vec(); 694 695 let signing_key = match SigningKey::from_slice(&secret_key_bytes) { 696 Ok(k) => k, 697 Err(e) => { 698 error!("Error creating signing key: {:?}", e); 699 return ( 700 StatusCode::INTERNAL_SERVER_ERROR, 701 Json(json!({"error": "InternalError"})), 702 ) 703 .into_response(); 704 } 705 }; 706 707 let rotation_key = std::env::var("PLC_ROTATION_KEY") 708 .unwrap_or_else(|_| crate::plc::signing_key_to_did_key(&signing_key)); 709 710 let genesis_result = match crate::plc::create_genesis_operation( 711 &signing_key, 712 &rotation_key, 713 &handle, 714 &pds_endpoint, 715 ) { 716 Ok(r) => r, 717 Err(e) => { 718 error!("Error creating PLC genesis operation: {:?}", e); 719 return ( 720 StatusCode::INTERNAL_SERVER_ERROR, 721 Json( 722 json!({"error": "InternalError", "message": "Failed to create PLC operation"}), 723 ), 724 ) 725 .into_response(); 726 } 727 }; 728 729 let plc_client = crate::plc::PlcClient::new(None); 730 if let Err(e) = plc_client 731 .send_operation(&genesis_result.did, &genesis_result.signed_operation) 732 .await 733 { 734 error!("Failed to submit PLC genesis operation: {:?}", e); 735 return ( 736 StatusCode::BAD_GATEWAY, 737 Json(json!({ 738 "error": "UpstreamError", 739 "message": format!("Failed to register DID with PLC directory: {}", e) 740 })), 741 ) 742 .into_response(); 743 } 744 745 let did = genesis_result.did; 746 info!(did = %did, handle = %handle, controller = %auth.0.did, "Created DID for delegated account"); 747 748 let mut tx = match state.db.begin().await { 749 Ok(tx) => tx, 750 Err(e) => { 751 error!("Error starting transaction: {:?}", e); 752 return ( 753 StatusCode::INTERNAL_SERVER_ERROR, 754 Json(json!({"error": "InternalError"})), 755 ) 756 .into_response(); 757 } 758 }; 759 760 let user_insert: Result<(uuid::Uuid,), _> = sqlx::query_as( 761 r#"INSERT INTO users ( 762 handle, email, did, password_hash, password_required, 763 account_type, preferred_comms_channel 764 ) VALUES ($1, $2, $3, NULL, FALSE, 'delegated'::account_type, 'email'::comms_channel) RETURNING id"#, 765 ) 766 .bind(&handle) 767 .bind(&email) 768 .bind(&did) 769 .fetch_one(&mut *tx) 770 .await; 771 772 let user_id = match user_insert { 773 Ok((id,)) => id, 774 Err(e) => { 775 if let Some(db_err) = e.as_database_error() 776 && db_err.code().as_deref() == Some("23505") 777 { 778 let constraint = db_err.constraint().unwrap_or(""); 779 if constraint.contains("handle") { 780 return ( 781 StatusCode::BAD_REQUEST, 782 Json(json!({"error": "HandleNotAvailable", "message": "Handle already taken"})), 783 ) 784 .into_response(); 785 } else if constraint.contains("email") { 786 return ( 787 StatusCode::BAD_REQUEST, 788 Json( 789 json!({"error": "InvalidEmail", "message": "Email already registered"}), 790 ), 791 ) 792 .into_response(); 793 } 794 } 795 error!("Error inserting user: {:?}", e); 796 return ( 797 StatusCode::INTERNAL_SERVER_ERROR, 798 Json(json!({"error": "InternalError"})), 799 ) 800 .into_response(); 801 } 802 }; 803 804 let encrypted_key_bytes = match crate::config::encrypt_key(&secret_key_bytes) { 805 Ok(bytes) => bytes, 806 Err(e) => { 807 error!("Error encrypting signing key: {:?}", e); 808 return ( 809 StatusCode::INTERNAL_SERVER_ERROR, 810 Json(json!({"error": "InternalError"})), 811 ) 812 .into_response(); 813 } 814 }; 815 816 if let Err(e) = sqlx::query!( 817 "INSERT INTO user_keys (user_id, key_bytes, encryption_version, encrypted_at) VALUES ($1, $2, $3, NOW())", 818 user_id, 819 &encrypted_key_bytes[..], 820 crate::config::ENCRYPTION_VERSION 821 ) 822 .execute(&mut *tx) 823 .await 824 { 825 error!("Error inserting user key: {:?}", e); 826 return ( 827 StatusCode::INTERNAL_SERVER_ERROR, 828 Json(json!({"error": "InternalError"})), 829 ) 830 .into_response(); 831 } 832 833 if let Err(e) = sqlx::query!( 834 r#"INSERT INTO account_delegations (delegated_did, controller_did, granted_scopes, granted_by) 835 VALUES ($1, $2, $3, $4)"#, 836 did, 837 auth.0.did, 838 input.controller_scopes, 839 auth.0.did 840 ) 841 .execute(&mut *tx) 842 .await 843 { 844 error!("Error creating initial delegation: {:?}", e); 845 return ( 846 StatusCode::INTERNAL_SERVER_ERROR, 847 Json(json!({"error": "InternalError"})), 848 ) 849 .into_response(); 850 } 851 852 let mst = Mst::new(Arc::new(state.block_store.clone())); 853 let mst_root = match mst.persist().await { 854 Ok(c) => c, 855 Err(e) => { 856 error!("Error persisting MST: {:?}", e); 857 return ( 858 StatusCode::INTERNAL_SERVER_ERROR, 859 Json(json!({"error": "InternalError"})), 860 ) 861 .into_response(); 862 } 863 }; 864 let rev = Tid::now(LimitedU32::MIN); 865 let (commit_bytes, _sig) = 866 match create_signed_commit(&did, mst_root, rev.as_ref(), None, &signing_key) { 867 Ok(result) => result, 868 Err(e) => { 869 error!("Error creating genesis commit: {:?}", e); 870 return ( 871 StatusCode::INTERNAL_SERVER_ERROR, 872 Json(json!({"error": "InternalError"})), 873 ) 874 .into_response(); 875 } 876 }; 877 let commit_cid: cid::Cid = match state.block_store.put(&commit_bytes).await { 878 Ok(c) => c, 879 Err(e) => { 880 error!("Error saving genesis commit: {:?}", e); 881 return ( 882 StatusCode::INTERNAL_SERVER_ERROR, 883 Json(json!({"error": "InternalError"})), 884 ) 885 .into_response(); 886 } 887 }; 888 let commit_cid_str = commit_cid.to_string(); 889 if let Err(e) = sqlx::query!( 890 "INSERT INTO repos (user_id, repo_root_cid) VALUES ($1, $2)", 891 user_id, 892 commit_cid_str 893 ) 894 .execute(&mut *tx) 895 .await 896 { 897 error!("Error inserting repo: {:?}", e); 898 return ( 899 StatusCode::INTERNAL_SERVER_ERROR, 900 Json(json!({"error": "InternalError"})), 901 ) 902 .into_response(); 903 } 904 905 if let Some(ref code) = input.invite_code { 906 let _ = sqlx::query!( 907 "UPDATE invite_codes SET available_uses = available_uses - 1 WHERE code = $1", 908 code 909 ) 910 .execute(&mut *tx) 911 .await; 912 913 let _ = sqlx::query!( 914 "INSERT INTO invite_code_uses (code, used_by_user) VALUES ($1, $2)", 915 code, 916 user_id 917 ) 918 .execute(&mut *tx) 919 .await; 920 } 921 922 if let Err(e) = tx.commit().await { 923 error!("Error committing transaction: {:?}", e); 924 return ( 925 StatusCode::INTERNAL_SERVER_ERROR, 926 Json(json!({"error": "InternalError"})), 927 ) 928 .into_response(); 929 } 930 931 if let Err(e) = 932 crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle)).await 933 { 934 warn!("Failed to sequence identity event for {}: {}", did, e); 935 } 936 if let Err(e) = crate::api::repo::record::sequence_account_event(&state, &did, true, None).await 937 { 938 warn!("Failed to sequence account event for {}: {}", did, e); 939 } 940 941 let profile_record = json!({ 942 "$type": "app.bsky.actor.profile", 943 "displayName": handle 944 }); 945 if let Err(e) = crate::api::repo::record::create_record_internal( 946 &state, 947 &did, 948 "app.bsky.actor.profile", 949 "self", 950 &profile_record, 951 ) 952 .await 953 { 954 warn!("Failed to create default profile for {}: {}", did, e); 955 } 956 957 let _ = delegation::log_delegation_action( 958 &state.db, 959 &did, 960 &auth.0.did, 961 Some(&auth.0.did), 962 DelegationActionType::GrantCreated, 963 Some(json!({ 964 "account_created": true, 965 "granted_scopes": input.controller_scopes 966 })), 967 None, 968 None, 969 ) 970 .await; 971 972 info!(did = %did, handle = %handle, controller = %auth.0.did, "Delegated account created"); 973 974 Json(CreateDelegatedAccountResponse { did, handle }).into_response() 975}