From bf7004c89c8780d991dd7210ffb1051481a8cc61 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 13 Jan 2024 12:35:59 +0800 Subject: [PATCH 1/9] :tada: Start developing! --- .gitignore | 6 +- Cargo.lock | 2005 +++++++++++++++++ Cargo.toml | 15 + Settings.toml | 4 + go.mod | 58 - go.sum | 577 ----- pkg/cmd/rds/conn/commands.go | 89 - pkg/cmd/rds/conn/connect.go | 42 - pkg/cmd/rds/conn/directory.go | 17 - pkg/cmd/rds/deploy/commands.go | 101 - pkg/cmd/rds/main.go | 48 - pkg/cmd/server/main.go | 86 - pkg/hypertext/proxies.go | 111 - pkg/hypertext/server.go | 79 - pkg/meta.go | 17 - pkg/sideload/connectivity.go | 13 - pkg/sideload/processes.go | 29 - pkg/sideload/publish.go | 85 - pkg/sideload/server.go | 69 - pkg/sideload/sites.go | 63 - pkg/sideload/statistics.go | 28 - pkg/sideload/view/.dockerignore | 3 - pkg/sideload/view/.eslintrc.cjs | 15 - pkg/sideload/view/.gitignore | 30 - pkg/sideload/view/.prettierrc.json | 8 - pkg/sideload/view/.vscode/extensions.json | 8 - pkg/sideload/view/README.md | 46 - pkg/sideload/view/embed.go | 6 - pkg/sideload/view/env.d.ts | 1 - pkg/sideload/view/index.html | 13 - pkg/sideload/view/package.json | 44 - pkg/sideload/view/src/assets/main.css | 6 - .../components/data/sites-table-action.vue | 135 -- .../src/components/data/sites-table-add.vue | 110 - .../components/data/sites-table-expand.vue | 36 - .../view/src/components/data/sites-table.vue | 76 - pkg/sideload/view/src/layouts/main.vue | 60 - pkg/sideload/view/src/main.ts | 16 - pkg/sideload/view/src/root.vue | 9 - pkg/sideload/view/src/router/index.ts | 21 - pkg/sideload/view/src/views/dashboard.vue | 35 - pkg/sideload/view/tsconfig.app.json | 13 - pkg/sideload/view/tsconfig.json | 11 - pkg/sideload/view/tsconfig.node.json | 17 - pkg/sideload/view/unocss.config.ts | 5 - pkg/sideload/view/vite.config.ts | 24 - pkg/sign/configurator.go | 45 - pkg/sign/pm.go | 144 -- pkg/sign/responder.go | 127 -- pkg/sign/router.go | 58 - pkg/sign/transformers/compress.go | 41 - pkg/sign/transformers/module.go | 61 - pkg/sign/transformers/replace_path.go | 25 - pkg/sign/upstream.go | 58 - settings.yml | 28 - src/config/loader.rs | 9 + src/config/mod.rs | 1 + src/main.rs | 39 + src/proxies/config.rs | 22 + src/proxies/mod.rs | 1 + src/sideload/mod.rs | 3 + src/sideload/overview.rs | 14 + test/README.md | 85 - test/data/.gitignore | 3 - test/data/index.html | 12 - test/nginx/nginx.conf | 117 - test/roadsign-spa/config/example.yaml | 8 - test/roadsign-spa/settings.yml | 26 - test/roadsign-ssr/config/example.yaml | 12 - test/roadsign-ssr/settings.yml | 26 - .../roadsign-with-prefork/config/example.yaml | 8 - test/roadsign-with-prefork/settings.yml | 26 - test/roadsign/config/example.yaml | 8 - test/roadsign/settings.yml | 26 - 74 files changed, 2118 insertions(+), 3205 deletions(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Settings.toml delete mode 100644 go.mod delete mode 100644 go.sum delete mode 100644 pkg/cmd/rds/conn/commands.go delete mode 100644 pkg/cmd/rds/conn/connect.go delete mode 100644 pkg/cmd/rds/conn/directory.go delete mode 100644 pkg/cmd/rds/deploy/commands.go delete mode 100644 pkg/cmd/rds/main.go delete mode 100644 pkg/cmd/server/main.go delete mode 100644 pkg/hypertext/proxies.go delete mode 100644 pkg/hypertext/server.go delete mode 100644 pkg/meta.go delete mode 100644 pkg/sideload/connectivity.go delete mode 100644 pkg/sideload/processes.go delete mode 100644 pkg/sideload/publish.go delete mode 100644 pkg/sideload/server.go delete mode 100644 pkg/sideload/sites.go delete mode 100644 pkg/sideload/statistics.go delete mode 100644 pkg/sideload/view/.dockerignore delete mode 100644 pkg/sideload/view/.eslintrc.cjs delete mode 100644 pkg/sideload/view/.gitignore delete mode 100644 pkg/sideload/view/.prettierrc.json delete mode 100644 pkg/sideload/view/.vscode/extensions.json delete mode 100644 pkg/sideload/view/README.md delete mode 100644 pkg/sideload/view/embed.go delete mode 100644 pkg/sideload/view/env.d.ts delete mode 100644 pkg/sideload/view/index.html delete mode 100644 pkg/sideload/view/package.json delete mode 100644 pkg/sideload/view/src/assets/main.css delete mode 100644 pkg/sideload/view/src/components/data/sites-table-action.vue delete mode 100644 pkg/sideload/view/src/components/data/sites-table-add.vue delete mode 100644 pkg/sideload/view/src/components/data/sites-table-expand.vue delete mode 100644 pkg/sideload/view/src/components/data/sites-table.vue delete mode 100644 pkg/sideload/view/src/layouts/main.vue delete mode 100644 pkg/sideload/view/src/main.ts delete mode 100644 pkg/sideload/view/src/root.vue delete mode 100644 pkg/sideload/view/src/router/index.ts delete mode 100644 pkg/sideload/view/src/views/dashboard.vue delete mode 100644 pkg/sideload/view/tsconfig.app.json delete mode 100644 pkg/sideload/view/tsconfig.json delete mode 100644 pkg/sideload/view/tsconfig.node.json delete mode 100644 pkg/sideload/view/unocss.config.ts delete mode 100644 pkg/sideload/view/vite.config.ts delete mode 100644 pkg/sign/configurator.go delete mode 100644 pkg/sign/pm.go delete mode 100644 pkg/sign/responder.go delete mode 100644 pkg/sign/router.go delete mode 100644 pkg/sign/transformers/compress.go delete mode 100644 pkg/sign/transformers/module.go delete mode 100644 pkg/sign/transformers/replace_path.go delete mode 100644 pkg/sign/upstream.go delete mode 100644 settings.yml create mode 100644 src/config/loader.rs create mode 100644 src/config/mod.rs create mode 100644 src/main.rs create mode 100644 src/proxies/config.rs create mode 100644 src/proxies/mod.rs create mode 100644 src/sideload/mod.rs create mode 100644 src/sideload/overview.rs delete mode 100644 test/README.md delete mode 100644 test/data/.gitignore delete mode 100644 test/data/index.html delete mode 100644 test/nginx/nginx.conf delete mode 100644 test/roadsign-spa/config/example.yaml delete mode 100644 test/roadsign-spa/settings.yml delete mode 100644 test/roadsign-ssr/config/example.yaml delete mode 100644 test/roadsign-ssr/settings.yml delete mode 100644 test/roadsign-with-prefork/config/example.yaml delete mode 100644 test/roadsign-with-prefork/settings.yml delete mode 100644 test/roadsign/config/example.yaml delete mode 100644 test/roadsign/settings.yml diff --git a/.gitignore b/.gitignore index e5f2b59..240112f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ /config -/letsencrypt \ No newline at end of file +/letsencrypt + +# Added by cargo + +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..da66b14 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2005 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "ahash" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets 0.48.5", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "config" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23738e11972c7643e4ec947840fc463b6a571afcd3e735bdfce7d03c7a784aca" +dependencies = [ + "async-trait", + "json5", + "lazy_static", + "nom", + "pathdiff", + "ron", + "rust-ini", + "serde", + "serde_json", + "toml", + "yaml-rust", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" +dependencies = [ + "aes-gcm", + "base64 0.21.7", + "hkdf", + "hmac", + "percent-encoding", + "rand", + "sha2", + "subtle", + "time", + "version_check", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.48", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dlv-list" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "h2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991910e35c615d8cab86b5ab04be67e6ad24d2bf5f4f11fdbbed26da999bbeab" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "headers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +dependencies = [ + "base64 0.21.7", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "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 = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "multer" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15d522be0a9c3e46fd2632e272d178f56387bdb5c9fbb3a36c649062e9b5219" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http", + "httparse", + "log", + "memchr", + "mime", + "spin", + "tokio", + "version_check", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "ordered-multimap" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" +dependencies = [ + "dlv-list", + "hashbrown 0.12.3", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "pest_meta" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "poem" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38a712ff53e257d60d3d22936c51cafa606552129d55539c8a400de44eff676d" +dependencies = [ + "async-trait", + "bytes", + "chrono", + "cookie", + "futures-util", + "headers", + "http", + "http-body-util", + "hyper", + "hyper-util", + "mime", + "multer", + "nix", + "parking_lot", + "percent-encoding", + "pin-project-lite", + "poem-derive", + "quick-xml", + "regex", + "rfc7239", + "serde", + "serde_json", + "serde_urlencoded", + "serde_yaml", + "smallvec", + "sync_wrapper", + "tempfile", + "thiserror", + "time", + "tokio", + "tokio-metrics", + "tokio-stream", + "tokio-util", + "tracing", + "wildmatch", +] + +[[package]] +name = "poem-derive" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e2bd3f82499a00ecb2d0e06bb1b9a17540aeaa28bab14336a94255ff5185f8f" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "poem-openapi" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7c12d9e19a0cda311f46515b819eb1e8425a4bee004540aff7b9c587e02b82" +dependencies = [ + "base64 0.21.7", + "bytes", + "derive_more", + "futures-util", + "indexmap", + "mime", + "num-traits", + "poem", + "poem-openapi-derive", + "quick-xml", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "serde_yaml", + "thiserror", + "tokio", +] + +[[package]] +name = "poem-openapi-derive" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d485fb9cc4ca9a8364beedd4ea81294b1f028d459c8fd7bb352e38f87f8ffa" +dependencies = [ + "darling", + "http", + "indexmap", + "mime", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "syn 2.0.48", + "thiserror", +] + +[[package]] +name = "polyval" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" +dependencies = [ + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rfc7239" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "087317b3cf7eb481f13bd9025d729324b7cd068d6f470e2d76d049e191f5ba47" +dependencies = [ + "uncased", +] + +[[package]] +name = "roadsign" +version = "0.1.0" +dependencies = [ + "config", + "poem", + "poem-openapi", + "serde", + "serde_json", + "tokio", + "tracing-subscriber", +] + +[[package]] +name = "ron" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" +dependencies = [ + "base64 0.13.1", + "bitflags 1.3.2", + "serde", +] + +[[package]] +name = "rust-ini" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "serde_json" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[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 = "serde_yaml" +version = "0.9.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[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 = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +dependencies = [ + "futures-core", +] + +[[package]] +name = "tempfile" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +dependencies = [ + "time-core", +] + +[[package]] +name = "tokio" +version = "1.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "tokio-metrics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eace09241d62c98b7eeb1107d4c5c64ca3bd7da92e8c218c153ab3a78f9be112" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", + "tokio-stream", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "uncased" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" + +[[package]] +name = "wildmatch" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "495ec47bf3c1345005f40724f0269362c8556cbc43aed0526ed44cae1d35fceb" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +dependencies = [ + "memchr", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6581414 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "roadsign" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +config = { version = "0.13.4", features = ["toml"] } +poem = { version = "2.0.0", features = ["tokio-metrics"] } +poem-openapi = "4.0.0" +serde = "1.0.195" +serde_json = "1.0.111" +tokio = { version = "1.35.1", features = ["rt-multi-thread", "macros", "time"] } +tracing-subscriber = "0.3.18" diff --git a/Settings.toml b/Settings.toml new file mode 100644 index 0000000..fc76f4a --- /dev/null +++ b/Settings.toml @@ -0,0 +1,4 @@ +[listen] +proxies = "0.0.0.0:80" +proxies_tls = "0.0.0.0:443" +sideload = "0.0.0.0:81" diff --git a/go.mod b/go.mod deleted file mode 100644 index 3f5d04c..0000000 --- a/go.mod +++ /dev/null @@ -1,58 +0,0 @@ -module code.smartsheep.studio/goatworks/roadsign - -go 1.21.4 - -require ( - github.com/gofiber/fiber/v2 v2.51.0 - github.com/google/uuid v1.4.0 - github.com/rs/zerolog v1.31.0 - github.com/samber/lo v1.38.1 - github.com/saracen/fastzip v0.1.11 - github.com/spf13/viper v1.17.0 - github.com/urfave/cli/v2 v2.26.0 - github.com/valyala/fasthttp v1.50.0 -) - -require ( - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/saracen/zipextra v0.0.0-20220303013732-0187cb0159ea // indirect - github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - golang.org/x/sync v0.5.0 // indirect -) - -require ( - github.com/andybalholm/brotli v1.0.5 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/klauspost/compress v1.17.4 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect - github.com/philhofer/fwd v1.1.2 // indirect - github.com/rivo/uniseg v0.2.0 // indirect - github.com/sagikazarmark/locafero v0.3.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.10.0 // indirect - github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/subosito/gotenv v1.6.0 // indirect - github.com/tinylib/msgp v1.1.8 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/tcplisten v1.0.0 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.9.0 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/text v0.13.0 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 393aba1..0000000 --- a/go.sum +++ /dev/null @@ -1,577 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ= -github.com/gofiber/fiber/v2 v2.51.0/go.mod h1:xaQRZQJGqnKOQnbQw+ltvku3/h8QxvNi8o6JiJ7Ll0U= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= -github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= -github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= -github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= -github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= -github.com/saracen/fastzip v0.1.11 h1:NnExbTEJbya7148cov09BCxwfur9tQ5BQ1QyQH6XleA= -github.com/saracen/fastzip v0.1.11/go.mod h1:/lN5BiU451/OZMS+hfhVsSDj/RNrxYmO9EYxCtMrFrY= -github.com/saracen/zipextra v0.0.0-20220303013732-0187cb0159ea h1:8czYLkvzZRE+AElIQeDffQdgR+CC3wKEFILYU/1PeX4= -github.com/saracen/zipextra v0.0.0-20220303013732-0187cb0159ea/go.mod h1:hnzuad9d2wdd3z8fC6UouHQK5qZxqv3F/E6MMzXc7q0= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= -github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= -github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= -github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= -github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= -github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI= -github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= -github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= -github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= -go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/pkg/cmd/rds/conn/commands.go b/pkg/cmd/rds/conn/commands.go deleted file mode 100644 index db438a2..0000000 --- a/pkg/cmd/rds/conn/commands.go +++ /dev/null @@ -1,89 +0,0 @@ -package conn - -import ( - "encoding/json" - "fmt" - - "github.com/rs/zerolog/log" - "github.com/samber/lo" - "github.com/spf13/viper" - "github.com/urfave/cli/v2" -) - -var CliCommands = []*cli.Command{ - { - Name: "list", - Aliases: []string{"ls"}, - Description: "List all connected remote server", - Action: func(ctx *cli.Context) error { - var servers []CliConnection - raw, _ := json.Marshal(viper.Get("servers")) - _ = json.Unmarshal(raw, &servers) - - log.Info().Msgf("There are %d server(s) connected in total.", len(servers)) - for idx, server := range servers { - log.Info().Msgf("%d) %s: %s", idx+1, server.ID, server.Url) - } - - return nil - }, - }, - { - Name: "connect", - Aliases: []string{"add"}, - Description: "Connect and save configuration of remote server", - ArgsUsage: " ", - Action: func(ctx *cli.Context) error { - if ctx.Args().Len() < 3 { - return fmt.Errorf("must have three arguments: ") - } - - c := CliConnection{ - ID: ctx.Args().Get(0), - Url: ctx.Args().Get(1), - Credential: ctx.Args().Get(2), - } - - if err := c.CheckConnectivity(); err != nil { - return fmt.Errorf("couldn't connect server: %s", err.Error()) - } else { - var servers []CliConnection - raw, _ := json.Marshal(viper.Get("servers")) - _ = json.Unmarshal(raw, &servers) - viper.Set("servers", append(servers, c)) - - if err := viper.WriteConfig(); err != nil { - return err - } else { - log.Info().Msg("Successfully connected a new remote server, enter \"rds ls\" to get more info.") - return nil - } - } - }, - }, - { - Name: "disconnect", - Aliases: []string{"remove"}, - Description: "Remove a remote server configuration", - ArgsUsage: "", - Action: func(ctx *cli.Context) error { - if ctx.Args().Len() < 1 { - return fmt.Errorf("must have more one arguments: ") - } - - var servers []CliConnection - raw, _ := json.Marshal(viper.Get("servers")) - _ = json.Unmarshal(raw, &servers) - viper.Set("servers", lo.Filter(servers, func(item CliConnection, idx int) bool { - return item.ID != ctx.Args().Get(0) - })) - - if err := viper.WriteConfig(); err != nil { - return err - } else { - log.Info().Msg("Successfully disconnected a remote server, enter \"rds ls\" to get more info.") - return nil - } - }, - }, -} diff --git a/pkg/cmd/rds/conn/connect.go b/pkg/cmd/rds/conn/connect.go deleted file mode 100644 index abd48a3..0000000 --- a/pkg/cmd/rds/conn/connect.go +++ /dev/null @@ -1,42 +0,0 @@ -package conn - -import ( - "encoding/json" - "fmt" - "strings" - - roadsign "code.smartsheep.studio/goatworks/roadsign/pkg" - "github.com/gofiber/fiber/v2" - "github.com/rs/zerolog/log" -) - -type CliConnection struct { - ID string `json:"id"` - Url string `json:"url"` - Credential string `json:"credential"` -} - -func (v CliConnection) CheckConnectivity() error { - client := fiber.Get(v.Url + "/cgi/connectivity") - client.BasicAuth("RoadSign CLI", v.Credential) - - if status, data, err := client.Bytes(); len(err) > 0 { - return fmt.Errorf("couldn't connect to server: %q", err) - } else if status != 200 { - return fmt.Errorf("server rejected request, may cause by invalid credential") - } else { - var resp fiber.Map - if err := json.Unmarshal(data, &resp); err != nil { - return err - } else if resp["server"] != "RoadSign" { - return fmt.Errorf("remote server isn't roadsign") - } else if resp["version"] != roadsign.AppVersion { - if strings.Contains(roadsign.AppVersion, "#") { - return fmt.Errorf("remote server version mismatch client version, update or downgrade client required") - } else { - log.Warn().Msg("RoadSign CLI didn't complied with vcs information, compatibility was disabled. To enable it, reinstall cli with -buildvcs flag.") - } - } - } - return nil -} diff --git a/pkg/cmd/rds/conn/directory.go b/pkg/cmd/rds/conn/directory.go deleted file mode 100644 index 60821b0..0000000 --- a/pkg/cmd/rds/conn/directory.go +++ /dev/null @@ -1,17 +0,0 @@ -package conn - -import ( - "encoding/json" - - "github.com/samber/lo" - "github.com/spf13/viper" -) - -func GetConnection(id string) (CliConnection, bool) { - var servers []CliConnection - raw, _ := json.Marshal(viper.Get("servers")) - _ = json.Unmarshal(raw, &servers) - return lo.Find(servers, func(item CliConnection) bool { - return item.ID == id - }) -} diff --git a/pkg/cmd/rds/deploy/commands.go b/pkg/cmd/rds/deploy/commands.go deleted file mode 100644 index 4770dae..0000000 --- a/pkg/cmd/rds/deploy/commands.go +++ /dev/null @@ -1,101 +0,0 @@ -package deploy - -import ( - "fmt" - jsoniter "github.com/json-iterator/go" - "io" - "os" - "strings" - - "code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rds/conn" - "code.smartsheep.studio/goatworks/roadsign/pkg/sign" - "github.com/gofiber/fiber/v2" - "github.com/rs/zerolog/log" - "github.com/urfave/cli/v2" - "gopkg.in/yaml.v2" -) - -var DeployCommands = []*cli.Command{ - { - Name: "deploy", - Aliases: []string{"dp"}, - ArgsUsage: " [path]", - Action: func(ctx *cli.Context) error { - if ctx.Args().Len() < 4 { - return fmt.Errorf("must have four arguments: ") - } - - if !strings.HasSuffix(ctx.Args().Get(3), ".zip") { - return fmt.Errorf("input file must be a zip file and ends with .zip") - } - - server, ok := conn.GetConnection(ctx.Args().Get(0)) - if !ok { - return fmt.Errorf("server was not found, use \"rds connect\" add one first") - } else if err := server.CheckConnectivity(); err != nil { - return fmt.Errorf("couldn't connect server: %s", err.Error()) - } - - // Send request - log.Info().Msg("Now publishing to remote server...") - - url := fmt.Sprintf("/webhooks/publish/%s/%s?mimetype=%s", ctx.Args().Get(1), ctx.Args().Get(2), "application/zip") - client := fiber.Put(server.Url+url). - SendFile(ctx.Args().Get(3), "attachments"). - MultipartForm(nil). - BasicAuth("RoadSign CLI", server.Credential) - - if status, data, err := client.Bytes(); len(err) > 0 { - return fmt.Errorf("failed to publish to remote: %q", err) - } else if status != 200 { - return fmt.Errorf("server rejected request, status code %d, response %s", status, string(data)) - } - - log.Info().Msg("Well done! Your site is successfully published! 🎉") - - return nil - }, - }, - { - Name: "sync", - Aliases: []string{"sc"}, - ArgsUsage: " ", - Action: func(ctx *cli.Context) error { - if ctx.Args().Len() < 3 { - return fmt.Errorf("must have three arguments: ") - } - - server, ok := conn.GetConnection(ctx.Args().Get(0)) - if !ok { - return fmt.Errorf("server was not found, use \"rds connect\" add one first") - } else if err := server.CheckConnectivity(); err != nil { - return fmt.Errorf("couldn't connect server: %s", err.Error()) - } - - var site sign.SiteConfig - if file, err := os.Open(ctx.Args().Get(2)); err != nil { - return err - } else { - raw, _ := io.ReadAll(file) - yaml.Unmarshal(raw, &site) - } - - url := fmt.Sprintf("/webhooks/sync/%s", ctx.Args().Get(1)) - client := fiber.Put(server.Url+url). - JSONEncoder(jsoniter.ConfigCompatibleWithStandardLibrary.Marshal). - JSONDecoder(jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal). - JSON(site). - BasicAuth("RoadSign CLI", server.Credential) - - if status, data, err := client.Bytes(); len(err) > 0 { - return fmt.Errorf("failed to sync to remote: %q", err) - } else if status != 200 { - return fmt.Errorf("server rejected request, status code %d, response %s", status, string(data)) - } - - log.Info().Msg("Well done! Your site configuration is up-to-date! 🎉") - - return nil - }, - }, -} diff --git a/pkg/cmd/rds/main.go b/pkg/cmd/rds/main.go deleted file mode 100644 index 775315d..0000000 --- a/pkg/cmd/rds/main.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - "os" - - roadsign "code.smartsheep.studio/goatworks/roadsign/pkg" - "code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rds/conn" - "code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rds/deploy" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - "github.com/spf13/viper" - "github.com/urfave/cli/v2" -) - -func init() { - zerolog.TimeFieldFormat = zerolog.TimeFormatUnix - log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout}) -} - -func main() { - // Configure settings - viper.AddConfigPath("$HOME") - viper.SetConfigName(".roadsignrc") - viper.SetConfigType("yaml") - - // Load settings - if err := viper.ReadInConfig(); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); ok { - viper.SafeWriteConfig() - viper.ReadInConfig() - } else { - log.Panic().Err(err).Msg("An error occurred when loading settings.") - } - } - - // Configure CLI - app := &cli.App{ - Name: "RoadSign CLI", - Version: roadsign.AppVersion, - Suggest: true, - Commands: append(append([]*cli.Command{}, conn.CliCommands...), deploy.DeployCommands...), - } - - // Run CLI - if err := app.Run(os.Args); err != nil { - log.Fatal().Err(err).Msg("An error occurred when running cli.") - } -} diff --git a/pkg/cmd/server/main.go b/pkg/cmd/server/main.go deleted file mode 100644 index 3d2af03..0000000 --- a/pkg/cmd/server/main.go +++ /dev/null @@ -1,86 +0,0 @@ -package main - -import ( - "os" - "os/signal" - "strings" - "syscall" - - roadsign "code.smartsheep.studio/goatworks/roadsign/pkg" - "code.smartsheep.studio/goatworks/roadsign/pkg/hypertext" - "code.smartsheep.studio/goatworks/roadsign/pkg/sideload" - "code.smartsheep.studio/goatworks/roadsign/pkg/sign" - "github.com/google/uuid" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - "github.com/spf13/viper" -) - -func init() { - zerolog.TimeFieldFormat = zerolog.TimeFormatUnix - log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout}) -} - -func main() { - // Configure settings - viper.AddConfigPath(".") - viper.AddConfigPath("..") - viper.SetConfigName("settings") - viper.SetConfigType("yaml") - - // Load settings - if err := viper.ReadInConfig(); err != nil { - log.Panic().Err(err).Msg("An error occurred when loading settings.") - } - - // Present settings - if len(viper.GetString("security.credential")) <= 0 { - credential := strings.ReplaceAll(uuid.NewString(), "-", "") - viper.Set("security.credential", credential) - _ = viper.WriteConfig() - - log.Warn().Msg("There isn't any api credential configured in settings.yml, auto generated a credential for api accessing.") - log.Warn().Msgf("RoadSign auto generated api credential is %s", credential) - } - - // Load & init sign - if err := sign.ReadInConfig(viper.GetString("paths.configs")); err != nil { - log.Panic().Err(err).Msg("An error occurred when loading configurations.") - } else { - log.Info().Int("count", len(sign.App.Sites)).Msg("All configuration has been loaded.") - } - - // Preheat processes - go func() { - log.Info().Msg("Preheating processes...") - sign.App.PreheatProcesses(func(total int, success int) { - log.Info().Int("requested", total).Int("succeed", success).Msgf("Preheat processes completed!") - }) - }() - - // Init hypertext server - hypertext.RunServer( - hypertext.InitServer(), - viper.GetStringSlice("hypertext.ports"), - viper.GetStringSlice("hypertext.secured_ports"), - viper.GetString("hypertext.certificate.pem"), - viper.GetString("hypertext.certificate.key"), - ) - - // Init sideload server - hypertext.RunServer( - sideload.InitSideload(), - viper.GetStringSlice("hypertext.sideload_ports"), - viper.GetStringSlice("hypertext.sideload_secured_ports"), - viper.GetString("hypertext.certificate.sideload_pem"), - viper.GetString("hypertext.certificate.sideload_key"), - ) - - log.Info().Msgf("RoadSign v%s is started...", roadsign.AppVersion) - - quit := make(chan os.Signal, 1) - signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) - <-quit - - log.Info().Msgf("RoadSign v%s is quitting...", roadsign.AppVersion) -} diff --git a/pkg/hypertext/proxies.go b/pkg/hypertext/proxies.go deleted file mode 100644 index 719d377..0000000 --- a/pkg/hypertext/proxies.go +++ /dev/null @@ -1,111 +0,0 @@ -package hypertext - -import ( - "regexp" - - "code.smartsheep.studio/goatworks/roadsign/pkg/sign" - "github.com/gofiber/fiber/v2" - "github.com/samber/lo" -) - -func UseProxies(app *fiber.App) { - app.All("/*", func(ctx *fiber.Ctx) error { - host := ctx.Hostname() - path := ctx.Path() - queries := ctx.Queries() - headers := ctx.GetReqHeaders() - - // Filtering sites - for _, site := range sign.App.Sites { - // Matching rules - for _, rule := range site.Rules { - if !lo.Contains(rule.Host, host) { - continue - } - - if !func() bool { - flag := false - for _, pattern := range rule.Path { - if ok, _ := regexp.MatchString(pattern, path); ok { - flag = true - break - } - } - return flag - }() { - continue - } - - // Filter query strings - flag := true - for rk, rv := range rule.Queries { - for ik, iv := range queries { - if rk != ik && rv != iv { - flag = false - break - } - } - if !flag { - break - } - } - if !flag { - continue - } - - // Filter headers - for rk, rv := range rule.Headers { - for ik, iv := range headers { - if rk == ik { - for _, ov := range iv { - if !lo.Contains(rv, ov) { - flag = false - break - } - } - } - if !flag { - break - } - } - if !flag { - break - } - } - if !flag { - continue - } - - // Passing all the rules means the site is what we are looking for. - // Let us respond to our client! - return makeResponse(ctx, site) - } - } - - // There is no site available for this request. - // Just ignore it and give our client a not found status. - // Do not care about the user experience, we can do it in custom error handler. - return fiber.ErrNotFound - }) -} - -func makeResponse(ctx *fiber.Ctx, site *sign.SiteConfig) error { - // Modify request - for _, transformer := range site.Transformers { - if err := transformer.TransformRequest(ctx); err != nil { - return err - } - } - - // Forward - err := sign.App.Forward(ctx, site) - - // Modify response - for _, transformer := range site.Transformers { - if err := transformer.TransformResponse(ctx); err != nil { - return err - } - } - - return err -} diff --git a/pkg/hypertext/server.go b/pkg/hypertext/server.go deleted file mode 100644 index 44c5a36..0000000 --- a/pkg/hypertext/server.go +++ /dev/null @@ -1,79 +0,0 @@ -package hypertext - -import ( - jsoniter "github.com/json-iterator/go" - "strings" - "time" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/limiter" - "github.com/gofiber/fiber/v2/middleware/logger" - "github.com/rs/zerolog/log" - "github.com/spf13/viper" -) - -func InitServer() *fiber.App { - app := fiber.New(fiber.Config{ - AppName: "RoadSign", - ServerHeader: "RoadSign", - DisableStartupMessage: true, - EnableIPValidation: true, - JSONDecoder: jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal, - JSONEncoder: jsoniter.ConfigCompatibleWithStandardLibrary.Marshal, - Prefork: viper.GetBool("performance.prefork"), - BodyLimit: viper.GetInt("hypertext.limitation.max_body_size"), - }) - - if viper.GetBool("performance.request_logging") { - app.Use(logger.New(logger.Config{ - Output: log.Logger, - Format: "[Proxies] [${time}] ${status} - ${latency} ${method} ${path}\n", - })) - } - - if viper.GetInt("hypertext.limitation.max_qps") > 0 { - app.Use(limiter.New(limiter.Config{ - Max: viper.GetInt("hypertext.limitation.max_qps"), - Expiration: 1 * time.Second, - })) - } - - UseProxies(app) - - return app -} - -func RunServer(app *fiber.App, ports []string, securedPorts []string, pem string, key string) { - for _, port := range ports { - port := port - go func() { - if viper.GetBool("hypertext.certificate.redirect") { - redirector := fiber.New(fiber.Config{ - AppName: "RoadSign", - ServerHeader: "RoadSign", - DisableStartupMessage: true, - EnableIPValidation: true, - }) - redirector.All("/", func(c *fiber.Ctx) error { - return c.Redirect(strings.ReplaceAll(string(c.Request().URI().FullURI()), "http", "https")) - }) - if err := redirector.Listen(port); err != nil { - log.Panic().Err(err).Msg("An error occurred when listening hypertext common ports.") - } - } else { - if err := app.Listen(port); err != nil { - log.Panic().Err(err).Msg("An error occurred when listening hypertext common ports.") - } - } - }() - } - - for _, port := range securedPorts { - port := port - go func() { - if err := app.ListenTLS(port, pem, key); err != nil { - log.Panic().Err(err).Msg("An error occurred when listening hypertext tls ports.") - } - }() - } -} diff --git a/pkg/meta.go b/pkg/meta.go deleted file mode 100644 index 0aa016b..0000000 --- a/pkg/meta.go +++ /dev/null @@ -1,17 +0,0 @@ -package roadsign - -import ( - "runtime/debug" -) - -func init() { - if info, ok := debug.ReadBuildInfo(); ok { - for _, setting := range info.Settings { - if setting.Key == "vcs.revision" { - AppVersion += "#" + setting.Value - } - } - } -} - -var AppVersion = "1.2.1" diff --git a/pkg/sideload/connectivity.go b/pkg/sideload/connectivity.go deleted file mode 100644 index 298c449..0000000 --- a/pkg/sideload/connectivity.go +++ /dev/null @@ -1,13 +0,0 @@ -package sideload - -import ( - roadsign "code.smartsheep.studio/goatworks/roadsign/pkg" - "github.com/gofiber/fiber/v2" -) - -func responseConnectivity(c *fiber.Ctx) error { - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "server": "RoadSign", - "version": roadsign.AppVersion, - }) -} diff --git a/pkg/sideload/processes.go b/pkg/sideload/processes.go deleted file mode 100644 index 6d4267e..0000000 --- a/pkg/sideload/processes.go +++ /dev/null @@ -1,29 +0,0 @@ -package sideload - -import ( - "code.smartsheep.studio/goatworks/roadsign/pkg/sign" - "github.com/gofiber/fiber/v2" - "github.com/samber/lo" -) - -func getProcesses(c *fiber.Ctx) error { - processes := lo.FlatMap(sign.App.Sites, func(item *sign.SiteConfig, idx int) []*sign.ProcessInstance { - return item.Processes - }) - - return c.JSON(processes) -} - -func getProcessLog(c *fiber.Ctx) error { - processes := lo.FlatMap(sign.App.Sites, func(item *sign.SiteConfig, idx int) []*sign.ProcessInstance { - return item.Processes - }) - - if target, ok := lo.Find(processes, func(item *sign.ProcessInstance) bool { - return item.ID == c.Params("id") - }); !ok { - return fiber.NewError(fiber.StatusNotFound) - } else { - return c.SendString(target.GetLogs()) - } -} diff --git a/pkg/sideload/publish.go b/pkg/sideload/publish.go deleted file mode 100644 index c27f85a..0000000 --- a/pkg/sideload/publish.go +++ /dev/null @@ -1,85 +0,0 @@ -package sideload - -import ( - "context" - "os" - "path/filepath" - - "code.smartsheep.studio/goatworks/roadsign/pkg/sign" - "github.com/gofiber/fiber/v2" - "github.com/google/uuid" - "github.com/samber/lo" - "github.com/saracen/fastzip" -) - -func doPublish(c *fiber.Ctx) error { - var workdir string - var site *sign.SiteConfig - var upstream *sign.UpstreamInstance - var process *sign.ProcessInstance - for _, item := range sign.App.Sites { - if item.ID == c.Params("site") { - site = item - for _, stream := range item.Upstreams { - if stream.ID == c.Params("slug") { - upstream = stream - workdir, _ = stream.GetRawURI() - break - } - } - for _, proc := range item.Processes { - if proc.ID == c.Params("slug") { - process = proc - workdir = proc.Workdir - break - } - } - break - } - } - - if upstream == nil && process == nil { - return fiber.ErrNotFound - } else if upstream != nil && upstream.GetType() != sign.UpstreamTypeFile { - return fiber.ErrUnprocessableEntity - } - - for _, process := range site.Processes { - process.StopProcess() - } - - if c.Query("overwrite", "yes") == "yes" { - files, _ := filepath.Glob(filepath.Join(workdir, "*")) - for _, file := range files { - _ = os.Remove(file) - } - } - - if form, err := c.MultipartForm(); err == nil { - files := form.File["attachments"] - for _, file := range files { - mimetype := lo.Ternary(len(c.Query("mimetype")) > 0, c.Query("mimetype"), file.Header["Content-Type"][0]) - switch mimetype { - case "application/zip": - dst := filepath.Join(os.TempDir(), uuid.NewString()+".zip") - if err := c.SaveFile(file, dst); err != nil { - return err - } else { - if ex, err := fastzip.NewExtractor(dst, workdir); err != nil { - return err - } else if err = ex.Extract(context.Background()); err != nil { - defer ex.Close() - return err - } - } - default: - dst := filepath.Join(workdir, file.Filename) - if err := c.SaveFile(file, dst); err != nil { - return err - } - } - } - } - - return c.SendStatus(fiber.StatusOK) -} diff --git a/pkg/sideload/server.go b/pkg/sideload/server.go deleted file mode 100644 index ce7bcba..0000000 --- a/pkg/sideload/server.go +++ /dev/null @@ -1,69 +0,0 @@ -package sideload - -import ( - "code.smartsheep.studio/goatworks/roadsign/pkg/sideload/view" - "fmt" - "github.com/gofiber/fiber/v2/middleware/filesystem" - jsoniter "github.com/json-iterator/go" - "net/http" - - roadsign "code.smartsheep.studio/goatworks/roadsign/pkg" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/basicauth" - "github.com/gofiber/fiber/v2/middleware/logger" - "github.com/rs/zerolog/log" - "github.com/spf13/viper" -) - -func InitSideload() *fiber.App { - app := fiber.New(fiber.Config{ - AppName: "RoadSign Sideload", - ServerHeader: "RoadSign Sideload", - DisableStartupMessage: true, - EnableIPValidation: true, - JSONDecoder: jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal, - JSONEncoder: jsoniter.ConfigCompatibleWithStandardLibrary.Marshal, - EnablePrintRoutes: viper.GetBool("debug.print_routes"), - TrustedProxies: viper.GetStringSlice("security.sideload_trusted_proxies"), - BodyLimit: viper.GetInt("hypertext.limitation.max_body_size"), - }) - - if viper.GetBool("performance.request_logging") { - app.Use(logger.New(logger.Config{ - Output: log.Logger, - Format: "[Sideload] [${time}] ${status} - ${latency} ${method} ${path}\n", - })) - } - - app.Use(basicauth.New(basicauth.Config{ - Realm: fmt.Sprintf("RoadSign v%s", roadsign.AppVersion), - Authorizer: func(_, password string) bool { - return password == viper.GetString("security.credential") - }, - })) - - app.Use("/", filesystem.New(filesystem.Config{ - Root: http.FS(view.FS), - PathPrefix: "dist", - Index: "index.html", - NotFoundFile: "index.html", - })) - - cgi := app.Group("/cgi").Name("CGI") - { - cgi.All("/connectivity", responseConnectivity) - cgi.Get("/statistics", getStatistics) - cgi.Get("/sites", getSites) - cgi.Get("/sites/cfg/:id", getSiteConfig) - cgi.Get("/processes", getProcesses) - cgi.Get("/processes/logs/:id", getProcessLog) - } - - webhooks := app.Group("/webhooks").Name("WebHooks") - { - webhooks.Put("/publish/:site/:slug", doPublish) - webhooks.Put("/sync/:slug", doSyncSite) - } - - return app -} diff --git a/pkg/sideload/sites.go b/pkg/sideload/sites.go deleted file mode 100644 index 6bb42b9..0000000 --- a/pkg/sideload/sites.go +++ /dev/null @@ -1,63 +0,0 @@ -package sideload - -import ( - "fmt" - "os" - "path/filepath" - - "code.smartsheep.studio/goatworks/roadsign/pkg/sign" - "github.com/gofiber/fiber/v2" - "github.com/samber/lo" - "github.com/spf13/viper" - "gopkg.in/yaml.v2" -) - -func getSites(c *fiber.Ctx) error { - return c.JSON(sign.App.Sites) -} - -func getSiteConfig(c *fiber.Ctx) error { - fp := filepath.Join(viper.GetString("paths.configs"), c.Params("id")) - - var err error - var data []byte - if data, err = os.ReadFile(fp + ".yml"); err != nil { - if data, err = os.ReadFile(fp + ".yaml"); err != nil { - return fiber.NewError(fiber.StatusNotFound, err.Error()) - } - } - - return c.Type("yaml").SendString(string(data)) -} - -func doSyncSite(c *fiber.Ctx) error { - var req sign.SiteConfig - - if err := c.BodyParser(&req); err != nil { - return err - } - - id := c.Params("slug") - path := filepath.Join(viper.GetString("paths.configs"), fmt.Sprintf("%s.yaml", id)) - - if file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755); err != nil { - return fiber.NewError(fiber.ErrInternalServerError.Code, err.Error()) - } else { - raw, _ := yaml.Marshal(req) - file.Write(raw) - defer file.Close() - } - if site, ok := lo.Find(sign.App.Sites, func(item *sign.SiteConfig) bool { - return item.ID == id - }); ok { - for _, process := range site.Processes { - process.StopProcess() - } - } - - // Reload - sign.ReadInConfig(viper.GetString("paths.configs")) - sign.App.PreheatProcesses() - - return c.SendStatus(fiber.StatusOK) -} diff --git a/pkg/sideload/statistics.go b/pkg/sideload/statistics.go deleted file mode 100644 index 576e641..0000000 --- a/pkg/sideload/statistics.go +++ /dev/null @@ -1,28 +0,0 @@ -package sideload - -import ( - "code.smartsheep.studio/goatworks/roadsign/pkg/sign" - "github.com/gofiber/fiber/v2" - "github.com/samber/lo" -) - -func getStatistics(c *fiber.Ctx) error { - upstreams := lo.FlatMap(sign.App.Sites, func(item *sign.SiteConfig, idx int) []*sign.UpstreamInstance { - return item.Upstreams - }) - processes := lo.FlatMap(sign.App.Sites, func(item *sign.SiteConfig, idx int) []*sign.ProcessInstance { - return item.Processes - }) - unhealthy := lo.FlatMap(sign.App.Sites, func(item *sign.SiteConfig, idx int) []*sign.ProcessInstance { - return lo.Filter(item.Processes, func(item *sign.ProcessInstance, idx int) bool { - return item.Status != sign.ProcessStarted - }) - }) - - return c.JSON(fiber.Map{ - "sites": len(sign.App.Sites), - "upstreams": len(upstreams), - "processes": len(processes), - "status": len(unhealthy) == 0, - }) -} diff --git a/pkg/sideload/view/.dockerignore b/pkg/sideload/view/.dockerignore deleted file mode 100644 index 0b71a35..0000000 --- a/pkg/sideload/view/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -/dist -/node_modules -/*.lock diff --git a/pkg/sideload/view/.eslintrc.cjs b/pkg/sideload/view/.eslintrc.cjs deleted file mode 100644 index 6f40582..0000000 --- a/pkg/sideload/view/.eslintrc.cjs +++ /dev/null @@ -1,15 +0,0 @@ -/* eslint-env node */ -require('@rushstack/eslint-patch/modern-module-resolution') - -module.exports = { - root: true, - 'extends': [ - 'plugin:vue/vue3-essential', - 'eslint:recommended', - '@vue/eslint-config-typescript', - '@vue/eslint-config-prettier/skip-formatting' - ], - parserOptions: { - ecmaVersion: 'latest' - } -} diff --git a/pkg/sideload/view/.gitignore b/pkg/sideload/view/.gitignore deleted file mode 100644 index 8ee54e8..0000000 --- a/pkg/sideload/view/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -.DS_Store -dist -dist-ssr -coverage -*.local - -/cypress/videos/ -/cypress/screenshots/ - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? - -*.tsbuildinfo diff --git a/pkg/sideload/view/.prettierrc.json b/pkg/sideload/view/.prettierrc.json deleted file mode 100644 index dbdb811..0000000 --- a/pkg/sideload/view/.prettierrc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/prettierrc", - "semi": false, - "tabWidth": 2, - "singleQuote": false, - "printWidth": 120, - "trailingComma": "none" -} \ No newline at end of file diff --git a/pkg/sideload/view/.vscode/extensions.json b/pkg/sideload/view/.vscode/extensions.json deleted file mode 100644 index 009a534..0000000 --- a/pkg/sideload/view/.vscode/extensions.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "recommendations": [ - "Vue.volar", - "Vue.vscode-typescript-vue-plugin", - "dbaeumer.vscode-eslint", - "esbenp.prettier-vscode" - ] -} diff --git a/pkg/sideload/view/README.md b/pkg/sideload/view/README.md deleted file mode 100644 index 61136fc..0000000 --- a/pkg/sideload/view/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# @roadsign/sideload-ui - -This template should help get you started developing with Vue 3 in Vite. - -## Recommended IDE Setup - -[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). - -## Type Support for `.vue` Imports in TS - -TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types. - -If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps: - -1. Disable the built-in TypeScript Extension - 1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette - 2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)` -2. Reload the VSCode window by running `Developer: Reload Window` from the command palette. - -## Customize configuration - -See [Vite Configuration Reference](https://vitejs.dev/config/). - -## Project Setup - -```sh -yarn -``` - -### Compile and Hot-Reload for Development - -```sh -yarn dev -``` - -### Type-Check, Compile and Minify for Production - -```sh -yarn build -``` - -### Lint with [ESLint](https://eslint.org/) - -```sh -yarn lint -``` diff --git a/pkg/sideload/view/embed.go b/pkg/sideload/view/embed.go deleted file mode 100644 index ec34587..0000000 --- a/pkg/sideload/view/embed.go +++ /dev/null @@ -1,6 +0,0 @@ -package view - -import "embed" - -//go:embed all:dist -var FS embed.FS diff --git a/pkg/sideload/view/env.d.ts b/pkg/sideload/view/env.d.ts deleted file mode 100644 index 11f02fe..0000000 --- a/pkg/sideload/view/env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/pkg/sideload/view/index.html b/pkg/sideload/view/index.html deleted file mode 100644 index c800b36..0000000 --- a/pkg/sideload/view/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - RoadSign - - -
- - - diff --git a/pkg/sideload/view/package.json b/pkg/sideload/view/package.json deleted file mode 100644 index 3f4be12..0000000 --- a/pkg/sideload/view/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "@roadsign/sideload-ui", - "version": "0.0.0", - "private": true, - "type": "module", - "scripts": { - "dev": "vite", - "build": "run-p type-check \"build-only {@}\" --", - "preview": "vite preview", - "build-only": "vite build", - "type-check": "vue-tsc --build --force", - "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", - "format": "prettier --write src/" - }, - "dependencies": { - "@guolao/vue-monaco-editor": "^1.4.1", - "highlight.js": "^11.9.0", - "js-yaml": "^4.1.0", - "pinia": "^2.1.7", - "vue": "^3.3.11", - "vue-router": "^4.2.5" - }, - "devDependencies": { - "@rushstack/eslint-patch": "^1.3.3", - "@tsconfig/node18": "^18.2.2", - "@types/js-yaml": "^4.0.9", - "@types/node": "^18.19.3", - "@vicons/carbon": "^0.12.0", - "@vitejs/plugin-vue": "^4.5.2", - "@vue/eslint-config-prettier": "^8.0.0", - "@vue/eslint-config-typescript": "^12.0.0", - "@vue/tsconfig": "^0.5.0", - "eslint": "^8.49.0", - "eslint-plugin-vue": "^9.17.0", - "naive-ui": "^2.36.0", - "npm-run-all2": "^6.1.1", - "prettier": "^3.0.3", - "typescript": "~5.3.0", - "unocss": "^0.58.2", - "vfonts": "^0.0.3", - "vite": "^5.0.10", - "vue-tsc": "^1.8.25" - } -} diff --git a/pkg/sideload/view/src/assets/main.css b/pkg/sideload/view/src/assets/main.css deleted file mode 100644 index b60180e..0000000 --- a/pkg/sideload/view/src/assets/main.css +++ /dev/null @@ -1,6 +0,0 @@ -@import "vfonts/IBMPlexSans.css"; -@import "vfonts/IBMPlexMono.css"; - -a { - color: #3f7ee8; -} diff --git a/pkg/sideload/view/src/components/data/sites-table-action.vue b/pkg/sideload/view/src/components/data/sites-table-action.vue deleted file mode 100644 index 2443c9c..0000000 --- a/pkg/sideload/view/src/components/data/sites-table-action.vue +++ /dev/null @@ -1,135 +0,0 @@ - - - - - \ No newline at end of file diff --git a/pkg/sideload/view/src/components/data/sites-table-add.vue b/pkg/sideload/view/src/components/data/sites-table-add.vue deleted file mode 100644 index 459c0b6..0000000 --- a/pkg/sideload/view/src/components/data/sites-table-add.vue +++ /dev/null @@ -1,110 +0,0 @@ - - - - - \ No newline at end of file diff --git a/pkg/sideload/view/src/components/data/sites-table-expand.vue b/pkg/sideload/view/src/components/data/sites-table-expand.vue deleted file mode 100644 index eeafa95..0000000 --- a/pkg/sideload/view/src/components/data/sites-table-expand.vue +++ /dev/null @@ -1,36 +0,0 @@ - - - \ No newline at end of file diff --git a/pkg/sideload/view/src/components/data/sites-table.vue b/pkg/sideload/view/src/components/data/sites-table.vue deleted file mode 100644 index cffd6d2..0000000 --- a/pkg/sideload/view/src/components/data/sites-table.vue +++ /dev/null @@ -1,76 +0,0 @@ - - - \ No newline at end of file diff --git a/pkg/sideload/view/src/layouts/main.vue b/pkg/sideload/view/src/layouts/main.vue deleted file mode 100644 index 88260be..0000000 --- a/pkg/sideload/view/src/layouts/main.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - - - diff --git a/pkg/sideload/view/src/main.ts b/pkg/sideload/view/src/main.ts deleted file mode 100644 index d8a2907..0000000 --- a/pkg/sideload/view/src/main.ts +++ /dev/null @@ -1,16 +0,0 @@ -import "./assets/main.css" - -import "virtual:uno.css" - -import { createApp } from "vue" -import { createPinia } from "pinia" - -import root from "./root.vue" -import router from "./router" - -const app = createApp(root) - -app.use(createPinia()) -app.use(router) - -app.mount("#app") diff --git a/pkg/sideload/view/src/root.vue b/pkg/sideload/view/src/root.vue deleted file mode 100644 index ad671c5..0000000 --- a/pkg/sideload/view/src/root.vue +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/pkg/sideload/view/src/router/index.ts b/pkg/sideload/view/src/router/index.ts deleted file mode 100644 index a58c1aa..0000000 --- a/pkg/sideload/view/src/router/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { createRouter, createWebHistory } from "vue-router" - -const router = createRouter({ - history: createWebHistory(import.meta.env.BASE_URL), - routes: [ - { - path: "/", - name: "layouts.main", - component: () => import("@/layouts/main.vue"), - children: [ - { - path: "/", - name: "dashboard", - component: () => import("@/views/dashboard.vue") - }, - ] - }, - ] -}) - -export default router diff --git a/pkg/sideload/view/src/views/dashboard.vue b/pkg/sideload/view/src/views/dashboard.vue deleted file mode 100644 index 5ed9d3d..0000000 --- a/pkg/sideload/view/src/views/dashboard.vue +++ /dev/null @@ -1,35 +0,0 @@ - - - \ No newline at end of file diff --git a/pkg/sideload/view/tsconfig.app.json b/pkg/sideload/view/tsconfig.app.json deleted file mode 100644 index 491e093..0000000 --- a/pkg/sideload/view/tsconfig.app.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "@vue/tsconfig/tsconfig.dom.json", - "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], - "exclude": ["src/**/__tests__/*"], - "compilerOptions": { - "composite": true, - "noEmit": true, - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - } - } -} diff --git a/pkg/sideload/view/tsconfig.json b/pkg/sideload/view/tsconfig.json deleted file mode 100644 index 66b5e57..0000000 --- a/pkg/sideload/view/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "files": [], - "references": [ - { - "path": "./tsconfig.node.json" - }, - { - "path": "./tsconfig.app.json" - } - ] -} diff --git a/pkg/sideload/view/tsconfig.node.json b/pkg/sideload/view/tsconfig.node.json deleted file mode 100644 index 46cf2e1..0000000 --- a/pkg/sideload/view/tsconfig.node.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "@tsconfig/node18/tsconfig.json", - "include": [ - "vite.config.*", - "vitest.config.*", - "cypress.config.*", - "nightwatch.conf.*", - "playwright.config.*" - ], - "compilerOptions": { - "composite": true, - "noEmit": true, - "module": "ESNext", - "moduleResolution": "Bundler", - "types": ["node"] - } -} diff --git a/pkg/sideload/view/unocss.config.ts b/pkg/sideload/view/unocss.config.ts deleted file mode 100644 index 84a0950..0000000 --- a/pkg/sideload/view/unocss.config.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { defineConfig, presetUno } from "unocss" - -export default defineConfig({ - presets: [presetUno({ preflight: false })] -}) \ No newline at end of file diff --git a/pkg/sideload/view/vite.config.ts b/pkg/sideload/view/vite.config.ts deleted file mode 100644 index ed77420..0000000 --- a/pkg/sideload/view/vite.config.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { fileURLToPath, URL } from "node:url" - -import { defineConfig } from "vite" -import vue from "@vitejs/plugin-vue" -import unocss from "unocss/vite" - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [ - vue(), - unocss() - ], - resolve: { - alias: { - "@": fileURLToPath(new URL("./src", import.meta.url)) - } - }, - server: { - proxy: { - "/webhooks": "http://127.0.0.1:81", - "/cgi": "http://127.0.0.1:81" - } - } -}) diff --git a/pkg/sign/configurator.go b/pkg/sign/configurator.go deleted file mode 100644 index 250a347..0000000 --- a/pkg/sign/configurator.go +++ /dev/null @@ -1,45 +0,0 @@ -package sign - -import ( - "io" - "os" - "path/filepath" - "strings" - - "gopkg.in/yaml.v2" -) - -var App *RoadApp - -func ReadInConfig(root string) error { - instance := &RoadApp{ - Sites: []*SiteConfig{}, - } - - if err := filepath.Walk(root, func(fp string, info os.FileInfo, err error) error { - var site SiteConfig - if info.IsDir() { - return nil - } else if file, err := os.OpenFile(fp, os.O_RDONLY, 0755); err != nil { - return err - } else if data, err := io.ReadAll(file); err != nil { - return err - } else if err := yaml.Unmarshal(data, &site); err != nil { - return err - } else { - defer file.Close() - - // Extract file name as site id - site.ID = strings.SplitN(filepath.Base(fp), ".", 2)[0] - instance.Sites = append(instance.Sites, &site) - } - - return nil - }); err != nil { - return err - } - - App = instance - - return nil -} diff --git a/pkg/sign/pm.go b/pkg/sign/pm.go deleted file mode 100644 index 7ca620d..0000000 --- a/pkg/sign/pm.go +++ /dev/null @@ -1,144 +0,0 @@ -package sign - -import ( - "fmt" - "github.com/samber/lo" - "os" - "os/exec" - "path/filepath" - "strings" - "time" -) - -type ProcessStatus = int8 - -const ( - ProcessCreated = ProcessStatus(iota) - ProcessStarting - ProcessStarted - ProcessExited - ProcessFailure -) - -type ProcessInstance struct { - ID string `json:"id" yaml:"id"` - Workdir string `json:"workdir" yaml:"workdir"` - Command []string `json:"command" yaml:"command"` - Environment []string `json:"environment" yaml:"environment"` - Prepares [][]string `json:"prepares" yaml:"prepares"` - Preheat bool `json:"preheat" yaml:"preheat"` - - Cmd *exec.Cmd `json:"-"` - Logger strings.Builder `json:"-"` - - Status ProcessStatus `json:"status"` -} - -func (v *ProcessInstance) BootProcess() error { - if v.Cmd != nil { - return nil - } - if err := v.PrepareProcess(); err != nil { - return err - } - if v.Cmd == nil { - return v.StartProcess() - } - if v.Cmd.Process == nil || v.Cmd.ProcessState == nil { - return v.StartProcess() - } - if v.Cmd.ProcessState.Exited() { - return v.StartProcess() - } else if v.Cmd.ProcessState.Exited() { - return fmt.Errorf("process already dead") - } - if v.Cmd.ProcessState.Exited() { - return fmt.Errorf("cannot start process") - } else { - return nil - } -} - -func (v *ProcessInstance) PrepareProcess() error { - for _, script := range v.Prepares { - if len(script) <= 0 { - continue - } - cmd := exec.Command(script[0], script[1:]...) - cmd.Dir = filepath.Join(v.Workdir) - if err := cmd.Run(); err != nil { - return err - } - } - return nil -} - -func (v *ProcessInstance) StartProcess() error { - if len(v.Command) <= 0 { - return fmt.Errorf("you need set the command for %s to enable process manager", v.ID) - } - - v.Cmd = exec.Command(v.Command[0], v.Command[1:]...) - v.Cmd.Dir = filepath.Join(v.Workdir) - v.Cmd.Env = append(v.Cmd.Env, v.Environment...) - v.Cmd.Stdout = &v.Logger - v.Cmd.Stderr = &v.Logger - - // Monitor - go func() { - for { - if v.Cmd.Process == nil || v.Cmd.ProcessState == nil { - v.Status = ProcessStarting - } else if !v.Cmd.ProcessState.Exited() { - v.Status = ProcessStarted - } else { - v.Status = lo.Ternary(v.Cmd.ProcessState.Success(), ProcessExited, ProcessFailure) - return - } - time.Sleep(100 * time.Millisecond) - } - }() - - return v.Cmd.Start() -} - -func (v *ProcessInstance) StopProcess() error { - if v.Cmd != nil && v.Cmd.Process != nil { - if err := v.Cmd.Process.Signal(os.Interrupt); err != nil { - v.Cmd.Process.Kill() - return err - } else { - v.Cmd = nil - } - } - - return nil -} - -func (v *ProcessInstance) GetLogs() string { - return v.Logger.String() -} - -func (v *RoadApp) PreheatProcesses(callbacks ...func(total int, success int)) { - var processes []*ProcessInstance - for _, site := range v.Sites { - for _, process := range site.Processes { - if process.Preheat { - processes = append(processes, process) - } - } - } - - success := 0 - for _, process := range processes { - if process.BootProcess() == nil { - success++ - } - } - - if len(callbacks) > 0 { - for _, callback := range callbacks { - callback(len(processes), success) - } - } -} diff --git a/pkg/sign/responder.go b/pkg/sign/responder.go deleted file mode 100644 index 5f53d9e..0000000 --- a/pkg/sign/responder.go +++ /dev/null @@ -1,127 +0,0 @@ -package sign - -import ( - "errors" - "fmt" - "io/fs" - "net/http" - "path/filepath" - "strconv" - "strings" - "time" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/proxy" - "github.com/gofiber/fiber/v2/utils" - "github.com/samber/lo" - "github.com/spf13/viper" - "github.com/valyala/fasthttp" -) - -func makeHypertextResponse(c *fiber.Ctx, upstream *UpstreamInstance) error { - timeout := time.Duration(viper.GetInt64("performance.network_timeout")) * time.Millisecond - return proxy.Do(c, upstream.MakeURI(c), &fasthttp.Client{ - ReadTimeout: timeout, - WriteTimeout: timeout, - }) -} - -func makeFileResponse(c *fiber.Ctx, upstream *UpstreamInstance) error { - uri, queries := upstream.GetRawURI() - root := http.Dir(uri) - - method := c.Method() - - // We only serve static assets for GET and HEAD methods - if method != fiber.MethodGet && method != fiber.MethodHead { - return c.Next() - } - - // Strip prefix - prefix := c.Route().Path - path := strings.TrimPrefix(c.Path(), prefix) - if !strings.HasPrefix(path, "/") { - path = "/" + path - } - - // Add prefix - if queries.Get("prefix") != "" { - path = queries.Get("prefix") + path - } - - if len(path) > 1 { - path = utils.TrimRight(path, '/') - } - - file, err := root.Open(path) - if err != nil && errors.Is(err, fs.ErrNotExist) { - if queries.Get("suffix") != "" { - file, err = root.Open(path + queries.Get("suffix")) - } - if err != nil && queries.Get("fallback") != "" { - file, err = root.Open(queries.Get("fallback")) - } - } - if err != nil { - if errors.Is(err, fs.ErrNotExist) { - return fiber.ErrNotFound - } - return fmt.Errorf("failed to open: %w", err) - } - - stat, err := file.Stat() - if err != nil { - return fmt.Errorf("failed to stat: %w", err) - } - - // Serve index if path is directory - if stat.IsDir() { - indexFile := lo.Ternary(len(queries.Get("index")) > 0, queries.Get("index"), "index.html") - indexPath := utils.TrimRight(path, '/') + indexFile - index, err := root.Open(indexPath) - if err == nil { - indexStat, err := index.Stat() - if err == nil { - file = index - stat = indexStat - } - } - } - - c.Status(fiber.StatusOK) - - modTime := stat.ModTime() - contentLength := int(stat.Size()) - - // Set Content-Type header - if queries.Get("charset") == "" { - c.Type(filepath.Ext(stat.Name())) - } else { - c.Type(filepath.Ext(stat.Name()), queries.Get("charset")) - } - - // Set Last-Modified header - if !modTime.IsZero() { - c.Set(fiber.HeaderLastModified, modTime.UTC().Format(http.TimeFormat)) - } - - if method == fiber.MethodGet { - maxAge, err := strconv.Atoi(queries.Get("maxAge")) - if lo.Ternary(err != nil, maxAge, 0) > 0 { - c.Set(fiber.HeaderCacheControl, "public, max-age="+queries.Get("maxAge")) - } - c.Response().SetBodyStream(file, contentLength) - return nil - } - if method == fiber.MethodHead { - c.Request().ResetBody() - c.Response().SkipBody = true - c.Response().Header.SetContentLength(contentLength) - if err := file.Close(); err != nil { - return fmt.Errorf("failed to close: %w", err) - } - return nil - } - - return fiber.ErrNotFound -} diff --git a/pkg/sign/router.go b/pkg/sign/router.go deleted file mode 100644 index 06d903f..0000000 --- a/pkg/sign/router.go +++ /dev/null @@ -1,58 +0,0 @@ -package sign - -import ( - "code.smartsheep.studio/goatworks/roadsign/pkg/sign/transformers" - "errors" - "math/rand" - - "github.com/gofiber/fiber/v2" - "github.com/rs/zerolog/log" -) - -type RoadApp struct { - Sites []*SiteConfig `json:"sites"` -} - -func (v *RoadApp) Forward(ctx *fiber.Ctx, site *SiteConfig) error { - if len(site.Upstreams) == 0 { - return errors.New("invalid configuration") - } - - // Boot processes - for _, process := range site.Processes { - if err := process.BootProcess(); err != nil { - log.Warn().Err(err).Msgf("An error occurred when booting process (%s) for %s", process.ID, site.ID) - return fiber.ErrBadGateway - } - } - - // Do forward - idx := rand.Intn(len(site.Upstreams)) - upstream := site.Upstreams[idx] - - switch upstream.GetType() { - case UpstreamTypeHypertext: - return makeHypertextResponse(ctx, upstream) - case UpstreamTypeFile: - return makeFileResponse(ctx, upstream) - default: - return fiber.ErrBadGateway - } -} - -type RequestTransformerConfig = transformers.RequestTransformerConfig - -type SiteConfig struct { - ID string `json:"id"` - Rules []*RouterRule `json:"rules" yaml:"rules"` - Transformers []*RequestTransformerConfig `json:"transformers" yaml:"transformers"` - Upstreams []*UpstreamInstance `json:"upstreams" yaml:"upstreams"` - Processes []*ProcessInstance `json:"processes" yaml:"processes"` -} - -type RouterRule struct { - Host []string `json:"host" yaml:"host"` - Path []string `json:"path" yaml:"path"` - Queries map[string]string `json:"queries" yaml:"queries"` - Headers map[string][]string `json:"headers" yaml:"headers"` -} diff --git a/pkg/sign/transformers/compress.go b/pkg/sign/transformers/compress.go deleted file mode 100644 index 5e8d3b1..0000000 --- a/pkg/sign/transformers/compress.go +++ /dev/null @@ -1,41 +0,0 @@ -package transformers - -import ( - "github.com/gofiber/fiber/v2" - "github.com/valyala/fasthttp" -) - -var CompressResponse = RequestTransformer{ - ModifyResponse: func(options any, ctx *fiber.Ctx) error { - opts := DeserializeOptions[struct { - Level int `json:"level" yaml:"level"` - }](options) - - var fctx = func(c *fasthttp.RequestCtx) {} - var compressor fasthttp.RequestHandler - switch opts.Level { - // Best Speed Mode - case 1: - compressor = fasthttp.CompressHandlerBrotliLevel(fctx, - fasthttp.CompressBrotliBestSpeed, - fasthttp.CompressBestSpeed, - ) - // Best Compression Mode - case 2: - compressor = fasthttp.CompressHandlerBrotliLevel(fctx, - fasthttp.CompressBrotliBestCompression, - fasthttp.CompressBestCompression, - ) - // Default Mode - default: - compressor = fasthttp.CompressHandlerBrotliLevel(fctx, - fasthttp.CompressBrotliDefaultCompression, - fasthttp.CompressDefaultCompression, - ) - } - - compressor(ctx.Context()) - - return nil - }, -} diff --git a/pkg/sign/transformers/module.go b/pkg/sign/transformers/module.go deleted file mode 100644 index 18cfe8a..0000000 --- a/pkg/sign/transformers/module.go +++ /dev/null @@ -1,61 +0,0 @@ -package transformers - -import ( - "github.com/gofiber/fiber/v2" - jsoniter "github.com/json-iterator/go" -) - -// Definitions - -var json = jsoniter.ConfigCompatibleWithStandardLibrary - -type RequestTransformer struct { - ModifyRequest func(options any, ctx *fiber.Ctx) error - ModifyResponse func(options any, ctx *fiber.Ctx) error -} - -type RequestTransformerConfig struct { - Type string `json:"type" yaml:"type"` - Options any `json:"options" yaml:"options"` -} - -func (v *RequestTransformerConfig) TransformRequest(ctx *fiber.Ctx) error { - for k, f := range Transformers { - if k == v.Type { - if f.ModifyRequest != nil { - return f.ModifyRequest(v.Options, ctx) - } - break - } - } - return nil -} - -func (v *RequestTransformerConfig) TransformResponse(ctx *fiber.Ctx) error { - for k, f := range Transformers { - if k == v.Type { - if f.ModifyResponse != nil { - return f.ModifyResponse(v.Options, ctx) - } - break - } - } - return nil -} - -// Helpers - -func DeserializeOptions[T any](data any) T { - var out T - raw, _ := json.Marshal(data) - _ = json.Unmarshal(raw, &out) - return out -} - -// Map of Transformers -// Every transformer need to be mapped here so that they can get work. - -var Transformers = map[string]RequestTransformer{ - "replacePath": ReplacePath, - "compressResponse": CompressResponse, -} diff --git a/pkg/sign/transformers/replace_path.go b/pkg/sign/transformers/replace_path.go deleted file mode 100644 index 8dc4d65..0000000 --- a/pkg/sign/transformers/replace_path.go +++ /dev/null @@ -1,25 +0,0 @@ -package transformers - -import ( - "github.com/gofiber/fiber/v2" - "regexp" - "strings" -) - -var ReplacePath = RequestTransformer{ - ModifyRequest: func(options any, ctx *fiber.Ctx) error { - opts := DeserializeOptions[struct { - Pattern string `json:"pattern" yaml:"pattern"` - Value string `json:"value" yaml:"value"` - Repl string `json:"repl" yaml:"repl"` // Use when complex mode(regexp) enabled - Complex bool `json:"complex" yaml:"complex"` - }](options) - path := string(ctx.Request().URI().Path()) - if !opts.Complex { - ctx.Path(strings.ReplaceAll(path, opts.Pattern, opts.Value)) - } else if ex := regexp.MustCompile(opts.Pattern); ex != nil { - ctx.Path(ex.ReplaceAllString(path, opts.Repl)) - } - return nil - }, -} diff --git a/pkg/sign/upstream.go b/pkg/sign/upstream.go deleted file mode 100644 index e4a20d6..0000000 --- a/pkg/sign/upstream.go +++ /dev/null @@ -1,58 +0,0 @@ -package sign - -import ( - "fmt" - "net/url" - "strings" - - "github.com/gofiber/fiber/v2" - "github.com/samber/lo" -) - -const ( - UpstreamTypeFile = "file" - UpstreamTypeHypertext = "hypertext" - UpstreamTypeUnknown = "unknown" -) - -type UpstreamInstance struct { - ID string `json:"id" yaml:"id"` - URI string `json:"uri" yaml:"uri"` -} - -func (v *UpstreamInstance) GetType() string { - protocol := strings.SplitN(v.URI, "://", 2)[0] - switch protocol { - case "file", "files": - return UpstreamTypeFile - case "http", "https": - return UpstreamTypeHypertext - } - - return UpstreamTypeUnknown -} - -func (v *UpstreamInstance) GetRawURI() (string, url.Values) { - uri := strings.SplitN(v.URI, "://", 2)[1] - data := strings.SplitN(uri, "?", 2) - data = append(data, " ") // Make data array least have two element - qs, _ := url.ParseQuery(data[0]) - - return data[0], qs -} - -func (v *UpstreamInstance) MakeURI(ctx *fiber.Ctx) string { - var queries []string - for k, v := range ctx.Queries() { - parsed, _ := url.QueryUnescape(v) - value := url.QueryEscape(parsed) - queries = append(queries, fmt.Sprintf("%s=%s", k, value)) - } - - path := string(ctx.Request().URI().Path()) - hash := string(ctx.Request().URI().Hash()) - - return v.URI + path + - lo.Ternary(len(queries) > 0, "?"+strings.Join(queries, "&"), "") + - lo.Ternary(len(hash) > 0, "#"+hash, "") -} diff --git a/settings.yml b/settings.yml deleted file mode 100644 index e473310..0000000 --- a/settings.yml +++ /dev/null @@ -1,28 +0,0 @@ -debug: - print_routes: true -hypertext: - sideload_ports: - - :81 - sideload_secured_ports: [ ] - certificate: - redirect: false - sideload_key: ./cert.key - sideload_pem: ./cert.pem - key: ./cert.key - pem: ./cert.pem - limitation: - max_body_size: 536870912 - max_qps: -1 - ports: - - :8000 - secured_ports: [ ] -paths: - configs: ./config -performance: - request_logging: true - network_timeout: 3000 - prefork: false -security: - sideload_trusted_proxies: - - localhost - credential: e81f43f32d934271af6322e5376f5f59 diff --git a/src/config/loader.rs b/src/config/loader.rs new file mode 100644 index 0000000..434a96e --- /dev/null +++ b/src/config/loader.rs @@ -0,0 +1,9 @@ +use config::Config; + +pub fn load_settings() -> Config { + Config::builder() + .add_source(config::File::with_name("Settings")) + .add_source(config::Environment::with_prefix("ROADSIGN")) + .build() + .unwrap() +} diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..186dcf6 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1 @@ +pub mod loader; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..ff651b5 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,39 @@ +mod config; +mod proxies; +mod sideload; + +use poem::{listener::TcpListener, Route, Server}; +use poem_openapi::OpenApiService; + +#[tokio::main] +async fn main() -> Result<(), std::io::Error> { + // Load settings + let settings = config::loader::load_settings(); + + println!( + "Will listen at {:?}", + settings.get_array("listen.proxies").unwrap() + ); + + if std::env::var_os("RUST_LOG").is_none() { + std::env::set_var("RUST_LOG", "poem=debug"); + } + tracing_subscriber::fmt::init(); + + // Proxies + + // Sideload + let sideload = OpenApiService::new(sideload::SideloadApi, "Sideload API", "1.0") + .server("http://localhost:3000/cgi"); + + let sideload_server = Server::new(TcpListener::bind( + settings + .get_string("listen.sideload") + .unwrap_or("0.0.0.0:81".to_string()), + )) + .run(Route::new().nest("/cgi", sideload)); + + tokio::try_join!(sideload_server)?; + + Ok(()) +} diff --git a/src/proxies/config.rs b/src/proxies/config.rs new file mode 100644 index 0000000..435977c --- /dev/null +++ b/src/proxies/config.rs @@ -0,0 +1,22 @@ +use std::{collections::HashMap, time::Duration}; + +#[derive(Debug, Clone)] +pub struct Region { + id: String, + locations: Vec, +} + +#[derive(Debug, Clone)] +pub struct Location { + hosts: Vec, + paths: Vec, + headers: Vec>, + query_strings: Vec>, + destination: Vec, +} + +#[derive(Debug, Clone)] +pub struct Destination { + uri: Vec, + timeout: Duration, +} diff --git a/src/proxies/mod.rs b/src/proxies/mod.rs new file mode 100644 index 0000000..ef68c36 --- /dev/null +++ b/src/proxies/mod.rs @@ -0,0 +1 @@ +pub mod config; diff --git a/src/sideload/mod.rs b/src/sideload/mod.rs new file mode 100644 index 0000000..95a4645 --- /dev/null +++ b/src/sideload/mod.rs @@ -0,0 +1,3 @@ +pub mod overview; + +pub struct SideloadApi; diff --git a/src/sideload/overview.rs b/src/sideload/overview.rs new file mode 100644 index 0000000..1f0b0f1 --- /dev/null +++ b/src/sideload/overview.rs @@ -0,0 +1,14 @@ +use poem_openapi::{param::Query, payload::PlainText, OpenApi}; + +use super::SideloadApi; + +#[OpenApi] +impl SideloadApi { + #[oai(path = "/hello", method = "get")] + async fn index(&self, name: Query>) -> PlainText { + match name.0 { + Some(name) => PlainText(format!("hello, {name}!")), + None => PlainText("hello!".to_string()), + } + } +} diff --git a/test/README.md b/test/README.md deleted file mode 100644 index ba76b2c..0000000 --- a/test/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# Benchmark - -This result is design for test the performance of the roadsign. -Welcome to contribute more tests of others reverse proxy software! - -## Platform - -All tests are running on my workstation: - -```text - ..' littlesheep@LittleSheepdeMacBook-Pro - ,xNMM. ------------------------------------ - .OMMMMo OS: macOS Sonoma 14.1 23B2073 arm64 - lMM" Host: MacBook Pro (14-inch, Nov 2023, Three Thunderbolt 4 ports) - .;loddo:. .olloddol;. Kernel: 23.1.0 - cKMMMMMMMMMMNWMMMMMMMMMM0: Uptime: 2 days, 1 hour, 16 mins - .KMMMMMMMMMMMMMMMMMMMMMMMWd. Packages: 63 (brew), 4 (brew-cask) - XMMMMMMMMMMMMMMMMMMMMMMMX. Shell: zsh 5.9 -;MMMMMMMMMMMMMMMMMMMMMMMM: Display (Color LCD): 3024x1964 @ 120Hz (as 1512x982) [Built-in] -:MMMMMMMMMMMMMMMMMMMMMMMM: DE: Aqua -.MMMMMMMMMMMMMMMMMMMMMMMMX. WM: Quartz Compositor - kMMMMMMMMMMMMMMMMMMMMMMMMWd. WM Theme: Multicolor (Dark) - 'XMMMMMMMMMMMMMMMMMMMMMMMMMMk Font: .AppleSystemUIFont [System], Helvetica [User] - 'XMMMMMMMMMMMMMMMMMMMMMMMMK. Cursor: Fill - Black, Outline - White (32px) - kMMMMMMMMMMMMMMMMMMMMMMd Terminal: iTerm 3.4.22 - ;KMMMMMMMWXXWMMMMMMMk. Terminal Font: MesloLGMNFM-Regular (12pt) - "cooc*" "*coo'" CPU: Apple M3 Max (14) @ 4.06 GHz - GPU: Apple M3 Max (30) [Integrated] - Memory: 18.45 GiB / 36.00 GiB (51%) - Swap: Disabled - Disk (/): 72.52 GiB / 926.35 GiB (8%) - apfs [Read-only] - Local IP (en0): 192.168.50.0/24 * - Battery: 100% [AC connected] - Power Adapter: 96W USB-C Power Adapter - Locale: zh_CN.UTF-8 -``` - -## Results - -The tests are run in the order `nginx -> roadsign without prefork -> roadsign with prefork`. There is no reason why nginx performance should be affected by hardware temperature. - -### Nginx - -```shell -go-wrk -c 60 -d 120 http://localhost:8001 -# => Running 120s test @ http://localhost:8001 -# => 60 goroutine(s) running concurrently -# => 515749 requests in 1m59.953302003s, 245.92MB read -# => Requests/sec: 4299.58 -# => Transfer/sec: 2.05MB -# => Avg Req Time: 13.954846ms -# => Fastest Request: 0s -# => Slowest Request: 410.6972ms -# => Number of Errors: 0 -``` - -### RoadSign - -```shell -go-wrk -c 60 -d 120 http://localhost:8000 -# => Running 120s test @ http://localhost:8000 -# => 60 goroutine(s) running concurrently -# => 8905230 requests in 1m56.215762709s, 3.52GB read -# => Requests/sec: 76626.70 -# => Transfer/sec: 30.98MB -# => Avg Req Time: 783.016µs -# => Fastest Request: 28.542µs -# => Slowest Request: 46.773083ms -# => Number of Errors: 0 -``` - -### RoadSign w/ Prefork - -```shell -go-wrk -c 60 -d 120 http://localhost:8000 -# => Running 120s test @ http://localhost:8000 -# => 60 goroutine(s) running concurrently -# => 4784308 requests in 1m59.100307178s, 1.89GB read -# => Requests/sec: 40170.41 -# => Transfer/sec: 16.24MB -# => Avg Req Time: 1.493636ms -# => Fastest Request: 34.291µs -# => Slowest Request: 8.727666ms -# => Number of Errors: 0 -``` diff --git a/test/data/.gitignore b/test/data/.gitignore deleted file mode 100644 index 29e9b8d..0000000 --- a/test/data/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/ssr -/spa -/congress \ No newline at end of file diff --git a/test/data/index.html b/test/data/index.html deleted file mode 100644 index 0efa986..0000000 --- a/test/data/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - Hello, World! - - -

Hello, there!

-

Here's the roadsign vs. nginx benchmarking test data!

- - \ No newline at end of file diff --git a/test/nginx/nginx.conf b/test/nginx/nginx.conf deleted file mode 100644 index a325f4a..0000000 --- a/test/nginx/nginx.conf +++ /dev/null @@ -1,117 +0,0 @@ - -#user nobody; -worker_processes 1; - -#error_log logs/error.log; -#error_log logs/error.log notice; -#error_log logs/error.log info; - -#pid logs/nginx.pid; - - -events { - worker_connections 1024; -} - - -http { - include mime.types; - default_type application/octet-stream; - - #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - # '$status $body_bytes_sent "$http_referer" ' - # '"$http_user_agent" "$http_x_forwarded_for"'; - - #access_log logs/access.log main; - - sendfile on; - #tcp_nopush on; - - #keepalive_timeout 0; - keepalive_timeout 65; - - #gzip on; - - server { - listen 8001; - server_name localhost; - - #charset koi8-r; - - #access_log logs/host.access.log main; - - location / { - root ../data; - index index.html index.htm; - } - - #error_page 404 /404.html; - - # redirect server error pages to the static page /50x.html - # - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root html; - } - - # proxy the PHP scripts to Apache listening on 127.0.0.1:80 - # - #location ~ \.php$ { - # proxy_pass http://127.0.0.1; - #} - - # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 - # - #location ~ \.php$ { - # root html; - # fastcgi_pass 127.0.0.1:9000; - # fastcgi_index index.php; - # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; - # include fastcgi_params; - #} - - # deny access to .htaccess files, if Apache's document root - # concurs with nginx's one - # - #location ~ /\.ht { - # deny all; - #} - } - - - # another virtual host using mix of IP-, name-, and port-based configuration - # - #server { - # listen 8000; - # listen somename:8080; - # server_name somename alias another.alias; - - # location / { - # root html; - # index index.html index.htm; - # } - #} - - - # HTTPS server - # - #server { - # listen 443 ssl; - # server_name localhost; - - # ssl_certificate cert.pem; - # ssl_certificate_key cert.key; - - # ssl_session_cache shared:SSL:1m; - # ssl_session_timeout 5m; - - # ssl_ciphers HIGH:!aNULL:!MD5; - # ssl_prefer_server_ciphers on; - - # location / { - # root html; - # index index.html index.htm; - # } - #} - -} diff --git a/test/roadsign-spa/config/example.yaml b/test/roadsign-spa/config/example.yaml deleted file mode 100644 index f94fd66..0000000 --- a/test/roadsign-spa/config/example.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: Example Site -rules: - - host: ["localhost:8000"] - path: ["/"] -upstreams: - - id: example - name: Benchmarking Data - uri: files://../data/spa?fallback=index.html diff --git a/test/roadsign-spa/settings.yml b/test/roadsign-spa/settings.yml deleted file mode 100644 index 2546065..0000000 --- a/test/roadsign-spa/settings.yml +++ /dev/null @@ -1,26 +0,0 @@ -debug: - print_routes: false -hypertext: - sideload_ports: [":81"] - sideload_secured_ports: [] - certificate: - sideload_key: ./cert.key - sideload_pem: ./cert.pem - key: ./cert.key - pem: ./cert.pem - limitation: - max_body_size: 536870912 - max_qps: -1 - ports: - - :8000 - secured_ports: [] -paths: - configs: ./config -performance: - request_logging: false - network_timeout: 3000 - prefork: false -security: - sideload_trusted_proxies: - - localhost - credential: e81f43f32d934271af6322e5376f5f59 diff --git a/test/roadsign-ssr/config/example.yaml b/test/roadsign-ssr/config/example.yaml deleted file mode 100644 index b061b3f..0000000 --- a/test/roadsign-ssr/config/example.yaml +++ /dev/null @@ -1,12 +0,0 @@ -name: Example Site -rules: - - host: ["localhost:8000"] - path: ["/"] -upstreams: - - id: example - name: Benchmarking Data - uri: http://localhost:3000 -processes: - - id: nuxt-ssr - workdir: ../data/ssr - command: ["node", ".output/server/index.mjs"] diff --git a/test/roadsign-ssr/settings.yml b/test/roadsign-ssr/settings.yml deleted file mode 100644 index 2546065..0000000 --- a/test/roadsign-ssr/settings.yml +++ /dev/null @@ -1,26 +0,0 @@ -debug: - print_routes: false -hypertext: - sideload_ports: [":81"] - sideload_secured_ports: [] - certificate: - sideload_key: ./cert.key - sideload_pem: ./cert.pem - key: ./cert.key - pem: ./cert.pem - limitation: - max_body_size: 536870912 - max_qps: -1 - ports: - - :8000 - secured_ports: [] -paths: - configs: ./config -performance: - request_logging: false - network_timeout: 3000 - prefork: false -security: - sideload_trusted_proxies: - - localhost - credential: e81f43f32d934271af6322e5376f5f59 diff --git a/test/roadsign-with-prefork/config/example.yaml b/test/roadsign-with-prefork/config/example.yaml deleted file mode 100644 index 2a46d7d..0000000 --- a/test/roadsign-with-prefork/config/example.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: Example Site -rules: - - host: ["localhost:8000"] - path: ["/"] -upstreams: - - id: example - name: Benchmarking Data - uri: files://../data diff --git a/test/roadsign-with-prefork/settings.yml b/test/roadsign-with-prefork/settings.yml deleted file mode 100644 index 2dec20e..0000000 --- a/test/roadsign-with-prefork/settings.yml +++ /dev/null @@ -1,26 +0,0 @@ -debug: - print_routes: false -hypertext: - sideload_ports: [":81"] - sideload_secured_ports: [] - certificate: - sideload_key: ./cert.key - sideload_pem: ./cert.pem - key: ./cert.key - pem: ./cert.pem - limitation: - max_body_size: 536870912 - max_qps: -1 - ports: - - :8000 - secured_ports: [] -paths: - configs: ./config -performance: - request_logging: false - network_timeout: 3000 - prefork: true -security: - sideload_trusted_proxies: - - localhost - credential: e81f43f32d934271af6322e5376f5f59 diff --git a/test/roadsign/config/example.yaml b/test/roadsign/config/example.yaml deleted file mode 100644 index 2a46d7d..0000000 --- a/test/roadsign/config/example.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: Example Site -rules: - - host: ["localhost:8000"] - path: ["/"] -upstreams: - - id: example - name: Benchmarking Data - uri: files://../data diff --git a/test/roadsign/settings.yml b/test/roadsign/settings.yml deleted file mode 100644 index 2546065..0000000 --- a/test/roadsign/settings.yml +++ /dev/null @@ -1,26 +0,0 @@ -debug: - print_routes: false -hypertext: - sideload_ports: [":81"] - sideload_secured_ports: [] - certificate: - sideload_key: ./cert.key - sideload_pem: ./cert.pem - key: ./cert.key - pem: ./cert.pem - limitation: - max_body_size: 536870912 - max_qps: -1 - ports: - - :8000 - secured_ports: [] -paths: - configs: ./config -performance: - request_logging: false - network_timeout: 3000 - prefork: false -security: - sideload_trusted_proxies: - - localhost - credential: e81f43f32d934271af6322e5376f5f59 -- 2.45.2 From 5de1d139077fc8eeca06f0645b8c81f6d1e59d3f Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 13 Jan 2024 14:46:26 +0800 Subject: [PATCH 2/9] :sparkles: Config loader --- Cargo.lock | 55 ++++++++++++++++++++++++++++++++++++------- Cargo.toml | 3 +++ Settings.toml | 2 ++ regions/index.toml | 9 +++++++ src/config/mod.rs | 11 +++++++++ src/main.rs | 38 ++++++++++++++++++++++-------- src/proxies/config.rs | 22 ++++++++++------- src/proxies/loader.rs | 53 +++++++++++++++++++++++++++++++++++++++++ src/proxies/mod.rs | 16 +++++++++++++ 9 files changed, 182 insertions(+), 27 deletions(-) create mode 100644 regions/index.toml create mode 100644 src/proxies/loader.rs diff --git a/Cargo.lock b/Cargo.lock index da66b14..5580b7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,7 +216,7 @@ dependencies = [ "rust-ini", "serde", "serde_json", - "toml", + "toml 0.5.11", "yaml-rust", ] @@ -1128,12 +1128,11 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-crate" -version = "2.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" dependencies = [ - "toml_datetime", - "toml_edit", + "toml_edit 0.20.2", ] [[package]] @@ -1246,11 +1245,14 @@ name = "roadsign" version = "0.1.0" dependencies = [ "config", + "lazy_static", "poem", "poem-openapi", "serde", "serde_json", "tokio", + "toml 0.8.8", + "tracing", "tracing-subscriber", ] @@ -1352,6 +1354,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1629,10 +1640,25 @@ dependencies = [ ] [[package]] -name = "toml_datetime" -version = "0.6.3" +name = "toml" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.21.0", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -1645,6 +1671,19 @@ dependencies = [ "winnow", ] +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tracing" version = "0.1.40" diff --git a/Cargo.toml b/Cargo.toml index 6581414..510e222 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,9 +7,12 @@ edition = "2021" [dependencies] config = { version = "0.13.4", features = ["toml"] } +lazy_static = "1.4.0" poem = { version = "2.0.0", features = ["tokio-metrics"] } poem-openapi = "4.0.0" serde = "1.0.195" serde_json = "1.0.111" tokio = { version = "1.35.1", features = ["rt-multi-thread", "macros", "time"] } +toml = "0.8.8" +tracing = "0.1.40" tracing-subscriber = "0.3.18" diff --git a/Settings.toml b/Settings.toml index fc76f4a..d1fc034 100644 --- a/Settings.toml +++ b/Settings.toml @@ -1,3 +1,5 @@ +regions = "./regions" + [listen] proxies = "0.0.0.0:80" proxies_tls = "0.0.0.0:443" diff --git a/regions/index.toml b/regions/index.toml new file mode 100644 index 0000000..b234b05 --- /dev/null +++ b/regions/index.toml @@ -0,0 +1,9 @@ +id = "index" + +[[locations]] +id = "root" +hosts = ["localhost"] +paths = ["/"] +[[locations.destinations]] +id = "example" +uri = "https://example.com" diff --git a/src/config/mod.rs b/src/config/mod.rs index 186dcf6..c7507d4 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1 +1,12 @@ +use std::sync::RwLock; + +use config::Config; +use lazy_static::lazy_static; + +use crate::config::loader::load_settings; + pub mod loader; + +lazy_static! { + pub static ref C: RwLock = RwLock::new(load_settings()); +} diff --git a/src/main.rs b/src/main.rs index ff651b5..dfdea62 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,21 +4,35 @@ mod sideload; use poem::{listener::TcpListener, Route, Server}; use poem_openapi::OpenApiService; +use tracing::{error, info, Level}; #[tokio::main] async fn main() -> Result<(), std::io::Error> { - // Load settings - let settings = config::loader::load_settings(); - - println!( - "Will listen at {:?}", - settings.get_array("listen.proxies").unwrap() - ); - + // Setting up logging if std::env::var_os("RUST_LOG").is_none() { std::env::set_var("RUST_LOG", "poem=debug"); } - tracing_subscriber::fmt::init(); + tracing_subscriber::fmt() + .with_max_level(Level::DEBUG) + .init(); + + // Prepare all the stuff + let mut instance = proxies::Instance::new(); + + info!("Loading proxy regions..."); + match proxies::loader::scan_regions( + config::C + .read() + .unwrap() + .get_string("regions") + .unwrap_or("./regions".to_string()), + ) { + Err(_) => error!("Loading proxy regions... failed"), + Ok((regions, count)) => { + instance.regions = regions; + info!(count, "Loading proxy regions... done") + } + }; // Proxies @@ -27,7 +41,9 @@ async fn main() -> Result<(), std::io::Error> { .server("http://localhost:3000/cgi"); let sideload_server = Server::new(TcpListener::bind( - settings + config::C + .read() + .unwrap() .get_string("listen.sideload") .unwrap_or("0.0.0.0:81".to_string()), )) @@ -37,3 +53,5 @@ async fn main() -> Result<(), std::io::Error> { Ok(()) } + + diff --git a/src/proxies/config.rs b/src/proxies/config.rs index 435977c..a7ab6a1 100644 --- a/src/proxies/config.rs +++ b/src/proxies/config.rs @@ -1,22 +1,26 @@ -use std::{collections::HashMap, time::Duration}; +use std::collections::HashMap; -#[derive(Debug, Clone)] +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Region { id: String, locations: Vec, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Location { + id: String, hosts: Vec, paths: Vec, - headers: Vec>, - query_strings: Vec>, - destination: Vec, + headers: Option>>, + query_strings: Option>>, + destinations: Vec, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Destination { - uri: Vec, - timeout: Duration, + id: String, + uri: String, + timeout: Option, } diff --git a/src/proxies/loader.rs b/src/proxies/loader.rs new file mode 100644 index 0000000..0928e00 --- /dev/null +++ b/src/proxies/loader.rs @@ -0,0 +1,53 @@ +use std::ffi::OsStr; +use std::fs::{self, DirEntry}; +use std::io; + +use tracing::warn; + +use crate::proxies::config; + +pub fn scan_regions(basepath: String) -> io::Result<(Vec, u32)> { + let mut count: u32 = 0; + let mut result = vec![]; + for entry in fs::read_dir(basepath)? { + if let Ok(val) = load_region(entry.unwrap()) { + result.push(val); + count += 1; + }; + } + + Ok((result, count)) +} + +pub fn load_region(file: DirEntry) -> Result { + if file.path().extension().and_then(OsStr::to_str).unwrap() != "toml" { + return Err("File entry wasn't toml file".to_string()); + } + + let fp = file.path(); + let content = match fs::read_to_string(fp.clone()) { + Ok(val) => val, + Err(err) => { + warn!( + err = format!("{:?}", err), + filepath = fp.clone().to_str(), + "An error occurred when loading region, skipped." + ); + return Err("Failed to load file".to_string()); + } + }; + + let data: config::Region = match toml::from_str(&content) { + Ok(val) => val, + Err(err) => { + warn!( + err = format!("{:?}", err), + filepath = fp.clone().to_str(), + "An error occurred when parsing region, skipped." + ); + return Err("Failed to parse file".to_string()); + } + }; + + Ok(data) +} diff --git a/src/proxies/mod.rs b/src/proxies/mod.rs index ef68c36..2f5af48 100644 --- a/src/proxies/mod.rs +++ b/src/proxies/mod.rs @@ -1 +1,17 @@ +use serde::{Deserialize, Serialize}; + +use self::config::Region; + pub mod config; +pub mod loader; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Instance { + pub regions: Vec, +} + +impl Instance { + pub fn new() -> Instance { + Instance { regions: vec![] } + } +} -- 2.45.2 From c991d0b54a328fbd1f983fa7de0b5331fb3c2b43 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 14 Jan 2024 02:01:56 +0800 Subject: [PATCH 3/9] :sparkles: Route and forward for http(s) --- Cargo.lock | 443 ++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 14 +- regions/index.toml | 4 +- src/main.rs | 23 ++- src/proxies/config.rs | 69 +++++-- src/proxies/mod.rs | 46 ++++- src/proxies/route.rs | 155 +++++++++++++++ 7 files changed, 732 insertions(+), 22 deletions(-) create mode 100644 src/proxies/route.rs diff --git a/Cargo.lock b/Cargo.lock index 5580b7b..aa87e5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -158,6 +158,12 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.5.0" @@ -244,6 +250,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -314,6 +330,12 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + [[package]] name = "deranged" version = "0.3.11" @@ -390,6 +412,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -634,6 +671,23 @@ dependencies = [ "itoa", "pin-project-lite", "tokio", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", ] [[package]] @@ -651,6 +705,8 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", + "tower", + "tower-service", "tracing", ] @@ -683,6 +739,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "2.1.0" @@ -702,6 +768,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "itoa" version = "1.0.10" @@ -825,6 +897,24 @@ dependencies = [ "version_check", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nix" version = "0.27.1" @@ -896,6 +986,50 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "ordered-multimap" version = "0.4.3" @@ -992,6 +1126,26 @@ dependencies = [ "sha2", ] +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -1004,6 +1158,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" + [[package]] name = "poem" version = "2.0.0" @@ -1011,6 +1171,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38a712ff53e257d60d3d22936c51cafa606552129d55539c8a400de44eff676d" dependencies = [ "async-trait", + "base64 0.21.7", "bytes", "chrono", "cookie", @@ -1042,6 +1203,7 @@ dependencies = [ "tokio", "tokio-metrics", "tokio-stream", + "tokio-tungstenite", "tokio-util", "tracing", "wildmatch", @@ -1231,6 +1393,45 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "reqwest" +version = "0.11.23" +source = "git+https://github.com/seanmonstar/reqwest.git?branch=hyper-v1#7f4bfcbaaa8d332464709262c645893702b1ae84" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "rfc7239" version = "0.1.0" @@ -1245,15 +1446,23 @@ name = "roadsign" version = "0.1.0" dependencies = [ "config", + "futures-util", + "http", + "hyper-util", "lazy_static", "poem", "poem-openapi", + "rand", + "regex", + "reqwest", "serde", "serde_json", "tokio", + "tokio-tungstenite", "toml 0.8.8", "tracing", "tracing-subscriber", + "wildmatch", ] [[package]] @@ -1311,12 +1520,44 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.21" @@ -1419,6 +1660,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.9" @@ -1493,6 +1743,27 @@ dependencies = [ "futures-core", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempfile" version = "3.9.0" @@ -1565,6 +1836,21 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.35.1" @@ -1576,7 +1862,9 @@ dependencies = [ "libc", "mio", "num_cpus", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.48.0", @@ -1605,6 +1893,16 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.14" @@ -1616,6 +1914,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.7.10" @@ -1684,12 +1994,41 @@ dependencies = [ "winnow", ] +[[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", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1741,6 +2080,31 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "typenum" version = "1.17.0" @@ -1762,12 +2126,27 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + [[package]] name = "universal-hash" version = "0.5.1" @@ -1784,18 +2163,50 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[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.0+wasi-snapshot-preview1" @@ -1827,6 +2238,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.90" @@ -1856,6 +2279,16 @@ version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +[[package]] +name = "web-sys" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "wildmatch" version = "2.3.0" @@ -2034,6 +2467,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/Cargo.toml b/Cargo.toml index 510e222..38196f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,12 +7,20 @@ edition = "2021" [dependencies] config = { version = "0.13.4", features = ["toml"] } +futures-util = "0.3.30" +http = "1.0.0" +hyper-util = { version = "0.1.2", features = ["full"] } lazy_static = "1.4.0" -poem = { version = "2.0.0", features = ["tokio-metrics"] } -poem-openapi = "4.0.0" +poem = { version = "2.0.0", features = ["tokio-metrics", "websocket"] } +poem-openapi = { version = "4.0.0", features = ["swagger-ui"] } +rand = "0.8.5" +regex = "1.10.2" +reqwest = { git = "https://github.com/seanmonstar/reqwest.git", branch = "hyper-v1", version = "0.11.23" } serde = "1.0.195" serde_json = "1.0.111" -tokio = { version = "1.35.1", features = ["rt-multi-thread", "macros", "time"] } +tokio = { version = "1.35.1", features = ["rt-multi-thread", "macros", "time", "full"] } +tokio-tungstenite = "0.21.0" toml = "0.8.8" tracing = "0.1.40" tracing-subscriber = "0.3.18" +wildmatch = "2.3.0" diff --git a/regions/index.toml b/regions/index.toml index b234b05..dbe1fe7 100644 --- a/regions/index.toml +++ b/regions/index.toml @@ -5,5 +5,5 @@ id = "root" hosts = ["localhost"] paths = ["/"] [[locations.destinations]] -id = "example" -uri = "https://example.com" +id = "echo" +uri = "https://postman-echo.com/get" diff --git a/src/main.rs b/src/main.rs index dfdea62..f7a69e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,10 +2,12 @@ mod config; mod proxies; mod sideload; -use poem::{listener::TcpListener, Route, Server}; +use poem::{listener::TcpListener, Endpoint, EndpointExt, Route, Server}; use poem_openapi::OpenApiService; use tracing::{error, info, Level}; +use crate::proxies::route; + #[tokio::main] async fn main() -> Result<(), std::io::Error> { // Setting up logging @@ -35,10 +37,19 @@ async fn main() -> Result<(), std::io::Error> { }; // Proxies + let proxies_server = Server::new(TcpListener::bind( + config::C + .read() + .unwrap() + .get_string("listen.proxies") + .unwrap_or("0.0.0.0:80".to_string()), + )) + .run(route::handle.data(instance)); // Sideload let sideload = OpenApiService::new(sideload::SideloadApi, "Sideload API", "1.0") .server("http://localhost:3000/cgi"); + let sideload_ui = sideload.swagger_ui(); let sideload_server = Server::new(TcpListener::bind( config::C @@ -47,11 +58,13 @@ async fn main() -> Result<(), std::io::Error> { .get_string("listen.sideload") .unwrap_or("0.0.0.0:81".to_string()), )) - .run(Route::new().nest("/cgi", sideload)); + .run( + Route::new() + .nest("/cgi", sideload) + .nest("/swagger", sideload_ui), + ); - tokio::try_join!(sideload_server)?; + tokio::try_join!(proxies_server, sideload_server)?; Ok(()) } - - diff --git a/src/proxies/config.rs b/src/proxies/config.rs index a7ab6a1..2bfa414 100644 --- a/src/proxies/config.rs +++ b/src/proxies/config.rs @@ -4,23 +4,70 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Region { - id: String, - locations: Vec, + pub id: String, + pub locations: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Location { - id: String, - hosts: Vec, - paths: Vec, - headers: Option>>, - query_strings: Option>>, - destinations: Vec, + pub id: String, + pub hosts: Vec, + pub paths: Vec, + pub headers: Option>, + pub queries: Option>, + pub destinations: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Destination { - id: String, - uri: String, - timeout: Option, + pub id: String, + pub uri: String, + pub timeout: Option, + pub weight: Option, +} + +pub enum DestinationType { + Hypertext, + StaticFiles, + Unknown, +} + +impl Destination { + pub fn get_type(&self) -> DestinationType { + match self.get_protocol() { + "http" | "https" => DestinationType::Hypertext, + "file" | "files" => DestinationType::StaticFiles, + _ => DestinationType::Unknown, + } + } + + pub fn get_protocol(&self) -> &str { + self.uri.as_str().splitn(2, "://").collect::>()[0] + } + + pub fn get_queries(&self) -> &str { + self.uri.as_str().splitn(2, "?").collect::>()[1] + } + + pub fn get_host(&self) -> &str { + (self.uri.as_str().splitn(2, "://").collect::>()[1]) + .splitn(2, "?") + .collect::>()[0] + } + + pub fn get_hypertext_uri(&self) -> Result { + match self.get_protocol() { + "http" => Ok("http://".to_string() + self.get_host()), + "https" => Ok("https://".to_string() + self.get_host()), + _ => Err(()), + } + } + + pub fn get_websocket_uri(&self) -> Result { + let url = self.uri.as_str().splitn(2, "://").collect::>()[1]; + match self.get_protocol() { + "http" | "https" => Ok(url.replace("http", "ws")), + _ => Err(()), + } + } } diff --git a/src/proxies/mod.rs b/src/proxies/mod.rs index 2f5af48..3ea38bb 100644 --- a/src/proxies/mod.rs +++ b/src/proxies/mod.rs @@ -1,9 +1,13 @@ +use poem::http::{HeaderMap, Uri}; +use regex::Regex; use serde::{Deserialize, Serialize}; +use wildmatch::WildMatch; -use self::config::Region; +use self::config::{Location, Region}; pub mod config; pub mod loader; +pub mod route; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Instance { @@ -14,4 +18,44 @@ impl Instance { pub fn new() -> Instance { Instance { regions: vec![] } } + + pub fn filter(&self, uri: &Uri, headers: &HeaderMap) -> Option<&Location> { + self.regions.iter().find_map(|region| { + region.locations.iter().find(|location| { + let mut hosts = location.hosts.iter(); + if !hosts.any(|item| { + WildMatch::new(item.as_str()).matches(uri.host().unwrap_or("localhost")) + }) { + return false; + } + + let mut paths = location.paths.iter(); + if !paths.any(|item| { + uri.path().starts_with(item) + || Regex::new(item.as_str()).unwrap().is_match(uri.path()) + }) { + return false; + } + + if let Some(val) = location.headers.clone() { + match !val.keys().all(|item| { + headers.get(item).unwrap() + == location.headers.clone().unwrap().get(item).unwrap() + }) { + true => return false, + false => (), + } + }; + + if let Some(val) = location.queries.clone() { + let queries: Vec<&str> = uri.query().unwrap_or("").split('&').collect(); + if !val.iter().all(|item| queries.contains(&item.as_str())) { + return false; + } + } + + true + }) + }) + } } diff --git a/src/proxies/route.rs b/src/proxies/route.rs new file mode 100644 index 0000000..9432fc0 --- /dev/null +++ b/src/proxies/route.rs @@ -0,0 +1,155 @@ +use futures_util::{SinkExt, StreamExt}; +use poem::{ + handler, + http::{HeaderMap, StatusCode, Uri}, + web::{websocket::WebSocket, Data}, + Body, Error, FromRequest, IntoResponse, Request, Response, Result, +}; +use rand::seq::SliceRandom; +use reqwest::Method; + +use lazy_static::lazy_static; +use std::sync::Arc; +use tokio::sync::RwLock; +use tokio_tungstenite::connect_async; + +use crate::proxies::config::{Destination, DestinationType}; + +lazy_static! { + pub static ref CLIENT: reqwest::Client = reqwest::Client::new(); +} + +#[handler] +pub async fn handle( + app: Data<&super::Instance>, + req: &Request, + uri: &Uri, + headers: &HeaderMap, + method: Method, + body: Body, +) -> Result { + let location = match app.filter(uri, headers) { + Some(val) => val, + None => { + return Err(Error::from_string( + "There are no region be able to respone this request.", + StatusCode::NOT_FOUND, + )) + } + }; + + let destination = location + .destinations + .choose_weighted(&mut rand::thread_rng(), |item| item.weight.unwrap_or(1)) + .unwrap(); + + async fn forward( + end: &Destination, + req: &Request, + ori: &Uri, + headers: &HeaderMap, + method: Method, + body: Body, + ) -> Result { + // Handle websocket + if let Ok(ws) = WebSocket::from_request_without_body(req).await { + // Get uri + let Ok(uri) = end.get_websocket_uri() else { + return Err(Error::from_string( + "Proxy endpoint not configured to support websockets!", + StatusCode::NOT_IMPLEMENTED, + )); + }; + + // Build request + let mut ws_req = http::Request::builder().uri(&uri); + for (key, value) in headers.iter() { + ws_req = ws_req.header(key, value); + } + + // Start the websocket connection + return Ok(ws + .on_upgrade(move |socket| async move { + let (mut clientsink, mut clientstream) = socket.split(); + + // Start connection to server + let (serversocket, _) = connect_async(ws_req.body(()).unwrap()).await.unwrap(); + let (mut serversink, mut serverstream) = serversocket.split(); + + let client_live = Arc::new(RwLock::new(true)); + let server_live = client_live.clone(); + + tokio::spawn(async move { + while let Some(Ok(msg)) = clientstream.next().await { + match serversink.send(msg.into()).await { + Err(_) => break, + _ => {} + }; + if !*client_live.read().await { + break; + }; + } + + *client_live.write().await = false; + }); + + // Relay server messages to the client + tokio::spawn(async move { + while let Some(Ok(msg)) = serverstream.next().await { + match clientsink.send(msg.into()).await { + Err(_) => break, + _ => {} + }; + if !*server_live.read().await { + break; + }; + } + + *server_live.write().await = false; + }); + }) + .into_response()); + } + + // Handle normal web request + match end.get_type() { + DestinationType::Hypertext => { + let Ok(uri) = end.get_hypertext_uri() else { + return Err(Error::from_string( + "Proxy endpoint not configured to support web requests!", + StatusCode::NOT_IMPLEMENTED, + )); + }; + + let res = CLIENT + .request(method, uri + ori.path() + ori.query().unwrap_or("")) + .headers(headers.clone()) + .body(body.into_bytes().await.unwrap()) + .send() + .await; + + match res { + Ok(result) => { + let mut res = Response::default(); + res.extensions().clone_from(&result.extensions()); + result.headers().iter().for_each(|(key, val)| { + res.headers_mut().insert(key, val.to_owned()); + }); + res.set_status(result.status()); + res.set_version(result.version()); + res.set_body(result.bytes().await.unwrap()); + Ok(res) + } + + Err(error) => Err(Error::from_string( + error.to_string(), + error.status().unwrap_or(StatusCode::BAD_GATEWAY), + )), + } + } + _ => Err(Error::from_status(StatusCode::NOT_IMPLEMENTED)), + } + } + + forward(destination, req, uri, headers, method, body).await +} -- 2.45.2 From 91ecf9d7bb28c2803d787ac7c19dccf39b647306 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 14 Jan 2024 13:14:41 +0800 Subject: [PATCH 4/9] :sparkles: Static files --- Cargo.lock | 37 +++++++ Cargo.toml | 5 +- regions/index.html | 12 +++ regions/index.toml | 4 +- regions/kokodayo.txt | 1 + src/main.rs | 2 +- src/proxies/browser.rs | 52 +++++++++ src/proxies/config.rs | 61 +++++++++-- src/proxies/mod.rs | 11 +- src/proxies/responder.rs | 221 +++++++++++++++++++++++++++++++++++++++ src/proxies/route.rs | 106 +++++-------------- 11 files changed, 418 insertions(+), 94 deletions(-) create mode 100644 regions/index.html create mode 100644 regions/kokodayo.txt create mode 100644 src/proxies/browser.rs create mode 100644 src/proxies/responder.rs diff --git a/Cargo.lock b/Cargo.lock index aa87e5f..9f7ba70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -852,6 +852,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1179,9 +1189,11 @@ dependencies = [ "headers", "http", "http-body-util", + "httpdate", "hyper", "hyper-util", "mime", + "mime_guess", "multer", "nix", "parking_lot", @@ -1306,6 +1318,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "queryst" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cbeb75ac695daf201ca2d66d9c684f873b135f28af4f2c79952478cab3b9d9" +dependencies = [ + "lazy_static", + "percent-encoding", + "regex", + "serde", + "serde_json", +] + [[package]] name = "quick-xml" version = "0.30.0" @@ -1450,8 +1475,11 @@ dependencies = [ "http", "hyper-util", "lazy_static", + "mime", + "percent-encoding", "poem", "poem-openapi", + "queryst", "rand", "regex", "reqwest", @@ -2126,6 +2154,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.14" diff --git a/Cargo.toml b/Cargo.toml index 38196f9..9c7a19c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,11 @@ futures-util = "0.3.30" http = "1.0.0" hyper-util = { version = "0.1.2", features = ["full"] } lazy_static = "1.4.0" -poem = { version = "2.0.0", features = ["tokio-metrics", "websocket"] } +mime = "0.3.17" +percent-encoding = "2.3.1" +poem = { version = "2.0.0", features = ["tokio-metrics", "websocket", "static-files"] } poem-openapi = { version = "4.0.0", features = ["swagger-ui"] } +queryst = "3.0.0" rand = "0.8.5" regex = "1.10.2" reqwest = { git = "https://github.com/seanmonstar/reqwest.git", branch = "hyper-v1", version = "0.11.23" } diff --git a/regions/index.html b/regions/index.html new file mode 100644 index 0000000..60a6c53 --- /dev/null +++ b/regions/index.html @@ -0,0 +1,12 @@ + + + + + + Hello, World! + + +

Hello, there!

+

Here's the roadsign benchmarking test data!

+ + diff --git a/regions/index.toml b/regions/index.toml index dbe1fe7..22e2901 100644 --- a/regions/index.toml +++ b/regions/index.toml @@ -5,5 +5,5 @@ id = "root" hosts = ["localhost"] paths = ["/"] [[locations.destinations]] -id = "echo" -uri = "https://postman-echo.com/get" +id = "static" +uri = "files://regions/index.html" diff --git a/regions/kokodayo.txt b/regions/kokodayo.txt new file mode 100644 index 0000000..035c7d9 --- /dev/null +++ b/regions/kokodayo.txt @@ -0,0 +1 @@ +Ko Ko Da Yo~ diff --git a/src/main.rs b/src/main.rs index f7a69e1..2906943 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ mod config; mod proxies; mod sideload; -use poem::{listener::TcpListener, Endpoint, EndpointExt, Route, Server}; +use poem::{listener::TcpListener, EndpointExt, Route, Server}; use poem_openapi::OpenApiService; use tracing::{error, info, Level}; diff --git a/src/proxies/browser.rs b/src/proxies/browser.rs new file mode 100644 index 0000000..56f0aff --- /dev/null +++ b/src/proxies/browser.rs @@ -0,0 +1,52 @@ +use std::fmt::Write; + +pub struct DirectoryTemplate<'a> { + pub path: &'a str, + pub files: Vec, +} + +impl<'a> DirectoryTemplate<'a> { + pub fn render(&self) -> String { + let mut s = format!( + r#" + + + Index of {} + + +

Index of /{}

+
    "#, + self.path, self.path + ); + + for file in &self.files { + if file.is_dir { + let _ = write!( + s, + r#"
  • {}/
  • "#, + file.url, file.filename + ); + } else { + let _ = write!( + s, + r#"
  • {}
  • "#, + file.url, file.filename + ); + } + } + + s.push_str( + r#"
+ + "#, + ); + + s + } +} + +pub struct FileRef { + pub url: String, + pub filename: String, + pub is_dir: bool, +} diff --git a/src/proxies/config.rs b/src/proxies/config.rs index 2bfa414..552f54a 100644 --- a/src/proxies/config.rs +++ b/src/proxies/config.rs @@ -1,6 +1,10 @@ use std::collections::HashMap; +use queryst::parse; use serde::{Deserialize, Serialize}; +use serde_json::json; + +use super::responder::StaticResponderConfig; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Region { @@ -15,6 +19,7 @@ pub struct Location { pub paths: Vec, pub headers: Option>, pub queries: Option>, + pub methods: Option>, pub destinations: Vec, } @@ -46,13 +51,33 @@ impl Destination { } pub fn get_queries(&self) -> &str { - self.uri.as_str().splitn(2, "?").collect::>()[1] + self.uri + .as_str() + .splitn(2, '?') + .collect::>() + .get(1) + .unwrap_or(&"") } pub fn get_host(&self) -> &str { - (self.uri.as_str().splitn(2, "://").collect::>()[1]) - .splitn(2, "?") - .collect::>()[0] + (self + .uri + .as_str() + .splitn(2, "://") + .collect::>() + .get(1) + .unwrap_or(&"")) + .splitn(2, '?') + .collect::>()[0] + } + + pub fn get_websocket_uri(&self) -> Result { + let parts = self.uri.as_str().splitn(2, "://").collect::>(); + let url = parts.get(1).unwrap_or(&""); + match self.get_protocol() { + "http" | "https" => Ok(url.replace("http", "ws")), + _ => Err(()), + } } pub fn get_hypertext_uri(&self) -> Result { @@ -63,10 +88,32 @@ impl Destination { } } - pub fn get_websocket_uri(&self) -> Result { - let url = self.uri.as_str().splitn(2, "://").collect::>()[1]; + pub fn get_static_config(&self) -> Result { match self.get_protocol() { - "http" | "https" => Ok(url.replace("http", "ws")), + "file" | "files" => { + let queries = parse(self.get_queries()).unwrap_or(json!({})); + Ok(StaticResponderConfig { + uri: self.get_host().to_string(), + utf8: queries + .get("utf8") + .and_then(|val| val.as_bool()) + .unwrap_or(false), + with_slash: queries + .get("slash") + .and_then(|val| val.as_bool()) + .unwrap_or(false), + browse: queries + .get("browse") + .and_then(|val| val.as_bool()) + .unwrap_or(false), + index: queries + .get("index") + .and_then(|val| val.as_str().map(str::to_string)), + fallback: queries + .get("fallback") + .and_then(|val| val.as_str().map(str::to_string)), + }) + } _ => Err(()), } } diff --git a/src/proxies/mod.rs b/src/proxies/mod.rs index 3ea38bb..aeb2178 100644 --- a/src/proxies/mod.rs +++ b/src/proxies/mod.rs @@ -1,3 +1,4 @@ +use http::Method; use poem::http::{HeaderMap, Uri}; use regex::Regex; use serde::{Deserialize, Serialize}; @@ -5,8 +6,10 @@ use wildmatch::WildMatch; use self::config::{Location, Region}; +pub mod browser; pub mod config; pub mod loader; +pub mod responder; pub mod route; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -19,7 +22,7 @@ impl Instance { Instance { regions: vec![] } } - pub fn filter(&self, uri: &Uri, headers: &HeaderMap) -> Option<&Location> { + pub fn filter(&self, uri: &Uri, method: Method, headers: &HeaderMap) -> Option<&Location> { self.regions.iter().find_map(|region| { region.locations.iter().find(|location| { let mut hosts = location.hosts.iter(); @@ -37,6 +40,12 @@ impl Instance { return false; } + if let Some(val) = location.methods.clone() { + if !val.iter().any(|item| *item == method.to_string()) { + return false; + } + } + if let Some(val) = location.headers.clone() { match !val.keys().all(|item| { headers.get(item).unwrap() diff --git a/src/proxies/responder.rs b/src/proxies/responder.rs new file mode 100644 index 0000000..f9fbeac --- /dev/null +++ b/src/proxies/responder.rs @@ -0,0 +1,221 @@ +use futures_util::{SinkExt, StreamExt}; +use http::{header, request::Builder, HeaderMap, Method, StatusCode, Uri}; +use lazy_static::lazy_static; +use poem::{ + web::{websocket::WebSocket, StaticFileRequest}, + Body, Error, FromRequest, IntoResponse, Request, Response, +}; +use std::{ + ffi::OsStr, + path::{Path, PathBuf}, + sync::Arc, +}; +use tokio::sync::RwLock; +use tokio_tungstenite::connect_async; + +use super::browser::{DirectoryTemplate, FileRef}; + +lazy_static! { + pub static ref CLIENT: reqwest::Client = reqwest::Client::new(); +} + +pub async fn repond_websocket(req: Builder, ws: WebSocket) -> Response { + ws.on_upgrade(move |socket| async move { + let (mut clientsink, mut clientstream) = socket.split(); + + // Start connection to server + let (serversocket, _) = connect_async(req.body(()).unwrap()).await.unwrap(); + let (mut serversink, mut serverstream) = serversocket.split(); + + let client_live = Arc::new(RwLock::new(true)); + let server_live = client_live.clone(); + + tokio::spawn(async move { + while let Some(Ok(msg)) = clientstream.next().await { + if (serversink.send(msg.into()).await).is_err() { + break; + }; + if !*client_live.read().await { + break; + }; + } + + *client_live.write().await = false; + }); + + // Relay server messages to the client + tokio::spawn(async move { + while let Some(Ok(msg)) = serverstream.next().await { + if (clientsink.send(msg.into()).await).is_err() { + break; + }; + if !*server_live.read().await { + break; + }; + } + + *server_live.write().await = false; + }); + }) + .into_response() +} + +pub async fn respond_hypertext( + uri: String, + ori: &Uri, + method: Method, + body: Body, + headers: &HeaderMap, +) -> Result { + let res = CLIENT + .request(method, uri + ori.path() + ori.query().unwrap_or("")) + .headers(headers.clone()) + .body(body.into_bytes().await.unwrap()) + .send() + .await; + + match res { + Ok(result) => { + let mut res = Response::default(); + res.extensions().clone_from(&result.extensions()); + result.headers().iter().for_each(|(key, val)| { + res.headers_mut().insert(key, val.to_owned()); + }); + res.set_status(result.status()); + res.set_version(result.version()); + res.set_body(result.bytes().await.unwrap()); + Ok(res) + } + + Err(error) => Err(Error::from_string( + error.to_string(), + error.status().unwrap_or(StatusCode::BAD_GATEWAY), + )), + } +} + +pub struct StaticResponderConfig { + pub uri: String, + pub utf8: bool, + pub with_slash: bool, + pub browse: bool, + pub index: Option, + pub fallback: Option, +} + +pub async fn respond_static( + cfg: StaticResponderConfig, + method: Method, + req: &Request, +) -> Result { + if method != Method::GET { + return Err(Error::from_string( + "This destination only support GET request.", + StatusCode::METHOD_NOT_ALLOWED, + )); + } + + let path = req + .uri() + .path() + .trim_start_matches('/') + .trim_end_matches('/'); + + let path = percent_encoding::percent_decode_str(path) + .decode_utf8() + .map_err(|_| Error::from_status(StatusCode::NOT_FOUND))?; + + let base_path = cfg.uri.parse::().unwrap(); + let mut file_path = base_path.clone(); + for p in Path::new(&*path) { + if p == OsStr::new(".") { + continue; + } else if p == OsStr::new("..") { + file_path.pop(); + } else { + file_path.push(p); + } + } + + if !file_path.starts_with(cfg.uri) { + return Err(Error::from_status(StatusCode::FORBIDDEN)); + } + + if !file_path.exists() { + if let Some(file) = cfg.fallback { + let fallback_path = base_path.join(file); + if fallback_path.is_file() { + return Ok(StaticFileRequest::from_request_without_body(req) + .await? + .create_response(&fallback_path, cfg.utf8)? + .into_response()); + } + } + return Err(Error::from_status(StatusCode::NOT_FOUND)); + } + + if file_path.is_file() { + Ok(StaticFileRequest::from_request_without_body(req) + .await? + .create_response(&file_path, cfg.utf8)? + .into_response()) + } else { + if cfg.with_slash + && !req.original_uri().path().ends_with('/') + && (cfg.index.is_some() || cfg.browse) + { + let redirect_to = format!("{}/", req.original_uri().path()); + return Ok(Response::builder() + .status(StatusCode::FOUND) + .header(header::LOCATION, redirect_to) + .finish()); + } + + if let Some(index_file) = &cfg.index { + let index_path = file_path.join(index_file); + if index_path.is_file() { + return Ok(StaticFileRequest::from_request_without_body(req) + .await? + .create_response(&index_path, cfg.utf8)? + .into_response()); + } + } + + if cfg.browse { + let read_dir = file_path + .read_dir() + .map_err(|_| Error::from_status(StatusCode::FORBIDDEN))?; + let mut template = DirectoryTemplate { + path: &path, + files: Vec::new(), + }; + + for res in read_dir { + let entry = res.map_err(|_| Error::from_status(StatusCode::FORBIDDEN))?; + + if let Some(filename) = entry.file_name().to_str() { + let mut base_url = req.original_uri().path().to_string(); + if !base_url.ends_with('/') { + base_url.push('/'); + } + let filename_url = percent_encoding::percent_encode( + filename.as_bytes(), + percent_encoding::NON_ALPHANUMERIC, + ); + template.files.push(FileRef { + url: format!("{base_url}{filename_url}"), + filename: filename.to_string(), + is_dir: entry.path().is_dir(), + }); + } + } + + let html = template.render(); + Ok(Response::builder() + .header(header::CONTENT_TYPE, mime::TEXT_HTML_UTF_8.as_ref()) + .body(Body::from_string(html))) + } else { + Err(Error::from_status(StatusCode::NOT_FOUND)) + } + } +} diff --git a/src/proxies/route.rs b/src/proxies/route.rs index 9432fc0..69c0be6 100644 --- a/src/proxies/route.rs +++ b/src/proxies/route.rs @@ -1,4 +1,3 @@ -use futures_util::{SinkExt, StreamExt}; use poem::{ handler, http::{HeaderMap, StatusCode, Uri}, @@ -8,16 +7,10 @@ use poem::{ use rand::seq::SliceRandom; use reqwest::Method; -use lazy_static::lazy_static; -use std::sync::Arc; -use tokio::sync::RwLock; -use tokio_tungstenite::connect_async; - -use crate::proxies::config::{Destination, DestinationType}; - -lazy_static! { - pub static ref CLIENT: reqwest::Client = reqwest::Client::new(); -} +use crate::proxies::{ + config::{Destination, DestinationType}, + responder, +}; #[handler] pub async fn handle( @@ -28,7 +21,7 @@ pub async fn handle( method: Method, body: Body, ) -> Result { - let location = match app.filter(uri, headers) { + let location = match app.filter(uri, method.clone(), headers) { Some(val) => val, None => { return Err(Error::from_string( @@ -50,13 +43,13 @@ pub async fn handle( headers: &HeaderMap, method: Method, body: Body, - ) -> Result { + ) -> Result { // Handle websocket if let Ok(ws) = WebSocket::from_request_without_body(req).await { // Get uri let Ok(uri) = end.get_websocket_uri() else { return Err(Error::from_string( - "Proxy endpoint not configured to support websockets!", + "This destination was not support websockets.", StatusCode::NOT_IMPLEMENTED, )); }; @@ -68,47 +61,7 @@ pub async fn handle( } // Start the websocket connection - return Ok(ws - .on_upgrade(move |socket| async move { - let (mut clientsink, mut clientstream) = socket.split(); - - // Start connection to server - let (serversocket, _) = connect_async(ws_req.body(()).unwrap()).await.unwrap(); - let (mut serversink, mut serverstream) = serversocket.split(); - - let client_live = Arc::new(RwLock::new(true)); - let server_live = client_live.clone(); - - tokio::spawn(async move { - while let Some(Ok(msg)) = clientstream.next().await { - match serversink.send(msg.into()).await { - Err(_) => break, - _ => {} - }; - if !*client_live.read().await { - break; - }; - } - - *client_live.write().await = false; - }); - - // Relay server messages to the client - tokio::spawn(async move { - while let Some(Ok(msg)) = serverstream.next().await { - match clientsink.send(msg.into()).await { - Err(_) => break, - _ => {} - }; - if !*server_live.read().await { - break; - }; - } - - *server_live.write().await = false; - }); - }) - .into_response()); + return Ok(responder::repond_websocket(ws_req, ws).await); } // Handle normal web request @@ -116,38 +69,27 @@ pub async fn handle( DestinationType::Hypertext => { let Ok(uri) = end.get_hypertext_uri() else { return Err(Error::from_string( - "Proxy endpoint not configured to support web requests!", + "This destination was not support web requests.", StatusCode::NOT_IMPLEMENTED, )); }; - let res = CLIENT - .request(method, uri + ori.path() + ori.query().unwrap_or("")) - .headers(headers.clone()) - .body(body.into_bytes().await.unwrap()) - .send() - .await; - - match res { - Ok(result) => { - let mut res = Response::default(); - res.extensions().clone_from(&result.extensions()); - result.headers().iter().for_each(|(key, val)| { - res.headers_mut().insert(key, val.to_owned()); - }); - res.set_status(result.status()); - res.set_version(result.version()); - res.set_body(result.bytes().await.unwrap()); - Ok(res) - } - - Err(error) => Err(Error::from_string( - error.to_string(), - error.status().unwrap_or(StatusCode::BAD_GATEWAY), - )), - } + responder::respond_hypertext(uri, ori, method, body, headers).await } - _ => Err(Error::from_status(StatusCode::NOT_IMPLEMENTED)), + DestinationType::StaticFiles => { + let Ok(cfg) = end.get_static_config() else { + return Err(Error::from_string( + "This destination was not support static files.", + StatusCode::NOT_IMPLEMENTED, + )); + }; + + responder::respond_static(cfg, method, req).await + } + _ => Err(Error::from_string( + "Unsupported destination protocol.", + StatusCode::NOT_IMPLEMENTED, + )), } } -- 2.45.2 From 905b70349bbd02a31779a64dcb2c6553051cd880 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 14 Jan 2024 14:52:19 +0800 Subject: [PATCH 5/9] :sparkles: Clean url with `suffix` --- regions/index.toml | 2 +- regions/subfolder/index.html | 15 +++++++++++++++ src/proxies/config.rs | 11 +++++++---- src/proxies/loader.rs | 4 +++- src/proxies/responder.rs | 19 ++++++++++++++++++- 5 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 regions/subfolder/index.html diff --git a/regions/index.toml b/regions/index.toml index 22e2901..db75202 100644 --- a/regions/index.toml +++ b/regions/index.toml @@ -6,4 +6,4 @@ hosts = ["localhost"] paths = ["/"] [[locations.destinations]] id = "static" -uri = "files://regions/index.html" +uri = "files://regions?index=index.html" diff --git a/regions/subfolder/index.html b/regions/subfolder/index.html new file mode 100644 index 0000000..9890e97 --- /dev/null +++ b/regions/subfolder/index.html @@ -0,0 +1,15 @@ + + + + + + Hello, World! + + +

Hello, there!

+

+ Here's the roadsign benchmarking test data! And you are in the subfolder + now! +

+ + diff --git a/src/proxies/config.rs b/src/proxies/config.rs index 552f54a..c6c1835 100644 --- a/src/proxies/config.rs +++ b/src/proxies/config.rs @@ -98,20 +98,23 @@ impl Destination { .get("utf8") .and_then(|val| val.as_bool()) .unwrap_or(false), - with_slash: queries - .get("slash") - .and_then(|val| val.as_bool()) - .unwrap_or(false), browse: queries .get("browse") .and_then(|val| val.as_bool()) .unwrap_or(false), + with_slash: queries + .get("slash") + .and_then(|val| val.as_bool()) + .unwrap_or(false), index: queries .get("index") .and_then(|val| val.as_str().map(str::to_string)), fallback: queries .get("fallback") .and_then(|val| val.as_str().map(str::to_string)), + suffix: queries + .get("suffix") + .and_then(|val| val.as_str().map(str::to_string)), }) } _ => Err(()), diff --git a/src/proxies/loader.rs b/src/proxies/loader.rs index 0928e00..d9bbb95 100644 --- a/src/proxies/loader.rs +++ b/src/proxies/loader.rs @@ -20,7 +20,9 @@ pub fn scan_regions(basepath: String) -> io::Result<(Vec, u32)> } pub fn load_region(file: DirEntry) -> Result { - if file.path().extension().and_then(OsStr::to_str).unwrap() != "toml" { + if file.metadata().map(|val| val.is_dir()).unwrap() + || file.path().extension().and_then(OsStr::to_str).unwrap() != "toml" + { return Err("File entry wasn't toml file".to_string()); } diff --git a/src/proxies/responder.rs b/src/proxies/responder.rs index f9fbeac..fa60aff 100644 --- a/src/proxies/responder.rs +++ b/src/proxies/responder.rs @@ -97,10 +97,11 @@ pub async fn respond_hypertext( pub struct StaticResponderConfig { pub uri: String, pub utf8: bool, - pub with_slash: bool, pub browse: bool, + pub with_slash: bool, pub index: Option, pub fallback: Option, + pub suffix: Option, } pub async fn respond_static( @@ -142,6 +143,22 @@ pub async fn respond_static( } if !file_path.exists() { + if let Some(suffix) = cfg.suffix { + let file_name = file_path + .file_name() + .and_then(OsStr::to_str) + .unwrap() + .to_string(); + file_path.pop(); + file_path.push((file_name + &suffix).as_str()); + if file_path.is_file() { + return Ok(StaticFileRequest::from_request_without_body(req) + .await? + .create_response(&file_path, cfg.utf8)? + .into_response()); + } + } + if let Some(file) = cfg.fallback { let fallback_path = base_path.join(file); if fallback_path.is_file() { -- 2.45.2 From f02977b7d7f3225b1914254644b77101b834f1bc Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 14 Jan 2024 17:55:14 +0800 Subject: [PATCH 6/9] :sparkles: Metrics --- Cargo.lock | 149 ++++++++++++++++++++++++++++++++------- Cargo.toml | 2 +- src/config/mod.rs | 3 +- src/main.rs | 33 ++++++--- src/proxies/metrics.rs | 80 +++++++++++++++++++++ src/proxies/mod.rs | 31 ++++---- src/proxies/route.rs | 41 ++++++++--- src/sideload/overview.rs | 81 +++++++++++++++++++-- 8 files changed, 357 insertions(+), 63 deletions(-) create mode 100644 src/proxies/metrics.rs diff --git a/Cargo.lock b/Cargo.lock index 9f7ba70..36b0e29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -526,6 +526,25 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "h2" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.11", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.1" @@ -537,7 +556,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 1.0.0", "indexmap", "slab", "tokio", @@ -569,7 +588,7 @@ dependencies = [ "base64 0.21.7", "bytes", "headers-core", - "http", + "http 1.0.0", "httpdate", "mime", "sha1", @@ -581,7 +600,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http", + "http 1.0.0", ] [[package]] @@ -608,6 +627,17 @@ dependencies = [ "digest", ] +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.0.0" @@ -619,6 +649,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.11", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.0" @@ -626,7 +667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http", + "http 1.0.0", ] [[package]] @@ -637,8 +678,8 @@ checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "pin-project-lite", ] @@ -654,6 +695,30 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.23", + "http 0.2.11", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.1.0" @@ -663,9 +728,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.1", + "http 1.0.0", + "http-body 1.0.0", "httparse", "httpdate", "itoa", @@ -682,7 +747,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper", + "hyper 1.1.0", "hyper-util", "native-tls", "tokio", @@ -699,9 +764,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", - "hyper", + "http 1.0.0", + "http-body 1.0.0", + "hyper 1.1.0", "pin-project-lite", "socket2", "tokio", @@ -897,7 +962,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http", + "http 1.0.0", "httparse", "log", "memchr", @@ -1187,10 +1252,10 @@ dependencies = [ "cookie", "futures-util", "headers", - "http", + "http 1.0.0", "http-body-util", "httpdate", - "hyper", + "hyper 1.1.0", "hyper-util", "mime", "mime_guess", @@ -1202,6 +1267,7 @@ dependencies = [ "poem-derive", "quick-xml", "regex", + "reqwest 0.11.23 (registry+https://github.com/rust-lang/crates.io-index)", "rfc7239", "serde", "serde_json", @@ -1265,7 +1331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5d485fb9cc4ca9a8364beedd4ea81294b1f028d459c8fd7bb352e38f87f8ffa" dependencies = [ "darling", - "http", + "http 1.0.0", "indexmap", "mime", "proc-macro-crate", @@ -1418,6 +1484,41 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "reqwest" +version = "0.11.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.23", + "http 0.2.11", + "http-body 0.4.6", + "hyper 0.14.28", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "reqwest" version = "0.11.23" @@ -1428,11 +1529,11 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.1", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", - "hyper", + "hyper 1.1.0", "hyper-tls", "hyper-util", "ipnet", @@ -1472,7 +1573,7 @@ version = "0.1.0" dependencies = [ "config", "futures-util", - "http", + "http 1.0.0", "hyper-util", "lazy_static", "mime", @@ -1482,7 +1583,7 @@ dependencies = [ "queryst", "rand", "regex", - "reqwest", + "reqwest 0.11.23 (git+https://github.com/seanmonstar/reqwest.git?branch=hyper-v1)", "serde", "serde_json", "tokio", @@ -2123,7 +2224,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 1.0.0", "httparse", "log", "rand", diff --git a/Cargo.toml b/Cargo.toml index 9c7a19c..95fae7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ hyper-util = { version = "0.1.2", features = ["full"] } lazy_static = "1.4.0" mime = "0.3.17" percent-encoding = "2.3.1" -poem = { version = "2.0.0", features = ["tokio-metrics", "websocket", "static-files"] } +poem = { version = "2.0.0", features = ["tokio-metrics", "websocket", "static-files", "reqwest"] } poem-openapi = { version = "4.0.0", features = ["swagger-ui"] } queryst = "3.0.0" rand = "0.8.5" diff --git a/src/config/mod.rs b/src/config/mod.rs index c7507d4..de6edc1 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,7 +1,6 @@ -use std::sync::RwLock; - use config::Config; use lazy_static::lazy_static; +use tokio::sync::RwLock; use crate::config::loader::load_settings; diff --git a/src/main.rs b/src/main.rs index 2906943..27195b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,11 +2,28 @@ mod config; mod proxies; mod sideload; -use poem::{listener::TcpListener, EndpointExt, Route, Server}; +use std::collections::VecDeque; + +use lazy_static::lazy_static; +use poem::{listener::TcpListener, Route, Server}; use poem_openapi::OpenApiService; +use proxies::RoadInstance; +use tokio::sync::Mutex; use tracing::{error, info, Level}; -use crate::proxies::route; +use crate::proxies::{metrics::RoadMetrics, route}; + +lazy_static! { + static ref ROAD: Mutex = Mutex::new(RoadInstance { + regions: vec![], + metrics: RoadMetrics { + requests_count: 0, + failures_count: 0, + recent_successes: VecDeque::new(), + recent_errors: VecDeque::new(), + } + }); +} #[tokio::main] async fn main() -> Result<(), std::io::Error> { @@ -19,19 +36,17 @@ async fn main() -> Result<(), std::io::Error> { .init(); // Prepare all the stuff - let mut instance = proxies::Instance::new(); - info!("Loading proxy regions..."); match proxies::loader::scan_regions( config::C .read() - .unwrap() + .await .get_string("regions") .unwrap_or("./regions".to_string()), ) { Err(_) => error!("Loading proxy regions... failed"), Ok((regions, count)) => { - instance.regions = regions; + ROAD.lock().await.regions = regions; info!(count, "Loading proxy regions... done") } }; @@ -40,11 +55,11 @@ async fn main() -> Result<(), std::io::Error> { let proxies_server = Server::new(TcpListener::bind( config::C .read() - .unwrap() + .await .get_string("listen.proxies") .unwrap_or("0.0.0.0:80".to_string()), )) - .run(route::handle.data(instance)); + .run(route::handle); // Sideload let sideload = OpenApiService::new(sideload::SideloadApi, "Sideload API", "1.0") @@ -54,7 +69,7 @@ async fn main() -> Result<(), std::io::Error> { let sideload_server = Server::new(TcpListener::bind( config::C .read() - .unwrap() + .await .get_string("listen.sideload") .unwrap_or("0.0.0.0:81".to_string()), )) diff --git a/src/proxies/metrics.rs b/src/proxies/metrics.rs new file mode 100644 index 0000000..dad7275 --- /dev/null +++ b/src/proxies/metrics.rs @@ -0,0 +1,80 @@ +use std::collections::VecDeque; + +use poem_openapi::Object; +use serde::{Deserialize, Serialize}; + +use super::config::{Destination, Location, Region}; + +#[derive(Debug, Object, Clone, Serialize, Deserialize, PartialEq)] +pub struct RoadTrace { + pub region: String, + pub location: String, + pub destination: String, + pub error: Option, +} + +impl RoadTrace { + pub fn from_structs(reg: Region, loc: Location, end: Destination) -> RoadTrace { + RoadTrace { + region: reg.id, + location: loc.id, + destination: end.id, + error: None, + } + } + + pub fn from_structs_with_error( + reg: Region, + loc: Location, + end: Destination, + err: String, + ) -> RoadTrace { + RoadTrace { + region: reg.id, + location: loc.id, + destination: end.id, + error: Some(err), + } + } +} + +#[derive(Debug, Clone)] +pub struct RoadMetrics { + pub requests_count: u64, + pub failures_count: u64, + + pub recent_successes: VecDeque, + pub recent_errors: VecDeque, +} + +impl RoadMetrics { + pub fn get_success_rate(&self) -> f64 { + if self.requests_count > 0 { + (self.requests_count - self.failures_count) as f64 / self.requests_count as f64 + } else { + 0.0 + } + } + + pub fn add_success_request(&mut self, reg: Region, loc: Location, end: Destination) { + self.requests_count += 1; + self.recent_successes + .push_back(RoadTrace::from_structs(reg, loc, end)); + } + + pub fn add_faliure_request( + &mut self, + reg: Region, + loc: Location, + end: Destination, + err: String, // For some reason error is rarely clonable, so we use preformatted message + ) { + self.requests_count += 1; + self.failures_count += 1; + self.recent_errors + .push_back(RoadTrace::from_structs_with_error(reg, loc, end, err)); + if self.recent_errors.len() > 10 { + self.recent_errors.pop_front(); + } + } +} diff --git a/src/proxies/mod.rs b/src/proxies/mod.rs index aeb2178..2433bb8 100644 --- a/src/proxies/mod.rs +++ b/src/proxies/mod.rs @@ -1,30 +1,35 @@ use http::Method; use poem::http::{HeaderMap, Uri}; use regex::Regex; -use serde::{Deserialize, Serialize}; use wildmatch::WildMatch; -use self::config::{Location, Region}; +use self::{ + config::{Location, Region}, + metrics::RoadMetrics, +}; pub mod browser; pub mod config; pub mod loader; +pub mod metrics; pub mod responder; pub mod route; -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Instance { +#[derive(Debug, Clone)] +pub struct RoadInstance { pub regions: Vec, + pub metrics: RoadMetrics, } -impl Instance { - pub fn new() -> Instance { - Instance { regions: vec![] } - } - - pub fn filter(&self, uri: &Uri, method: Method, headers: &HeaderMap) -> Option<&Location> { +impl RoadInstance { + pub fn filter( + &self, + uri: &Uri, + method: Method, + headers: &HeaderMap, + ) -> Option<(&Region, &Location)> { self.regions.iter().find_map(|region| { - region.locations.iter().find(|location| { + let location = region.locations.iter().find(|location| { let mut hosts = location.hosts.iter(); if !hosts.any(|item| { WildMatch::new(item.as_str()).matches(uri.host().unwrap_or("localhost")) @@ -64,7 +69,9 @@ impl Instance { } true - }) + }); + + location.map(|location| (region, location)) }) } } diff --git a/src/proxies/route.rs b/src/proxies/route.rs index 69c0be6..c45b43c 100644 --- a/src/proxies/route.rs +++ b/src/proxies/route.rs @@ -1,27 +1,30 @@ +use http::Method; use poem::{ handler, http::{HeaderMap, StatusCode, Uri}, - web::{websocket::WebSocket, Data}, + web::websocket::WebSocket, Body, Error, FromRequest, IntoResponse, Request, Response, Result, }; use rand::seq::SliceRandom; -use reqwest::Method; -use crate::proxies::{ - config::{Destination, DestinationType}, - responder, +use crate::{ + proxies::{ + config::{Destination, DestinationType}, + responder, + }, + ROAD, }; #[handler] pub async fn handle( - app: Data<&super::Instance>, req: &Request, uri: &Uri, headers: &HeaderMap, method: Method, body: Body, ) -> Result { - let location = match app.filter(uri, method.clone(), headers) { + let readable_app = ROAD.lock().await; + let (region, location) = match readable_app.filter(uri, method.clone(), headers) { Some(val) => val, None => { return Err(Error::from_string( @@ -93,5 +96,27 @@ pub async fn handle( } } - forward(destination, req, uri, headers, method, body).await + let reg = region.clone(); + let loc = location.clone(); + let end = destination.clone(); + + match forward(&end, req, uri, headers, method, body).await { + Ok(resp) => { + tokio::spawn(async move { + let writable_app = &mut ROAD.lock().await; + writable_app.metrics.add_success_request(reg, loc, end); + }); + Ok(resp) + } + Err(err) => { + let message = format!("{:}", err); + tokio::spawn(async move { + let writable_app = &mut ROAD.lock().await; + writable_app + .metrics + .add_faliure_request(reg, loc, end, message); + }); + Err(err) + } + } } diff --git a/src/sideload/overview.rs b/src/sideload/overview.rs index 1f0b0f1..ba2bc4c 100644 --- a/src/sideload/overview.rs +++ b/src/sideload/overview.rs @@ -1,14 +1,81 @@ -use poem_openapi::{param::Query, payload::PlainText, OpenApi}; +use poem_openapi::{payload::Json, ApiResponse, Object, OpenApi}; + +use crate::{ + proxies::{ + config::{Destination, Location}, + metrics::RoadTrace, + }, + ROAD, +}; use super::SideloadApi; +#[derive(ApiResponse)] +enum OverviewResponse { + /// Return the overview data. + #[oai(status = 200)] + Ok(Json), +} + +#[derive(Debug, Object, Clone, PartialEq)] +struct OverviewData { + /// Loaded regions count + #[oai(read_only)] + regions: usize, + /// Loaded locations count + #[oai(read_only)] + locations: usize, + /// Loaded destnations count + #[oai(read_only)] + destinations: usize, + /// Recent requests count + requests_count: u64, + /// Recent requests success count + faliures_count: u64, + /// Recent requests falied count + successes_count: u64, + /// Recent requests success rate + success_rate: f64, + /// Recent successes + recent_successes: Vec, + /// Recent errors + recent_errors: Vec, +} + #[OpenApi] impl SideloadApi { - #[oai(path = "/hello", method = "get")] - async fn index(&self, name: Query>) -> PlainText { - match name.0 { - Some(name) => PlainText(format!("hello, {name}!")), - None => PlainText("hello!".to_string()), - } + #[oai(path = "/", method = "get")] + async fn index(&self) -> OverviewResponse { + let locked_app = ROAD.lock().await; + let regions = locked_app.regions.clone(); + let locations = regions + .iter() + .flat_map(|item| item.locations.clone()) + .collect::>(); + let destinations = locations + .iter() + .flat_map(|item| item.destinations.clone()) + .collect::>(); + OverviewResponse::Ok(Json(OverviewData { + regions: regions.len(), + locations: locations.len(), + destinations: destinations.len(), + requests_count: locked_app.metrics.requests_count, + successes_count: locked_app.metrics.requests_count - locked_app.metrics.failures_count, + faliures_count: locked_app.metrics.failures_count, + success_rate: locked_app.metrics.get_success_rate(), + recent_successes: locked_app + .metrics + .recent_successes + .clone() + .into_iter() + .collect::>(), + recent_errors: locked_app + .metrics + .recent_errors + .clone() + .into_iter() + .collect::>(), + })) } } -- 2.45.2 From a088f6224ecb935150c90c5d2c82c32e156138ee Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 14 Jan 2024 18:39:22 +0800 Subject: [PATCH 7/9] :sparkles: Basic Auth --- Cargo.toml | 16 +++++++-- Settings.toml | 1 + src/auth.rs | 50 ++++++++++++++++++++++++++ src/main.rs | 15 +++++--- src/proxies/config.rs | 7 ++-- src/proxies/metrics.rs | 7 +++- src/sideload/mod.rs | 16 +++++++++ src/sideload/overview.rs | 76 ++++++++++++++++++---------------------- src/sideload/regions.rs | 25 +++++++++++++ 9 files changed, 160 insertions(+), 53 deletions(-) create mode 100644 src/auth.rs create mode 100644 src/sideload/regions.rs diff --git a/Cargo.toml b/Cargo.toml index 95fae7b..3bd45fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,15 +13,25 @@ hyper-util = { version = "0.1.2", features = ["full"] } lazy_static = "1.4.0" mime = "0.3.17" percent-encoding = "2.3.1" -poem = { version = "2.0.0", features = ["tokio-metrics", "websocket", "static-files", "reqwest"] } -poem-openapi = { version = "4.0.0", features = ["swagger-ui"] } +poem = { version = "2.0.0", features = [ + "tokio-metrics", + "websocket", + "static-files", + "reqwest", +] } +poem-openapi = { version = "4.0.0" } queryst = "3.0.0" rand = "0.8.5" regex = "1.10.2" reqwest = { git = "https://github.com/seanmonstar/reqwest.git", branch = "hyper-v1", version = "0.11.23" } serde = "1.0.195" serde_json = "1.0.111" -tokio = { version = "1.35.1", features = ["rt-multi-thread", "macros", "time", "full"] } +tokio = { version = "1.35.1", features = [ + "rt-multi-thread", + "macros", + "time", + "full", +] } tokio-tungstenite = "0.21.0" toml = "0.8.8" tracing = "0.1.40" diff --git a/Settings.toml b/Settings.toml index d1fc034..b31488d 100644 --- a/Settings.toml +++ b/Settings.toml @@ -1,4 +1,5 @@ regions = "./regions" +secret = "aEXcED5xJ3" [listen] proxies = "0.0.0.0:80" diff --git a/src/auth.rs b/src/auth.rs new file mode 100644 index 0000000..79b28c4 --- /dev/null +++ b/src/auth.rs @@ -0,0 +1,50 @@ +use http::StatusCode; +use poem::{ + web::headers::{self, authorization::Basic, HeaderMapExt}, + Endpoint, Error, Middleware, Request, Response, Result, +}; + +pub struct BasicAuth { + pub username: String, + pub password: String, +} + +impl Middleware for BasicAuth { + type Output = BasicAuthEndpoint; + + fn transform(&self, ep: E) -> Self::Output { + BasicAuthEndpoint { + ep, + username: self.username.clone(), + password: self.password.clone(), + } + } +} + +pub struct BasicAuthEndpoint { + ep: E, + username: String, + password: String, +} + +#[poem::async_trait] +impl Endpoint for BasicAuthEndpoint { + type Output = E::Output; + + async fn call(&self, req: Request) -> Result { + if let Some(auth) = req.headers().typed_get::>() { + if auth.0.username() == self.username && auth.0.password() == self.password { + return self.ep.call(req).await; + } + } + Err(Error::from_response( + Response::builder() + .header( + "WWW-Authenticate", + "Basic realm=\"RoadSig\", charset=\"UTF-8\"", + ) + .status(StatusCode::UNAUTHORIZED) + .finish(), + )) + } +} diff --git a/src/main.rs b/src/main.rs index 27195b2..b5859be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +pub mod auth; mod config; mod proxies; mod sideload; @@ -5,7 +6,7 @@ mod sideload; use std::collections::VecDeque; use lazy_static::lazy_static; -use poem::{listener::TcpListener, Route, Server}; +use poem::{listener::TcpListener, EndpointExt, Route, Server}; use poem_openapi::OpenApiService; use proxies::RoadInstance; use tokio::sync::Mutex; @@ -64,7 +65,6 @@ async fn main() -> Result<(), std::io::Error> { // Sideload let sideload = OpenApiService::new(sideload::SideloadApi, "Sideload API", "1.0") .server("http://localhost:3000/cgi"); - let sideload_ui = sideload.swagger_ui(); let sideload_server = Server::new(TcpListener::bind( config::C @@ -74,9 +74,14 @@ async fn main() -> Result<(), std::io::Error> { .unwrap_or("0.0.0.0:81".to_string()), )) .run( - Route::new() - .nest("/cgi", sideload) - .nest("/swagger", sideload_ui), + Route::new().nest("/cgi", sideload).with(auth::BasicAuth { + username: "RoadSign".to_string(), + password: config::C + .read() + .await + .get_string("secret") + .unwrap_or("password".to_string()), + }), ); tokio::try_join!(proxies_server, sideload_server)?; diff --git a/src/proxies/config.rs b/src/proxies/config.rs index c6c1835..ae4f2d5 100644 --- a/src/proxies/config.rs +++ b/src/proxies/config.rs @@ -1,18 +1,19 @@ use std::collections::HashMap; +use poem_openapi::Object; use queryst::parse; use serde::{Deserialize, Serialize}; use serde_json::json; use super::responder::StaticResponderConfig; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Object, Clone, Serialize, Deserialize)] pub struct Region { pub id: String, pub locations: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Object, Clone, Serialize, Deserialize)] pub struct Location { pub id: String, pub hosts: Vec, @@ -23,7 +24,7 @@ pub struct Location { pub destinations: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Object, Clone, Serialize, Deserialize)] pub struct Destination { pub id: String, pub uri: String, diff --git a/src/proxies/metrics.rs b/src/proxies/metrics.rs index dad7275..4ecd101 100644 --- a/src/proxies/metrics.rs +++ b/src/proxies/metrics.rs @@ -47,6 +47,8 @@ pub struct RoadMetrics { pub recent_errors: VecDeque, } +const MAX_TRACE_COUNT: usize = 10; + impl RoadMetrics { pub fn get_success_rate(&self) -> f64 { if self.requests_count > 0 { @@ -60,6 +62,9 @@ impl RoadMetrics { self.requests_count += 1; self.recent_successes .push_back(RoadTrace::from_structs(reg, loc, end)); + if self.recent_successes.len() > MAX_TRACE_COUNT { + self.recent_successes.pop_front(); + } } pub fn add_faliure_request( @@ -73,7 +78,7 @@ impl RoadMetrics { self.failures_count += 1; self.recent_errors .push_back(RoadTrace::from_structs_with_error(reg, loc, end, err)); - if self.recent_errors.len() > 10 { + if self.recent_errors.len() > MAX_TRACE_COUNT { self.recent_errors.pop_front(); } } diff --git a/src/sideload/mod.rs b/src/sideload/mod.rs index 95a4645..e69231e 100644 --- a/src/sideload/mod.rs +++ b/src/sideload/mod.rs @@ -1,3 +1,19 @@ +use poem_openapi::OpenApi; + pub mod overview; +pub mod regions; pub struct SideloadApi; + +#[OpenApi] +impl SideloadApi { + #[oai(path = "/", method = "get")] + async fn index(&self) -> overview::OverviewResponse { + overview::index().await + } + + #[oai(path = "/regions", method = "get")] + async fn regions_index(&self) -> regions::RegionResponse { + regions::index().await + } +} diff --git a/src/sideload/overview.rs b/src/sideload/overview.rs index ba2bc4c..8c3ac6b 100644 --- a/src/sideload/overview.rs +++ b/src/sideload/overview.rs @@ -1,4 +1,4 @@ -use poem_openapi::{payload::Json, ApiResponse, Object, OpenApi}; +use poem_openapi::{payload::Json, ApiResponse, Object}; use crate::{ proxies::{ @@ -8,17 +8,15 @@ use crate::{ ROAD, }; -use super::SideloadApi; - #[derive(ApiResponse)] -enum OverviewResponse { +pub enum OverviewResponse { /// Return the overview data. #[oai(status = 200)] Ok(Json), } #[derive(Debug, Object, Clone, PartialEq)] -struct OverviewData { +pub struct OverviewData { /// Loaded regions count #[oai(read_only)] regions: usize, @@ -42,40 +40,36 @@ struct OverviewData { recent_errors: Vec, } -#[OpenApi] -impl SideloadApi { - #[oai(path = "/", method = "get")] - async fn index(&self) -> OverviewResponse { - let locked_app = ROAD.lock().await; - let regions = locked_app.regions.clone(); - let locations = regions - .iter() - .flat_map(|item| item.locations.clone()) - .collect::>(); - let destinations = locations - .iter() - .flat_map(|item| item.destinations.clone()) - .collect::>(); - OverviewResponse::Ok(Json(OverviewData { - regions: regions.len(), - locations: locations.len(), - destinations: destinations.len(), - requests_count: locked_app.metrics.requests_count, - successes_count: locked_app.metrics.requests_count - locked_app.metrics.failures_count, - faliures_count: locked_app.metrics.failures_count, - success_rate: locked_app.metrics.get_success_rate(), - recent_successes: locked_app - .metrics - .recent_successes - .clone() - .into_iter() - .collect::>(), - recent_errors: locked_app - .metrics - .recent_errors - .clone() - .into_iter() - .collect::>(), - })) - } +pub async fn index() -> OverviewResponse { + let locked_app = ROAD.lock().await; + let regions = locked_app.regions.clone(); + let locations = regions + .iter() + .flat_map(|item| item.locations.clone()) + .collect::>(); + let destinations = locations + .iter() + .flat_map(|item| item.destinations.clone()) + .collect::>(); + OverviewResponse::Ok(Json(OverviewData { + regions: regions.len(), + locations: locations.len(), + destinations: destinations.len(), + requests_count: locked_app.metrics.requests_count, + successes_count: locked_app.metrics.requests_count - locked_app.metrics.failures_count, + faliures_count: locked_app.metrics.failures_count, + success_rate: locked_app.metrics.get_success_rate(), + recent_successes: locked_app + .metrics + .recent_successes + .clone() + .into_iter() + .collect::>(), + recent_errors: locked_app + .metrics + .recent_errors + .clone() + .into_iter() + .collect::>(), + })) } diff --git a/src/sideload/regions.rs b/src/sideload/regions.rs new file mode 100644 index 0000000..827d603 --- /dev/null +++ b/src/sideload/regions.rs @@ -0,0 +1,25 @@ +use poem_openapi::{payload::Json, ApiResponse}; + +use crate::{proxies::config::Region, ROAD}; + +#[derive(ApiResponse)] +pub enum RegionResponse { + /// Return the region data. + #[oai(status = 200)] + Ok(Json), + /// Return the list of region data. + #[oai(status = 200)] + OkMany(Json>), + /// Return the region data after created. + #[oai(status = 201)] + Created(Json), + /// Return was not found. + #[oai(status = 404)] + NotFound, +} + +pub async fn index() -> RegionResponse { + let locked_app = ROAD.lock().await; + + RegionResponse::OkMany(Json(locked_app.regions.clone())) +} -- 2.45.2 From 4c08d78bed0e8079f0e2ed416e0e7422355eafe3 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 14 Jan 2024 19:12:38 +0800 Subject: [PATCH 8/9] :sparkles: Add headers in reserve proxy --- src/proxies/responder.rs | 20 ++++++++++++++++++++ src/proxies/route.rs | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/proxies/responder.rs b/src/proxies/responder.rs index fa60aff..5322ab4 100644 --- a/src/proxies/responder.rs +++ b/src/proxies/responder.rs @@ -63,10 +63,28 @@ pub async fn repond_websocket(req: Builder, ws: WebSocket) -> Response { pub async fn respond_hypertext( uri: String, ori: &Uri, + req: &Request, method: Method, body: Body, headers: &HeaderMap, ) -> Result { + let ip = req.remote_addr().to_string(); + let proto = req.uri().scheme_str().unwrap(); + let host = req.uri().host().unwrap(); + + let mut headers = headers.clone(); + headers.insert("Server", "RoadSign".parse().unwrap()); + headers.insert("X-Forward-For", ip.parse().unwrap()); + headers.insert("X-Forwarded-Proto", proto.parse().unwrap()); + headers.insert("X-Forwarded-Host", host.parse().unwrap()); + headers.insert("X-Real-IP", ip.parse().unwrap()); + headers.insert( + "Forwarded", + format!("by={};for={};host={};proto={}", ip, ip, host, proto) + .parse() + .unwrap(), + ); + let res = CLIENT .request(method, uri + ori.path() + ori.query().unwrap_or("")) .headers(headers.clone()) @@ -81,6 +99,8 @@ pub async fn respond_hypertext( result.headers().iter().for_each(|(key, val)| { res.headers_mut().insert(key, val.to_owned()); }); + res.headers_mut() + .insert("Server", "RoadSign".parse().unwrap()); res.set_status(result.status()); res.set_version(result.version()); res.set_body(result.bytes().await.unwrap()); diff --git a/src/proxies/route.rs b/src/proxies/route.rs index c45b43c..ec86e45 100644 --- a/src/proxies/route.rs +++ b/src/proxies/route.rs @@ -77,7 +77,7 @@ pub async fn handle( )); }; - responder::respond_hypertext(uri, ori, method, body, headers).await + responder::respond_hypertext(uri, ori, req, method, body, headers).await } DestinationType::StaticFiles => { let Ok(cfg) = end.get_static_config() else { -- 2.45.2 From ead748a508da4eda44cf511dddb85693601d932a Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Mon, 15 Jan 2024 23:22:53 +0800 Subject: [PATCH 9/9] :sparkles: Warden process manager --- .idea/RoadSign.iml | 5 +- Cargo.lock | 16 +++---- regions/index.toml | 5 ++ regions/script.sh | 3 ++ src/main.rs | 25 +++++----- src/proxies/config.rs | 3 ++ src/proxies/metrics.rs | 9 ++++ src/proxies/mod.rs | 13 ++++++ src/warden/mod.rs | 73 +++++++++++++++++++++++++++++ src/warden/runner.rs | 104 +++++++++++++++++++++++++++++++++++++++++ 10 files changed, 235 insertions(+), 21 deletions(-) create mode 100755 regions/script.sh create mode 100644 src/warden/mod.rs create mode 100644 src/warden/runner.rs diff --git a/.idea/RoadSign.iml b/.idea/RoadSign.iml index 5e764c4..758ceaa 100644 --- a/.idea/RoadSign.iml +++ b/.idea/RoadSign.iml @@ -2,7 +2,10 @@ - + + + + diff --git a/Cargo.lock b/Cargo.lock index 36b0e29..70acf64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -133,9 +133,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bitflags" -version = "1.3.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bitflags" @@ -1452,7 +1452,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", ] [[package]] @@ -1601,7 +1601,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" dependencies = [ "base64 0.13.1", - "bitflags 1.3.2", + "bitflags 1.2.1", "serde", ] @@ -1666,11 +1666,11 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.9.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", "core-foundation", "core-foundation-sys", "libc", @@ -1878,7 +1878,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", "core-foundation", "system-configuration-sys", ] diff --git a/regions/index.toml b/regions/index.toml index db75202..9d87b77 100644 --- a/regions/index.toml +++ b/regions/index.toml @@ -7,3 +7,8 @@ paths = ["/"] [[locations.destinations]] id = "static" uri = "files://regions?index=index.html" + +[[applications]] +id = "script" +exe = "./script.sh" +workdir = "regions" diff --git a/regions/script.sh b/regions/script.sh new file mode 100755 index 0000000..eca748e --- /dev/null +++ b/regions/script.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +echo "Good morning!" diff --git a/src/main.rs b/src/main.rs index b5859be..a634ae5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,8 +2,7 @@ pub mod auth; mod config; mod proxies; mod sideload; - -use std::collections::VecDeque; +pub mod warden; use lazy_static::lazy_static; use poem::{listener::TcpListener, EndpointExt, Route, Server}; @@ -12,18 +11,10 @@ use proxies::RoadInstance; use tokio::sync::Mutex; use tracing::{error, info, Level}; -use crate::proxies::{metrics::RoadMetrics, route}; +use crate::proxies::route; lazy_static! { - static ref ROAD: Mutex = Mutex::new(RoadInstance { - regions: vec![], - metrics: RoadMetrics { - requests_count: 0, - failures_count: 0, - recent_successes: VecDeque::new(), - recent_errors: VecDeque::new(), - } - }); + static ref ROAD: Mutex = Mutex::new(RoadInstance::new()); } #[tokio::main] @@ -84,6 +75,16 @@ async fn main() -> Result<(), std::io::Error> { }), ); + // Process manager + { + let mut app = ROAD.lock().await; + { + let reg = app.regions.clone(); + app.warden.scan(reg); + } + app.warden.start().await; + } + tokio::try_join!(proxies_server, sideload_server)?; Ok(()) diff --git a/src/proxies/config.rs b/src/proxies/config.rs index ae4f2d5..108e6f8 100644 --- a/src/proxies/config.rs +++ b/src/proxies/config.rs @@ -5,12 +5,15 @@ use queryst::parse; use serde::{Deserialize, Serialize}; use serde_json::json; +use crate::warden::Application; + use super::responder::StaticResponderConfig; #[derive(Debug, Object, Clone, Serialize, Deserialize)] pub struct Region { pub id: String, pub locations: Vec, + pub applications: Vec, } #[derive(Debug, Object, Clone, Serialize, Deserialize)] diff --git a/src/proxies/metrics.rs b/src/proxies/metrics.rs index 4ecd101..5d06a0e 100644 --- a/src/proxies/metrics.rs +++ b/src/proxies/metrics.rs @@ -50,6 +50,15 @@ pub struct RoadMetrics { const MAX_TRACE_COUNT: usize = 10; impl RoadMetrics { + pub fn new() -> RoadMetrics { + RoadMetrics { + requests_count: 0, + failures_count: 0, + recent_successes: VecDeque::new(), + recent_errors: VecDeque::new(), + } + } + pub fn get_success_rate(&self) -> f64 { if self.requests_count > 0 { (self.requests_count - self.failures_count) as f64 / self.requests_count as f64 diff --git a/src/proxies/mod.rs b/src/proxies/mod.rs index 2433bb8..c62bfe0 100644 --- a/src/proxies/mod.rs +++ b/src/proxies/mod.rs @@ -3,6 +3,8 @@ use poem::http::{HeaderMap, Uri}; use regex::Regex; use wildmatch::WildMatch; +use crate::warden::WardenInstance; + use self::{ config::{Location, Region}, metrics::RoadMetrics, @@ -19,9 +21,20 @@ pub mod route; pub struct RoadInstance { pub regions: Vec, pub metrics: RoadMetrics, + pub warden: WardenInstance, } impl RoadInstance { + pub fn new() -> RoadInstance { + RoadInstance { + regions: vec![], + warden: WardenInstance { + applications: vec![], + }, + metrics: RoadMetrics::new(), + } + } + pub fn filter( &self, uri: &Uri, diff --git a/src/warden/mod.rs b/src/warden/mod.rs new file mode 100644 index 0000000..e7265b4 --- /dev/null +++ b/src/warden/mod.rs @@ -0,0 +1,73 @@ +pub mod runner; + +use std::collections::HashMap; + +use futures_util::lock::Mutex; +use lazy_static::lazy_static; +use poem_openapi::Object; +use serde::{Deserialize, Serialize}; +use tracing::{debug, warn}; + +use crate::proxies::config::Region; + +use self::runner::AppInstance; + +lazy_static! { + static ref INSTANCES: Mutex> = Mutex::new(HashMap::new()); +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WardenInstance { + pub applications: Vec, +} + +impl WardenInstance { + pub fn new() -> WardenInstance { + WardenInstance { + applications: vec![], + } + } + + pub fn scan(&mut self, regions: Vec) { + self.applications = regions + .iter() + .flat_map(|item| item.applications.clone()) + .collect::>(); + debug!( + applications = format!("{:?}", self.applications), + "Warden scan accomplished." + ) + } + + pub async fn start(&self) { + for item in self.applications.iter() { + let mut instance = AppInstance::new(); + match instance.start(item.clone()).await { + Ok(_) => { + debug!(id = item.id, "Warden successfully created instance for"); + INSTANCES.lock().await.insert(item.clone().id, instance); + } + Err(err) => warn!( + id = item.id, + err = format!("{:?}", err), + "Warden failed to create an instance for" + ), + }; + } + } +} + +impl Default for WardenInstance { + fn default() -> Self { + Self::new() + } +} + +#[derive(Debug, Object, Clone, Serialize, Deserialize)] +pub struct Application { + pub id: String, + pub exe: String, + pub args: Option>, + pub env: Option>, + pub workdir: String, +} diff --git a/src/warden/runner.rs b/src/warden/runner.rs new file mode 100644 index 0000000..080388f --- /dev/null +++ b/src/warden/runner.rs @@ -0,0 +1,104 @@ +use std::{borrow::BorrowMut, collections::HashMap, io}; + +use super::Application; +use futures_util::lock::Mutex; +use lazy_static::lazy_static; +use tokio::{ + io::{AsyncBufReadExt, BufReader}, + process::{Child, Command}, +}; + +lazy_static! { + static ref STDOUT: Mutex> = Mutex::new(HashMap::new()); + static ref STDERR: Mutex> = Mutex::new(HashMap::new()); +} + +#[derive(Debug)] +pub struct AppInstance { + pub app: Option, + pub program: Option, +} + +impl AppInstance { + pub fn new() -> AppInstance { + AppInstance { + app: None, + program: None, + } + } + + pub async fn start(&mut self, app: Application) -> io::Result<()> { + return match Command::new(app.exe.clone()) + .args(app.args.clone().unwrap_or_default()) + .envs(app.env.clone().unwrap_or_default()) + .current_dir(app.workdir.clone()) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .spawn() + { + Ok(mut child) => { + let stderr_reader = BufReader::new(child.stderr.take().unwrap()); + let stdout_reader = BufReader::new(child.stdout.take().unwrap()); + + tokio::spawn(read_stream_and_capture(stderr_reader, app.id.clone(), true)); + tokio::spawn(read_stream_and_capture( + stdout_reader, + app.id.clone(), + false, + )); + + self.app = Some(app.clone()); + self.program = Some(child); + + Ok(()) + } + Err(err) => Err(err), + }; + } + + pub async fn stop(&mut self) -> Result<(), io::Error> { + if let Some(child) = self.program.borrow_mut() { + return child.kill().await; + } + Ok(()) + } + + pub async fn get_stdout(&self) -> Option { + if let Some(app) = self.app.clone() { + STDOUT.lock().await.get(&app.id).cloned() + } else { + None + } + } + + pub async fn get_stderr(&self) -> Option { + if let Some(app) = self.app.clone() { + STDERR.lock().await.get(&app.id).cloned() + } else { + None + } + } +} + +impl Default for AppInstance { + fn default() -> Self { + Self::new() + } +} + +async fn read_stream_and_capture(reader: R, id: String, is_err: bool) -> io::Result<()> +where + R: tokio::io::AsyncBufRead + Unpin, +{ + let mut lines = reader.lines(); + while let Some(line) = lines.next_line().await? { + if !is_err { + if let Some(out) = STDOUT.lock().await.get_mut(&id) { + out.push_str(&line); + } + } else if let Some(out) = STDERR.lock().await.get_mut(&id) { + out.push_str(&line); + } + } + Ok(()) +} -- 2.45.2