mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-08-31 13:17:05 +00:00
Proxying requests
This commit is contained in:
@@ -15,3 +15,4 @@ OBJECT_STORAGE_FORCE_PATH_STYLE=false
|
||||
JWT_SECRET=your-super-secret-jwt-key-please-change-me
|
||||
PDS_HOSTNAME=localhost:3000 # The public-facing hostname of the PDS
|
||||
PLC_URL=plc.directory
|
||||
APPVIEW_URL=https://api.bsky.app
|
||||
|
||||
Generated
+516
-2
@@ -90,6 +90,22 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
|
||||
|
||||
[[package]]
|
||||
name = "astral-tokio-tar"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec179a06c1769b1e42e1e2cbe74c7dcdb3d6383c838454d063eaac5bbb7ebbe5"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"futures-core",
|
||||
"libc",
|
||||
"portable-atomic",
|
||||
"rustc-hash",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
version = "0.4.34"
|
||||
@@ -103,6 +119,28 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stream"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
|
||||
dependencies = [
|
||||
"async-stream-impl",
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stream-impl"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.89"
|
||||
@@ -215,6 +253,12 @@ version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
@@ -268,6 +312,83 @@ dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bollard"
|
||||
version = "0.19.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87a52479c9237eb04047ddb94788c41ca0d26eaff8b697ecfbb4c32f7fdc3b1b"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"base64 0.22.1",
|
||||
"bitflags",
|
||||
"bollard-buildkit-proto",
|
||||
"bollard-stubs",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"home",
|
||||
"http",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-named-pipe",
|
||||
"hyper-rustls",
|
||||
"hyper-util",
|
||||
"hyperlocal",
|
||||
"log",
|
||||
"num",
|
||||
"pin-project-lite",
|
||||
"rand 0.9.2",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"rustls-pemfile",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"serde_urlencoded",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-util",
|
||||
"tonic",
|
||||
"tower-service",
|
||||
"url",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bollard-buildkit-proto"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85a885520bf6249ab931a764ffdb87b0ceef48e6e7d807cfdb21b751e086e1ad"
|
||||
dependencies = [
|
||||
"prost",
|
||||
"prost-types",
|
||||
"tonic",
|
||||
"tonic-prost",
|
||||
"ureq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bollard-stubs"
|
||||
version = "1.49.1-rc.28.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5731fe885755e92beff1950774068e0cae67ea6ec7587381536fca84f1779623"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bollard-buildkit-proto",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"prost",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"serde_with",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bon"
|
||||
version = "3.8.1"
|
||||
@@ -345,6 +466,8 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sqlx",
|
||||
"testcontainers",
|
||||
"testcontainers-modules",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
@@ -934,6 +1057,17 @@ dependencies = [
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docker_credential"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d89dfcba45b4afad7450a99b39e751590463e45c04728cf555d36bb66940de8"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
@@ -1072,6 +1206,16 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "etcetera"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "5.4.1"
|
||||
@@ -1089,6 +1233,17 @@ version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "ferroid"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0e9414a6ae93ef993ce40a1e02944f13d4508e2bf6f1ced1580ce6910f08253"
|
||||
dependencies = [
|
||||
"portable-atomic",
|
||||
"rand 0.9.2",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.13.1"
|
||||
@@ -1674,6 +1829,21 @@ dependencies = [
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-named-pipe"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.7"
|
||||
@@ -1691,6 +1861,19 @@ dependencies = [
|
||||
"webpki-roots 1.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-timeout"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
|
||||
dependencies = [
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.6.0"
|
||||
@@ -1733,6 +1916,21 @@ dependencies = [
|
||||
"windows-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyperlocal"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.64"
|
||||
@@ -1970,6 +2168,15 @@ dependencies = [
|
||||
"unsigned-varint 0.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
@@ -2662,7 +2869,7 @@ dependencies = [
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework 2.11.1",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
@@ -2698,6 +2905,20 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
@@ -2724,6 +2945,15 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
@@ -2750,6 +2980,17 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
@@ -2931,6 +3172,31 @@ dependencies = [
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse-display"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a"
|
||||
dependencies = [
|
||||
"parse-display-derive",
|
||||
"regex",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse-display-derive"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ae7800a4c974efd12df917266338e79a7a74415173caf7e70aa0a0707345281"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"regex-syntax",
|
||||
"structmeta",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "3.0.6"
|
||||
@@ -3053,6 +3319,12 @@ version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.4"
|
||||
@@ -3148,6 +3420,38 @@ dependencies = [
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-types"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72"
|
||||
dependencies = [
|
||||
"prost",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.9.6"
|
||||
@@ -3520,6 +3824,7 @@ version = "0.23.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
@@ -3528,6 +3833,27 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pki-types",
|
||||
"schannel",
|
||||
"security-framework 3.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.13.1"
|
||||
@@ -3648,6 +3974,19 @@ dependencies = [
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "3.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation 0.10.1",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.15.0"
|
||||
@@ -3860,6 +4199,15 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.2.0"
|
||||
@@ -4125,7 +4473,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"crc",
|
||||
"dotenvy",
|
||||
"etcetera",
|
||||
"etcetera 0.8.0",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
@@ -4251,6 +4599,29 @@ version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "structmeta"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e1575d8d40908d70f6fd05537266b90ae71b15dbbe7a8b7dffa2b759306d329"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"structmeta-derive",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "structmeta-derive"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
@@ -4350,6 +4721,45 @@ dependencies = [
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "testcontainers"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a347cac4368ba4f1871743adb27dc14829024d26b1763572404726b0b9943eb8"
|
||||
dependencies = [
|
||||
"astral-tokio-tar",
|
||||
"async-trait",
|
||||
"bollard",
|
||||
"bytes",
|
||||
"docker_credential",
|
||||
"either",
|
||||
"etcetera 0.11.0",
|
||||
"ferroid",
|
||||
"futures",
|
||||
"itertools",
|
||||
"log",
|
||||
"memchr",
|
||||
"parse-display",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-util",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "testcontainers-modules"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e75e78ff453128a2c7da9a5d5a3325ea34ea214d4bf51eab3417de23a4e5147"
|
||||
dependencies = [
|
||||
"testcontainers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
@@ -4488,6 +4898,7 @@ dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2 0.6.1",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.2",
|
||||
@@ -4549,6 +4960,46 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tonic"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb7613188ce9f7df5bfe185db26c5814347d110db17920415cf2fbcad85e7203"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-timeout",
|
||||
"hyper-util",
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
"socket2 0.6.1",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tonic-prost"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66bd50ad6ce1252d87ef024b3d64fe4c3cf54a86fb9ef4c631fdd0ded7aeaa67"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost",
|
||||
"tonic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.2"
|
||||
@@ -4557,9 +5008,12 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"indexmap 2.12.1",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
@@ -4765,6 +5219,34 @@ version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "3.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"log",
|
||||
"percent-encoding",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"ureq-proto",
|
||||
"utf-8",
|
||||
"webpki-roots 1.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ureq-proto"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.7"
|
||||
@@ -5018,6 +5500,22 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
@@ -5027,6 +5525,12 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.61.3"
|
||||
@@ -5496,6 +6000,16 @@ version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xml5ever"
|
||||
version = "0.18.1"
|
||||
|
||||
@@ -26,3 +26,7 @@ tokio = { version = "1.48.0", features = ["macros", "rt-multi-thread", "time"] }
|
||||
tracing = "0.1.43"
|
||||
tracing-subscriber = "0.3.22"
|
||||
uuid = { version = "1.19.0", features = ["v4", "fast-rng"] }
|
||||
|
||||
[dev-dependencies]
|
||||
testcontainers = "0.26.0"
|
||||
testcontainers-modules = { version = "0.14.0", features = ["postgres"] }
|
||||
|
||||
@@ -1,108 +1,81 @@
|
||||
# Implementation TODOs
|
||||
# PDS Implementation TODOs
|
||||
|
||||
Lewis' special big boy todofile
|
||||
Lewis' corrected big boy todofile
|
||||
|
||||
## 1. Server Infrastructure & Health
|
||||
## 1. Server Infrastructure & Proxying
|
||||
- [x] Health Check
|
||||
- [x] Implement `GET /health` endpoint (returns "OK").
|
||||
- [x] Server Description
|
||||
- [x] Implement `com.atproto.server.describeServer` (returns available user domains).
|
||||
- [x] XRPC Proxying
|
||||
- [x] Implement strict forwarding for all `app.bsky.*` and `chat.bsky.*` requests to an appview.
|
||||
- [x] Forward Auth headers correctly.
|
||||
- [x] Handle AppView errors/timeouts gracefully.
|
||||
|
||||
## 2. Authentication & Account Management (`com.atproto.server`)
|
||||
- [x] Account Creation
|
||||
- [x] Implement `com.atproto.server.createAccount`.
|
||||
- [x] Validate handle format (reject invalid characters).
|
||||
- [x] Create DID for new user.
|
||||
- [x] Initialize user repository.
|
||||
- [x] Create DID for new user (PLC directory).
|
||||
- [x] Initialize user repository (Root commit).
|
||||
- [x] Return access JWT and DID.
|
||||
- [x] MST stuff I think...
|
||||
|
||||
- [ ] Create DID for new user (did:web).
|
||||
- [x] Session Management
|
||||
- [x] Implement `com.atproto.server.createSession` (Login).
|
||||
- [x] Validate identifier (handle/email) and password.
|
||||
- [x] Return access JWT, refresh JWT, and DID.
|
||||
- [x] Implement `com.atproto.server.getSession`.
|
||||
- [x] Verify JWT validity.
|
||||
- [x] Implement `com.atproto.server.refreshSession`.
|
||||
- [x] Implement `com.atproto.server.deleteSession` (Logout).
|
||||
- [x] Invalidate current session/token.
|
||||
|
||||
## 3. Repository Operations (`com.atproto.repo`)
|
||||
- [ ] Record CRUD
|
||||
- [ ] Implement `com.atproto.repo.createRecord`.
|
||||
- [ ] Generate `rkey` if not provided.
|
||||
- [ ] Validate schema against Lexicon.
|
||||
- [ ] Handle `swapCommit` for optimistic locking.
|
||||
- [ ] Validate schema against Lexicon (just structure, not complex logic).
|
||||
- [ ] Generate `rkey` (TID) if not provided.
|
||||
- [ ] Handle MST (Merkle Search Tree) insertion.
|
||||
- [ ] **Trigger Firehose Event**.
|
||||
- [ ] Implement `com.atproto.repo.putRecord`.
|
||||
- [ ] Handle create vs update logic.
|
||||
- [ ] Validate `repo` matches authenticated user.
|
||||
- [ ] Validate record schema (e.g., missing required fields).
|
||||
- [ ] Implement `com.atproto.repo.getRecord`.
|
||||
- [ ] Handle missing params (400 Bad Request).
|
||||
- [ ] Handle non-existent record (404 Not Found).
|
||||
- [ ] Implement `com.atproto.repo.deleteRecord`.
|
||||
- [ ] Implement `com.atproto.repo.listRecords`.
|
||||
- [ ] Support pagination (`limit`, `cursor`).
|
||||
- [ ] Implement `com.atproto.repo.describeRepo`.
|
||||
- [ ] Blob Management
|
||||
- [ ] Implement `com.atproto.repo.uploadBlob`.
|
||||
- [ ] Enforce authentication.
|
||||
- [ ] Validate MIME types (reject unsupported).
|
||||
- [ ] Return blob reference (`$link`).
|
||||
- [ ] Repo Meta
|
||||
- [ ] Implement `com.atproto.repo.describeRepo`.
|
||||
- [ ] Store blob (S3).
|
||||
- [ ] return `blob` ref (CID + MimeType).
|
||||
|
||||
## 4. Actor & Profile (`app.bsky.actor`)
|
||||
- [ ] Profile Management
|
||||
- [ ] Implement `app.bsky.actor.getProfile`.
|
||||
- [ ] Resolve handle to DID.
|
||||
- [ ] Return profile record data.
|
||||
- [ ] Discovery
|
||||
- [ ] Implement `app.bsky.actor.searchActors`.
|
||||
## 4. Sync & Federation (`com.atproto.sync`)
|
||||
- [ ] The Firehose (WebSocket)
|
||||
- [ ] Implement `com.atproto.sync.subscribeRepos`.
|
||||
- [ ] Broadcast real-time commit events.
|
||||
- [ ] Handle cursor replay (backfill).
|
||||
- [ ] Bulk Export
|
||||
- [ ] Implement `com.atproto.sync.getRepo` (Return full CAR file of repo).
|
||||
- [ ] Implement `com.atproto.sync.getBlocks` (Return specific blocks via CIDs).
|
||||
- [ ] Implement `com.atproto.sync.getLatestCommit`.
|
||||
- [ ] Implement `com.atproto.sync.getRecord` (Sync version, distinct from repo.getRecord).
|
||||
- [ ] Blob Sync
|
||||
- [ ] Implement `com.atproto.sync.getBlob`.
|
||||
- [ ] Implement `com.atproto.sync.listBlobs`.
|
||||
- [ ] Crawler Interaction
|
||||
- [ ] Implement `com.atproto.sync.requestCrawl` (Notify relays to index us).
|
||||
|
||||
## 5. Feed & Timeline (`app.bsky.feed`)
|
||||
- [ ] Feed Retrieval
|
||||
- [ ] Implement `app.bsky.feed.getTimeline`.
|
||||
- [ ] Implement `app.bsky.feed.getAuthorFeed`.
|
||||
- [ ] Filter by actor.
|
||||
- [ ] Respect mutes (if viewer is authenticated).
|
||||
- [ ] Implement `app.bsky.feed.getPostThread`.
|
||||
- [ ] Construct thread tree (parents, replies).
|
||||
- [ ] Handle deleted posts (return `notFoundPost` view).
|
||||
- [ ] Record Types
|
||||
- [ ] Support `app.bsky.feed.post` record type.
|
||||
- [ ] Support `app.bsky.feed.like` record type.
|
||||
- [ ] Support `app.bsky.embed.images` in posts.
|
||||
|
||||
## 6. Social Graph (`app.bsky.graph`)
|
||||
- [ ] Relationships
|
||||
- [ ] Implement `app.bsky.graph.getFollows`.
|
||||
- [ ] Implement `app.bsky.graph.getFollowers`.
|
||||
- [ ] Implement `app.bsky.graph.getMutes`.
|
||||
- [ ] Implement `app.bsky.graph.getBlocks`.
|
||||
- [ ] Record Types
|
||||
- [ ] Support `app.bsky.graph.follow` record type.
|
||||
- [ ] Support `app.bsky.graph.mute` record type.
|
||||
|
||||
## 7. Notifications (`app.bsky.notification`)
|
||||
- [ ] Notification Management
|
||||
- [ ] Implement `app.bsky.notification.listNotifications`.
|
||||
- [ ] Aggregate notifications (likes, follows, replies).
|
||||
- [ ] Implement `app.bsky.notification.getUnreadCount`.
|
||||
- [ ] Track read state.
|
||||
- [ ] Reset count on list/read.
|
||||
|
||||
## 8. Identity (`com.atproto.identity`)
|
||||
## 5. Identity (`com.atproto.identity`)
|
||||
- [ ] Resolution
|
||||
- [ ] Implement `com.atproto.identity.resolveHandle`.
|
||||
- [ ] Implement `com.atproto.identity.resolveHandle` (Can be internal or proxy to PLC).
|
||||
- [ ] Implement `/.well-known/did.json` (Depends on supporting did:web).
|
||||
|
||||
## 9. Sync & Federation (`com.atproto.sync`)
|
||||
- [ ] Data Export
|
||||
- [ ] Implement `com.atproto.sync.getRepo` (Export CAR file).
|
||||
- [ ] Implement `com.atproto.sync.getBlocks`.
|
||||
## 6. Record Schema Validation
|
||||
- [ ] `app.bsky.feed.post`
|
||||
- [ ] `app.bsky.feed.like`
|
||||
- [ ] `app.bsky.feed.repost`
|
||||
- [ ] `app.bsky.graph.follow`
|
||||
- [ ] `app.bsky.graph.block`
|
||||
- [ ] `app.bsky.actor.profile`
|
||||
- [ ] Other app(view) validation too!!!
|
||||
|
||||
## 10. General Requirements
|
||||
## 7. General Requirements
|
||||
- [ ] IPLD & MST
|
||||
- [ ] Implement Merkle Search Tree (MST) logic for repo signing.
|
||||
- [ ] Implement CAR (Content Addressable Archives) encoding/decoding.
|
||||
- [ ] Validation
|
||||
- [ ] Ensure all endpoints validate input parameters.
|
||||
- [ ] Ensure proper error codes (400, 401, 404, 409).
|
||||
- [ ] Concurrency
|
||||
- [ ] Ensure thread safety for repo updates.
|
||||
- [ ] DID PLC Operations (Sign rotation keys).
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Run all tests with correct threading models
|
||||
test: test-proxy test-lifecycle test-others
|
||||
|
||||
# Proxy tests modify environment variables, so must run single-threaded
|
||||
# TODO: figure out how to run in parallel
|
||||
test-proxy:
|
||||
cargo test --test proxy -- --test-threads=1
|
||||
|
||||
# Lifecycle tests involve complex state mutations, run single-threaded to be safe
|
||||
# TODO: figure out how to run in parallel
|
||||
test-lifecycle:
|
||||
cargo test --test lifecycle -- --test-threads=1
|
||||
|
||||
test-others:
|
||||
cargo test --lib
|
||||
cargo test --test actor
|
||||
cargo test --test feed
|
||||
cargo test --test graph
|
||||
cargo test --test identity
|
||||
cargo test --test notification
|
||||
cargo test --test repo
|
||||
cargo test --test server
|
||||
cargo test --test sync
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod server;
|
||||
pub mod repo;
|
||||
pub mod proxy;
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
use axum::{
|
||||
extract::{Path, Query},
|
||||
http::{HeaderMap, Method, StatusCode},
|
||||
response::{IntoResponse, Response},
|
||||
body::Bytes,
|
||||
};
|
||||
use reqwest::Client;
|
||||
use tracing::{info, error};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub async fn proxy_handler(
|
||||
Path(method): Path<String>,
|
||||
method_verb: Method,
|
||||
headers: HeaderMap,
|
||||
Query(params): Query<HashMap<String, String>>,
|
||||
body: Bytes,
|
||||
) -> Response {
|
||||
|
||||
let proxy_header = headers.get("atproto-proxy")
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
let appview_url = match proxy_header {
|
||||
Some(url) => url,
|
||||
None => match std::env::var("APPVIEW_URL") {
|
||||
Ok(url) => url,
|
||||
Err(_) => return (StatusCode::BAD_GATEWAY, "No upstream AppView configured").into_response(),
|
||||
},
|
||||
};
|
||||
|
||||
let target_url = format!("{}/xrpc/{}", appview_url, method);
|
||||
|
||||
info!("Proxying {} request to {}", method_verb, target_url);
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
let mut request_builder = client
|
||||
.request(method_verb, &target_url)
|
||||
.query(¶ms);
|
||||
|
||||
for (key, value) in headers.iter() {
|
||||
if key != "host" && key != "content-length" {
|
||||
request_builder = request_builder.header(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
request_builder = request_builder.body(body);
|
||||
|
||||
match request_builder.send().await {
|
||||
Ok(resp) => {
|
||||
let status = resp.status();
|
||||
let headers = resp.headers().clone();
|
||||
let body = match resp.bytes().await {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
error!("Error reading proxy response body: {:?}", e);
|
||||
return (StatusCode::BAD_GATEWAY, "Error reading upstream response").into_response();
|
||||
}
|
||||
};
|
||||
|
||||
let mut response_builder = Response::builder().status(status);
|
||||
|
||||
for (key, value) in headers.iter() {
|
||||
response_builder = response_builder.header(key, value);
|
||||
}
|
||||
|
||||
match response_builder.body(axum::body::Body::from(body)) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
error!("Error building proxy response: {:?}", e);
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error").into_response()
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Error sending proxy request: {:?}", e);
|
||||
if e.is_timeout() {
|
||||
(StatusCode::GATEWAY_TIMEOUT, "Upstream Timeout").into_response()
|
||||
} else {
|
||||
(StatusCode::BAD_GATEWAY, "Upstream Error").into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,25 @@ use jacquard_repo::{mst::Mst, commit::Commit, storage::BlockStore};
|
||||
use jacquard::types::{string::Tid, did::Did, integer::LimitedU32};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub async fn describe_server() -> impl IntoResponse {
|
||||
let domains_str = std::env::var("AVAILABLE_USER_DOMAINS").unwrap_or_else(|_| "example.com".to_string());
|
||||
let domains: Vec<&str> = domains_str.split(',').map(|s| s.trim()).collect();
|
||||
|
||||
Json(json!({
|
||||
"availableUserDomains": domains
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn health(State(state): State<AppState>) -> impl IntoResponse {
|
||||
match sqlx::query("SELECT 1").execute(&state.db).await {
|
||||
Ok(_) => (StatusCode::OK, "OK"),
|
||||
Err(e) => {
|
||||
error!("Health check failed: {:?}", e);
|
||||
(StatusCode::SERVICE_UNAVAILABLE, "Service Unavailable")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CreateAccountInput {
|
||||
pub handle: String,
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
pub mod api;
|
||||
pub mod state;
|
||||
pub mod auth;
|
||||
pub mod repo;
|
||||
|
||||
use axum::{
|
||||
routing::{get, post, any},
|
||||
Router,
|
||||
};
|
||||
use state::AppState;
|
||||
|
||||
pub fn app(state: AppState) -> Router {
|
||||
Router::new()
|
||||
.route("/health", get(api::server::health))
|
||||
.route("/xrpc/com.atproto.server.describeServer", get(api::server::describe_server))
|
||||
.route("/xrpc/com.atproto.server.createAccount", post(api::server::create_account))
|
||||
.route("/xrpc/com.atproto.server.createSession", post(api::server::create_session))
|
||||
.route("/xrpc/com.atproto.server.getSession", get(api::server::get_session))
|
||||
.route("/xrpc/com.atproto.server.deleteSession", post(api::server::delete_session))
|
||||
.route("/xrpc/com.atproto.server.refreshSession", post(api::server::refresh_session))
|
||||
.route("/xrpc/com.atproto.repo.createRecord", post(api::repo::create_record))
|
||||
.route("/xrpc/{*method}", any(api::proxy::proxy_handler))
|
||||
.with_state(state)
|
||||
}
|
||||
+3
-45
@@ -1,20 +1,6 @@
|
||||
mod api;
|
||||
mod state;
|
||||
mod auth;
|
||||
mod repo;
|
||||
|
||||
use axum::{
|
||||
extract::State,
|
||||
routing::{get, post},
|
||||
Router,
|
||||
Json,
|
||||
response::IntoResponse,
|
||||
http::StatusCode,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::net::SocketAddr;
|
||||
use state::AppState;
|
||||
use tracing::{info, error};
|
||||
use bspds::state::AppState;
|
||||
use tracing::info;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
@@ -36,38 +22,10 @@ async fn main() {
|
||||
|
||||
let state = AppState::new(pool);
|
||||
|
||||
let app = Router::new()
|
||||
.route("/health", get(health))
|
||||
.route("/xrpc/com.atproto.server.describeServer", get(describe_server))
|
||||
.route("/xrpc/com.atproto.server.createAccount", post(api::server::create_account))
|
||||
.route("/xrpc/com.atproto.server.createSession", post(api::server::create_session))
|
||||
.route("/xrpc/com.atproto.server.getSession", get(api::server::get_session))
|
||||
.route("/xrpc/com.atproto.server.deleteSession", post(api::server::delete_session))
|
||||
.route("/xrpc/com.atproto.server.refreshSession", post(api::server::refresh_session))
|
||||
.route("/xrpc/com.atproto.repo.createRecord", post(api::repo::create_record))
|
||||
.with_state(state);
|
||||
let app = bspds::app(state);
|
||||
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
||||
info!("listening on {}", addr);
|
||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
}
|
||||
|
||||
async fn health(State(state): State<AppState>) -> impl IntoResponse {
|
||||
match sqlx::query("SELECT 1").execute(&state.db).await {
|
||||
Ok(_) => (StatusCode::OK, "OK"),
|
||||
Err(e) => {
|
||||
error!("Health check failed: {:?}", e);
|
||||
(StatusCode::SERVICE_UNAVAILABLE, "Service Unavailable")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn describe_server() -> impl IntoResponse {
|
||||
let domains_str = std::env::var("AVAILABLE_USER_DOMAINS").unwrap_or_else(|_| "example.com".to_string());
|
||||
let domains: Vec<&str> = domains_str.split(',').map(|s| s.trim()).collect();
|
||||
|
||||
Json(json!({
|
||||
"availableUserDomains": domains
|
||||
}))
|
||||
}
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ async fn test_get_profile() {
|
||||
let params = [
|
||||
("actor", AUTH_DID),
|
||||
];
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.actor.getProfile", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.actor.getProfile", base_url().await))
|
||||
.query(¶ms)
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
@@ -25,7 +25,7 @@ async fn test_search_actors() {
|
||||
("q", "test"),
|
||||
("limit", "10"),
|
||||
];
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.actor.searchActors", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.actor.searchActors", base_url().await))
|
||||
.query(¶ms)
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
|
||||
+70
-4
@@ -5,8 +5,16 @@ use chrono::Utc;
|
||||
use std::collections::HashMap;
|
||||
#[allow(unused_imports)]
|
||||
use std::time::Duration;
|
||||
use std::sync::OnceLock;
|
||||
use bspds::state::AppState;
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
use tokio::net::TcpListener;
|
||||
use testcontainers::{runners::AsyncRunner, ContainerAsync, ImageExt};
|
||||
use testcontainers_modules::postgres::Postgres;
|
||||
|
||||
static SERVER_URL: OnceLock<String> = OnceLock::new();
|
||||
static DB_CONTAINER: OnceLock<ContainerAsync<Postgres>> = OnceLock::new();
|
||||
|
||||
pub const BASE_URL: &str = "http://127.0.0.1:3000";
|
||||
#[allow(dead_code)]
|
||||
pub const AUTH_TOKEN: &str = "test-token";
|
||||
#[allow(dead_code)]
|
||||
@@ -20,9 +28,67 @@ pub fn client() -> Client {
|
||||
Client::new()
|
||||
}
|
||||
|
||||
pub async fn base_url() -> &'static str {
|
||||
SERVER_URL.get_or_init(|| {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
if std::env::var("DOCKER_HOST").is_err() {
|
||||
if let Ok(runtime_dir) = std::env::var("XDG_RUNTIME_DIR") {
|
||||
let podman_sock = std::path::Path::new(&runtime_dir).join("podman/podman.sock");
|
||||
if podman_sock.exists() {
|
||||
unsafe { std::env::set_var("DOCKER_HOST", format!("unix://{}", podman_sock.display())); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
rt.block_on(async move {
|
||||
let container = Postgres::default().with_tag("18-alpine").start().await.expect("Failed to start Postgres");
|
||||
let connection_string = format!(
|
||||
"postgres://postgres:postgres@127.0.0.1:{}/postgres",
|
||||
container.get_host_port_ipv4(5432).await.expect("Failed to get port")
|
||||
);
|
||||
|
||||
DB_CONTAINER.set(container).ok();
|
||||
|
||||
let url = spawn_app(connection_string).await;
|
||||
tx.send(url).unwrap();
|
||||
std::future::pending::<()>().await;
|
||||
});
|
||||
});
|
||||
|
||||
rx.recv().expect("Failed to start test server")
|
||||
})
|
||||
}
|
||||
|
||||
async fn spawn_app(database_url: String) -> String {
|
||||
let pool = PgPoolOptions::new()
|
||||
.connect(&database_url)
|
||||
.await
|
||||
.expect("Failed to connect to Postgres. Make sure the database is running.");
|
||||
|
||||
sqlx::migrate!("./migrations")
|
||||
.run(&pool)
|
||||
.await
|
||||
.expect("Failed to run migrations");
|
||||
|
||||
let state = AppState::new(pool);
|
||||
let app = bspds::app(state);
|
||||
|
||||
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
|
||||
tokio::spawn(async move {
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
});
|
||||
|
||||
format!("http://{}", addr)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn upload_test_blob(client: &Client, data: &'static str, mime: &'static str) -> Value {
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", base_url().await))
|
||||
.header(header::CONTENT_TYPE, mime)
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.body(data)
|
||||
@@ -59,7 +125,7 @@ pub async fn create_test_post(
|
||||
"record": record
|
||||
});
|
||||
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", base_url().await))
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.json(&payload)
|
||||
.send()
|
||||
@@ -84,7 +150,7 @@ pub async fn create_account_and_login(client: &Client) -> (String, String) {
|
||||
"password": "password"
|
||||
});
|
||||
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.server.createAccount", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.server.createAccount", base_url().await))
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await
|
||||
|
||||
+3
-3
@@ -8,7 +8,7 @@ use std::collections::HashMap;
|
||||
async fn test_get_timeline() {
|
||||
let client = client();
|
||||
let params = [("limit", "30")];
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.feed.getTimeline", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.feed.getTimeline", base_url().await))
|
||||
.query(¶ms)
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
@@ -25,7 +25,7 @@ async fn test_get_author_feed() {
|
||||
("actor", AUTH_DID),
|
||||
("limit", "30")
|
||||
];
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.feed.getAuthorFeed", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.feed.getAuthorFeed", base_url().await))
|
||||
.query(¶ms)
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
@@ -42,7 +42,7 @@ async fn test_get_post_thread() {
|
||||
params.insert("uri", "at://did:plc:other/app.bsky.feed.post/3k12345");
|
||||
params.insert("depth", "5");
|
||||
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.feed.getPostThread", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.feed.getPostThread", base_url().await))
|
||||
.query(¶ms)
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
|
||||
+4
-4
@@ -8,7 +8,7 @@ async fn test_get_follows() {
|
||||
let params = [
|
||||
("actor", AUTH_DID),
|
||||
];
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.graph.getFollows", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.graph.getFollows", base_url().await))
|
||||
.query(¶ms)
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
@@ -24,7 +24,7 @@ async fn test_get_followers() {
|
||||
let params = [
|
||||
("actor", AUTH_DID),
|
||||
];
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.graph.getFollowers", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.graph.getFollowers", base_url().await))
|
||||
.query(¶ms)
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
@@ -40,7 +40,7 @@ async fn test_get_mutes() {
|
||||
let params = [
|
||||
("limit", "25"),
|
||||
];
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.graph.getMutes", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.graph.getMutes", base_url().await))
|
||||
.query(¶ms)
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
@@ -57,7 +57,7 @@ async fn test_get_user_blocks() {
|
||||
let params = [
|
||||
("limit", "25"),
|
||||
];
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.graph.getBlocks", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.graph.getBlocks", base_url().await))
|
||||
.query(¶ms)
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ async fn test_resolve_handle() {
|
||||
let params = [
|
||||
("handle", "bsky.app"),
|
||||
];
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.identity.resolveHandle", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.identity.resolveHandle", base_url().await))
|
||||
.query(¶ms)
|
||||
.send()
|
||||
.await
|
||||
|
||||
+42
-42
@@ -30,7 +30,7 @@ async fn test_post_crud_lifecycle() {
|
||||
}
|
||||
});
|
||||
|
||||
let create_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL))
|
||||
let create_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await))
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.json(&create_payload)
|
||||
.send()
|
||||
@@ -47,7 +47,7 @@ async fn test_post_crud_lifecycle() {
|
||||
("collection", collection),
|
||||
("rkey", &rkey),
|
||||
];
|
||||
let get_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL))
|
||||
let get_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", base_url().await))
|
||||
.query(¶ms)
|
||||
.send()
|
||||
.await
|
||||
@@ -71,7 +71,7 @@ async fn test_post_crud_lifecycle() {
|
||||
}
|
||||
});
|
||||
|
||||
let update_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL))
|
||||
let update_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await))
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.json(&update_payload)
|
||||
.send()
|
||||
@@ -81,7 +81,7 @@ async fn test_post_crud_lifecycle() {
|
||||
assert_eq!(update_res.status(), StatusCode::OK, "Failed to update record");
|
||||
|
||||
|
||||
let get_updated_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL))
|
||||
let get_updated_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", base_url().await))
|
||||
.query(¶ms)
|
||||
.send()
|
||||
.await
|
||||
@@ -98,7 +98,7 @@ async fn test_post_crud_lifecycle() {
|
||||
"rkey": rkey
|
||||
});
|
||||
|
||||
let delete_res = client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", BASE_URL))
|
||||
let delete_res = client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", base_url().await))
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.json(&delete_payload)
|
||||
.send()
|
||||
@@ -108,7 +108,7 @@ async fn test_post_crud_lifecycle() {
|
||||
assert_eq!(delete_res.status(), StatusCode::OK, "Failed to delete record");
|
||||
|
||||
|
||||
let get_deleted_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL))
|
||||
let get_deleted_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", base_url().await))
|
||||
.query(¶ms)
|
||||
.send()
|
||||
.await
|
||||
@@ -157,7 +157,7 @@ async fn test_post_with_image_lifecycle() {
|
||||
}
|
||||
});
|
||||
|
||||
let create_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL))
|
||||
let create_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await))
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.json(&create_payload)
|
||||
.send()
|
||||
@@ -172,7 +172,7 @@ async fn test_post_with_image_lifecycle() {
|
||||
("collection", collection),
|
||||
("rkey", &rkey),
|
||||
];
|
||||
let get_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL))
|
||||
let get_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", base_url().await))
|
||||
.query(¶ms)
|
||||
.send()
|
||||
.await
|
||||
@@ -202,7 +202,7 @@ async fn test_graph_lifecycle_follow_unfollow() {
|
||||
}
|
||||
});
|
||||
|
||||
let create_res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", BASE_URL))
|
||||
let create_res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", base_url().await))
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.json(&create_payload)
|
||||
.send()
|
||||
@@ -219,7 +219,7 @@ async fn test_graph_lifecycle_follow_unfollow() {
|
||||
let params_get_follows = [
|
||||
("actor", AUTH_DID),
|
||||
];
|
||||
let get_follows_res = client.get(format!("{}/xrpc/app.bsky.graph.getFollows", BASE_URL))
|
||||
let get_follows_res = client.get(format!("{}/xrpc/app.bsky.graph.getFollows", base_url().await))
|
||||
.query(¶ms_get_follows)
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
@@ -243,7 +243,7 @@ async fn test_graph_lifecycle_follow_unfollow() {
|
||||
"rkey": rkey
|
||||
});
|
||||
|
||||
let delete_res = client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", BASE_URL))
|
||||
let delete_res = client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", base_url().await))
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.json(&delete_payload)
|
||||
.send()
|
||||
@@ -253,7 +253,7 @@ async fn test_graph_lifecycle_follow_unfollow() {
|
||||
assert_eq!(delete_res.status(), StatusCode::OK, "Failed to delete follow record");
|
||||
|
||||
|
||||
let get_unfollowed_res = client.get(format!("{}/xrpc/app.bsky.graph.getFollows", BASE_URL))
|
||||
let get_unfollowed_res = client.get(format!("{}/xrpc/app.bsky.graph.getFollows", base_url().await))
|
||||
.query(¶ms_get_follows)
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
@@ -290,7 +290,7 @@ async fn test_list_records_pagination() {
|
||||
}
|
||||
});
|
||||
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await))
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.json(&payload)
|
||||
.send()
|
||||
@@ -308,7 +308,7 @@ async fn test_list_records_pagination() {
|
||||
("limit", "2"),
|
||||
];
|
||||
|
||||
let page1_res = client.get(format!("{}/xrpc/com.atproto.repo.listRecords", BASE_URL))
|
||||
let page1_res = client.get(format!("{}/xrpc/com.atproto.repo.listRecords", base_url().await))
|
||||
.query(¶ms_page1)
|
||||
.send()
|
||||
.await
|
||||
@@ -330,7 +330,7 @@ async fn test_list_records_pagination() {
|
||||
("cursor", cursor),
|
||||
];
|
||||
|
||||
let page2_res = client.get(format!("{}/xrpc/com.atproto.repo.listRecords", BASE_URL))
|
||||
let page2_res = client.get(format!("{}/xrpc/com.atproto.repo.listRecords", base_url().await))
|
||||
.query(¶ms_page2)
|
||||
.send()
|
||||
.await
|
||||
@@ -351,7 +351,7 @@ async fn test_list_records_pagination() {
|
||||
"collection": collection,
|
||||
"rkey": rkey
|
||||
});
|
||||
client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", BASE_URL))
|
||||
client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", base_url().await))
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.json(&delete_payload)
|
||||
.send()
|
||||
@@ -386,7 +386,7 @@ async fn test_reply_thread_lifecycle() {
|
||||
let params = [
|
||||
("uri", &root_uri),
|
||||
];
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.feed.getPostThread", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.feed.getPostThread", base_url().await))
|
||||
.query(¶ms)
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
@@ -410,12 +410,12 @@ async fn test_reply_thread_lifecycle() {
|
||||
|
||||
|
||||
let collection = "app.bsky.feed.post";
|
||||
client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", BASE_URL))
|
||||
client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", base_url().await))
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.json(&json!({ "repo": AUTH_DID, "collection": collection, "rkey": reply_rkey }))
|
||||
.send().await.expect("Failed to delete reply");
|
||||
|
||||
client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", BASE_URL))
|
||||
client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", base_url().await))
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.json(&json!({ "repo": AUTH_DID, "collection": collection, "rkey": root_rkey }))
|
||||
.send().await.expect("Failed to delete root post");
|
||||
@@ -436,7 +436,7 @@ async fn test_account_journey_lifecycle() {
|
||||
"password": password
|
||||
});
|
||||
|
||||
let create_res = client.post(format!("{}/xrpc/com.atproto.server.createAccount", BASE_URL))
|
||||
let create_res = client.post(format!("{}/xrpc/com.atproto.server.createAccount", base_url().await))
|
||||
.json(&create_account_payload)
|
||||
.send()
|
||||
.await
|
||||
@@ -455,7 +455,7 @@ async fn test_account_journey_lifecycle() {
|
||||
"password": password
|
||||
});
|
||||
|
||||
let session_res = client.post(format!("{}/xrpc/com.atproto.server.createSession", BASE_URL))
|
||||
let session_res = client.post(format!("{}/xrpc/com.atproto.server.createSession", base_url().await))
|
||||
.json(&session_payload)
|
||||
.send()
|
||||
.await
|
||||
@@ -479,7 +479,7 @@ async fn test_account_journey_lifecycle() {
|
||||
}
|
||||
});
|
||||
|
||||
let profile_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL))
|
||||
let profile_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await))
|
||||
.bearer_auth(&session_jwt)
|
||||
.json(&profile_payload)
|
||||
.send()
|
||||
@@ -492,7 +492,7 @@ async fn test_account_journey_lifecycle() {
|
||||
let params_get_profile = [
|
||||
("actor", &handle),
|
||||
];
|
||||
let get_profile_res = client.get(format!("{}/xrpc/app.bsky.actor.getProfile", BASE_URL))
|
||||
let get_profile_res = client.get(format!("{}/xrpc/app.bsky.actor.getProfile", base_url().await))
|
||||
.query(¶ms_get_profile)
|
||||
.send()
|
||||
.await
|
||||
@@ -506,7 +506,7 @@ async fn test_account_journey_lifecycle() {
|
||||
assert_eq!(profile_body["displayName"], "E2E Test User");
|
||||
|
||||
|
||||
let logout_res = client.post(format!("{}/xrpc/com.atproto.server.deleteSession", BASE_URL))
|
||||
let logout_res = client.post(format!("{}/xrpc/com.atproto.server.deleteSession", base_url().await))
|
||||
.bearer_auth(&session_jwt)
|
||||
.send()
|
||||
.await
|
||||
@@ -515,7 +515,7 @@ async fn test_account_journey_lifecycle() {
|
||||
assert_eq!(logout_res.status(), StatusCode::OK, "Failed to delete session");
|
||||
|
||||
|
||||
let get_session_res = client.get(format!("{}/xrpc/com.atproto.server.getSession", BASE_URL))
|
||||
let get_session_res = client.get(format!("{}/xrpc/com.atproto.server.getSession", base_url().await))
|
||||
.bearer_auth(&session_jwt)
|
||||
.send()
|
||||
.await
|
||||
@@ -536,7 +536,7 @@ async fn setup_new_user(handle_prefix: &str) -> (String, String) {
|
||||
"email": email,
|
||||
"password": password
|
||||
});
|
||||
let create_res = client.post(format!("{}/xrpc/com.atproto.server.createAccount", BASE_URL))
|
||||
let create_res = client.post(format!("{}/xrpc/com.atproto.server.createAccount", base_url().await))
|
||||
.json(&create_account_payload)
|
||||
.send()
|
||||
.await
|
||||
@@ -557,7 +557,7 @@ async fn setup_new_user(handle_prefix: &str) -> (String, String) {
|
||||
"description": "A user created by the e2e test suite."
|
||||
}
|
||||
});
|
||||
let profile_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL))
|
||||
let profile_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await))
|
||||
.bearer_auth(&new_jwt)
|
||||
.json(&profile_payload)
|
||||
.send()
|
||||
@@ -581,7 +581,7 @@ async fn create_record_as(
|
||||
"record": record
|
||||
});
|
||||
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", base_url().await))
|
||||
.bearer_auth(jwt)
|
||||
.json(&payload)
|
||||
.send()
|
||||
@@ -609,7 +609,7 @@ async fn delete_record_as(
|
||||
"rkey": rkey
|
||||
});
|
||||
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", base_url().await))
|
||||
.bearer_auth(jwt)
|
||||
.json(&payload)
|
||||
.send()
|
||||
@@ -640,7 +640,7 @@ async fn test_notification_lifecycle() {
|
||||
).await;
|
||||
let post_ref = json!({ "uri": post_uri, "cid": post_cid });
|
||||
|
||||
let count_res_1 = client.get(format!("{}/xrpc/app.bsky.notification.getUnreadCount", BASE_URL))
|
||||
let count_res_1 = client.get(format!("{}/xrpc/app.bsky.notification.getUnreadCount", base_url().await))
|
||||
.bearer_auth(&user_a_jwt)
|
||||
.send().await.expect("getUnreadCount 1 failed");
|
||||
let count_body_1: Value = count_res_1.json().await.expect("count 1 not json");
|
||||
@@ -677,13 +677,13 @@ async fn test_notification_lifecycle() {
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||
|
||||
let count_res_2 = client.get(format!("{}/xrpc/app.bsky.notification.getUnreadCount", BASE_URL))
|
||||
let count_res_2 = client.get(format!("{}/xrpc/app.bsky.notification.getUnreadCount", base_url().await))
|
||||
.bearer_auth(&user_a_jwt)
|
||||
.send().await.expect("getUnreadCount 2 failed");
|
||||
let count_body_2: Value = count_res_2.json().await.expect("count 2 not json");
|
||||
assert_eq!(count_body_2["count"], 3, "Unread count was not 3 after actions");
|
||||
|
||||
let list_res = client.get(format!("{}/xrpc/app.bsky.notification.listNotifications", BASE_URL))
|
||||
let list_res = client.get(format!("{}/xrpc/app.bsky.notification.listNotifications", base_url().await))
|
||||
.bearer_auth(&user_a_jwt)
|
||||
.send().await.expect("listNotifications failed");
|
||||
let list_body: Value = list_res.json().await.expect("list not json");
|
||||
@@ -699,7 +699,7 @@ async fn test_notification_lifecycle() {
|
||||
assert!(has_like, "Notification list missing 'like'");
|
||||
assert!(has_reply, "Notification list missing 'reply'");
|
||||
|
||||
let count_res_3 = client.get(format!("{}/xrpc/app.bsky.notification.getUnreadCount", BASE_URL))
|
||||
let count_res_3 = client.get(format!("{}/xrpc/app.bsky.notification.getUnreadCount", base_url().await))
|
||||
.bearer_auth(&user_a_jwt)
|
||||
.send().await.expect("getUnreadCount 3 failed");
|
||||
let count_body_3: Value = count_res_3.json().await.expect("count 3 not json");
|
||||
@@ -727,7 +727,7 @@ async fn test_mute_lifecycle_filters_feed() {
|
||||
).await;
|
||||
|
||||
let feed_params_1 = [("actor", &user_b_did)];
|
||||
let feed_res_1 = client.get(format!("{}/xrpc/app.bsky.feed.getAuthorFeed", BASE_URL))
|
||||
let feed_res_1 = client.get(format!("{}/xrpc/app.bsky.feed.getAuthorFeed", base_url().await))
|
||||
.query(&feed_params_1)
|
||||
.bearer_auth(&user_a_jwt)
|
||||
.send().await.expect("getAuthorFeed 1 failed");
|
||||
@@ -749,7 +749,7 @@ async fn test_mute_lifecycle_filters_feed() {
|
||||
let mute_rkey = mute_uri.split('/').last().unwrap();
|
||||
|
||||
let feed_params_2 = [("actor", &user_b_did)];
|
||||
let feed_res_2 = client.get(format!("{}/xrpc/app.bsky.feed.getAuthorFeed", BASE_URL))
|
||||
let feed_res_2 = client.get(format!("{}/xrpc/app.bsky.feed.getAuthorFeed", base_url().await))
|
||||
.query(&feed_params_2)
|
||||
.bearer_auth(&user_a_jwt)
|
||||
.send().await.expect("getAuthorFeed 2 failed");
|
||||
@@ -765,7 +765,7 @@ async fn test_mute_lifecycle_filters_feed() {
|
||||
).await;
|
||||
|
||||
let feed_params_3 = [("actor", &user_b_did)];
|
||||
let feed_res_3 = client.get(format!("{}/xrpc/app.bsky.feed.getAuthorFeed", BASE_URL))
|
||||
let feed_res_3 = client.get(format!("{}/xrpc/app.bsky.feed.getAuthorFeed", base_url().await))
|
||||
.query(&feed_params_3)
|
||||
.bearer_auth(&user_a_jwt)
|
||||
.send().await.expect("getAuthorFeed 3 failed");
|
||||
@@ -783,7 +783,7 @@ async fn test_record_update_conflict_lifecycle() {
|
||||
|
||||
let (user_did, user_jwt) = setup_new_user("user-conflict").await;
|
||||
|
||||
let get_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL))
|
||||
let get_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", base_url().await))
|
||||
.query(&[
|
||||
("repo", &user_did),
|
||||
("collection", &"app.bsky.actor.profile".to_string()),
|
||||
@@ -803,7 +803,7 @@ async fn test_record_update_conflict_lifecycle() {
|
||||
},
|
||||
"swapCommit": cid_v1 // <-- Correctly point to v1
|
||||
});
|
||||
let update_res_v2 = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL))
|
||||
let update_res_v2 = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await))
|
||||
.bearer_auth(&user_jwt)
|
||||
.json(&update_payload_v2)
|
||||
.send().await.expect("putRecord v2 failed");
|
||||
@@ -821,7 +821,7 @@ async fn test_record_update_conflict_lifecycle() {
|
||||
},
|
||||
"swapCommit": cid_v1
|
||||
});
|
||||
let update_res_v3_stale = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL))
|
||||
let update_res_v3_stale = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await))
|
||||
.bearer_auth(&user_jwt)
|
||||
.json(&update_payload_v3_stale)
|
||||
.send().await.expect("putRecord v3 (stale) failed");
|
||||
@@ -842,7 +842,7 @@ async fn test_record_update_conflict_lifecycle() {
|
||||
},
|
||||
"swapCommit": cid_v2 // <-- Correct
|
||||
});
|
||||
let update_res_v3_good = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL))
|
||||
let update_res_v3_good = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await))
|
||||
.bearer_auth(&user_jwt)
|
||||
.json(&update_payload_v3_good)
|
||||
.send().await.expect("putRecord v3 (good) failed");
|
||||
@@ -894,7 +894,7 @@ async fn test_complex_thread_deletion_lifecycle() {
|
||||
}),
|
||||
).await;
|
||||
|
||||
let thread_res_1 = client.get(format!("{}/xrpc/app.bsky.feed.getPostThread", BASE_URL))
|
||||
let thread_res_1 = client.get(format!("{}/xrpc/app.bsky.feed.getPostThread", base_url().await))
|
||||
.query(&[("uri", &p1_uri)])
|
||||
.bearer_auth(&user_a_jwt)
|
||||
.send().await.expect("getThread 1 failed");
|
||||
@@ -914,7 +914,7 @@ async fn test_complex_thread_deletion_lifecycle() {
|
||||
&p2_rkey,
|
||||
).await;
|
||||
|
||||
let thread_res_2 = client.get(format!("{}/xrpc/app.bsky.feed.getPostThread", BASE_URL))
|
||||
let thread_res_2 = client.get(format!("{}/xrpc/app.bsky.feed.getPostThread", base_url().await))
|
||||
.query(&[("uri", &p1_uri)])
|
||||
.bearer_auth(&user_a_jwt)
|
||||
.send().await.expect("getThread 2 failed");
|
||||
|
||||
@@ -8,7 +8,7 @@ async fn test_list_notifications() {
|
||||
let params = [
|
||||
("limit", "30"),
|
||||
];
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.notification.listNotifications", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.notification.listNotifications", base_url().await))
|
||||
.query(¶ms)
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
@@ -21,7 +21,7 @@ async fn test_list_notifications() {
|
||||
#[tokio::test]
|
||||
async fn test_get_unread_count() {
|
||||
let client = client();
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.notification.getUnreadCount", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/app.bsky.notification.getUnreadCount", base_url().await))
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
.await
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
mod common;
|
||||
|
||||
use axum::{
|
||||
routing::any,
|
||||
Router,
|
||||
extract::Request,
|
||||
http::StatusCode,
|
||||
};
|
||||
use tokio::net::TcpListener;
|
||||
use reqwest::Client;
|
||||
use std::sync::Arc;
|
||||
|
||||
async fn spawn_mock_upstream() -> (String, tokio::sync::mpsc::Receiver<(String, String, Option<String>)>) {
|
||||
let (tx, rx) = tokio::sync::mpsc::channel(10);
|
||||
let tx = Arc::new(tx);
|
||||
|
||||
let app = Router::new().fallback(any(move |req: Request| {
|
||||
let tx = tx.clone();
|
||||
async move {
|
||||
let method = req.method().to_string();
|
||||
let uri = req.uri().to_string();
|
||||
let auth = req.headers().get("Authorization")
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
let _ = tx.send((method, uri, auth)).await;
|
||||
(StatusCode::OK, "Mock Response")
|
||||
}
|
||||
}));
|
||||
|
||||
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
|
||||
tokio::spawn(async move {
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
});
|
||||
|
||||
(format!("http://{}", addr), rx)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_proxy_via_header() {
|
||||
let app_url = common::base_url().await;
|
||||
let (upstream_url, mut rx) = spawn_mock_upstream().await;
|
||||
let client = Client::new();
|
||||
|
||||
let res = client.get(format!("{}/xrpc/com.example.test", app_url))
|
||||
.header("atproto-proxy", &upstream_url)
|
||||
.header("Authorization", "Bearer test-token")
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
|
||||
let (method, uri, auth) = rx.recv().await.expect("Upstream should receive request");
|
||||
assert_eq!(method, "GET");
|
||||
assert_eq!(uri, "/xrpc/com.example.test");
|
||||
assert_eq!(auth, Some("Bearer test-token".to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_proxy_via_env_var() {
|
||||
let (upstream_url, mut rx) = spawn_mock_upstream().await;
|
||||
|
||||
unsafe { std::env::set_var("APPVIEW_URL", &upstream_url); }
|
||||
|
||||
let app_url = common::base_url().await;
|
||||
let client = Client::new();
|
||||
|
||||
let res = client.get(format!("{}/xrpc/com.example.envtest", app_url))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
|
||||
let (method, uri, _) = rx.recv().await.expect("Upstream should receive request");
|
||||
assert_eq!(method, "GET");
|
||||
assert_eq!(uri, "/xrpc/com.example.envtest");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_proxy_missing_config() {
|
||||
unsafe { std::env::remove_var("APPVIEW_URL"); }
|
||||
|
||||
let app_url = common::base_url().await;
|
||||
let client = Client::new();
|
||||
|
||||
let res = client.get(format!("{}/xrpc/com.example.fail", app_url))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::BAD_GATEWAY);
|
||||
}
|
||||
+16
-16
@@ -15,7 +15,7 @@ async fn test_get_record() {
|
||||
("rkey", "self"),
|
||||
];
|
||||
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", base_url().await))
|
||||
.query(¶ms)
|
||||
.send()
|
||||
.await
|
||||
@@ -36,7 +36,7 @@ async fn test_get_record_not_found() {
|
||||
("rkey", "nonexistent"),
|
||||
];
|
||||
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", base_url().await))
|
||||
.query(¶ms)
|
||||
.send()
|
||||
.await
|
||||
@@ -51,7 +51,7 @@ async fn test_get_record_not_found() {
|
||||
#[ignore]
|
||||
async fn test_upload_blob_no_auth() {
|
||||
let client = client();
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", base_url().await))
|
||||
.header(header::CONTENT_TYPE, "text/plain")
|
||||
.body("no auth")
|
||||
.send()
|
||||
@@ -68,7 +68,7 @@ async fn test_upload_blob_no_auth() {
|
||||
async fn test_upload_blob_success() {
|
||||
let client = client();
|
||||
let (token, _) = create_account_and_login(&client).await;
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", base_url().await))
|
||||
.header(header::CONTENT_TYPE, "text/plain")
|
||||
.bearer_auth(token)
|
||||
.body("This is our blob data")
|
||||
@@ -92,7 +92,7 @@ async fn test_put_record_no_auth() {
|
||||
"record": {}
|
||||
});
|
||||
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await))
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await
|
||||
@@ -120,7 +120,7 @@ async fn test_put_record_success() {
|
||||
}
|
||||
});
|
||||
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await))
|
||||
.bearer_auth(token)
|
||||
.json(&payload)
|
||||
.send()
|
||||
@@ -142,7 +142,7 @@ async fn test_get_record_missing_params() {
|
||||
("repo", "did:plc:12345"),
|
||||
];
|
||||
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", base_url().await))
|
||||
.query(¶ms)
|
||||
.send()
|
||||
.await
|
||||
@@ -156,7 +156,7 @@ async fn test_get_record_missing_params() {
|
||||
#[ignore]
|
||||
async fn test_upload_blob_bad_token() {
|
||||
let client = client();
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", base_url().await))
|
||||
.header(header::CONTENT_TYPE, "text/plain")
|
||||
.bearer_auth(BAD_AUTH_TOKEN)
|
||||
.body("This is our blob data")
|
||||
@@ -187,7 +187,7 @@ async fn test_put_record_mismatched_repo() {
|
||||
}
|
||||
});
|
||||
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await))
|
||||
.bearer_auth(token)
|
||||
.json(&payload)
|
||||
.send()
|
||||
@@ -215,7 +215,7 @@ async fn test_put_record_invalid_schema() {
|
||||
}
|
||||
});
|
||||
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await))
|
||||
.bearer_auth(token)
|
||||
.json(&payload)
|
||||
.send()
|
||||
@@ -231,7 +231,7 @@ async fn test_put_record_invalid_schema() {
|
||||
async fn test_upload_blob_unsupported_mime_type() {
|
||||
let client = client();
|
||||
let (token, _) = create_account_and_login(&client).await;
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", base_url().await))
|
||||
.header(header::CONTENT_TYPE, "application/xml")
|
||||
.bearer_auth(token)
|
||||
.body("<xml>not an image</xml>")
|
||||
@@ -252,7 +252,7 @@ async fn test_list_records() {
|
||||
("collection", "app.bsky.feed.post"),
|
||||
("limit", "10"),
|
||||
];
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.repo.listRecords", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.repo.listRecords", base_url().await))
|
||||
.query(¶ms)
|
||||
.send()
|
||||
.await
|
||||
@@ -270,7 +270,7 @@ async fn test_delete_record() {
|
||||
"collection": "app.bsky.feed.post",
|
||||
"rkey": "some_post_to_delete"
|
||||
});
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", base_url().await))
|
||||
.bearer_auth(token)
|
||||
.json(&payload)
|
||||
.send()
|
||||
@@ -287,7 +287,7 @@ async fn test_describe_repo() {
|
||||
let params = [
|
||||
("repo", did.as_str()),
|
||||
];
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.repo.describeRepo", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.repo.describeRepo", base_url().await))
|
||||
.query(¶ms)
|
||||
.send()
|
||||
.await
|
||||
@@ -310,7 +310,7 @@ async fn test_create_record_success_with_generated_rkey() {
|
||||
}
|
||||
});
|
||||
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", base_url().await))
|
||||
.json(&payload)
|
||||
.bearer_auth(token) // Assuming auth is required
|
||||
.send()
|
||||
@@ -340,7 +340,7 @@ async fn test_create_record_success_with_provided_rkey() {
|
||||
}
|
||||
});
|
||||
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", base_url().await))
|
||||
.json(&payload)
|
||||
.bearer_auth(token) // Assuming auth is required
|
||||
.send()
|
||||
|
||||
+11
-11
@@ -7,7 +7,7 @@ use serde_json::{json, Value};
|
||||
#[tokio::test]
|
||||
async fn test_health() {
|
||||
let client = client();
|
||||
let res = client.get(format!("{}/health", BASE_URL))
|
||||
let res = client.get(format!("{}/health", base_url().await))
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to send request");
|
||||
@@ -19,7 +19,7 @@ async fn test_health() {
|
||||
#[tokio::test]
|
||||
async fn test_describe_server() {
|
||||
let client = client();
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.server.describeServer", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.server.describeServer", base_url().await))
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to send request");
|
||||
@@ -39,7 +39,7 @@ async fn test_create_session() {
|
||||
"email": format!("{}@example.com", handle),
|
||||
"password": "password"
|
||||
});
|
||||
let _ = client.post(format!("{}/xrpc/com.atproto.server.createAccount", BASE_URL))
|
||||
let _ = client.post(format!("{}/xrpc/com.atproto.server.createAccount", base_url().await))
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await;
|
||||
@@ -49,7 +49,7 @@ async fn test_create_session() {
|
||||
"password": "password"
|
||||
});
|
||||
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.server.createSession", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.server.createSession", base_url().await))
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await
|
||||
@@ -67,7 +67,7 @@ async fn test_create_session_missing_identifier() {
|
||||
"password": "password"
|
||||
});
|
||||
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.server.createSession", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.server.createSession", base_url().await))
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await
|
||||
@@ -86,7 +86,7 @@ async fn test_create_account_invalid_handle() {
|
||||
"password": "password"
|
||||
});
|
||||
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.server.createAccount", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.server.createAccount", base_url().await))
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await
|
||||
@@ -98,7 +98,7 @@ async fn test_create_account_invalid_handle() {
|
||||
#[tokio::test]
|
||||
async fn test_get_session() {
|
||||
let client = client();
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.server.getSession", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.server.getSession", base_url().await))
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
.await
|
||||
@@ -117,7 +117,7 @@ async fn test_refresh_session() {
|
||||
"email": format!("{}@example.com", handle),
|
||||
"password": "password"
|
||||
});
|
||||
let _ = client.post(format!("{}/xrpc/com.atproto.server.createAccount", BASE_URL))
|
||||
let _ = client.post(format!("{}/xrpc/com.atproto.server.createAccount", base_url().await))
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await;
|
||||
@@ -126,7 +126,7 @@ async fn test_refresh_session() {
|
||||
"identifier": handle,
|
||||
"password": "password"
|
||||
});
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.server.createSession", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.server.createSession", base_url().await))
|
||||
.json(&login_payload)
|
||||
.send()
|
||||
.await
|
||||
@@ -137,7 +137,7 @@ async fn test_refresh_session() {
|
||||
let refresh_jwt = body["refreshJwt"].as_str().expect("No refreshJwt").to_string();
|
||||
let access_jwt = body["accessJwt"].as_str().expect("No accessJwt").to_string();
|
||||
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.server.refreshSession", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.server.refreshSession", base_url().await))
|
||||
.bearer_auth(&refresh_jwt)
|
||||
.send()
|
||||
.await
|
||||
@@ -154,7 +154,7 @@ async fn test_refresh_session() {
|
||||
#[tokio::test]
|
||||
async fn test_delete_session() {
|
||||
let client = client();
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.server.deleteSession", BASE_URL))
|
||||
let res = client.post(format!("{}/xrpc/com.atproto.server.deleteSession", base_url().await))
|
||||
.bearer_auth(AUTH_TOKEN)
|
||||
.send()
|
||||
.await
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ async fn test_get_repo() {
|
||||
let params = [
|
||||
("did", AUTH_DID),
|
||||
];
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.sync.getRepo", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.sync.getRepo", base_url().await))
|
||||
.query(¶ms)
|
||||
.send()
|
||||
.await
|
||||
@@ -24,7 +24,7 @@ async fn test_get_blocks() {
|
||||
("did", AUTH_DID),
|
||||
// "cids" would be a list of CIDs
|
||||
];
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.sync.getBlocks", BASE_URL))
|
||||
let res = client.get(format!("{}/xrpc/com.atproto.sync.getBlocks", base_url().await))
|
||||
.query(¶ms)
|
||||
.send()
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user