tangled
alpha
login
or
join now
tranquil.farm
/
tranquil-pds
151
fork
atom
Our Personal Data Server from scratch!
tranquil.farm
oauth
atproto
pds
rust
postgresql
objectstorage
fun
151
fork
atom
overview
issues
19
pulls
2
pipelines
Better repo action code quality
lewis.moe
2 months ago
cae667d2
fbc24777
+413
-301
15 changed files
expand all
collapse all
unified
split
src
api
admin
account
delete.rs
update.rs
status.rs
delegation.rs
identity
account.rs
did.rs
repo
record
batch.rs
delete.rs
read.rs
utils.rs
validation.rs
write.rs
server
account_status.rs
passkey_account.rs
tests
commit_signing.rs
+1
-1
src/api/admin/account/delete.rs
···
132
132
}
133
133
if let Err(e) = crate::api::repo::record::sequence_account_event(
134
134
&state,
135
135
-
did.as_str(),
135
135
+
did,
136
136
false,
137
137
Some("deleted"),
138
138
)
+4
-3
src/api/admin/account/update.rs
···
2
2
use crate::api::error::ApiError;
3
3
use crate::auth::BearerAuthAdmin;
4
4
use crate::state::AppState;
5
5
-
use crate::types::{Did, PlainPassword};
5
5
+
use crate::types::{Did, Handle, PlainPassword};
6
6
use axum::{
7
7
Json,
8
8
extract::State,
···
103
103
let _ = state.cache.delete(&format!("handle:{}", old)).await;
104
104
}
105
105
let _ = state.cache.delete(&format!("handle:{}", handle)).await;
106
106
+
let handle_typed = Handle::new_unchecked(&handle);
106
107
if let Err(e) = crate::api::repo::record::sequence_identity_event(
107
108
&state,
108
108
-
did.as_str(),
109
109
-
Some(&handle),
109
109
+
did,
110
110
+
Some(&handle_typed),
110
111
)
111
112
.await
112
113
{
+10
-8
src/api/admin/status.rs
···
1
1
use crate::api::error::ApiError;
2
2
use crate::auth::BearerAuthAdmin;
3
3
use crate::state::AppState;
4
4
+
use crate::types::Did;
4
5
use axum::{
5
6
Json,
6
7
extract::{Query, State},
···
183
184
let subject_type = input.subject.get("$type").and_then(|t| t.as_str());
184
185
match subject_type {
185
186
Some("com.atproto.admin.defs#repoRef") => {
186
186
-
let did = input.subject.get("did").and_then(|d| d.as_str());
187
187
-
if let Some(did) = did {
187
187
+
let did_str = input.subject.get("did").and_then(|d| d.as_str());
188
188
+
if let Some(did_str) = did_str {
189
189
+
let did = Did::new_unchecked(did_str);
188
190
let mut tx = match state.db.begin().await {
189
191
Ok(tx) => tx,
190
192
Err(e) => {
···
201
203
if let Err(e) = sqlx::query!(
202
204
"UPDATE users SET takedown_ref = $1 WHERE did = $2",
203
205
takedown_ref,
204
204
-
did
206
206
+
did.as_str()
205
207
)
206
208
.execute(&mut *tx)
207
209
.await
···
217
219
let result = if deactivated.applied {
218
220
sqlx::query!(
219
221
"UPDATE users SET deactivated_at = NOW() WHERE did = $1",
220
220
-
did
222
222
+
did.as_str()
221
223
)
222
224
.execute(&mut *tx)
223
225
.await
224
226
} else {
225
225
-
sqlx::query!("UPDATE users SET deactivated_at = NULL WHERE did = $1", did)
227
227
+
sqlx::query!("UPDATE users SET deactivated_at = NULL WHERE did = $1", did.as_str())
226
228
.execute(&mut *tx)
227
229
.await
228
230
};
···
249
251
};
250
252
if let Err(e) = crate::api::repo::record::sequence_account_event(
251
253
&state,
252
252
-
did,
254
254
+
&did,
253
255
!takedown.applied,
254
256
status,
255
257
)
···
266
268
};
267
269
if let Err(e) = crate::api::repo::record::sequence_account_event(
268
270
&state,
269
269
-
did,
271
271
+
&did,
270
272
!deactivated.applied,
271
273
status,
272
274
)
···
276
278
}
277
279
}
278
280
if let Ok(Some(handle)) =
279
279
-
sqlx::query_scalar!("SELECT handle FROM users WHERE did = $1", did)
281
281
+
sqlx::query_scalar!("SELECT handle FROM users WHERE did = $1", did.as_str())
280
282
.fetch_optional(&state.db)
281
283
.await
282
284
{
+12
-9
src/api/delegation.rs
···
4
4
use crate::delegation::{self, DelegationActionType};
5
5
use crate::oauth::db as oauth_db;
6
6
use crate::state::{AppState, RateLimitKind};
7
7
-
use crate::types::{Did, Handle};
7
7
+
use crate::types::{Did, Handle, Nsid, Rkey};
8
8
use crate::util::extract_client_ip;
9
9
use axum::{
10
10
Json,
···
568
568
.into_response();
569
569
}
570
570
571
571
-
let did = genesis_result.did;
571
571
+
let did = Did::new_unchecked(&genesis_result.did);
572
572
+
let handle = Handle::new_unchecked(&handle);
572
573
info!(did = %did, handle = %handle, controller = %&auth.0.did, "Created DID for delegated account");
573
574
574
575
let mut tx = match state.db.begin().await {
···
585
586
account_type, preferred_comms_channel
586
587
) VALUES ($1, $2, $3, NULL, FALSE, 'delegated'::account_type, 'email'::comms_channel) RETURNING id"#,
587
588
)
588
588
-
.bind(&handle)
589
589
+
.bind(handle.as_str())
589
590
.bind(&email)
590
590
-
.bind(&did)
591
591
+
.bind(did.as_str())
591
592
.fetch_one(&mut *tx)
592
593
.await;
593
594
···
633
634
if let Err(e) = sqlx::query!(
634
635
r#"INSERT INTO account_delegations (delegated_did, controller_did, granted_scopes, granted_by)
635
636
VALUES ($1, $2, $3, $4)"#,
636
636
-
did,
637
637
-
&auth.0.did,
637
637
+
did.as_str(),
638
638
+
auth.0.did.as_str(),
638
639
input.controller_scopes,
639
639
-
&auth.0.did
640
640
+
auth.0.did.as_str()
640
641
)
641
642
.execute(&mut *tx)
642
643
.await
···
736
737
"$type": "app.bsky.actor.profile",
737
738
"displayName": handle
738
739
});
740
740
+
let profile_collection = Nsid::new_unchecked("app.bsky.actor.profile");
741
741
+
let profile_rkey = Rkey::new_unchecked("self");
739
742
if let Err(e) = crate::api::repo::record::create_record_internal(
740
743
&state,
741
744
&did,
742
742
-
"app.bsky.actor.profile",
743
743
-
"self",
745
745
+
&profile_collection,
746
746
+
&profile_rkey,
744
747
&profile_record,
745
748
)
746
749
.await
+14
-9
src/api/identity/account.rs
···
4
4
use crate::auth::{ServiceTokenVerifier, is_service_token};
5
5
use crate::plc::{PlcClient, create_genesis_operation, signing_key_to_did_key};
6
6
use crate::state::{AppState, RateLimitKind};
7
7
-
use crate::types::{Did, Handle, PlainPassword};
7
7
+
use crate::types::{Did, Handle, Nsid, PlainPassword, Rkey};
8
8
use crate::validation::validate_password;
9
9
use axum::{
10
10
Json,
···
710
710
}
711
711
};
712
712
let rev = Tid::now(LimitedU32::MIN);
713
713
+
let did_for_commit = Did::new_unchecked(&did);
713
714
let (commit_bytes, _sig) =
714
714
-
match create_signed_commit(&did, mst_root, rev.as_ref(), None, &signing_key) {
715
715
+
match create_signed_commit(&did_for_commit, mst_root, rev.as_ref(), None, &signing_key) {
715
716
Ok(result) => result,
716
717
Err(e) => {
717
718
error!("Error creating genesis commit: {:?}", e);
···
793
794
return ApiError::InternalError(None).into_response();
794
795
}
795
796
if !is_migration && !is_did_web_byod {
797
797
+
let did_typed = Did::new_unchecked(&did);
798
798
+
let handle_typed = Handle::new_unchecked(&handle);
796
799
if let Err(e) =
797
797
-
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle)).await
800
800
+
crate::api::repo::record::sequence_identity_event(&state, &did_typed, Some(&handle_typed)).await
798
801
{
799
802
warn!("Failed to sequence identity event for {}: {}", did, e);
800
803
}
801
804
if let Err(e) =
802
802
-
crate::api::repo::record::sequence_account_event(&state, &did, true, None).await
805
805
+
crate::api::repo::record::sequence_account_event(&state, &did_typed, true, None).await
803
806
{
804
807
warn!("Failed to sequence account event for {}: {}", did, e);
805
808
}
806
809
if let Err(e) = crate::api::repo::record::sequence_genesis_commit(
807
810
&state,
808
808
-
&did,
811
811
+
&did_typed,
809
812
&commit_cid,
810
813
&mst_root,
811
814
&rev_str,
···
816
819
}
817
820
if let Err(e) = crate::api::repo::record::sequence_sync_event(
818
821
&state,
819
819
-
&did,
822
822
+
&did_typed,
820
823
&commit_cid_str,
821
824
Some(rev.as_ref()),
822
825
)
···
828
831
"$type": "app.bsky.actor.profile",
829
832
"displayName": input.handle
830
833
});
834
834
+
let profile_collection = Nsid::new_unchecked("app.bsky.actor.profile");
835
835
+
let profile_rkey = Rkey::new_unchecked("self");
831
836
if let Err(e) = crate::api::repo::record::create_record_internal(
832
837
&state,
833
833
-
&did,
834
834
-
"app.bsky.actor.profile",
835
835
-
"self",
838
838
+
&did_typed,
839
839
+
&profile_collection,
840
840
+
&profile_rkey,
836
841
&profile_record,
837
842
)
838
843
.await
+7
-3
src/api/identity/did.rs
···
2
2
use crate::auth::BearerAuthAllowDeactivated;
3
3
use crate::plc::signing_key_to_did_key;
4
4
use crate::state::AppState;
5
5
+
use crate::types::Handle;
5
6
use axum::{
6
7
Json,
7
8
extract::{Path, Query, State},
···
669
670
format!("{}.{}", new_handle, hostname)
670
671
};
671
672
if full_handle == current_handle {
673
673
+
let handle_typed = Handle::new_unchecked(&full_handle);
672
674
if let Err(e) =
673
673
-
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&full_handle))
675
675
+
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle_typed))
674
676
.await
675
677
{
676
678
warn!("Failed to sequence identity event for handle update: {}", e);
···
692
694
full_handle
693
695
} else {
694
696
if new_handle == current_handle {
697
697
+
let handle_typed = Handle::new_unchecked(&new_handle);
695
698
if let Err(e) =
696
696
-
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&new_handle))
699
699
+
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle_typed))
697
700
.await
698
701
{
699
702
warn!("Failed to sequence identity event for handle update: {}", e);
···
749
752
.await;
750
753
}
751
754
let _ = state.cache.delete(&format!("handle:{}", handle)).await;
755
755
+
let handle_typed = Handle::new_unchecked(&handle);
752
756
if let Err(e) =
753
753
-
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle)).await
757
757
+
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle_typed)).await
754
758
{
755
759
warn!("Failed to sequence identity event for handle update: {}", e);
756
760
}
+190
-139
src/api/repo/record/batch.rs
···
6
6
use crate::delegation::{self, DelegationActionType};
7
7
use crate::repo::tracking::TrackingBlockStore;
8
8
use crate::state::AppState;
9
9
-
use crate::types::{AtIdentifier, AtUri, Nsid, Rkey};
9
9
+
use crate::types::{AtIdentifier, AtUri, Did, Nsid, Rkey};
10
10
use axum::{
11
11
Json,
12
12
extract::State,
···
22
22
use tracing::{error, info};
23
23
24
24
const MAX_BATCH_WRITES: usize = 200;
25
25
+
26
26
+
struct WriteAccumulator {
27
27
+
mst: Mst<TrackingBlockStore>,
28
28
+
results: Vec<WriteResult>,
29
29
+
ops: Vec<RecordOp>,
30
30
+
modified_keys: Vec<String>,
31
31
+
all_blob_cids: Vec<String>,
32
32
+
}
33
33
+
34
34
+
async fn process_single_write(
35
35
+
write: &WriteOp,
36
36
+
acc: WriteAccumulator,
37
37
+
did: &Did,
38
38
+
validate: Option<bool>,
39
39
+
tracking_store: &TrackingBlockStore,
40
40
+
) -> Result<WriteAccumulator, Response> {
41
41
+
let WriteAccumulator {
42
42
+
mst,
43
43
+
mut results,
44
44
+
mut ops,
45
45
+
mut modified_keys,
46
46
+
mut all_blob_cids,
47
47
+
} = acc;
48
48
+
49
49
+
match write {
50
50
+
WriteOp::Create {
51
51
+
collection,
52
52
+
rkey,
53
53
+
value,
54
54
+
} => {
55
55
+
let validation_status = match validate {
56
56
+
Some(false) => None,
57
57
+
_ => {
58
58
+
let require_lexicon = validate == Some(true);
59
59
+
match validate_record_with_status(
60
60
+
value,
61
61
+
collection,
62
62
+
rkey.as_ref(),
63
63
+
require_lexicon,
64
64
+
) {
65
65
+
Ok(status) => Some(status),
66
66
+
Err(err_response) => return Err(*err_response),
67
67
+
}
68
68
+
}
69
69
+
};
70
70
+
all_blob_cids.extend(extract_blob_cids(value));
71
71
+
let rkey = rkey.clone().unwrap_or_else(Rkey::generate);
72
72
+
let record_ipld = crate::util::json_to_ipld(value);
73
73
+
let record_bytes = serde_ipld_dagcbor::to_vec(&record_ipld).map_err(|_| {
74
74
+
ApiError::InvalidRecord("Failed to serialize record".into()).into_response()
75
75
+
})?;
76
76
+
let record_cid = tracking_store.put(&record_bytes).await.map_err(|_| {
77
77
+
ApiError::InternalError(Some("Failed to store record".into())).into_response()
78
78
+
})?;
79
79
+
let key = format!("{}/{}", collection, rkey);
80
80
+
modified_keys.push(key.clone());
81
81
+
let new_mst = mst.add(&key, record_cid).await.map_err(|_| {
82
82
+
ApiError::InternalError(Some("Failed to add to MST".into())).into_response()
83
83
+
})?;
84
84
+
let uri = AtUri::from_parts(did, collection, &rkey);
85
85
+
results.push(WriteResult::CreateResult {
86
86
+
uri,
87
87
+
cid: record_cid.to_string(),
88
88
+
validation_status: validation_status.map(|s| s.to_string()),
89
89
+
});
90
90
+
ops.push(RecordOp::Create {
91
91
+
collection: collection.clone(),
92
92
+
rkey: rkey.clone(),
93
93
+
cid: record_cid,
94
94
+
});
95
95
+
Ok(WriteAccumulator {
96
96
+
mst: new_mst,
97
97
+
results,
98
98
+
ops,
99
99
+
modified_keys,
100
100
+
all_blob_cids,
101
101
+
})
102
102
+
}
103
103
+
WriteOp::Update {
104
104
+
collection,
105
105
+
rkey,
106
106
+
value,
107
107
+
} => {
108
108
+
let validation_status = match validate {
109
109
+
Some(false) => None,
110
110
+
_ => {
111
111
+
let require_lexicon = validate == Some(true);
112
112
+
match validate_record_with_status(
113
113
+
value,
114
114
+
collection,
115
115
+
Some(rkey),
116
116
+
require_lexicon,
117
117
+
) {
118
118
+
Ok(status) => Some(status),
119
119
+
Err(err_response) => return Err(*err_response),
120
120
+
}
121
121
+
}
122
122
+
};
123
123
+
all_blob_cids.extend(extract_blob_cids(value));
124
124
+
let record_ipld = crate::util::json_to_ipld(value);
125
125
+
let record_bytes = serde_ipld_dagcbor::to_vec(&record_ipld).map_err(|_| {
126
126
+
ApiError::InvalidRecord("Failed to serialize record".into()).into_response()
127
127
+
})?;
128
128
+
let record_cid = tracking_store.put(&record_bytes).await.map_err(|_| {
129
129
+
ApiError::InternalError(Some("Failed to store record".into())).into_response()
130
130
+
})?;
131
131
+
let key = format!("{}/{}", collection, rkey);
132
132
+
modified_keys.push(key.clone());
133
133
+
let prev_record_cid = mst.get(&key).await.ok().flatten();
134
134
+
let new_mst = mst.update(&key, record_cid).await.map_err(|_| {
135
135
+
ApiError::InternalError(Some("Failed to update MST".into())).into_response()
136
136
+
})?;
137
137
+
let uri = AtUri::from_parts(did, collection, rkey);
138
138
+
results.push(WriteResult::UpdateResult {
139
139
+
uri,
140
140
+
cid: record_cid.to_string(),
141
141
+
validation_status: validation_status.map(|s| s.to_string()),
142
142
+
});
143
143
+
ops.push(RecordOp::Update {
144
144
+
collection: collection.clone(),
145
145
+
rkey: rkey.clone(),
146
146
+
cid: record_cid,
147
147
+
prev: prev_record_cid,
148
148
+
});
149
149
+
Ok(WriteAccumulator {
150
150
+
mst: new_mst,
151
151
+
results,
152
152
+
ops,
153
153
+
modified_keys,
154
154
+
all_blob_cids,
155
155
+
})
156
156
+
}
157
157
+
WriteOp::Delete { collection, rkey } => {
158
158
+
let key = format!("{}/{}", collection, rkey);
159
159
+
modified_keys.push(key.clone());
160
160
+
let prev_record_cid = mst.get(&key).await.ok().flatten();
161
161
+
let new_mst = mst.delete(&key).await.map_err(|_| {
162
162
+
ApiError::InternalError(Some("Failed to delete from MST".into())).into_response()
163
163
+
})?;
164
164
+
results.push(WriteResult::DeleteResult {});
165
165
+
ops.push(RecordOp::Delete {
166
166
+
collection: collection.clone(),
167
167
+
rkey: rkey.clone(),
168
168
+
prev: prev_record_cid,
169
169
+
});
170
170
+
Ok(WriteAccumulator {
171
171
+
mst: new_mst,
172
172
+
results,
173
173
+
ops,
174
174
+
modified_keys,
175
175
+
all_blob_cids,
176
176
+
})
177
177
+
}
178
178
+
}
179
179
+
}
180
180
+
181
181
+
async fn process_writes(
182
182
+
writes: &[WriteOp],
183
183
+
initial_mst: Mst<TrackingBlockStore>,
184
184
+
did: &Did,
185
185
+
validate: Option<bool>,
186
186
+
tracking_store: &TrackingBlockStore,
187
187
+
) -> Result<WriteAccumulator, Response> {
188
188
+
use futures::stream::{self, TryStreamExt};
189
189
+
let initial_acc = WriteAccumulator {
190
190
+
mst: initial_mst,
191
191
+
results: Vec::new(),
192
192
+
ops: Vec::new(),
193
193
+
modified_keys: Vec::new(),
194
194
+
all_blob_cids: Vec::new(),
195
195
+
};
196
196
+
stream::iter(writes.iter().map(Ok::<_, Response>))
197
197
+
.try_fold(initial_acc, |acc, write| async move {
198
198
+
process_single_write(write, acc, did, validate, tracking_store).await
199
199
+
})
200
200
+
.await
201
201
+
}
25
202
26
203
#[derive(Deserialize)]
27
204
#[serde(tag = "$type")]
···
237
414
_ => return ApiError::InternalError(Some("Failed to parse commit".into())).into_response(),
238
415
};
239
416
let original_mst = Mst::load(Arc::new(tracking_store.clone()), commit.data, None);
240
240
-
let mut mst = Mst::load(Arc::new(tracking_store.clone()), commit.data, None);
241
241
-
let mut results: Vec<WriteResult> = Vec::new();
242
242
-
let mut ops: Vec<RecordOp> = Vec::new();
243
243
-
let mut modified_keys: Vec<String> = Vec::new();
244
244
-
let mut all_blob_cids: Vec<String> = Vec::new();
245
245
-
for write in &input.writes {
246
246
-
match write {
247
247
-
WriteOp::Create {
248
248
-
collection,
249
249
-
rkey,
250
250
-
value,
251
251
-
} => {
252
252
-
let validation_status = if input.validate == Some(false) {
253
253
-
None
254
254
-
} else {
255
255
-
let require_lexicon = input.validate == Some(true);
256
256
-
match validate_record_with_status(
257
257
-
value,
258
258
-
collection,
259
259
-
rkey.as_ref().map(|r| r.as_str()),
260
260
-
require_lexicon,
261
261
-
) {
262
262
-
Ok(status) => Some(status),
263
263
-
Err(err_response) => return *err_response,
264
264
-
}
265
265
-
};
266
266
-
all_blob_cids.extend(extract_blob_cids(value));
267
267
-
let rkey = rkey.clone().unwrap_or_else(Rkey::generate);
268
268
-
let record_ipld = crate::util::json_to_ipld(value);
269
269
-
let mut record_bytes = Vec::new();
270
270
-
if serde_ipld_dagcbor::to_writer(&mut record_bytes, &record_ipld).is_err() {
271
271
-
return ApiError::InvalidRecord("Failed to serialize record".into())
272
272
-
.into_response();
273
273
-
}
274
274
-
let record_cid = match tracking_store.put(&record_bytes).await {
275
275
-
Ok(c) => c,
276
276
-
Err(_) => {
277
277
-
return ApiError::InternalError(Some("Failed to store record".into()))
278
278
-
.into_response();
279
279
-
}
280
280
-
};
281
281
-
let key = format!("{}/{}", collection, rkey);
282
282
-
modified_keys.push(key.clone());
283
283
-
mst = match mst.add(&key, record_cid).await {
284
284
-
Ok(m) => m,
285
285
-
Err(_) => {
286
286
-
return ApiError::InternalError(Some("Failed to add to MST".into()))
287
287
-
.into_response();
288
288
-
}
289
289
-
};
290
290
-
let uri = AtUri::from_parts(&did, collection, &rkey);
291
291
-
results.push(WriteResult::CreateResult {
292
292
-
uri,
293
293
-
cid: record_cid.to_string(),
294
294
-
validation_status: validation_status.map(|s| s.to_string()),
295
295
-
});
296
296
-
ops.push(RecordOp::Create {
297
297
-
collection: collection.to_string(),
298
298
-
rkey: rkey.to_string(),
299
299
-
cid: record_cid,
300
300
-
});
301
301
-
}
302
302
-
WriteOp::Update {
303
303
-
collection,
304
304
-
rkey,
305
305
-
value,
306
306
-
} => {
307
307
-
let validation_status = if input.validate == Some(false) {
308
308
-
None
309
309
-
} else {
310
310
-
let require_lexicon = input.validate == Some(true);
311
311
-
match validate_record_with_status(
312
312
-
value,
313
313
-
collection,
314
314
-
Some(rkey.as_str()),
315
315
-
require_lexicon,
316
316
-
) {
317
317
-
Ok(status) => Some(status),
318
318
-
Err(err_response) => return *err_response,
319
319
-
}
320
320
-
};
321
321
-
all_blob_cids.extend(extract_blob_cids(value));
322
322
-
let record_ipld = crate::util::json_to_ipld(value);
323
323
-
let mut record_bytes = Vec::new();
324
324
-
if serde_ipld_dagcbor::to_writer(&mut record_bytes, &record_ipld).is_err() {
325
325
-
return ApiError::InvalidRecord("Failed to serialize record".into())
326
326
-
.into_response();
327
327
-
}
328
328
-
let record_cid = match tracking_store.put(&record_bytes).await {
329
329
-
Ok(c) => c,
330
330
-
Err(_) => {
331
331
-
return ApiError::InternalError(Some("Failed to store record".into()))
332
332
-
.into_response();
333
333
-
}
334
334
-
};
335
335
-
let key = format!("{}/{}", collection, rkey);
336
336
-
modified_keys.push(key.clone());
337
337
-
let prev_record_cid = mst.get(&key).await.ok().flatten();
338
338
-
mst = match mst.update(&key, record_cid).await {
339
339
-
Ok(m) => m,
340
340
-
Err(_) => {
341
341
-
return ApiError::InternalError(Some("Failed to update MST".into()))
342
342
-
.into_response();
343
343
-
}
344
344
-
};
345
345
-
let uri = AtUri::from_parts(&did, collection, rkey);
346
346
-
results.push(WriteResult::UpdateResult {
347
347
-
uri,
348
348
-
cid: record_cid.to_string(),
349
349
-
validation_status: validation_status.map(|s| s.to_string()),
350
350
-
});
351
351
-
ops.push(RecordOp::Update {
352
352
-
collection: collection.to_string(),
353
353
-
rkey: rkey.to_string(),
354
354
-
cid: record_cid,
355
355
-
prev: prev_record_cid,
356
356
-
});
357
357
-
}
358
358
-
WriteOp::Delete { collection, rkey } => {
359
359
-
let key = format!("{}/{}", collection, rkey);
360
360
-
modified_keys.push(key.clone());
361
361
-
let prev_record_cid = mst.get(&key).await.ok().flatten();
362
362
-
mst = match mst.delete(&key).await {
363
363
-
Ok(m) => m,
364
364
-
Err(_) => {
365
365
-
return ApiError::InternalError(Some("Failed to delete from MST".into()))
366
366
-
.into_response();
367
367
-
}
368
368
-
};
369
369
-
results.push(WriteResult::DeleteResult {});
370
370
-
ops.push(RecordOp::Delete {
371
371
-
collection: collection.to_string(),
372
372
-
rkey: rkey.to_string(),
373
373
-
prev: prev_record_cid,
374
374
-
});
375
375
-
}
376
376
-
}
377
377
-
}
417
417
+
let initial_mst = Mst::load(Arc::new(tracking_store.clone()), commit.data, None);
418
418
+
let WriteAccumulator {
419
419
+
mst,
420
420
+
results,
421
421
+
ops,
422
422
+
modified_keys,
423
423
+
all_blob_cids,
424
424
+
} = match process_writes(&input.writes, initial_mst, &did, input.validate, &tracking_store).await
425
425
+
{
426
426
+
Ok(acc) => acc,
427
427
+
Err(response) => return response,
428
428
+
};
378
429
let new_mst_root = match mst.persist().await {
379
430
Ok(c) => c,
380
431
Err(_) => {
+2
-9
src/api/repo/record/delete.rs
···
65
65
return e;
66
66
}
67
67
68
68
-
if crate::util::is_account_migrated(&state.db, &auth.did)
69
69
-
.await
70
70
-
.unwrap_or(false)
71
71
-
{
72
72
-
return ApiError::AccountMigrated.into_response();
73
73
-
}
74
74
-
75
68
let did = auth.did;
76
69
let user_id = auth.user_id;
77
70
let current_root_cid = auth.current_root_cid;
···
125
118
let collection_for_audit = input.collection.to_string();
126
119
let rkey_for_audit = input.rkey.to_string();
127
120
let op = RecordOp::Delete {
128
128
-
collection: input.collection.to_string(),
129
129
-
rkey: rkey_for_audit.clone(),
121
121
+
collection: input.collection.clone(),
122
122
+
rkey: input.rkey.clone(),
130
123
prev: prev_record_cid,
131
124
};
132
125
let mut new_mst_blocks = std::collections::BTreeMap::new();
+24
-23
src/api/repo/record/read.rs
···
13
13
use jacquard_repo::storage::BlockStore;
14
14
use serde::{Deserialize, Serialize};
15
15
use serde_json::{Map, Value, json};
16
16
-
use std::collections::HashMap;
17
16
use std::str::FromStr;
18
17
use tracing::error;
19
18
···
237
236
}
238
237
};
239
238
let last_rkey = rows.last().map(|(rkey, _)| rkey.clone());
240
240
-
let mut cid_to_rkey: HashMap<Cid, (String, String)> = HashMap::new();
241
241
-
let mut cids: Vec<Cid> = Vec::with_capacity(rows.len());
242
242
-
for (rkey, cid_str) in &rows {
243
243
-
if let Ok(cid) = Cid::from_str(cid_str) {
244
244
-
cid_to_rkey.insert(cid, (rkey.clone(), cid_str.clone()));
245
245
-
cids.push(cid);
246
246
-
}
247
247
-
}
239
239
+
let parsed_rows: Vec<(Cid, String, String)> = rows
240
240
+
.iter()
241
241
+
.filter_map(|(rkey, cid_str)| {
242
242
+
Cid::from_str(cid_str)
243
243
+
.ok()
244
244
+
.map(|cid| (cid, rkey.clone(), cid_str.clone()))
245
245
+
})
246
246
+
.collect();
247
247
+
let cids: Vec<Cid> = parsed_rows.iter().map(|(cid, _, _)| *cid).collect();
248
248
let blocks = match state.block_store.get_many(&cids).await {
249
249
Ok(b) => b,
250
250
Err(e) => {
···
252
252
return ApiError::InternalError(None).into_response();
253
253
}
254
254
};
255
255
-
let mut records = Vec::new();
256
256
-
for (cid, block_opt) in cids.iter().zip(blocks.into_iter()) {
257
257
-
if let Some(block) = block_opt
258
258
-
&& let Some((rkey, cid_str)) = cid_to_rkey.get(cid)
259
259
-
&& let Ok(ipld) = serde_ipld_dagcbor::from_slice::<Ipld>(&block)
260
260
-
{
261
261
-
let value = ipld_to_json(ipld);
262
262
-
records.push(json!({
263
263
-
"uri": format!("at://{}/{}/{}", input.repo, input.collection, rkey),
264
264
-
"cid": cid_str,
265
265
-
"value": value
266
266
-
}));
267
267
-
}
268
268
-
}
255
255
+
let records: Vec<Value> = parsed_rows
256
256
+
.iter()
257
257
+
.zip(blocks.into_iter())
258
258
+
.filter_map(|((_, rkey, cid_str), block_opt)| {
259
259
+
block_opt.and_then(|block| {
260
260
+
serde_ipld_dagcbor::from_slice::<Ipld>(&block).ok().map(|ipld| {
261
261
+
json!({
262
262
+
"uri": format!("at://{}/{}/{}", input.repo, input.collection, rkey),
263
263
+
"cid": cid_str,
264
264
+
"value": ipld_to_json(ipld)
265
265
+
})
266
266
+
})
267
267
+
})
268
268
+
})
269
269
+
.collect();
269
270
Json(ListRecordsOutput {
270
271
cursor: last_rkey,
271
272
records,
+104
-63
src/api/repo/record/utils.rs
···
1
1
use crate::state::AppState;
2
2
+
use crate::types::{Did, Handle, Nsid, Rkey};
2
3
use bytes::Bytes;
3
4
use cid::Cid;
4
5
use jacquard::types::{integer::LimitedU32, string::Tid};
···
38
39
}
39
40
40
41
pub fn create_signed_commit(
41
41
-
did: &str,
42
42
+
did: &Did,
42
43
data: Cid,
43
44
rev: &str,
44
45
prev: Option<Cid>,
45
46
signing_key: &SigningKey,
46
47
) -> Result<(Vec<u8>, Bytes), String> {
47
47
-
let did =
48
48
-
jacquard::types::string::Did::new(did).map_err(|e| format!("Invalid DID: {:?}", e))?;
48
48
+
let did = jacquard::types::string::Did::new(did.as_str())
49
49
+
.map_err(|e| format!("Invalid DID: {:?}", e))?;
49
50
let rev =
50
51
jacquard::types::string::Tid::from_str(rev).map_err(|e| format!("Invalid TID: {:?}", e))?;
51
52
let unsigned = Commit::new_unsigned(did, data, rev, prev);
···
61
62
62
63
pub enum RecordOp {
63
64
Create {
64
64
-
collection: String,
65
65
-
rkey: String,
65
65
+
collection: Nsid,
66
66
+
rkey: Rkey,
66
67
cid: Cid,
67
68
},
68
69
Update {
69
69
-
collection: String,
70
70
-
rkey: String,
70
70
+
collection: Nsid,
71
71
+
rkey: Rkey,
71
72
cid: Cid,
72
73
prev: Option<Cid>,
73
74
},
74
75
Delete {
75
75
-
collection: String,
76
76
-
rkey: String,
76
76
+
collection: Nsid,
77
77
+
rkey: Rkey,
77
78
prev: Option<Cid>,
78
79
},
79
80
}
···
84
85
}
85
86
86
87
pub struct CommitParams<'a> {
87
87
-
pub did: &'a str,
88
88
+
pub did: &'a Did,
88
89
pub user_id: Uuid,
89
90
pub current_root_cid: Option<Cid>,
90
91
pub prev_data_cid: Option<Cid>,
···
218
219
.await
219
220
.map_err(|e| format!("DB Error (user_blocks delete obsolete): {}", e))?;
220
221
}
221
221
-
let mut upsert_collections: Vec<String> = Vec::new();
222
222
-
let mut upsert_rkeys: Vec<String> = Vec::new();
223
223
-
let mut upsert_cids: Vec<String> = Vec::new();
224
224
-
let mut delete_collections: Vec<String> = Vec::new();
225
225
-
let mut delete_rkeys: Vec<String> = Vec::new();
226
226
-
for op in &ops {
227
227
-
match op {
228
228
-
RecordOp::Create {
229
229
-
collection,
230
230
-
rkey,
231
231
-
cid,
232
232
-
}
233
233
-
| RecordOp::Update {
234
234
-
collection,
235
235
-
rkey,
236
236
-
cid,
237
237
-
..
238
238
-
} => {
239
239
-
upsert_collections.push(collection.clone());
240
240
-
upsert_rkeys.push(rkey.clone());
241
241
-
upsert_cids.push(cid.to_string());
242
242
-
}
222
222
+
let (upserts, deletes): (Vec<_>, Vec<_>) = ops.iter().partition(|op| {
223
223
+
matches!(op, RecordOp::Create { .. } | RecordOp::Update { .. })
224
224
+
});
225
225
+
let (upsert_collections, upsert_rkeys, upsert_cids): (Vec<String>, Vec<String>, Vec<String>) =
226
226
+
upserts
227
227
+
.into_iter()
228
228
+
.filter_map(|op| match op {
229
229
+
RecordOp::Create {
230
230
+
collection,
231
231
+
rkey,
232
232
+
cid,
233
233
+
}
234
234
+
| RecordOp::Update {
235
235
+
collection,
236
236
+
rkey,
237
237
+
cid,
238
238
+
..
239
239
+
} => Some((collection.to_string(), rkey.to_string(), cid.to_string())),
240
240
+
_ => None,
241
241
+
})
242
242
+
.fold(
243
243
+
(Vec::new(), Vec::new(), Vec::new()),
244
244
+
|(mut cols, mut rkeys, mut cids), (c, r, ci)| {
245
245
+
cols.push(c);
246
246
+
rkeys.push(r);
247
247
+
cids.push(ci);
248
248
+
(cols, rkeys, cids)
249
249
+
},
250
250
+
);
251
251
+
let (delete_collections, delete_rkeys): (Vec<String>, Vec<String>) = deletes
252
252
+
.into_iter()
253
253
+
.filter_map(|op| match op {
243
254
RecordOp::Delete {
244
255
collection, rkey, ..
245
245
-
} => {
246
246
-
delete_collections.push(collection.clone());
247
247
-
delete_rkeys.push(rkey.clone());
248
248
-
}
249
249
-
}
250
250
-
}
256
256
+
} => Some((collection.to_string(), rkey.to_string())),
257
257
+
_ => None,
258
258
+
})
259
259
+
.unzip();
251
260
if !upsert_collections.is_empty() {
252
261
sqlx::query!(
253
262
r#"
···
337
346
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
338
347
RETURNING seq
339
348
"#,
340
340
-
did,
349
349
+
did.as_str(),
341
350
event_type,
342
351
new_root_cid.to_string(),
343
352
prev_cid_str,
···
367
376
}
368
377
pub async fn create_record_internal(
369
378
state: &AppState,
370
370
-
did: &str,
371
371
-
collection: &str,
372
372
-
rkey: &str,
379
379
+
did: &Did,
380
380
+
collection: &Nsid,
381
381
+
rkey: &Rkey,
373
382
record: &serde_json::Value,
374
383
) -> Result<(String, Cid), String> {
375
384
use crate::repo::tracking::TrackingBlockStore;
376
385
use jacquard_repo::mst::Mst;
377
386
use std::sync::Arc;
378
378
-
let user_id: Uuid = sqlx::query_scalar!("SELECT id FROM users WHERE did = $1", did)
387
387
+
let user_id: Uuid = sqlx::query_scalar!("SELECT id FROM users WHERE did = $1", did.as_str())
379
388
.fetch_optional(&state.db)
380
389
.await
381
390
.map_err(|e| format!("DB error: {}", e))?
···
417
426
.await
418
427
.map_err(|e| format!("Failed to persist MST: {:?}", e))?;
419
428
let op = RecordOp::Create {
420
420
-
collection: collection.to_string(),
421
421
-
rkey: rkey.to_string(),
429
429
+
collection: collection.clone(),
430
430
+
rkey: rkey.clone(),
422
431
cid: record_cid,
423
432
};
424
433
let mut new_mst_blocks = std::collections::BTreeMap::new();
···
471
480
472
481
pub async fn sequence_identity_event(
473
482
state: &AppState,
474
474
-
did: &str,
475
475
-
handle: Option<&str>,
483
483
+
did: &Did,
484
484
+
handle: Option<&Handle>,
476
485
) -> Result<i64, String> {
486
486
+
let mut tx = state
487
487
+
.db
488
488
+
.begin()
489
489
+
.await
490
490
+
.map_err(|e| format!("Failed to begin transaction: {}", e))?;
477
491
let seq_row = sqlx::query!(
478
492
r#"
479
493
INSERT INTO repo_seq (did, event_type, handle)
480
494
VALUES ($1, 'identity', $2)
481
495
RETURNING seq
482
496
"#,
483
483
-
did,
484
484
-
handle,
497
497
+
did.as_str(),
498
498
+
handle.map(|h| h.as_str()),
485
499
)
486
486
-
.fetch_one(&state.db)
500
500
+
.fetch_one(&mut *tx)
487
501
.await
488
502
.map_err(|e| format!("DB Error (repo_seq identity): {}", e))?;
489
503
sqlx::query(&format!("NOTIFY repo_updates, '{}'", seq_row.seq))
490
490
-
.execute(&state.db)
504
504
+
.execute(&mut *tx)
491
505
.await
492
506
.map_err(|e| format!("DB Error (notify): {}", e))?;
507
507
+
tx.commit()
508
508
+
.await
509
509
+
.map_err(|e| format!("Failed to commit transaction: {}", e))?;
493
510
Ok(seq_row.seq)
494
511
}
495
512
pub async fn sequence_account_event(
496
513
state: &AppState,
497
497
-
did: &str,
514
514
+
did: &Did,
498
515
active: bool,
499
516
status: Option<&str>,
500
517
) -> Result<i64, String> {
518
518
+
let mut tx = state
519
519
+
.db
520
520
+
.begin()
521
521
+
.await
522
522
+
.map_err(|e| format!("Failed to begin transaction: {}", e))?;
501
523
let seq_row = sqlx::query!(
502
524
r#"
503
525
INSERT INTO repo_seq (did, event_type, active, status)
504
526
VALUES ($1, 'account', $2, $3)
505
527
RETURNING seq
506
528
"#,
507
507
-
did,
529
529
+
did.as_str(),
508
530
active,
509
531
status,
510
532
)
511
511
-
.fetch_one(&state.db)
533
533
+
.fetch_one(&mut *tx)
512
534
.await
513
535
.map_err(|e| format!("DB Error (repo_seq account): {}", e))?;
514
536
sqlx::query(&format!("NOTIFY repo_updates, '{}'", seq_row.seq))
515
515
-
.execute(&state.db)
537
537
+
.execute(&mut *tx)
516
538
.await
517
539
.map_err(|e| format!("DB Error (notify): {}", e))?;
540
540
+
tx.commit()
541
541
+
.await
542
542
+
.map_err(|e| format!("Failed to commit transaction: {}", e))?;
518
543
Ok(seq_row.seq)
519
544
}
520
545
pub async fn sequence_sync_event(
521
546
state: &AppState,
522
522
-
did: &str,
547
547
+
did: &Did,
523
548
commit_cid: &str,
524
549
rev: Option<&str>,
525
550
) -> Result<i64, String> {
551
551
+
let mut tx = state
552
552
+
.db
553
553
+
.begin()
554
554
+
.await
555
555
+
.map_err(|e| format!("Failed to begin transaction: {}", e))?;
526
556
let seq_row = sqlx::query!(
527
557
r#"
528
558
INSERT INTO repo_seq (did, event_type, commit_cid, rev)
529
559
VALUES ($1, 'sync', $2, $3)
530
560
RETURNING seq
531
561
"#,
532
532
-
did,
562
562
+
did.as_str(),
533
563
commit_cid,
534
564
rev,
535
565
)
536
536
-
.fetch_one(&state.db)
566
566
+
.fetch_one(&mut *tx)
537
567
.await
538
568
.map_err(|e| format!("DB Error (repo_seq sync): {}", e))?;
539
569
sqlx::query(&format!("NOTIFY repo_updates, '{}'", seq_row.seq))
540
540
-
.execute(&state.db)
570
570
+
.execute(&mut *tx)
541
571
.await
542
572
.map_err(|e| format!("DB Error (notify): {}", e))?;
573
573
+
tx.commit()
574
574
+
.await
575
575
+
.map_err(|e| format!("Failed to commit transaction: {}", e))?;
543
576
Ok(seq_row.seq)
544
577
}
545
578
546
579
pub async fn sequence_genesis_commit(
547
580
state: &AppState,
548
548
-
did: &str,
581
581
+
did: &Did,
549
582
commit_cid: &Cid,
550
583
mst_root_cid: &Cid,
551
584
rev: &str,
···
555
588
let blocks_cids: Vec<String> = vec![mst_root_cid.to_string(), commit_cid.to_string()];
556
589
let prev_cid: Option<&str> = None;
557
590
let commit_cid_str = commit_cid.to_string();
591
591
+
let mut tx = state
592
592
+
.db
593
593
+
.begin()
594
594
+
.await
595
595
+
.map_err(|e| format!("Failed to begin transaction: {}", e))?;
558
596
let seq_row = sqlx::query!(
559
597
r#"
560
598
INSERT INTO repo_seq (did, event_type, commit_cid, prev_cid, ops, blobs, blocks_cids, rev)
561
599
VALUES ($1, 'commit', $2, $3::TEXT, $4, $5, $6, $7)
562
600
RETURNING seq
563
601
"#,
564
564
-
did,
602
602
+
did.as_str(),
565
603
commit_cid_str,
566
604
prev_cid,
567
605
ops,
···
569
607
&blocks_cids,
570
608
rev
571
609
)
572
572
-
.fetch_one(&state.db)
610
610
+
.fetch_one(&mut *tx)
573
611
.await
574
612
.map_err(|e| format!("DB Error (repo_seq genesis commit): {}", e))?;
575
613
sqlx::query(&format!("NOTIFY repo_updates, '{}'", seq_row.seq))
576
576
-
.execute(&state.db)
614
614
+
.execute(&mut *tx)
577
615
.await
578
616
.map_err(|e| format!("DB Error (notify): {}", e))?;
617
617
+
tx.commit()
618
618
+
.await
619
619
+
.map_err(|e| format!("Failed to commit transaction: {}", e))?;
579
620
Ok(seq_row.seq)
580
621
}
+12
-7
src/api/repo/record/validation.rs
···
1
1
use crate::api::error::ApiError;
2
2
+
use crate::types::{Nsid, Rkey};
2
3
use crate::validation::{RecordValidator, ValidationError, ValidationStatus};
3
4
use axum::response::Response;
4
5
5
5
-
pub fn validate_record(record: &serde_json::Value, collection: &str) -> Result<(), Box<Response>> {
6
6
+
pub fn validate_record(record: &serde_json::Value, collection: &Nsid) -> Result<(), Box<Response>> {
6
7
validate_record_with_rkey(record, collection, None)
7
8
}
8
9
9
10
pub fn validate_record_with_rkey(
10
11
record: &serde_json::Value,
11
11
-
collection: &str,
12
12
-
rkey: Option<&str>,
12
12
+
collection: &Nsid,
13
13
+
rkey: Option<&Rkey>,
13
14
) -> Result<(), Box<Response>> {
14
15
let validator = RecordValidator::new();
15
15
-
validation_error_to_response(validator.validate_with_rkey(record, collection, rkey))
16
16
+
validation_error_to_response(validator.validate_with_rkey(
17
17
+
record,
18
18
+
collection.as_str(),
19
19
+
rkey.map(|r| r.as_str()),
20
20
+
))
16
21
}
17
22
18
23
pub fn validate_record_with_status(
19
24
record: &serde_json::Value,
20
20
-
collection: &str,
21
21
-
rkey: Option<&str>,
25
25
+
collection: &Nsid,
26
26
+
rkey: Option<&Rkey>,
22
27
require_lexicon: bool,
23
28
) -> Result<ValidationStatus, Box<Response>> {
24
29
let validator = RecordValidator::new().require_lexicon(require_lexicon);
25
25
-
match validator.validate_with_rkey(record, collection, rkey) {
30
30
+
match validator.validate_with_rkey(record, collection.as_str(), rkey.map(|r| r.as_str())) {
26
31
Ok(status) => Ok(status),
27
32
Err(e) => Err(validation_error_to_box_response(e)),
28
33
}
+12
-12
src/api/repo/record/write.rs
···
21
21
use tracing::error;
22
22
use uuid::Uuid;
23
23
24
24
-
pub async fn has_verified_comms_channel(db: &PgPool, did: &str) -> Result<bool, sqlx::Error> {
24
24
+
pub async fn has_verified_comms_channel(db: &PgPool, did: &Did) -> Result<bool, sqlx::Error> {
25
25
let row = sqlx::query(
26
26
r#"
27
27
SELECT
···
33
33
WHERE did = $1
34
34
"#,
35
35
)
36
36
-
.bind(did)
36
36
+
.bind(did.as_str())
37
37
.fetch_optional(db)
38
38
.await?;
39
39
match row {
···
60
60
pub async fn prepare_repo_write(
61
61
state: &AppState,
62
62
headers: &HeaderMap,
63
63
-
repo_did: &str,
63
63
+
repo: &AtIdentifier,
64
64
http_method: &str,
65
65
http_uri: &str,
66
66
) -> Result<RepoWriteAuth, Response> {
···
96
96
}
97
97
response
98
98
})?;
99
99
-
if repo_did != auth_user.did {
99
99
+
if repo.as_str() != auth_user.did.as_str() {
100
100
return Err(
101
101
ApiError::InvalidRepo("Repo does not match authenticated user".into()).into_response(),
102
102
);
···
229
229
match validate_record_with_status(
230
230
&input.record,
231
231
&input.collection,
232
232
-
input.rkey.as_ref().map(|r| r.as_str()),
232
232
+
input.rkey.as_ref(),
233
233
require_lexicon,
234
234
) {
235
235
Ok(status) => Some(status),
···
259
259
_ => return ApiError::InternalError(Some("Failed to persist MST".into())).into_response(),
260
260
};
261
261
let op = RecordOp::Create {
262
262
-
collection: input.collection.to_string(),
263
263
-
rkey: rkey.to_string(),
262
262
+
collection: input.collection.clone(),
263
263
+
rkey: rkey.clone(),
264
264
cid: record_cid,
265
265
};
266
266
let mut new_mst_blocks = std::collections::BTreeMap::new();
···
443
443
match validate_record_with_status(
444
444
&input.record,
445
445
&input.collection,
446
446
-
Some(input.rkey.as_str()),
446
446
+
Some(&input.rkey),
447
447
require_lexicon,
448
448
) {
449
449
Ok(status) => Some(status),
···
510
510
};
511
511
let op = if existing_cid.is_some() {
512
512
RecordOp::Update {
513
513
-
collection: input.collection.to_string(),
514
514
-
rkey: input.rkey.to_string(),
513
513
+
collection: input.collection.clone(),
514
514
+
rkey: input.rkey.clone(),
515
515
cid: record_cid,
516
516
prev: existing_cid,
517
517
}
518
518
} else {
519
519
RecordOp::Create {
520
520
-
collection: input.collection.to_string(),
521
521
-
rkey: input.rkey.to_string(),
520
520
+
collection: input.collection.clone(),
521
521
+
rkey: input.rkey.clone(),
522
522
cid: record_cid,
523
523
}
524
524
};
+7
-6
src/api/server/account_status.rs
···
3
3
use crate::cache::Cache;
4
4
use crate::plc::PlcClient;
5
5
use crate::state::AppState;
6
6
-
use crate::types::PlainPassword;
6
6
+
use crate::types::{Handle, PlainPassword};
7
7
use axum::{
8
8
Json,
9
9
extract::State,
···
449
449
did
450
450
);
451
451
if let Err(e) =
452
452
-
crate::api::repo::record::sequence_account_event(&state, did.as_str(), true, None)
452
452
+
crate::api::repo::record::sequence_account_event(&state, &did, true, None)
453
453
.await
454
454
{
455
455
warn!(
···
463
463
"[MIGRATION] activateAccount: Sequencing identity event for did={} handle={:?}",
464
464
did, handle
465
465
);
466
466
+
let handle_typed = handle.as_ref().map(|h| Handle::new_unchecked(h));
466
467
if let Err(e) = crate::api::repo::record::sequence_identity_event(
467
468
&state,
468
468
-
did.as_str(),
469
469
-
handle.as_deref(),
469
469
+
&did,
470
470
+
handle_typed.as_ref(),
470
471
)
471
472
.await
472
473
{
···
501
502
};
502
503
if let Err(e) = crate::api::repo::record::sequence_sync_event(
503
504
&state,
504
504
-
did.as_str(),
505
505
+
&did,
505
506
&root_cid,
506
507
rev.as_deref(),
507
508
)
···
609
610
}
610
611
if let Err(e) = crate::api::repo::record::sequence_account_event(
611
612
&state,
612
612
-
did.as_str(),
613
613
+
&did,
613
614
false,
614
615
Some("deactivated"),
615
616
)
+11
-7
src/api/server/passkey_account.rs
···
20
20
use crate::api::repo::record::utils::create_signed_commit;
21
21
use crate::auth::{ServiceTokenVerifier, is_service_token};
22
22
use crate::state::{AppState, RateLimitKind};
23
23
-
use crate::types::{Did, Handle, PlainPassword};
23
23
+
use crate::types::{Did, Handle, Nsid, PlainPassword, Rkey};
24
24
use crate::validation::validate_password;
25
25
26
26
fn extract_client_ip(headers: &HeaderMap) -> String {
···
512
512
}
513
513
};
514
514
let rev = Tid::now(LimitedU32::MIN);
515
515
+
let did_typed = Did::new_unchecked(&did);
515
516
let (commit_bytes, _sig) =
516
516
-
match create_signed_commit(&did, mst_root, rev.as_ref(), None, &secret_key) {
517
517
+
match create_signed_commit(&did_typed, mst_root, rev.as_ref(), None, &secret_key) {
517
518
Ok(result) => result,
518
519
Err(e) => {
519
520
error!("Error creating genesis commit: {:?}", e);
···
600
601
}
601
602
602
603
if !is_byod_did_web {
604
604
+
let handle_typed = Handle::new_unchecked(&handle);
603
605
if let Err(e) =
604
604
-
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle)).await
606
606
+
crate::api::repo::record::sequence_identity_event(&state, &did_typed, Some(&handle_typed)).await
605
607
{
606
608
warn!("Failed to sequence identity event for {}: {}", did, e);
607
609
}
608
610
if let Err(e) =
609
609
-
crate::api::repo::record::sequence_account_event(&state, &did, true, None).await
611
611
+
crate::api::repo::record::sequence_account_event(&state, &did_typed, true, None).await
610
612
{
611
613
warn!("Failed to sequence account event for {}: {}", did, e);
612
614
}
···
614
616
"$type": "app.bsky.actor.profile",
615
617
"displayName": handle
616
618
});
619
619
+
let profile_collection = Nsid::new_unchecked("app.bsky.actor.profile");
620
620
+
let profile_rkey = Rkey::new_unchecked("self");
617
621
if let Err(e) = crate::api::repo::record::create_record_internal(
618
622
&state,
619
619
-
&did,
620
620
-
"app.bsky.actor.profile",
621
621
-
"self",
623
623
+
&did_typed,
624
624
+
&profile_collection,
625
625
+
&profile_rkey,
622
626
&profile_record,
623
627
)
624
628
.await
+3
-2
tests/commit_signing.rs
···
3
3
use jacquard_repo::commit::Commit;
4
4
use k256::ecdsa::SigningKey;
5
5
use std::str::FromStr;
6
6
+
use tranquil_pds::Did;
6
7
7
8
#[test]
8
9
fn test_commit_signing_produces_valid_signature() {
···
98
99
use tranquil_pds::api::repo::record::utils::create_signed_commit;
99
100
100
101
let signing_key = SigningKey::random(&mut rand::thread_rng());
101
101
-
let did = "did:plc:testuser123456789abcdef";
102
102
+
let did = Did::new_unchecked("did:plc:testuser123456789abcdef");
102
103
let data_cid =
103
104
Cid::from_str("bafyreib2rxk3ryblouj3fxza5jvx6psmwewwessc4m6g6e7pqhhkwqomfi").unwrap();
104
105
let rev = Tid::now(LimitedU32::MIN).to_string();
105
106
106
106
-
let (signed_bytes, sig) = create_signed_commit(did, data_cid, &rev, None, &signing_key)
107
107
+
let (signed_bytes, sig) = create_signed_commit(&did, data_cid, &rev, None, &signing_key)
107
108
.expect("signing should succeed");
108
109
109
110
assert!(!signed_bytes.is_empty());