From 310cebbaab85729563fedc54e5d0961b94f09918 Mon Sep 17 00:00:00 2001 From: oiupoyt Date: Fri, 25 Sep 2026 16:21:26 +0530 Subject: [PATCH] feat: rewrite backend in rust (axum/tokio), remove settings modal from frontend --- .gitignore | 2 + README.md | 2 +- backend/Cargo.lock | 2045 +++++++++++++++++++++++++++++++++++++ backend/Cargo.toml | 26 + backend/checker.js | 93 -- backend/fetcher.js | 166 --- backend/package-lock.json | 863 ---------------- backend/package.json | 19 - backend/server.js | 277 ----- backend/src/checker.rs | 61 ++ backend/src/fetcher.rs | 192 ++++ backend/src/main.rs | 438 ++++++++ backend/src/models.rs | 117 +++ backend/start.sh | 8 +- backend/status.sh | 2 +- package.json | 14 +- public/app.js | 45 +- public/index.html | 31 +- public/style.css | 105 -- 19 files changed, 2895 insertions(+), 1611 deletions(-) create mode 100644 backend/Cargo.lock create mode 100644 backend/Cargo.toml delete mode 100644 backend/checker.js delete mode 100644 backend/fetcher.js delete mode 100644 backend/package-lock.json delete mode 100644 backend/package.json delete mode 100644 backend/server.js create mode 100644 backend/src/checker.rs create mode 100644 backend/src/fetcher.rs create mode 100644 backend/src/main.rs create mode 100644 backend/src/models.rs diff --git a/.gitignore b/.gitignore index 011e9af..fcb629c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ node_modules/ npm-debug.log* yarn-debug.log* yarn-error.log* +target/ +*.pid diff --git a/README.md b/README.md index 9a21a5f..1d3ec6f 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ curl -s "https://katproxy-api.oiupoyt.space/api/raw?protocol=socks5&alive=true" ## Architecture -- **Backend (`/backend`)**: Node.js service running on port `5050` with controlled memory allocation (`--max-old-space-size=256`). Includes background socket prober and auto-refresh worker. +- **Backend (`/backend`)**: High-performance Rust (Axum + Tokio) service running on port `5050`. Features non-blocking TCP socket prober and automated refresh background worker. - **Frontend (`/public`)**: Vanilla JavaScript, CSS, and HTML with JetBrains Mono typography, auto-refresh countdown, and one-click copy actions. - **Tunnel**: Managed by `cloudflared` routing `katproxy-api.oiupoyt.space` to `localhost:5050`. diff --git a/backend/Cargo.lock b/backend/Cargo.lock new file mode 100644 index 0000000..d717915 --- /dev/null +++ b/backend/Cargo.lock @@ -0,0 +1,2045 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + +[[package]] +name = "async-compression" +version = "0.4.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb61aea1a7def73ee7c350a184f0e70b32c182344e2e75bf70c9b621b83417fd" +dependencies = [ + "compression-codecs", + "compression-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-trait" +version = "0.1.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower 0.5.3", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + +[[package]] +name = "brotli" +version = "8.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + +[[package]] +name = "cc" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "207aedd8d29a0eb804986bc99eed0d43719fec84a9b7f9c8b70cf83b97cffcc1" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "chacha20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cpufeatures", + "rand_core", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "compression-codecs" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef16c47ba2797aa6a909cc37d39911f3a6743811fe7408ac0b0cc0276b656e9" +dependencies = [ + "brotli", + "compression-core", + "flate2", + "memchr", + "zstd", + "zstd-safe", +] + +[[package]] +name = "compression-core" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e8ccc4ea9f6acc32d102c0f6d471d11d913ad15f20c04de743374861fa1d414" + +[[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.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" + +[[package]] +name = "flate2" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-executor" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "futures-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +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.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "rand_core", + "wasm-bindgen", +] + +[[package]] +name = "h2" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc03d96684f9226b8a787cdb71488417b53ab5ea8fdb1dac946cb9431cc8bff" +dependencies = [ + "base64 0.23.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "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]] +name = "icu_collections" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" + +[[package]] +name = "icu_properties" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" + +[[package]] +name = "icu_provider" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "katproxy-backend" +version = "1.0.0" +dependencies = [ + "axum", + "chrono", + "futures", + "reqwest", + "serde", + "serde_json", + "tokio", + "tower 0.4.13", + "tower-http 0.5.2", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "lru-slab" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +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]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "zerovec", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4051e23e9185c255a7e33ef59cdbca87a22d359052eecd22fc6b901fb37d9d11" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9746dbde176634f4f2f1faf2404e30a31b2bc1e9cafb5329c95d8177a18c9fc" +dependencies = [ + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand", + "rand_pcg", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "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 0.5.3", + "tower-http 0.6.11", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustls" +version = "0.23.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e" + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +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" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "thread_local" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tinystr" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" + +[[package]] +name = "tokio" +version = "1.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c85f2c3ef0b1cd58b36682f4b17aaa995f0e5db534d85692b4903abce21f67" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "libc", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "async-compression", + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower 0.5.3", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-ident" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[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 = "wasm-bindgen" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbab34de2d982e9b48e18d216d04c4a6f641066ff19ffb699980f591ee3610e" +dependencies = [ + "js-sys", + "tokio", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.6", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88261b9deccee56594c11a3460c462c41f58d148598fe70ad77070126a68aba4" +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 = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +dependencies = [ + "rustls-pki-types", +] + +[[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 2.0.119", +] + +[[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 2.0.119", +] + +[[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.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +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_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33811428bee40dbceb6d545e95754741d17a6aef9a4849f0fd62e2ba4f412a78" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "zlib-rs" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" + +[[package]] +name = "zstd" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf06bd8162af0734b344780deb55b42a2429ae430870d13fcc12f238e880fe6e" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae42c0555055784c70058d19ba8e275528e8a99a706684868ace5da4e716a4ab" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.1.0+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef0a8027ec3ee71300ab3bcbcd0393f434aa72b91ca6d635a39941deae8eea0" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/backend/Cargo.toml b/backend/Cargo.toml new file mode 100644 index 0000000..6621fc2 --- /dev/null +++ b/backend/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "katproxy-backend" +version = "1.0.0" +edition = "2021" +authors = ["oiupoyt"] +description = "High-performance minimal Rust proxy aggregator and latency tester" + +[dependencies] +axum = { version = "0.7", default-features = false, features = ["tokio", "http1", "http2", "json", "matched-path", "query"] } +tokio = { version = "1.38", features = ["full"] } +tower = { version = "0.4", features = ["util"] } +tower-http = { version = "0.5", features = ["cors", "trace", "fs", "compression-full"] } +futures = "0.3" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } +chrono = { version = "0.4", features = ["serde"] } + +[profile.release] +opt-level = 3 +lto = "thin" +codegen-units = 1 +panic = "abort" +strip = true diff --git a/backend/checker.js b/backend/checker.js deleted file mode 100644 index a6a0cb4..0000000 --- a/backend/checker.js +++ /dev/null @@ -1,93 +0,0 @@ -const net = require('net'); - -/** - * Probe a single proxy host:port via TCP socket to check if open and measure round-trip latency (ms) - */ -function probeProxy(host, port, timeoutMs = 2000) { - return new Promise((resolve) => { - const startTime = Date.now(); - const socket = new net.Socket(); - - let resolved = false; - const cleanup = () => { - socket.removeAllListeners(); - socket.destroy(); - }; - - socket.setTimeout(timeoutMs); - - socket.connect(port, host, () => { - if (!resolved) { - resolved = true; - const latency = Date.now() - startTime; - cleanup(); - resolve({ alive: true, latency }); - } - }); - - socket.on('timeout', () => { - if (!resolved) { - resolved = true; - cleanup(); - resolve({ alive: false, latency: null, reason: 'timeout' }); - } - }); - - socket.on('error', (err) => { - if (!resolved) { - resolved = true; - cleanup(); - resolve({ alive: false, latency: null, reason: err.code || 'error' }); - } - }); - }); -} - -/** - * Check a batch of proxies with concurrency limit - */ -async function batchCheckProxies(proxies, concurrency = 50, timeoutMs = 2000, onProgress = null) { - let index = 0; - let active = 0; - let completed = 0; - const total = proxies.length; - - return new Promise((resolve) => { - if (total === 0) return resolve(); - - function next() { - while (active < concurrency && index < total) { - const currentIndex = index++; - const proxy = proxies[currentIndex]; - active++; - - probeProxy(proxy.host, proxy.port, timeoutMs).then((res) => { - proxy.alive = res.alive; - if (res.alive) { - proxy.latency = res.latency; - } - proxy.lastChecked = Date.now(); - active--; - completed++; - - if (onProgress && completed % 25 === 0) { - onProgress(completed, total); - } - - if (completed === total) { - resolve(); - } else { - next(); - } - }); - } - } - - next(); - }); -} - -module.exports = { - probeProxy, - batchCheckProxies -}; diff --git a/backend/fetcher.js b/backend/fetcher.js deleted file mode 100644 index e1e48c8..0000000 --- a/backend/fetcher.js +++ /dev/null @@ -1,166 +0,0 @@ -const https = require('https'); -const http = require('http'); - -/** - * Helper to fetch text or JSON over HTTPS with timeout - */ -function fetchUrl(url, timeoutMs = 12000) { - return new Promise((resolve, reject) => { - const client = url.startsWith('https') ? https : http; - const req = client.get(url, { headers: { 'User-Agent': 'katproxy/1.0' } }, (res) => { - if (res.statusCode < 200 || res.statusCode >= 300) { - res.resume(); - return reject(new Error(`Failed to fetch ${url}, status: ${res.statusCode}`)); - } - let rawData = ''; - res.setEncoding('utf8'); - res.on('data', (chunk) => { rawData += chunk; }); - res.on('end', () => resolve(rawData)); - }); - - req.on('error', (err) => reject(err)); - req.setTimeout(timeoutMs, () => { - req.destroy(); - reject(new Error(`Timeout fetching ${url}`)); - }); - }); -} - -/** - * Fetch and merge proxies from multiple premier GitHub repositories - */ -async function fetchAllProxies() { - console.log('[Fetcher] Starting proxy list aggregation...'); - const proxyMap = new Map(); - - // Helper to add or merge a proxy - function addProxy(host, port, protocol, extra = {}) { - if (!host || !port) return; - const cleanHost = String(host).trim(); - const cleanPort = parseInt(port, 10); - if (isNaN(cleanPort) || cleanPort <= 0 || cleanPort > 65535) return; - // Basic IP validation regex - if (!/^(\d{1,3}\.){3}\d{1,3}$/.test(cleanHost)) return; - - const key = `${cleanHost}:${cleanPort}`; - const existing = proxyMap.get(key); - - const protoClean = (protocol || 'http').toLowerCase(); - - if (!existing) { - proxyMap.set(key, { - id: key, - host: cleanHost, - port: cleanPort, - protocols: [protoClean], - country: (extra.country || 'UN').toUpperCase(), - countryName: extra.countryName || 'Unknown', - city: extra.city || '', - org: extra.org || '', - latency: extra.latency != null ? Math.round(extra.latency * 1000) : null, - alive: extra.alive !== undefined ? extra.alive : null, - lastChecked: extra.lastChecked || null, - sources: [extra.source || 'upstream'] - }); - } else { - if (!existing.protocols.includes(protoClean)) { - existing.protocols.push(protoClean); - } - if (existing.country === 'UN' && extra.country) { - existing.country = extra.country.toUpperCase(); - existing.countryName = extra.countryName || existing.countryName; - } - if (!existing.org && extra.org) { - existing.org = extra.org; - } - if (extra.source && !existing.sources.includes(extra.source)) { - existing.sources.push(extra.source); - } - } - } - - // 1. Fetch monosans structured JSON (provides rich metadata: country, org, timeout) - try { - console.log('[Fetcher] Fetching monosans/proxy-list JSON...'); - const monosansData = await fetchUrl('https://raw.githubusercontent.com/monosans/proxy-list/main/proxies.json', 15000); - const parsed = JSON.parse(monosansData); - if (Array.isArray(parsed)) { - for (const item of parsed) { - const countryCode = item.geolocation?.country?.iso_code || 'UN'; - const countryName = item.geolocation?.country?.names?.en || 'Unknown'; - const city = item.geolocation?.city?.names?.en || ''; - const org = item.asn?.autonomous_system_organization || ''; - const latencyMs = item.timeout ? Math.round(item.timeout * 1000) : null; - - addProxy(item.host, item.port, item.protocol, { - country: countryCode, - countryName, - city, - org, - latency: item.timeout != null ? item.timeout : null, - alive: true, - source: 'monosans' - }); - } - console.log(`[Fetcher] monosans parsed: ${parsed.length} proxies`); - } - } catch (err) { - console.warn('[Fetcher] Warning: monosans JSON fetch failed:', err.message); - } - - // 2. TheSpeedX lists (HTTP, SOCKS4, SOCKS5) - const speedXTasks = [ - { url: 'https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/http.txt', protocol: 'http' }, - { url: 'https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/socks4.txt', protocol: 'socks4' }, - { url: 'https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/socks5.txt', protocol: 'socks5' } - ]; - - await Promise.allSettled( - speedXTasks.map(async (task) => { - try { - const text = await fetchUrl(task.url); - const lines = text.split('\n'); - let count = 0; - for (const line of lines) { - const trimmed = line.trim(); - if (!trimmed || trimmed.startsWith('#')) continue; - const [host, port] = trimmed.split(':'); - if (host && port) { - addProxy(host, port, task.protocol, { source: 'thespeedx' }); - count++; - } - } - console.log(`[Fetcher] TheSpeedX ${task.protocol} parsed: ${count} proxies`); - } catch (err) { - console.warn(`[Fetcher] TheSpeedX ${task.protocol} error:`, err.message); - } - }) - ); - - // 3. hookzof SOCKS5 list - try { - const hookzofText = await fetchUrl('https://raw.githubusercontent.com/hookzof/socks5_list/master/proxy.txt'); - const lines = hookzofText.split('\n'); - let count = 0; - for (const line of lines) { - const trimmed = line.trim(); - if (!trimmed || trimmed.startsWith('#')) continue; - const [host, port] = trimmed.split(':'); - if (host && port) { - addProxy(host, port, 'socks5', { source: 'hookzof' }); - count++; - } - } - console.log(`[Fetcher] hookzof SOCKS5 parsed: ${count} proxies`); - } catch (err) { - console.warn('[Fetcher] hookzof SOCKS5 error:', err.message); - } - - const results = Array.from(proxyMap.values()); - console.log(`[Fetcher] Completed aggregation. Total unique proxies: ${results.length}`); - return results; -} - -module.exports = { - fetchAllProxies -}; diff --git a/backend/package-lock.json b/backend/package-lock.json deleted file mode 100644 index 34fcdba..0000000 --- a/backend/package-lock.json +++ /dev/null @@ -1,863 +0,0 @@ -{ - "name": "katproxy-backend", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "katproxy-backend", - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "cors": "^2.8.5", - "express": "^4.19.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/body-parser": { - "version": "1.20.8", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.8.tgz", - "integrity": "sha512-JNcyFQ64OiijEkPzUBTCe+hyPXUD/3LEldGQ6iF5LR1w00mx9o7xtDWHXBY2iItjdCFGoilOLNQbH943ut7pHA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.16.0", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.22.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.3.tgz", - "integrity": "sha512-Bdcs4+3qlpVlx2NRn6fgX2Ue2/gGRaPeawebgclM0ERSCqDpA+owF1fdPwjJUTAJWMTuAaxjDf+hzb0/4eKvvw==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.5", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.3.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.13", - "proxy-addr": "~2.0.7", - "qs": "~6.16.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", - "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", - "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", - "license": "MIT" - }, - "node_modules/proxy-addr": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", - "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/qs": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", - "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", - "license": "BSD-3-Clause", - "dependencies": { - "es-define-property": "^1.0.1", - "side-channel": "^1.1.1" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", - "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4", - "side-channel-list": "^1.0.1", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - } - } -} diff --git a/backend/package.json b/backend/package.json deleted file mode 100644 index 7e2b997..0000000 --- a/backend/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "katproxy-backend", - "version": "1.0.0", - "description": "High-performance minimal proxy aggregator and latency checker backend", - "main": "server.js", - "scripts": { - "start": "node server.js", - "dev": "node --watch server.js" - }, - "dependencies": { - "cors": "^2.8.5", - "express": "^4.19.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "author": "oiupoyt", - "license": "MIT" -} diff --git a/backend/server.js b/backend/server.js deleted file mode 100644 index e87e246..0000000 --- a/backend/server.js +++ /dev/null @@ -1,277 +0,0 @@ -const express = require('express'); -const cors = require('cors'); -const path = require('path'); -const { fetchAllProxies } = require('./fetcher'); -const { batchCheckProxies } = require('./checker'); - -const app = express(); -const PORT = process.env.PORT || 5050; -const REFRESH_INTERVAL_MS = 15 * 60 * 1000; // 15 minutes - -// State -let proxyStore = []; -let lastUpdated = null; -let nextRefreshTime = null; -let isRefreshing = false; -let isChecking = false; - -// Middleware -app.use(cors({ - origin: '*', - methods: ['GET', 'POST', 'OPTIONS'], - allowedHeaders: ['Content-Type', 'Authorization'] -})); -app.use(express.json()); - -// Optional: serve static frontend if public directory exists -const publicDir = path.join(__dirname, '../public'); -app.use(express.static(publicDir)); - -/** - * Trigger full refresh cycle - */ -async function refreshCycle() { - if (isRefreshing) return; - isRefreshing = true; - console.log(`[Server] Starting auto-refresh at ${new Date().toISOString()}`); - - try { - const rawList = await fetchAllProxies(); - if (rawList && rawList.length > 0) { - proxyStore = rawList; - lastUpdated = Date.now(); - nextRefreshTime = Date.now() + REFRESH_INTERVAL_MS; - console.log(`[Server] Aggregated ${proxyStore.length} proxies successfully.`); - - // Launch background latency checker on the first batch of proxies - // Check high-priority proxies (e.g. up to 300) without hogging CPU/sockets - probeTopProxiesInBackground(); - } - } catch (err) { - console.error('[Server] Refresh cycle error:', err); - } finally { - isRefreshing = false; - } -} - -/** - * Background latency verification pool - */ -async function probeTopProxiesInBackground() { - if (isChecking || proxyStore.length === 0) return; - isChecking = true; - console.log('[Server] Starting background TCP latency verification...'); - - try { - // Select top 300 proxies to probe (or all if fewer) - const targetProxies = proxyStore.slice(0, 350); - await batchCheckProxies(targetProxies, 40, 2000, (completed, total) => { - if (completed % 100 === 0 || completed === total) { - console.log(`[Checker] Latency probe progress: ${completed}/${total}`); - } - }); - console.log('[Checker] Latency probing completed.'); - } catch (err) { - console.error('[Checker] Latency probing error:', err); - } finally { - isChecking = false; - } -} - -// Routes - -// 1. Health -app.get('/api/health', (req, res) => { - res.json({ - status: 'ok', - service: 'katproxy-backend', - uptime: Math.round(process.uptime()), - timestamp: new Date().toISOString() - }); -}); - -// 2. Stats -app.get('/api/stats', (req, res) => { - const total = proxyStore.length; - const aliveList = proxyStore.filter(p => p.alive === true); - - const protocols = { http: 0, socks4: 0, socks5: 0 }; - const countryCounts = {}; - - for (const p of proxyStore) { - for (const proto of p.protocols) { - if (protocols[proto] !== undefined) protocols[proto]++; - } - const c = p.country || 'UN'; - countryCounts[c] = (countryCounts[c] || 0) + 1; - } - - // Top 10 countries - const topCountries = Object.entries(countryCounts) - .sort((a, b) => b[1] - a[1]) - .slice(0, 10) - .map(([country, count]) => ({ country, count })); - - // Average latency of alive proxies - const latencies = aliveList.filter(p => p.latency != null).map(p => p.latency); - const avgLatency = latencies.length > 0 - ? Math.round(latencies.reduce((a, b) => a + b, 0) / latencies.length) - : 0; - - res.json({ - total, - aliveCount: aliveList.length, - protocols, - topCountries, - avgLatency, - lastUpdated, - nextRefreshTime, - remainingSeconds: nextRefreshTime ? Math.max(0, Math.round((nextRefreshTime - Date.now()) / 1000)) : 0, - isRefreshing, - isChecking - }); -}); - -// 3. Proxies list with filter, search, sort, pagination -app.get('/api/proxies', (req, res) => { - let { - protocol, - country, - alive, - maxLatency, - search, - sort = 'latency_asc', - limit = 100, - page = 1 - } = req.query; - - limit = Math.min(Math.max(parseInt(limit, 10) || 100, 1), 1000); - page = Math.max(parseInt(page, 10) || 1, 1); - - let results = proxyStore; - - // Filter: protocol - if (protocol && protocol !== 'all') { - const protoLower = protocol.toLowerCase(); - results = results.filter(p => p.protocols.includes(protoLower)); - } - - // Filter: country - if (country && country !== 'all') { - const countryUpper = country.toUpperCase(); - results = results.filter(p => p.country === countryUpper); - } - - // Filter: alive - if (alive === 'true') { - results = results.filter(p => p.alive === true); - } else if (alive === 'false') { - results = results.filter(p => p.alive === false); - } - - // Filter: max latency - if (maxLatency) { - const maxMs = parseInt(maxLatency, 10); - if (!isNaN(maxMs)) { - results = results.filter(p => p.latency != null && p.latency <= maxMs); - } - } - - // Filter: search query (IP, port, country name, org, city) - if (search && typeof search === 'string' && search.trim()) { - const q = search.trim().toLowerCase(); - results = results.filter(p => - p.host.includes(q) || - String(p.port).includes(q) || - p.country.toLowerCase().includes(q) || - (p.countryName && p.countryName.toLowerCase().includes(q)) || - (p.org && p.org.toLowerCase().includes(q)) || - (p.city && p.city.toLowerCase().includes(q)) - ); - } - - // Sort - results = results.slice().sort((a, b) => { - if (sort === 'latency_asc') { - const latA = a.latency != null ? a.latency : 999999; - const latB = b.latency != null ? b.latency : 999999; - return latA - latB; - } - if (sort === 'latency_desc') { - const latA = a.latency != null ? a.latency : -1; - const latB = b.latency != null ? b.latency : -1; - return latB - latA; - } - if (sort === 'country') { - return (a.country || '').localeCompare(b.country || ''); - } - if (sort === 'port') { - return a.port - b.port; - } - return 0; - }); - - const totalMatches = results.length; - const startIndex = (page - 1) * limit; - const paginated = results.slice(startIndex, startIndex + limit); - - res.json({ - total: totalMatches, - page, - limit, - totalPages: Math.ceil(totalMatches / limit), - proxies: paginated - }); -}); - -// 4. Manual Refresh Trigger -app.post('/api/refresh', async (req, res) => { - if (isRefreshing) { - return res.status(429).json({ error: 'Refresh already in progress' }); - } - refreshCycle().catch(console.error); - res.json({ success: true, message: 'Proxy aggregation started in background' }); -}); - -// 5. Raw Text Export (for curl / bash scripts) -app.get('/api/raw', (req, res) => { - const { protocol, alive, format } = req.query; - let results = proxyStore; - - if (protocol && protocol !== 'all') { - results = results.filter(p => p.protocols.includes(protocol.toLowerCase())); - } - if (alive === 'true') { - results = results.filter(p => p.alive === true); - } - - res.setHeader('Content-Type', 'text/plain; charset=utf-8'); - if (format === 'url') { - const lines = results.map(p => { - const proto = p.protocols[0] || 'http'; - return `${proto}://${p.host}:${p.port}`; - }); - return res.send(lines.join('\n')); - } - - // Default: IP:PORT - const lines = results.map(p => `${p.host}:${p.port}`); - res.send(lines.join('\n')); -}); - -// Fallback for spa / static index if accessed directly -app.get('*', (req, res, next) => { - if (req.accepts('html')) { - return res.sendFile(path.join(publicDir, 'index.html'), (err) => { - if (err) res.status(404).send('KatProxy API Server is running.'); - }); - } - next(); -}); - -// Start server and launch initial fetch -app.listen(PORT, '0.0.0.0', () => { - console.log(`[Server] katproxy backend listening on http://0.0.0.0:${PORT}`); - refreshCycle(); - setInterval(refreshCycle, REFRESH_INTERVAL_MS); -}); diff --git a/backend/src/checker.rs b/backend/src/checker.rs new file mode 100644 index 0000000..f65219d --- /dev/null +++ b/backend/src/checker.rs @@ -0,0 +1,61 @@ +use std::sync::Arc; +use std::time::{Duration, Instant}; +use tokio::net::TcpStream; +use tokio::sync::Semaphore; +use tokio::time::timeout; +use tracing::info; +use crate::models::Proxy; + +/// Probe a single proxy's host:port over TCP +pub async fn probe_proxy(host: &str, port: u16, timeout_duration: Duration) -> (bool, Option) { + let target = format!("{}:{}", host, port); + let start = Instant::now(); + + match timeout(timeout_duration, TcpStream::connect(&target)).await { + Ok(Ok(_stream)) => { + let elapsed = start.elapsed().as_millis() as u32; + (true, Some(elapsed)) + } + _ => (false, None), + } +} + +/// Concurrently probe a slice of proxies up to limit using a semaphore +pub async fn probe_proxies_batch(proxies: &mut [Proxy], concurrency: usize, timeout_duration: Duration) { + let semaphore = Arc::new(Semaphore::new(concurrency)); + let mut handles = Vec::new(); + + let now_ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_millis() as u64; + + for (idx, p) in proxies.iter().enumerate() { + let sem = semaphore.clone(); + let host = p.host.clone(); + let port = p.port; + + handles.push(tokio::spawn(async move { + let _permit = sem.acquire().await.unwrap(); + let (alive, latency) = probe_proxy(&host, port, timeout_duration).await; + (idx, alive, latency) + })); + } + + let mut completed = 0; + let total = handles.len(); + + for handle in handles { + if let Ok((idx, alive, latency)) = handle.await { + proxies[idx].alive = Some(alive); + if alive { + proxies[idx].latency = latency; + } + proxies[idx].last_checked = Some(now_ts); + completed += 1; + if completed % 100 == 0 || completed == total { + info!("[Checker] Latency probe progress: {}/{}", completed, total); + } + } + } +} diff --git a/backend/src/fetcher.rs b/backend/src/fetcher.rs new file mode 100644 index 0000000..a1e258f --- /dev/null +++ b/backend/src/fetcher.rs @@ -0,0 +1,192 @@ +use std::collections::HashMap; +use std::time::Duration; +use reqwest::Client; +use tracing::{info, warn}; +use crate::models::{MonosansProxy, Proxy}; + +pub async fn fetch_all_proxies(client: &Client) -> Vec { + info!("[Fetcher] Starting proxy list aggregation..."); + let mut proxy_map: HashMap<(String, u16), Proxy> = HashMap::new(); + + // Helper closure to insert or merge + let mut add_proxy = |host: String, port: u16, protocol: String, country: String, country_name: String, city: String, org: String, latency: Option, alive: Option, source: String| { + let clean_host = host.trim().to_string(); + if clean_host.is_empty() || port == 0 { + return; + } + + let key = (clean_host.clone(), port); + let proto_clean = protocol.to_lowercase(); + + if let Some(existing) = proxy_map.get_mut(&key) { + if !existing.protocols.contains(&proto_clean) { + existing.protocols.push(proto_clean); + } + if existing.country == "UN" && country != "UN" { + existing.country = country; + if !country_name.is_empty() { + existing.country_name = country_name; + } + } + if existing.org.is_empty() && !org.is_empty() { + existing.org = org; + } + if !existing.sources.contains(&source) { + existing.sources.push(source); + } + } else { + let id = format!("{}:{}", clean_host, port); + proxy_map.insert(key, Proxy { + id, + host: clean_host, + port, + protocols: vec![proto_clean], + country, + country_name, + city, + org, + latency, + alive, + last_checked: None, + sources: vec![source], + }); + } + }; + + // 1. Fetch monosans structured JSON + let monosans_url = "https://raw.githubusercontent.com/monosans/proxy-list/main/proxies.json"; + match client.get(monosans_url).timeout(Duration::from_secs(15)).send().await { + Ok(resp) => { + if resp.status().is_success() { + if let Ok(items) = resp.json::>().await { + let count = items.len(); + for item in items { + let country = item.geolocation.as_ref() + .and_then(|g| g.country.as_ref()) + .and_then(|c| c.iso_code.clone()) + .unwrap_or_else(|| "UN".to_string()) + .to_uppercase(); + + let country_name = item.geolocation.as_ref() + .and_then(|g| g.country.as_ref()) + .and_then(|c| c.names.as_ref()) + .and_then(|n| n.get("en").cloned()) + .unwrap_or_else(|| "Unknown".to_string()); + + let city = item.geolocation.as_ref() + .and_then(|g| g.city.as_ref()) + .and_then(|c| c.names.as_ref()) + .and_then(|n| n.get("en").cloned()) + .unwrap_or_default(); + + let org = item.asn.as_ref() + .and_then(|a| a.autonomous_system_organization.clone()) + .unwrap_or_default(); + + let latency = item.timeout.map(|t| (t * 1000.0).round() as u32); + + add_proxy( + item.host, + item.port, + item.protocol, + country, + country_name, + city, + org, + latency, + Some(true), + "monosans".to_string(), + ); + } + info!("[Fetcher] monosans parsed: {} proxies", count); + } + } + } + Err(e) => warn!("[Fetcher] monosans fetch error: {}", e), + } + + // 2. TheSpeedX lists (HTTP, SOCKS4, SOCKS5) + let speedx_tasks = vec![ + ("https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/http.txt", "http"), + ("https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/socks4.txt", "socks4"), + ("https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/socks5.txt", "socks5"), + ]; + + for (url, proto) in speedx_tasks { + match client.get(url).timeout(Duration::from_secs(12)).send().await { + Ok(resp) => { + if resp.status().is_success() { + if let Ok(text) = resp.text().await { + let mut count = 0; + for line in text.lines() { + let trimmed = line.trim(); + if trimmed.is_empty() || trimmed.starts_with('#') { + continue; + } + if let Some((h, p)) = trimmed.split_once(':') { + if let Ok(port) = p.parse::() { + add_proxy( + h.to_string(), + port, + proto.to_string(), + "UN".to_string(), + "Unknown".to_string(), + String::new(), + String::new(), + None, + None, + "thespeedx".to_string(), + ); + count += 1; + } + } + } + info!("[Fetcher] TheSpeedX {} parsed: {} proxies", proto, count); + } + } + } + Err(e) => warn!("[Fetcher] TheSpeedX {} error: {}", proto, e), + } + } + + // 3. hookzof SOCKS5 list + let hookzof_url = "https://raw.githubusercontent.com/hookzof/socks5_list/master/proxy.txt"; + match client.get(hookzof_url).timeout(Duration::from_secs(12)).send().await { + Ok(resp) => { + if resp.status().is_success() { + if let Ok(text) = resp.text().await { + let mut count = 0; + for line in text.lines() { + let trimmed = line.trim(); + if trimmed.is_empty() || trimmed.starts_with('#') { + continue; + } + if let Some((h, p)) = trimmed.split_once(':') { + if let Ok(port) = p.parse::() { + add_proxy( + h.to_string(), + port, + "socks5".to_string(), + "UN".to_string(), + "Unknown".to_string(), + String::new(), + String::new(), + None, + None, + "hookzof".to_string(), + ); + count += 1; + } + } + } + info!("[Fetcher] hookzof SOCKS5 parsed: {} proxies", count); + } + } + } + Err(e) => warn!("[Fetcher] hookzof error: {}", e), + } + + let results: Vec = proxy_map.into_values().collect(); + info!("[Fetcher] Completed aggregation. Total unique proxies: {}", results.len()); + results +} diff --git a/backend/src/main.rs b/backend/src/main.rs new file mode 100644 index 0000000..f3a1e76 --- /dev/null +++ b/backend/src/main.rs @@ -0,0 +1,438 @@ +mod models; +mod fetcher; +mod checker; + +use std::collections::HashMap; +use std::net::SocketAddr; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::Arc; +use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; + +use axum::extract::{Query, State}; +use axum::http::{header, HeaderMap, StatusCode}; +use axum::response::{IntoResponse, Json, Response}; +use axum::routing::{get, post}; +use axum::Router; +use reqwest::Client; +use tokio::sync::RwLock; +use tower_http::cors::{Any, CorsLayer}; +use tower_http::services::ServeDir; +use tracing::info; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +use crate::models::{ + CountryCount, ProtocolStats, ProxiesQuery, ProxiesResponse, Proxy, RawQuery, StatsResponse, +}; + +const REFRESH_INTERVAL_SECS: u64 = 15 * 60; // 15 mins + +#[derive(Clone)] +pub struct AppState { + pub proxies: Arc>>, + pub last_updated: Arc, + pub next_refresh_time: Arc, + pub is_refreshing: Arc, + pub is_checking: Arc, + pub start_time: Instant, + pub http_client: Client, +} + +#[tokio::main] +async fn main() { + tracing_subscriber::registry() + .with(tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into())) + .with(tracing_subscriber::fmt::layer().compact()) + .init(); + + let client = Client::builder() + .user_agent("katproxy-rs/1.0") + .timeout(Duration::from_secs(15)) + .build() + .expect("Failed to build HTTP client"); + + let state = AppState { + proxies: Arc::new(RwLock::new(Vec::new())), + last_updated: Arc::new(AtomicU64::new(0)), + next_refresh_time: Arc::new(AtomicU64::new(0)), + is_refreshing: Arc::new(AtomicBool::new(false)), + is_checking: Arc::new(AtomicBool::new(false)), + start_time: Instant::now(), + http_client: client, + }; + + // Spawn background refresh worker + let worker_state = state.clone(); + tokio::spawn(async move { + // Initial fetch immediately + run_refresh_cycle(&worker_state).await; + + let mut interval = tokio::time::interval(Duration::from_secs(REFRESH_INTERVAL_SECS)); + // First tick completes immediately, skip it + interval.tick().await; + + loop { + interval.tick().await; + run_refresh_cycle(&worker_state).await; + } + }); + + let cors = CorsLayer::new() + .allow_origin(Any) + .allow_methods(Any) + .allow_headers(Any); + + // Serve public directory if exists + let public_dir = std::path::Path::new("public"); + let fallback_public_dir = std::path::Path::new("../public"); + let static_service = if public_dir.exists() { + ServeDir::new(public_dir) + } else { + ServeDir::new(fallback_public_dir) + }; + + let app = Router::new() + .route("/api/health", get(health_handler)) + .route("/api/stats", get(stats_handler)) + .route("/api/proxies", get(proxies_handler)) + .route("/api/raw", get(raw_handler)) + .route("/api/refresh", post(refresh_handler)) + .fallback_service(static_service) + .layer(cors) + .with_state(state); + + let port: u16 = std::env::var("PORT") + .ok() + .and_then(|p| p.parse().ok()) + .unwrap_or(5050); + + let addr = SocketAddr::from(([0, 0, 0, 0], port)); + info!("[Server] katproxy-backend (Rust) listening on http://{}", addr); + + let listener = tokio::net::TcpListener::bind(addr) + .await + .expect("Failed to bind TCP listener"); + + axum::serve(listener, app) + .await + .expect("Server failed during execution"); +} + +/// Run full refresh cycle: fetch upstreams, deduplicate, probe top batch +async fn run_refresh_cycle(state: &AppState) { + if state.is_refreshing.swap(true, Ordering::SeqCst) { + return; + } + + info!("[Server] Running background proxy refresh cycle..."); + let fetched = fetcher::fetch_all_proxies(&state.http_client).await; + + if !fetched.is_empty() { + let now_ts = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_millis() as u64; + + let next_refresh = now_ts + (REFRESH_INTERVAL_SECS * 1000); + + { + let mut lock = state.proxies.write().await; + *lock = fetched; + } + + state.last_updated.store(now_ts, Ordering::SeqCst); + state.next_refresh_time.store(next_refresh, Ordering::SeqCst); + + // Run background TCP latency probe on top 350 proxies + if !state.is_checking.swap(true, Ordering::SeqCst) { + let probe_state = state.clone(); + tokio::spawn(async move { + info!("[Server] Starting background TCP latency verification..."); + let mut proxies_clone = { + let lock = probe_state.proxies.read().await; + lock.clone() + }; + + let target_len = proxies_clone.len().min(350); + if target_len > 0 { + checker::probe_proxies_batch( + &mut proxies_clone[..target_len], + 40, + Duration::from_millis(2000), + ) + .await; + + // Update store with probed results + let mut lock = probe_state.proxies.write().await; + for (i, p) in proxies_clone.into_iter().take(target_len).enumerate() { + if i < lock.len() { + lock[i].alive = p.alive; + lock[i].latency = p.latency; + lock[i].last_checked = p.last_checked; + } + } + } + probe_state.is_checking.store(false, Ordering::SeqCst); + info!("[Checker] Latency probing completed."); + }); + } + } + + state.is_refreshing.store(false, Ordering::SeqCst); +} + +// Handlers + +async fn health_handler(State(state): State) -> impl IntoResponse { + let now = chrono::Utc::now().to_rfc3339(); + Json(serde_json::json!({ + "status": "ok", + "service": "katproxy-backend-rs", + "uptime": state.start_time.elapsed().as_secs(), + "timestamp": now + })) +} + +async fn stats_handler(State(state): State) -> impl IntoResponse { + let proxies = state.proxies.read().await; + let total = proxies.len(); + + let mut alive_count = 0; + let mut http_count = 0; + let mut socks4_count = 0; + let mut socks5_count = 0; + let mut latencies = Vec::new(); + let mut country_map: HashMap = HashMap::new(); + + for p in proxies.iter() { + if p.alive == Some(true) { + alive_count += 1; + if let Some(lat) = p.latency { + latencies.push(lat); + } + } + + for proto in &p.protocols { + match proto.as_str() { + "http" => http_count += 1, + "socks4" => socks4_count += 1, + "socks5" => socks5_count += 1, + _ => {} + } + } + + let c = if p.country.is_empty() { "UN" } else { &p.country }; + *country_map.entry(c.to_string()).or_insert(0) += 1; + } + + let mut top_countries: Vec = country_map + .into_iter() + .map(|(country, count)| CountryCount { country, count }) + .collect(); + top_countries.sort_by(|a, b| b.count.cmp(&a.count)); + top_countries.truncate(10); + + let avg_latency = if !latencies.is_empty() { + (latencies.iter().map(|&v| v as u64).sum::() / latencies.len() as u64) as u32 + } else { + 0 + }; + + let now_ts = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_millis() as u64; + + let next_refresh = state.next_refresh_time.load(Ordering::SeqCst); + let remaining_seconds = if next_refresh > now_ts { + (next_refresh - now_ts) / 1000 + } else { + 0 + }; + + let last_updated = state.last_updated.load(Ordering::SeqCst); + + Json(StatsResponse { + total, + alive_count, + protocols: ProtocolStats { + http: http_count, + socks4: socks4_count, + socks5: socks5_count, + }, + top_countries, + avg_latency, + last_updated: if last_updated > 0 { Some(last_updated) } else { None }, + next_refresh_time: if next_refresh > 0 { Some(next_refresh) } else { None }, + remaining_seconds, + is_refreshing: state.is_refreshing.load(Ordering::SeqCst), + is_checking: state.is_checking.load(Ordering::SeqCst), + }) +} + +async fn proxies_handler( + State(state): State, + Query(query): Query, +) -> impl IntoResponse { + let limit = query.limit.unwrap_or(60).clamp(1, 1000); + let page = query.page.unwrap_or(1).max(1); + + let proxies = state.proxies.read().await; + + // Filter + let mut filtered: Vec = proxies + .iter() + .filter(|p| { + // Protocol filter + if let Some(ref proto) = query.protocol { + if proto != "all" && !p.protocols.contains(&proto.to_lowercase()) { + return false; + } + } + // Country filter + if let Some(ref c) = query.country { + if c != "all" && p.country != c.to_uppercase() { + return false; + } + } + // Alive filter + if let Some(ref alv) = query.alive { + if alv == "true" && p.alive != Some(true) { + return false; + } else if alv == "false" && p.alive != Some(false) { + return false; + } + } + // Max latency + if let Some(max_lat) = query.max_latency { + if let Some(lat) = p.latency { + if lat > max_lat { + return false; + } + } else { + return false; + } + } + // Search text + if let Some(ref search) = query.search { + let q = search.trim().to_lowercase(); + if !q.is_empty() { + let port_str = p.port.to_string(); + let match_host = p.host.contains(&q); + let match_port = port_str.contains(&q); + let match_country = p.country.to_lowercase().contains(&q) || p.country_name.to_lowercase().contains(&q); + let match_org = p.org.to_lowercase().contains(&q); + let match_city = p.city.to_lowercase().contains(&q); + + if !(match_host || match_port || match_country || match_org || match_city) { + return false; + } + } + } + true + }) + .cloned() + .collect(); + + // Sort + let sort = query.sort.as_deref().unwrap_or("latency_asc"); + match sort { + "latency_asc" => { + filtered.sort_by_key(|p| p.latency.unwrap_or(u32::MAX)); + } + "latency_desc" => { + filtered.sort_by(|a, b| { + let lat_a = a.latency.unwrap_or(0); + let lat_b = b.latency.unwrap_or(0); + lat_b.cmp(&lat_a) + }); + } + "country" => { + filtered.sort_by(|a, b| a.country.cmp(&b.country)); + } + "port" => { + filtered.sort_by_key(|p| p.port); + } + _ => {} + } + + let total = filtered.len(); + let total_pages = if total == 0 { 1 } else { (total + limit - 1) / limit }; + let start_idx = (page - 1) * limit; + + let paginated = if start_idx >= total { + Vec::new() + } else { + filtered + .into_iter() + .skip(start_idx) + .take(limit) + .collect() + }; + + Json(ProxiesResponse { + total, + page, + limit, + total_pages, + proxies: paginated, + }) +} + +async fn raw_handler( + State(state): State, + Query(query): Query, +) -> impl IntoResponse { + let proxies = state.proxies.read().await; + + let lines: Vec = proxies + .iter() + .filter(|p| { + if let Some(ref proto) = query.protocol { + if proto != "all" && !p.protocols.contains(&proto.to_lowercase()) { + return false; + } + } + if let Some(ref alv) = query.alive { + if alv == "true" && p.alive != Some(true) { + return false; + } + } + true + }) + .map(|p| { + if query.format.as_deref() == Some("url") { + let proto = p.protocols.first().map(|s| s.as_str()).unwrap_or("http"); + format!("{}://{}:{}", proto, p.host, p.port) + } else { + format!("{}:{}", p.host, p.port) + } + }) + .collect(); + + let output = lines.join("\n"); + let mut headers = HeaderMap::new(); + headers.insert(header::CONTENT_TYPE, "text/plain; charset=utf-8".parse().unwrap()); + + (headers, output) +} + +async fn refresh_handler(State(state): State) -> Response { + if state.is_refreshing.load(Ordering::SeqCst) { + return ( + StatusCode::TOO_MANY_REQUESTS, + Json(serde_json::json!({ "error": "Refresh already in progress" })), + ) + .into_response(); + } + + let worker_state = state.clone(); + tokio::spawn(async move { + run_refresh_cycle(&worker_state).await; + }); + + Json(serde_json::json!({ + "success": true, + "message": "Proxy aggregation started in background" + })) + .into_response() +} diff --git a/backend/src/models.rs b/backend/src/models.rs new file mode 100644 index 0000000..8aebc18 --- /dev/null +++ b/backend/src/models.rs @@ -0,0 +1,117 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct Proxy { + pub id: String, + pub host: String, + pub port: u16, + pub protocols: Vec, + pub country: String, + #[serde(rename = "countryName")] + pub country_name: String, + pub city: String, + pub org: String, + pub latency: Option, + pub alive: Option, + #[serde(rename = "lastChecked")] + pub last_checked: Option, + pub sources: Vec, +} + +#[derive(Serialize)] +pub struct StatsResponse { + pub total: usize, + #[serde(rename = "aliveCount")] + pub alive_count: usize, + pub protocols: ProtocolStats, + #[serde(rename = "topCountries")] + pub top_countries: Vec, + #[serde(rename = "avgLatency")] + pub avg_latency: u32, + #[serde(rename = "lastUpdated")] + pub last_updated: Option, + #[serde(rename = "nextRefreshTime")] + pub next_refresh_time: Option, + #[serde(rename = "remainingSeconds")] + pub remaining_seconds: u64, + #[serde(rename = "isRefreshing")] + pub is_refreshing: bool, + #[serde(rename = "isChecking")] + pub is_checking: bool, +} + +#[derive(Serialize)] +pub struct ProtocolStats { + pub http: usize, + pub socks4: usize, + pub socks5: usize, +} + +#[derive(Serialize)] +pub struct CountryCount { + pub country: String, + pub count: usize, +} + +#[derive(Serialize)] +pub struct ProxiesResponse { + pub total: usize, + pub page: usize, + pub limit: usize, + #[serde(rename = "totalPages")] + pub total_pages: usize, + pub proxies: Vec, +} + +#[derive(Deserialize, Default)] +pub struct ProxiesQuery { + pub protocol: Option, + pub country: Option, + pub alive: Option, + #[serde(rename = "maxLatency")] + pub max_latency: Option, + pub search: Option, + pub sort: Option, + pub limit: Option, + pub page: Option, +} + +#[derive(Deserialize, Default)] +pub struct RawQuery { + pub protocol: Option, + pub alive: Option, + pub format: Option, +} + +// Struct for parsing monosans proxies.json +#[derive(Deserialize, Debug)] +pub struct MonosansProxy { + pub host: String, + pub port: u16, + pub protocol: String, + pub timeout: Option, + pub asn: Option, + pub geolocation: Option, +} + +#[derive(Deserialize, Debug)] +pub struct MonosansAsn { + pub autonomous_system_organization: Option, +} + +#[derive(Deserialize, Debug)] +pub struct MonosansGeo { + pub country: Option, + pub city: Option, +} + +#[derive(Deserialize, Debug)] +pub struct MonosansCountry { + pub iso_code: Option, + pub names: Option>, +} + +#[derive(Deserialize, Debug)] +pub struct MonosansCity { + pub names: Option>, +} diff --git a/backend/start.sh b/backend/start.sh index 5fc146e..2759567 100755 --- a/backend/start.sh +++ b/backend/start.sh @@ -1,22 +1,18 @@ #!/data/data/com.termux/files/usr/bin/bash cd "$(dirname "$0")" -# Acquire Android partial wake-lock so CPU does not sleep when screen turns off /data/data/com.termux/files/usr/bin/termux-wake-lock 2>/dev/null || true -# Stop existing backend instance if running if [ -f katproxy.pid ]; then kill $(cat katproxy.pid) 2>/dev/null || true rm -f katproxy.pid fi sleep 0.5 -# Rotate log file to save tablet storage if [ -f katproxy.log ]; then tail -n 200 katproxy.log > katproxy.log.tmp 2>/dev/null && mv katproxy.log.tmp katproxy.log 2>/dev/null || true fi -# Start backend with 256MB capped V8 heap for ARM Android stability -PORT=5050 nohup node --max-old-space-size=256 server.js >> katproxy.log 2>&1 & +PORT=5050 nohup ./target/release/katproxy-backend >> katproxy.log 2>&1 & echo $! > katproxy.pid -echo "katproxy-backend running with PID $(cat katproxy.pid) on port 5050" +echo "katproxy-backend (rust) running with PID $(cat katproxy.pid) on port 5050" diff --git a/backend/status.sh b/backend/status.sh index cb8af33..e3988da 100755 --- a/backend/status.sh +++ b/backend/status.sh @@ -1,7 +1,7 @@ #!/data/data/com.termux/files/usr/bin/bash cd "$(dirname "$0")" -echo "=== KatProxy Backend Status ===" +echo "=== KatProxy Backend (Rust) Status ===" if [ -f katproxy.pid ]; then PID=$(cat katproxy.pid) if ps -p "$PID" > /dev/null 2>&1; then diff --git a/package.json b/package.json index 05bce3c..27b367c 100644 --- a/package.json +++ b/package.json @@ -3,15 +3,11 @@ "version": "1.0.0", "description": "Minimal, sleek multi-source proxy list stream & active latency tester", "scripts": { - "start": "node backend/server.js", - "backend": "cd backend && npm start", - "dev": "cd backend && npm run dev" + "start": "cd backend && cargo run --release", + "build": "cd backend && cargo build --release", + "dev": "cd backend && cargo run" }, - "keywords": ["proxy", "socks5", "socks4", "http", "stream", "minimal"], + "keywords": ["proxy", "socks5", "socks4", "http", "stream", "minimal", "rust", "axum"], "author": "oiupoyt", - "license": "MIT", - "dependencies": { - "cors": "^2.8.5", - "express": "^4.19.2" - } + "license": "MIT" } diff --git a/public/app.js b/public/app.js index 62bddda..bc8123a 100644 --- a/public/app.js +++ b/public/app.js @@ -3,14 +3,12 @@ */ // Configuration & State -const DEFAULT_TUNNEL_URL = 'https://katproxy-api.oiupoyt.space'; +const DEFAULT_API_URL = 'https://katproxy-api.oiupoyt.space'; const LOCAL_DEV_URL = 'http://localhost:5050'; -let apiBase = localStorage.getItem('katproxy_api_url') || ( - window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1' - ? LOCAL_DEV_URL - : DEFAULT_TUNNEL_URL -); +const apiBase = (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') + ? LOCAL_DEV_URL + : DEFAULT_API_URL; const state = { protocol: 'all', @@ -35,7 +33,6 @@ const el = { timerText: document.getElementById('timer-text'), progressCircle: document.getElementById('progress-circle'), btnManualRefresh: document.getElementById('btn-manual-refresh'), - btnSettings: document.getElementById('btn-settings'), protocolFilters: document.getElementById('protocol-filters'), toggleAlive: document.getElementById('toggle-alive'), searchInput: document.getElementById('search-input'), @@ -53,11 +50,6 @@ const el = { btnNext: document.getElementById('btn-next'), pageIndicator: document.getElementById('page-indicator'), linkRawApi: document.getElementById('link-raw-api'), - settingsModal: document.getElementById('settings-modal'), - modalClose: document.getElementById('modal-close'), - modalCancel: document.getElementById('modal-cancel'), - modalSave: document.getElementById('modal-save'), - apiUrlInput: document.getElementById('api-url-input'), toastContainer: document.getElementById('toast-container') }; @@ -469,35 +461,6 @@ function initEvents() { state.page++; loadProxies(); window.scrollTo({ top: 0, behavior: 'smooth' }); - } - }); - - // Settings Modal - el.btnSettings.addEventListener('click', () => { - el.apiUrlInput.value = apiBase; - el.settingsModal.classList.remove('hidden'); - }); - - el.modalClose.addEventListener('click', () => el.settingsModal.classList.add('hidden')); - el.modalCancel.addEventListener('click', () => el.settingsModal.classList.add('hidden')); - - // Quick preset buttons in modal - el.settingsModal.querySelectorAll('.quick-endpoints button').forEach(btn => { - btn.addEventListener('click', () => { - el.apiUrlInput.value = btn.dataset.url; - }); - }); - - el.modalSave.addEventListener('click', () => { - const newUrl = el.apiUrlInput.value.trim().replace(/\/+$/, ''); - if (newUrl) { - apiBase = newUrl; - localStorage.setItem('katproxy_api_url', newUrl); - showToast(`API endpoint updated to ${newUrl}`); - el.settingsModal.classList.add('hidden'); - loadStats(); - loadProxies(); - } }); } diff --git a/public/index.html b/public/index.html index 20d0742..8cfe4f2 100644 --- a/public/index.html +++ b/public/index.html @@ -56,12 +56,6 @@ - @@ -171,30 +165,7 @@ - - +
diff --git a/public/style.css b/public/style.css index 3e71182..7070fa0 100644 --- a/public/style.css +++ b/public/style.css @@ -783,111 +783,6 @@ body { color: rgba(255, 255, 255, 0.15); } -/* Modal */ -.modal-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.7); - backdrop-filter: blur(8px); - display: flex; - align-items: center; - justify-content: center; - z-index: 100; - padding: 20px; -} - -.modal-card { - background: #121216; - border: 1px solid var(--border-hover); - border-radius: var(--radius-lg); - max-width: 440px; - width: 100%; - padding: 24px; - box-shadow: 0 20px 40px rgba(0,0,0,0.6); - animation: modal-in 0.18s ease-out; -} - -@keyframes modal-in { - from { opacity: 0; transform: scale(0.96); } - to { opacity: 1; transform: scale(1); } -} - -.modal-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 16px; -} - -.modal-header h3 { - font-size: 15px; - font-weight: 600; - color: #fff; -} - -.modal-close { - background: transparent; - border: none; - color: var(--text-dim); - font-size: 20px; - cursor: pointer; -} - -.modal-close:hover { - color: #fff; -} - -.modal-body { - margin-bottom: 20px; -} - -.modal-body label { - display: block; - font-size: 12px; - font-weight: 500; - color: var(--text-muted); - margin-bottom: 6px; -} - -.modal-input { - width: 100%; - background: #09090b; - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - padding: 8px 12px; - color: #fff; - font-family: var(--font-mono); - font-size: 13px; - margin-bottom: 8px; -} - -.modal-input:focus { - outline: none; - border-color: var(--accent-emerald); -} - -.modal-hint { - font-size: 11px; - color: var(--text-dim); - line-height: 1.5; - margin-bottom: 12px; -} - -.quick-endpoints { - display: flex; - gap: 6px; - flex-wrap: wrap; -} - -.modal-footer { - display: flex; - justify-content: flex-end; - gap: 8px; -} - /* Toast System */ .toast-container { position: fixed;