Compare commits

..

4 Commits

9 changed files with 863 additions and 88 deletions

14
.env.example Normal file
View File

@@ -0,0 +1,14 @@
# optional, used for fetching build logs, not configured automatically
RAILWAY_TOKEN=your_railway_token_here
# optional but automatically configured by Railway
# RAILWAY_PROJECT_ID=your_project_id_here
# RAILWAY_SERVICE_ID=your_service_id_here
# RAILWAY_ENVIRONMENT_ID=your_environment_id_here
# RAILWAY_DEPLOYMENT_ID=your_deployment_id_here
# optional, automatically configured by Railway
# PORT=5800
# optional, has a default you may not want
# RAILWAY_PUBLIC_DOMAIN=your-domain.railway.app

513
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "addr2line"
@@ -61,6 +61,21 @@ dependencies = [
"memchr",
]
[[package]]
name = "alloc-no-stdlib"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
[[package]]
name = "alloc-stdlib"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
dependencies = [
"alloc-no-stdlib",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
@@ -117,7 +132,7 @@ dependencies = [
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -132,6 +147,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.6.0"
@@ -147,6 +168,27 @@ dependencies = [
"generic-array",
]
[[package]]
name = "brotli"
version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor",
]
[[package]]
name = "brotli-decompressor"
version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
]
[[package]]
name = "bumpalo"
version = "3.16.0"
@@ -171,6 +213,8 @@ version = "1.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e"
dependencies = [
"jobserver",
"libc",
"shlex",
]
@@ -198,7 +242,7 @@ dependencies = [
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -263,6 +307,15 @@ dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
@@ -315,14 +368,23 @@ dependencies = [
"syn",
]
[[package]]
name = "dotenv"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
name = "dynamic-preauth"
version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
"dotenv",
"futures-util",
"rand",
"regex",
"reqwest 0.11.27",
"salvo",
"serde",
"serde_json",
@@ -393,6 +455,16 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "flate2"
version = "1.0.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
@@ -523,6 +595,25 @@ version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "h2"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http 0.2.12",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "h2"
version = "0.4.7"
@@ -534,7 +625,7 @@ dependencies = [
"fnv",
"futures-core",
"futures-sink",
"http",
"http 1.2.0",
"indexmap",
"slab",
"tokio",
@@ -557,7 +648,7 @@ dependencies = [
"base64 0.21.7",
"bytes",
"headers-core",
"http",
"http 1.2.0",
"httpdate",
"mime",
"sha1",
@@ -569,7 +660,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4"
dependencies = [
"http",
"http 1.2.0",
]
[[package]]
@@ -587,6 +678,17 @@ dependencies = [
"digest",
]
[[package]]
name = "http"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http"
version = "1.2.0"
@@ -598,6 +700,17 @@ dependencies = [
"itoa",
]
[[package]]
name = "http-body"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
dependencies = [
"bytes",
"http 0.2.12",
"pin-project-lite",
]
[[package]]
name = "http-body"
version = "1.0.1"
@@ -605,7 +718,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
"http",
"http 1.2.0",
]
[[package]]
@@ -616,8 +729,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
dependencies = [
"bytes",
"futures-util",
"http",
"http-body",
"http 1.2.0",
"http-body 1.0.1",
"pin-project-lite",
]
@@ -633,6 +746,30 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "0.14.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2 0.3.27",
"http 0.2.12",
"http-body 0.4.6",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"socket2",
"tokio",
"tower-service",
"tracing",
"want",
]
[[package]]
name = "hyper"
version = "1.5.2"
@@ -642,9 +779,9 @@ dependencies = [
"bytes",
"futures-channel",
"futures-util",
"h2",
"http",
"http-body",
"h2 0.4.7",
"http 1.2.0",
"http-body 1.0.1",
"httparse",
"httpdate",
"itoa",
@@ -654,6 +791,20 @@ dependencies = [
"want",
]
[[package]]
name = "hyper-rustls"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
dependencies = [
"futures-util",
"http 0.2.12",
"hyper 0.14.32",
"rustls 0.21.12",
"tokio",
"tokio-rustls 0.24.1",
]
[[package]]
name = "hyper-rustls"
version = "0.27.5"
@@ -661,15 +812,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
dependencies = [
"futures-util",
"http",
"hyper",
"http 1.2.0",
"hyper 1.5.2",
"hyper-util",
"log",
"rustls",
"rustls 0.23.20",
"rustls-native-certs",
"rustls-pki-types",
"tokio",
"tokio-rustls",
"tokio-rustls 0.26.1",
"tower-service",
]
@@ -681,7 +832,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"bytes",
"http-body-util",
"hyper",
"hyper 1.5.2",
"hyper-util",
"native-tls",
"tokio",
@@ -698,9 +849,9 @@ dependencies = [
"bytes",
"futures-channel",
"futures-util",
"http",
"http-body",
"hyper",
"http 1.2.0",
"http-body 1.0.1",
"hyper 1.5.2",
"pin-project-lite",
"socket2",
"tokio",
@@ -901,6 +1052,15 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "jobserver"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
dependencies = [
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.76"
@@ -1026,7 +1186,7 @@ dependencies = [
"bytes",
"encoding_rs",
"futures-util",
"http",
"http 1.2.0",
"httparse",
"memchr",
"mime",
@@ -1066,7 +1226,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
"bitflags",
"bitflags 2.6.0",
"cfg-if",
"cfg_aliases",
"libc",
@@ -1143,7 +1303,7 @@ version = "0.10.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
dependencies = [
"bitflags",
"bitflags 2.6.0",
"cfg-if",
"foreign-types",
"libc",
@@ -1207,7 +1367,7 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -1360,7 +1520,7 @@ version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [
"bitflags",
"bitflags 2.6.0",
]
[[package]]
@@ -1407,6 +1567,47 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "reqwest"
version = "0.11.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
dependencies = [
"base64 0.21.7",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2 0.3.27",
"http 0.2.12",
"http-body 0.4.6",
"hyper 0.14.32",
"hyper-rustls 0.24.2",
"ipnet",
"js-sys",
"log",
"mime",
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls 0.21.12",
"rustls-pemfile 1.0.4",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper 0.1.2",
"system-configuration 0.5.1",
"tokio",
"tokio-rustls 0.24.1",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots",
"winreg",
]
[[package]]
name = "reqwest"
version = "0.12.9"
@@ -1418,12 +1619,12 @@ dependencies = [
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"h2 0.4.7",
"http 1.2.0",
"http-body 1.0.1",
"http-body-util",
"hyper",
"hyper-rustls",
"hyper 1.5.2",
"hyper-rustls 0.27.5",
"hyper-tls",
"hyper-util",
"ipnet",
@@ -1434,12 +1635,12 @@ dependencies = [
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls-pemfile",
"rustls-pemfile 2.2.0",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper 1.0.2",
"system-configuration",
"system-configuration 0.6.1",
"tokio",
"tokio-native-tls",
"tokio-util",
@@ -1513,13 +1714,25 @@ version = "0.38.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
dependencies = [
"bitflags",
"bitflags 2.6.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.59.0",
]
[[package]]
name = "rustls"
version = "0.21.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
dependencies = [
"log",
"ring",
"rustls-webpki 0.101.7",
"sct",
]
[[package]]
name = "rustls"
version = "0.23.20"
@@ -1530,7 +1743,7 @@ dependencies = [
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"rustls-webpki 0.102.8",
"subtle",
"zeroize",
]
@@ -1547,6 +1760,15 @@ dependencies = [
"security-framework 3.1.0",
]
[[package]]
name = "rustls-pemfile"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
dependencies = [
"base64 0.21.7",
]
[[package]]
name = "rustls-pemfile"
version = "2.2.0"
@@ -1562,6 +1784,16 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37"
[[package]]
name = "rustls-webpki"
version = "0.101.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "rustls-webpki"
version = "0.102.8"
@@ -1613,7 +1845,7 @@ dependencies = [
"base64 0.22.1",
"bytes",
"http-body-util",
"hyper-rustls",
"hyper-rustls 0.27.5",
"hyper-util",
"jsonwebtoken",
"salvo_core",
@@ -1632,11 +1864,11 @@ checksum = "ef1f0487e3987c47c2e26d71a9ec696282f0c79203b22f4b9d50afc33273df5f"
dependencies = [
"fastrand",
"futures-util",
"hyper",
"hyper-rustls",
"hyper 1.5.2",
"hyper-rustls 0.27.5",
"hyper-util",
"percent-encoding",
"reqwest",
"reqwest 0.12.9",
"salvo_core",
"tokio",
"tracing",
@@ -1681,17 +1913,20 @@ checksum = "447c7ab93300c57c76c95412cc469ef07943c698ef022dc08f743f92c05847a8"
dependencies = [
"async-trait",
"base64 0.22.1",
"brotli",
"bytes",
"cookie",
"encoding_rs",
"enumflags2",
"flate2",
"form_urlencoded",
"futures-channel",
"futures-util",
"headers",
"http",
"http 1.2.0",
"http-body-util",
"hyper",
"hyper-rustls",
"hyper 1.5.2",
"hyper-rustls 0.27.5",
"hyper-util",
"indexmap",
"mime",
@@ -1704,18 +1939,21 @@ dependencies = [
"pin-project",
"rand",
"regex",
"rustls-pemfile",
"rustls-pemfile 2.2.0",
"salvo_macros",
"serde",
"serde-xml-rs",
"serde_json",
"serde_urlencoded",
"sync_wrapper 1.0.2",
"tempfile",
"thiserror 2.0.9",
"tokio",
"tokio-rustls",
"tokio-rustls 0.26.1",
"tokio-util",
"tracing",
"url",
"zstd",
]
[[package]]
@@ -1728,7 +1966,7 @@ dependencies = [
"etag",
"futures-util",
"http-body-util",
"hyper",
"hyper 1.5.2",
"pin-project",
"salvo_core",
"serde",
@@ -1778,13 +2016,23 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sct"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "security-framework"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags",
"bitflags 2.6.0",
"core-foundation 0.9.4",
"core-foundation-sys",
"libc",
@@ -1797,7 +2045,7 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81d3f8c9bfcc3cbb6b0179eb57042d75b1582bdc65c3cb95f3fa999509c03cbc"
dependencies = [
"bitflags",
"bitflags 2.6.0",
"core-foundation 0.10.0",
"core-foundation-sys",
"libc",
@@ -2005,15 +2253,36 @@ dependencies = [
"syn",
]
[[package]]
name = "system-configuration"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
dependencies = [
"bitflags 1.3.2",
"core-foundation 0.9.4",
"system-configuration-sys 0.5.0",
]
[[package]]
name = "system-configuration"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
"bitflags",
"bitflags 2.6.0",
"core-foundation 0.9.4",
"system-configuration-sys",
"system-configuration-sys 0.6.0",
]
[[package]]
name = "system-configuration-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
@@ -2167,13 +2436,23 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
"rustls 0.21.12",
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
dependencies = [
"rustls",
"rustls 0.23.20",
"tokio",
]
@@ -2557,6 +2836,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki-roots"
version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "winapi"
version = "0.3.9"
@@ -2594,7 +2879,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -2605,7 +2890,7 @@ checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
dependencies = [
"windows-result",
"windows-strings",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -2614,7 +2899,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -2624,7 +2909,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
@@ -2633,7 +2927,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -2642,7 +2936,22 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
@@ -2651,28 +2960,46 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@@ -2685,24 +3012,48 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
@@ -2718,6 +3069,16 @@ dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
]
[[package]]
name = "write16"
version = "1.0.0"
@@ -2835,3 +3196,31 @@ dependencies = [
"quote",
"syn",
]
[[package]]
name = "zstd"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "7.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.13+zstd.1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
dependencies = [
"cc",
"pkg-config",
]

View File

@@ -6,9 +6,12 @@ edition = "2021"
[dependencies]
anyhow = "1.0.95"
chrono = { version = "0.4.39", features = ["serde"] }
dotenv = "0.15"
futures-util = "0.3.31"
rand = "0.8.5"
salvo = { version = "0.74.3", features = ["affix-state", "catch-panic", "cors", "logging", "serve-static", "websocket"] }
regex = "1.10"
reqwest = { version = "0.11", features = ["json", "rustls-tls"], default-features = false }
salvo = { version = "0.74.3", features = ["affix-state", "catch-panic", "cors", "logging", "serve-static", "test", "websocket"] }
serde = { version = "1.0.216", features = ["derive"] }
serde_json = "1.0.134"
tokio = { version = "1", features = ["macros"] }

View File

@@ -51,5 +51,10 @@ COPY --from=builder-demo /build/demo/target/x86_64-pc-windows-gnu/release/demo.e
COPY --from=builder-demo /build/demo/target/x86_64-unknown-linux-gnu/release/demo ./demo-linux
COPY --from=builder-server /build/server/target/release/dynamic-preauth ./dynamic-preauth
EXPOSE 5800
# Build-time arg for PORT, default to 5800
ARG PORT=5800
# Runtime environment var for PORT, default to build-time arg
ENV PORT=${PORT}
EXPOSE ${PORT}
CMD ["/app/dynamic-preauth"]

View File

@@ -55,7 +55,7 @@ fn main() {
request(token);
}
Err(e) => {
eprintln!("Token was changed, but is not a valid u32 integer: {}", e);
eprintln!("Token is not a valid u32 integer: {}", e);
eprintln!("Original Value: {}", key_data.value);
return;
}

View File

@@ -130,7 +130,7 @@ export default function DownloadButton({
<MenuItem>
<a
className="group flex w-full items-center gap-2 rounded-lg py-1.5 px-2 data-[focus]:bg-white/10"
href={buildLog}
href={buildLog.startsWith('/') ? withBackend(buildLog) : buildLog}
target="_blank"
>
<BeakerIcon className="size-4 fill-white/40" />

View File

@@ -19,9 +19,10 @@ use tracing_subscriber::EnvFilter;
use crate::models::State;
static STORE: LazyLock<Mutex<State>> = LazyLock::new(State::new);
static STORE: LazyLock<Mutex<State>> = LazyLock::new(|| Mutex::new(State::new()));
mod models;
mod railway;
mod utility;
#[handler]
@@ -110,7 +111,11 @@ async fn handle_socket(session_id: u32, websocket: WebSocket) {
// Create the executable message first, borrow issues
let executable_message = OutgoingMessage::Executables {
executables: store.executable_json(),
build_log: store.build_log.clone(),
build_log: if store.build_logs.is_some() {
Some("/build-logs".to_string())
} else {
None
},
};
let session = store
@@ -185,6 +190,53 @@ async fn handle_socket(session_id: u32, websocket: WebSocket) {
tokio::task::spawn(fut);
}
#[handler]
pub async fn get_build_logs(req: &mut Request, res: &mut Response, _depot: &mut Depot) {
let store = STORE.lock().await;
if let Some(build_logs) = &store.build_logs {
// Use pre-computed hash for ETag
let etag = format!("\"{:x}\"", build_logs.content_hash);
// Check If-None-Match header
if let Some(if_none_match) = req.headers().get("If-None-Match") {
if if_none_match == &etag {
res.status_code(StatusCode::NOT_MODIFIED);
return;
}
}
// Check If-Modified-Since header
if let Some(if_modified_since) = req.headers().get("If-Modified-Since") {
if let Ok(if_modified_since_str) = if_modified_since.to_str() {
if let Ok(if_modified_since_time) =
chrono::DateTime::parse_from_rfc2822(if_modified_since_str)
{
if build_logs.fetched_at <= if_modified_since_time {
res.status_code(StatusCode::NOT_MODIFIED);
return;
}
}
}
}
res.headers_mut().insert("ETag", etag.parse().unwrap());
res.headers_mut()
.insert("Content-Type", "text/plain; charset=utf-8".parse().unwrap());
res.headers_mut()
.insert("Cache-Control", "public, max-age=300".parse().unwrap());
res.headers_mut().insert(
"Last-Modified",
build_logs.fetched_at.to_rfc2822().parse().unwrap(),
);
res.render(&build_logs.content);
} else {
res.status_code(StatusCode::NOT_FOUND);
res.render("Build logs not available");
}
}
#[handler]
pub async fn download(req: &mut Request, res: &mut Response, depot: &mut Depot) {
let download_id = req
@@ -333,6 +385,9 @@ fn get_session_id(req: &Request, depot: &Depot) -> Option<u32> {
#[tokio::main]
async fn main() {
// Load environment variables from .env file
dotenv::dotenv().ok();
let port = std::env::var("PORT").unwrap_or_else(|_| "5800".to_string());
let addr = format!("0.0.0.0:{}", port);
tracing_subscriber::fmt()
@@ -352,16 +407,41 @@ async fn main() {
// Check if we are deployed on Railway
let is_railway = env::var("RAILWAY_PROJECT_ID").is_ok();
if is_railway {
let build_logs = format!(
// In debug mode, we might not have RAILWAY_DEPLOYMENT_ID set
let deployment_id = if cfg!(debug_assertions) {
env::var("RAILWAY_DEPLOYMENT_ID").unwrap_or_else(|_| "latest".to_string())
} else {
env::var("RAILWAY_DEPLOYMENT_ID").unwrap()
};
let build_logs_url = format!(
"https://railway.com/project/{}/service/{}?environmentId={}&id={}#build",
env::var("RAILWAY_PROJECT_ID").unwrap(),
env::var("RAILWAY_SERVICE_ID").unwrap(),
env::var("RAILWAY_ENVIRONMENT_ID").unwrap(),
env::var("RAILWAY_DEPLOYMENT_ID").unwrap()
deployment_id
);
tracing::info!("Build logs available here: {}", build_logs);
store.build_log = Some(build_logs);
tracing::info!("Build logs available here: {}", build_logs_url);
store.build_log_url = Some(build_logs_url);
// Try to fetch actual build logs using Railway API
if env::var("RAILWAY_TOKEN").is_ok() {
match crate::railway::fetch_build_logs().await {
Ok(build_logs) => {
tracing::info!(
"Successfully fetched build logs ({} bytes)",
build_logs.content.len()
);
store.build_logs = Some(build_logs);
}
Err(e) => {
tracing::warn!("Failed to fetch build logs from Railway API: {}", e);
}
}
} else {
tracing::warn!("RAILWAY_TOKEN not set, skipping build log fetch");
}
}
store.add_executable("Windows", "./demo.exe");
@@ -400,6 +480,8 @@ async fn main() {
.hoop(CatchPanic::new())
// /notify does not need a session, nor should it have one
.push(Router::with_path("notify").post(notify))
// /build-logs does not need a session
.push(Router::with_path("build-logs").get(get_build_logs))
.push(
Router::new()
.hoop(session_middleware)

View File

@@ -1,10 +1,17 @@
use salvo::{http::cookie::Cookie, websocket::Message, Response};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, path};
use tokio::sync::{mpsc::UnboundedSender, Mutex};
use tokio::sync::mpsc::UnboundedSender;
use crate::utility::search;
#[derive(Debug, Clone)]
pub struct BuildLogs {
pub content: String,
pub fetched_at: chrono::DateTime<chrono::Utc>,
pub content_hash: u64,
}
#[derive(Debug, Serialize, Clone)]
pub struct Session {
pub id: u32,
@@ -98,26 +105,25 @@ pub struct SessionDownload {
impl SessionDownload {}
#[derive(Clone, Debug)]
pub struct State<'a> {
// A map of executables, keyed by their type/platform
pub executables: HashMap<&'a str, Executable>,
// A map of sessions, keyed by their identifier (a random number)
#[derive(Default)]
pub struct State {
pub sessions: HashMap<u32, Session>,
// Provided on startup, the URL to the build log of the current deployment
pub build_log: Option<String>,
pub executables: HashMap<String, Executable>,
pub build_logs: Option<BuildLogs>,
pub build_log_url: Option<String>,
}
impl<'a> State<'a> {
pub fn new() -> Mutex<Self> {
Mutex::new(Self {
build_log: None,
executables: HashMap::new(),
impl State {
pub fn new() -> Self {
Self {
sessions: HashMap::new(),
})
executables: HashMap::new(),
build_logs: None,
build_log_url: None,
}
}
pub fn add_executable(&mut self, exe_type: &'a str, exe_path: &str) {
pub fn add_executable(&mut self, exe_type: &str, exe_path: &str) {
let data = std::fs::read(&exe_path).expect("Unable to read file");
let pattern = "a".repeat(1024);
@@ -140,7 +146,7 @@ impl<'a> State<'a> {
key_end: key_end,
};
self.executables.insert(exe_type, exe);
self.executables.insert(exe_type.to_string(), exe);
}
pub async fn new_session(&mut self, res: &mut Response) -> u32 {

276
src/railway.rs Normal file
View File

@@ -0,0 +1,276 @@
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::env;
#[derive(Debug, Serialize)]
struct GraphQLRequest {
query: String,
variables: serde_json::Value,
}
#[derive(Debug, Deserialize)]
struct GraphQLResponse {
data: Option<serde_json::Value>,
errors: Option<Vec<GraphQLError>>,
}
#[derive(Debug, Deserialize)]
struct GraphQLError {
message: String,
}
#[derive(Debug, Deserialize)]
struct BuildLogEntry {
message: String,
severity: String,
timestamp: String,
}
#[derive(Debug, Deserialize)]
struct DeploymentNode {
id: String,
}
#[derive(Debug, Deserialize)]
struct DeploymentEdge {
node: DeploymentNode,
}
#[derive(Debug, Deserialize)]
struct DeploymentsConnection {
edges: Vec<DeploymentEdge>,
}
fn strip_ansi_codes(text: &str) -> String {
// Simple regex to remove ANSI escape sequences
let re = regex::Regex::new(r"\x1b\[[0-9;]*[a-zA-Z]").unwrap();
re.replace_all(text, "").to_string()
}
fn should_stop_at_message(message: &str) -> bool {
let clean_message = strip_ansi_codes(message);
// Check for "Build time: X seconds" pattern (case insensitive)
let build_time_pattern = regex::Regex::new(r"(?i)Build\s+time:\s+\d+").unwrap();
if build_time_pattern.is_match(&clean_message) {
return true;
}
// Check for "Starting Container" (case insensitive)
let starting_container_pattern = regex::Regex::new(r"(?i)Starting\s+Container").unwrap();
if starting_container_pattern.is_match(&clean_message) {
return true;
}
false
}
async fn fetch_latest_deployment_id() -> Result<String> {
let token = env::var("RAILWAY_TOKEN")?;
let service_id = env::var("RAILWAY_SERVICE_ID")?;
let project_id = env::var("RAILWAY_PROJECT_ID")?;
let environment_id = env::var("RAILWAY_ENVIRONMENT_ID")?;
let query = r#"
query deployments($input: DeploymentListInput!, $first: Int) {
deployments(input: $input, first: $first) {
edges {
node {
id
}
}
}
}
"#;
let variables = serde_json::json!({
"input": {
"projectId": project_id,
"serviceId": service_id,
"environmentId": environment_id,
"status": {"in": ["SUCCESS", "DEPLOYING", "SLEEPING", "BUILDING"]}
},
"first": 1
});
let request = GraphQLRequest {
query: query.to_string(),
variables,
};
let client = reqwest::Client::new();
let response = client
.post("https://backboard.railway.app/graphql/v2")
.header("Authorization", format!("Bearer {}", token))
.json(&request)
.send()
.await?;
let response_text = response.text().await?;
let graphql_response: GraphQLResponse = serde_json::from_str(&response_text)?;
if let Some(errors) = graphql_response.errors {
let error_messages: Vec<String> = errors.iter().map(|e| e.message.clone()).collect();
return Err(anyhow::anyhow!(
"GraphQL errors: {}",
error_messages.join(", ")
));
}
if let Some(data) = graphql_response.data {
if let Some(deployments_value) = data.get("deployments") {
if let Ok(deployments) =
serde_json::from_value::<DeploymentsConnection>(deployments_value.clone())
{
if let Some(first_edge) = deployments.edges.first() {
return Ok(first_edge.node.id.clone());
}
}
}
}
Err(anyhow::anyhow!(
"No deployments found or unexpected response structure"
))
}
pub async fn fetch_build_logs() -> Result<crate::models::BuildLogs> {
let token = env::var("RAILWAY_TOKEN")?;
// Get deployment ID - in debug mode, fetch latest if not specified
let deployment_id = if cfg!(debug_assertions) {
match env::var("RAILWAY_DEPLOYMENT_ID") {
Ok(id) => id,
Err(_) => {
tracing::debug!(
"No RAILWAY_DEPLOYMENT_ID specified in debug mode, fetching latest deployment"
);
fetch_latest_deployment_id().await?
}
}
} else {
env::var("RAILWAY_DEPLOYMENT_ID")?
};
let query = r#"
query buildLogs($deploymentId: String!, $endDate: DateTime, $filter: String, $limit: Int, $startDate: DateTime) {
buildLogs(
deploymentId: $deploymentId
endDate: $endDate
filter: $filter
limit: $limit
startDate: $startDate
) {
message
severity
timestamp
}
}
"#;
let variables = serde_json::json!({
"deploymentId": deployment_id,
"limit": 1000
});
let request = GraphQLRequest {
query: query.to_string(),
variables,
};
let client = reqwest::Client::new();
let response = client
.post("https://backboard.railway.app/graphql/v2")
.header("Authorization", format!("Bearer {}", token))
.json(&request)
.send()
.await?;
let response_text = response.text().await?;
let graphql_response: GraphQLResponse = serde_json::from_str(&response_text)?;
if let Some(errors) = graphql_response.errors {
let error_messages: Vec<String> = errors.iter().map(|e| e.message.clone()).collect();
return Err(anyhow::anyhow!(
"GraphQL errors: {}",
error_messages.join(", ")
));
}
if let Some(data) = graphql_response.data {
if let Some(build_logs_value) = data.get("buildLogs") {
if let Ok(build_logs) =
serde_json::from_value::<Vec<BuildLogEntry>>(build_logs_value.clone())
{
let mut filtered_logs = Vec::new();
for entry in build_logs {
// Check if we should stop at this message
if should_stop_at_message(&entry.message) {
// For "Build time" messages, include them
// For "Starting Container" messages, stop before them
let clean_message = strip_ansi_codes(&entry.message);
let starting_container_pattern =
regex::Regex::new(r"(?i)Starting\s+Container").unwrap();
if starting_container_pattern.is_match(&clean_message) {
// Stop before "Starting Container" message
break;
} else {
// Include "Build time" message and stop
let formatted_entry = format!(
"{} {} {}",
entry.timestamp,
entry.severity,
clean_message.trim()
);
filtered_logs.push(formatted_entry);
break;
}
}
// Include this log entry
let clean_message = strip_ansi_codes(&entry.message);
let formatted_entry = format!(
"{} {} {}",
entry.timestamp,
entry.severity,
clean_message.trim()
);
filtered_logs.push(formatted_entry);
}
// Add Railway URL header to the logs
let railway_url = format!(
"Railway Build Logs: https://railway.com/project/{}/service/{}?environmentId={}&id={}#build\n\n",
env::var("RAILWAY_PROJECT_ID").unwrap_or_default(),
env::var("RAILWAY_SERVICE_ID").unwrap_or_default(),
env::var("RAILWAY_ENVIRONMENT_ID").unwrap_or_default(),
deployment_id
);
let content = format!("{}{}", railway_url, filtered_logs.join("\n"));
let fetched_at = chrono::Utc::now();
// Generate hash for the content
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
content.hash(&mut hasher);
let content_hash = hasher.finish();
return Ok(crate::models::BuildLogs {
content,
fetched_at,
content_hash,
});
}
}
Err(anyhow::anyhow!(
"Unexpected response structure from Railway API"
))
} else {
Err(anyhow::anyhow!("No data received from Railway API"))
}
}