mirror of https://github.com/ospab/ostp.git
feat: embed web panel via rust-embed with login page and custom webpath
This commit is contained in:
parent
8c03903524
commit
7f499d6263
|
|
@ -120,6 +120,17 @@ jobs:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# ── Frontend Build ─────────────────────────────────────────────────────
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
- name: Build Web Panel
|
||||||
|
working-directory: ostp-control
|
||||||
|
run: |
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
|
||||||
# ── Rust toolchain ─────────────────────────────────────────────────────
|
# ── Rust toolchain ─────────────────────────────────────────────────────
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
|
||||||
|
|
@ -441,6 +441,12 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.14"
|
version = "0.3.14"
|
||||||
|
|
@ -463,6 +469,12 @@ version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foldhash"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
|
|
@ -549,11 +561,24 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"r-efi",
|
"r-efi 5.3.0",
|
||||||
"wasip2",
|
"wasip2",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi 6.0.0",
|
||||||
|
"wasip2",
|
||||||
|
"wasip3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ghash"
|
name = "ghash"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
|
@ -564,6 +589,21 @@ dependencies = [
|
||||||
"polyval",
|
"polyval",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||||
|
dependencies = [
|
||||||
|
"foldhash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.17.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
|
@ -790,6 +830,12 @@ dependencies = [
|
||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "id-arena"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
@ -811,6 +857,18 @@ dependencies = [
|
||||||
"icu_properties",
|
"icu_properties",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown 0.17.1",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inout"
|
name = "inout"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
|
@ -906,6 +964,12 @@ version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "leb128fmt"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.185"
|
version = "0.2.185"
|
||||||
|
|
@ -957,6 +1021,16 @@ version = "0.3.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mime_guess"
|
||||||
|
version = "2.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
|
||||||
|
dependencies = [
|
||||||
|
"mime",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
|
@ -1098,11 +1172,13 @@ dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hmac",
|
"hmac",
|
||||||
"json_comments",
|
"json_comments",
|
||||||
|
"mime_guess",
|
||||||
"ostp-core",
|
"ostp-core",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rcgen",
|
"rcgen",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"rust-embed",
|
||||||
"rustls",
|
"rustls",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
@ -1112,6 +1188,7 @@ dependencies = [
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1203,6 +1280,16 @@ dependencies = [
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prettyplease"
|
||||||
|
version = "0.2.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.106"
|
version = "1.0.106"
|
||||||
|
|
@ -1282,6 +1369,12 @@ version = "5.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
|
@ -1423,6 +1516,40 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed"
|
||||||
|
version = "8.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27"
|
||||||
|
dependencies = [
|
||||||
|
"rust-embed-impl",
|
||||||
|
"rust-embed-utils",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed-impl"
|
||||||
|
version = "8.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rust-embed-utils",
|
||||||
|
"syn",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed-utils"
|
||||||
|
version = "8.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1"
|
||||||
|
dependencies = [
|
||||||
|
"sha2",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "2.1.2"
|
version = "2.1.2"
|
||||||
|
|
@ -1949,12 +2076,24 @@ version = "1.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.24"
|
version = "1.0.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "universal-hash"
|
name = "universal-hash"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
|
@ -1995,6 +2134,18 @@ version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.23.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.4.2",
|
||||||
|
"js-sys",
|
||||||
|
"serde_core",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "valuable"
|
name = "valuable"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
|
@ -2038,7 +2189,16 @@ version = "1.0.3+wasi-0.2.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
|
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wit-bindgen",
|
"wit-bindgen 0.57.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasip3"
|
||||||
|
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen 0.51.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2096,6 +2256,40 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-encoder"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||||
|
dependencies = [
|
||||||
|
"leb128fmt",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-metadata"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"indexmap",
|
||||||
|
"wasm-encoder",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasmparser"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"hashbrown 0.15.5",
|
||||||
|
"indexmap",
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.98"
|
version = "0.3.98"
|
||||||
|
|
@ -2359,12 +2553,100 @@ dependencies = [
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen-rust-macro",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen"
|
name = "wit-bindgen"
|
||||||
version = "0.57.1"
|
version = "0.57.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
|
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-core"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"heck",
|
||||||
|
"wit-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rust"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"heck",
|
||||||
|
"indexmap",
|
||||||
|
"prettyplease",
|
||||||
|
"syn",
|
||||||
|
"wasm-metadata",
|
||||||
|
"wit-bindgen-core",
|
||||||
|
"wit-component",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rust-macro"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"prettyplease",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wit-bindgen-core",
|
||||||
|
"wit-bindgen-rust",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-component"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bitflags",
|
||||||
|
"indexmap",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"wasm-encoder",
|
||||||
|
"wasm-metadata",
|
||||||
|
"wasmparser",
|
||||||
|
"wit-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-parser"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"id-arena",
|
||||||
|
"indexmap",
|
||||||
|
"log",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"unicode-xid",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "writeable"
|
name = "writeable"
|
||||||
version = "0.6.3"
|
version = "0.6.3"
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,9 @@ sha2.workspace = true
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] }
|
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] }
|
||||||
tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "logging", "tls12"] }
|
tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "logging", "tls12"] }
|
||||||
|
rust-embed = "8.4"
|
||||||
|
mime_guess = "2.0"
|
||||||
|
uuid = { version = "1", features = ["v4", "serde"] }
|
||||||
rcgen = "0.13"
|
rcgen = "0.13"
|
||||||
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] }
|
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] }
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,15 @@ use portable_atomic::AtomicU64;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
|
body::Body,
|
||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
http::StatusCode,
|
http::{header, Request, StatusCode, Uri},
|
||||||
response::IntoResponse,
|
response::{IntoResponse, Response},
|
||||||
routing::{get, post, put},
|
routing::{get, post, put},
|
||||||
Json, Router,
|
Json, Router,
|
||||||
};
|
};
|
||||||
|
use rust_embed::RustEmbed;
|
||||||
|
use sha2::Digest;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tower_http::cors::{Any, CorsLayer};
|
use tower_http::cors::{Any, CorsLayer};
|
||||||
|
|
||||||
|
|
@ -39,7 +42,10 @@ pub struct ApiState {
|
||||||
pub access_keys: Arc<RwLock<HashMap<String, UserMeta>>>,
|
pub access_keys: Arc<RwLock<HashMap<String, UserMeta>>>,
|
||||||
pub user_stats: Arc<RwLock<HashMap<String, Arc<UserStats>>>>,
|
pub user_stats: Arc<RwLock<HashMap<String, Arc<UserStats>>>>,
|
||||||
pub start_time: Instant,
|
pub start_time: Instant,
|
||||||
pub api_token: String,
|
pub session_token: Arc<RwLock<Option<String>>>,
|
||||||
|
pub webpath: String,
|
||||||
|
pub username: String,
|
||||||
|
pub password_hash: String,
|
||||||
/// Server address for subscription links (e.g. "example.com")
|
/// Server address for subscription links (e.g. "example.com")
|
||||||
pub server_host: String,
|
pub server_host: String,
|
||||||
pub server_port: u16,
|
pub server_port: u16,
|
||||||
|
|
@ -53,7 +59,12 @@ pub struct ApiState {
|
||||||
pub struct ApiConfig {
|
pub struct ApiConfig {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub bind: String,
|
pub bind: String,
|
||||||
pub token: String,
|
#[serde(default)]
|
||||||
|
pub webpath: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub username: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub password_hash: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ApiConfig {
|
impl Default for ApiConfig {
|
||||||
|
|
@ -61,7 +72,9 @@ impl Default for ApiConfig {
|
||||||
Self {
|
Self {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
bind: "127.0.0.1:9090".to_string(),
|
bind: "127.0.0.1:9090".to_string(),
|
||||||
token: String::new(),
|
webpath: String::new(),
|
||||||
|
username: String::new(),
|
||||||
|
password_hash: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -100,6 +113,17 @@ pub struct SetLimitRequest {
|
||||||
pub limit_bytes: Option<u64>,
|
pub limit_bytes: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct LoginRequest {
|
||||||
|
pub username: String,
|
||||||
|
pub password: Option<String>, // We'll accept raw password and hash it
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct LoginResponse {
|
||||||
|
pub token: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct ApiResponse<T: Serialize> {
|
struct ApiResponse<T: Serialize> {
|
||||||
ok: bool,
|
ok: bool,
|
||||||
|
|
@ -123,6 +147,37 @@ fn api_unauthorized<T: Serialize>() -> (StatusCode, Json<ApiResponse<T>>) {
|
||||||
(StatusCode::UNAUTHORIZED, Json(ApiResponse { ok: false, data: None, error: Some("unauthorized".to_string()) }))
|
(StatusCode::UNAUTHORIZED, Json(ApiResponse { ok: false, data: None, error: Some("unauthorized".to_string()) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(RustEmbed)]
|
||||||
|
#[folder = "../ostp-control/dist/"]
|
||||||
|
struct Assets;
|
||||||
|
|
||||||
|
async fn static_handler(State(state): State<ApiState>, uri: Uri) -> impl IntoResponse {
|
||||||
|
let mut path = uri.path();
|
||||||
|
let prefix = format!("/{}", state.webpath);
|
||||||
|
if path.starts_with(&prefix) {
|
||||||
|
path = &path[prefix.len()..];
|
||||||
|
}
|
||||||
|
path = path.trim_start_matches('/');
|
||||||
|
|
||||||
|
if path.is_empty() || path == "index.html" {
|
||||||
|
path = "index.html";
|
||||||
|
}
|
||||||
|
|
||||||
|
match Assets::get(path) {
|
||||||
|
Some(content) => {
|
||||||
|
let mime = mime_guess::from_path(path).first_or_octet_stream();
|
||||||
|
([(header::CONTENT_TYPE, mime.as_ref())], content.data).into_response()
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if let Some(index) = Assets::get("index.html") {
|
||||||
|
([(header::CONTENT_TYPE, "text/html")], index.data).into_response()
|
||||||
|
} else {
|
||||||
|
(StatusCode::NOT_FOUND, "404 Not Found").into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── API router ───────────────────────────────────────────────────────────────
|
// ── API router ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
pub fn create_api_router(state: ApiState) -> Router {
|
pub fn create_api_router(state: ApiState) -> Router {
|
||||||
|
|
@ -131,22 +186,37 @@ pub fn create_api_router(state: ApiState) -> Router {
|
||||||
.allow_methods(Any)
|
.allow_methods(Any)
|
||||||
.allow_headers(Any);
|
.allow_headers(Any);
|
||||||
|
|
||||||
Router::new()
|
let api_router = Router::new()
|
||||||
.route("/api/server/status", get(handle_status))
|
.route("/server/status", get(handle_status))
|
||||||
.route("/api/server/config", get(handle_get_config).put(handle_put_config))
|
.route("/server/config", get(handle_get_config).put(handle_put_config))
|
||||||
.route(
|
.route(
|
||||||
"/api/users",
|
"/users",
|
||||||
get(handle_list_users).post(handle_create_user),
|
get(handle_list_users).post(handle_create_user),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/api/users/{key}",
|
"/users/{key}",
|
||||||
get(handle_get_user)
|
get(handle_get_user)
|
||||||
.put(update_user)
|
.put(update_user)
|
||||||
.delete(delete_user),
|
.delete(delete_user),
|
||||||
)
|
)
|
||||||
.route("/api/users/{key}/limit", put(handle_set_limit))
|
.route("/users/{key}/limit", put(handle_set_limit))
|
||||||
.route("/api/users/{key}/reset", post(handle_reset_stats))
|
.route("/users/{key}/reset", post(handle_reset_stats))
|
||||||
.route("/api/subscribe/{key}", get(handle_subscribe))
|
.route("/subscribe/{key}", get(handle_subscribe))
|
||||||
|
.route("/login", post(handle_login));
|
||||||
|
|
||||||
|
let webpath = state.webpath.clone();
|
||||||
|
let webpath = webpath.trim_matches('/');
|
||||||
|
|
||||||
|
// If no webpath is provided, default to random path to hide panel
|
||||||
|
let base_route = if webpath.is_empty() {
|
||||||
|
"/panel".to_string()
|
||||||
|
} else {
|
||||||
|
format!("/{}", webpath)
|
||||||
|
};
|
||||||
|
|
||||||
|
Router::new()
|
||||||
|
.nest(&format!("{}/api", base_route), api_router)
|
||||||
|
.nest(&base_route, Router::new().fallback(get(static_handler)))
|
||||||
.layer(cors)
|
.layer(cors)
|
||||||
.with_state(state)
|
.with_state(state)
|
||||||
}
|
}
|
||||||
|
|
@ -165,7 +235,10 @@ pub async fn start_api_server(
|
||||||
access_keys,
|
access_keys,
|
||||||
user_stats,
|
user_stats,
|
||||||
start_time: Instant::now(),
|
start_time: Instant::now(),
|
||||||
api_token: config.token.clone(),
|
session_token: Arc::new(RwLock::new(None)),
|
||||||
|
webpath: config.webpath.clone(),
|
||||||
|
username: config.username.clone(),
|
||||||
|
password_hash: config.password_hash.clone(),
|
||||||
server_host,
|
server_host,
|
||||||
server_port,
|
server_port,
|
||||||
reality_query,
|
reality_query,
|
||||||
|
|
@ -192,18 +265,53 @@ pub async fn start_api_server(
|
||||||
// ── Middleware: token check ──────────────────────────────────────────────────
|
// ── Middleware: token check ──────────────────────────────────────────────────
|
||||||
|
|
||||||
fn check_token(state: &ApiState, headers: &axum::http::HeaderMap) -> bool {
|
fn check_token(state: &ApiState, headers: &axum::http::HeaderMap) -> bool {
|
||||||
if state.api_token.is_empty() {
|
// If no credentials configured, panel is open (unsafe but possible)
|
||||||
return true; // No auth required if token is empty
|
if state.username.is_empty() && state.password_hash.is_empty() {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
match headers.get("authorization") {
|
match headers.get("authorization") {
|
||||||
Some(value) => {
|
Some(value) => {
|
||||||
let val = value.to_str().unwrap_or("");
|
let val = value.to_str().unwrap_or("");
|
||||||
val == format!("Bearer {}", state.api_token) || val == state.api_token
|
if let Some(token) = val.strip_prefix("Bearer ") {
|
||||||
|
let current_session = state.session_token.read().unwrap().clone();
|
||||||
|
if let Some(session) = current_session {
|
||||||
|
if token == session {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_login(
|
||||||
|
State(state): State<ApiState>,
|
||||||
|
Json(payload): Json<LoginRequest>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
if state.username.is_empty() || state.password_hash.is_empty() {
|
||||||
|
return api_error("Auth not configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload.username != state.username {
|
||||||
|
return api_unauthorized::<LoginResponse>();
|
||||||
|
}
|
||||||
|
|
||||||
|
let password = payload.password.unwrap_or_default();
|
||||||
|
let hash = sha2::Sha256::digest(password.as_bytes());
|
||||||
|
let hash_hex = format!("{:x}", hash);
|
||||||
|
|
||||||
|
if hash_hex == state.password_hash {
|
||||||
|
let token = uuid::Uuid::new_v4().to_string();
|
||||||
|
*state.session_token.write().unwrap() = Some(token.clone());
|
||||||
|
(StatusCode::OK, ApiResponse::success(LoginResponse { token }))
|
||||||
|
} else {
|
||||||
|
api_unauthorized::<LoginResponse>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn save_config_keys(state: &ApiState) -> Result<(), String> {
|
fn save_config_keys(state: &ApiState) -> Result<(), String> {
|
||||||
let Some(ref path) = state.config_path else {
|
let Some(ref path) = state.config_path else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
|
||||||
|
|
@ -293,7 +293,9 @@ impl ListenConfig {
|
||||||
struct ApiConfig {
|
struct ApiConfig {
|
||||||
enabled: Option<bool>,
|
enabled: Option<bool>,
|
||||||
bind: Option<String>,
|
bind: Option<String>,
|
||||||
token: Option<String>,
|
webpath: Option<String>,
|
||||||
|
username: Option<String>,
|
||||||
|
password_hash: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
|
@ -861,7 +863,9 @@ async fn run_app() -> Result<()> {
|
||||||
let api_config = server_cfg.api.map(|a| ostp_server::ApiConfig {
|
let api_config = server_cfg.api.map(|a| ostp_server::ApiConfig {
|
||||||
enabled: a.enabled.unwrap_or(false),
|
enabled: a.enabled.unwrap_or(false),
|
||||||
bind: a.bind.unwrap_or_else(|| "127.0.0.1:9090".to_string()),
|
bind: a.bind.unwrap_or_else(|| "127.0.0.1:9090".to_string()),
|
||||||
token: a.token.unwrap_or_default(),
|
webpath: a.webpath.unwrap_or_default(),
|
||||||
|
username: a.username.unwrap_or_default(),
|
||||||
|
password_hash: a.password_hash.unwrap_or_default(),
|
||||||
});
|
});
|
||||||
let fallback_config = server_cfg.fallback.map(|f| ostp_server::FallbackConfig {
|
let fallback_config = server_cfg.fallback.map(|f| ostp_server::FallbackConfig {
|
||||||
enabled: f.enabled.unwrap_or(false),
|
enabled: f.enabled.unwrap_or(false),
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,14 @@ if (Test-Path $CargoToml) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# --- Pre-flight: frontend build ---
|
||||||
|
Write-Output ""
|
||||||
|
Write-Output "Building frontend control panel..."
|
||||||
|
Push-Location (Join-Path $ProjectRoot "ostp-control")
|
||||||
|
& npm install | Out-Null
|
||||||
|
& npm run build | Out-Null
|
||||||
|
Pop-Location
|
||||||
|
|
||||||
# --- Pre-flight: cargo check ---
|
# --- Pre-flight: cargo check ---
|
||||||
Write-Output ""
|
Write-Output ""
|
||||||
Write-Output "Running pre-flight cargo check..."
|
Write-Output "Running pre-flight cargo check..."
|
||||||
|
|
|
||||||
|
|
@ -199,8 +199,9 @@ echo "Select mode:"
|
||||||
echo " 1) Server"
|
echo " 1) Server"
|
||||||
echo " 2) Client"
|
echo " 2) Client"
|
||||||
echo " 3) Relay"
|
echo " 3) Relay"
|
||||||
|
echo " 4) Server + Web Panel"
|
||||||
echo "--------------------------------------------------------"
|
echo "--------------------------------------------------------"
|
||||||
read -p "Choice [1-3]: " NODE_MODE
|
read -p "Choice [1-4]: " NODE_MODE
|
||||||
|
|
||||||
cd "$INSTALL_DIR"
|
cd "$INSTALL_DIR"
|
||||||
|
|
||||||
|
|
@ -242,6 +243,59 @@ with open('$CONFIG_FILE', 'w') as f:
|
||||||
echo ""
|
echo ""
|
||||||
echo "Server configuration saved: $CONFIG_FILE"
|
echo "Server configuration saved: $CONFIG_FILE"
|
||||||
|
|
||||||
|
elif [ "$NODE_MODE" == "4" ]; then
|
||||||
|
echo "Initializing server configuration..."
|
||||||
|
./ostp --init server --config "$CONFIG_FILE"
|
||||||
|
|
||||||
|
read -p "Listen address [default: 0.0.0.0:50000]: " LISTEN_ADDR
|
||||||
|
if [ -n "$LISTEN_ADDR" ]; then
|
||||||
|
sed -i "s/\"listen\": \".*\"/\"listen\": \"$LISTEN_ADDR\"/g" "$CONFIG_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Panel Setup
|
||||||
|
echo "--- Web Panel Setup ---"
|
||||||
|
read -p "Panel port [default: 9090]: " PANEL_PORT
|
||||||
|
PANEL_PORT=${PANEL_PORT:-9090}
|
||||||
|
|
||||||
|
RANDOM_PATH=$(tr -dc 'a-zA-Z0-9' < /dev/urandom | head -c 8)
|
||||||
|
read -p "WebPath (leave empty for random: /$RANDOM_PATH/): " WEBPATH
|
||||||
|
WEBPATH=${WEBPATH:-$RANDOM_PATH}
|
||||||
|
|
||||||
|
read -p "Username [default: admin]: " USERNAME
|
||||||
|
USERNAME=${USERNAME:-admin}
|
||||||
|
|
||||||
|
RANDOM_PASS=$(tr -dc 'a-zA-Z0-9' < /dev/urandom | head -c 12)
|
||||||
|
read -p "Password (leave empty for random: $RANDOM_PASS): " PASSWORD
|
||||||
|
PASSWORD=${PASSWORD:-$RANDOM_PASS}
|
||||||
|
|
||||||
|
# Hash password with python
|
||||||
|
PASS_HASH=$(python3 -c "import hashlib; print(hashlib.sha256('$PASSWORD'.encode()).hexdigest())")
|
||||||
|
|
||||||
|
# Inject into config
|
||||||
|
python3 -c "
|
||||||
|
import json
|
||||||
|
with open('$CONFIG_FILE') as f:
|
||||||
|
lines = [l for l in f.read().split('\n') if not l.strip().startswith('//')]
|
||||||
|
cfg = json.loads('\n'.join(lines))
|
||||||
|
if 'api' not in cfg:
|
||||||
|
cfg['api'] = {}
|
||||||
|
cfg['api']['enabled'] = True
|
||||||
|
cfg['api']['bind'] = '0.0.0.0:' + str('$PANEL_PORT')
|
||||||
|
cfg['api']['webpath'] = '$WEBPATH'
|
||||||
|
cfg['api']['username'] = '$USERNAME'
|
||||||
|
cfg['api']['password_hash'] = '$PASS_HASH'
|
||||||
|
with open('$CONFIG_FILE', 'w') as f:
|
||||||
|
json.dump(cfg, f, indent=2)
|
||||||
|
" 2>/dev/null || echo "[warn] Failed to configure panel via python. Edit config manually."
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================================"
|
||||||
|
echo "Panel installed successfully!"
|
||||||
|
echo "URL: http://<your_server_ip>:$PANEL_PORT/$WEBPATH/"
|
||||||
|
echo "Username: $USERNAME"
|
||||||
|
echo "Password: $PASSWORD"
|
||||||
|
echo "========================================================"
|
||||||
|
|
||||||
elif [ "$NODE_MODE" == "2" ]; then
|
elif [ "$NODE_MODE" == "2" ]; then
|
||||||
echo "Initializing client configuration..."
|
echo "Initializing client configuration..."
|
||||||
./ostp --init client --config "$CONFIG_FILE"
|
./ostp --init client --config "$CONFIG_FILE"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue