+1730
-1404
Diff
round #0
+142
-19
Cargo.lock
+142
-19
Cargo.lock
···
6092
6092
"syn 2.0.111",
6093
6093
]
6094
6094
6095
+
[[package]]
6096
+
name = "tranquil-api"
6097
+
version = "0.4.2"
6098
+
dependencies = [
6099
+
"anyhow",
6100
+
"axum",
6101
+
"backon",
6102
+
"base32",
6103
+
"base64 0.22.1",
6104
+
"bcrypt",
6105
+
"bs58",
6106
+
"bytes",
6107
+
"chrono",
6108
+
"cid",
6109
+
"ed25519-dalek",
6110
+
"futures",
6111
+
"hex",
6112
+
"http 1.4.0",
6113
+
"infer",
6114
+
"ipld-core",
6115
+
"jacquard-common",
6116
+
"jacquard-repo",
6117
+
"k256",
6118
+
"multibase",
6119
+
"multihash",
6120
+
"rand 0.8.5",
6121
+
"reqwest",
6122
+
"serde",
6123
+
"serde_ipld_dagcbor",
6124
+
"serde_json",
6125
+
"sha2",
6126
+
"subtle",
6127
+
"thiserror 2.0.17",
6128
+
"tokio",
6129
+
"tracing",
6130
+
"tranquil-config",
6131
+
"tranquil-db",
6132
+
"tranquil-db-traits",
6133
+
"tranquil-lexicon",
6134
+
"tranquil-pds",
6135
+
"tranquil-scopes",
6136
+
"tranquil-types",
6137
+
"urlencoding",
6138
+
"uuid",
6139
+
"webauthn-rs",
6140
+
"zip",
6141
+
]
6142
+
6095
6143
[[package]]
6096
6144
name = "tranquil-auth"
6097
-
version = "0.4.1"
6145
+
version = "0.4.2"
6098
6146
dependencies = [
6099
6147
"anyhow",
6100
6148
"base32",
···
6117
6165
6118
6166
[[package]]
6119
6167
name = "tranquil-cache"
6120
-
version = "0.4.1"
6168
+
version = "0.4.2"
6121
6169
dependencies = [
6122
6170
"async-trait",
6123
6171
"base64 0.22.1",
···
6131
6179
6132
6180
[[package]]
6133
6181
name = "tranquil-comms"
6134
-
version = "0.4.1"
6182
+
version = "0.4.2"
6135
6183
dependencies = [
6136
6184
"async-trait",
6137
6185
"base64 0.22.1",
···
6146
6194
6147
6195
[[package]]
6148
6196
name = "tranquil-config"
6149
-
version = "0.4.1"
6197
+
version = "0.4.2"
6150
6198
dependencies = [
6151
6199
"confique",
6152
6200
"serde",
···
6154
6202
6155
6203
[[package]]
6156
6204
name = "tranquil-crypto"
6157
-
version = "0.4.1"
6205
+
version = "0.4.2"
6158
6206
dependencies = [
6159
6207
"aes-gcm",
6160
6208
"base64 0.22.1",
···
6170
6218
6171
6219
[[package]]
6172
6220
name = "tranquil-db"
6173
-
version = "0.4.1"
6221
+
version = "0.4.2"
6174
6222
dependencies = [
6175
6223
"async-trait",
6176
6224
"chrono",
···
6187
6235
6188
6236
[[package]]
6189
6237
name = "tranquil-db-traits"
6190
-
version = "0.4.1"
6238
+
version = "0.4.2"
6191
6239
dependencies = [
6192
6240
"async-trait",
6193
6241
"base64 0.22.1",
···
6203
6251
6204
6252
[[package]]
6205
6253
name = "tranquil-infra"
6206
-
version = "0.4.1"
6254
+
version = "0.4.2"
6207
6255
dependencies = [
6208
6256
"async-trait",
6209
6257
"bytes",
···
6214
6262
6215
6263
[[package]]
6216
6264
name = "tranquil-lexicon"
6217
-
version = "0.4.1"
6265
+
version = "0.4.2"
6218
6266
dependencies = [
6219
6267
"chrono",
6220
6268
"hickory-resolver",
···
6232
6280
6233
6281
[[package]]
6234
6282
name = "tranquil-oauth"
6235
-
version = "0.4.1"
6283
+
version = "0.4.2"
6236
6284
dependencies = [
6237
6285
"anyhow",
6238
6286
"axum",
···
6253
6301
"uuid",
6254
6302
]
6255
6303
6304
+
[[package]]
6305
+
name = "tranquil-oauth-server"
6306
+
version = "0.4.2"
6307
+
dependencies = [
6308
+
"axum",
6309
+
"base64 0.22.1",
6310
+
"bcrypt",
6311
+
"chrono",
6312
+
"cid",
6313
+
"hmac",
6314
+
"http 1.4.0",
6315
+
"jacquard-common",
6316
+
"jacquard-repo",
6317
+
"k256",
6318
+
"rand 0.8.5",
6319
+
"serde",
6320
+
"serde_json",
6321
+
"serde_urlencoded",
6322
+
"sha2",
6323
+
"subtle",
6324
+
"tokio",
6325
+
"tracing",
6326
+
"tranquil-api",
6327
+
"tranquil-config",
6328
+
"tranquil-crypto",
6329
+
"tranquil-db-traits",
6330
+
"tranquil-pds",
6331
+
"tranquil-types",
6332
+
"urlencoding",
6333
+
"uuid",
6334
+
"webauthn-rs",
6335
+
]
6336
+
6256
6337
[[package]]
6257
6338
name = "tranquil-pds"
6258
-
version = "0.4.1"
6339
+
version = "0.4.2"
6259
6340
dependencies = [
6260
6341
"aes-gcm",
6261
6342
"anyhow",
···
6272
6353
"chrono",
6273
6354
"ciborium",
6274
6355
"cid",
6275
-
"clap",
6276
6356
"ctor",
6277
-
"dotenvy",
6278
6357
"ed25519-dalek",
6279
6358
"futures",
6280
6359
"futures-util",
···
6319
6398
"tower-http",
6320
6399
"tower-layer",
6321
6400
"tracing",
6322
-
"tracing-subscriber",
6401
+
"tranquil-api",
6323
6402
"tranquil-auth",
6324
6403
"tranquil-cache",
6325
6404
"tranquil-comms",
···
6329
6408
"tranquil-db-traits",
6330
6409
"tranquil-lexicon",
6331
6410
"tranquil-oauth",
6411
+
"tranquil-oauth-server",
6332
6412
"tranquil-repo",
6333
6413
"tranquil-ripple",
6334
6414
"tranquil-scopes",
6335
6415
"tranquil-storage",
6416
+
"tranquil-sync",
6336
6417
"tranquil-types",
6337
6418
"urlencoding",
6338
6419
"uuid",
···
6343
6424
6344
6425
[[package]]
6345
6426
name = "tranquil-repo"
6346
-
version = "0.4.1"
6427
+
version = "0.4.2"
6347
6428
dependencies = [
6348
6429
"bytes",
6349
6430
"cid",
···
6355
6436
6356
6437
[[package]]
6357
6438
name = "tranquil-ripple"
6358
-
version = "0.4.1"
6439
+
version = "0.4.2"
6359
6440
dependencies = [
6360
6441
"async-trait",
6361
6442
"backon",
···
6380
6461
6381
6462
[[package]]
6382
6463
name = "tranquil-scopes"
6383
-
version = "0.4.1"
6464
+
version = "0.4.2"
6384
6465
dependencies = [
6385
6466
"axum",
6386
6467
"futures",
···
6394
6475
"urlencoding",
6395
6476
]
6396
6477
6478
+
[[package]]
6479
+
name = "tranquil-server"
6480
+
version = "0.4.2"
6481
+
dependencies = [
6482
+
"axum",
6483
+
"clap",
6484
+
"dotenvy",
6485
+
"ed25519-dalek",
6486
+
"hex",
6487
+
"tokio",
6488
+
"tokio-util",
6489
+
"tracing",
6490
+
"tracing-subscriber",
6491
+
"tranquil-api",
6492
+
"tranquil-config",
6493
+
"tranquil-oauth-server",
6494
+
"tranquil-pds",
6495
+
"tranquil-sync",
6496
+
]
6497
+
6397
6498
[[package]]
6398
6499
name = "tranquil-storage"
6399
-
version = "0.4.1"
6500
+
version = "0.4.2"
6400
6501
dependencies = [
6401
6502
"async-trait",
6402
6503
"aws-config",
···
6411
6512
"uuid",
6412
6513
]
6413
6514
6515
+
[[package]]
6516
+
name = "tranquil-sync"
6517
+
version = "0.4.2"
6518
+
dependencies = [
6519
+
"anyhow",
6520
+
"axum",
6521
+
"bytes",
6522
+
"chrono",
6523
+
"cid",
6524
+
"futures",
6525
+
"ipld-core",
6526
+
"jacquard-repo",
6527
+
"serde",
6528
+
"serde_ipld_dagcbor",
6529
+
"tokio",
6530
+
"tracing",
6531
+
"tranquil-config",
6532
+
"tranquil-db-traits",
6533
+
"tranquil-pds",
6534
+
"tranquil-types",
6535
+
]
6536
+
6414
6537
[[package]]
6415
6538
name = "tranquil-types"
6416
-
version = "0.4.1"
6539
+
version = "0.4.2"
6417
6540
dependencies = [
6418
6541
"chrono",
6419
6542
"cid",
+10
-1
Cargo.toml
+10
-1
Cargo.toml
···
16
16
"crates/tranquil-db-traits",
17
17
"crates/tranquil-db",
18
18
"crates/tranquil-pds",
19
+
"crates/tranquil-server",
20
+
"crates/tranquil-sync",
21
+
"crates/tranquil-oauth-server",
22
+
"crates/tranquil-api",
19
23
"crates/tranquil-lexicon",
20
24
]
21
25
22
26
[workspace.package]
23
-
version = "0.4.1"
27
+
version = "0.4.2"
24
28
edition = "2024"
25
29
license = "AGPL-3.0-or-later"
26
30
···
40
44
tranquil-db = { path = "crates/tranquil-db" }
41
45
tranquil-ripple = { path = "crates/tranquil-ripple" }
42
46
tranquil-lexicon = { path = "crates/tranquil-lexicon" }
47
+
tranquil-pds = { path = "crates/tranquil-pds" }
48
+
tranquil-server = { path = "crates/tranquil-server" }
49
+
tranquil-sync = { path = "crates/tranquil-sync" }
50
+
tranquil-oauth-server = { path = "crates/tranquil-oauth-server" }
51
+
tranquil-api = { path = "crates/tranquil-api" }
43
52
44
53
unicode-segmentation = "1"
45
54
+7
-3
Dockerfile
+7
-3
Dockerfile
···
25
25
COPY crates/tranquil-storage ./crates/tranquil-storage
26
26
COPY crates/tranquil-cache ./crates/tranquil-cache
27
27
COPY crates/tranquil-pds ./crates/tranquil-pds
28
+
COPY crates/tranquil-sync ./crates/tranquil-sync
29
+
COPY crates/tranquil-api ./crates/tranquil-api
30
+
COPY crates/tranquil-oauth-server ./crates/tranquil-oauth-server
31
+
COPY crates/tranquil-server ./crates/tranquil-server
28
32
COPY migrations ./crates/tranquil-pds/migrations
29
33
RUN --mount=type=cache,target=/usr/local/cargo/registry \
30
34
--mount=type=cache,target=/app/target \
31
35
if [ "$SLIM" = "true" ]; then \
32
-
SQLX_OFFLINE=true cargo build --release -p tranquil-pds --no-default-features; \
36
+
SQLX_OFFLINE=true cargo build --release -p tranquil-server --no-default-features; \
33
37
else \
34
-
SQLX_OFFLINE=true cargo build --release -p tranquil-pds; \
38
+
SQLX_OFFLINE=true cargo build --release -p tranquil-server; \
35
39
fi && \
36
-
cp target/release/tranquil-pds /tmp/tranquil-pds
40
+
cp target/release/tranquil-server /tmp/tranquil-pds
37
41
38
42
FROM alpine:3.23 AS signal-cli
39
43
RUN apk add --no-cache curl tar
+50
crates/tranquil-api/Cargo.toml
+50
crates/tranquil-api/Cargo.toml
···
1
+
[package]
2
+
name = "tranquil-api"
3
+
version.workspace = true
4
+
edition.workspace = true
5
+
license.workspace = true
6
+
7
+
[dependencies]
8
+
tranquil-pds = { workspace = true }
9
+
tranquil-types = { workspace = true }
10
+
tranquil-config = { workspace = true }
11
+
tranquil-db = { workspace = true }
12
+
tranquil-db-traits = { workspace = true }
13
+
tranquil-lexicon = { workspace = true, features = ["resolve"] }
14
+
tranquil-scopes = { workspace = true }
15
+
16
+
anyhow = { workspace = true }
17
+
axum = { workspace = true }
18
+
backon = { workspace = true }
19
+
base32 = { workspace = true }
20
+
base64 = { workspace = true }
21
+
bcrypt = { workspace = true }
22
+
bs58 = { workspace = true }
23
+
bytes = { workspace = true }
24
+
chrono = { workspace = true }
25
+
cid = { workspace = true }
26
+
ed25519-dalek = { workspace = true }
27
+
futures = { workspace = true }
28
+
hex = { workspace = true }
29
+
http = { workspace = true }
30
+
infer = { workspace = true }
31
+
ipld-core = { workspace = true }
32
+
jacquard-common = { workspace = true }
33
+
jacquard-repo = { workspace = true }
34
+
k256 = { workspace = true }
35
+
multibase = { workspace = true }
36
+
multihash = { workspace = true }
37
+
rand = { workspace = true }
38
+
reqwest = { workspace = true }
39
+
serde = { workspace = true }
40
+
serde_json = { workspace = true }
41
+
serde_ipld_dagcbor = { workspace = true }
42
+
sha2 = { workspace = true }
43
+
subtle = { workspace = true }
44
+
thiserror = { workspace = true }
45
+
tokio = { workspace = true }
46
+
tracing = { workspace = true }
47
+
urlencoding = { workspace = true }
48
+
uuid = { workspace = true }
49
+
webauthn-rs = { workspace = true }
50
+
zip = { workspace = true }
crates/tranquil-pds/src/api/actor/mod.rs
crates/tranquil-api/src/actor/mod.rs
crates/tranquil-pds/src/api/actor/mod.rs
crates/tranquil-api/src/actor/mod.rs
+3
-3
crates/tranquil-pds/src/api/actor/preferences.rs
crates/tranquil-api/src/actor/preferences.rs
+3
-3
crates/tranquil-pds/src/api/actor/preferences.rs
crates/tranquil-api/src/actor/preferences.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::auth::{Auth, NotTakendown, Permissive};
3
-
use crate::state::AppState;
1
+
use tranquil_pds::api::error::ApiError;
2
+
use tranquil_pds::auth::{Auth, NotTakendown, Permissive};
3
+
use tranquil_pds::state::AppState;
4
4
use axum::{
5
5
Json,
6
6
extract::State,
+7
-7
crates/tranquil-pds/src/api/admin/account/delete.rs
crates/tranquil-api/src/admin/account/delete.rs
+7
-7
crates/tranquil-pds/src/api/admin/account/delete.rs
crates/tranquil-api/src/admin/account/delete.rs
···
1
-
use crate::api::EmptyResponse;
2
-
use crate::api::error::{ApiError, DbResultExt};
3
-
use crate::auth::{Admin, Auth};
4
-
use crate::state::AppState;
5
-
use crate::types::Did;
1
+
use tranquil_pds::api::EmptyResponse;
2
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
3
+
use tranquil_pds::auth::{Admin, Auth};
4
+
use tranquil_pds::state::AppState;
5
+
use tranquil_pds::types::Did;
6
6
use axum::{
7
7
Json,
8
8
extract::State,
···
36
36
.await
37
37
.log_db_err("deleting account")?;
38
38
39
-
if let Err(e) = crate::api::repo::record::sequence_account_event(
39
+
if let Err(e) = tranquil_pds::repo_ops::sequence_account_event(
40
40
&state,
41
41
did,
42
42
tranquil_db_traits::AccountStatus::Deleted,
···
50
50
}
51
51
let _ = state
52
52
.cache
53
-
.delete(&crate::cache_keys::handle_key(&handle))
53
+
.delete(&tranquil_pds::cache_keys::handle_key(&handle))
54
54
.await;
55
55
Ok(EmptyResponse::ok().into_response())
56
56
}
+4
-4
crates/tranquil-pds/src/api/admin/account/email.rs
crates/tranquil-api/src/admin/account/email.rs
+4
-4
crates/tranquil-pds/src/api/admin/account/email.rs
crates/tranquil-api/src/admin/account/email.rs
···
1
-
use crate::api::error::{ApiError, DbResultExt};
2
-
use crate::auth::{Admin, Auth};
3
-
use crate::state::AppState;
4
-
use crate::types::Did;
1
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
2
+
use tranquil_pds::auth::{Admin, Auth};
3
+
use tranquil_pds::state::AppState;
4
+
use tranquil_pds::types::Did;
5
5
use axum::{
6
6
Json,
7
7
extract::State,
+5
-5
crates/tranquil-pds/src/api/admin/account/info.rs
crates/tranquil-api/src/admin/account/info.rs
+5
-5
crates/tranquil-pds/src/api/admin/account/info.rs
crates/tranquil-api/src/admin/account/info.rs
···
1
-
use crate::api::error::{ApiError, DbResultExt};
2
-
use crate::auth::{Admin, Auth};
3
-
use crate::state::AppState;
4
-
use crate::types::{Did, Handle};
1
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
2
+
use tranquil_pds::auth::{Admin, Auth};
3
+
use tranquil_pds::state::AppState;
4
+
use tranquil_pds::types::{Did, Handle};
5
5
use axum::{
6
6
Json,
7
7
extract::{Query, RawQuery, State},
···
196
196
_auth: Auth<Admin>,
197
197
RawQuery(raw_query): RawQuery,
198
198
) -> Result<Response, ApiError> {
199
-
let dids: Vec<String> = crate::util::parse_repeated_query_param(raw_query.as_deref(), "dids")
199
+
let dids: Vec<String> = tranquil_pds::util::parse_repeated_query_param(raw_query.as_deref(), "dids")
200
200
.into_iter()
201
201
.filter(|d| !d.is_empty())
202
202
.collect();
crates/tranquil-pds/src/api/admin/account/mod.rs
crates/tranquil-api/src/admin/account/mod.rs
crates/tranquil-pds/src/api/admin/account/mod.rs
crates/tranquil-api/src/admin/account/mod.rs
+4
-4
crates/tranquil-pds/src/api/admin/account/search.rs
crates/tranquil-api/src/admin/account/search.rs
+4
-4
crates/tranquil-pds/src/api/admin/account/search.rs
crates/tranquil-api/src/admin/account/search.rs
···
1
-
use crate::api::error::{ApiError, DbResultExt};
2
-
use crate::auth::{Admin, Auth};
3
-
use crate::state::AppState;
4
-
use crate::types::{Did, Handle};
1
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
2
+
use tranquil_pds::auth::{Admin, Auth};
3
+
use tranquil_pds::state::AppState;
4
+
use tranquil_pds::types::{Did, Handle};
5
5
use axum::{
6
6
Json,
7
7
extract::{Query, State},
+9
-9
crates/tranquil-pds/src/api/admin/account/update.rs
crates/tranquil-api/src/admin/account/update.rs
+9
-9
crates/tranquil-pds/src/api/admin/account/update.rs
crates/tranquil-api/src/admin/account/update.rs
···
1
-
use crate::api::EmptyResponse;
2
-
use crate::api::error::ApiError;
3
-
use crate::auth::{Admin, Auth};
4
-
use crate::state::AppState;
5
-
use crate::types::{Did, Handle, PlainPassword};
1
+
use tranquil_pds::api::EmptyResponse;
2
+
use tranquil_pds::api::error::ApiError;
3
+
use tranquil_pds::auth::{Admin, Auth};
4
+
use tranquil_pds::state::AppState;
5
+
use tranquil_pds::types::{Did, Handle, PlainPassword};
6
6
use axum::{
7
7
Json,
8
8
extract::State,
···
101
101
if let Some(old) = old_handle {
102
102
let _ = state
103
103
.cache
104
-
.delete(&crate::cache_keys::handle_key(&old))
104
+
.delete(&tranquil_pds::cache_keys::handle_key(&old))
105
105
.await;
106
106
}
107
107
let _ = state
108
108
.cache
109
-
.delete(&crate::cache_keys::handle_key(&handle))
109
+
.delete(&tranquil_pds::cache_keys::handle_key(&handle))
110
110
.await;
111
-
if let Err(e) = crate::api::repo::record::sequence_identity_event(
111
+
if let Err(e) = tranquil_pds::repo_ops::sequence_identity_event(
112
112
&state,
113
113
did,
114
114
Some(&handle_for_check),
···
121
121
);
122
122
}
123
123
if let Err(e) =
124
-
crate::api::identity::did::update_plc_handle(&state, did, &handle_for_check).await
124
+
crate::identity::did::update_plc_handle(&state, did, &handle_for_check).await
125
125
{
126
126
warn!("Failed to update PLC handle for admin handle update: {}", e);
127
127
}
+3
-3
crates/tranquil-pds/src/api/admin/config.rs
crates/tranquil-api/src/admin/config.rs
+3
-3
crates/tranquil-pds/src/api/admin/config.rs
crates/tranquil-api/src/admin/config.rs
···
1
-
use crate::api::error::{ApiError, DbResultExt};
2
-
use crate::auth::{Admin, Auth};
3
-
use crate::state::AppState;
1
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
2
+
use tranquil_pds::auth::{Admin, Auth};
3
+
use tranquil_pds::state::AppState;
4
4
use axum::{Json, extract::State};
5
5
use serde::{Deserialize, Serialize};
6
6
use tracing::{error, warn};
+4
-4
crates/tranquil-pds/src/api/admin/invite.rs
crates/tranquil-api/src/admin/invite.rs
+4
-4
crates/tranquil-pds/src/api/admin/invite.rs
crates/tranquil-api/src/admin/invite.rs
···
1
-
use crate::api::EmptyResponse;
2
-
use crate::api::error::{ApiError, DbResultExt};
3
-
use crate::auth::{Admin, Auth};
4
-
use crate::state::AppState;
1
+
use tranquil_pds::api::EmptyResponse;
2
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
3
+
use tranquil_pds::auth::{Admin, Auth};
4
+
use tranquil_pds::state::AppState;
5
5
use axum::{
6
6
Json,
7
7
extract::{Query, State},
crates/tranquil-pds/src/api/admin/mod.rs
crates/tranquil-api/src/admin/mod.rs
crates/tranquil-pds/src/api/admin/mod.rs
crates/tranquil-api/src/admin/mod.rs
+3
-3
crates/tranquil-pds/src/api/admin/server_stats.rs
crates/tranquil-api/src/admin/server_stats.rs
+3
-3
crates/tranquil-pds/src/api/admin/server_stats.rs
crates/tranquil-api/src/admin/server_stats.rs
+7
-7
crates/tranquil-pds/src/api/admin/status.rs
crates/tranquil-api/src/admin/status.rs
+7
-7
crates/tranquil-pds/src/api/admin/status.rs
crates/tranquil-api/src/admin/status.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::auth::{Admin, Auth};
3
-
use crate::state::AppState;
4
-
use crate::types::{CidLink, Did};
1
+
use tranquil_pds::api::error::ApiError;
2
+
use tranquil_pds::auth::{Admin, Auth};
3
+
use tranquil_pds::state::AppState;
4
+
use tranquil_pds::types::{CidLink, Did};
5
5
use axum::{
6
6
Json,
7
7
extract::{Query, State},
···
215
215
tranquil_db_traits::AccountStatus::Active
216
216
};
217
217
if let Err(e) =
218
-
crate::api::repo::record::sequence_account_event(&state, &did, status).await
218
+
tranquil_pds::repo_ops::sequence_account_event(&state, &did, status).await
219
219
{
220
220
warn!("Failed to sequence account event for takedown: {}", e);
221
221
}
···
227
227
tranquil_db_traits::AccountStatus::Active
228
228
};
229
229
if let Err(e) =
230
-
crate::api::repo::record::sequence_account_event(&state, &did, status).await
230
+
tranquil_pds::repo_ops::sequence_account_event(&state, &did, status).await
231
231
{
232
232
warn!("Failed to sequence account event for deactivation: {}", e);
233
233
}
···
235
235
if let Ok(Some(handle)) = state.user_repo.get_handle_by_did(&did).await {
236
236
let _ = state
237
237
.cache
238
-
.delete(&crate::cache_keys::handle_key(&handle))
238
+
.delete(&tranquil_pds::cache_keys::handle_key(&handle))
239
239
.await;
240
240
}
241
241
return Ok((
+4
-4
crates/tranquil-pds/src/api/age_assurance.rs
crates/tranquil-api/src/age_assurance.rs
+4
-4
crates/tranquil-pds/src/api/age_assurance.rs
crates/tranquil-api/src/age_assurance.rs
···
1
-
use crate::auth::{AccountRequirement, extract_auth_token_from_header, validate_token_with_dpop};
2
-
use crate::state::AppState;
1
+
use tranquil_pds::auth::{AccountRequirement, extract_auth_token_from_header, validate_token_with_dpop};
2
+
use tranquil_pds::state::AppState;
3
3
use axum::{
4
4
Json,
5
5
extract::State,
···
33
33
}
34
34
35
35
async fn get_account_created_at(state: &AppState, headers: &HeaderMap) -> Option<String> {
36
-
let auth_header = crate::util::get_header_str(headers, http::header::AUTHORIZATION);
36
+
let auth_header = tranquil_pds::util::get_header_str(headers, http::header::AUTHORIZATION);
37
37
tracing::debug!(?auth_header, "age assurance: extracting token");
38
38
39
39
let extracted = extract_auth_token_from_header(auth_header)?;
40
40
tracing::debug!("age assurance: got token, validating");
41
41
42
-
let dpop_proof = crate::util::get_header_str(headers, crate::util::HEADER_DPOP);
42
+
let dpop_proof = tranquil_pds::util::get_header_str(headers, tranquil_pds::util::HEADER_DPOP);
43
43
let http_uri = "/";
44
44
45
45
let auth_user = match validate_token_with_dpop(
+13
-13
crates/tranquil-pds/src/api/backup.rs
crates/tranquil-api/src/backup.rs
+13
-13
crates/tranquil-pds/src/api/backup.rs
crates/tranquil-api/src/backup.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::api::{EmptyResponse, EnabledResponse};
3
-
use crate::auth::{Active, Auth};
4
-
use crate::scheduled::generate_full_backup;
5
-
use crate::state::AppState;
6
-
use crate::storage::{BackupStorage, backup_retention_count};
1
+
use tranquil_pds::api::error::ApiError;
2
+
use tranquil_pds::api::{EmptyResponse, EnabledResponse};
3
+
use tranquil_pds::auth::{Active, Auth};
4
+
use tranquil_pds::scheduled::generate_full_backup;
5
+
use tranquil_pds::state::AppState;
6
+
use tranquil_pds::storage::{BackupStorage, backup_retention_count};
7
7
use anyhow::Context;
8
8
use axum::{
9
9
Json,
···
39
39
pub async fn list_backups(
40
40
State(state): State<AppState>,
41
41
auth: Auth<Active>,
42
-
) -> Result<Response, crate::api::error::ApiError> {
42
+
) -> Result<Response, tranquil_pds::api::error::ApiError> {
43
43
let (user_id, backup_enabled) = match state.backup_repo.get_user_backup_status(&auth.did).await
44
44
{
45
45
Ok(Some(status)) => status,
···
91
91
State(state): State<AppState>,
92
92
auth: Auth<Active>,
93
93
Query(query): Query<GetBackupQuery>,
94
-
) -> Result<Response, crate::api::error::ApiError> {
94
+
) -> Result<Response, tranquil_pds::api::error::ApiError> {
95
95
let backup_id = match uuid::Uuid::parse_str(&query.id) {
96
96
Ok(id) => id,
97
97
Err(_) => {
···
157
157
pub async fn create_backup(
158
158
State(state): State<AppState>,
159
159
auth: Auth<Active>,
160
-
) -> Result<Response, crate::api::error::ApiError> {
160
+
) -> Result<Response, tranquil_pds::api::error::ApiError> {
161
161
let backup_storage = match state.backup_storage.as_ref() {
162
162
Some(storage) => storage,
163
163
None => {
···
213
213
}
214
214
};
215
215
216
-
let block_count = crate::scheduled::count_car_blocks(&car_bytes);
216
+
let block_count = tranquil_pds::scheduled::count_car_blocks(&car_bytes);
217
217
let size_bytes = i64::try_from(car_bytes.len()).unwrap_or(i64::MAX);
218
218
219
219
let storage_key = match backup_storage
···
327
327
State(state): State<AppState>,
328
328
auth: Auth<Active>,
329
329
Query(query): Query<DeleteBackupQuery>,
330
-
) -> Result<Response, crate::api::error::ApiError> {
330
+
) -> Result<Response, tranquil_pds::api::error::ApiError> {
331
331
let backup_id = match uuid::Uuid::parse_str(&query.id) {
332
332
Ok(id) => id,
333
333
Err(_) => {
···
384
384
State(state): State<AppState>,
385
385
auth: Auth<Active>,
386
386
Json(input): Json<SetBackupEnabledInput>,
387
-
) -> Result<Response, crate::api::error::ApiError> {
387
+
) -> Result<Response, tranquil_pds::api::error::ApiError> {
388
388
let deactivated_at = match state
389
389
.backup_repo
390
390
.get_user_deactivated_status(&auth.did)
···
423
423
pub async fn export_blobs(
424
424
State(state): State<AppState>,
425
425
auth: Auth<Active>,
426
-
) -> Result<Response, crate::api::error::ApiError> {
426
+
) -> Result<Response, tranquil_pds::api::error::ApiError> {
427
427
let user_id = match state.backup_repo.get_user_id_by_did(&auth.did).await {
428
428
Ok(Some(id)) => id,
429
429
Ok(None) => {
+19
-19
crates/tranquil-pds/src/api/delegation.rs
crates/tranquil-api/src/delegation.rs
+19
-19
crates/tranquil-pds/src/api/delegation.rs
crates/tranquil-api/src/delegation.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::api::repo::record::utils::create_signed_commit;
3
-
use crate::auth::{Active, Auth};
4
-
use crate::delegation::{
1
+
use tranquil_pds::api::error::ApiError;
2
+
use tranquil_pds::repo_ops::create_signed_commit;
3
+
use tranquil_pds::auth::{Active, Auth};
4
+
use tranquil_pds::delegation::{
5
5
DelegationActionType, SCOPE_PRESETS, ValidatedDelegationScope, verify_can_add_controllers,
6
6
verify_can_be_controller, verify_can_control_accounts,
7
7
};
8
-
use crate::rate_limit::{AccountCreationLimit, RateLimited};
9
-
use crate::state::AppState;
10
-
use crate::types::{Did, Handle};
8
+
use tranquil_pds::rate_limit::{AccountCreationLimit, RateLimited};
9
+
use tranquil_pds::state::AppState;
10
+
use tranquil_pds::types::{Did, Handle};
11
11
use axum::{
12
12
Json,
13
13
extract::{Query, State},
···
449
449
.unwrap_or(&input.handle),
450
450
None => &input.handle,
451
451
};
452
-
match crate::api::validation::validate_short_handle(handle_to_validate) {
452
+
match tranquil_pds::api::validation::validate_short_handle(handle_to_validate) {
453
453
Ok(h) => format!("{}.{}", h, matched_domain.unwrap_or(&available_domains[0])),
454
454
Err(e) => {
455
455
return Ok(ApiError::InvalidRequest(e.to_string()).into_response());
···
465
465
.map(|e| e.trim().to_string())
466
466
.filter(|e| !e.is_empty());
467
467
if let Some(ref email) = email
468
-
&& !crate::api::validation::is_valid_email(email)
468
+
&& !tranquil_pds::api::validation::is_valid_email(email)
469
469
{
470
470
return Ok(ApiError::InvalidEmail.into_response());
471
471
}
···
502
502
.secrets
503
503
.plc_rotation_key
504
504
.clone()
505
-
.unwrap_or_else(|| crate::plc::signing_key_to_did_key(&signing_key));
505
+
.unwrap_or_else(|| tranquil_pds::plc::signing_key_to_did_key(&signing_key));
506
506
507
-
let genesis_result = match crate::plc::create_genesis_operation(
507
+
let genesis_result = match tranquil_pds::plc::create_genesis_operation(
508
508
&signing_key,
509
509
&rotation_key,
510
510
&handle,
···
520
520
}
521
521
};
522
522
523
-
let plc_client = crate::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
523
+
let plc_client = tranquil_pds::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
524
524
if let Err(e) = plc_client
525
525
.send_operation(&genesis_result.did, &genesis_result.signed_operation)
526
526
.await
···
540
540
let handle: Handle = handle.parse().map_err(|_| ApiError::InvalidHandle(None))?;
541
541
info!(did = %did, handle = %handle, controller = %can_control.did(), "Created DID for delegated account");
542
542
543
-
let encrypted_key_bytes = match crate::config::encrypt_key(&secret_key_bytes) {
543
+
let encrypted_key_bytes = match tranquil_pds::config::encrypt_key(&secret_key_bytes) {
544
544
Ok(bytes) => bytes,
545
545
Err(e) => {
546
546
error!("Error encrypting signing key: {:?}", e);
···
581
581
controller_did: can_control.did().clone(),
582
582
controller_scopes: input.controller_scopes.as_str().to_string(),
583
583
encrypted_key_bytes,
584
-
encryption_version: crate::config::ENCRYPTION_VERSION,
584
+
encryption_version: tranquil_pds::config::ENCRYPTION_VERSION,
585
585
commit_cid: commit_cid.to_string(),
586
586
repo_rev: rev.as_ref().to_string(),
587
587
genesis_block_cids,
···
616
616
}
617
617
618
618
if let Err(e) =
619
-
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle)).await
619
+
tranquil_pds::repo_ops::sequence_identity_event(&state, &did, Some(&handle)).await
620
620
{
621
621
warn!("Failed to sequence identity event for {}: {}", did, e);
622
622
}
623
-
if let Err(e) = crate::api::repo::record::sequence_account_event(
623
+
if let Err(e) = tranquil_pds::repo_ops::sequence_account_event(
624
624
&state,
625
625
&did,
626
626
tranquil_db_traits::AccountStatus::Active,
···
634
634
"$type": "app.bsky.actor.profile",
635
635
"displayName": handle
636
636
});
637
-
if let Err(e) = crate::api::repo::record::create_record_internal(
637
+
if let Err(e) = tranquil_pds::repo_ops::create_record_internal(
638
638
&state,
639
639
&did,
640
-
&crate::types::PROFILE_COLLECTION,
641
-
&crate::types::PROFILE_RKEY,
640
+
&tranquil_pds::types::PROFILE_COLLECTION,
641
+
&tranquil_pds::types::PROFILE_RKEY,
642
642
&profile_record,
643
643
)
644
644
.await
+3
-3
crates/tranquil-pds/src/api/discord_webhook.rs
crates/tranquil-api/src/discord_webhook.rs
+3
-3
crates/tranquil-pds/src/api/discord_webhook.rs
crates/tranquil-api/src/discord_webhook.rs
···
10
10
use tracing::{debug, info, warn};
11
11
use tranquil_types::Handle;
12
12
13
-
use crate::comms::comms_repo;
14
-
use crate::state::AppState;
15
-
use crate::util::discord_public_key;
13
+
use tranquil_pds::comms::comms_repo;
14
+
use tranquil_pds::state::AppState;
15
+
use tranquil_pds::util::discord_public_key;
16
16
17
17
#[derive(Deserialize)]
18
18
struct Interaction {
+38
-38
crates/tranquil-pds/src/api/identity/account.rs
crates/tranquil-api/src/identity/account.rs
+38
-38
crates/tranquil-pds/src/api/identity/account.rs
crates/tranquil-api/src/identity/account.rs
···
1
1
use super::did::verify_did_web;
2
-
use crate::api::error::ApiError;
3
-
use crate::api::repo::record::utils::create_signed_commit;
4
-
use crate::auth::{ServiceTokenVerifier, extract_auth_token_from_header, is_service_token};
5
-
use crate::plc::{PlcClient, create_genesis_operation, signing_key_to_did_key};
6
-
use crate::rate_limit::{AccountCreationLimit, RateLimited};
7
-
use crate::state::AppState;
8
-
use crate::types::{Did, Handle, PlainPassword};
9
-
use crate::validation::validate_password;
2
+
use tranquil_pds::api::error::ApiError;
3
+
use tranquil_pds::repo_ops::create_signed_commit;
4
+
use tranquil_pds::auth::{ServiceTokenVerifier, extract_auth_token_from_header, is_service_token};
5
+
use tranquil_pds::plc::{PlcClient, create_genesis_operation, signing_key_to_did_key};
6
+
use tranquil_pds::rate_limit::{AccountCreationLimit, RateLimited};
7
+
use tranquil_pds::state::AppState;
8
+
use tranquil_pds::types::{Did, Handle, PlainPassword};
9
+
use tranquil_pds::validation::validate_password;
10
10
use axum::{
11
11
Json,
12
12
extract::State,
···
73
73
}
74
74
75
75
let migration_auth = if let Some(extracted) = extract_auth_token_from_header(
76
-
crate::util::get_header_str(&headers, http::header::AUTHORIZATION),
76
+
tranquil_pds::util::get_header_str(&headers, http::header::AUTHORIZATION),
77
77
) {
78
78
let token = extracted.token;
79
79
if is_service_token(&token) {
···
155
155
.unwrap_or(&input.handle),
156
156
None => &input.handle,
157
157
};
158
-
match crate::api::validation::validate_short_handle(handle_to_validate) {
158
+
match tranquil_pds::api::validation::validate_short_handle(handle_to_validate) {
159
159
Ok(h) => h,
160
160
Err(e) => {
161
161
return ApiError::from(e).into_response();
162
162
}
163
163
}
164
164
} else {
165
-
match crate::api::validation::validate_full_domain_handle(&input.handle) {
165
+
match tranquil_pds::api::validation::validate_full_domain_handle(&input.handle) {
166
166
Ok(h) => h,
167
167
Err(e) => return ApiError::from(e).into_response(),
168
168
}
···
173
173
.map(|e| e.trim().to_string())
174
174
.filter(|e| !e.is_empty());
175
175
if let Some(ref email) = email
176
-
&& !crate::api::validation::is_valid_email(email)
176
+
&& !tranquil_pds::api::validation::is_valid_email(email)
177
177
{
178
178
return ApiError::InvalidEmail.into_response();
179
179
}
···
191
191
tranquil_db_traits::CommsChannel::Discord => match &input.discord_username {
192
192
Some(username) if !username.trim().is_empty() => {
193
193
let clean = username.trim().to_lowercase();
194
-
if !crate::api::validation::is_valid_discord_username(&clean) {
194
+
if !tranquil_pds::api::validation::is_valid_discord_username(&clean) {
195
195
return ApiError::InvalidRequest(
196
196
"Invalid Discord username. Must be 2-32 lowercase characters (letters, numbers, underscores, periods)".into(),
197
197
).into_response();
···
203
203
tranquil_db_traits::CommsChannel::Telegram => match &input.telegram_username {
204
204
Some(username) if !username.trim().is_empty() => {
205
205
let clean = username.trim().trim_start_matches('@');
206
-
if !crate::api::validation::is_valid_telegram_username(clean) {
206
+
if !tranquil_pds::api::validation::is_valid_telegram_username(clean) {
207
207
return ApiError::InvalidRequest(
208
208
"Invalid Telegram username. Must be 5-32 characters, alphanumeric or underscore".into(),
209
209
).into_response();
···
257
257
let did_type = input.did_type.as_deref().unwrap_or("plc");
258
258
let did = match did_type {
259
259
"web" => {
260
-
if !crate::api::server::meta::is_self_hosted_did_web_enabled() {
260
+
if !tranquil_pds::util::is_self_hosted_did_web_enabled() {
261
261
return ApiError::SelfHostedDidWebDisabled.into_response();
262
262
}
263
263
let encoded_handle = handle.replace(':', "%3A");
···
408
408
.await
409
409
{
410
410
Ok(Some(key_info)) => {
411
-
match crate::config::decrypt_key(
411
+
match tranquil_pds::config::decrypt_key(
412
412
&key_info.key_bytes,
413
413
key_info.encryption_version,
414
414
) {
···
428
428
}
429
429
};
430
430
let access_meta =
431
-
match crate::auth::create_access_token_with_metadata(&did, &secret_key_bytes) {
431
+
match tranquil_pds::auth::create_access_token_with_metadata(&did, &secret_key_bytes) {
432
432
Ok(m) => m,
433
433
Err(e) => {
434
434
error!("Error creating access token: {:?}", e);
435
435
return ApiError::InternalError(None).into_response();
436
436
}
437
437
};
438
-
let refresh_meta = match crate::auth::create_refresh_token_with_metadata(
438
+
let refresh_meta = match tranquil_pds::auth::create_refresh_token_with_metadata(
439
439
&did,
440
440
&secret_key_bytes,
441
441
) {
···
463
463
}
464
464
let hostname = &tranquil_config::get().server.hostname;
465
465
let verification_required = if let Some(ref user_email) = email {
466
-
let token = crate::auth::verification_token::generate_migration_token(
466
+
let token = tranquil_pds::auth::verification_token::generate_migration_token(
467
467
&did_typed, user_email,
468
468
);
469
469
let formatted_token =
470
-
crate::auth::verification_token::format_token_for_display(&token);
471
-
if let Err(e) = crate::comms::comms_repo::enqueue_migration_verification(
470
+
tranquil_pds::auth::verification_token::format_token_for_display(&token);
471
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_migration_verification(
472
472
state.user_repo.as_ref(),
473
473
state.infra_repo.as_ref(),
474
474
reactivated.user_id,
···
590
590
None
591
591
};
592
592
593
-
let encrypted_key_bytes = match crate::config::encrypt_key(&secret_key_bytes) {
593
+
let encrypted_key_bytes = match tranquil_pds::config::encrypt_key(&secret_key_bytes) {
594
594
Ok(enc) => enc,
595
595
Err(e) => {
596
596
error!("Error encrypting user key: {:?}", e);
···
666
666
.map(|s| s.to_lowercase()),
667
667
deactivated_at,
668
668
encrypted_key_bytes,
669
-
encryption_version: crate::config::ENCRYPTION_VERSION,
669
+
encryption_version: tranquil_pds::config::ENCRYPTION_VERSION,
670
670
reserved_key_id,
671
671
commit_cid: commit_cid_str.clone(),
672
672
repo_rev: rev_str.clone(),
···
697
697
};
698
698
let user_id = create_result.user_id;
699
699
if !is_migration && !is_did_web_byod {
700
-
if let Err(e) = crate::api::repo::record::sequence_identity_event(
700
+
if let Err(e) = tranquil_pds::repo_ops::sequence_identity_event(
701
701
&state,
702
702
&did_for_commit,
703
703
Some(&handle_typed),
···
706
706
{
707
707
warn!("Failed to sequence identity event for {}: {}", did, e);
708
708
}
709
-
if let Err(e) = crate::api::repo::record::sequence_account_event(
709
+
if let Err(e) = tranquil_pds::repo_ops::sequence_account_event(
710
710
&state,
711
711
&did_for_commit,
712
712
tranquil_db_traits::AccountStatus::Active,
···
715
715
{
716
716
warn!("Failed to sequence account event for {}: {}", did, e);
717
717
}
718
-
if let Err(e) = crate::api::repo::record::sequence_genesis_commit(
718
+
if let Err(e) = tranquil_pds::repo_ops::sequence_genesis_commit(
719
719
&state,
720
720
&did_for_commit,
721
721
&commit_cid,
···
726
726
{
727
727
warn!("Failed to sequence commit event for {}: {}", did, e);
728
728
}
729
-
if let Err(e) = crate::api::repo::record::sequence_sync_event(
729
+
if let Err(e) = tranquil_pds::repo_ops::sequence_sync_event(
730
730
&state,
731
731
&did_for_commit,
732
732
&commit_cid_str,
···
740
740
"$type": "app.bsky.actor.profile",
741
741
"displayName": input.handle
742
742
});
743
-
if let Err(e) = crate::api::repo::record::create_record_internal(
743
+
if let Err(e) = tranquil_pds::repo_ops::create_record_internal(
744
744
&state,
745
745
&did_for_commit,
746
-
&crate::types::PROFILE_COLLECTION,
747
-
&crate::types::PROFILE_RKEY,
746
+
&tranquil_pds::types::PROFILE_COLLECTION,
747
+
&tranquil_pds::types::PROFILE_RKEY,
748
748
&profile_record,
749
749
)
750
750
.await
···
755
755
let hostname = &tranquil_config::get().server.hostname;
756
756
if !is_migration {
757
757
if let Some(ref recipient) = verification_recipient {
758
-
let verification_token = crate::auth::verification_token::generate_signup_token(
758
+
let verification_token = tranquil_pds::auth::verification_token::generate_signup_token(
759
759
&did_for_commit,
760
760
verification_channel,
761
761
recipient,
762
762
);
763
763
let formatted_token =
764
-
crate::auth::verification_token::format_token_for_display(&verification_token);
765
-
if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification(
764
+
tranquil_pds::auth::verification_token::format_token_for_display(&verification_token);
765
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification(
766
766
state.user_repo.as_ref(),
767
767
state.infra_repo.as_ref(),
768
768
user_id,
···
781
781
}
782
782
} else if let Some(ref user_email) = email {
783
783
let token =
784
-
crate::auth::verification_token::generate_migration_token(&did_for_commit, user_email);
785
-
let formatted_token = crate::auth::verification_token::format_token_for_display(&token);
786
-
if let Err(e) = crate::comms::comms_repo::enqueue_migration_verification(
784
+
tranquil_pds::auth::verification_token::generate_migration_token(&did_for_commit, user_email);
785
+
let formatted_token = tranquil_pds::auth::verification_token::format_token_for_display(&token);
786
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_migration_verification(
787
787
state.user_repo.as_ref(),
788
788
state.infra_repo.as_ref(),
789
789
user_id,
···
797
797
}
798
798
}
799
799
800
-
let access_meta = match crate::auth::create_access_token_with_metadata(&did, &secret_key_bytes)
800
+
let access_meta = match tranquil_pds::auth::create_access_token_with_metadata(&did, &secret_key_bytes)
801
801
{
802
802
Ok(m) => m,
803
803
Err(e) => {
···
806
806
}
807
807
};
808
808
let refresh_meta =
809
-
match crate::auth::create_refresh_token_with_metadata(&did, &secret_key_bytes) {
809
+
match tranquil_pds::auth::create_refresh_token_with_metadata(&did, &secret_key_bytes) {
810
810
Ok(m) => m,
811
811
Err(e) => {
812
812
error!("createAccount: Error creating refresh token: {:?}", e);
+37
-37
crates/tranquil-pds/src/api/identity/did.rs
crates/tranquil-api/src/identity/did.rs
+37
-37
crates/tranquil-pds/src/api/identity/did.rs
crates/tranquil-api/src/identity/did.rs
···
1
-
use crate::api::{ApiError, DidResponse, EmptyResponse};
2
-
use crate::auth::{Auth, NotTakendown};
3
-
use crate::plc::signing_key_to_did_key;
4
-
use crate::rate_limit::{
1
+
use tranquil_pds::api::{ApiError, DidResponse, EmptyResponse};
2
+
use tranquil_pds::auth::{Auth, NotTakendown};
3
+
use tranquil_pds::plc::signing_key_to_did_key;
4
+
use tranquil_pds::rate_limit::{
5
5
HandleUpdateDailyLimit, HandleUpdateLimit, check_user_rate_limit_with_message,
6
6
};
7
-
use crate::state::AppState;
8
-
use crate::types::Handle;
9
-
use crate::util::get_header_str;
7
+
use tranquil_pds::state::AppState;
8
+
use tranquil_pds::types::Handle;
9
+
use tranquil_pds::util::get_header_str;
10
10
use axum::{
11
11
Json,
12
12
extract::{Path, Query, State},
···
42
42
if handle_str.is_empty() {
43
43
return ApiError::InvalidRequest("handle is required".into()).into_response();
44
44
}
45
-
let cache_key = crate::cache_keys::handle_key(handle_str);
45
+
let cache_key = tranquil_pds::cache_keys::handle_key(handle_str);
46
46
if let Some(did) = state.cache.get(&cache_key).await {
47
47
return DidResponse::response(did).into_response();
48
48
}
···
61
61
.await;
62
62
DidResponse::response(row.did).into_response()
63
63
}
64
-
Ok(None) => match crate::handle::resolve_handle(handle.as_str()).await {
64
+
Ok(None) => match tranquil_pds::handle::resolve_handle(handle.as_str()).await {
65
65
Ok(did) => {
66
66
let _ = state
67
67
.cache
···
148
148
"id": did,
149
149
"service": [{
150
150
"id": "#atproto_pds",
151
-
"type": crate::plc::ServiceType::Pds.as_str(),
151
+
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
152
152
"serviceEndpoint": format!("https://{}", hostname)
153
153
}]
154
154
}))
···
158
158
async fn serve_handle_did_doc(state: &AppState, handle: &str, hostname: &str) -> Response {
159
159
let encoded_handle = handle.replace(':', "%3A");
160
160
let expected_did = format!("did:web:{}", encoded_handle);
161
-
let expected_did_typed: crate::types::Did = match expected_did.parse() {
161
+
let expected_did_typed: tranquil_pds::types::Did = match expected_did.parse() {
162
162
Ok(d) => d,
163
163
Err(_) => return ApiError::InvalidRequest("Invalid DID format".into()).into_response(),
164
164
};
···
216
216
})).collect::<Vec<_>>(),
217
217
"service": [{
218
218
"id": "#atproto_pds",
219
-
"type": crate::plc::ServiceType::Pds.as_str(),
219
+
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
220
220
"serviceEndpoint": service_endpoint
221
221
}]
222
222
}))
···
229
229
Err(_) => return ApiError::InternalError(None).into_response(),
230
230
};
231
231
let key_bytes: Vec<u8> =
232
-
match crate::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) {
232
+
match tranquil_pds::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) {
233
233
Ok(k) => k,
234
234
Err(_) => {
235
235
return ApiError::InternalError(None).into_response();
···
269
269
}],
270
270
"service": [{
271
271
"id": "#atproto_pds",
272
-
"type": crate::plc::ServiceType::Pds.as_str(),
272
+
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
273
273
"serviceEndpoint": service_endpoint
274
274
}]
275
275
}))
···
351
351
})).collect::<Vec<_>>(),
352
352
"service": [{
353
353
"id": "#atproto_pds",
354
-
"type": crate::plc::ServiceType::Pds.as_str(),
354
+
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
355
355
"serviceEndpoint": service_endpoint
356
356
}]
357
357
}))
···
364
364
Err(_) => return ApiError::InternalError(None).into_response(),
365
365
};
366
366
let key_bytes: Vec<u8> =
367
-
match crate::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) {
367
+
match tranquil_pds::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) {
368
368
Ok(k) => k,
369
369
Err(_) => {
370
370
return ApiError::InternalError(None).into_response();
···
404
404
}],
405
405
"service": [{
406
406
"id": "#atproto_pds",
407
-
"type": crate::plc::ServiceType::Pds.as_str(),
407
+
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
408
408
"serviceEndpoint": service_endpoint
409
409
}]
410
410
}))
···
480
480
let path = parts[3..].join("/");
481
481
format!("{}://{}/{}/did.json", scheme, domain, path)
482
482
};
483
-
let client = crate::api::proxy_client::did_resolution_client();
483
+
let client = tranquil_pds::api::proxy_client::did_resolution_client();
484
484
let resp = client
485
485
.get(&url)
486
486
.send()
···
503
503
))?;
504
504
let pds_endpoint = format!("https://{}", hostname);
505
505
let has_valid_service = services.iter().any(|s| {
506
-
s["type"] == crate::plc::ServiceType::Pds.as_str() && s["serviceEndpoint"] == pds_endpoint
506
+
s["type"] == tranquil_pds::plc::ServiceType::Pds.as_str() && s["serviceEndpoint"] == pds_endpoint
507
507
});
508
508
if !has_valid_service {
509
509
return Err(DidWebVerifyError::PdsNotListed(pds_endpoint));
···
600
600
verification_methods: VerificationMethods { atproto: did_key },
601
601
services: Services {
602
602
atproto_pds: AtprotoPds {
603
-
service_type: crate::plc::ServiceType::Pds.as_str().to_string(),
603
+
service_type: tranquil_pds::plc::ServiceType::Pds.as_str().to_string(),
604
604
endpoint: pds_endpoint,
605
605
},
606
606
},
···
619
619
auth: Auth<NotTakendown>,
620
620
Json(input): Json<UpdateHandleInput>,
621
621
) -> Result<Response, ApiError> {
622
-
if let Err(e) = crate::auth::scope_check::check_identity_scope(
622
+
if let Err(e) = tranquil_pds::auth::scope_check::check_identity_scope(
623
623
&auth.auth_source,
624
624
auth.scope.as_deref(),
625
-
crate::oauth::scopes::IdentityAttr::Handle,
625
+
tranquil_pds::oauth::scopes::IdentityAttr::Handle,
626
626
) {
627
627
return Ok(e);
628
628
}
···
672
672
"Handle segment cannot start or end with hyphen".into(),
673
673
)));
674
674
}
675
-
if crate::moderation::has_explicit_slur(&new_handle) {
675
+
if tranquil_pds::moderation::has_explicit_slur(&new_handle) {
676
676
return Err(ApiError::InvalidHandle(Some(
677
677
"Inappropriate language in handle".into(),
678
678
)));
···
704
704
Err(_) => return Err(ApiError::InvalidHandle(None)),
705
705
};
706
706
if let Err(e) =
707
-
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle_typed))
707
+
tranquil_pds::repo_ops::sequence_identity_event(&state, &did, Some(&handle_typed))
708
708
.await
709
709
{
710
710
warn!("Failed to sequence identity event for handle update: {}", e);
···
730
730
Err(_) => return Err(ApiError::InvalidHandle(None)),
731
731
};
732
732
if let Err(e) =
733
-
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle_typed))
733
+
tranquil_pds::repo_ops::sequence_identity_event(&state, &did, Some(&handle_typed))
734
734
.await
735
735
{
736
736
warn!("Failed to sequence identity event for handle update: {}", e);
737
737
}
738
738
return Ok(EmptyResponse::ok().into_response());
739
739
}
740
-
match crate::handle::verify_handle_ownership(&new_handle, &did).await {
740
+
match tranquil_pds::handle::verify_handle_ownership(&new_handle, &did).await {
741
741
Ok(()) => {}
742
-
Err(crate::handle::HandleResolutionError::NotFound) => {
742
+
Err(tranquil_pds::handle::HandleResolutionError::NotFound) => {
743
743
return Err(ApiError::HandleNotAvailable(None));
744
744
}
745
-
Err(crate::handle::HandleResolutionError::DidMismatch { expected, actual }) => {
745
+
Err(tranquil_pds::handle::HandleResolutionError::DidMismatch { expected, actual }) => {
746
746
return Err(ApiError::HandleNotAvailable(Some(format!(
747
747
"Handle points to different DID. Expected {}, got {}",
748
748
expected, actual
···
781
781
if !current_handle.is_empty() {
782
782
let _ = state
783
783
.cache
784
-
.delete(&crate::cache_keys::handle_key(¤t_handle))
784
+
.delete(&tranquil_pds::cache_keys::handle_key(¤t_handle))
785
785
.await;
786
786
}
787
787
let _ = state
788
788
.cache
789
-
.delete(&crate::cache_keys::handle_key(&handle))
789
+
.delete(&tranquil_pds::cache_keys::handle_key(&handle))
790
790
.await;
791
791
if let Err(e) =
792
-
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle_typed)).await
792
+
tranquil_pds::repo_ops::sequence_identity_event(&state, &did, Some(&handle_typed)).await
793
793
{
794
794
warn!("Failed to sequence identity event for handle update: {}", e);
795
795
}
···
801
801
802
802
pub async fn update_plc_handle(
803
803
state: &AppState,
804
-
did: &crate::types::Did,
804
+
did: &tranquil_pds::types::Did,
805
805
new_handle: &Handle,
806
806
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
807
807
if !did.as_str().starts_with("did:plc:") {
···
811
811
Some(r) => r,
812
812
None => return Ok(()),
813
813
};
814
-
let key_bytes = crate::config::decrypt_key(&user_row.key_bytes, user_row.encryption_version)?;
814
+
let key_bytes = tranquil_pds::config::decrypt_key(&user_row.key_bytes, user_row.encryption_version)?;
815
815
let signing_key = k256::ecdsa::SigningKey::from_slice(&key_bytes)?;
816
-
let plc_client = crate::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
816
+
let plc_client = tranquil_pds::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
817
817
let last_op = plc_client.get_last_op(did).await?;
818
818
let new_also_known_as = vec![format!("at://{}", new_handle)];
819
819
let update_op =
820
-
crate::plc::create_update_op(&last_op, None, None, Some(new_also_known_as), None)?;
821
-
let signed_op = crate::plc::sign_operation(&update_op, &signing_key)?;
820
+
tranquil_pds::plc::create_update_op(&last_op, None, None, Some(new_also_known_as), None)?;
821
+
let signed_op = tranquil_pds::plc::sign_operation(&update_op, &signing_key)?;
822
822
plc_client.send_operation(did, &signed_op).await?;
823
823
Ok(())
824
824
}
825
825
826
826
pub async fn well_known_atproto_did(State(state): State<AppState>, headers: HeaderMap) -> Response {
827
-
let host = match crate::util::get_header_str(&headers, http::header::HOST) {
827
+
let host = match tranquil_pds::util::get_header_str(&headers, http::header::HOST) {
828
828
Some(h) => h,
829
829
None => return (StatusCode::BAD_REQUEST, "Missing host header").into_response(),
830
830
};
+4
-4
crates/tranquil-pds/src/api/identity/handle.rs
crates/tranquil-api/src/identity/handle.rs
+4
-4
crates/tranquil-pds/src/api/identity/handle.rs
crates/tranquil-api/src/identity/handle.rs
···
1
-
use crate::rate_limit::{HandleVerificationLimit, RateLimited};
2
-
use crate::types::{Did, Handle};
1
+
use tranquil_pds::rate_limit::{HandleVerificationLimit, RateLimited};
2
+
use tranquil_pds::types::{Did, Handle};
3
3
use axum::{
4
4
Json,
5
5
response::{IntoResponse, Response},
···
29
29
let handle_str = input.handle.as_str();
30
30
let did_str = input.did.as_str();
31
31
32
-
let dns_mismatch = match crate::handle::resolve_handle_dns(handle_str).await {
32
+
let dns_mismatch = match tranquil_pds::handle::resolve_handle_dns(handle_str).await {
33
33
Ok(did) if did == did_str => {
34
34
return Json(VerifyHandleOwnershipOutput {
35
35
verified: true,
···
45
45
Err(_) => None,
46
46
};
47
47
48
-
match crate::handle::resolve_handle_http(handle_str).await {
48
+
match tranquil_pds::handle::resolve_handle_http(handle_str).await {
49
49
Ok(did) if did == did_str => Json(VerifyHandleOwnershipOutput {
50
50
verified: true,
51
51
method: Some("http".to_string()),
crates/tranquil-pds/src/api/identity/mod.rs
crates/tranquil-api/src/identity/mod.rs
crates/tranquil-pds/src/api/identity/mod.rs
crates/tranquil-api/src/identity/mod.rs
crates/tranquil-pds/src/api/identity/plc/mod.rs
crates/tranquil-api/src/identity/plc/mod.rs
crates/tranquil-pds/src/api/identity/plc/mod.rs
crates/tranquil-api/src/identity/plc/mod.rs
+8
-8
crates/tranquil-pds/src/api/identity/plc/request.rs
crates/tranquil-api/src/identity/plc/request.rs
+8
-8
crates/tranquil-pds/src/api/identity/plc/request.rs
crates/tranquil-api/src/identity/plc/request.rs
···
1
-
use crate::api::EmptyResponse;
2
-
use crate::api::error::{ApiError, DbResultExt};
3
-
use crate::auth::{Auth, Permissive};
4
-
use crate::state::AppState;
1
+
use tranquil_pds::api::EmptyResponse;
2
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
3
+
use tranquil_pds::auth::{Auth, Permissive};
4
+
use tranquil_pds::state::AppState;
5
5
use axum::{
6
6
extract::State,
7
7
response::{IntoResponse, Response},
···
10
10
use tracing::{info, warn};
11
11
12
12
fn generate_plc_token() -> String {
13
-
crate::util::generate_token_code()
13
+
tranquil_pds::util::generate_token_code()
14
14
}
15
15
16
16
pub async fn request_plc_operation_signature(
17
17
State(state): State<AppState>,
18
18
auth: Auth<Permissive>,
19
19
) -> Result<Response, ApiError> {
20
-
if let Err(e) = crate::auth::scope_check::check_identity_scope(
20
+
if let Err(e) = tranquil_pds::auth::scope_check::check_identity_scope(
21
21
&auth.auth_source,
22
22
auth.scope.as_deref(),
23
-
crate::oauth::scopes::IdentityAttr::Wildcard,
23
+
tranquil_pds::oauth::scopes::IdentityAttr::Wildcard,
24
24
) {
25
25
return Ok(e);
26
26
}
···
41
41
.log_db_err("creating PLC token")?;
42
42
43
43
let hostname = &tranquil_config::get().server.hostname;
44
-
if let Err(e) = crate::comms::comms_repo::enqueue_plc_operation(
44
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_plc_operation(
45
45
state.user_repo.as_ref(),
46
46
state.infra_repo.as_ref(),
47
47
user_id,
+9
-9
crates/tranquil-pds/src/api/identity/plc/sign.rs
crates/tranquil-api/src/identity/plc/sign.rs
+9
-9
crates/tranquil-pds/src/api/identity/plc/sign.rs
crates/tranquil-api/src/identity/plc/sign.rs
···
1
-
use crate::api::ApiError;
2
-
use crate::api::error::DbResultExt;
3
-
use crate::auth::{Auth, Permissive};
4
-
use crate::circuit_breaker::with_circuit_breaker;
5
-
use crate::plc::{PlcClient, PlcError, PlcService, ServiceType, create_update_op, sign_operation};
6
-
use crate::state::AppState;
1
+
use tranquil_pds::api::ApiError;
2
+
use tranquil_pds::api::error::DbResultExt;
3
+
use tranquil_pds::auth::{Auth, Permissive};
4
+
use tranquil_pds::circuit_breaker::with_circuit_breaker;
5
+
use tranquil_pds::plc::{PlcClient, PlcError, PlcService, ServiceType, create_update_op, sign_operation};
6
+
use tranquil_pds::state::AppState;
7
7
use axum::{
8
8
Json,
9
9
extract::State,
···
44
44
auth: Auth<Permissive>,
45
45
Json(input): Json<SignPlcOperationInput>,
46
46
) -> Result<Response, ApiError> {
47
-
if let Err(e) = crate::auth::scope_check::check_identity_scope(
47
+
if let Err(e) = tranquil_pds::auth::scope_check::check_identity_scope(
48
48
&auth.auth_source,
49
49
auth.scope.as_deref(),
50
-
crate::oauth::scopes::IdentityAttr::Wildcard,
50
+
tranquil_pds::oauth::scopes::IdentityAttr::Wildcard,
51
51
) {
52
52
return Ok(e);
53
53
}
···
86
86
.log_db_err("fetching user key")?
87
87
.ok_or_else(|| ApiError::InternalError(Some("User signing key not found".into())))?;
88
88
89
-
let key_bytes = crate::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version)
89
+
let key_bytes = tranquil_pds::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version)
90
90
.map_err(|e| {
91
91
error!("Failed to decrypt user key: {}", e);
92
92
ApiError::InternalError(None)
+13
-13
crates/tranquil-pds/src/api/identity/plc/submit.rs
crates/tranquil-api/src/identity/plc/submit.rs
+13
-13
crates/tranquil-pds/src/api/identity/plc/submit.rs
crates/tranquil-api/src/identity/plc/submit.rs
···
1
-
use crate::api::error::DbResultExt;
2
-
use crate::api::{ApiError, EmptyResponse};
3
-
use crate::auth::{Auth, Permissive};
4
-
use crate::circuit_breaker::with_circuit_breaker;
5
-
use crate::plc::{PlcClient, signing_key_to_did_key, validate_plc_operation};
6
-
use crate::state::AppState;
1
+
use tranquil_pds::api::error::DbResultExt;
2
+
use tranquil_pds::api::{ApiError, EmptyResponse};
3
+
use tranquil_pds::auth::{Auth, Permissive};
4
+
use tranquil_pds::circuit_breaker::with_circuit_breaker;
5
+
use tranquil_pds::plc::{PlcClient, signing_key_to_did_key, validate_plc_operation};
6
+
use tranquil_pds::state::AppState;
7
7
use axum::{
8
8
Json,
9
9
extract::State,
···
24
24
auth: Auth<Permissive>,
25
25
Json(input): Json<SubmitPlcOperationInput>,
26
26
) -> Result<Response, ApiError> {
27
-
if let Err(e) = crate::auth::scope_check::check_identity_scope(
27
+
if let Err(e) = tranquil_pds::auth::scope_check::check_identity_scope(
28
28
&auth.auth_source,
29
29
auth.scope.as_deref(),
30
-
crate::oauth::scopes::IdentityAttr::Wildcard,
30
+
tranquil_pds::oauth::scopes::IdentityAttr::Wildcard,
31
31
) {
32
32
return Ok(e);
33
33
}
···
57
57
.log_db_err("fetching user key")?
58
58
.ok_or_else(|| ApiError::InternalError(Some("User signing key not found".into())))?;
59
59
60
-
let key_bytes = crate::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version)
60
+
let key_bytes = tranquil_pds::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version)
61
61
.map_err(|e| {
62
62
error!("Failed to decrypt user key: {}", e);
63
63
ApiError::InternalError(None)
···
89
89
{
90
90
let service_type = pds.get("type").and_then(|v| v.as_str());
91
91
let endpoint = pds.get("endpoint").and_then(|v| v.as_str());
92
-
if service_type != Some(crate::plc::ServiceType::Pds.as_str()) {
92
+
if service_type != Some(tranquil_pds::plc::ServiceType::Pds.as_str()) {
93
93
return Err(ApiError::InvalidRequest(
94
94
"Incorrect type on atproto_pds service".into(),
95
95
));
···
147
147
}
148
148
let _ = state
149
149
.cache
150
-
.delete(&crate::cache_keys::handle_key(&user.handle))
150
+
.delete(&tranquil_pds::cache_keys::handle_key(&user.handle))
151
151
.await;
152
152
let _ = state
153
153
.cache
154
-
.delete(&crate::cache_keys::plc_doc_key(did))
154
+
.delete(&tranquil_pds::cache_keys::plc_doc_key(did))
155
155
.await;
156
156
let _ = state
157
157
.cache
158
-
.delete(&crate::cache_keys::plc_data_key(did))
158
+
.delete(&tranquil_pds::cache_keys::plc_data_key(did))
159
159
.await;
160
160
if state.did_resolver.refresh_did(did).await.is_none() {
161
161
warn!(did = %did, "Failed to refresh DID cache after PLC update");
+273
crates/tranquil-api/src/lib.rs
+273
crates/tranquil-api/src/lib.rs
···
1
+
pub mod actor;
2
+
pub mod admin;
3
+
pub mod age_assurance;
4
+
pub mod backup;
5
+
pub mod delegation;
6
+
pub mod discord_webhook;
7
+
pub mod identity;
8
+
pub mod moderation;
9
+
pub mod notification_prefs;
10
+
pub mod repo;
11
+
pub mod server;
12
+
pub mod telegram_webhook;
13
+
pub mod temp;
14
+
pub mod verification;
15
+
16
+
use tranquil_pds::state::AppState;
17
+
18
+
pub fn api_routes() -> axum::Router<AppState> {
19
+
use axum::routing::{get, post};
20
+
21
+
axum::Router::new()
22
+
.route("/_health", get(server::health))
23
+
.route(
24
+
"/com.atproto.server.describeServer",
25
+
get(server::describe_server),
26
+
)
27
+
.route(
28
+
"/com.atproto.server.createAccount",
29
+
post(identity::create_account),
30
+
)
31
+
.route(
32
+
"/com.atproto.server.createSession",
33
+
post(server::create_session),
34
+
)
35
+
.route(
36
+
"/com.atproto.server.getSession",
37
+
get(server::get_session),
38
+
)
39
+
.route("/_account.listSessions", get(server::list_sessions))
40
+
.route("/_account.revokeSession", post(server::revoke_session))
41
+
.route(
42
+
"/_account.revokeAllSessions",
43
+
post(server::revoke_all_sessions),
44
+
)
45
+
.route(
46
+
"/com.atproto.server.deleteSession",
47
+
post(server::delete_session),
48
+
)
49
+
.route(
50
+
"/com.atproto.server.refreshSession",
51
+
post(server::refresh_session),
52
+
)
53
+
.route(
54
+
"/com.atproto.server.confirmSignup",
55
+
post(server::confirm_signup),
56
+
)
57
+
.route(
58
+
"/com.atproto.server.resendVerification",
59
+
post(server::resend_verification),
60
+
)
61
+
.route(
62
+
"/com.atproto.server.getServiceAuth",
63
+
get(server::get_service_auth),
64
+
)
65
+
.route(
66
+
"/com.atproto.identity.resolveHandle",
67
+
get(identity::resolve_handle),
68
+
)
69
+
.route(
70
+
"/com.atproto.repo.createRecord",
71
+
post(repo::create_record),
72
+
)
73
+
.route("/com.atproto.repo.putRecord", post(repo::put_record))
74
+
.route("/com.atproto.repo.getRecord", get(repo::get_record))
75
+
.route(
76
+
"/com.atproto.repo.deleteRecord",
77
+
post(repo::delete_record),
78
+
)
79
+
.route(
80
+
"/com.atproto.repo.listRecords",
81
+
get(repo::list_records),
82
+
)
83
+
.route(
84
+
"/com.atproto.repo.describeRepo",
85
+
get(repo::describe_repo),
86
+
)
87
+
.route("/com.atproto.repo.uploadBlob", post(repo::upload_blob))
88
+
.route(
89
+
"/com.atproto.repo.applyWrites",
90
+
post(repo::apply_writes),
91
+
)
92
+
.route(
93
+
"/com.atproto.server.checkAccountStatus",
94
+
get(server::check_account_status),
95
+
)
96
+
.route(
97
+
"/com.atproto.identity.getRecommendedDidCredentials",
98
+
get(identity::get_recommended_did_credentials),
99
+
)
100
+
.route(
101
+
"/com.atproto.repo.listMissingBlobs",
102
+
get(repo::list_missing_blobs),
103
+
)
104
+
.route(
105
+
"/com.atproto.moderation.createReport",
106
+
post(moderation::create_report),
107
+
)
108
+
.route(
109
+
"/com.atproto.admin.getAccountInfo",
110
+
get(admin::get_account_info),
111
+
)
112
+
.route(
113
+
"/com.atproto.admin.getAccountInfos",
114
+
get(admin::get_account_infos),
115
+
)
116
+
.route(
117
+
"/com.atproto.admin.searchAccounts",
118
+
get(admin::search_accounts),
119
+
)
120
+
.route(
121
+
"/com.atproto.server.activateAccount",
122
+
post(server::activate_account),
123
+
)
124
+
.route(
125
+
"/com.atproto.server.deactivateAccount",
126
+
post(server::deactivate_account),
127
+
)
128
+
.route(
129
+
"/com.atproto.server.requestAccountDelete",
130
+
post(server::request_account_delete),
131
+
)
132
+
.route(
133
+
"/com.atproto.server.deleteAccount",
134
+
post(server::delete_account),
135
+
)
136
+
.route(
137
+
"/com.atproto.server.requestPasswordReset",
138
+
post(server::request_password_reset),
139
+
)
140
+
.route(
141
+
"/com.atproto.server.resetPassword",
142
+
post(server::reset_password),
143
+
)
144
+
.route("/_account.changePassword", post(server::change_password))
145
+
.route("/_account.removePassword", post(server::remove_password))
146
+
.route("/_account.setPassword", post(server::set_password))
147
+
.route("/_account.getPasswordStatus", get(server::get_password_status))
148
+
.route("/_account.getReauthStatus", get(server::get_reauth_status))
149
+
.route("/_account.reauthPassword", post(server::reauth_password))
150
+
.route("/_account.reauthTotp", post(server::reauth_totp))
151
+
.route("/_account.reauthPasskeyStart", post(server::reauth_passkey_start))
152
+
.route("/_account.reauthPasskeyFinish", post(server::reauth_passkey_finish))
153
+
.route("/_account.getLegacyLoginPreference", get(server::get_legacy_login_preference))
154
+
.route("/_account.updateLegacyLoginPreference", post(server::update_legacy_login_preference))
155
+
.route("/_account.updateLocale", post(server::update_locale))
156
+
.route("/_account.listTrustedDevices", get(server::list_trusted_devices))
157
+
.route("/_account.revokeTrustedDevice", post(server::revoke_trusted_device))
158
+
.route("/_account.updateTrustedDevice", post(server::update_trusted_device))
159
+
.route("/_account.createPasskeyAccount", post(server::create_passkey_account))
160
+
.route("/_account.startPasskeyRegistrationForSetup", post(server::start_passkey_registration_for_setup))
161
+
.route("/_account.completePasskeySetup", post(server::complete_passkey_setup))
162
+
.route("/_account.requestPasskeyRecovery", post(server::request_passkey_recovery))
163
+
.route("/_account.recoverPasskeyAccount", post(server::recover_passkey_account))
164
+
.route("/_account.updateDidDocument", post(server::update_did_document))
165
+
.route("/_account.getDidDocument", get(server::get_did_document))
166
+
.route("/com.atproto.server.requestEmailUpdate", post(server::request_email_update))
167
+
.route("/_checkEmailVerified", post(server::check_email_verified))
168
+
.route("/_checkChannelVerified", post(server::check_channel_verified))
169
+
.route("/com.atproto.server.confirmEmail", post(server::confirm_email))
170
+
.route("/com.atproto.server.updateEmail", post(server::update_email))
171
+
.route("/_account.authorizeEmailUpdate", get(server::authorize_email_update))
172
+
.route("/_account.checkEmailUpdateStatus", get(server::check_email_update_status))
173
+
.route("/_account.checkEmailInUse", post(server::check_email_in_use))
174
+
.route("/_account.checkCommsChannelInUse", post(server::check_comms_channel_in_use))
175
+
.route("/com.atproto.server.reserveSigningKey", post(server::reserve_signing_key))
176
+
.route("/com.atproto.server.verifyMigrationEmail", post(server::verify_migration_email))
177
+
.route("/com.atproto.server.resendMigrationVerification", post(server::resend_migration_verification))
178
+
.route("/com.atproto.identity.updateHandle", post(identity::update_handle))
179
+
.route("/com.atproto.identity.requestPlcOperationSignature", post(identity::request_plc_operation_signature))
180
+
.route("/com.atproto.identity.signPlcOperation", post(identity::sign_plc_operation))
181
+
.route("/com.atproto.identity.submitPlcOperation", post(identity::submit_plc_operation))
182
+
.route("/_identity.verifyHandleOwnership", post(identity::verify_handle_ownership))
183
+
.route("/com.atproto.repo.importRepo", post(repo::import_repo))
184
+
.route("/com.atproto.admin.deleteAccount", post(admin::delete_account))
185
+
.route("/com.atproto.admin.updateAccountEmail", post(admin::update_account_email))
186
+
.route("/com.atproto.admin.updateAccountHandle", post(admin::update_account_handle))
187
+
.route("/com.atproto.admin.updateAccountPassword", post(admin::update_account_password))
188
+
.route("/com.atproto.server.listAppPasswords", get(server::list_app_passwords))
189
+
.route("/com.atproto.server.createAppPassword", post(server::create_app_password))
190
+
.route("/com.atproto.server.revokeAppPassword", post(server::revoke_app_password))
191
+
.route("/com.atproto.server.createInviteCode", post(server::create_invite_code))
192
+
.route("/com.atproto.server.createInviteCodes", post(server::create_invite_codes))
193
+
.route("/com.atproto.server.getAccountInviteCodes", get(server::get_account_invite_codes))
194
+
.route("/com.atproto.server.createTotpSecret", post(server::create_totp_secret))
195
+
.route("/com.atproto.server.enableTotp", post(server::enable_totp))
196
+
.route("/com.atproto.server.disableTotp", post(server::disable_totp))
197
+
.route("/com.atproto.server.getTotpStatus", get(server::get_totp_status))
198
+
.route("/com.atproto.server.regenerateBackupCodes", post(server::regenerate_backup_codes))
199
+
.route("/com.atproto.server.startPasskeyRegistration", post(server::start_passkey_registration))
200
+
.route("/com.atproto.server.finishPasskeyRegistration", post(server::finish_passkey_registration))
201
+
.route("/com.atproto.server.listPasskeys", get(server::list_passkeys))
202
+
.route("/com.atproto.server.deletePasskey", post(server::delete_passkey))
203
+
.route("/com.atproto.server.updatePasskey", post(server::update_passkey))
204
+
.route("/com.atproto.admin.getInviteCodes", get(admin::get_invite_codes))
205
+
.route("/_admin.getServerStats", get(admin::get_server_stats))
206
+
.route("/_server.getConfig", get(admin::get_server_config))
207
+
.route("/_admin.updateServerConfig", post(admin::update_server_config))
208
+
.route("/com.atproto.admin.disableAccountInvites", post(admin::disable_account_invites))
209
+
.route("/com.atproto.admin.enableAccountInvites", post(admin::enable_account_invites))
210
+
.route("/com.atproto.admin.disableInviteCodes", post(admin::disable_invite_codes))
211
+
.route("/com.atproto.admin.getSubjectStatus", get(admin::get_subject_status))
212
+
.route("/com.atproto.admin.updateSubjectStatus", post(admin::update_subject_status))
213
+
.route("/com.atproto.admin.sendEmail", post(admin::send_email))
214
+
.route("/app.bsky.actor.getPreferences", get(actor::get_preferences))
215
+
.route("/app.bsky.actor.putPreferences", post(actor::put_preferences))
216
+
.route("/com.atproto.temp.checkSignupQueue", get(temp::check_signup_queue))
217
+
.route("/com.atproto.temp.dereferenceScope", post(temp::dereference_scope))
218
+
.route("/_account.getNotificationPrefs", get(notification_prefs::get_notification_prefs))
219
+
.route("/_account.updateNotificationPrefs", post(notification_prefs::update_notification_prefs))
220
+
.route("/_account.getNotificationHistory", get(notification_prefs::get_notification_history))
221
+
.route("/_account.confirmChannelVerification", post(verification::confirm_channel_verification))
222
+
.route("/_account.verifyToken", post(server::verify_token))
223
+
.route("/_delegation.listControllers", get(delegation::list_controllers))
224
+
.route("/_delegation.addController", post(delegation::add_controller))
225
+
.route("/_delegation.removeController", post(delegation::remove_controller))
226
+
.route("/_delegation.updateControllerScopes", post(delegation::update_controller_scopes))
227
+
.route("/_delegation.listControlledAccounts", get(delegation::list_controlled_accounts))
228
+
.route("/_delegation.getAuditLog", get(delegation::get_audit_log))
229
+
.route("/_delegation.getScopePresets", get(delegation::get_scope_presets))
230
+
.route("/_delegation.createDelegatedAccount", post(delegation::create_delegated_account))
231
+
.route("/_backup.listBackups", get(backup::list_backups))
232
+
.route("/_backup.getBackup", get(backup::get_backup))
233
+
.route("/_backup.createBackup", post(backup::create_backup))
234
+
.route("/_backup.deleteBackup", post(backup::delete_backup))
235
+
.route("/_backup.setEnabled", post(backup::set_backup_enabled))
236
+
.route("/_backup.exportBlobs", get(backup::export_blobs))
237
+
.route("/app.bsky.ageassurance.getState", get(age_assurance::get_state))
238
+
.route("/app.bsky.unspecced.getAgeAssuranceState", get(age_assurance::get_age_assurance_state))
239
+
}
240
+
241
+
pub fn well_known_api_routes() -> axum::Router<AppState> {
242
+
use axum::routing::get;
243
+
244
+
axum::Router::new()
245
+
.route("/did.json", get(identity::well_known_did))
246
+
.route("/atproto-did", get(identity::well_known_atproto_did))
247
+
}
248
+
249
+
pub fn webhook_routes() -> axum::Router<AppState> {
250
+
use axum::{extract::DefaultBodyLimit, routing::post};
251
+
252
+
axum::Router::new()
253
+
.route(
254
+
"/webhook/telegram",
255
+
post(telegram_webhook::handle_telegram_webhook)
256
+
.layer(DefaultBodyLimit::max(64 * 1024)),
257
+
)
258
+
.route(
259
+
"/webhook/discord",
260
+
post(discord_webhook::handle_discord_webhook)
261
+
.layer(DefaultBodyLimit::max(64 * 1024)),
262
+
)
263
+
}
264
+
265
+
pub fn misc_routes() -> axum::Router<AppState> {
266
+
use axum::routing::get;
267
+
268
+
axum::Router::new()
269
+
.route("/health", get(server::health))
270
+
.route("/robots.txt", get(server::robots_txt))
271
+
.route("/favicon.ico", get(server::get_logo))
272
+
.route("/u/{handle}/did.json", get(identity::user_did_doc))
273
+
}
+8
-8
crates/tranquil-pds/src/api/moderation/mod.rs
crates/tranquil-api/src/moderation/mod.rs
+8
-8
crates/tranquil-pds/src/api/moderation/mod.rs
crates/tranquil-api/src/moderation/mod.rs
···
1
-
use crate::api::ApiError;
2
-
use crate::api::proxy_client::{is_ssrf_safe, proxy_client};
3
-
use crate::auth::{AnyUser, Auth};
4
-
use crate::state::AppState;
1
+
use tranquil_pds::api::ApiError;
2
+
use tranquil_pds::api::proxy_client::{is_ssrf_safe, proxy_client};
3
+
use tranquil_pds::auth::{AnyUser, Auth};
4
+
use tranquil_pds::state::AppState;
5
5
use axum::{
6
6
Json,
7
7
extract::State,
···
94
94
95
95
async fn proxy_to_report_service(
96
96
state: &AppState,
97
-
auth_user: &crate::auth::AuthenticatedUser,
97
+
auth_user: &tranquil_pds::auth::AuthenticatedUser,
98
98
service_url: &str,
99
99
service_did: &str,
100
100
input: &CreateReportInput,
···
109
109
Some(kb) => kb.clone(),
110
110
None => match state.user_repo.get_with_key_by_did(&auth_user.did).await {
111
111
Ok(Some(user_with_key)) => {
112
-
match crate::config::decrypt_key(
112
+
match tranquil_pds::config::decrypt_key(
113
113
&user_with_key.key_bytes,
114
114
user_with_key.encryption_version,
115
115
) {
···
135
135
},
136
136
};
137
137
138
-
let service_token = match crate::auth::create_service_token(
138
+
let service_token = match tranquil_pds::auth::create_service_token(
139
139
&auth_user.did,
140
140
service_did,
141
141
"com.atproto.moderation.createReport",
···
211
211
212
212
async fn create_report_locally(
213
213
state: &AppState,
214
-
did: &crate::types::Did,
214
+
did: &tranquil_pds::types::Did,
215
215
is_takendown: bool,
216
216
input: CreateReportInput,
217
217
) -> Response {
+13
-13
crates/tranquil-pds/src/api/notification_prefs.rs
crates/tranquil-api/src/notification_prefs.rs
+13
-13
crates/tranquil-pds/src/api/notification_prefs.rs
crates/tranquil-api/src/notification_prefs.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::auth::{Active, Auth};
3
-
use crate::state::AppState;
1
+
use tranquil_pds::api::error::ApiError;
2
+
use tranquil_pds::auth::{Active, Auth};
3
+
use tranquil_pds::state::AppState;
4
4
use axum::{
5
5
Json,
6
6
extract::State,
···
142
142
handle: Option<&str>,
143
143
) -> Result<String, ApiError> {
144
144
let token =
145
-
crate::auth::verification_token::generate_channel_update_token(did, channel, identifier);
146
-
let formatted_token = crate::auth::verification_token::format_token_for_display(&token);
145
+
tranquil_pds::auth::verification_token::generate_channel_update_token(did, channel, identifier);
146
+
let formatted_token = tranquil_pds::auth::verification_token::format_token_for_display(&token);
147
147
148
148
match channel {
149
149
CommsChannel::Email => {
150
150
let hostname = &tranquil_config::get().server.hostname;
151
151
let handle_str = handle.unwrap_or("user");
152
-
crate::comms::comms_repo::enqueue_email_update(
152
+
tranquil_pds::comms::comms_repo::enqueue_email_update(
153
153
state.infra_repo.as_ref(),
154
154
user_id,
155
155
identifier,
···
183
183
.as_ref()
184
184
.and_then(|p| p.preferred_locale.as_deref())
185
185
.unwrap_or("en");
186
-
let strings = crate::comms::get_strings(locale);
187
-
let body = crate::comms::format_message(
186
+
let strings = tranquil_pds::comms::get_strings(locale);
187
+
let body = tranquil_pds::comms::format_message(
188
188
strings.channel_verification_body,
189
189
&[("code", &formatted_token), ("verify_link", &verify_link)],
190
190
);
191
-
let subject = crate::comms::format_message(
191
+
let subject = tranquil_pds::comms::format_message(
192
192
strings.channel_verification_subject,
193
193
&[("hostname", hostname)],
194
194
);
···
277
277
return Err(ApiError::InvalidRequest("Email cannot be empty".into()));
278
278
}
279
279
280
-
if !crate::api::validation::is_valid_email(&email_clean) {
280
+
if !tranquil_pds::api::validation::is_valid_email(&email_clean) {
281
281
return Err(ApiError::InvalidEmail);
282
282
}
283
283
···
310
310
.await
311
311
.map_err(|e| ApiError::InternalError(Some(format!("Database error: {}", e))))?;
312
312
info!(did = %auth.did, "Cleared Discord");
313
-
} else if !crate::api::validation::is_valid_discord_username(&discord_clean) {
313
+
} else if !tranquil_pds::api::validation::is_valid_discord_username(&discord_clean) {
314
314
return Err(ApiError::InvalidRequest(
315
315
"Invalid Discord username. Must be 2-32 lowercase characters (letters, numbers, underscores, periods)"
316
316
.into(),
···
340
340
.await
341
341
.map_err(|e| ApiError::InternalError(Some(format!("Database error: {}", e))))?;
342
342
info!(did = %auth.did, "Cleared Telegram username");
343
-
} else if !crate::api::validation::is_valid_telegram_username(telegram_clean) {
343
+
} else if !tranquil_pds::api::validation::is_valid_telegram_username(telegram_clean) {
344
344
return Err(ApiError::InvalidRequest(
345
345
"Invalid Telegram username. Must be 5-32 characters, alphanumeric or underscore"
346
346
.into(),
···
370
370
.await
371
371
.map_err(|e| ApiError::InternalError(Some(format!("Database error: {}", e))))?;
372
372
info!(did = %auth.did, "Cleared Signal username");
373
-
} else if !crate::comms::is_valid_signal_username(&signal_clean) {
373
+
} else if !tranquil_pds::comms::is_valid_signal_username(&signal_clean) {
374
374
return Err(ApiError::InvalidRequest(
375
375
"Invalid Signal username. Must be 3-32 characters followed by .XX (e.g. username.01)"
376
376
.into(),
+6
-6
crates/tranquil-pds/src/api/repo/blob.rs
crates/tranquil-api/src/repo/blob.rs
+6
-6
crates/tranquil-pds/src/api/repo/blob.rs
crates/tranquil-api/src/repo/blob.rs
···
1
-
use crate::api::error::{ApiError, DbResultExt};
2
-
use crate::auth::{Auth, AuthAny, NotTakendown, Permissive, VerifyScope};
3
-
use crate::delegation::DelegationActionType;
4
-
use crate::state::AppState;
5
-
use crate::types::{CidLink, Did};
6
-
use crate::util::get_header_str;
1
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
2
+
use tranquil_pds::auth::{Auth, AuthAny, NotTakendown, Permissive, VerifyScope};
3
+
use tranquil_pds::delegation::DelegationActionType;
4
+
use tranquil_pds::state::AppState;
5
+
use tranquil_pds::types::{CidLink, Did};
6
+
use tranquil_pds::util::get_header_str;
7
7
use axum::body::Body;
8
8
use axum::{
9
9
Json,
+16
-16
crates/tranquil-pds/src/api/repo/import.rs
crates/tranquil-api/src/repo/import.rs
+16
-16
crates/tranquil-pds/src/api/repo/import.rs
crates/tranquil-api/src/repo/import.rs
···
1
-
use crate::api::EmptyResponse;
2
-
use crate::api::error::{ApiError, DbResultExt};
3
-
use crate::api::repo::record::create_signed_commit;
4
-
use crate::auth::{Auth, NotTakendown};
5
-
use crate::state::AppState;
6
-
use crate::sync::import::{ImportError, apply_import, parse_car};
7
-
use crate::sync::verify::CarVerifier;
8
-
use crate::types::Did;
1
+
use tranquil_pds::api::EmptyResponse;
2
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
3
+
use tranquil_pds::repo_ops::create_signed_commit;
4
+
use tranquil_pds::auth::{Auth, NotTakendown};
5
+
use tranquil_pds::state::AppState;
6
+
use tranquil_pds::sync::import::{ImportError, apply_import, parse_car};
7
+
use tranquil_pds::sync::verify::CarVerifier;
8
+
use tranquil_pds::types::Did;
9
9
use axum::{
10
10
body::Bytes,
11
11
extract::State,
···
121
121
verified.rev, verified.data_cid
122
122
);
123
123
}
124
-
Err(crate::sync::verify::VerifyError::DidMismatch {
124
+
Err(tranquil_pds::sync::verify::VerifyError::DidMismatch {
125
125
commit_did,
126
126
expected_did,
127
127
}) => {
···
130
130
commit_did, expected_did
131
131
)));
132
132
}
133
-
Err(crate::sync::verify::VerifyError::MstValidationFailed(msg)) => {
133
+
Err(tranquil_pds::sync::verify::VerifyError::MstValidationFailed(msg)) => {
134
134
return Err(ApiError::InvalidRequest(format!(
135
135
"MST validation failed: {}",
136
136
msg
···
154
154
verified.rev, verified.data_cid
155
155
);
156
156
}
157
-
Err(crate::sync::verify::VerifyError::DidMismatch {
157
+
Err(tranquil_pds::sync::verify::VerifyError::DidMismatch {
158
158
commit_did,
159
159
expected_did,
160
160
}) => {
···
163
163
commit_did, expected_did
164
164
)));
165
165
}
166
-
Err(crate::sync::verify::VerifyError::InvalidSignature) => {
166
+
Err(tranquil_pds::sync::verify::VerifyError::InvalidSignature) => {
167
167
return Err(ApiError::InvalidRequest(
168
168
"CAR file commit signature verification failed".into(),
169
169
));
170
170
}
171
-
Err(crate::sync::verify::VerifyError::DidResolutionFailed(msg)) => {
171
+
Err(tranquil_pds::sync::verify::VerifyError::DidResolutionFailed(msg)) => {
172
172
warn!("DID resolution failed during import verification: {}", msg);
173
173
return Err(ApiError::InvalidRequest(format!(
174
174
"Failed to verify DID: {}",
175
175
msg
176
176
)));
177
177
}
178
-
Err(crate::sync::verify::VerifyError::NoSigningKey) => {
178
+
Err(tranquil_pds::sync::verify::VerifyError::NoSigningKey) => {
179
179
return Err(ApiError::InvalidRequest(
180
180
"DID document does not contain a signing key".into(),
181
181
));
182
182
}
183
-
Err(crate::sync::verify::VerifyError::MstValidationFailed(msg)) => {
183
+
Err(tranquil_pds::sync::verify::VerifyError::MstValidationFailed(msg)) => {
184
184
return Err(ApiError::InvalidRequest(format!(
185
185
"MST validation failed: {}",
186
186
msg
···
264
264
ApiError::InternalError(Some("Signing key not found".into()))
265
265
})?;
266
266
let key_bytes =
267
-
crate::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version)
267
+
tranquil_pds::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version)
268
268
.map_err(|e| {
269
269
error!("Failed to decrypt signing key: {}", e);
270
270
ApiError::InternalError(None)
+5
-5
crates/tranquil-pds/src/api/repo/meta.rs
crates/tranquil-api/src/repo/meta.rs
+5
-5
crates/tranquil-pds/src/api/repo/meta.rs
crates/tranquil-api/src/repo/meta.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::state::AppState;
3
-
use crate::types::AtIdentifier;
1
+
use tranquil_pds::api::error::ApiError;
2
+
use tranquil_pds::state::AppState;
3
+
use tranquil_pds::types::AtIdentifier;
4
4
use axum::{
5
5
Json,
6
6
extract::{Query, State},
···
20
20
) -> Response {
21
21
let hostname_for_handles = tranquil_config::get().server.hostname_without_port();
22
22
let user_row = if input.repo.is_did() {
23
-
let did: crate::types::Did = match input.repo.as_str().parse() {
23
+
let did: tranquil_pds::types::Did = match input.repo.as_str().parse() {
24
24
Ok(d) => d,
25
25
Err(_) => return ApiError::InvalidRequest("Invalid DID format".into()).into_response(),
26
26
};
···
36
36
} else {
37
37
repo_str.to_string()
38
38
};
39
-
let handle: crate::types::Handle = match handle_str.parse() {
39
+
let handle: tranquil_pds::types::Handle = match handle_str.parse() {
40
40
Ok(h) => h,
41
41
Err(_) => {
42
42
return ApiError::InvalidRequest("Invalid handle format".into()).into_response();
crates/tranquil-pds/src/api/repo/mod.rs
crates/tranquil-api/src/repo/mod.rs
crates/tranquil-pds/src/api/repo/mod.rs
crates/tranquil-api/src/repo/mod.rs
+11
-11
crates/tranquil-pds/src/api/repo/record/batch.rs
crates/tranquil-api/src/repo/record/batch.rs
+11
-11
crates/tranquil-pds/src/api/repo/record/batch.rs
crates/tranquil-api/src/repo/record/batch.rs
···
1
1
use super::validation::validate_record_with_status;
2
2
use super::validation_mode::{ValidationMode, deserialize_validation_mode};
3
-
use crate::api::error::ApiError;
4
-
use crate::api::repo::record::utils::{CommitParams, RecordOp, commit_and_log, extract_blob_cids};
5
-
use crate::auth::{
3
+
use tranquil_pds::api::error::ApiError;
4
+
use crate::repo::record::utils::{CommitParams, RecordOp, commit_and_log, extract_blob_cids};
5
+
use tranquil_pds::auth::{
6
6
Active, Auth, WriteOpKind, require_not_migrated, require_verified_or_delegated,
7
7
verify_batch_write_scopes,
8
8
};
9
-
use crate::cid_types::CommitCid;
10
-
use crate::delegation::DelegationActionType;
11
-
use crate::repo::tracking::TrackingBlockStore;
12
-
use crate::state::AppState;
13
-
use crate::types::{AtIdentifier, AtUri, Did, Nsid, Rkey};
14
-
use crate::validation::ValidationStatus;
9
+
use tranquil_pds::cid_types::CommitCid;
10
+
use tranquil_pds::delegation::DelegationActionType;
11
+
use tranquil_pds::repo::tracking::TrackingBlockStore;
12
+
use tranquil_pds::state::AppState;
13
+
use tranquil_pds::types::{AtIdentifier, AtUri, Did, Nsid, Rkey};
14
+
use tranquil_pds::validation::ValidationStatus;
15
15
use axum::{
16
16
Json,
17
17
extract::State,
···
74
74
};
75
75
all_blob_cids.extend(extract_blob_cids(value));
76
76
let rkey = rkey.clone().unwrap_or_else(Rkey::generate);
77
-
let record_ipld = crate::util::json_to_ipld(value);
77
+
let record_ipld = tranquil_pds::util::json_to_ipld(value);
78
78
let record_bytes = serde_ipld_dagcbor::to_vec(&record_ipld).map_err(|_| {
79
79
ApiError::InvalidRecord("Failed to serialize record".into()).into_response()
80
80
})?;
···
126
126
}
127
127
};
128
128
all_blob_cids.extend(extract_blob_cids(value));
129
-
let record_ipld = crate::util::json_to_ipld(value);
129
+
let record_ipld = tranquil_pds::util::json_to_ipld(value);
130
130
let record_bytes = serde_ipld_dagcbor::to_vec(&record_ipld).map_err(|_| {
131
131
ApiError::InvalidRecord("Failed to serialize record".into()).into_response()
132
132
})?;
+11
-11
crates/tranquil-pds/src/api/repo/record/delete.rs
crates/tranquil-api/src/repo/record/delete.rs
+11
-11
crates/tranquil-pds/src/api/repo/record/delete.rs
crates/tranquil-api/src/repo/record/delete.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::api::repo::record::utils::{
1
+
use tranquil_pds::api::error::ApiError;
2
+
use crate::repo::record::utils::{
3
3
CommitError, CommitParams, RecordOp, commit_and_log, get_current_root_cid,
4
4
};
5
-
use crate::api::repo::record::write::{CommitInfo, prepare_repo_write};
6
-
use crate::auth::{Active, Auth, VerifyScope};
7
-
use crate::cid_types::CommitCid;
8
-
use crate::delegation::DelegationActionType;
9
-
use crate::repo::tracking::TrackingBlockStore;
10
-
use crate::state::AppState;
11
-
use crate::types::{AtIdentifier, AtUri, Nsid, Rkey};
5
+
use crate::repo::record::write::{CommitInfo, prepare_repo_write};
6
+
use tranquil_pds::auth::{Active, Auth, VerifyScope};
7
+
use tranquil_pds::cid_types::CommitCid;
8
+
use tranquil_pds::delegation::DelegationActionType;
9
+
use tranquil_pds::repo::tracking::TrackingBlockStore;
10
+
use tranquil_pds::state::AppState;
11
+
use tranquil_pds::types::{AtIdentifier, AtUri, Nsid, Rkey};
12
12
use axum::{
13
13
Json,
14
14
extract::State,
···
45
45
State(state): State<AppState>,
46
46
auth: Auth<Active>,
47
47
Json(input): Json<DeleteRecordInput>,
48
-
) -> Result<Response, crate::api::error::ApiError> {
48
+
) -> Result<Response, tranquil_pds::api::error::ApiError> {
49
49
let scope_proof = match auth.verify_repo_delete(&input.collection) {
50
50
Ok(proof) => proof,
51
51
Err(e) => return Ok(e.into_response()),
···
229
229
.into_response())
230
230
}
231
231
232
-
use crate::types::Did;
232
+
use tranquil_pds::types::Did;
233
233
use uuid::Uuid;
234
234
235
235
pub async fn delete_record_internal(
crates/tranquil-pds/src/api/repo/record/mod.rs
crates/tranquil-api/src/repo/record/mod.rs
crates/tranquil-pds/src/api/repo/record/mod.rs
crates/tranquil-api/src/repo/record/mod.rs
crates/tranquil-pds/src/api/repo/record/pagination.rs
crates/tranquil-api/src/repo/record/pagination.rs
crates/tranquil-pds/src/api/repo/record/pagination.rs
crates/tranquil-api/src/repo/record/pagination.rs
+8
-8
crates/tranquil-pds/src/api/repo/record/read.rs
crates/tranquil-api/src/repo/record/read.rs
+8
-8
crates/tranquil-pds/src/api/repo/record/read.rs
crates/tranquil-api/src/repo/record/read.rs
···
1
1
use super::pagination::{PaginationDirection, deserialize_pagination_direction};
2
-
use crate::api::error::ApiError;
3
-
use crate::state::AppState;
4
-
use crate::types::{AtIdentifier, Nsid, Rkey};
2
+
use tranquil_pds::api::error::ApiError;
3
+
use tranquil_pds::state::AppState;
4
+
use tranquil_pds::types::{AtIdentifier, Nsid, Rkey};
5
5
use axum::{
6
6
Json,
7
7
extract::{Query, State},
···
61
61
) -> Response {
62
62
let hostname_for_handles = tranquil_config::get().server.hostname_without_port();
63
63
let user_id_opt = if input.repo.is_did() {
64
-
let did: crate::types::Did = match input.repo.as_str().parse() {
64
+
let did: tranquil_pds::types::Did = match input.repo.as_str().parse() {
65
65
Ok(d) => d,
66
66
Err(_) => return ApiError::InvalidRequest("Invalid DID format".into()).into_response(),
67
67
};
···
73
73
} else {
74
74
repo_str.to_string()
75
75
};
76
-
let handle: crate::types::Handle = match handle_str.parse() {
76
+
let handle: tranquil_pds::types::Handle = match handle_str.parse() {
77
77
Ok(h) => h,
78
78
Err(_) => {
79
79
return ApiError::InvalidRequest("Invalid handle format".into()).into_response();
···
160
160
) -> Response {
161
161
let hostname_for_handles = tranquil_config::get().server.hostname_without_port();
162
162
let user_id_opt = if input.repo.is_did() {
163
-
let did: crate::types::Did = match input.repo.as_str().parse() {
163
+
let did: tranquil_pds::types::Did = match input.repo.as_str().parse() {
164
164
Ok(d) => d,
165
165
Err(_) => return ApiError::InvalidRequest("Invalid DID format".into()).into_response(),
166
166
};
···
172
172
} else {
173
173
repo_str.to_string()
174
174
};
175
-
let handle: crate::types::Handle = match handle_str.parse() {
175
+
let handle: tranquil_pds::types::Handle = match handle_str.parse() {
176
176
Ok(h) => h,
177
177
Err(_) => {
178
178
return ApiError::InvalidRequest("Invalid handle format".into()).into_response();
···
198
198
let cursor_rkey = input
199
199
.cursor
200
200
.as_ref()
201
-
.and_then(|c| c.parse::<crate::types::Rkey>().ok());
201
+
.and_then(|c| c.parse::<tranquil_pds::types::Rkey>().ok());
202
202
let rows = match state
203
203
.repo_repo
204
204
.list_records(
+1
crates/tranquil-api/src/repo/record/utils.rs
+1
crates/tranquil-api/src/repo/record/utils.rs
···
1
+
pub use tranquil_pds::repo_ops::*;
+3
-3
crates/tranquil-pds/src/api/repo/record/validation.rs
crates/tranquil-api/src/repo/record/validation.rs
+3
-3
crates/tranquil-pds/src/api/repo/record/validation.rs
crates/tranquil-api/src/repo/record/validation.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::types::{Nsid, Rkey};
3
-
use crate::validation::{RecordValidator, ValidationError, ValidationStatus};
1
+
use tranquil_pds::api::error::ApiError;
2
+
use tranquil_pds::types::{Nsid, Rkey};
3
+
use tranquil_pds::validation::{RecordValidator, ValidationError, ValidationStatus};
4
4
use axum::response::Response;
5
5
6
6
pub async fn validate_record_with_status(
crates/tranquil-pds/src/api/repo/record/validation_mode.rs
crates/tranquil-api/src/repo/record/validation_mode.rs
crates/tranquil-pds/src/api/repo/record/validation_mode.rs
crates/tranquil-api/src/repo/record/validation_mode.rs
+13
-13
crates/tranquil-pds/src/api/repo/record/write.rs
crates/tranquil-api/src/repo/record/write.rs
+13
-13
crates/tranquil-pds/src/api/repo/record/write.rs
crates/tranquil-api/src/repo/record/write.rs
···
1
1
use super::validation::validate_record_with_status;
2
2
use super::validation_mode::{ValidationMode, deserialize_validation_mode};
3
-
use crate::api::error::ApiError;
4
-
use crate::api::repo::record::utils::{
3
+
use tranquil_pds::api::error::ApiError;
4
+
use crate::repo::record::utils::{
5
5
CommitParams, RecordOp, commit_and_log, extract_backlinks, extract_blob_cids,
6
6
get_current_root_cid,
7
7
};
8
-
use crate::auth::{
8
+
use tranquil_pds::auth::{
9
9
Active, Auth, AuthSource, RepoScopeAction, ScopeVerified, VerifyScope, require_not_migrated,
10
10
require_verified_or_delegated,
11
11
};
12
-
use crate::cid_types::CommitCid;
13
-
use crate::delegation::DelegationActionType;
14
-
use crate::repo::tracking::TrackingBlockStore;
15
-
use crate::state::AppState;
16
-
use crate::types::{AtIdentifier, AtUri, Did, Nsid, Rkey};
17
-
use crate::validation::ValidationStatus;
12
+
use tranquil_pds::cid_types::CommitCid;
13
+
use tranquil_pds::delegation::DelegationActionType;
14
+
use tranquil_pds::repo::tracking::TrackingBlockStore;
15
+
use tranquil_pds::state::AppState;
16
+
use tranquil_pds::types::{AtIdentifier, AtUri, Did, Nsid, Rkey};
17
+
use tranquil_pds::validation::ValidationStatus;
18
18
use axum::{
19
19
Json,
20
20
extract::State,
···
104
104
State(state): State<AppState>,
105
105
auth: Auth<Active>,
106
106
Json(input): Json<CreateRecordInput>,
107
-
) -> Result<Response, crate::api::error::ApiError> {
107
+
) -> Result<Response, tranquil_pds::api::error::ApiError> {
108
108
let scope_proof = match auth.verify_repo_create(&input.collection) {
109
109
Ok(proof) => proof,
110
110
Err(e) => return Ok(e.into_response()),
···
232
232
}
233
233
}
234
234
235
-
let record_ipld = crate::util::json_to_ipld(&input.record);
235
+
let record_ipld = tranquil_pds::util::json_to_ipld(&input.record);
236
236
let mut record_bytes = Vec::new();
237
237
if serde_ipld_dagcbor::to_writer(&mut record_bytes, &record_ipld).is_err() {
238
238
return Ok(ApiError::InvalidRecord("Failed to serialize record".into()).into_response());
···
408
408
State(state): State<AppState>,
409
409
auth: Auth<Active>,
410
410
Json(input): Json<PutRecordInput>,
411
-
) -> Result<Response, crate::api::error::ApiError> {
411
+
) -> Result<Response, tranquil_pds::api::error::ApiError> {
412
412
let upsert_proof = match auth.verify_repo_upsert(&input.collection) {
413
413
Ok(proof) => proof,
414
414
Err(e) => return Ok(e.into_response()),
···
476
476
}
477
477
}
478
478
let existing_cid = mst.get(&key).await.ok().flatten();
479
-
let record_ipld = crate::util::json_to_ipld(&input.record);
479
+
let record_ipld = tranquil_pds::util::json_to_ipld(&input.record);
480
480
let mut record_bytes = Vec::new();
481
481
if serde_ipld_dagcbor::to_writer(&mut record_bytes, &record_ipld).is_err() {
482
482
return Ok(ApiError::InvalidRecord("Failed to serialize record".into()).into_response());
+31
-31
crates/tranquil-pds/src/api/server/account_status.rs
crates/tranquil-api/src/server/account_status.rs
+31
-31
crates/tranquil-pds/src/api/server/account_status.rs
crates/tranquil-api/src/server/account_status.rs
···
1
-
use crate::api::EmptyResponse;
2
-
use crate::api::error::{ApiError, DbResultExt};
3
-
use crate::auth::{Auth, NotTakendown, Permissive, require_legacy_session_mfa};
4
-
use crate::cache::Cache;
5
-
use crate::plc::PlcClient;
6
-
use crate::state::AppState;
7
-
use crate::types::PlainPassword;
1
+
use tranquil_pds::api::EmptyResponse;
2
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
3
+
use tranquil_pds::auth::{Auth, NotTakendown, Permissive, require_legacy_session_mfa};
4
+
use tranquil_pds::cache::Cache;
5
+
use tranquil_pds::plc::PlcClient;
6
+
use tranquil_pds::state::AppState;
7
+
use tranquil_pds::types::PlainPassword;
8
8
use axum::{
9
9
Json,
10
10
extract::State,
···
117
117
async fn is_valid_did_for_service(
118
118
user_repo: &dyn tranquil_db_traits::UserRepository,
119
119
cache: Arc<dyn Cache>,
120
-
did: &crate::types::Did,
120
+
did: &tranquil_pds::types::Did,
121
121
) -> bool {
122
122
assert_valid_did_document_for_service(user_repo, cache, did, false)
123
123
.await
···
127
127
async fn assert_valid_did_document_for_service(
128
128
user_repo: &dyn tranquil_db_traits::UserRepository,
129
129
cache: Arc<dyn Cache>,
130
-
did: &crate::types::Did,
130
+
did: &tranquil_pds::types::Did,
131
131
with_retry: bool,
132
132
) -> Result<(), ApiError> {
133
133
let hostname = &tranquil_config::get().server.hostname;
···
226
226
227
227
if let Some(key_info) = user_key {
228
228
let key_bytes =
229
-
crate::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version)
229
+
tranquil_pds::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version)
230
230
.map_err(|e| {
231
231
error!("Failed to decrypt user key: {}", e);
232
232
ApiError::InternalError(None)
···
235
235
error!("Failed to create signing key: {:?}", e);
236
236
ApiError::InternalError(None)
237
237
})?;
238
-
let expected_did_key = crate::plc::signing_key_to_did_key(&signing_key);
238
+
let expected_did_key = tranquil_pds::plc::signing_key_to_did_key(&signing_key);
239
239
240
240
if doc_signing_key != Some(&expected_did_key) {
241
241
warn!(
···
248
248
}
249
249
}
250
250
} else if let Some(host_and_path) = did.as_str().strip_prefix("did:web:") {
251
-
let client = crate::api::proxy_client::did_resolution_client();
251
+
let client = tranquil_pds::api::proxy_client::did_resolution_client();
252
252
let decoded = host_and_path.replace("%3A", ":");
253
253
let parts: Vec<&str> = decoded.split(':').collect();
254
254
let (host, path_parts) = if parts.len() > 1 && parts[1].chars().all(|c| c.is_ascii_digit())
···
284
284
arr.iter().find(|svc| {
285
285
svc.get("id").and_then(|id| id.as_str()) == Some("#atproto_pds")
286
286
|| svc.get("type").and_then(|t| t.as_str())
287
-
== Some(crate::plc::ServiceType::Pds.as_str())
287
+
== Some(tranquil_pds::plc::ServiceType::Pds.as_str())
288
288
})
289
289
})
290
290
.and_then(|svc| svc.get("serviceEndpoint"))
···
314
314
auth.did
315
315
);
316
316
317
-
if let Err(e) = crate::auth::scope_check::check_account_scope(
317
+
if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope(
318
318
&auth.auth_source,
319
319
auth.scope.as_deref(),
320
-
crate::oauth::scopes::AccountAttr::Repo,
321
-
crate::oauth::scopes::AccountAction::Manage,
320
+
tranquil_pds::oauth::scopes::AccountAttr::Repo,
321
+
tranquil_pds::oauth::scopes::AccountAction::Manage,
322
322
) {
323
323
info!("[MIGRATION] activateAccount: Scope check failed");
324
324
return Ok(e);
···
365
365
did
366
366
);
367
367
if let Some(ref h) = handle {
368
-
let _ = state.cache.delete(&crate::cache_keys::handle_key(h)).await;
368
+
let _ = state.cache.delete(&tranquil_pds::cache_keys::handle_key(h)).await;
369
369
}
370
370
let _ = state
371
371
.cache
372
-
.delete(&crate::cache_keys::plc_doc_key(&did))
372
+
.delete(&tranquil_pds::cache_keys::plc_doc_key(&did))
373
373
.await;
374
374
let _ = state
375
375
.cache
376
-
.delete(&crate::cache_keys::plc_data_key(&did))
376
+
.delete(&tranquil_pds::cache_keys::plc_data_key(&did))
377
377
.await;
378
378
if state.did_resolver.refresh_did(did.as_str()).await.is_none() {
379
379
warn!(
···
385
385
"[MIGRATION] activateAccount: Sequencing account event (active=true) for did={}",
386
386
did
387
387
);
388
-
if let Err(e) = crate::api::repo::record::sequence_account_event(
388
+
if let Err(e) = tranquil_pds::repo_ops::sequence_account_event(
389
389
&state,
390
390
&did,
391
391
tranquil_db_traits::AccountStatus::Active,
···
404
404
did, handle
405
405
);
406
406
let handle_typed = handle.clone();
407
-
if let Err(e) = crate::api::repo::record::sequence_identity_event(
407
+
if let Err(e) = tranquil_pds::repo_ops::sequence_identity_event(
408
408
&state,
409
409
&did,
410
410
handle_typed.as_ref(),
···
438
438
} else {
439
439
None
440
440
};
441
-
if let Err(e) = crate::api::repo::record::sequence_sync_event(
441
+
if let Err(e) = tranquil_pds::repo_ops::sequence_sync_event(
442
442
&state,
443
443
&did,
444
444
root_cid_link.as_str(),
···
483
483
auth: Auth<Permissive>,
484
484
Json(input): Json<DeactivateAccountInput>,
485
485
) -> Result<Response, ApiError> {
486
-
if let Err(e) = crate::auth::scope_check::check_account_scope(
486
+
if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope(
487
487
&auth.auth_source,
488
488
auth.scope.as_deref(),
489
-
crate::oauth::scopes::AccountAttr::Repo,
490
-
crate::oauth::scopes::AccountAction::Manage,
489
+
tranquil_pds::oauth::scopes::AccountAttr::Repo,
490
+
tranquil_pds::oauth::scopes::AccountAction::Manage,
491
491
) {
492
492
return Ok(e);
493
493
}
···
507
507
match result {
508
508
Ok(true) => {
509
509
if let Some(ref h) = handle {
510
-
let _ = state.cache.delete(&crate::cache_keys::handle_key(h)).await;
510
+
let _ = state.cache.delete(&tranquil_pds::cache_keys::handle_key(h)).await;
511
511
}
512
-
if let Err(e) = crate::api::repo::record::sequence_account_event(
512
+
if let Err(e) = tranquil_pds::repo_ops::sequence_account_event(
513
513
&state,
514
514
&did,
515
515
tranquil_db_traits::AccountStatus::Deactivated,
···
552
552
.await
553
553
.log_db_err("creating deletion token")?;
554
554
let hostname = &tranquil_config::get().server.hostname;
555
-
if let Err(e) = crate::comms::comms_repo::enqueue_account_deletion(
555
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_account_deletion(
556
556
state.user_repo.as_ref(),
557
557
state.infra_repo.as_ref(),
558
558
user_id,
···
569
569
570
570
#[derive(Deserialize)]
571
571
pub struct DeleteAccountInput {
572
-
pub did: crate::types::Did,
572
+
pub did: tranquil_pds::types::Did,
573
573
pub password: PlainPassword,
574
574
pub token: String,
575
575
}
···
642
642
error!("DB error deleting account: {:?}", e);
643
643
return ApiError::InternalError(None).into_response();
644
644
}
645
-
let account_seq = crate::api::repo::record::sequence_account_event(
645
+
let account_seq = tranquil_pds::repo_ops::sequence_account_event(
646
646
&state,
647
647
did,
648
648
tranquil_db_traits::AccountStatus::Deleted,
···
666
666
}
667
667
let _ = state
668
668
.cache
669
-
.delete(&crate::cache_keys::handle_key(&handle))
669
+
.delete(&tranquil_pds::cache_keys::handle_key(&handle))
670
670
.await;
671
671
info!("Account {} deleted successfully", did);
672
672
EmptyResponse::ok().into_response()
+7
-7
crates/tranquil-pds/src/api/server/app_password.rs
crates/tranquil-api/src/server/app_password.rs
+7
-7
crates/tranquil-pds/src/api/server/app_password.rs
crates/tranquil-api/src/server/app_password.rs
···
1
-
use crate::api::EmptyResponse;
2
-
use crate::api::error::{ApiError, DbResultExt};
3
-
use crate::auth::{Auth, NotTakendown, Permissive, generate_app_password};
4
-
use crate::delegation::{DelegationActionType, intersect_scopes};
5
-
use crate::rate_limit::{AppPasswordLimit, RateLimited};
6
-
use crate::state::AppState;
1
+
use tranquil_pds::api::EmptyResponse;
2
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
3
+
use tranquil_pds::auth::{Auth, NotTakendown, Permissive, generate_app_password};
4
+
use tranquil_pds::delegation::{DelegationActionType, intersect_scopes};
5
+
use tranquil_pds::rate_limit::{AppPasswordLimit, RateLimited};
6
+
use tranquil_pds::state::AppState;
7
7
use axum::{
8
8
Json,
9
9
extract::State,
···
233
233
.log_db_err("revoking sessions for app password")?;
234
234
235
235
futures::future::join_all(sessions_to_invalidate.iter().map(|jti| {
236
-
let cache_key = crate::cache_keys::session_key(&auth.did, jti);
236
+
let cache_key = tranquil_pds::cache_keys::session_key(&auth.did, jti);
237
237
let cache = state.cache.clone();
238
238
async move {
239
239
let _ = cache.delete(&cache_key).await;
+43
-43
crates/tranquil-pds/src/api/server/email.rs
crates/tranquil-api/src/server/email.rs
+43
-43
crates/tranquil-pds/src/api/server/email.rs
crates/tranquil-api/src/server/email.rs
···
1
-
use crate::api::error::{ApiError, DbResultExt};
2
-
use crate::api::{EmptyResponse, TokenRequiredResponse, VerifiedResponse};
3
-
use crate::auth::{Auth, NotTakendown};
4
-
use crate::rate_limit::{EmailUpdateLimit, RateLimited, VerificationCheckLimit};
5
-
use crate::state::AppState;
1
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
2
+
use tranquil_pds::api::{EmptyResponse, TokenRequiredResponse, VerifiedResponse};
3
+
use tranquil_pds::auth::{Auth, NotTakendown};
4
+
use tranquil_pds::rate_limit::{EmailUpdateLimit, RateLimited, VerificationCheckLimit};
5
+
use tranquil_pds::state::AppState;
6
6
use axum::{
7
7
Json,
8
8
extract::State,
···
20
20
const EMAIL_UPDATE_TTL: Duration = Duration::from_secs(30 * 60);
21
21
22
22
fn email_update_cache_key(did: &str) -> String {
23
-
crate::cache_keys::email_update_key(did)
23
+
tranquil_pds::cache_keys::email_update_key(did)
24
24
}
25
25
26
26
fn hash_token(token: &str) -> String {
···
49
49
auth: Auth<NotTakendown>,
50
50
input: Option<Json<RequestEmailUpdateInput>>,
51
51
) -> Result<Response, ApiError> {
52
-
if let Err(e) = crate::auth::scope_check::check_account_scope(
52
+
if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope(
53
53
&auth.auth_source,
54
54
auth.scope.as_deref(),
55
-
crate::oauth::scopes::AccountAttr::Email,
56
-
crate::oauth::scopes::AccountAction::Manage,
55
+
tranquil_pds::oauth::scopes::AccountAttr::Email,
56
+
tranquil_pds::oauth::scopes::AccountAction::Manage,
57
57
) {
58
58
return Ok(e);
59
59
}
···
74
74
let token_required = user.email_verified;
75
75
76
76
if token_required {
77
-
let token = crate::auth::email_token::create_email_token(
77
+
let token = tranquil_pds::auth::email_token::create_email_token(
78
78
state.cache.as_ref(),
79
79
auth.did.as_str(),
80
-
crate::auth::email_token::EmailTokenPurpose::UpdateEmail,
80
+
tranquil_pds::auth::email_token::EmailTokenPurpose::UpdateEmail,
81
81
)
82
82
.await
83
83
.map_err(|e| {
···
89
89
&& let Some(ref new_email) = inp.new_email
90
90
{
91
91
let new_email = new_email.trim().to_lowercase();
92
-
if !new_email.is_empty() && crate::api::validation::is_valid_email(&new_email) {
92
+
if !new_email.is_empty() && tranquil_pds::api::validation::is_valid_email(&new_email) {
93
93
let pending = PendingEmailUpdate {
94
94
new_email,
95
95
token_hash: hash_token(&token),
···
105
105
}
106
106
107
107
let hostname = &tranquil_config::get().server.hostname;
108
-
if let Err(e) = crate::comms::comms_repo::enqueue_short_token_email(
108
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_short_token_email(
109
109
state.user_repo.as_ref(),
110
110
state.infra_repo.as_ref(),
111
111
user.id,
···
135
135
auth: Auth<NotTakendown>,
136
136
Json(input): Json<ConfirmEmailInput>,
137
137
) -> Result<Response, ApiError> {
138
-
if let Err(e) = crate::auth::scope_check::check_account_scope(
138
+
if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope(
139
139
&auth.auth_source,
140
140
auth.scope.as_deref(),
141
-
crate::oauth::scopes::AccountAttr::Email,
142
-
crate::oauth::scopes::AccountAction::Manage,
141
+
tranquil_pds::oauth::scopes::AccountAttr::Email,
142
+
tranquil_pds::oauth::scopes::AccountAction::Manage,
143
143
) {
144
144
return Ok(e);
145
145
}
···
167
167
}
168
168
169
169
let confirmation_code =
170
-
crate::auth::verification_token::normalize_token_input(input.token.trim());
170
+
tranquil_pds::auth::verification_token::normalize_token_input(input.token.trim());
171
171
172
-
let verified = crate::auth::verification_token::verify_signup_token(
172
+
let verified = tranquil_pds::auth::verification_token::verify_signup_token(
173
173
&confirmation_code,
174
174
CommsChannel::Email,
175
175
&provided_email,
···
181
181
return Err(ApiError::InvalidToken(None));
182
182
}
183
183
}
184
-
Err(crate::auth::verification_token::VerifyError::Expired) => {
184
+
Err(tranquil_pds::auth::verification_token::VerifyError::Expired) => {
185
185
return Err(ApiError::ExpiredToken(None));
186
186
}
187
187
Err(_) => {
···
213
213
auth: Auth<NotTakendown>,
214
214
Json(input): Json<UpdateEmailInput>,
215
215
) -> Result<Response, ApiError> {
216
-
if let Err(e) = crate::auth::scope_check::check_account_scope(
216
+
if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope(
217
217
&auth.auth_source,
218
218
auth.scope.as_deref(),
219
-
crate::oauth::scopes::AccountAttr::Email,
220
-
crate::oauth::scopes::AccountAction::Manage,
219
+
tranquil_pds::oauth::scopes::AccountAttr::Email,
220
+
tranquil_pds::oauth::scopes::AccountAction::Manage,
221
221
) {
222
222
return Ok(e);
223
223
}
···
235
235
let email_verified = user.email_verified;
236
236
let new_email = input.email.trim().to_lowercase();
237
237
238
-
if !crate::api::validation::is_valid_email(&new_email) {
238
+
if !tranquil_pds::api::validation::is_valid_email(&new_email) {
239
239
return Err(ApiError::InvalidRequest(
240
240
"This email address is not supported, please use a different email.".into(),
241
241
));
···
255
255
.filter(|t| !t.is_empty())
256
256
.ok_or(ApiError::TokenRequired)?;
257
257
258
-
crate::auth::email_token::validate_email_token(
258
+
tranquil_pds::auth::email_token::validate_email_token(
259
259
state.cache.as_ref(),
260
260
did.as_str(),
261
-
crate::auth::email_token::EmailTokenPurpose::UpdateEmail,
261
+
tranquil_pds::auth::email_token::EmailTokenPurpose::UpdateEmail,
262
262
token,
263
263
)
264
264
.await
265
265
.map_err(|e| match e {
266
-
crate::auth::email_token::TokenError::ExpiredToken => {
266
+
tranquil_pds::auth::email_token::TokenError::ExpiredToken => {
267
267
ApiError::ExpiredToken(None)
268
268
}
269
269
_ => ApiError::InvalidToken(None),
···
303
303
.filter(|t| !t.is_empty())
304
304
.ok_or(ApiError::TokenRequired)?;
305
305
306
-
let short_token_result = crate::auth::email_token::validate_email_token(
306
+
let short_token_result = tranquil_pds::auth::email_token::validate_email_token(
307
307
state.cache.as_ref(),
308
308
did.as_str(),
309
-
crate::auth::email_token::EmailTokenPurpose::UpdateEmail,
309
+
tranquil_pds::auth::email_token::EmailTokenPurpose::UpdateEmail,
310
310
token,
311
311
)
312
312
.await;
313
313
314
314
if let Err(e) = short_token_result {
315
315
let confirmation_token =
316
-
crate::auth::verification_token::normalize_token_input(token.trim());
316
+
tranquil_pds::auth::verification_token::normalize_token_input(token.trim());
317
317
318
318
let current_email_lower = current_email
319
319
.as_ref()
320
320
.map(|e| e.to_lowercase())
321
321
.unwrap_or_default();
322
322
323
-
let verified = crate::auth::verification_token::verify_channel_update_token(
323
+
let verified = tranquil_pds::auth::verification_token::verify_channel_update_token(
324
324
&confirmation_token,
325
325
CommsChannel::Email,
326
326
¤t_email_lower,
···
332
332
return Err(ApiError::InvalidToken(None));
333
333
}
334
334
}
335
-
Err(crate::auth::verification_token::VerifyError::Expired) => {
335
+
Err(tranquil_pds::auth::verification_token::VerifyError::Expired) => {
336
336
return Err(match e {
337
-
crate::auth::email_token::TokenError::ExpiredToken => {
337
+
tranquil_pds::auth::email_token::TokenError::ExpiredToken => {
338
338
ApiError::ExpiredToken(None)
339
339
}
340
340
_ => ApiError::InvalidToken(None),
···
342
342
}
343
343
Err(_) => {
344
344
return Err(match e {
345
-
crate::auth::email_token::TokenError::ExpiredToken => {
345
+
tranquil_pds::auth::email_token::TokenError::ExpiredToken => {
346
346
ApiError::ExpiredToken(None)
347
347
}
348
348
_ => ApiError::InvalidToken(None),
···
359
359
.await
360
360
.log_db_err("updating email")?;
361
361
362
-
let verification_token = crate::auth::verification_token::generate_signup_token(
362
+
let verification_token = tranquil_pds::auth::verification_token::generate_signup_token(
363
363
did,
364
364
CommsChannel::Email,
365
365
&new_email,
366
366
);
367
367
let formatted_token =
368
-
crate::auth::verification_token::format_token_for_display(&verification_token);
368
+
tranquil_pds::auth::verification_token::format_token_for_display(&verification_token);
369
369
let hostname = &tranquil_config::get().server.hostname;
370
-
if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification(
370
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification(
371
371
state.user_repo.as_ref(),
372
372
state.infra_repo.as_ref(),
373
373
user_id,
···
423
423
424
424
#[derive(Deserialize)]
425
425
pub struct CheckChannelVerifiedInput {
426
-
pub did: crate::types::Did,
426
+
pub did: tranquil_pds::types::Did,
427
427
pub channel: CommsChannel,
428
428
}
429
429
···
456
456
_rate_limit: RateLimited<VerificationCheckLimit>,
457
457
axum::extract::Query(query): axum::extract::Query<AuthorizeEmailUpdateQuery>,
458
458
) -> Response {
459
-
let verified = crate::auth::verification_token::verify_token_signature(&query.token);
459
+
let verified = tranquil_pds::auth::verification_token::verify_token_signature(&query.token);
460
460
461
461
let token_data = match verified {
462
462
Ok(data) => data,
463
-
Err(crate::auth::verification_token::VerifyError::Expired) => {
463
+
Err(tranquil_pds::auth::verification_token::VerifyError::Expired) => {
464
464
warn!("authorize_email_update: token expired");
465
465
return ApiError::ExpiredToken(None).into_response();
466
466
}
···
470
470
}
471
471
};
472
472
473
-
if token_data.purpose != crate::auth::verification_token::VerificationPurpose::ChannelUpdate {
473
+
if token_data.purpose != tranquil_pds::auth::verification_token::VerificationPurpose::ChannelUpdate {
474
474
warn!(
475
475
"authorize_email_update: wrong purpose: {:?}",
476
476
token_data.purpose
···
544
544
_rate_limit: RateLimited<VerificationCheckLimit>,
545
545
auth: Auth<NotTakendown>,
546
546
) -> Result<Response, ApiError> {
547
-
if let Err(e) = crate::auth::scope_check::check_account_scope(
547
+
if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope(
548
548
&auth.auth_source,
549
549
auth.scope.as_deref(),
550
-
crate::oauth::scopes::AccountAttr::Email,
551
-
crate::oauth::scopes::AccountAction::Read,
550
+
tranquil_pds::oauth::scopes::AccountAttr::Email,
551
+
tranquil_pds::oauth::scopes::AccountAction::Read,
552
552
) {
553
553
return Ok(e);
554
554
}
+5
-5
crates/tranquil-pds/src/api/server/invite.rs
crates/tranquil-api/src/server/invite.rs
+5
-5
crates/tranquil-pds/src/api/server/invite.rs
crates/tranquil-api/src/server/invite.rs
···
1
-
use crate::api::ApiError;
2
-
use crate::api::error::DbResultExt;
3
-
use crate::auth::{Admin, Auth, NotTakendown};
4
-
use crate::state::AppState;
5
-
use crate::types::Did;
1
+
use tranquil_pds::api::ApiError;
2
+
use tranquil_pds::api::error::DbResultExt;
3
+
use tranquil_pds::auth::{Admin, Auth, NotTakendown};
4
+
use tranquil_pds::state::AppState;
5
+
use tranquil_pds::types::Did;
6
6
use axum::{
7
7
Json,
8
8
extract::State,
+2
-2
crates/tranquil-pds/src/api/server/logo.rs
crates/tranquil-api/src/server/logo.rs
+2
-2
crates/tranquil-pds/src/api/server/logo.rs
crates/tranquil-api/src/server/logo.rs
···
1
-
use crate::state::AppState;
1
+
use tranquil_pds::state::AppState;
2
2
use axum::{
3
3
body::Body,
4
4
extract::State,
···
21
21
Some(c) if !c.is_empty() => c,
22
22
_ => return StatusCode::NOT_FOUND.into_response(),
23
23
};
24
-
let cid = match crate::types::CidLink::new(&cid_str) {
24
+
let cid = match tranquil_pds::types::CidLink::new(&cid_str) {
25
25
Ok(c) => c,
26
26
Err(_) => return StatusCode::NOT_FOUND.into_response(),
27
27
};
+3
-3
crates/tranquil-pds/src/api/server/meta.rs
crates/tranquil-api/src/server/meta.rs
+3
-3
crates/tranquil-pds/src/api/server/meta.rs
crates/tranquil-api/src/server/meta.rs
···
1
-
use crate::BUILD_VERSION;
2
-
use crate::state::AppState;
3
-
use crate::util::{discord_app_id, discord_bot_username, telegram_bot_username};
1
+
use tranquil_pds::BUILD_VERSION;
2
+
use tranquil_pds::state::AppState;
3
+
use tranquil_pds::util::{discord_app_id, discord_bot_username, telegram_bot_username};
4
4
use axum::{Json, extract::State, http::StatusCode, response::IntoResponse};
5
5
use serde_json::json;
6
6
+9
-9
crates/tranquil-pds/src/api/server/migration.rs
crates/tranquil-api/src/server/migration.rs
+9
-9
crates/tranquil-pds/src/api/server/migration.rs
crates/tranquil-api/src/server/migration.rs
···
1
-
use crate::api::ApiError;
2
-
use crate::api::error::DbResultExt;
3
-
use crate::auth::{Active, Auth};
4
-
use crate::state::AppState;
1
+
use tranquil_pds::api::ApiError;
2
+
use tranquil_pds::api::error::DbResultExt;
3
+
use tranquil_pds::auth::{Active, Auth};
4
+
use tranquil_pds::state::AppState;
5
5
use axum::{
6
6
Json,
7
7
extract::State,
···
145
145
Ok((StatusCode::OK, Json(json!({ "didDocument": did_doc }))).into_response())
146
146
}
147
147
148
-
async fn build_did_document(state: &AppState, did: &crate::types::Did) -> serde_json::Value {
148
+
async fn build_did_document(state: &AppState, did: &tranquil_pds::types::Did) -> serde_json::Value {
149
149
let hostname = &tranquil_config::get().server.hostname;
150
150
151
151
let user = match state.user_repo.get_user_for_did_doc_build(did).await {
···
195
195
})).collect::<Vec<_>>(),
196
196
"service": [{
197
197
"id": "#atproto_pds",
198
-
"type": crate::plc::ServiceType::Pds.as_str(),
198
+
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
199
199
"serviceEndpoint": service_endpoint
200
200
}]
201
201
});
···
209
209
.flatten();
210
210
211
211
let public_key_multibase = match key_info {
212
-
Some(info) => match crate::config::decrypt_key(&info.key_bytes, info.encryption_version) {
213
-
Ok(key_bytes) => crate::api::identity::did::get_public_key_multibase(&key_bytes)
212
+
Some(info) => match tranquil_pds::config::decrypt_key(&info.key_bytes, info.encryption_version) {
213
+
Ok(key_bytes) => crate::identity::did::get_public_key_multibase(&key_bytes)
214
214
.unwrap_or_else(|_| "error".to_string()),
215
215
Err(_) => "error".to_string(),
216
216
},
···
243
243
}],
244
244
"service": [{
245
245
"id": "#atproto_pds",
246
-
"type": crate::plc::ServiceType::Pds.as_str(),
246
+
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
247
247
"serviceEndpoint": service_endpoint
248
248
}]
249
249
})
crates/tranquil-pds/src/api/server/mod.rs
crates/tranquil-api/src/server/mod.rs
crates/tranquil-pds/src/api/server/mod.rs
crates/tranquil-api/src/server/mod.rs
+34
-34
crates/tranquil-pds/src/api/server/passkey_account.rs
crates/tranquil-api/src/server/passkey_account.rs
+34
-34
crates/tranquil-pds/src/api/server/passkey_account.rs
crates/tranquil-api/src/server/passkey_account.rs
···
1
-
use crate::api::SuccessResponse;
2
-
use crate::api::error::ApiError;
3
-
use crate::auth::NormalizedLoginIdentifier;
1
+
use tranquil_pds::api::SuccessResponse;
2
+
use tranquil_pds::api::error::ApiError;
3
+
use tranquil_pds::auth::NormalizedLoginIdentifier;
4
4
use axum::{
5
5
Json,
6
6
extract::State,
···
19
19
use tranquil_db_traits::WebauthnChallengeType;
20
20
use uuid::Uuid;
21
21
22
-
use crate::api::repo::record::utils::create_signed_commit;
23
-
use crate::auth::{ServiceTokenVerifier, generate_app_password, is_service_token};
24
-
use crate::rate_limit::{AccountCreationLimit, PasswordResetLimit, RateLimited};
25
-
use crate::state::AppState;
26
-
use crate::types::{Did, Handle, PlainPassword};
27
-
use crate::validation::validate_password;
22
+
use tranquil_pds::repo_ops::create_signed_commit;
23
+
use tranquil_pds::auth::{ServiceTokenVerifier, generate_app_password, is_service_token};
24
+
use tranquil_pds::rate_limit::{AccountCreationLimit, PasswordResetLimit, RateLimited};
25
+
use tranquil_pds::state::AppState;
26
+
use tranquil_pds::types::{Did, Handle, PlainPassword};
27
+
use tranquil_pds::validation::validate_password;
28
28
29
29
fn generate_setup_token() -> String {
30
30
let mut rng = rand::thread_rng();
···
72
72
headers: HeaderMap,
73
73
Json(input): Json<CreatePasskeyAccountInput>,
74
74
) -> Response {
75
-
let byod_auth = if let Some(extracted) = crate::auth::extract_auth_token_from_header(
76
-
crate::util::get_header_str(&headers, http::header::AUTHORIZATION),
75
+
let byod_auth = if let Some(extracted) = tranquil_pds::auth::extract_auth_token_from_header(
76
+
tranquil_pds::util::get_header_str(&headers, http::header::AUTHORIZATION),
77
77
) {
78
78
let token = extracted.token;
79
79
if is_service_token(&token) {
···
128
128
.unwrap_or(&input.handle),
129
129
None => &input.handle,
130
130
};
131
-
match crate::api::validation::validate_short_handle(handle_to_validate) {
131
+
match tranquil_pds::api::validation::validate_short_handle(handle_to_validate) {
132
132
Ok(h) => format!("{}.{}", h, matched_domain.unwrap_or(&available_domains[0])),
133
133
Err(_) => {
134
134
return ApiError::InvalidHandle(None).into_response();
135
135
}
136
136
}
137
137
} else {
138
-
match crate::api::validation::validate_full_domain_handle(&input.handle) {
138
+
match tranquil_pds::api::validation::validate_full_domain_handle(&input.handle) {
139
139
Ok(h) => h,
140
140
Err(_) => return ApiError::InvalidHandle(None).into_response(),
141
141
}
···
147
147
.map(|e| e.trim().to_string())
148
148
.filter(|e| !e.is_empty());
149
149
if let Some(ref email) = email
150
-
&& !crate::api::validation::is_valid_email(email)
150
+
&& !tranquil_pds::api::validation::is_valid_email(email)
151
151
{
152
152
return ApiError::InvalidEmail.into_response();
153
153
}
···
184
184
tranquil_db_traits::CommsChannel::Discord => match &input.discord_username {
185
185
Some(username) if !username.trim().is_empty() => {
186
186
let clean = username.trim().to_lowercase();
187
-
if !crate::api::validation::is_valid_discord_username(&clean) {
187
+
if !tranquil_pds::api::validation::is_valid_discord_username(&clean) {
188
188
return ApiError::InvalidRequest(
189
189
"Invalid Discord username. Must be 2-32 lowercase characters (letters, numbers, underscores, periods)".into(),
190
190
).into_response();
···
196
196
tranquil_db_traits::CommsChannel::Telegram => match &input.telegram_username {
197
197
Some(username) if !username.trim().is_empty() => {
198
198
let clean = username.trim().trim_start_matches('@');
199
-
if !crate::api::validation::is_valid_telegram_username(clean) {
199
+
if !tranquil_pds::api::validation::is_valid_telegram_username(clean) {
200
200
return ApiError::InvalidRequest(
201
201
"Invalid Telegram username. Must be 5-32 characters, alphanumeric or underscore".into(),
202
202
).into_response();
···
250
250
251
251
let did = match did_type {
252
252
"web" => {
253
-
if !crate::api::server::meta::is_self_hosted_did_web_enabled() {
253
+
if !tranquil_pds::util::is_self_hosted_did_web_enabled() {
254
254
return ApiError::SelfHostedDidWebDisabled.into_response();
255
255
}
256
256
let encoded_handle = handle.replace(':', "%3A");
···
284
284
}
285
285
info!(did = %d, "Creating external did:web passkey account (BYOD key)");
286
286
} else {
287
-
if let Err(e) = crate::api::identity::did::verify_did_web(
287
+
if let Err(e) = crate::identity::did::verify_did_web(
288
288
d,
289
289
hostname,
290
290
&input.handle,
···
328
328
.secrets
329
329
.plc_rotation_key
330
330
.clone()
331
-
.unwrap_or_else(|| crate::plc::signing_key_to_did_key(&secret_key));
331
+
.unwrap_or_else(|| tranquil_pds::plc::signing_key_to_did_key(&secret_key));
332
332
333
-
let genesis_result = match crate::plc::create_genesis_operation(
333
+
let genesis_result = match tranquil_pds::plc::create_genesis_operation(
334
334
&secret_key,
335
335
&rotation_key,
336
336
&handle,
···
346
346
}
347
347
};
348
348
349
-
let plc_client = crate::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
349
+
let plc_client = tranquil_pds::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
350
350
if let Err(e) = plc_client
351
351
.send_operation(&genesis_result.did, &genesis_result.signed_operation)
352
352
.await
···
381
381
None
382
382
};
383
383
384
-
let encrypted_key_bytes = match crate::config::encrypt_key(&secret_key_bytes) {
384
+
let encrypted_key_bytes = match tranquil_pds::config::encrypt_key(&secret_key_bytes) {
385
385
Ok(bytes) => bytes,
386
386
Err(e) => {
387
387
error!("Error encrypting signing key: {:?}", e);
···
458
458
setup_expires_at,
459
459
deactivated_at,
460
460
encrypted_key_bytes,
461
-
encryption_version: crate::config::ENCRYPTION_VERSION,
461
+
encryption_version: tranquil_pds::config::ENCRYPTION_VERSION,
462
462
reserved_key_id,
463
463
commit_cid: commit_cid.to_string(),
464
464
repo_rev: rev.as_ref().to_string(),
···
487
487
let user_id = create_result.user_id;
488
488
489
489
if !is_byod_did_web {
490
-
if let Err(e) = crate::api::repo::record::sequence_identity_event(
490
+
if let Err(e) = tranquil_pds::repo_ops::sequence_identity_event(
491
491
&state,
492
492
&did_typed,
493
493
Some(&handle_typed),
···
496
496
{
497
497
warn!("Failed to sequence identity event for {}: {}", did, e);
498
498
}
499
-
if let Err(e) = crate::api::repo::record::sequence_account_event(
499
+
if let Err(e) = tranquil_pds::repo_ops::sequence_account_event(
500
500
&state,
501
501
&did_typed,
502
502
tranquil_db_traits::AccountStatus::Active,
···
509
509
"$type": "app.bsky.actor.profile",
510
510
"displayName": handle
511
511
});
512
-
if let Err(e) = crate::api::repo::record::create_record_internal(
512
+
if let Err(e) = tranquil_pds::repo_ops::create_record_internal(
513
513
&state,
514
514
&did_typed,
515
-
&crate::types::PROFILE_COLLECTION,
516
-
&crate::types::PROFILE_RKEY,
515
+
&tranquil_pds::types::PROFILE_COLLECTION,
516
+
&tranquil_pds::types::PROFILE_RKEY,
517
517
&profile_record,
518
518
)
519
519
.await
···
522
522
}
523
523
}
524
524
525
-
let verification_token = crate::auth::verification_token::generate_signup_token(
525
+
let verification_token = tranquil_pds::auth::verification_token::generate_signup_token(
526
526
&did_typed,
527
527
verification_channel,
528
528
&verification_recipient,
529
529
);
530
530
let formatted_token =
531
-
crate::auth::verification_token::format_token_for_display(&verification_token);
532
-
if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification(
531
+
tranquil_pds::auth::verification_token::format_token_for_display(&verification_token);
532
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification(
533
533
state.user_repo.as_ref(),
534
534
state.infra_repo.as_ref(),
535
535
user_id,
···
546
546
info!(did = %did, handle = %handle, "Passkey-only account created, awaiting setup completion");
547
547
548
548
let access_jwt = if byod_auth.is_some() {
549
-
match crate::auth::create_access_token_with_metadata(&did, &secret_key_bytes) {
549
+
match tranquil_pds::auth::create_access_token_with_metadata(&did, &secret_key_bytes) {
550
550
Ok(token_meta) => {
551
551
let refresh_jti = uuid::Uuid::new_v4().to_string();
552
552
let refresh_expires = chrono::Utc::now() + chrono::Duration::hours(24);
···
887
887
urlencoding::encode(&recovery_token)
888
888
);
889
889
890
-
let _ = crate::comms::comms_repo::enqueue_passkey_recovery(
890
+
let _ = tranquil_pds::comms::comms_repo::enqueue_passkey_recovery(
891
891
state.user_repo.as_ref(),
892
892
state.infra_repo.as_ref(),
893
893
user.id,
···
968
968
}
969
969
if let Ok(Some(prefs)) = state.user_repo.get_comms_prefs(user.id).await {
970
970
let actual_channel =
971
-
crate::comms::resolve_delivery_channel(&prefs, user.preferred_comms_channel);
971
+
tranquil_pds::comms::resolve_delivery_channel(&prefs, user.preferred_comms_channel);
972
972
if let Err(e) = state
973
973
.user_repo
974
974
.set_channel_verified(&input.did, actual_channel)
+5
-5
crates/tranquil-pds/src/api/server/passkeys.rs
crates/tranquil-api/src/server/passkeys.rs
+5
-5
crates/tranquil-pds/src/api/server/passkeys.rs
crates/tranquil-api/src/server/passkeys.rs
···
1
-
use crate::api::EmptyResponse;
2
-
use crate::api::error::{ApiError, DbResultExt};
3
-
use crate::auth::{Active, Auth, require_legacy_session_mfa, require_reauth_window};
4
-
use crate::state::AppState;
1
+
use tranquil_pds::api::EmptyResponse;
2
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
3
+
use tranquil_pds::auth::{Active, Auth, require_legacy_session_mfa, require_reauth_window};
4
+
use tranquil_pds::state::AppState;
5
5
use axum::{
6
6
Json,
7
7
extract::State,
···
271
271
}
272
272
}
273
273
274
-
pub async fn has_passkeys_for_user(state: &AppState, did: &crate::types::Did) -> bool {
274
+
pub async fn has_passkeys_for_user(state: &AppState, did: &tranquil_pds::types::Did) -> bool {
275
275
state.user_repo.has_passkeys(did).await.unwrap_or(false)
276
276
}
+12
-12
crates/tranquil-pds/src/api/server/password.rs
crates/tranquil-api/src/server/password.rs
+12
-12
crates/tranquil-pds/src/api/server/password.rs
crates/tranquil-api/src/server/password.rs
···
1
-
use crate::api::error::{ApiError, DbResultExt};
2
-
use crate::api::{EmptyResponse, HasPasswordResponse, SuccessResponse};
3
-
use crate::auth::{
1
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
2
+
use tranquil_pds::api::{EmptyResponse, HasPasswordResponse, SuccessResponse};
3
+
use tranquil_pds::auth::{
4
4
Active, Auth, NormalizedLoginIdentifier, require_legacy_session_mfa, require_reauth_window,
5
5
require_reauth_window_if_available,
6
6
};
7
-
use crate::rate_limit::{PasswordResetLimit, RateLimited, ResetPasswordLimit};
8
-
use crate::state::AppState;
9
-
use crate::types::PlainPassword;
10
-
use crate::validation::validate_password;
7
+
use tranquil_pds::rate_limit::{PasswordResetLimit, RateLimited, ResetPasswordLimit};
8
+
use tranquil_pds::state::AppState;
9
+
use tranquil_pds::types::PlainPassword;
10
+
use tranquil_pds::validation::validate_password;
11
11
use axum::{
12
12
Json,
13
13
extract::State,
···
19
19
use tracing::{error, info, warn};
20
20
21
21
fn generate_reset_code() -> String {
22
-
crate::util::generate_token_code()
22
+
tranquil_pds::util::generate_token_code()
23
23
}
24
24
25
25
#[derive(Deserialize)]
···
78
78
return ApiError::InternalError(None).into_response();
79
79
}
80
80
let hostname = &tranquil_config::get().server.hostname;
81
-
if let Err(e) = crate::comms::comms_repo::enqueue_password_reset(
81
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_password_reset(
82
82
state.user_repo.as_ref(),
83
83
state.infra_repo.as_ref(),
84
84
user_id,
···
170
170
}
171
171
};
172
172
futures::future::join_all(result.session_jtis.iter().map(|jti| {
173
-
let cache_key = crate::cache_keys::session_key(&result.did, jti);
173
+
let cache_key = tranquil_pds::cache_keys::session_key(&result.did, jti);
174
174
let cache = state.cache.clone();
175
175
async move {
176
176
if let Err(e) = cache.delete(&cache_key).await {
···
184
184
.await;
185
185
if let Ok(Some(prefs)) = state.user_repo.get_comms_prefs(user_id).await {
186
186
let actual_channel =
187
-
crate::comms::resolve_delivery_channel(&prefs, user.preferred_comms_channel);
187
+
tranquil_pds::comms::resolve_delivery_channel(&prefs, user.preferred_comms_channel);
188
188
if let Err(e) = state
189
189
.user_repo
190
190
.set_channel_verified(&user.did, actual_channel)
···
212
212
auth: Auth<Active>,
213
213
Json(input): Json<ChangePasswordInput>,
214
214
) -> Result<Response, ApiError> {
215
-
use crate::auth::verify_password_mfa;
215
+
use tranquil_pds::auth::verify_password_mfa;
216
216
217
217
let session_mfa = match require_legacy_session_mfa(&state, &auth).await {
218
218
Ok(proof) => proof,
+18
-18
crates/tranquil-pds/src/api/server/reauth.rs
crates/tranquil-api/src/server/reauth.rs
+18
-18
crates/tranquil-pds/src/api/server/reauth.rs
crates/tranquil-api/src/server/reauth.rs
···
1
-
use crate::api::error::{ApiError, DbResultExt};
1
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
2
2
use axum::{
3
3
Json,
4
4
extract::State,
···
10
10
use tracing::{error, info, warn};
11
11
use tranquil_db_traits::{SessionRepository, UserRepository, WebauthnChallengeType};
12
12
13
-
use crate::auth::{Active, Auth};
14
-
use crate::rate_limit::{TotpVerifyLimit, check_user_rate_limit_with_message};
15
-
use crate::state::AppState;
16
-
use crate::types::PlainPassword;
13
+
use tranquil_pds::auth::{Active, Auth};
14
+
use tranquil_pds::rate_limit::{TotpVerifyLimit, check_user_rate_limit_with_message};
15
+
use tranquil_pds::state::AppState;
16
+
use tranquil_pds::types::PlainPassword;
17
17
18
18
pub const REAUTH_WINDOW_SECONDS: i64 = 300;
19
19
···
125
125
.await?;
126
126
127
127
let valid =
128
-
crate::api::server::totp::verify_totp_or_backup_for_user(&state, &auth.did, &input.code)
128
+
crate::server::totp::verify_totp_or_backup_for_user(&state, &auth.did, &input.code)
129
129
.await;
130
130
131
131
if !valid {
···
276
276
277
277
pub async fn update_last_reauth_cached(
278
278
session_repo: &dyn SessionRepository,
279
-
cache: &std::sync::Arc<dyn crate::cache::Cache>,
280
-
did: &crate::types::Did,
279
+
cache: &std::sync::Arc<dyn tranquil_pds::cache::Cache>,
280
+
did: &tranquil_pds::types::Did,
281
281
) -> Result<DateTime<Utc>, tranquil_db_traits::DbError> {
282
282
let now = session_repo.update_last_reauth(did).await?;
283
-
let cache_key = crate::cache_keys::reauth_key(did);
283
+
let cache_key = tranquil_pds::cache_keys::reauth_key(did);
284
284
let _ = cache
285
285
.set(
286
286
&cache_key,
···
304
304
async fn get_available_reauth_methods(
305
305
user_repo: &dyn UserRepository,
306
306
_session_repo: &dyn SessionRepository,
307
-
did: &crate::types::Did,
307
+
did: &tranquil_pds::types::Did,
308
308
) -> Vec<ReauthMethod> {
309
309
let mut methods = Vec::new();
310
310
···
334
334
335
335
pub async fn check_reauth_required(
336
336
session_repo: &dyn SessionRepository,
337
-
did: &crate::types::Did,
337
+
did: &tranquil_pds::types::Did,
338
338
) -> bool {
339
339
match session_repo.get_last_reauth_at(did).await {
340
340
Ok(last_reauth_at) => is_reauth_required(last_reauth_at),
···
344
344
345
345
pub async fn check_reauth_required_cached(
346
346
session_repo: &dyn SessionRepository,
347
-
cache: &std::sync::Arc<dyn crate::cache::Cache>,
348
-
did: &crate::types::Did,
347
+
cache: &std::sync::Arc<dyn tranquil_pds::cache::Cache>,
348
+
did: &tranquil_pds::types::Did,
349
349
) -> bool {
350
-
let cache_key = crate::cache_keys::reauth_key(did);
350
+
let cache_key = tranquil_pds::cache_keys::reauth_key(did);
351
351
if let Some(timestamp_str) = cache.get(&cache_key).await
352
352
&& let Ok(timestamp) = timestamp_str.parse::<i64>()
353
353
{
···
376
376
pub async fn reauth_required_response(
377
377
user_repo: &dyn UserRepository,
378
378
session_repo: &dyn SessionRepository,
379
-
did: &crate::types::Did,
379
+
did: &tranquil_pds::types::Did,
380
380
) -> Response {
381
381
let methods = get_available_reauth_methods(user_repo, session_repo, did).await;
382
382
(
···
392
392
393
393
pub async fn check_legacy_session_mfa(
394
394
session_repo: &dyn SessionRepository,
395
-
did: &crate::types::Did,
395
+
did: &tranquil_pds::types::Did,
396
396
) -> bool {
397
397
match session_repo.get_session_mfa_status(did).await {
398
398
Ok(Some(status)) => {
···
416
416
417
417
pub async fn update_mfa_verified(
418
418
session_repo: &dyn SessionRepository,
419
-
did: &crate::types::Did,
419
+
did: &tranquil_pds::types::Did,
420
420
) -> Result<(), tranquil_db_traits::DbError> {
421
421
session_repo.update_mfa_verified(did).await
422
422
}
···
424
424
pub async fn legacy_mfa_required_response(
425
425
user_repo: &dyn UserRepository,
426
426
session_repo: &dyn SessionRepository,
427
-
did: &crate::types::Did,
427
+
did: &tranquil_pds::types::Did,
428
428
) -> Response {
429
429
let methods = get_available_reauth_methods(user_repo, session_repo, did).await;
430
430
(
+7
-7
crates/tranquil-pds/src/api/server/service_auth.rs
crates/tranquil-api/src/server/service_auth.rs
+7
-7
crates/tranquil-pds/src/api/server/service_auth.rs
crates/tranquil-api/src/server/service_auth.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::auth::extractor::{Auth, Permissive};
3
-
use crate::state::AppState;
4
-
use crate::types::Did;
1
+
use tranquil_pds::api::error::ApiError;
2
+
use tranquil_pds::auth::extractor::{Auth, Permissive};
3
+
use tranquil_pds::state::AppState;
4
+
use tranquil_pds::types::Did;
5
5
use axum::{
6
6
Json,
7
7
extract::{Query, State},
···
75
75
match state.user_repo.get_user_info_by_did(&auth.did).await {
76
76
Ok(Some(info)) => match info.key_bytes {
77
77
Some(key_bytes_enc) => {
78
-
match crate::config::decrypt_key(&key_bytes_enc, info.encryption_version) {
78
+
match tranquil_pds::config::decrypt_key(&key_bytes_enc, info.encryption_version) {
79
79
Ok(key) => key,
80
80
Err(e) => {
81
81
error!(error = ?e, "Failed to decrypt user key for service auth");
···
112
112
let lxm_for_token = lxm.map_or("*", |n| n.as_str());
113
113
114
114
if let Some(method) = lxm {
115
-
if let Err(e) = crate::auth::scope_check::check_rpc_scope(
115
+
if let Err(e) = tranquil_pds::auth::scope_check::check_rpc_scope(
116
116
&auth.auth_source,
117
117
auth.scope.as_deref(),
118
118
params.aud.as_str(),
···
167
167
}
168
168
}
169
169
170
-
let service_token = match crate::auth::create_service_token(
170
+
let service_token = match tranquil_pds::auth::create_service_token(
171
171
&auth.did,
172
172
params.aud.as_str(),
173
173
lxm_for_token,
+51
-51
crates/tranquil-pds/src/api/server/session.rs
crates/tranquil-api/src/server/session.rs
+51
-51
crates/tranquil-pds/src/api/server/session.rs
crates/tranquil-api/src/server/session.rs
···
1
-
use crate::api::error::{ApiError, DbResultExt};
2
-
use crate::api::{EmptyResponse, SuccessResponse};
3
-
use crate::auth::{
1
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
2
+
use tranquil_pds::api::{EmptyResponse, SuccessResponse};
3
+
use tranquil_pds::auth::{
4
4
Active, Auth, NormalizedLoginIdentifier, Permissive, require_legacy_session_mfa,
5
5
require_reauth_window,
6
6
};
7
-
use crate::rate_limit::{LoginLimit, RateLimited, RefreshSessionLimit};
8
-
use crate::state::AppState;
9
-
use crate::types::{AccountState, Did, Handle, PlainPassword};
7
+
use tranquil_pds::rate_limit::{LoginLimit, RateLimited, RefreshSessionLimit};
8
+
use tranquil_pds::state::AppState;
9
+
use tranquil_pds::types::{AccountState, Did, Handle, PlainPassword};
10
10
use axum::{
11
11
Json,
12
12
extract::State,
···
93
93
return ApiError::InternalError(None).into_response();
94
94
}
95
95
};
96
-
let key_bytes = match crate::config::decrypt_key(&row.key_bytes, row.encryption_version) {
96
+
let key_bytes = match tranquil_pds::config::decrypt_key(&row.key_bytes, row.encryption_version) {
97
97
Ok(k) => k,
98
98
Err(e) => {
99
99
error!("Failed to decrypt user key: {:?}", e);
···
173
173
let has_totp = row.totp_enabled;
174
174
let email_2fa_enabled = row.email_2fa_enabled;
175
175
let is_legacy_login = has_totp || email_2fa_enabled;
176
-
let twofa_ctx = crate::auth::legacy_2fa::Legacy2faContext {
176
+
let twofa_ctx = tranquil_pds::auth::legacy_2fa::Legacy2faContext {
177
177
email_2fa_enabled,
178
178
has_totp,
179
179
allow_legacy_login: row.allow_legacy_login,
180
180
};
181
-
match crate::auth::legacy_2fa::process_legacy_2fa(
181
+
match tranquil_pds::auth::legacy_2fa::process_legacy_2fa(
182
182
state.cache.as_ref(),
183
183
&row.did,
184
184
&twofa_ctx,
···
186
186
)
187
187
.await
188
188
{
189
-
Ok(crate::auth::legacy_2fa::Legacy2faOutcome::NotRequired) => {}
190
-
Ok(crate::auth::legacy_2fa::Legacy2faOutcome::Blocked) => {
189
+
Ok(tranquil_pds::auth::legacy_2fa::Legacy2faOutcome::NotRequired) => {}
190
+
Ok(tranquil_pds::auth::legacy_2fa::Legacy2faOutcome::Blocked) => {
191
191
warn!("Legacy login blocked for TOTP-enabled account: {}", row.did);
192
192
return ApiError::LegacyLoginBlocked.into_response();
193
193
}
194
-
Ok(crate::auth::legacy_2fa::Legacy2faOutcome::ChallengeSent(code)) => {
194
+
Ok(tranquil_pds::auth::legacy_2fa::Legacy2faOutcome::ChallengeSent(code)) => {
195
195
let hostname = &tranquil_config::get().server.hostname;
196
-
if let Err(e) = crate::comms::comms_repo::enqueue_2fa_code(
196
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_2fa_code(
197
197
state.user_repo.as_ref(),
198
198
state.infra_repo.as_ref(),
199
199
row.id,
···
203
203
.await
204
204
{
205
205
error!("Failed to send 2FA code: {:?}", e);
206
-
crate::auth::legacy_2fa::clear_challenge(state.cache.as_ref(), &row.did).await;
206
+
tranquil_pds::auth::legacy_2fa::clear_challenge(state.cache.as_ref(), &row.did).await;
207
207
return ApiError::InternalError(Some(
208
208
"Failed to send verification code. Please try again.".into(),
209
209
))
···
211
211
}
212
212
return ApiError::AuthFactorTokenRequired.into_response();
213
213
}
214
-
Ok(crate::auth::legacy_2fa::Legacy2faOutcome::Verified) => {}
215
-
Err(crate::auth::legacy_2fa::Legacy2faFlowError::Challenge(e)) => {
216
-
use crate::auth::legacy_2fa::ChallengeError;
214
+
Ok(tranquil_pds::auth::legacy_2fa::Legacy2faOutcome::Verified) => {}
215
+
Err(tranquil_pds::auth::legacy_2fa::Legacy2faFlowError::Challenge(e)) => {
216
+
use tranquil_pds::auth::legacy_2fa::ChallengeError;
217
217
return match e {
218
218
ChallengeError::CacheUnavailable => {
219
219
error!("Cache unavailable for 2FA, blocking legacy login");
···
232
232
}
233
233
};
234
234
}
235
-
Err(crate::auth::legacy_2fa::Legacy2faFlowError::Validation(e)) => {
236
-
use crate::auth::legacy_2fa::ValidationError;
235
+
Err(tranquil_pds::auth::legacy_2fa::Legacy2faFlowError::Validation(e)) => {
236
+
use tranquil_pds::auth::legacy_2fa::ValidationError;
237
237
warn!("Invalid 2FA code for {}: {:?}", row.did, e);
238
238
let msg = match e {
239
239
ValidationError::TooManyAttempts => "Too many attempts. Please request a new code.",
···
248
248
return ApiError::InvalidCode(Some(msg.into())).into_response();
249
249
}
250
250
}
251
-
let access_meta = match crate::auth::create_access_token_with_delegation(
251
+
let access_meta = match tranquil_pds::auth::create_access_token_with_delegation(
252
252
&row.did,
253
253
&key_bytes,
254
254
app_password_scopes.as_deref(),
···
261
261
return ApiError::InternalError(None).into_response();
262
262
}
263
263
};
264
-
let refresh_meta = match crate::auth::create_refresh_token_with_metadata(&row.did, &key_bytes) {
264
+
let refresh_meta = match tranquil_pds::auth::create_refresh_token_with_metadata(&row.did, &key_bytes) {
265
265
Ok(m) => m,
266
266
Err(e) => {
267
267
error!("Failed to create refresh token: {:?}", e);
···
297
297
"Legacy login on TOTP-enabled account - sending notification"
298
298
);
299
299
let hostname = &tranquil_config::get().server.hostname;
300
-
if let Err(e) = crate::comms::comms_repo::enqueue_legacy_login(
300
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_legacy_login(
301
301
state.user_repo.as_ref(),
302
302
state.infra_repo.as_ref(),
303
303
row.id,
···
406
406
headers: axum::http::HeaderMap,
407
407
_auth: Auth<Active>,
408
408
) -> Result<Response, ApiError> {
409
-
let extracted = crate::auth::extract_auth_token_from_header(crate::util::get_header_str(
409
+
let extracted = tranquil_pds::auth::extract_auth_token_from_header(tranquil_pds::util::get_header_str(
410
410
&headers,
411
411
http::header::AUTHORIZATION,
412
412
))
413
413
.ok_or(ApiError::AuthenticationRequired)?;
414
-
let jti = crate::auth::get_jti_from_token(&extracted.token)
414
+
let jti = tranquil_pds::auth::get_jti_from_token(&extracted.token)
415
415
.map_err(|_| ApiError::AuthenticationFailed(None))?;
416
-
let did = crate::auth::get_did_from_token(&extracted.token).ok();
416
+
let did = tranquil_pds::auth::get_did_from_token(&extracted.token).ok();
417
417
match state.session_repo.delete_session_by_access_jti(&jti).await {
418
418
Ok(rows) if rows > 0 => {
419
419
if let Some(did) = did {
420
-
let session_cache_key = crate::cache_keys::session_key(&did, &jti);
420
+
let session_cache_key = tranquil_pds::cache_keys::session_key(&did, &jti);
421
421
let _ = state.cache.delete(&session_cache_key).await;
422
422
}
423
423
Ok(EmptyResponse::ok().into_response())
···
432
432
_rate_limit: RateLimited<RefreshSessionLimit>,
433
433
headers: axum::http::HeaderMap,
434
434
) -> Response {
435
-
let extracted = match crate::auth::extract_auth_token_from_header(crate::util::get_header_str(
435
+
let extracted = match tranquil_pds::auth::extract_auth_token_from_header(tranquil_pds::util::get_header_str(
436
436
&headers,
437
437
http::header::AUTHORIZATION,
438
438
)) {
···
440
440
None => return ApiError::AuthenticationRequired.into_response(),
441
441
};
442
442
let refresh_token = extracted.token;
443
-
let refresh_jti = match crate::auth::get_jti_from_token(&refresh_token) {
443
+
let refresh_jti = match tranquil_pds::auth::get_jti_from_token(&refresh_token) {
444
444
Ok(jti) => jti,
445
445
Err(_) => {
446
446
return ApiError::AuthenticationFailed(Some("Invalid token format".into()))
···
473
473
return ApiError::InternalError(None).into_response();
474
474
}
475
475
};
476
-
let key_bytes = match crate::config::decrypt_key(
476
+
let key_bytes = match tranquil_pds::config::decrypt_key(
477
477
&session_row.key_bytes,
478
478
Some(session_row.encryption_version),
479
479
) {
···
483
483
return ApiError::InternalError(None).into_response();
484
484
}
485
485
};
486
-
if crate::auth::verify_refresh_token(&refresh_token, &key_bytes).is_err() {
486
+
if tranquil_pds::auth::verify_refresh_token(&refresh_token, &key_bytes).is_err() {
487
487
return ApiError::AuthenticationFailed(Some("Invalid refresh token".into()))
488
488
.into_response();
489
489
}
490
-
let new_access_meta = match crate::auth::create_access_token_with_delegation(
490
+
let new_access_meta = match tranquil_pds::auth::create_access_token_with_delegation(
491
491
&session_row.did,
492
492
&key_bytes,
493
493
session_row.scope.as_deref(),
···
501
501
}
502
502
};
503
503
let new_refresh_meta =
504
-
match crate::auth::create_refresh_token_with_metadata(&session_row.did, &key_bytes) {
504
+
match tranquil_pds::auth::create_refresh_token_with_metadata(&session_row.did, &key_bytes) {
505
505
Ok(m) => m,
506
506
Err(e) => {
507
507
error!("Failed to create refresh token: {:?}", e);
···
641
641
};
642
642
643
643
let normalized_token =
644
-
crate::auth::verification_token::normalize_token_input(&input.verification_code);
645
-
match crate::auth::verification_token::verify_signup_token(
644
+
tranquil_pds::auth::verification_token::normalize_token_input(&input.verification_code);
645
+
match tranquil_pds::auth::verification_token::verify_signup_token(
646
646
&normalized_token,
647
647
row.channel,
648
648
&identifier,
···
657
657
.into_response();
658
658
}
659
659
}
660
-
Err(crate::auth::verification_token::VerifyError::Expired) => {
660
+
Err(tranquil_pds::auth::verification_token::VerifyError::Expired) => {
661
661
warn!("Verification code expired for user: {}", input.did);
662
662
return ApiError::ExpiredToken(Some("Verification code has expired".into()))
663
663
.into_response();
···
668
668
}
669
669
}
670
670
671
-
let key_bytes = match crate::config::decrypt_key(&row.key_bytes, row.encryption_version) {
671
+
let key_bytes = match tranquil_pds::config::decrypt_key(&row.key_bytes, row.encryption_version) {
672
672
Ok(k) => k,
673
673
Err(e) => {
674
674
error!("Failed to decrypt user key: {:?}", e);
···
676
676
}
677
677
};
678
678
679
-
let access_meta = match crate::auth::create_access_token_with_metadata(&row.did, &key_bytes) {
679
+
let access_meta = match tranquil_pds::auth::create_access_token_with_metadata(&row.did, &key_bytes) {
680
680
Ok(m) => m,
681
681
Err(e) => {
682
682
error!("Failed to create access token: {:?}", e);
683
683
return ApiError::InternalError(None).into_response();
684
684
}
685
685
};
686
-
let refresh_meta = match crate::auth::create_refresh_token_with_metadata(&row.did, &key_bytes) {
686
+
let refresh_meta = match tranquil_pds::auth::create_refresh_token_with_metadata(&row.did, &key_bytes) {
687
687
Ok(m) => m,
688
688
Err(e) => {
689
689
error!("Failed to create refresh token: {:?}", e);
···
718
718
}
719
719
720
720
let hostname = &tranquil_config::get().server.hostname;
721
-
if let Err(e) = crate::comms::comms_repo::enqueue_welcome(
721
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_welcome(
722
722
state.user_repo.as_ref(),
723
723
state.infra_repo.as_ref(),
724
724
row.id,
···
749
749
}
750
750
751
751
pub async fn auto_resend_verification(state: &AppState, did: &Did) -> Option<AutoResendResult> {
752
-
let debounce_key = crate::cache_keys::auto_verify_sent_key(did.as_str());
752
+
let debounce_key = tranquil_pds::cache_keys::auto_verify_sent_key(did.as_str());
753
753
let debounced = state.cache.get(&debounce_key).await.is_some();
754
754
let row = match state.user_repo.get_resend_verification_by_did(did).await {
755
755
Ok(Some(row)) => row,
···
789
789
return Some(result);
790
790
}
791
791
let verification_token =
792
-
crate::auth::verification_token::generate_signup_token(did, row.channel, &recipient);
792
+
tranquil_pds::auth::verification_token::generate_signup_token(did, row.channel, &recipient);
793
793
let formatted_token =
794
-
crate::auth::verification_token::format_token_for_display(&verification_token);
794
+
tranquil_pds::auth::verification_token::format_token_for_display(&verification_token);
795
795
let hostname = &tranquil_config::get().server.hostname;
796
-
if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification(
796
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification(
797
797
state.user_repo.as_ref(),
798
798
state.infra_repo.as_ref(),
799
799
row.id,
···
856
856
};
857
857
858
858
let verification_token =
859
-
crate::auth::verification_token::generate_signup_token(&input.did, row.channel, &recipient);
859
+
tranquil_pds::auth::verification_token::generate_signup_token(&input.did, row.channel, &recipient);
860
860
let formatted_token =
861
-
crate::auth::verification_token::format_token_for_display(&verification_token);
861
+
tranquil_pds::auth::verification_token::format_token_for_display(&verification_token);
862
862
863
863
let hostname = &tranquil_config::get().server.hostname;
864
-
if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification(
864
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification(
865
865
state.user_repo.as_ref(),
866
866
state.infra_repo.as_ref(),
867
867
row.id,
···
910
910
.get("authorization")
911
911
.and_then(|v| v.to_str().ok())
912
912
.and_then(|v| v.strip_prefix("Bearer "))
913
-
.and_then(|token| crate::auth::get_jti_from_token(token).ok());
913
+
.and_then(|token| tranquil_pds::auth::get_jti_from_token(token).ok());
914
914
915
915
let jwt_rows = state
916
916
.session_repo
···
990
990
.delete_session_by_id(session_id)
991
991
.await
992
992
.log_db_err("deleting session")?;
993
-
let cache_key = crate::cache_keys::session_key(&auth.did, &access_jti);
993
+
let cache_key = tranquil_pds::cache_keys::session_key(&auth.did, &access_jti);
994
994
if let Err(e) = state.cache.delete(&cache_key).await {
995
995
warn!("Failed to invalidate session cache: {:?}", e);
996
996
}
···
1020
1020
headers: HeaderMap,
1021
1021
auth: Auth<Active>,
1022
1022
) -> Result<Response, ApiError> {
1023
-
let jti = crate::auth::extract_auth_token_from_header(
1023
+
let jti = tranquil_pds::auth::extract_auth_token_from_header(
1024
1024
headers.get("authorization").and_then(|v| v.to_str().ok()),
1025
1025
)
1026
-
.and_then(|extracted| crate::auth::get_jti_from_token(&extracted.token).ok())
1026
+
.and_then(|extracted| tranquil_pds::auth::get_jti_from_token(&extracted.token).ok())
1027
1027
.ok_or(ApiError::InvalidToken(None))?;
1028
1028
1029
1029
if auth.is_oauth() {
···
1119
1119
.into_response())
1120
1120
}
1121
1121
1122
-
use crate::comms::VALID_LOCALES;
1122
+
use tranquil_pds::comms::VALID_LOCALES;
1123
1123
1124
1124
#[derive(Deserialize)]
1125
1125
#[serde(rename_all = "camelCase")]
+3
-3
crates/tranquil-pds/src/api/server/signing_key.rs
crates/tranquil-api/src/server/signing_key.rs
+3
-3
crates/tranquil-pds/src/api/server/signing_key.rs
crates/tranquil-api/src/server/signing_key.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::state::AppState;
1
+
use tranquil_pds::api::error::ApiError;
2
+
use tranquil_pds::state::AppState;
3
3
use axum::{
4
4
Json,
5
5
extract::State,
···
25
25
26
26
#[derive(Deserialize)]
27
27
pub struct ReserveSigningKeyInput {
28
-
pub did: Option<crate::types::Did>,
28
+
pub did: Option<tranquil_pds::types::Did>,
29
29
}
30
30
31
31
#[derive(Serialize)]
+10
-10
crates/tranquil-pds/src/api/server/totp.rs
crates/tranquil-api/src/server/totp.rs
+10
-10
crates/tranquil-pds/src/api/server/totp.rs
crates/tranquil-api/src/server/totp.rs
···
1
-
use crate::api::EmptyResponse;
2
-
use crate::api::error::{ApiError, DbResultExt};
3
-
use crate::auth::{
1
+
use tranquil_pds::api::EmptyResponse;
2
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
3
+
use tranquil_pds::auth::{
4
4
Active, Auth, decrypt_totp_secret, encrypt_totp_secret, generate_backup_codes,
5
5
generate_qr_png_base64, generate_totp_secret, generate_totp_uri, hash_backup_code,
6
6
is_backup_code_format, require_legacy_session_mfa, verify_backup_code, verify_password_mfa,
7
7
verify_totp_code, verify_totp_mfa,
8
8
};
9
-
use crate::rate_limit::{TotpVerifyLimit, check_user_rate_limit_with_message};
10
-
use crate::state::AppState;
11
-
use crate::types::PlainPassword;
9
+
use tranquil_pds::rate_limit::{TotpVerifyLimit, check_user_rate_limit_with_message};
10
+
use tranquil_pds::state::AppState;
11
+
use tranquil_pds::types::PlainPassword;
12
12
use axum::{
13
13
Json,
14
14
extract::State,
···
186
186
.await
187
187
.log_db_err("deleting TOTP")?;
188
188
189
-
crate::auth::legacy_2fa::clear_challenge(state.cache.as_ref(), &auth.did).await;
189
+
tranquil_pds::auth::legacy_2fa::clear_challenge(state.cache.as_ref(), &auth.did).await;
190
190
191
191
info!(did = %session_mfa.did(), "TOTP disabled (verified via {} and {})", password_mfa.method(), totp_mfa.method());
192
192
···
280
280
281
281
async fn verify_backup_code_for_user(
282
282
state: &AppState,
283
-
did: &crate::types::Did,
283
+
did: &tranquil_pds::types::Did,
284
284
code: &str,
285
285
) -> bool {
286
286
let code = code.trim().to_uppercase();
···
308
308
309
309
pub async fn verify_totp_or_backup_for_user(
310
310
state: &AppState,
311
-
did: &crate::types::Did,
311
+
did: &tranquil_pds::types::Did,
312
312
code: &str,
313
313
) -> bool {
314
314
use tranquil_db_traits::TotpRecordState;
···
340
340
false
341
341
}
342
342
343
-
pub async fn has_totp_enabled(state: &AppState, did: &crate::types::Did) -> bool {
343
+
pub async fn has_totp_enabled(state: &AppState, did: &tranquil_pds::types::Did) -> bool {
344
344
state.user_repo.has_totp_enabled(did).await.unwrap_or(false)
345
345
}
+4
-4
crates/tranquil-pds/src/api/server/trusted_devices.rs
crates/tranquil-api/src/server/trusted_devices.rs
+4
-4
crates/tranquil-pds/src/api/server/trusted_devices.rs
crates/tranquil-api/src/server/trusted_devices.rs
···
1
-
use crate::api::SuccessResponse;
2
-
use crate::api::error::{ApiError, DbResultExt};
1
+
use tranquil_pds::api::SuccessResponse;
2
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
3
3
use axum::{
4
4
Json,
5
5
extract::State,
···
11
11
use tranquil_db_traits::OAuthRepository;
12
12
use tranquil_types::DeviceId;
13
13
14
-
use crate::auth::{Active, Auth};
15
-
use crate::state::AppState;
14
+
use tranquil_pds::auth::{Active, Auth};
15
+
use tranquil_pds::state::AppState;
16
16
17
17
const TRUST_DURATION_DAYS: i64 = 30;
18
18
+6
-6
crates/tranquil-pds/src/api/server/verify_email.rs
crates/tranquil-api/src/server/verify_email.rs
+6
-6
crates/tranquil-pds/src/api/server/verify_email.rs
crates/tranquil-api/src/server/verify_email.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::types::Did;
1
+
use tranquil_pds::api::error::ApiError;
2
+
use tranquil_pds::types::Did;
3
3
use axum::{Json, extract::State};
4
4
use serde::{Deserialize, Serialize};
5
5
use tracing::{info, warn};
6
6
7
-
use crate::state::AppState;
7
+
use tranquil_pds::state::AppState;
8
8
9
9
#[derive(Deserialize)]
10
10
#[serde(rename_all = "camelCase")]
···
71
71
}
72
72
73
73
let hostname = &tranquil_config::get().server.hostname;
74
-
let token = crate::auth::verification_token::generate_migration_token(&user.did, &email);
75
-
let formatted_token = crate::auth::verification_token::format_token_for_display(&token);
74
+
let token = tranquil_pds::auth::verification_token::generate_migration_token(&user.did, &email);
75
+
let formatted_token = tranquil_pds::auth::verification_token::format_token_for_display(&token);
76
76
77
-
if let Err(e) = crate::comms::comms_repo::enqueue_migration_verification(
77
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_migration_verification(
78
78
state.user_repo.as_ref(),
79
79
state.infra_repo.as_ref(),
80
80
user.id,
+6
-6
crates/tranquil-pds/src/api/server/verify_token.rs
crates/tranquil-api/src/server/verify_token.rs
+6
-6
crates/tranquil-pds/src/api/server/verify_token.rs
crates/tranquil-api/src/server/verify_token.rs
···
1
-
use crate::api::error::{ApiError, DbResultExt};
2
-
use crate::comms::comms_repo;
3
-
use crate::types::Did;
1
+
use tranquil_pds::api::error::{ApiError, DbResultExt};
2
+
use tranquil_pds::comms::comms_repo;
3
+
use tranquil_pds::types::Did;
4
4
use axum::{Json, extract::State};
5
5
use serde::{Deserialize, Serialize};
6
6
use tracing::{info, warn};
7
7
8
-
use crate::auth::verification_token::{
8
+
use tranquil_pds::auth::verification_token::{
9
9
VerificationPurpose, normalize_token_input, verify_token_signature,
10
10
};
11
-
use crate::state::AppState;
11
+
use tranquil_pds::state::AppState;
12
12
use tranquil_db_traits::CommsChannel;
13
13
14
14
#[derive(Deserialize, Clone)]
···
46
46
ApiError::from(e)
47
47
})?;
48
48
49
-
let expected_hash = crate::auth::verification_token::hash_identifier(&identifier);
49
+
let expected_hash = tranquil_pds::auth::verification_token::hash_identifier(&identifier);
50
50
if token_data.identifier_hash != expected_hash {
51
51
return Err(ApiError::IdentifierMismatch);
52
52
}
+2
-2
crates/tranquil-pds/src/api/telegram_webhook.rs
crates/tranquil-api/src/telegram_webhook.rs
+2
-2
crates/tranquil-pds/src/api/telegram_webhook.rs
crates/tranquil-api/src/telegram_webhook.rs
+4
-4
crates/tranquil-pds/src/api/temp.rs
crates/tranquil-api/src/temp.rs
+4
-4
crates/tranquil-pds/src/api/temp.rs
crates/tranquil-api/src/temp.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::auth::{Active, Auth, Permissive};
3
-
use crate::state::AppState;
1
+
use tranquil_pds::api::error::ApiError;
2
+
use tranquil_pds::auth::{Active, Auth, Permissive};
3
+
use tranquil_pds::state::AppState;
4
4
use axum::{
5
5
Json,
6
6
extract::State,
···
57
57
58
58
for part in scope_parts {
59
59
if let Some(cid_str) = part.strip_prefix("ref:") {
60
-
let cache_key = crate::cache_keys::scope_ref_key(cid_str);
60
+
let cache_key = tranquil_pds::cache_keys::scope_ref_key(cid_str);
61
61
if let Some(cached) = state.cache.get(&cache_key).await {
62
62
for s in cached.split_whitespace() {
63
63
if !resolved_scopes.contains(&s.to_string()) {
+4
-4
crates/tranquil-pds/src/api/verification.rs
crates/tranquil-api/src/verification.rs
+4
-4
crates/tranquil-pds/src/api/verification.rs
crates/tranquil-api/src/verification.rs
···
1
-
use crate::api::SuccessResponse;
2
-
use crate::state::AppState;
1
+
use tranquil_pds::api::SuccessResponse;
2
+
use tranquil_pds::state::AppState;
3
3
use axum::{
4
4
Json,
5
5
extract::State,
···
19
19
State(state): State<AppState>,
20
20
Json(input): Json<ConfirmChannelVerificationInput>,
21
21
) -> Response {
22
-
let token_input = crate::api::server::VerifyTokenInput {
22
+
let token_input = crate::server::VerifyTokenInput {
23
23
token: input.code,
24
24
identifier: input.identifier,
25
25
};
26
26
27
-
match crate::api::server::verify_token_internal(&state, token_input).await {
27
+
match crate::server::verify_token_internal(&state, token_input).await {
28
28
Ok(_output) => SuccessResponse::ok().into_response(),
29
29
Err(e) => e.into_response(),
30
30
}
+35
crates/tranquil-oauth-server/Cargo.toml
+35
crates/tranquil-oauth-server/Cargo.toml
···
1
+
[package]
2
+
name = "tranquil-oauth-server"
3
+
version.workspace = true
4
+
edition.workspace = true
5
+
license.workspace = true
6
+
7
+
[dependencies]
8
+
tranquil-pds = { workspace = true }
9
+
tranquil-api = { workspace = true }
10
+
tranquil-types = { workspace = true }
11
+
tranquil-config = { workspace = true }
12
+
tranquil-crypto = { workspace = true }
13
+
tranquil-db-traits = { workspace = true }
14
+
15
+
axum = { workspace = true }
16
+
base64 = { workspace = true }
17
+
bcrypt = { workspace = true }
18
+
chrono = { workspace = true }
19
+
cid = { workspace = true }
20
+
hmac = { workspace = true }
21
+
http = { workspace = true }
22
+
jacquard-common = { workspace = true }
23
+
jacquard-repo = { workspace = true }
24
+
k256 = { workspace = true }
25
+
rand = { workspace = true }
26
+
serde = { workspace = true }
27
+
serde_json = { workspace = true }
28
+
serde_urlencoded = { workspace = true }
29
+
sha2 = { workspace = true }
30
+
subtle = { workspace = true }
31
+
tokio = { workspace = true }
32
+
tracing = { workspace = true }
33
+
urlencoding = { workspace = true }
34
+
uuid = { workspace = true }
35
+
webauthn-rs = { workspace = true }
+8
-8
crates/tranquil-pds/src/oauth/endpoints/delegation.rs
crates/tranquil-oauth-server/src/endpoints/delegation.rs
+8
-8
crates/tranquil-pds/src/oauth/endpoints/delegation.rs
crates/tranquil-oauth-server/src/endpoints/delegation.rs
···
1
-
use crate::auth::{Active, Auth};
2
-
use crate::delegation::DelegationActionType;
3
-
use crate::rate_limit::{LoginLimit, OAuthRateLimited, TotpVerifyLimit};
4
-
use crate::state::AppState;
5
-
use crate::types::PlainPassword;
6
-
use crate::util::extract_client_ip;
1
+
use tranquil_pds::auth::{Active, Auth};
2
+
use tranquil_pds::delegation::DelegationActionType;
3
+
use tranquil_pds::rate_limit::{LoginLimit, OAuthRateLimited, TotpVerifyLimit};
4
+
use tranquil_pds::state::AppState;
5
+
use tranquil_pds::types::PlainPassword;
6
+
use tranquil_pds::util::extract_client_ip;
7
7
use axum::{
8
8
Json,
9
9
extract::State,
···
220
220
.into_response();
221
221
}
222
222
223
-
let has_totp = crate::api::server::has_totp_enabled(&state, &controller_did).await;
223
+
let has_totp = tranquil_api::server::has_totp_enabled(&state, &controller_did).await;
224
224
if has_totp {
225
225
return Json(DelegationAuthResponse {
226
226
success: true,
···
377
377
};
378
378
379
379
let totp_valid =
380
-
crate::api::server::verify_totp_or_backup_for_user(&state, &controller_did, &form.code)
380
+
tranquil_api::server::verify_totp_or_backup_for_user(&state, &controller_did, &form.code)
381
381
.await;
382
382
if !totp_valid {
383
383
return Json(DelegationAuthResponse {
+4
-4
crates/tranquil-pds/src/oauth/endpoints/metadata.rs
crates/tranquil-oauth-server/src/endpoints/metadata.rs
+4
-4
crates/tranquil-pds/src/oauth/endpoints/metadata.rs
crates/tranquil-oauth-server/src/endpoints/metadata.rs
···
1
1
use std::fmt::Debug;
2
2
3
-
use crate::oauth::jwks::{JwkSet, create_jwk_set};
4
-
use crate::state::AppState;
3
+
use crate::jwks::{JwkSet, create_jwk_set};
4
+
use tranquil_pds::state::AppState;
5
5
use axum::{Json, extract::State};
6
6
use http::{HeaderName, header};
7
7
use serde::{Deserialize, Serialize};
···
129
129
}
130
130
131
131
pub async fn oauth_jwks(State(_state): State<AppState>) -> Json<JwkSet> {
132
-
use crate::config::AuthConfig;
133
-
use crate::oauth::jwks::Jwk;
132
+
use tranquil_pds::config::AuthConfig;
133
+
use crate::jwks::Jwk;
134
134
let config = AuthConfig::get();
135
135
let server_key = Jwk {
136
136
kty: "EC".to_string(),
crates/tranquil-pds/src/oauth/endpoints/mod.rs
crates/tranquil-oauth-server/src/endpoints/mod.rs
crates/tranquil-pds/src/oauth/endpoints/mod.rs
crates/tranquil-oauth-server/src/endpoints/mod.rs
+5
-5
crates/tranquil-pds/src/oauth/endpoints/par.rs
crates/tranquil-oauth-server/src/endpoints/par.rs
+5
-5
crates/tranquil-pds/src/oauth/endpoints/par.rs
crates/tranquil-oauth-server/src/endpoints/par.rs
···
1
-
use crate::oauth::{
1
+
use tranquil_pds::oauth::{
2
2
AuthorizationRequestParameters, ClientAuth, ClientMetadataCache, CodeChallengeMethod,
3
3
OAuthError, Prompt, RequestData, RequestId, ResponseMode, ResponseType,
4
4
scopes::{ParsedScope, parse_scope},
5
5
};
6
-
use crate::rate_limit::{OAuthParLimit, OAuthRateLimited};
7
-
use crate::state::AppState;
6
+
use tranquil_pds::rate_limit::{OAuthParLimit, OAuthRateLimited};
7
+
use tranquil_pds::state::AppState;
8
8
use axum::body::Bytes;
9
9
use axum::{Json, extract::State, http::HeaderMap};
10
10
use chrono::{Duration, Utc};
···
118
118
.oauth_repo
119
119
.create_authorization_request(&request_id_typed, &request_data)
120
120
.await
121
-
.map_err(crate::oauth::db_err_to_oauth)?;
121
+
.map_err(tranquil_pds::oauth::db_err_to_oauth)?;
122
122
tokio::spawn({
123
123
let oauth_repo = state.oauth_repo.clone();
124
124
async move {
···
159
159
160
160
fn validate_scope(
161
161
requested_scope: &Option<String>,
162
-
client_metadata: &crate::oauth::ClientMetadata,
162
+
client_metadata: &tranquil_pds::oauth::ClientMetadata,
163
163
) -> Result<Option<String>, OAuthError> {
164
164
let scope_str = match requested_scope {
165
165
Some(s) if !s.is_empty() => s,
+11
-11
crates/tranquil-pds/src/oauth/endpoints/token/grants.rs
crates/tranquil-oauth-server/src/endpoints/token/grants.rs
+11
-11
crates/tranquil-pds/src/oauth/endpoints/token/grants.rs
crates/tranquil-oauth-server/src/endpoints/token/grants.rs
···
2
2
use super::types::{
3
3
RequestClientAuth, TokenGrant, TokenResponse, TokenType, ValidatedTokenRequest,
4
4
};
5
-
use crate::config::AuthConfig;
6
-
use crate::delegation::intersect_scopes;
7
-
use crate::oauth::{
5
+
use tranquil_pds::config::AuthConfig;
6
+
use tranquil_pds::delegation::intersect_scopes;
7
+
use tranquil_pds::oauth::{
8
8
AuthFlow, ClientAuth, ClientMetadataCache, DPoPVerifier, OAuthError, RefreshToken, TokenData,
9
9
TokenId,
10
10
db::{enforce_token_limit_for_user, lookup_refresh_token},
11
11
scopes::expand_include_scopes,
12
12
verify_client_auth,
13
13
};
14
-
use crate::state::AppState;
14
+
use tranquil_pds::state::AppState;
15
15
use axum::Json;
16
16
use axum::http::{HeaderMap, Method};
17
17
use chrono::{Duration, Utc};
···
50
50
.oauth_repo
51
51
.consume_authorization_request_by_code(&auth_code)
52
52
.await
53
-
.map_err(crate::oauth::db_err_to_oauth)?
53
+
.map_err(tranquil_pds::oauth::db_err_to_oauth)?
54
54
.ok_or_else(|| OAuthError::InvalidGrant("Invalid or expired code".to_string()))?;
55
55
56
56
let flow = AuthFlow::from_request_data(auth_request)
···
107
107
.oauth_repo
108
108
.check_and_record_dpop_jti(&result.jti)
109
109
.await
110
-
.map_err(crate::oauth::db_err_to_oauth)?
110
+
.map_err(tranquil_pds::oauth::db_err_to_oauth)?
111
111
{
112
112
return Err(OAuthError::InvalidDpopProof(
113
113
"DPoP proof has already been used".to_string(),
···
201
201
.oauth_repo
202
202
.create_token(&token_data)
203
203
.await
204
-
.map_err(crate::oauth::db_err_to_oauth)?;
204
+
.map_err(tranquil_pds::oauth::db_err_to_oauth)?;
205
205
tracing::info!(
206
206
did = %did,
207
207
token_id = %token_id.0,
···
321
321
.oauth_repo
322
322
.delete_token_family(original_token_id)
323
323
.await
324
-
.map_err(crate::oauth::db_err_to_oauth)?;
324
+
.map_err(tranquil_pds::oauth::db_err_to_oauth)?;
325
325
return Err(OAuthError::InvalidGrant(
326
326
"Refresh token reuse detected, token family revoked".to_string(),
327
327
));
···
332
332
.oauth_repo
333
333
.delete_token_family(db_id)
334
334
.await
335
-
.map_err(crate::oauth::db_err_to_oauth)?;
335
+
.map_err(tranquil_pds::oauth::db_err_to_oauth)?;
336
336
return Err(OAuthError::InvalidGrant(
337
337
"Refresh token has expired".to_string(),
338
338
));
···
354
354
.oauth_repo
355
355
.check_and_record_dpop_jti(&result.jti)
356
356
.await
357
-
.map_err(crate::oauth::db_err_to_oauth)?
357
+
.map_err(tranquil_pds::oauth::db_err_to_oauth)?
358
358
{
359
359
return Err(OAuthError::InvalidDpopProof(
360
360
"DPoP proof has already been used".to_string(),
···
387
387
.oauth_repo
388
388
.rotate_token(db_id, &new_refresh_typed, new_expires_at)
389
389
.await
390
-
.map_err(crate::oauth::db_err_to_oauth)?;
390
+
.map_err(tranquil_pds::oauth::db_err_to_oauth)?;
391
391
tracing::info!(
392
392
did = %token_data.did,
393
393
new_expires_at = %new_expires_at,
+2
-2
crates/tranquil-pds/src/oauth/endpoints/token/helpers.rs
crates/tranquil-oauth-server/src/endpoints/token/helpers.rs
+2
-2
crates/tranquil-pds/src/oauth/endpoints/token/helpers.rs
crates/tranquil-oauth-server/src/endpoints/token/helpers.rs
+6
-6
crates/tranquil-pds/src/oauth/endpoints/token/introspect.rs
crates/tranquil-oauth-server/src/endpoints/token/introspect.rs
+6
-6
crates/tranquil-pds/src/oauth/endpoints/token/introspect.rs
crates/tranquil-oauth-server/src/endpoints/token/introspect.rs
···
1
1
use super::helpers::extract_token_claims;
2
-
use crate::oauth::OAuthError;
3
-
use crate::rate_limit::{OAuthIntrospectLimit, OAuthRateLimited};
4
-
use crate::state::AppState;
2
+
use tranquil_pds::oauth::OAuthError;
3
+
use tranquil_pds::rate_limit::{OAuthIntrospectLimit, OAuthRateLimited};
4
+
use tranquil_pds::state::AppState;
5
5
use axum::extract::State;
6
6
use axum::http::StatusCode;
7
7
use axum::{Form, Json};
···
27
27
.oauth_repo
28
28
.get_token_by_refresh_token(&refresh_token)
29
29
.await
30
-
.map_err(crate::oauth::db_err_to_oauth)?
30
+
.map_err(tranquil_pds::oauth::db_err_to_oauth)?
31
31
{
32
32
state
33
33
.oauth_repo
34
34
.delete_token_family(db_id)
35
35
.await
36
-
.map_err(crate::oauth::db_err_to_oauth)?;
36
+
.map_err(tranquil_pds::oauth::db_err_to_oauth)?;
37
37
} else {
38
38
let token_id = TokenId::from(token.clone());
39
39
state
40
40
.oauth_repo
41
41
.delete_token(&token_id)
42
42
.await
43
-
.map_err(crate::oauth::db_err_to_oauth)?;
43
+
.map_err(tranquil_pds::oauth::db_err_to_oauth)?;
44
44
}
45
45
}
46
46
Ok(StatusCode::OK)
+4
-4
crates/tranquil-pds/src/oauth/endpoints/token/mod.rs
crates/tranquil-oauth-server/src/endpoints/token/mod.rs
+4
-4
crates/tranquil-pds/src/oauth/endpoints/token/mod.rs
crates/tranquil-oauth-server/src/endpoints/token/mod.rs
···
3
3
mod introspect;
4
4
mod types;
5
5
6
-
use crate::oauth::OAuthError;
7
-
use crate::rate_limit::{OAuthRateLimited, OAuthTokenLimit};
8
-
use crate::state::AppState;
6
+
use tranquil_pds::oauth::OAuthError;
7
+
use tranquil_pds::rate_limit::{OAuthRateLimited, OAuthTokenLimit};
8
+
use tranquil_pds::state::AppState;
9
9
use axum::body::Bytes;
10
10
use axum::{Json, extract::State, http::HeaderMap};
11
11
···
41
41
));
42
42
};
43
43
let dpop_proof = headers
44
-
.get(crate::util::HEADER_DPOP)
44
+
.get(tranquil_pds::util::HEADER_DPOP)
45
45
.and_then(|v| v.to_str().ok())
46
46
.map(|s| s.to_string());
47
47
let validated = request.validate()?;
+1
-1
crates/tranquil-pds/src/oauth/endpoints/token/types.rs
crates/tranquil-oauth-server/src/endpoints/token/types.rs
+1
-1
crates/tranquil-pds/src/oauth/endpoints/token/types.rs
crates/tranquil-oauth-server/src/endpoints/token/types.rs
crates/tranquil-pds/src/oauth/jwks.rs
crates/tranquil-oauth-server/src/jwks.rs
crates/tranquil-pds/src/oauth/jwks.rs
crates/tranquil-oauth-server/src/jwks.rs
+116
crates/tranquil-oauth-server/src/lib.rs
+116
crates/tranquil-oauth-server/src/lib.rs
···
1
+
pub mod endpoints;
2
+
pub mod jwks;
3
+
pub mod sso_endpoints;
4
+
5
+
use tranquil_pds::state::AppState;
6
+
7
+
pub fn oauth_routes() -> axum::Router<AppState> {
8
+
use axum::{middleware, routing::{get, post}};
9
+
10
+
axum::Router::new()
11
+
.route("/jwks", get(endpoints::oauth_jwks))
12
+
.route("/par", post(endpoints::pushed_authorization_request))
13
+
.route("/authorize", get(endpoints::authorize_get))
14
+
.route("/authorize", post(endpoints::authorize_post))
15
+
.route(
16
+
"/authorize/accounts",
17
+
get(endpoints::authorize_accounts),
18
+
)
19
+
.route(
20
+
"/authorize/select",
21
+
post(endpoints::authorize_select),
22
+
)
23
+
.route("/authorize/2fa", get(endpoints::authorize_2fa_get))
24
+
.route("/authorize/2fa", post(endpoints::authorize_2fa_post))
25
+
.route(
26
+
"/authorize/passkey",
27
+
get(endpoints::authorize_passkey_start),
28
+
)
29
+
.route(
30
+
"/authorize/passkey",
31
+
post(endpoints::authorize_passkey_finish),
32
+
)
33
+
.route(
34
+
"/passkey/check",
35
+
get(endpoints::check_user_has_passkeys),
36
+
)
37
+
.route(
38
+
"/security-status",
39
+
get(endpoints::check_user_security_status),
40
+
)
41
+
.route("/passkey/start", post(endpoints::passkey_start))
42
+
.route("/passkey/finish", post(endpoints::passkey_finish))
43
+
.route("/authorize/deny", post(endpoints::authorize_deny))
44
+
.route(
45
+
"/register/complete",
46
+
post(endpoints::register_complete),
47
+
)
48
+
.route(
49
+
"/establish-session",
50
+
post(endpoints::establish_session),
51
+
)
52
+
.route("/authorize/consent", get(endpoints::consent_get))
53
+
.route("/authorize/consent", post(endpoints::consent_post))
54
+
.route("/authorize/renew", post(endpoints::authorize_renew))
55
+
.route(
56
+
"/authorize/redirect",
57
+
get(endpoints::authorize_redirect),
58
+
)
59
+
.route("/delegation/auth", post(endpoints::delegation_auth))
60
+
.route(
61
+
"/delegation/auth-token",
62
+
post(endpoints::delegation_auth_token),
63
+
)
64
+
.route(
65
+
"/delegation/totp",
66
+
post(endpoints::delegation_totp_verify),
67
+
)
68
+
.route("/token", post(endpoints::token_endpoint))
69
+
.route("/revoke", post(endpoints::revoke_token))
70
+
.route("/introspect", post(endpoints::introspect_token))
71
+
.route("/sso/providers", get(sso_endpoints::get_sso_providers))
72
+
.route("/sso/initiate", post(sso_endpoints::sso_initiate))
73
+
.route(
74
+
"/sso/callback",
75
+
get(sso_endpoints::sso_callback).post(sso_endpoints::sso_callback_post),
76
+
)
77
+
.route("/sso/linked", get(sso_endpoints::get_linked_accounts))
78
+
.route("/sso/unlink", post(sso_endpoints::unlink_account))
79
+
.route(
80
+
"/sso/pending-registration",
81
+
get(sso_endpoints::get_pending_registration),
82
+
)
83
+
.route(
84
+
"/sso/complete-registration",
85
+
post(sso_endpoints::complete_registration),
86
+
)
87
+
.route(
88
+
"/sso/check-handle-available",
89
+
get(sso_endpoints::check_handle_available),
90
+
)
91
+
.layer(middleware::from_fn(tranquil_pds::oauth::verify::dpop_nonce_middleware))
92
+
}
93
+
94
+
pub fn well_known_oauth_routes() -> axum::Router<AppState> {
95
+
use axum::routing::get;
96
+
97
+
axum::Router::new()
98
+
.route(
99
+
"/oauth-protected-resource",
100
+
get(endpoints::oauth_protected_resource),
101
+
)
102
+
.route(
103
+
"/oauth-authorization-server",
104
+
get(endpoints::oauth_authorization_server),
105
+
)
106
+
}
107
+
108
+
pub fn frontend_client_metadata_route() -> axum::Router<AppState> {
109
+
use axum::routing::get;
110
+
111
+
axum::Router::new()
112
+
.route(
113
+
"/oauth-client-metadata.json",
114
+
get(endpoints::frontend_client_metadata),
115
+
)
116
+
}
+40
-40
crates/tranquil-pds/src/sso/endpoints.rs
crates/tranquil-oauth-server/src/sso_endpoints.rs
+40
-40
crates/tranquil-pds/src/sso/endpoints.rs
crates/tranquil-oauth-server/src/sso_endpoints.rs
···
9
9
use tranquil_db_traits::{SsoAction, SsoProviderType};
10
10
use tranquil_types::RequestId;
11
11
12
-
use super::config::SsoConfig;
13
-
use crate::api::error::ApiError;
14
-
use crate::auth::extractor::extract_auth_token_from_header;
15
-
use crate::auth::{generate_app_password, validate_bearer_token_cached};
16
-
use crate::rate_limit::{
12
+
use tranquil_pds::sso::SsoConfig;
13
+
use tranquil_pds::api::error::ApiError;
14
+
use tranquil_pds::auth::extractor::extract_auth_token_from_header;
15
+
use tranquil_pds::auth::{generate_app_password, validate_bearer_token_cached};
16
+
use tranquil_pds::rate_limit::{
17
17
AccountCreationLimit, RateLimited, SsoCallbackLimit, SsoInitiateLimit, SsoUnlinkLimit,
18
18
check_user_rate_limit_with_message,
19
19
};
20
-
use crate::state::AppState;
20
+
use tranquil_pds::state::AppState;
21
21
22
22
fn generate_state() -> String {
23
23
use rand::RngCore;
···
367
367
state: &AppState,
368
368
request_uri: &str,
369
369
provider: SsoProviderType,
370
-
user_info: &crate::sso::providers::SsoUserInfo,
370
+
user_info: &tranquil_pds::sso::providers::SsoUserInfo,
371
371
) -> Response {
372
372
let identity = match state
373
373
.sso_repo
···
482
482
state: &AppState,
483
483
did: tranquil_types::Did,
484
484
provider: SsoProviderType,
485
-
user_info: &crate::sso::providers::SsoUserInfo,
485
+
user_info: &tranquil_pds::sso::providers::SsoUserInfo,
486
486
) -> Response {
487
487
let existing = state
488
488
.sso_repo
···
555
555
state: &AppState,
556
556
request_uri: &str,
557
557
provider: SsoProviderType,
558
-
user_info: &crate::sso::providers::SsoUserInfo,
558
+
user_info: &tranquil_pds::sso::providers::SsoUserInfo,
559
559
) -> Response {
560
560
match state
561
561
.sso_repo
···
616
616
617
617
pub async fn get_linked_accounts(
618
618
State(state): State<AppState>,
619
-
auth: crate::auth::Auth<crate::auth::Active>,
619
+
auth: tranquil_pds::auth::Auth<tranquil_pds::auth::Active>,
620
620
) -> Result<Json<LinkedAccountsResponse>, ApiError> {
621
621
let identities = state
622
622
.sso_repo
···
651
651
652
652
pub async fn unlink_account(
653
653
State(state): State<AppState>,
654
-
auth: crate::auth::Auth<crate::auth::Active>,
654
+
auth: tranquil_pds::auth::Auth<tranquil_pds::auth::Active>,
655
655
Json(input): Json<UnlinkAccountRequest>,
656
656
) -> Result<Json<UnlinkAccountResponse>, ApiError> {
657
657
let _rate_limit = check_user_rate_limit_with_message::<SsoUnlinkLimit>(
···
763
763
}));
764
764
}
765
765
766
-
let validated = match crate::api::validation::validate_short_handle(&query.handle) {
766
+
let validated = match tranquil_pds::api::validation::validate_short_handle(&query.handle) {
767
767
Ok(h) => h,
768
768
Err(e) => {
769
769
return Ok(Json(CheckHandleResponse {
···
781
781
}
782
782
let domain = query.domain.as_deref().unwrap_or(&available_domains[0]);
783
783
let full_handle = format!("{}.{}", validated, domain);
784
-
let handle_typed: crate::types::Handle = match full_handle.parse() {
784
+
let handle_typed: tranquil_pds::types::Handle = match full_handle.parse() {
785
785
Ok(h) => h,
786
786
Err(_) => return Err(ApiError::InvalidHandle(None)),
787
787
};
···
879
879
.unwrap_or(&input.handle),
880
880
None => &input.handle,
881
881
};
882
-
match crate::api::validation::validate_short_handle(handle_to_validate) {
882
+
match tranquil_pds::api::validation::validate_short_handle(handle_to_validate) {
883
883
Ok(h) => format!("{}.{}", h, matched_domain.unwrap_or(&available_domains[0])),
884
884
Err(_) => return Err(ApiError::InvalidHandle(None)),
885
885
}
886
886
} else {
887
-
match crate::api::validation::validate_full_domain_handle(&input.handle) {
887
+
match tranquil_pds::api::validation::validate_full_domain_handle(&input.handle) {
888
888
Ok(h) => h,
889
889
Err(_) => return Err(ApiError::InvalidHandle(None)),
890
890
}
···
914
914
tranquil_db_traits::CommsChannel::Discord => match &input.discord_username {
915
915
Some(username) if !username.trim().is_empty() => {
916
916
let clean = username.trim().to_lowercase();
917
-
if !crate::api::validation::is_valid_discord_username(&clean) {
917
+
if !tranquil_pds::api::validation::is_valid_discord_username(&clean) {
918
918
return Err(ApiError::InvalidRequest(
919
919
"Invalid Discord username. Must be 2-32 lowercase characters (letters, numbers, underscores, periods)".into(),
920
920
));
···
926
926
tranquil_db_traits::CommsChannel::Telegram => match &input.telegram_username {
927
927
Some(username) if !username.trim().is_empty() => {
928
928
let clean = username.trim().trim_start_matches('@');
929
-
if !crate::api::validation::is_valid_telegram_username(clean) {
929
+
if !tranquil_pds::api::validation::is_valid_telegram_username(clean) {
930
930
return Err(ApiError::InvalidRequest(
931
931
"Invalid Telegram username. Must be 5-32 characters, alphanumeric or underscore".into(),
932
932
));
···
960
960
if e.len() > 254 {
961
961
return Err(ApiError::InvalidEmail);
962
962
}
963
-
if !crate::api::validation::is_valid_email(e) {
963
+
if !tranquil_pds::api::validation::is_valid_email(e) {
964
964
return Err(ApiError::InvalidEmail);
965
965
}
966
966
Some(e.clone())
···
981
981
None
982
982
};
983
983
984
-
let handle_typed: crate::types::Handle =
984
+
let handle_typed: tranquil_pds::types::Handle =
985
985
handle.parse().map_err(|_| ApiError::InvalidHandle(None))?;
986
986
let reserved = state
987
987
.user_repo
···
1008
1008
1009
1009
let did = match did_type {
1010
1010
"web" => {
1011
-
if !crate::api::server::meta::is_self_hosted_did_web_enabled() {
1011
+
if !tranquil_pds::util::is_self_hosted_did_web_enabled() {
1012
1012
return Err(ApiError::SelfHostedDidWebDisabled);
1013
1013
}
1014
1014
let encoded_handle = handle.replace(':', "%3A");
···
1038
1038
.secrets
1039
1039
.plc_rotation_key
1040
1040
.clone()
1041
-
.unwrap_or_else(|| crate::plc::signing_key_to_did_key(&signing_key));
1041
+
.unwrap_or_else(|| tranquil_pds::plc::signing_key_to_did_key(&signing_key));
1042
1042
1043
-
let genesis_result = match crate::plc::create_genesis_operation(
1043
+
let genesis_result = match tranquil_pds::plc::create_genesis_operation(
1044
1044
&signing_key,
1045
1045
&rotation_key,
1046
1046
&handle,
···
1055
1055
}
1056
1056
};
1057
1057
1058
-
let plc_client = crate::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
1058
+
let plc_client = tranquil_pds::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
1059
1059
if let Err(e) = plc_client
1060
1060
.send_operation(&genesis_result.did, &genesis_result.signed_operation)
1061
1061
.await
···
1071
1071
};
1072
1072
tracing::info!(did = %did, handle = %handle, provider = %pending_preview.provider.as_str(), "Created DID for SSO account");
1073
1073
1074
-
let encrypted_key_bytes = match crate::config::encrypt_key(&secret_key_bytes) {
1074
+
let encrypted_key_bytes = match tranquil_pds::config::encrypt_key(&secret_key_bytes) {
1075
1075
Ok(bytes) => bytes,
1076
1076
Err(e) => {
1077
1077
tracing::error!("Error encrypting signing key: {:?}", e);
···
1089
1089
};
1090
1090
1091
1091
let rev = Tid::now(LimitedU32::MIN);
1092
-
let did_typed: crate::types::Did = did
1092
+
let did_typed: tranquil_pds::types::Did = did
1093
1093
.parse()
1094
1094
.map_err(|_| ApiError::InternalError(Some("Invalid DID".into())))?;
1095
-
let (commit_bytes, _sig) = match crate::api::repo::record::utils::create_signed_commit(
1095
+
let (commit_bytes, _sig) = match tranquil_pds::repo_ops::create_signed_commit(
1096
1096
&did_typed,
1097
1097
mst_root,
1098
1098
rev.as_ref(),
···
1146
1146
.map(|s| s.trim().trim_start_matches('@').to_lowercase())
1147
1147
.filter(|s| !s.is_empty()),
1148
1148
encrypted_key_bytes: encrypted_key_bytes.clone(),
1149
-
encryption_version: crate::config::ENCRYPTION_VERSION,
1149
+
encryption_version: tranquil_pds::config::ENCRYPTION_VERSION,
1150
1150
commit_cid: commit_cid.to_string(),
1151
1151
repo_rev: rev.as_ref().to_string(),
1152
1152
genesis_block_cids,
···
1189
1189
.await;
1190
1190
1191
1191
if let Err(e) =
1192
-
crate::api::repo::record::sequence_identity_event(&state, &did_typed, Some(&handle_typed))
1192
+
tranquil_pds::repo_ops::sequence_identity_event(&state, &did_typed, Some(&handle_typed))
1193
1193
.await
1194
1194
{
1195
1195
tracing::warn!("Failed to sequence identity event for {}: {}", did, e);
1196
1196
}
1197
-
if let Err(e) = crate::api::repo::record::sequence_account_event(
1197
+
if let Err(e) = tranquil_pds::repo_ops::sequence_account_event(
1198
1198
&state,
1199
1199
&did_typed,
1200
1200
tranquil_db_traits::AccountStatus::Active,
···
1208
1208
"$type": "app.bsky.actor.profile",
1209
1209
"displayName": handle_typed.as_str()
1210
1210
});
1211
-
if let Err(e) = crate::api::repo::record::create_record_internal(
1211
+
if let Err(e) = tranquil_pds::repo_ops::create_record_internal(
1212
1212
&state,
1213
1213
&did_typed,
1214
-
&crate::types::PROFILE_COLLECTION,
1215
-
&crate::types::PROFILE_RKEY,
1214
+
&tranquil_pds::types::PROFILE_COLLECTION,
1215
+
&tranquil_pds::types::PROFILE_RKEY,
1216
1216
&profile_record,
1217
1217
)
1218
1218
.await
···
1287
1287
tracing::info!(did = %did, "Auto-verified email from SSO provider");
1288
1288
1289
1289
if is_standalone {
1290
-
let key_bytes = match crate::config::decrypt_key(
1290
+
let key_bytes = match tranquil_pds::config::decrypt_key(
1291
1291
&encrypted_key_bytes,
1292
-
Some(crate::config::ENCRYPTION_VERSION),
1292
+
Some(tranquil_pds::config::ENCRYPTION_VERSION),
1293
1293
) {
1294
1294
Ok(k) => k,
1295
1295
Err(e) => {
···
1298
1298
}
1299
1299
};
1300
1300
1301
-
let access_meta = match crate::auth::create_access_token_with_metadata(&did, &key_bytes)
1301
+
let access_meta = match tranquil_pds::auth::create_access_token_with_metadata(&did, &key_bytes)
1302
1302
{
1303
1303
Ok(m) => m,
1304
1304
Err(e) => {
···
1307
1307
}
1308
1308
};
1309
1309
let refresh_meta =
1310
-
match crate::auth::create_refresh_token_with_metadata(&did, &key_bytes) {
1310
+
match tranquil_pds::auth::create_refresh_token_with_metadata(&did, &key_bytes) {
1311
1311
Ok(m) => m,
1312
1312
Err(e) => {
1313
1313
tracing::error!("Failed to create refresh token: {:?}", e);
···
1333
1333
}
1334
1334
1335
1335
let hostname = &tranquil_config::get().server.hostname;
1336
-
if let Err(e) = crate::comms::comms_repo::enqueue_welcome(
1336
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_welcome(
1337
1337
state.user_repo.as_ref(),
1338
1338
state.infra_repo.as_ref(),
1339
1339
user_id.unwrap_or(uuid::Uuid::nil()),
···
1370
1370
}
1371
1371
1372
1372
if let Some(uid) = user_id {
1373
-
let verification_token = crate::auth::verification_token::generate_signup_token(
1373
+
let verification_token = tranquil_pds::auth::verification_token::generate_signup_token(
1374
1374
&did_typed,
1375
1375
verification_channel,
1376
1376
&verification_recipient,
1377
1377
);
1378
1378
let formatted_token =
1379
-
crate::auth::verification_token::format_token_for_display(&verification_token);
1380
-
if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification(
1379
+
tranquil_pds::auth::verification_token::format_token_for_display(&verification_token);
1380
+
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification(
1381
1381
state.user_repo.as_ref(),
1382
1382
state.infra_repo.as_ref(),
1383
1383
uid,
+3
-3
crates/tranquil-pds/Cargo.toml
+3
-3
crates/tranquil-pds/Cargo.toml
···
31
31
bytes = { workspace = true }
32
32
chrono = { workspace = true }
33
33
cid = { workspace = true }
34
-
clap = { workspace = true }
35
-
dotenvy = { workspace = true }
36
34
ed25519-dalek = { workspace = true }
37
35
futures = { workspace = true }
38
36
hex = { workspace = true }
···
75
73
tower-http = { workspace = true }
76
74
tower-layer = { workspace = true }
77
75
tracing = { workspace = true }
78
-
tracing-subscriber = { workspace = true }
79
76
urlencoding = { workspace = true }
80
77
uuid = { workspace = true }
81
78
webauthn-rs = { workspace = true }
···
97
94
testcontainers = { workspace = true }
98
95
testcontainers-modules = { workspace = true }
99
96
tranquil-ripple = { workspace = true }
97
+
tranquil-sync = { workspace = true }
98
+
tranquil-api = { workspace = true }
99
+
tranquil-oauth-server = { workspace = true }
100
100
wiremock = { workspace = true }
-14
crates/tranquil-pds/src/api/mod.rs
-14
crates/tranquil-pds/src/api/mod.rs
···
1
-
pub mod actor;
2
-
pub mod admin;
3
-
pub mod age_assurance;
4
-
pub mod backup;
5
-
pub mod delegation;
6
-
pub mod discord_webhook;
7
1
pub mod error;
8
-
pub mod identity;
9
-
pub mod moderation;
10
-
pub mod notification_prefs;
11
2
pub mod proxy;
12
3
pub mod proxy_client;
13
-
pub mod repo;
14
4
pub mod responses;
15
-
pub mod server;
16
-
pub mod telegram_webhook;
17
-
pub mod temp;
18
5
pub mod validation;
19
-
pub mod verification;
20
6
21
7
pub use error::ApiError;
22
8
pub use proxy_client::{AtUriParts, proxy_client, validate_at_uri, validate_limit};
+3
-3
crates/tranquil-pds/src/auth/mfa_verified.rs
+3
-3
crates/tranquil-pds/src/auth/mfa_verified.rs
···
74
74
state: &AppState,
75
75
user: &'a AuthenticatedUser,
76
76
) -> Result<MfaVerified<'a>, Response> {
77
-
use crate::api::server::reauth::{check_legacy_session_mfa, legacy_mfa_required_response};
77
+
use crate::auth::reauth::{check_legacy_session_mfa, legacy_mfa_required_response};
78
78
79
79
if check_legacy_session_mfa(&*state.session_repo, &user.did).await {
80
80
Ok(MfaVerified::from_session_reauth(user))
···
87
87
state: &AppState,
88
88
user: &'a AuthenticatedUser,
89
89
) -> Result<MfaVerified<'a>, Response> {
90
-
use crate::api::server::reauth::{REAUTH_WINDOW_SECONDS, reauth_required_response};
90
+
use crate::auth::reauth::{REAUTH_WINDOW_SECONDS, reauth_required_response};
91
91
use chrono::Utc;
92
92
93
93
let status = state
···
117
117
state: &AppState,
118
118
user: &'a AuthenticatedUser,
119
119
) -> Result<Option<MfaVerified<'a>>, Response> {
120
-
use crate::api::server::reauth::{check_reauth_required_cached, reauth_required_response};
120
+
use crate::auth::reauth::{check_reauth_required_cached, reauth_required_response};
121
121
122
122
let has_password = state
123
123
.user_repo
+1
crates/tranquil-pds/src/auth/mod.rs
+1
crates/tranquil-pds/src/auth/mod.rs
+156
crates/tranquil-pds/src/auth/reauth.rs
+156
crates/tranquil-pds/src/auth/reauth.rs
···
1
+
use axum::{
2
+
Json,
3
+
http::StatusCode,
4
+
response::{IntoResponse, Response},
5
+
};
6
+
use chrono::Utc;
7
+
use serde::Serialize;
8
+
use tranquil_db_traits::{SessionRepository, UserRepository};
9
+
10
+
pub const REAUTH_WINDOW_SECONDS: i64 = 300;
11
+
12
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
13
+
#[serde(rename_all = "lowercase")]
14
+
pub enum ReauthMethod {
15
+
Password,
16
+
Totp,
17
+
Passkey,
18
+
}
19
+
20
+
fn is_reauth_required(last_reauth_at: Option<chrono::DateTime<Utc>>) -> bool {
21
+
match last_reauth_at {
22
+
None => true,
23
+
Some(t) => {
24
+
let elapsed = Utc::now().signed_duration_since(t);
25
+
elapsed.num_seconds() > REAUTH_WINDOW_SECONDS
26
+
}
27
+
}
28
+
}
29
+
30
+
async fn get_available_reauth_methods(
31
+
user_repo: &dyn UserRepository,
32
+
_session_repo: &dyn SessionRepository,
33
+
did: &crate::types::Did,
34
+
) -> Vec<ReauthMethod> {
35
+
let mut methods = Vec::new();
36
+
37
+
let has_password = user_repo
38
+
.get_password_hash_by_did(did)
39
+
.await
40
+
.ok()
41
+
.flatten()
42
+
.is_some();
43
+
44
+
if has_password {
45
+
methods.push(ReauthMethod::Password);
46
+
}
47
+
48
+
let has_totp = user_repo.has_totp_enabled(did).await.unwrap_or(false);
49
+
if has_totp {
50
+
methods.push(ReauthMethod::Totp);
51
+
}
52
+
53
+
let has_passkeys = user_repo.has_passkeys(did).await.unwrap_or(false);
54
+
if has_passkeys {
55
+
methods.push(ReauthMethod::Passkey);
56
+
}
57
+
58
+
methods
59
+
}
60
+
61
+
pub async fn check_reauth_required_cached(
62
+
session_repo: &dyn SessionRepository,
63
+
cache: &std::sync::Arc<dyn crate::cache::Cache>,
64
+
did: &crate::types::Did,
65
+
) -> bool {
66
+
let cache_key = crate::cache_keys::reauth_key(did);
67
+
if let Some(timestamp_str) = cache.get(&cache_key).await
68
+
&& let Ok(timestamp) = timestamp_str.parse::<i64>()
69
+
{
70
+
let reauth_time = chrono::DateTime::from_timestamp(timestamp, 0);
71
+
if let Some(t) = reauth_time {
72
+
let elapsed = Utc::now().signed_duration_since(t);
73
+
if elapsed.num_seconds() <= REAUTH_WINDOW_SECONDS {
74
+
return false;
75
+
}
76
+
}
77
+
}
78
+
match session_repo.get_last_reauth_at(did).await {
79
+
Ok(last_reauth_at) => is_reauth_required(last_reauth_at),
80
+
_ => true,
81
+
}
82
+
}
83
+
84
+
#[derive(Serialize)]
85
+
#[serde(rename_all = "camelCase")]
86
+
pub struct ReauthRequiredError {
87
+
pub error: String,
88
+
pub message: String,
89
+
pub reauth_methods: Vec<ReauthMethod>,
90
+
}
91
+
92
+
pub async fn reauth_required_response(
93
+
user_repo: &dyn UserRepository,
94
+
session_repo: &dyn SessionRepository,
95
+
did: &crate::types::Did,
96
+
) -> Response {
97
+
let methods = get_available_reauth_methods(user_repo, session_repo, did).await;
98
+
(
99
+
StatusCode::UNAUTHORIZED,
100
+
Json(ReauthRequiredError {
101
+
error: "ReauthRequired".to_string(),
102
+
message: "Re-authentication required for this action".to_string(),
103
+
reauth_methods: methods,
104
+
}),
105
+
)
106
+
.into_response()
107
+
}
108
+
109
+
pub async fn check_legacy_session_mfa(
110
+
session_repo: &dyn SessionRepository,
111
+
did: &crate::types::Did,
112
+
) -> bool {
113
+
match session_repo.get_session_mfa_status(did).await {
114
+
Ok(Some(status)) => {
115
+
if status.login_type.is_modern() {
116
+
return true;
117
+
}
118
+
if status.mfa_verified {
119
+
return true;
120
+
}
121
+
if let Some(last_reauth) = status.last_reauth_at {
122
+
let elapsed = chrono::Utc::now().signed_duration_since(last_reauth);
123
+
if elapsed.num_seconds() <= REAUTH_WINDOW_SECONDS {
124
+
return true;
125
+
}
126
+
}
127
+
false
128
+
}
129
+
_ => true,
130
+
}
131
+
}
132
+
133
+
#[derive(Serialize)]
134
+
#[serde(rename_all = "camelCase")]
135
+
pub struct MfaVerificationRequiredError {
136
+
pub error: String,
137
+
pub message: String,
138
+
pub reauth_methods: Vec<ReauthMethod>,
139
+
}
140
+
141
+
pub async fn legacy_mfa_required_response(
142
+
user_repo: &dyn UserRepository,
143
+
session_repo: &dyn SessionRepository,
144
+
did: &crate::types::Did,
145
+
) -> Response {
146
+
let methods = get_available_reauth_methods(user_repo, session_repo, did).await;
147
+
(
148
+
StatusCode::FORBIDDEN,
149
+
Json(MfaVerificationRequiredError {
150
+
error: "MfaVerificationRequired".to_string(),
151
+
message: "This sensitive operation requires MFA verification. Your session was created via a legacy app that doesn't support MFA during login.".to_string(),
152
+
reauth_methods: methods,
153
+
}),
154
+
)
155
+
.into_response()
156
+
}
+1
-1
crates/tranquil-pds/src/crawlers.rs
+1
-1
crates/tranquil-pds/src/crawlers.rs
+28
-601
crates/tranquil-pds/src/lib.rs
+28
-601
crates/tranquil-pds/src/lib.rs
···
17
17
pub mod plc;
18
18
pub mod rate_limit;
19
19
pub mod repo;
20
+
pub mod repo_ops;
20
21
pub mod repo_write_lock;
21
22
pub mod scheduled;
22
23
pub mod sso;
···
33
34
extract::DefaultBodyLimit,
34
35
http::Method,
35
36
middleware,
36
-
routing::{get, post},
37
+
routing::get,
37
38
};
38
39
use http::StatusCode;
39
40
use serde_json::json;
···
56
57
#[cfg(not(debug_assertions))]
57
58
pub const BUILD_VERSION: &str = env!("CARGO_PKG_VERSION");
58
59
60
+
pub struct ExternalRoutes {
61
+
pub xrpc: Router<AppState>,
62
+
pub oauth: Router<AppState>,
63
+
pub well_known: Router<AppState>,
64
+
pub extra: Router<AppState>,
65
+
}
66
+
67
+
impl Default for ExternalRoutes {
68
+
fn default() -> Self {
69
+
Self {
70
+
xrpc: Router::new(),
71
+
oauth: Router::new(),
72
+
well_known: Router::new(),
73
+
extra: Router::new(),
74
+
}
75
+
}
76
+
}
77
+
59
78
pub fn app(state: AppState) -> Router {
60
-
let xrpc_router = Router::new()
61
-
.route("/_health", get(api::server::health))
62
-
.route(
63
-
"/com.atproto.server.describeServer",
64
-
get(api::server::describe_server),
65
-
)
66
-
.route(
67
-
"/com.atproto.server.createAccount",
68
-
post(api::identity::create_account),
69
-
)
70
-
.route(
71
-
"/com.atproto.server.createSession",
72
-
post(api::server::create_session),
73
-
)
74
-
.route(
75
-
"/com.atproto.server.getSession",
76
-
get(api::server::get_session),
77
-
)
78
-
.route("/_account.listSessions", get(api::server::list_sessions))
79
-
.route("/_account.revokeSession", post(api::server::revoke_session))
80
-
.route(
81
-
"/_account.revokeAllSessions",
82
-
post(api::server::revoke_all_sessions),
83
-
)
84
-
.route(
85
-
"/com.atproto.server.deleteSession",
86
-
post(api::server::delete_session),
87
-
)
88
-
.route(
89
-
"/com.atproto.server.refreshSession",
90
-
post(api::server::refresh_session),
91
-
)
92
-
.route(
93
-
"/com.atproto.server.confirmSignup",
94
-
post(api::server::confirm_signup),
95
-
)
96
-
.route(
97
-
"/com.atproto.server.resendVerification",
98
-
post(api::server::resend_verification),
99
-
)
100
-
.route(
101
-
"/com.atproto.server.getServiceAuth",
102
-
get(api::server::get_service_auth),
103
-
)
104
-
.route(
105
-
"/com.atproto.identity.resolveHandle",
106
-
get(api::identity::resolve_handle),
107
-
)
108
-
.route(
109
-
"/com.atproto.repo.createRecord",
110
-
post(api::repo::create_record),
111
-
)
112
-
.route("/com.atproto.repo.putRecord", post(api::repo::put_record))
113
-
.route("/com.atproto.repo.getRecord", get(api::repo::get_record))
114
-
.route(
115
-
"/com.atproto.repo.deleteRecord",
116
-
post(api::repo::delete_record),
117
-
)
118
-
.route(
119
-
"/com.atproto.repo.listRecords",
120
-
get(api::repo::list_records),
121
-
)
122
-
.route(
123
-
"/com.atproto.repo.describeRepo",
124
-
get(api::repo::describe_repo),
125
-
)
126
-
.route("/com.atproto.repo.uploadBlob", post(api::repo::upload_blob))
127
-
.route(
128
-
"/com.atproto.repo.applyWrites",
129
-
post(api::repo::apply_writes),
130
-
)
131
-
.route(
132
-
"/com.atproto.sync.getLatestCommit",
133
-
get(sync::get_latest_commit),
134
-
)
135
-
.route("/com.atproto.sync.listRepos", get(sync::list_repos))
136
-
.route("/com.atproto.sync.getBlob", get(sync::get_blob))
137
-
.route("/com.atproto.sync.listBlobs", get(sync::list_blobs))
138
-
.route(
139
-
"/com.atproto.sync.getRepoStatus",
140
-
get(sync::get_repo_status),
141
-
)
142
-
.route(
143
-
"/com.atproto.server.checkAccountStatus",
144
-
get(api::server::check_account_status),
145
-
)
146
-
.route(
147
-
"/com.atproto.identity.getRecommendedDidCredentials",
148
-
get(api::identity::get_recommended_did_credentials),
149
-
)
150
-
.route(
151
-
"/com.atproto.repo.listMissingBlobs",
152
-
get(api::repo::list_missing_blobs),
153
-
)
154
-
.route(
155
-
"/com.atproto.sync.notifyOfUpdate",
156
-
post(sync::notify_of_update),
157
-
)
158
-
.route("/com.atproto.sync.requestCrawl", post(sync::request_crawl))
159
-
.route("/com.atproto.sync.getBlocks", get(sync::get_blocks))
160
-
.route("/com.atproto.sync.getRepo", get(sync::get_repo))
161
-
.route("/com.atproto.sync.getRecord", get(sync::get_record))
162
-
.route(
163
-
"/com.atproto.sync.subscribeRepos",
164
-
get(sync::subscribe_repos),
165
-
)
166
-
.route("/com.atproto.sync.getHead", get(sync::get_head))
167
-
.route("/com.atproto.sync.getCheckout", get(sync::get_checkout))
168
-
.route(
169
-
"/com.atproto.moderation.createReport",
170
-
post(api::moderation::create_report),
171
-
)
172
-
.route(
173
-
"/com.atproto.admin.getAccountInfo",
174
-
get(api::admin::get_account_info),
175
-
)
176
-
.route(
177
-
"/com.atproto.admin.getAccountInfos",
178
-
get(api::admin::get_account_infos),
179
-
)
180
-
.route(
181
-
"/com.atproto.admin.searchAccounts",
182
-
get(api::admin::search_accounts),
183
-
)
184
-
.route(
185
-
"/com.atproto.server.activateAccount",
186
-
post(api::server::activate_account),
187
-
)
188
-
.route(
189
-
"/com.atproto.server.deactivateAccount",
190
-
post(api::server::deactivate_account),
191
-
)
192
-
.route(
193
-
"/com.atproto.server.requestAccountDelete",
194
-
post(api::server::request_account_delete),
195
-
)
196
-
.route(
197
-
"/com.atproto.server.deleteAccount",
198
-
post(api::server::delete_account),
199
-
)
200
-
.route(
201
-
"/com.atproto.server.requestPasswordReset",
202
-
post(api::server::request_password_reset),
203
-
)
204
-
.route(
205
-
"/com.atproto.server.resetPassword",
206
-
post(api::server::reset_password),
207
-
)
208
-
.route(
209
-
"/_account.changePassword",
210
-
post(api::server::change_password),
211
-
)
212
-
.route(
213
-
"/_account.removePassword",
214
-
post(api::server::remove_password),
215
-
)
216
-
.route(
217
-
"/_account.setPassword",
218
-
post(api::server::set_password),
219
-
)
220
-
.route(
221
-
"/_account.getPasswordStatus",
222
-
get(api::server::get_password_status),
223
-
)
224
-
.route(
225
-
"/_account.getReauthStatus",
226
-
get(api::server::get_reauth_status),
227
-
)
228
-
.route(
229
-
"/_account.reauthPassword",
230
-
post(api::server::reauth_password),
231
-
)
232
-
.route("/_account.reauthTotp", post(api::server::reauth_totp))
233
-
.route(
234
-
"/_account.reauthPasskeyStart",
235
-
post(api::server::reauth_passkey_start),
236
-
)
237
-
.route(
238
-
"/_account.reauthPasskeyFinish",
239
-
post(api::server::reauth_passkey_finish),
240
-
)
241
-
.route(
242
-
"/_account.getLegacyLoginPreference",
243
-
get(api::server::get_legacy_login_preference),
244
-
)
245
-
.route(
246
-
"/_account.updateLegacyLoginPreference",
247
-
post(api::server::update_legacy_login_preference),
248
-
)
249
-
.route("/_account.updateLocale", post(api::server::update_locale))
250
-
.route(
251
-
"/_account.listTrustedDevices",
252
-
get(api::server::list_trusted_devices),
253
-
)
254
-
.route(
255
-
"/_account.revokeTrustedDevice",
256
-
post(api::server::revoke_trusted_device),
257
-
)
258
-
.route(
259
-
"/_account.updateTrustedDevice",
260
-
post(api::server::update_trusted_device),
261
-
)
262
-
.route(
263
-
"/_account.createPasskeyAccount",
264
-
post(api::server::create_passkey_account),
265
-
)
266
-
.route(
267
-
"/_account.startPasskeyRegistrationForSetup",
268
-
post(api::server::start_passkey_registration_for_setup),
269
-
)
270
-
.route(
271
-
"/_account.completePasskeySetup",
272
-
post(api::server::complete_passkey_setup),
273
-
)
274
-
.route(
275
-
"/_account.requestPasskeyRecovery",
276
-
post(api::server::request_passkey_recovery),
277
-
)
278
-
.route(
279
-
"/_account.recoverPasskeyAccount",
280
-
post(api::server::recover_passkey_account),
281
-
)
282
-
.route(
283
-
"/_account.updateDidDocument",
284
-
post(api::server::update_did_document),
285
-
)
286
-
.route(
287
-
"/_account.getDidDocument",
288
-
get(api::server::get_did_document),
289
-
)
290
-
.route(
291
-
"/com.atproto.server.requestEmailUpdate",
292
-
post(api::server::request_email_update),
293
-
)
294
-
.route(
295
-
"/_checkEmailVerified",
296
-
post(api::server::check_email_verified),
297
-
)
298
-
.route(
299
-
"/_checkChannelVerified",
300
-
post(api::server::check_channel_verified),
301
-
)
302
-
.route(
303
-
"/com.atproto.server.confirmEmail",
304
-
post(api::server::confirm_email),
305
-
)
306
-
.route(
307
-
"/com.atproto.server.updateEmail",
308
-
post(api::server::update_email),
309
-
)
310
-
.route(
311
-
"/_account.authorizeEmailUpdate",
312
-
get(api::server::authorize_email_update),
313
-
)
314
-
.route(
315
-
"/_account.checkEmailUpdateStatus",
316
-
get(api::server::check_email_update_status),
317
-
)
318
-
.route(
319
-
"/_account.checkEmailInUse",
320
-
post(api::server::check_email_in_use),
321
-
)
322
-
.route(
323
-
"/_account.checkCommsChannelInUse",
324
-
post(api::server::check_comms_channel_in_use),
325
-
)
326
-
.route(
327
-
"/com.atproto.server.reserveSigningKey",
328
-
post(api::server::reserve_signing_key),
329
-
)
330
-
.route(
331
-
"/com.atproto.server.verifyMigrationEmail",
332
-
post(api::server::verify_migration_email),
333
-
)
334
-
.route(
335
-
"/com.atproto.server.resendMigrationVerification",
336
-
post(api::server::resend_migration_verification),
337
-
)
338
-
.route(
339
-
"/com.atproto.identity.updateHandle",
340
-
post(api::identity::update_handle),
341
-
)
342
-
.route(
343
-
"/com.atproto.identity.requestPlcOperationSignature",
344
-
post(api::identity::request_plc_operation_signature),
345
-
)
346
-
.route(
347
-
"/com.atproto.identity.signPlcOperation",
348
-
post(api::identity::sign_plc_operation),
349
-
)
350
-
.route(
351
-
"/com.atproto.identity.submitPlcOperation",
352
-
post(api::identity::submit_plc_operation),
353
-
)
354
-
.route(
355
-
"/_identity.verifyHandleOwnership",
356
-
post(api::identity::verify_handle_ownership),
357
-
)
358
-
.route("/com.atproto.repo.importRepo", post(api::repo::import_repo))
359
-
.route(
360
-
"/com.atproto.admin.deleteAccount",
361
-
post(api::admin::delete_account),
362
-
)
363
-
.route(
364
-
"/com.atproto.admin.updateAccountEmail",
365
-
post(api::admin::update_account_email),
366
-
)
367
-
.route(
368
-
"/com.atproto.admin.updateAccountHandle",
369
-
post(api::admin::update_account_handle),
370
-
)
371
-
.route(
372
-
"/com.atproto.admin.updateAccountPassword",
373
-
post(api::admin::update_account_password),
374
-
)
375
-
.route(
376
-
"/com.atproto.server.listAppPasswords",
377
-
get(api::server::list_app_passwords),
378
-
)
379
-
.route(
380
-
"/com.atproto.server.createAppPassword",
381
-
post(api::server::create_app_password),
382
-
)
383
-
.route(
384
-
"/com.atproto.server.revokeAppPassword",
385
-
post(api::server::revoke_app_password),
386
-
)
387
-
.route(
388
-
"/com.atproto.server.createInviteCode",
389
-
post(api::server::create_invite_code),
390
-
)
391
-
.route(
392
-
"/com.atproto.server.createInviteCodes",
393
-
post(api::server::create_invite_codes),
394
-
)
395
-
.route(
396
-
"/com.atproto.server.getAccountInviteCodes",
397
-
get(api::server::get_account_invite_codes),
398
-
)
399
-
.route(
400
-
"/com.atproto.server.createTotpSecret",
401
-
post(api::server::create_totp_secret),
402
-
)
403
-
.route(
404
-
"/com.atproto.server.enableTotp",
405
-
post(api::server::enable_totp),
406
-
)
407
-
.route(
408
-
"/com.atproto.server.disableTotp",
409
-
post(api::server::disable_totp),
410
-
)
411
-
.route(
412
-
"/com.atproto.server.getTotpStatus",
413
-
get(api::server::get_totp_status),
414
-
)
415
-
.route(
416
-
"/com.atproto.server.regenerateBackupCodes",
417
-
post(api::server::regenerate_backup_codes),
418
-
)
419
-
.route(
420
-
"/com.atproto.server.startPasskeyRegistration",
421
-
post(api::server::start_passkey_registration),
422
-
)
423
-
.route(
424
-
"/com.atproto.server.finishPasskeyRegistration",
425
-
post(api::server::finish_passkey_registration),
426
-
)
427
-
.route(
428
-
"/com.atproto.server.listPasskeys",
429
-
get(api::server::list_passkeys),
430
-
)
431
-
.route(
432
-
"/com.atproto.server.deletePasskey",
433
-
post(api::server::delete_passkey),
434
-
)
435
-
.route(
436
-
"/com.atproto.server.updatePasskey",
437
-
post(api::server::update_passkey),
438
-
)
439
-
.route(
440
-
"/com.atproto.admin.getInviteCodes",
441
-
get(api::admin::get_invite_codes),
442
-
)
443
-
.route("/_admin.getServerStats", get(api::admin::get_server_stats))
444
-
.route("/_server.getConfig", get(api::admin::get_server_config))
445
-
.route(
446
-
"/_admin.updateServerConfig",
447
-
post(api::admin::update_server_config),
448
-
)
449
-
.route(
450
-
"/com.atproto.admin.disableAccountInvites",
451
-
post(api::admin::disable_account_invites),
452
-
)
453
-
.route(
454
-
"/com.atproto.admin.enableAccountInvites",
455
-
post(api::admin::enable_account_invites),
456
-
)
457
-
.route(
458
-
"/com.atproto.admin.disableInviteCodes",
459
-
post(api::admin::disable_invite_codes),
460
-
)
461
-
.route(
462
-
"/com.atproto.admin.getSubjectStatus",
463
-
get(api::admin::get_subject_status),
464
-
)
465
-
.route(
466
-
"/com.atproto.admin.updateSubjectStatus",
467
-
post(api::admin::update_subject_status),
468
-
)
469
-
.route("/com.atproto.admin.sendEmail", post(api::admin::send_email))
470
-
.route(
471
-
"/app.bsky.actor.getPreferences",
472
-
get(api::actor::get_preferences),
473
-
)
474
-
.route(
475
-
"/app.bsky.actor.putPreferences",
476
-
post(api::actor::put_preferences),
477
-
)
478
-
.route(
479
-
"/com.atproto.temp.checkSignupQueue",
480
-
get(api::temp::check_signup_queue),
481
-
)
482
-
.route(
483
-
"/com.atproto.temp.dereferenceScope",
484
-
post(api::temp::dereference_scope),
485
-
)
486
-
.route(
487
-
"/_account.getNotificationPrefs",
488
-
get(api::notification_prefs::get_notification_prefs),
489
-
)
490
-
.route(
491
-
"/_account.updateNotificationPrefs",
492
-
post(api::notification_prefs::update_notification_prefs),
493
-
)
494
-
.route(
495
-
"/_account.getNotificationHistory",
496
-
get(api::notification_prefs::get_notification_history),
497
-
)
498
-
.route(
499
-
"/_account.confirmChannelVerification",
500
-
post(api::verification::confirm_channel_verification),
501
-
)
502
-
.route("/_account.verifyToken", post(api::server::verify_token))
503
-
.route(
504
-
"/_delegation.listControllers",
505
-
get(api::delegation::list_controllers),
506
-
)
507
-
.route(
508
-
"/_delegation.addController",
509
-
post(api::delegation::add_controller),
510
-
)
511
-
.route(
512
-
"/_delegation.removeController",
513
-
post(api::delegation::remove_controller),
514
-
)
515
-
.route(
516
-
"/_delegation.updateControllerScopes",
517
-
post(api::delegation::update_controller_scopes),
518
-
)
519
-
.route(
520
-
"/_delegation.listControlledAccounts",
521
-
get(api::delegation::list_controlled_accounts),
522
-
)
523
-
.route(
524
-
"/_delegation.getAuditLog",
525
-
get(api::delegation::get_audit_log),
526
-
)
527
-
.route(
528
-
"/_delegation.getScopePresets",
529
-
get(api::delegation::get_scope_presets),
530
-
)
531
-
.route(
532
-
"/_delegation.createDelegatedAccount",
533
-
post(api::delegation::create_delegated_account),
534
-
)
535
-
.route("/_backup.listBackups", get(api::backup::list_backups))
536
-
.route("/_backup.getBackup", get(api::backup::get_backup))
537
-
.route("/_backup.createBackup", post(api::backup::create_backup))
538
-
.route("/_backup.deleteBackup", post(api::backup::delete_backup))
539
-
.route("/_backup.setEnabled", post(api::backup::set_backup_enabled))
540
-
.route("/_backup.exportBlobs", get(api::backup::export_blobs))
541
-
.route(
542
-
"/app.bsky.ageassurance.getState",
543
-
get(api::age_assurance::get_state),
544
-
)
545
-
.route(
546
-
"/app.bsky.unspecced.getAgeAssuranceState",
547
-
get(api::age_assurance::get_age_assurance_state),
548
-
)
79
+
app_with_routes(state, ExternalRoutes::default())
80
+
}
81
+
82
+
pub fn app_with_routes(state: AppState, external: ExternalRoutes) -> Router {
83
+
let xrpc_router = external.xrpc
549
84
.fallback(async || (
550
85
StatusCode::NOT_IMPLEMENTED,
551
86
Json(json!({"error": "MethodNotImplemented", "message": "Method not implemented. For app.bsky.* methods, include an atproto-proxy header specifying your AppView."})),
···
558
93
.with_state(state.clone()),
559
94
);
560
95
561
-
let oauth_router = Router::new()
562
-
.route("/jwks", get(oauth::endpoints::oauth_jwks))
563
-
.route("/par", post(oauth::endpoints::pushed_authorization_request))
564
-
.route("/authorize", get(oauth::endpoints::authorize_get))
565
-
.route("/authorize", post(oauth::endpoints::authorize_post))
566
-
.route(
567
-
"/authorize/accounts",
568
-
get(oauth::endpoints::authorize_accounts),
569
-
)
570
-
.route(
571
-
"/authorize/select",
572
-
post(oauth::endpoints::authorize_select),
573
-
)
574
-
.route("/authorize/2fa", get(oauth::endpoints::authorize_2fa_get))
575
-
.route("/authorize/2fa", post(oauth::endpoints::authorize_2fa_post))
576
-
.route(
577
-
"/authorize/passkey",
578
-
get(oauth::endpoints::authorize_passkey_start),
579
-
)
580
-
.route(
581
-
"/authorize/passkey",
582
-
post(oauth::endpoints::authorize_passkey_finish),
583
-
)
584
-
.route(
585
-
"/passkey/check",
586
-
get(oauth::endpoints::check_user_has_passkeys),
587
-
)
588
-
.route(
589
-
"/security-status",
590
-
get(oauth::endpoints::check_user_security_status),
591
-
)
592
-
.route("/passkey/start", post(oauth::endpoints::passkey_start))
593
-
.route("/passkey/finish", post(oauth::endpoints::passkey_finish))
594
-
.route("/authorize/deny", post(oauth::endpoints::authorize_deny))
595
-
.route(
596
-
"/register/complete",
597
-
post(oauth::endpoints::register_complete),
598
-
)
599
-
.route(
600
-
"/establish-session",
601
-
post(oauth::endpoints::establish_session),
602
-
)
603
-
.route("/authorize/consent", get(oauth::endpoints::consent_get))
604
-
.route("/authorize/consent", post(oauth::endpoints::consent_post))
605
-
.route("/authorize/renew", post(oauth::endpoints::authorize_renew))
606
-
.route(
607
-
"/authorize/redirect",
608
-
get(oauth::endpoints::authorize_redirect),
609
-
)
610
-
.route("/delegation/auth", post(oauth::endpoints::delegation_auth))
611
-
.route(
612
-
"/delegation/auth-token",
613
-
post(oauth::endpoints::delegation_auth_token),
614
-
)
615
-
.route(
616
-
"/delegation/totp",
617
-
post(oauth::endpoints::delegation_totp_verify),
618
-
)
619
-
.route("/token", post(oauth::endpoints::token_endpoint))
620
-
.route("/revoke", post(oauth::endpoints::revoke_token))
621
-
.route("/introspect", post(oauth::endpoints::introspect_token))
622
-
.route("/sso/providers", get(sso::endpoints::get_sso_providers))
623
-
.route("/sso/initiate", post(sso::endpoints::sso_initiate))
624
-
.route(
625
-
"/sso/callback",
626
-
get(sso::endpoints::sso_callback).post(sso::endpoints::sso_callback_post),
627
-
)
628
-
.route("/sso/linked", get(sso::endpoints::get_linked_accounts))
629
-
.route("/sso/unlink", post(sso::endpoints::unlink_account))
630
-
.route(
631
-
"/sso/pending-registration",
632
-
get(sso::endpoints::get_pending_registration),
633
-
)
634
-
.route(
635
-
"/sso/complete-registration",
636
-
post(sso::endpoints::complete_registration),
637
-
)
638
-
.route(
639
-
"/sso/check-handle-available",
640
-
get(sso::endpoints::check_handle_available),
641
-
)
642
-
.layer(middleware::from_fn(oauth::verify::dpop_nonce_middleware));
96
+
let oauth_router = external.oauth;
643
97
644
-
let well_known_router = Router::new()
645
-
.route("/did.json", get(api::identity::well_known_did))
646
-
.route("/atproto-did", get(api::identity::well_known_atproto_did))
647
-
.route(
648
-
"/oauth-protected-resource",
649
-
get(oauth::endpoints::oauth_protected_resource),
650
-
)
651
-
.route(
652
-
"/oauth-authorization-server",
653
-
get(oauth::endpoints::oauth_authorization_server),
654
-
);
98
+
let well_known_router = external.well_known;
655
99
656
100
let router = Router::new()
657
101
.nest_service("/xrpc", xrpc_service)
658
102
.nest("/oauth", oauth_router)
659
103
.nest("/.well-known", well_known_router)
660
104
.route("/metrics", get(metrics::metrics_handler))
661
-
.route("/health", get(api::server::health))
662
-
.route("/robots.txt", get(api::server::robots_txt))
663
-
.route("/favicon.ico", get(api::server::get_logo))
664
-
.route("/u/{handle}/did.json", get(api::identity::user_did_doc))
665
-
.route(
666
-
"/webhook/telegram",
667
-
post(api::telegram_webhook::handle_telegram_webhook)
668
-
.layer(DefaultBodyLimit::max(64 * 1024)),
669
-
)
670
-
.route(
671
-
"/webhook/discord",
672
-
post(api::discord_webhook::handle_discord_webhook)
673
-
.layer(DefaultBodyLimit::max(64 * 1024)),
674
-
)
105
+
.merge(external.extra)
675
106
.layer(DefaultBodyLimit::max(
676
107
tranquil_config::get().server.max_blob_size as usize,
677
108
))
···
717
148
let serve_dir = ServeDir::new(frontend_dir).not_found_service(ServeFile::new(&index_path));
718
149
719
150
return router
720
-
.route(
721
-
"/oauth-client-metadata.json",
722
-
get(oauth::endpoints::frontend_client_metadata),
723
-
)
724
151
.route_service("/", ServeFile::new(&homepage_file))
725
152
.nest("/app", spa_router)
726
153
.fallback_service(serve_dir);
-2
crates/tranquil-pds/src/oauth/mod.rs
-2
crates/tranquil-pds/src/oauth/mod.rs
crates/tranquil-pds/src/api/repo/record/utils.rs
crates/tranquil-pds/src/repo_ops.rs
crates/tranquil-pds/src/api/repo/record/utils.rs
crates/tranquil-pds/src/repo_ops.rs
-1
crates/tranquil-pds/src/sso/mod.rs
-1
crates/tranquil-pds/src/sso/mod.rs
+2
-2
crates/tranquil-pds/src/state.rs
+2
-2
crates/tranquil-pds/src/state.rs
···
8
8
use crate::repo_write_lock::RepoWriteLocks;
9
9
use crate::sso::{SsoConfig, SsoManager};
10
10
use crate::storage::{BackupStorage, BlobStorage, create_backup_storage, create_blob_storage};
11
-
use crate::sync::firehose::SequencedEvent;
11
+
use tranquil_db_traits::SequencedEvent;
12
12
use sqlx::PgPool;
13
13
use std::error::Error;
14
14
use std::sync::Arc;
···
240
240
.await,
241
241
) {
242
242
(true, Ok(Some(0))) => {
243
-
let code = crate::api::server::invite::gen_invite_code();
243
+
let code = crate::util::gen_invite_code();
244
244
tracing::info!(
245
245
"No users exist and invite codes are required. Bootstrap invite code: {}",
246
246
code
+4
-14
crates/tranquil-pds/src/sync/mod.rs
+4
-14
crates/tranquil-pds/src/sync/mod.rs
···
1
-
pub mod blob;
2
1
pub mod car;
3
-
pub mod commit;
4
-
pub mod crawl;
5
-
pub mod deprecated;
6
2
pub mod firehose;
7
3
pub mod frame;
8
4
pub mod import;
9
-
pub mod listener;
10
-
pub mod repo;
11
-
pub mod subscribe_repos;
12
5
pub mod util;
13
6
pub mod verify;
14
7
15
-
pub use blob::{get_blob, list_blobs};
16
-
pub use commit::{get_latest_commit, get_repo_status, list_repos};
17
-
pub use crawl::{notify_of_update, request_crawl};
18
-
pub use deprecated::{get_checkout, get_head};
19
-
pub use repo::{get_blocks, get_record, get_repo};
20
-
pub use subscribe_repos::subscribe_repos;
21
-
pub use tranquil_db_traits::AccountStatus;
8
+
#[cfg(test)]
9
+
mod verify_tests;
10
+
11
+
pub use firehose::SequencedEvent;
22
12
pub use util::{
23
13
RepoAccessLevel, RepoAccount, RepoAvailabilityError, assert_repo_availability,
24
14
get_account_with_status,
+2
-2
crates/tranquil-pds/src/sync/verify.rs
+2
-2
crates/tranquil-pds/src/sync/verify.rs
···
130
130
.ok_or(VerifyError::NoSigningKey)
131
131
}
132
132
133
-
async fn resolve_did_document(&self, did: &Did) -> Result<DidDocument<'static>, VerifyError> {
133
+
pub(crate) async fn resolve_did_document(&self, did: &Did) -> Result<DidDocument<'static>, VerifyError> {
134
134
let did_str = did.as_str();
135
135
if did_str.starts_with("did:plc:") {
136
136
self.resolve_plc_did(did_str).await
···
197
197
Ok(doc.into_static())
198
198
}
199
199
200
-
fn verify_mst_structure(
200
+
pub(crate) fn verify_mst_structure(
201
201
&self,
202
202
data_cid: &Cid,
203
203
blocks: &HashMap<Cid, Bytes>,
+1
-1
crates/tranquil-pds/src/sync/verify_tests.rs
+1
-1
crates/tranquil-pds/src/sync/verify_tests.rs
+21
crates/tranquil-pds/src/util.rs
+21
crates/tranquil-pds/src/util.rs
···
535
535
);
536
536
}
537
537
}
538
+
539
+
pub(crate) fn gen_invite_random_token() -> String {
540
+
let mut rng = rand::thread_rng();
541
+
let chars: Vec<char> = BASE32_ALPHABET.chars().collect();
542
+
let gen_segment = |rng: &mut rand::rngs::ThreadRng, len: usize| -> String {
543
+
(0..len)
544
+
.map(|_| chars[rng.gen_range(0..chars.len())])
545
+
.collect()
546
+
};
547
+
format!("{}-{}", gen_segment(&mut rng, 5), gen_segment(&mut rng, 5))
548
+
}
549
+
550
+
pub fn gen_invite_code() -> String {
551
+
let hostname = &tranquil_config::get().server.hostname;
552
+
let hostname_prefix = hostname.replace('.', "-");
553
+
format!("{}-{}", hostname_prefix, gen_invite_random_token())
554
+
}
555
+
556
+
pub fn is_self_hosted_did_web_enabled() -> bool {
557
+
tranquil_config::get().server.enable_pds_hosted_did_web
558
+
}
+1
-1
crates/tranquil-pds/tests/commit_signing.rs
+1
-1
crates/tranquil-pds/tests/commit_signing.rs
···
96
96
97
97
#[test]
98
98
fn test_create_signed_commit_helper() {
99
-
use tranquil_pds::api::repo::record::utils::create_signed_commit;
99
+
use tranquil_pds::repo_ops::create_signed_commit;
100
100
101
101
let signing_key = SigningKey::random(&mut rand::thread_rng());
102
102
let did: Did = "did:plc:testuser123456789abcdef"
+13
-2
crates/tranquil-pds/tests/common/mod.rs
+13
-2
crates/tranquil-pds/tests/common/mod.rs
···
563
563
if let Some((cache, distributed_rate_limiter)) = config.cache {
564
564
state = state.with_cache(cache, distributed_rate_limiter);
565
565
}
566
-
tranquil_pds::sync::listener::start_sequencer_listener(state.clone()).await;
567
-
let app = tranquil_pds::app(state);
566
+
tranquil_sync::listener::start_sequencer_listener(state.clone()).await;
567
+
let app = tranquil_pds::app_with_routes(
568
+
state,
569
+
tranquil_pds::ExternalRoutes {
570
+
xrpc: tranquil_api::api_routes().merge(tranquil_sync::sync_routes()),
571
+
oauth: tranquil_oauth_server::oauth_routes(),
572
+
well_known: tranquil_oauth_server::well_known_oauth_routes()
573
+
.merge(tranquil_api::well_known_api_routes()),
574
+
extra: tranquil_api::misc_routes()
575
+
.merge(tranquil_api::webhook_routes())
576
+
.merge(tranquil_oauth_server::frontend_client_metadata_route()),
577
+
},
578
+
);
568
579
tokio::spawn(async move {
569
580
axum::serve(listener, app).await.unwrap();
570
581
});
+28
crates/tranquil-server/Cargo.toml
+28
crates/tranquil-server/Cargo.toml
···
1
+
[package]
2
+
name = "tranquil-server"
3
+
version.workspace = true
4
+
edition.workspace = true
5
+
license.workspace = true
6
+
7
+
[dependencies]
8
+
tranquil-pds = { workspace = true }
9
+
tranquil-sync = { workspace = true }
10
+
tranquil-api = { workspace = true }
11
+
tranquil-oauth-server = { workspace = true }
12
+
tranquil-config = { workspace = true }
13
+
14
+
axum = { workspace = true }
15
+
clap = { workspace = true }
16
+
dotenvy = { workspace = true }
17
+
ed25519-dalek = { workspace = true }
18
+
hex = { workspace = true }
19
+
tokio = { workspace = true }
20
+
tokio-util = { workspace = true }
21
+
tracing = { workspace = true }
22
+
tracing-subscriber = { workspace = true }
23
+
24
+
[features]
25
+
default = ["frontend", "s3", "valkey"]
26
+
frontend = ["tranquil-pds/frontend"]
27
+
s3 = ["tranquil-pds/s3"]
28
+
valkey = ["tranquil-pds/valkey"]
+12
crates/tranquil-server/build.rs
+12
crates/tranquil-server/build.rs
···
1
+
use std::process::Command;
2
+
3
+
fn main() {
4
+
let timestamp = Command::new("date")
5
+
.arg("+%Y-%m-%d %H:%M:%S UTC")
6
+
.output()
7
+
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
8
+
.unwrap_or_else(|_| "unknown".to_string());
9
+
10
+
println!("cargo:rustc-env=BUILD_TIMESTAMP={}", timestamp);
11
+
println!("cargo:rerun-if-changed=build.rs");
12
+
}
+13
-9
crates/tranquil-pds/src/main.rs
crates/tranquil-server/src/main.rs
+13
-9
crates/tranquil-pds/src/main.rs
crates/tranquil-server/src/main.rs
···
18
18
#[derive(Parser)]
19
19
#[command(name = "tranquil-pds", version = BUILD_VERSION, about = "Tranquil AT Protocol PDS")]
20
20
struct Cli {
21
-
/// Path to a TOML configuration file (also settable via TRANQUIL_PDS_CONFIG env var)
22
21
#[arg(short, long, value_name = "FILE", env = "TRANQUIL_PDS_CONFIG")]
23
22
config: Option<PathBuf>,
24
23
···
28
27
29
28
#[derive(Subcommand)]
30
29
enum Command {
31
-
/// Validate the configuration and exit
32
30
Validate {
33
-
/// Skip validation of secrets and database URL (useful when secrets
34
-
/// are provided at runtime via environment variables / secret files)
35
31
#[arg(long)]
36
32
ignore_secrets: bool,
37
33
},
38
-
/// Print a TOML configuration template to stdout
39
34
ConfigTemplate,
40
35
}
41
36
···
45
40
46
41
let cli = Cli::parse();
47
42
48
-
// Handle subcommands that don't need full startup
49
43
if let Some(command) = &cli.command {
50
44
return match command {
51
45
Command::ConfigTemplate => {
···
116
110
spawn_signal_handler(shutdown.clone());
117
111
118
112
let state = AppState::new(shutdown.clone()).await?;
119
-
tranquil_pds::sync::listener::start_sequencer_listener(state.clone()).await;
113
+
tranquil_sync::listener::start_sequencer_listener(state.clone()).await;
120
114
121
115
let backfill_repo_repo = state.repo_repo.clone();
122
116
let backfill_block_store = state.block_store.clone();
···
189
183
}
190
184
191
185
if let Some(telegram_sender) = TelegramSender::from_config(cfg) {
192
-
// Safe to unwrap: validated in TranquilConfig::validate()
193
186
let secret_token = tranquil_config::get()
194
187
.telegram
195
188
.webhook_secret
···
262
255
shutdown.clone(),
263
256
));
264
257
265
-
let app = tranquil_pds::app(state);
258
+
let app = tranquil_pds::app_with_routes(
259
+
state,
260
+
tranquil_pds::ExternalRoutes {
261
+
xrpc: tranquil_api::api_routes().merge(tranquil_sync::sync_routes()),
262
+
oauth: tranquil_oauth_server::oauth_routes(),
263
+
well_known: tranquil_oauth_server::well_known_oauth_routes()
264
+
.merge(tranquil_api::well_known_api_routes()),
265
+
extra: tranquil_api::misc_routes()
266
+
.merge(tranquil_api::webhook_routes())
267
+
.merge(tranquil_oauth_server::frontend_client_metadata_route()),
268
+
},
269
+
);
266
270
267
271
let cfg = tranquil_config::get();
268
272
let host = &cfg.server.host;
+24
crates/tranquil-sync/Cargo.toml
+24
crates/tranquil-sync/Cargo.toml
···
1
+
[package]
2
+
name = "tranquil-sync"
3
+
version.workspace = true
4
+
edition.workspace = true
5
+
license.workspace = true
6
+
7
+
[dependencies]
8
+
tranquil-pds = { workspace = true }
9
+
tranquil-types = { workspace = true }
10
+
tranquil-config = { workspace = true }
11
+
tranquil-db-traits = { workspace = true }
12
+
13
+
anyhow = { workspace = true }
14
+
axum = { workspace = true }
15
+
bytes = { workspace = true }
16
+
chrono = { workspace = true }
17
+
cid = { workspace = true }
18
+
futures = { workspace = true }
19
+
ipld-core = { workspace = true }
20
+
jacquard-repo = { workspace = true }
21
+
serde = { workspace = true }
22
+
serde_ipld_dagcbor = { workspace = true }
23
+
tokio = { workspace = true }
24
+
tracing = { workspace = true }
+3
-3
crates/tranquil-pds/src/sync/blob.rs
crates/tranquil-sync/src/blob.rs
+3
-3
crates/tranquil-pds/src/sync/blob.rs
crates/tranquil-sync/src/blob.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::state::AppState;
3
-
use crate::sync::util::{RepoAccessLevel, assert_repo_availability};
1
+
use tranquil_pds::api::error::ApiError;
2
+
use tranquil_pds::state::AppState;
3
+
use tranquil_pds::sync::util::{RepoAccessLevel, assert_repo_availability};
4
4
use axum::{
5
5
Json,
6
6
body::Body,
+3
-3
crates/tranquil-pds/src/sync/commit.rs
crates/tranquil-sync/src/commit.rs
+3
-3
crates/tranquil-pds/src/sync/commit.rs
crates/tranquil-sync/src/commit.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::state::AppState;
3
-
use crate::sync::util::{RepoAccessLevel, assert_repo_availability, get_account_with_status};
1
+
use tranquil_pds::api::error::ApiError;
2
+
use tranquil_pds::state::AppState;
3
+
use tranquil_pds::sync::util::{RepoAccessLevel, assert_repo_availability, get_account_with_status};
4
4
use axum::{
5
5
Json,
6
6
extract::{Query, State},
+2
-2
crates/tranquil-pds/src/sync/crawl.rs
crates/tranquil-sync/src/crawl.rs
+2
-2
crates/tranquil-pds/src/sync/crawl.rs
crates/tranquil-sync/src/crawl.rs
+8
-8
crates/tranquil-pds/src/sync/deprecated.rs
crates/tranquil-sync/src/deprecated.rs
+8
-8
crates/tranquil-pds/src/sync/deprecated.rs
crates/tranquil-sync/src/deprecated.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::state::AppState;
3
-
use crate::sync::car::{encode_car_block, encode_car_header};
4
-
use crate::sync::util::{RepoAccessLevel, assert_repo_availability};
1
+
use tranquil_pds::api::error::ApiError;
2
+
use tranquil_pds::state::AppState;
3
+
use tranquil_pds::sync::car::{encode_car_block, encode_car_header};
4
+
use tranquil_pds::sync::util::{RepoAccessLevel, assert_repo_availability};
5
5
use axum::{
6
6
Json,
7
7
extract::{Query, State},
···
18
18
const MAX_REPO_BLOCKS_TRAVERSAL: usize = 20_000;
19
19
20
20
async fn check_admin_or_self(state: &AppState, headers: &HeaderMap, did: &Did) -> bool {
21
-
let extracted = match crate::auth::extract_auth_token_from_header(crate::util::get_header_str(
21
+
let extracted = match tranquil_pds::auth::extract_auth_token_from_header(tranquil_pds::util::get_header_str(
22
22
headers,
23
23
axum::http::header::AUTHORIZATION,
24
24
)) {
25
25
Some(t) => t,
26
26
None => return false,
27
27
};
28
-
let dpop_proof = crate::util::get_header_str(headers, crate::util::HEADER_DPOP);
28
+
let dpop_proof = tranquil_pds::util::get_header_str(headers, tranquil_pds::util::HEADER_DPOP);
29
29
let http_uri = "/";
30
-
match crate::auth::validate_token_with_dpop(
30
+
match tranquil_pds::auth::validate_token_with_dpop(
31
31
state.user_repo.as_ref(),
32
32
state.oauth_repo.as_ref(),
33
33
&extracted.token,
···
35
35
dpop_proof,
36
36
Method::GET.as_str(),
37
37
http_uri,
38
-
crate::auth::AccountRequirement::AnyStatus,
38
+
tranquil_pds::auth::AccountRequirement::AnyStatus,
39
39
)
40
40
.await
41
41
{
+38
crates/tranquil-sync/src/lib.rs
+38
crates/tranquil-sync/src/lib.rs
···
1
+
pub mod blob;
2
+
pub mod commit;
3
+
pub mod crawl;
4
+
pub mod deprecated;
5
+
pub mod listener;
6
+
pub mod repo;
7
+
pub mod subscribe_repos;
8
+
9
+
pub use blob::{get_blob, list_blobs};
10
+
pub use commit::{get_latest_commit, get_repo_status, list_repos};
11
+
pub use crawl::{notify_of_update, request_crawl};
12
+
pub use deprecated::{get_checkout, get_head};
13
+
pub use repo::{get_blocks, get_record, get_repo};
14
+
pub use subscribe_repos::subscribe_repos;
15
+
16
+
use tranquil_pds::state::AppState;
17
+
18
+
pub fn sync_routes() -> axum::Router<AppState> {
19
+
use axum::routing::{get, post};
20
+
21
+
axum::Router::new()
22
+
.route("/com.atproto.sync.getLatestCommit", get(get_latest_commit))
23
+
.route("/com.atproto.sync.listRepos", get(list_repos))
24
+
.route("/com.atproto.sync.getBlob", get(get_blob))
25
+
.route("/com.atproto.sync.listBlobs", get(list_blobs))
26
+
.route("/com.atproto.sync.getRepoStatus", get(get_repo_status))
27
+
.route("/com.atproto.sync.notifyOfUpdate", post(notify_of_update))
28
+
.route("/com.atproto.sync.requestCrawl", post(request_crawl))
29
+
.route("/com.atproto.sync.getBlocks", get(get_blocks))
30
+
.route("/com.atproto.sync.getRepo", get(get_repo))
31
+
.route("/com.atproto.sync.getRecord", get(get_record))
32
+
.route(
33
+
"/com.atproto.sync.subscribeRepos",
34
+
get(subscribe_repos),
35
+
)
36
+
.route("/com.atproto.sync.getHead", get(get_head))
37
+
.route("/com.atproto.sync.getCheckout", get(get_checkout))
38
+
}
+2
-2
crates/tranquil-pds/src/sync/listener.rs
crates/tranquil-sync/src/listener.rs
+2
-2
crates/tranquil-pds/src/sync/listener.rs
crates/tranquil-sync/src/listener.rs
···
1
-
use crate::state::AppState;
2
-
use crate::sync::firehose::SequencedEvent;
1
+
use tranquil_pds::state::AppState;
2
+
use tranquil_pds::sync::firehose::SequencedEvent;
3
3
use std::sync::atomic::{AtomicI64, Ordering};
4
4
use tracing::{debug, error, info, warn};
5
5
use tranquil_db_traits::SequenceNumber;
+8
-8
crates/tranquil-pds/src/sync/repo.rs
crates/tranquil-sync/src/repo.rs
+8
-8
crates/tranquil-pds/src/sync/repo.rs
crates/tranquil-sync/src/repo.rs
···
1
-
use crate::api::error::ApiError;
2
-
use crate::scheduled::generate_repo_car_from_user_blocks;
3
-
use crate::state::AppState;
4
-
use crate::sync::car::{encode_car_block, encode_car_header};
5
-
use crate::sync::util::{RepoAccessLevel, assert_repo_availability};
1
+
use tranquil_pds::api::error::ApiError;
2
+
use tranquil_pds::scheduled::generate_repo_car_from_user_blocks;
3
+
use tranquil_pds::state::AppState;
4
+
use tranquil_pds::sync::car::{encode_car_block, encode_car_header};
5
+
use tranquil_pds::sync::util::{RepoAccessLevel, assert_repo_availability};
6
6
use axum::{
7
7
extract::{Query, RawQuery, State},
8
8
http::StatusCode,
···
21
21
}
22
22
23
23
fn parse_get_blocks_query(query_string: &str) -> Result<GetBlocksParams, ApiError> {
24
-
let did_str = crate::util::parse_repeated_query_param(Some(query_string), "did")
24
+
let did_str = tranquil_pds::util::parse_repeated_query_param(Some(query_string), "did")
25
25
.into_iter()
26
26
.next()
27
27
.ok_or_else(|| ApiError::InvalidRequest("Missing required parameter: did".into()))?;
28
28
let did: Did = did_str
29
29
.parse()
30
30
.map_err(|_| ApiError::InvalidRequest("invalid did".into()))?;
31
-
let cids = crate::util::parse_repeated_query_param(Some(query_string), "cids");
31
+
let cids = tranquil_pds::util::parse_repeated_query_param(Some(query_string), "cids");
32
32
Ok(GetBlocksParams { did, cids })
33
33
}
34
34
···
90
90
.into_response();
91
91
}
92
92
93
-
let header = match crate::sync::car::encode_car_header_null_root() {
93
+
let header = match tranquil_pds::sync::car::encode_car_header_null_root() {
94
94
Ok(h) => h,
95
95
Err(e) => {
96
96
error!("Failed to encode CAR header: {}", e);
+9
-9
crates/tranquil-pds/src/sync/subscribe_repos.rs
crates/tranquil-sync/src/subscribe_repos.rs
+9
-9
crates/tranquil-pds/src/sync/subscribe_repos.rs
crates/tranquil-sync/src/subscribe_repos.rs
···
1
-
use crate::state::AppState;
2
-
use crate::sync::firehose::SequencedEvent;
3
-
use crate::sync::frame::{ErrorFrameName, InfoFrameName};
4
-
use crate::sync::util::{
1
+
use tranquil_pds::state::AppState;
2
+
use tranquil_pds::sync::firehose::SequencedEvent;
3
+
use tranquil_pds::sync::frame::{ErrorFrameName, InfoFrameName};
4
+
use tranquil_pds::sync::util::{
5
5
format_error_frame, format_event_for_sending, format_event_with_prefetched_blocks,
6
6
format_info_frame, prefetch_blocks_for_events,
7
7
};
···
50
50
51
51
async fn handle_socket(mut socket: WebSocket, state: AppState, params: SubscribeReposParams) {
52
52
let count = SUBSCRIBER_COUNT.fetch_add(1, Ordering::SeqCst) + 1;
53
-
crate::metrics::set_firehose_subscribers(count);
53
+
tranquil_pds::metrics::set_firehose_subscribers(count);
54
54
info!(cursor = ?params.cursor, subscribers = count, "New firehose subscriber");
55
55
let _ = handle_socket_inner(&mut socket, &state, params).await;
56
56
let count = SUBSCRIBER_COUNT.fetch_sub(1, Ordering::SeqCst) - 1;
57
-
crate::metrics::set_firehose_subscribers(count);
57
+
tranquil_pds::metrics::set_firehose_subscribers(count);
58
58
info!(subscribers = count, "Firehose subscriber disconnected");
59
59
}
60
60
···
157
157
warn!("Failed to send backfill event: {}", e);
158
158
return Err(());
159
159
}
160
-
crate::metrics::record_firehose_event();
160
+
tranquil_pds::metrics::record_firehose_event();
161
161
}
162
162
if i64::try_from(events_count).unwrap_or(i64::MAX) < BACKFILL_BATCH_SIZE {
163
163
break;
···
197
197
warn!("Failed to send cutover event: {}", e);
198
198
return Err(());
199
199
}
200
-
crate::metrics::record_firehose_event();
200
+
tranquil_pds::metrics::record_firehose_event();
201
201
}
202
202
}
203
203
}
···
215
215
warn!("Failed to send event: {}", e);
216
216
break;
217
217
}
218
-
crate::metrics::record_firehose_event();
218
+
tranquil_pds::metrics::record_firehose_event();
219
219
}
220
220
Err(RecvError::Lagged(skipped)) => {
221
221
warn!(skipped = skipped, "Firehose subscriber lagged behind");
+1
-1
default.nix
+1
-1
default.nix
+2
-2
justfile
+2
-2
justfile
+1
-1
scripts/install-debian.sh
+1
-1
scripts/install-debian.sh
···
317
317
318
318
log_info "Installing Tranquil PDS..."
319
319
id -u tranquil-pds &>/dev/null || useradd -r -s /sbin/nologin tranquil-pds
320
-
cp /opt/tranquil-pds/target/release/tranquil-pds /usr/local/bin/
320
+
cp /opt/tranquil-pds/target/release/tranquil-server /usr/local/bin/tranquil-pds
321
321
mkdir -p /var/lib/tranquil-pds
322
322
cp -r /opt/tranquil-pds/frontend/dist /var/lib/tranquil-pds/frontend
323
323
chown -R tranquil-pds:tranquil-pds /var/lib/tranquil-pds
+35
-15
test.nix
+35
-15
test.nix
···
17
17
enable = true;
18
18
database.createLocally = true;
19
19
20
-
nginx = {
21
-
enable = true;
22
-
enableACME = false;
23
-
};
24
-
25
20
settings = {
26
-
PDS_HOSTNAME = "pds.test";
27
-
SERVER_HOST = "0.0.0.0";
28
-
29
-
DISABLE_RATE_LIMITING = 1;
30
-
TRANQUIL_PDS_ALLOW_INSECURE_SECRETS = 1;
21
+
server.hostname = "pds.test";
22
+
server.host = "0.0.0.0";
23
+
server.disable_rate_limiting = true;
24
+
server.invite_code_required = false;
25
+
server.enable_pds_hosted_did_web = true;
26
+
27
+
secrets.jwt_secret = "test-jwt-secret-must-be-32-chars-long";
28
+
secrets.dpop_secret = "test-dpop-secret-must-be-32-chars-long";
29
+
secrets.master_key = "test-master-key-must-be-32-chars-long";
30
+
secrets.allow_insecure = true;
31
+
};
32
+
};
31
33
32
-
JWT_SECRET="test-jwt-secret-must-be-32-chars-long";
33
-
DPOP_SECRET="test-dpop-secret-must-be-32-chars-long";
34
-
MASTER_KEY="test-master-key-must-be-32-chars-long";
34
+
services.nginx = let
35
+
vhost = {
36
+
locations."/" = {
37
+
proxyPass = "http://127.0.0.1:3000";
38
+
proxyWebsockets = true;
39
+
extraConfig = ''
40
+
proxy_set_header Host $host;
41
+
proxy_set_header X-Forwarded-Proto $scheme;
42
+
proxy_read_timeout 120s;
43
+
'';
44
+
};
35
45
};
46
+
in {
47
+
enable = true;
48
+
recommendedProxySettings = true;
49
+
virtualHosts."pds.test" = vhost;
50
+
virtualHosts."*.pds.test" = vhost;
36
51
};
37
52
};
38
53
···
50
65
base = "http://localhost" if via == "nginx" else "http://localhost:3000"
51
66
url = f"{base}/xrpc/{endpoint}"
52
67
53
-
parts = ["curl", "-sf", "-X", method, host_header]
68
+
parts = ["curl", "-s", "-w", r"'\n%{http_code}'", "-X", method, host_header]
54
69
if headers:
55
70
parts.extend(f"-H '{k}: {v}'" for k, v in headers.items())
56
71
if data is not None:
···
60
75
parts.append(f"--data-binary @{raw_body}")
61
76
parts.append(f"'{url}'")
62
77
63
-
return server.succeed(" ".join(parts))
78
+
result = server.succeed(" ".join(parts))
79
+
lines = result.rsplit("\n", 1)
80
+
body = lines[0] if len(lines) > 1 else result
81
+
status = lines[1].strip() if len(lines) > 1 else "000"
82
+
assert status.startswith("2"), f"xrpc {endpoint} returned HTTP {status}: {body}"
83
+
return body
64
84
65
85
def xrpc_json(method, endpoint, **kwargs):
66
86
return json.loads(xrpc(method, endpoint, **kwargs))
History
1 round
0 comments
oyster.cafe
submitted
#0
1 commit
expand
collapse
fix: move code to more correct crates
expand 0 comments
pull request successfully merged