aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.env.example1
-rw-r--r--Cargo.lock625
-rw-r--r--Cargo.toml42
-rw-r--r--README.md5
-rw-r--r--lib/auth/Cargo.toml13
-rw-r--r--lib/auth/src/lib.rs41
-rw-r--r--misc/compose.yaml36
-rw-r--r--misc/sellershut.toml (renamed from sellershut.toml)6
-rw-r--r--src/config/cli.rs23
-rw-r--r--src/config/mod.rs60
-rw-r--r--src/main.rs2
-rw-r--r--src/server/state/database.rs1
-rw-r--r--src/server/state/mod.rs27
13 files changed, 846 insertions, 36 deletions
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..36fa84f
--- /dev/null
+++ b/.env.example
@@ -0,0 +1 @@
+DATABASE_URL=postgres://postgres:password@localhost:5432/sellershut
diff --git a/Cargo.lock b/Cargo.lock
index 7671681..3215826 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -24,6 +24,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "anstream"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -183,6 +192,31 @@ dependencies = [
]
[[package]]
+name = "bon"
+version = "3.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "234655ec178edd82b891e262ea7cf71f6584bcd09eff94db786be23f1821825c"
+dependencies = [
+ "bon-macros",
+ "rustversion",
+]
+
+[[package]]
+name = "bon-macros"
+version = "3.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89ec27229c38ed0eb3c0feee3d2c1d6a4379ae44f418a29a658890e062d8f365"
+dependencies = [
+ "darling",
+ "ident_case",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn",
+]
+
+[[package]]
name = "bumpalo"
version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -217,6 +251,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
+name = "chrono"
+version = "0.4.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
+dependencies = [
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "serde",
+ "wasm-bindgen",
+ "windows-link",
+]
+
+[[package]]
name = "clap"
version = "4.5.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -272,6 +326,12 @@ dependencies = [
]
[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -330,6 +390,40 @@ dependencies = [
]
[[package]]
+name = "darling"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0"
+dependencies = [
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "derive_arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -525,8 +619,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "r-efi",
+ "wasip2",
+ "wasm-bindgen",
]
[[package]]
@@ -658,6 +768,24 @@ dependencies = [
"pin-utils",
"smallvec",
"tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.27.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
+dependencies = [
+ "http",
+ "hyper",
+ "hyper-util",
+ "rustls",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+ "webpki-roots 1.0.5",
]
[[package]]
@@ -666,14 +794,46 @@ version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f"
dependencies = [
+ "base64",
"bytes",
+ "futures-channel",
"futures-core",
+ "futures-util",
"http",
"http-body",
"hyper",
+ "ipnet",
+ "libc",
+ "percent-encoding",
"pin-project-lite",
+ "socket2",
"tokio",
"tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
]
[[package]]
@@ -758,6 +918,12 @@ dependencies = [
]
[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
name = "idna"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -791,6 +957,22 @@ dependencies = [
]
[[package]]
+name = "ipnet"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
+
+[[package]]
+name = "iri-string"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -803,6 +985,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]]
+name = "js-sys"
+version = "0.3.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -847,6 +1039,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
+name = "lru-slab"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
+
+[[package]]
name = "matchers"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -933,6 +1131,26 @@ dependencies = [
]
[[package]]
+name = "oauth2"
+version = "5.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d"
+dependencies = [
+ "base64",
+ "chrono",
+ "getrandom 0.2.17",
+ "http",
+ "rand 0.8.5",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "sha2",
+ "thiserror 1.0.69",
+ "url",
+]
+
+[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1010,6 +1228,16 @@ dependencies = [
]
[[package]]
+name = "prettyplease"
+version = "0.2.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
+
+[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1019,6 +1247,61 @@ dependencies = [
]
[[package]]
+name = "quinn"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
+dependencies = [
+ "bytes",
+ "cfg_aliases",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash",
+ "rustls",
+ "socket2",
+ "thiserror 2.0.18",
+ "tokio",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.11.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
+dependencies = [
+ "bytes",
+ "getrandom 0.3.4",
+ "lru-slab",
+ "rand 0.9.2",
+ "ring",
+ "rustc-hash",
+ "rustls",
+ "rustls-pki-types",
+ "slab",
+ "thiserror 2.0.18",
+ "tinyvec",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
+dependencies = [
+ "cfg_aliases",
+ "libc",
+ "once_cell",
+ "socket2",
+ "tracing",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
name = "quote"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1028,14 +1311,30 @@ dependencies = [
]
[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
- "rand_chacha",
- "rand_core",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
+dependencies = [
+ "rand_chacha 0.9.0",
+ "rand_core 0.9.5",
]
[[package]]
@@ -1045,7 +1344,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
- "rand_core",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.9.5",
]
[[package]]
@@ -1054,7 +1363,16 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
- "getrandom",
+ "getrandom 0.2.17",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
+dependencies = [
+ "getrandom 0.3.4",
]
[[package]]
@@ -1105,6 +1423,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
+name = "reqwest"
+version = "0.12.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
+dependencies = [
+ "base64",
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-rustls",
+ "hyper-util",
+ "js-sys",
+ "log",
+ "percent-encoding",
+ "pin-project-lite",
+ "quinn",
+ "rustls",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tokio-rustls",
+ "tower",
+ "tower-http",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "webpki-roots 1.0.5",
+]
+
+[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1112,7 +1468,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
- "getrandom",
+ "getrandom 0.2.17",
"libc",
"untrusted",
"windows-sys 0.52.0",
@@ -1153,6 +1509,12 @@ dependencies = [
]
[[package]]
+name = "rustc-hash"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
+
+[[package]]
name = "rustls"
version = "0.23.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1172,6 +1534,7 @@ version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
dependencies = [
+ "web-time",
"zeroize",
]
@@ -1187,6 +1550,12 @@ dependencies = [
]
[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
name = "ryu"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1208,12 +1577,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
+name = "secrecy"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a"
+dependencies = [
+ "serde",
+ "zeroize",
+]
+
+[[package]]
name = "sellershut"
version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"clap",
+ "oauth2",
+ "rand 0.9.2",
+ "secrecy",
+ "sellershut-auth",
"serde",
"sqlx",
"tokio",
@@ -1229,6 +1612,17 @@ dependencies = [
]
[[package]]
+name = "sellershut-auth"
+version = "0.1.0"
+dependencies = [
+ "bon",
+ "oauth2",
+ "secrecy",
+ "thiserror 2.0.18",
+ "url",
+]
+
+[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1409,7 +1803,7 @@ dependencies = [
"serde_json",
"sha2",
"smallvec",
- "thiserror",
+ "thiserror 2.0.18",
"tokio",
"tokio-stream",
"tracing",
@@ -1478,14 +1872,14 @@ dependencies = [
"md-5",
"memchr",
"once_cell",
- "rand",
+ "rand 0.8.5",
"serde",
"serde_json",
"sha2",
"smallvec",
"sqlx-core",
"stringprep",
- "thiserror",
+ "thiserror 2.0.18",
"tracing",
"whoami",
]
@@ -1535,6 +1929,9 @@ name = "sync_wrapper"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+dependencies = [
+ "futures-core",
+]
[[package]]
name = "synstructure"
@@ -1549,11 +1946,31 @@ dependencies = [
[[package]]
name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
- "thiserror-impl",
+ "thiserror-impl 2.0.18",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
@@ -1629,6 +2046,16 @@ dependencies = [
]
[[package]]
+name = "tokio-rustls"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
+[[package]]
name = "tokio-stream"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1695,6 +2122,24 @@ dependencies = [
]
[[package]]
+name = "tower-http"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
+dependencies = [
+ "bitflags",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "iri-string",
+ "pin-project-lite",
+ "tower",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
name = "tower-layer"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1769,6 +2214,12 @@ dependencies = [
]
[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
name = "typenum"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1934,18 +2385,115 @@ dependencies = [
]
[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
+name = "wasip2"
+version = "1.0.2+wasi-0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
name = "wasite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
[[package]]
+name = "wasm-bindgen"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f"
+dependencies = [
+ "cfg-if",
+ "futures-util",
+ "js-sys",
+ "once_cell",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
name = "webpki-roots"
version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1983,12 +2531,65 @@ dependencies = [
]
[[package]]
+name = "windows-core"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
+name = "windows-result"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2217,6 +2818,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
[[package]]
+name = "wit-bindgen"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+
+[[package]]
name = "writeable"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index de917e4..f80e8c9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,21 +1,46 @@
+[workspace]
+resolver = "3"
+members = [ ".", "lib/*" ]
+
+[workspace.package]
+description = "A federated marketplace platform"
+documentation = "https://books.kanjala.com/sellershut"
+homepage = "https://git.kanjala.com/sellershut"
+license = "AGPL-3.0-only"
+
+[workspace.dependencies]
+anyhow = "1.0.100"
+bon = "3.8.2"
+oauth2 = "5.0.0"
+rand = "0.9.2"
+secrecy = "0.10.3"
+serde = "1.0.228"
+thiserror = "2.0.18"
+tracing = "0.1.44"
+url = "2.5.8"
+
[package]
name = "sellershut"
version = "0.1.0"
edition = "2024"
-license = "AGPL-3.0-only"
-description = "A federated marketplace platform"
-homepage = "https://git.kanjala.com/sellershut"
+description.workspace = true
+documentation.workspace = true
+homepage.workspace = true
+license.workspace = true
[dependencies]
-anyhow = "1.0.100"
+anyhow.workspace = true
axum = "0.8.8"
clap = { version = "4.5.56", features = ["derive", "env"] }
-serde = { version = "1.0.228", features = ["derive"] }
+oauth2.workspace = true
+secrecy = { workspace = true, features = ["serde"] }
+sellershut-auth = { path = "lib/auth" }
+serde = { workspace = true, features = ["derive"] }
tokio = { version = "1.49.0", features = ["rt-multi-thread", "macros", "signal"] }
toml = "0.9.11"
-tracing = "0.1.44"
+tracing.workspace = true
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
-url = { version = "2.5.8", features = ["serde"] }
+url = { workspace = true, features = ["serde"] }
utoipa = "5.4.0"
utoipa-rapidoc = { version = "6.0.0", optional = true }
utoipa-redoc = { version = "6.0.0", optional = true }
@@ -37,3 +62,6 @@ utoipa-swagger-ui = ["dep:utoipa-swagger-ui"]
[profile.dev.package.sqlx-macros]
opt-level = 3
+
+[dev-dependencies]
+rand.workspace = true
diff --git a/README.md b/README.md
index f198bb7..0418116 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,6 @@
# sellershut
+
+```sh
+cp .env.example .env
+cargo sqlx database create
+```
diff --git a/lib/auth/Cargo.toml b/lib/auth/Cargo.toml
new file mode 100644
index 0000000..0852935
--- /dev/null
+++ b/lib/auth/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "sellershut-auth"
+version = "0.1.0"
+edition = "2024"
+license.workspace = true
+documentation.workspace = true
+
+[dependencies]
+bon.workspace = true
+oauth2.workspace = true
+secrecy.workspace = true
+thiserror.workspace = true
+url.workspace = true
diff --git a/lib/auth/src/lib.rs b/lib/auth/src/lib.rs
new file mode 100644
index 0000000..2a1390e
--- /dev/null
+++ b/lib/auth/src/lib.rs
@@ -0,0 +1,41 @@
+use bon::Builder;
+use oauth2::{AuthUrl, ClientId, ClientSecret, EndpointNotSet, EndpointSet, RedirectUrl, TokenUrl};
+use secrecy::{ExposeSecret, SecretString};
+use thiserror::Error;
+
+#[derive(Builder)]
+pub struct ClientOptions {
+ client_id: String,
+ client_secret: SecretString,
+ token_url: String,
+ auth_url: String,
+ redirect_url: String,
+}
+
+#[derive(Error, Debug)]
+pub enum OauthError {
+ #[error("invalid url")]
+ InvalidUrl(#[from] url::ParseError),
+}
+
+pub type OauthClient = oauth2::basic::BasicClient<
+ EndpointSet,
+ EndpointNotSet,
+ EndpointNotSet,
+ EndpointNotSet,
+ EndpointSet,
+>;
+
+pub fn oauth_client(opts: &ClientOptions) -> Result<OauthClient, OauthError> {
+ let redirect_url = RedirectUrl::new(opts.redirect_url.to_owned())?;
+ let client_id = ClientId::new(opts.client_id.to_owned());
+ let auth_url = AuthUrl::new(opts.auth_url.to_owned())?;
+ let token_url = TokenUrl::new(opts.token_url.to_owned())?;
+ let client_secret = ClientSecret::new(opts.client_secret.expose_secret().to_string());
+
+ Ok(oauth2::basic::BasicClient::new(client_id)
+ .set_client_secret(client_secret)
+ .set_auth_uri(auth_url)
+ .set_token_uri(token_url)
+ .set_redirect_uri(redirect_url))
+}
diff --git a/misc/compose.yaml b/misc/compose.yaml
new file mode 100644
index 0000000..1fccb81
--- /dev/null
+++ b/misc/compose.yaml
@@ -0,0 +1,36 @@
+name: sellershut
+
+services:
+ database:
+ image: docker.io/postgres:18.1-alpine
+ restart: always
+ shm_size: 128mb
+ environment:
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password}
+ PGDATA: /data/postgres
+ ports:
+ - 5432:5432
+ networks:
+ - sellershut
+ volumes:
+ - db:/data/postgres
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
+ interval: 10s
+ timeout: 5s
+ retries: 3
+
+ adminer:
+ image: docker.io/adminer:5.4.1
+ restart: always
+ ports:
+ - 8080:8080
+ networks:
+ - sellershut
+
+volumes:
+ db:
+ driver: local
+
+networks:
+ sellershut:
diff --git a/sellershut.toml b/misc/sellershut.toml
index 15225f5..0fdef72 100644
--- a/sellershut.toml
+++ b/misc/sellershut.toml
@@ -7,13 +7,13 @@ system-name = "sellershut"
environment = "dev"
[oauth]
-redirect-url = ""
+redirect-url = "https://example.com"
[oauth.discord]
client-id = ""
client-secret = ""
-token-url = ""
-auth-url = ""
+token-url = "https://example.com"
+auth-url = "https://example.com"
[database]
url = "postgres://postres:password@localhost:5432/sellershut"
diff --git a/src/config/cli.rs b/src/config/cli.rs
index dab7216..5254135 100644
--- a/src/config/cli.rs
+++ b/src/config/cli.rs
@@ -1,6 +1,7 @@
use std::path::PathBuf;
use clap::Parser;
+use serde::Deserialize;
use url::Url;
use crate::config::{logging::LogLevel, port::port_in_range};
@@ -49,25 +50,33 @@ pub struct Cli {
pub oauth: Option<OAuth>,
}
-#[derive(Debug, Clone, Parser)]
+#[derive(Debug, Clone, Parser, Deserialize)]
pub struct OAuth {
#[cfg(feature = "oauth-discord")]
#[command(flatten)]
discord: DiscordOauth,
- #[arg(long)]
+ #[arg(long, env = "OAUTH_REDIRECT_URL")]
oauth_redirect_url: Option<Url>,
}
#[cfg(feature = "oauth-discord")]
-#[derive(Debug, Clone, Parser)]
+#[derive(Debug, Clone, Parser, Deserialize)]
pub struct DiscordOauth {
- #[arg(long)]
+ #[arg(long, env = "OAUTH_DISCORD_CLIENT_ID")]
discord_client_id: Option<String>,
- #[arg(long)]
+ #[arg(long, env = "OAUTH_DISCORD_CLIENT_SECRET")]
discord_client_secret: Option<String>,
- #[arg(long)]
+ #[arg(
+ long,
+ env = "OAUTH_DISCORD_TOKEN_URL",
+ default_value = "https://discord.com/api/oauth2/token"
+ )]
discord_token_url: Option<Url>,
- #[arg(long)]
+ #[arg(
+ long,
+ env = "OAUTH_DISCORD_AUTH_URL",
+ default_value = "https://discord.com/api/oauth2/authorize?response_type=code"
+ )]
discord_auth_url: Option<Url>,
}
diff --git a/src/config/mod.rs b/src/config/mod.rs
index 45e12c3..19ee241 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -2,10 +2,12 @@ mod cli;
mod logging;
mod port;
pub use cli::Cli;
+#[cfg(feature = "oauth-discord")]
+use secrecy::SecretString;
use serde::Deserialize;
use url::Url;
-use crate::{config::logging::LogLevel};
+use crate::config::logging::LogLevel;
#[derive(Default, Deserialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
@@ -22,6 +24,8 @@ pub struct Config {
pub database: DatabaseOptions,
#[serde(default)]
pub server: Api,
+ #[serde(default)]
+ pub oauth: OAuth,
}
#[derive(Debug, Deserialize)]
@@ -46,6 +50,52 @@ pub struct Api {
pub environment: Environment,
}
+#[derive(Debug, Clone, Deserialize)]
+pub struct OAuth {
+ #[cfg(feature = "oauth-discord")]
+ pub discord: DiscordOauth,
+ #[serde(rename = "redirect-url")]
+ pub oauth_redirect_url: Url,
+}
+
+#[cfg(feature = "oauth-discord")]
+#[derive(Debug, Clone, Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub struct DiscordOauth {
+ pub client_id: String,
+ pub client_secret: SecretString,
+ #[serde(default = "discord_token_url")]
+ pub token_url: Url,
+ #[serde(default = "discord_auth_url")]
+ pub auth_url: Url,
+}
+
+fn discord_token_url() -> Url {
+ Url::parse("https://discord.com/api/oauth2/authorize?response_type=code").expect("valid url")
+}
+
+fn discord_auth_url() -> Url {
+ Url::parse("https://discord.com/api/oauth2/authorize?response_type=code").expect("valid url")
+}
+
+fn redirect_url() -> Url {
+ Url::parse("http://127.0.0.1:2210/auth/authorised").expect("valid url")
+}
+
+impl Default for OAuth {
+ fn default() -> Self {
+ Self {
+ discord: DiscordOauth {
+ client_id: String::default(),
+ client_secret: SecretString::default(),
+ token_url: discord_token_url(),
+ auth_url: discord_auth_url(),
+ },
+ oauth_redirect_url: redirect_url(),
+ }
+}
+}
+
impl Default for Api {
fn default() -> Self {
Self {
@@ -68,7 +118,7 @@ pub struct DatabaseOptions {
}
impl DatabaseOptions {
- pub fn create(url: & Url, pool_size: Option<u32>) -> Self {
+ pub fn create(url: &Url, pool_size: Option<u32>) -> Self {
Self {
url: url.to_owned(),
pool_size: pool_size.unwrap_or_else(|| {
@@ -89,8 +139,7 @@ impl Default for DatabaseOptions {
fn default() -> Self {
Self {
url: default_database(),
- pool_size: 100
-
+ pool_size: 100,
}
}
}
@@ -115,7 +164,6 @@ fn default_log_level() -> LogLevel {
LogLevel::Debug
}
-
impl Config {
pub fn merge_with_cli(&mut self, cli: &Cli) {
let server = &mut self.server;
@@ -149,7 +197,7 @@ mod tests {
#[test]
fn config_file() {
- let s = include_str!("../../sellershut.toml");
+ let s = include_str!("../../misc/sellershut.toml");
assert!(toml::from_str::<Config>(s).is_ok())
}
}
diff --git a/src/main.rs b/src/main.rs
index cb8c2a9..8ee10a1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -8,7 +8,7 @@ use clap::Parser;
use tokio::net::TcpListener;
use tracing::info;
-use crate::{config::Config, logging::initialise_logging, server::state::{AppState }};
+use crate::{config::Config, logging::initialise_logging, server::state::AppState};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
diff --git a/src/server/state/database.rs b/src/server/state/database.rs
index 32d3f98..f8fd332 100644
--- a/src/server/state/database.rs
+++ b/src/server/state/database.rs
@@ -4,7 +4,6 @@ use tracing::{debug, trace};
use crate::config::DatabaseOptions;
-
pub(super) async fn connect(opts: &DatabaseOptions) -> Result<PgPool> {
trace!(host = ?opts.url.host(), "connecting to database");
let pg = PgPoolOptions::new()
diff --git a/src/server/state/mod.rs b/src/server/state/mod.rs
index f4bf029..0726689 100644
--- a/src/server/state/mod.rs
+++ b/src/server/state/mod.rs
@@ -1,17 +1,40 @@
pub mod database;
+use sellershut_auth::{ClientOptions, OauthClient};
use sqlx::PgPool;
+#[cfg(feature = "oauth-discord")]
+use url::Url;
-use crate::{config::Config};
+use crate::config::Config;
+#[cfg(feature = "oauth-discord")]
+use crate::config::DiscordOauth;
pub struct AppState {
database: PgPool,
+ #[cfg(feature = "oauth-discord")]
+ oauth_discord: OauthClient,
}
impl AppState {
pub async fn new(config: &Config) -> anyhow::Result<Self> {
let database = database::connect(&config.database).await?;
- Ok(Self{database})
+ Ok(Self {
+ database,
+ oauth_discord: discord_client(&config.oauth.discord, &config.oauth.oauth_redirect_url)?,
+ })
}
}
+
+#[cfg(feature = "oauth-discord")]
+fn discord_client(disc: &DiscordOauth, redirect: &Url)->anyhow::Result<OauthClient> {
+ let discord_opts = ClientOptions::builder()
+ .client_id(disc.client_id.to_owned())
+ .redirect_url(redirect.to_string())
+ .auth_url(disc.auth_url.to_string())
+ .client_secret(disc.client_secret.clone())
+ .token_url(disc.token_url.to_string())
+ .build();
+
+ Ok(sellershut_auth::oauth_client(&discord_opts)?)
+}