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}