commit 0fcae66f546650636f96abe8dde0106ac534f8f5 Author: lkhsss Date: Fri Jun 13 22:40:00 2025 +0800 momo的首次提交! diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..5e5fbab --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,141 @@ + +name: Release + +on: + push: + tags: + - "v*.*.*" + +jobs: + release: + name: Publish to Github Releases + permissions: + contents: write + outputs: + rc: ${{ steps.check-tag.outputs.rc }} + + strategy: + fail-fast: false + matrix: + include: + - target: aarch64-unknown-linux-musl + use-cross: true + os: ubuntu-latest + cargo-flags: "" + - target: aarch64-apple-darwin + os: macos-latest + use-cross: true + cargo-flags: "" + - target: aarch64-pc-windows-msvc + os: windows-latest + use-cross: true + cargo-flags: "" + - target: x86_64-apple-darwin + os: macos-latest + cargo-flags: "" + - target: x86_64-pc-windows-msvc + os: windows-latest + cargo-flags: "" + - target: x86_64-unknown-linux-musl + os: ubuntu-latest + use-cross: true + cargo-flags: "" + - target: i686-unknown-linux-musl + os: ubuntu-latest + use-cross: true + cargo-flags: "" + - target: i686-pc-windows-msvc + os: windows-latest + use-cross: true + cargo-flags: "" + - target: armv7-unknown-linux-musleabihf + os: ubuntu-latest + use-cross: true + cargo-flags: "" + - target: arm-unknown-linux-musleabihf + os: ubuntu-latest + use-cross: true + cargo-flags: "" + + runs-on: ${{matrix.os}} + env: + BUILD_CMD: cargo + + steps: + - uses: actions/checkout@v4 + + - name: Check Tag + id: check-tag + shell: bash + run: | + ver=${GITHUB_REF##*/} + echo "version=$ver" >> $GITHUB_OUTPUT + if [[ "$ver" =~ [0-9]+.[0-9]+.[0-9]+$ ]]; then + echo "rc=false" >> $GITHUB_OUTPUT + else + echo "rc=true" >> $GITHUB_OUTPUT + fi + - name: Install Rust Toolchain Components + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Install cross + if: matrix.use-cross + uses: taiki-e/install-action@v2 + with: + tool: cross + + - name: Overwrite build command env variable + if: matrix.use-cross + shell: bash + run: echo "BUILD_CMD=cross" >> $GITHUB_ENV + + - name: Show Version Information (Rust, cargo, GCC) + shell: bash + run: | + gcc --version || true + rustup -V + rustup toolchain list + rustup default + cargo -V + rustc -V + + - name: Build + shell: bash + run: $BUILD_CMD build --locked --release --target=${{ matrix.target }} ${{ matrix.cargo-flags }} + + - name: Build Archive + shell: bash + id: package + env: + target: ${{ matrix.target }} + version: ${{ steps.check-tag.outputs.version }} + run: | + set -euxo pipefail + bin=${GITHUB_REPOSITORY##*/} + dist_dir=`pwd`/dist + name=$bin-$version-$target + executable=target/$target/release/$bin + if [[ "$RUNNER_OS" == "Windows" ]]; then + executable=$executable.exe + fi + mkdir $dist_dir + cp $executable $dist_dir + cd $dist_dir + if [[ "$RUNNER_OS" == "Windows" ]]; then + archive=$dist_dir/$name.zip + 7z a $archive * + echo "archive=dist/$name.zip" >> $GITHUB_OUTPUT + else + archive=$dist_dir/$name.tar.gz + tar -czf $archive * + echo "archive=dist/$name.tar.gz" >> $GITHUB_OUTPUT + fi + - name: Publish Archive + uses: softprops/action-gh-release@v2 + if: ${{ startsWith(github.ref, 'refs/tags/') }} + with: + draft: false + files: ${{ steps.package.outputs.archive }} + prerelease: ${{ steps.check-tag.outputs.rc == 'true' }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93ee0ac --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +\target \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..bcfd27e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1285 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "anstream" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.59.0", +] + +[[package]] +name = "askama" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4" +dependencies = [ + "askama_derive", + "itoa", + "percent-encoding", + "serde", + "serde_json", +] + +[[package]] +name = "askama_derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f" +dependencies = [ + "askama_parser", + "basic-toml", + "memchr", + "proc-macro2", + "quote", + "rustc-hash", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "askama_parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358" +dependencies = [ + "memchr", + "serde", + "serde_derive", + "winnow", +] + +[[package]] +name = "axum" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "basic-toml" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "clap" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.59.0", +] + +[[package]] +name = "momo" +version = "0.1.0" +dependencies = [ + "askama", + "axum", + "clap", + "lazy_static", + "serde", + "thiserror", + "tokio", + "tower", + "tower-http", + "tracing", + "tracing-subscriber", + "url", +] + +[[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 = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" +dependencies = [ + "itoa", + "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 = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +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.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[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-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +dependencies = [ + "memchr", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4ae4328 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "momo" +version = "0.1.0" +edition = "2024" + +[dependencies] +axum = "0.8.4" +askama = "0.14.0" +clap = { version = "4.5.37", features = ["cargo", "derive"] } +lazy_static = "1.5.0" +serde = { version = "1.0.219", features = ["serde_derive"] } +tokio = { version = "1.44.2", features = ["macros", "rt-multi-thread"] } +tower = "0.5.2" +tower-http = { version = "0.6.2", features = ["fs","trace"] } +tracing-subscriber = "0.3.19" +url = "2.5.4" +thiserror = "2.0.12" +tracing = "0.1.41" + +[profile.release] +#缩小编译后体积 +strip = true +# strip = "debuginfo" #仅移除debug信息 +lto = true #启用链接时间优化 +panic = "abort" #panic时直接abort +opt-level = "z" #优化级别 diff --git a/README.md b/README.md new file mode 100644 index 0000000..ae2660b --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +## Momo +简单的图片浏览服务 + +### 使用 +```bash +momo.exe [OPTIONS] +``` +```bash +Options: + -d, --directory 设置工作目录 + -p, --port 监听的端口号 [default: 3000] + -l, --loglevel... 启用调试信息 + -w, --width 设置图片的宽度,该设置决定了瀑布流的宽度 [default: 350] + -h, --help Print help + -V, --version Print version +``` diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..44e06f4 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,11 @@ +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("无法获取目录")] + CannotGetItemList(#[from] std::io::Error), + #[error("路径穿越")] + PathTraversal, + #[error("路径不合法")] + PathNotValid, + #[error("无法获取工作目录")] + CannotGetWorkDir +} diff --git a/src/filetype.rs b/src/filetype.rs new file mode 100644 index 0000000..28c0832 --- /dev/null +++ b/src/filetype.rs @@ -0,0 +1,86 @@ +use std::fmt::Display; + +pub const IMAGE_FILE_EXTENSION: [&str; 16] = [ + "apng", "avif", "gif", "jpg", "jpeg", "jfif", "pjpeg", "pjp", "png", "svg", "webp", "bmp", + "ico", "cur", "tif", "tiff", +]; + +#[derive(Debug, PartialEq, Eq)] +pub enum FileType { + Image(ImageType), + Other, + //TODO 完成其他类型 +} + +impl ToContentType for FileType { + fn get_content_type(&self) -> impl std::fmt::Display { + match self { + FileType::Image(image) => image.get_content_type().to_string(), + FileType::Other => "application/octet-stream".to_string(), + } + } +} + +#[allow(dead_code)] +#[derive(Debug, PartialEq, Eq)] +pub enum ImageType { + Apng, + Avif, + Gif, + Jpeg, + Png, + Svg, + WebP, + Bmp, + Ico, + Tiff, +} + + + +impl Display for ImageType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + ImageType::Apng => "apng", + ImageType::Avif => "avif", + ImageType::Gif => "gif", + ImageType::Jpeg => "jpeg", + ImageType::Png => "png", + ImageType::Svg => "svg", + ImageType::WebP => "webp", + ImageType::Bmp => "bmp", + ImageType::Ico => "ico", + ImageType::Tiff => "tiff", + }; + write!(f, "{}", s) + } +} + +impl ImageType { + //全转小写,方便比对 + pub fn is_image_file(extension: &str) -> bool { + IMAGE_FILE_EXTENSION.contains(&extension.to_lowercase().as_str()) + } +} + +impl ToContentType for ImageType { + fn get_content_type(&self) -> impl Display { + match self { + ImageType::Apng => "image/apng", + ImageType::Avif => "image/avif", + ImageType::Gif => "image/gif", + ImageType::Jpeg => "image/jpeg", + ImageType::Png => "image/png", + ImageType::Svg => "image/svg+xml", + ImageType::WebP => "image/webp", + ImageType::Bmp => "image/bmp", + ImageType::Ico => "image/x-icon", + ImageType::Tiff => "image/tiff", + } + } +} + +/// 实现此trait后可以通过结构体获取Content-Type +pub trait ToContentType { + fn get_content_type(&self) -> impl Display; +} diff --git a/src/handlers.rs b/src/handlers.rs new file mode 100644 index 0000000..97b81d5 --- /dev/null +++ b/src/handlers.rs @@ -0,0 +1,296 @@ +use askama::Template; +use axum::{ + body::Bytes, + http::{StatusCode, header}, + response::{Html, IntoResponse}, +}; +use std::{ + fmt::Display, + path::{Path, PathBuf}, +}; +use tokio::fs::{self}; +use tracing::{info, warn}; + +use crate::{ + CONFIG, + error::Error, + filetype::{self, FileType, IMAGE_FILE_EXTENSION, ImageType, ToContentType}, + template::{ImageFallTemplate, Imgs}, +}; + +pub async fn handler(uri: Option>) -> impl IntoResponse { + let request_path = match uri { + Some(u) => PathBuf::from(u.0), + None => PathBuf::from("/"), + }; + + let base_path = match std::env::current_dir() { + Ok(p) => p, + Err(e) => { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("无法获取工作目录。\n原因:{e}"), + ) + .into_response(); + } + }; + let uri = Uri::frompath(&request_path); + + let path = match uri.0.as_str() { + //看看是否为请求网站更目录目录(即工作目录根目录) + "/" => base_path, + _ => format_path(base_path.join(&request_path)), + }; + + let request_uri = UriBox::new(path, uri); + match request_uri.path.try_exists() { + // 尝试判断是否存在,避免硬件错误 + Ok(status) => { + //判断是否存在 + if status { + //存在 + if request_uri.path.is_file() { + file_handler(request_uri).await.into_response() + } else if request_uri.path.is_dir() { + dir_handler(request_uri).await.into_response() + } else { + (StatusCode::INTERNAL_SERVER_ERROR, "不是文件也不是目录").into_response() + } + } else { + //不存在 + (StatusCode::NOT_FOUND, "目标不存在").into_response() + } + } + Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("{:?}", e)).into_response(), + } +} + +pub async fn dir_handler(dir: UriBox) -> impl IntoResponse { + //获取请求目录的列表 + let items = match get_item(dir) { + Ok(o) => o, + Err(e) => match e { + Error::CannotGetItemList(error) => { + return (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response(); + } + Error::PathTraversal => { + return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(); + } + Error::PathNotValid => { + return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(); + } + Error::CannotGetWorkDir => { + return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(); + } + }, + }; + + let mut imgs = Vec::new(); + for i in items { + if let Some(ext) = i.path.extension() { + let ext = match ext.to_str() { + Some(e) => e, + None => panic!(), + }; + if filetype::ImageType::is_image_file(ext) { + // info!("{}",i.uri.0); + imgs.push(Imgs::new(i.uri.0)); + } + } + } + + if imgs.len() == 0 { + return ( + StatusCode::OK, + Html(include_bytes!("../templates/NoResources.html")), + ) + .into_response(); + } + + // 返回模板数据 + let html = match (ImageFallTemplate { + imgs, + column_width: CONFIG.width, + }) + .render() + { + Ok(h) => h, + Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, ("无法返回模板文件")).into_response(), + }; + (StatusCode::OK, Html(html)).into_response() +} + +/// # 读取文件并且设置CONTENT_TYPE +/// ### 需要确定文件存在! +pub async fn file_handler(file: UriBox) -> impl IntoResponse { + let extension = file.path.extension(); + let header = match extension { + //如果有后缀 + Some(e) => { + let extension = e.to_string_lossy().to_string().to_lowercase(); //全部转小写,避免出错 + + let content_type = extension_handler(extension).get_content_type().to_string(); + [(header::CONTENT_TYPE, content_type)] + } //如果没后缀 + None => [( + header::CONTENT_TYPE, + FileType::Other.get_content_type().to_string(), + )], + }; + // 异步读取文件 + match fs::read(&file.path).await { + Ok(data) => (header, Bytes::from(data)).into_response(), + Err(e) => ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("文件读取出错, 原因:{e}"), + ) + .into_response(), + } +} + +fn extension_handler(ext: String) -> FileType { + if IMAGE_FILE_EXTENSION.contains(&ext.to_lowercase().as_str()) + //如果为图片格式 + { + FileType::Image(image_extension_handler(ext)) + } else { + FileType::Other + } +} +// 全部自动转小写 +fn image_extension_handler(extension: impl ToString) -> ImageType { + match &extension.to_string().to_lowercase()[..] { + "apng" => ImageType::Apng, + "jpeg" | "jpg" | "jfif" | "pjpeg" | "pjp" => ImageType::Jpeg, + "png" => ImageType::Png, + "svg" => ImageType::Svg, + "webp" => ImageType::WebP, + "bmp" => ImageType::Bmp, + "ico" | "cur" => ImageType::Ico, + "tif" | "tiff" => ImageType::Tiff, + _ => panic!(), + } +} + +fn format_path(ori: PathBuf) -> PathBuf { + let mut new_path = PathBuf::new(); + for p in ori.components() { + new_path.push(p); + } + new_path +} + +fn get_item(p: UriBox) -> Result, Error> { + let mut items = Vec::new(); + let base_path = match std::env::current_dir() { + Ok(p) => p, + Err(_) => { + return Err(Error::CannotGetWorkDir); + } + }; + for entry in std::fs::read_dir(&p.path)? { + let entry = entry?; + if entry.file_type()?.is_file() { + match reslove_relative_path(&base_path, entry.path()) { + Ok(uri) => items.push(UriBox { + path: entry.path(), + uri, + }), + Err(e) => return Err(e), + }; //计算相对路径 + } + } + Ok(items) +} + +#[derive(Debug, Clone)] +pub struct Uri(String); + +impl Uri { + //直接转化为Uri + fn new(s: impl Into) -> Self { + Self(s.into()) + } + fn frompath(url: impl AsRef) -> Self { + if url.as_ref() == PathBuf::from("/") { + Self("/".into()) + } else { + let mut new_url = String::new(); + for i in url.as_ref().components() { + new_url.push('/'); + new_url.push_str(&format!("{}", i.as_os_str().to_string_lossy())); + } + Self(new_url) + } + } + + //将迭代器转为Uri + fn from_iter>(slice: impl Iterator) -> Self { + let mut uri = String::new(); + for i in slice { + uri.push('/'); + uri.push_str(&i.into()); + } + Self::new(uri) + } +} + +impl Display for Uri { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +//处理url和本地目录的关系 +#[derive(Debug, Clone)] +pub struct UriBox { + path: PathBuf, + uri: Uri, +} + +impl UriBox { + fn new(path: PathBuf, uri: Uri) -> Self { + Self { path, uri } + } +} + +/// 计算相对路径 +fn reslove_relative_path( + base_path: impl AsRef, + relative_path: impl AsRef, +) -> Result { + let mut base_path_components: Vec = match base_path.as_ref().canonicalize() { + Ok(p) => p, + Err(_) => return Err(Error::PathNotValid), + } + .components() + .map(|x| x.as_os_str().to_string_lossy().to_string()) + .collect(); + + let relative_path_canonicalize = match relative_path.as_ref().canonicalize() { + Ok(p) => p, + Err(_) => return Err(Error::PathNotValid), + }; + let mut relative_path_components: Vec = relative_path_canonicalize + .components() + .map(|x| x.as_os_str().to_string_lossy().to_string()) + .collect(); + base_path_components.retain(|x| x != "\\"); + relative_path_components.retain(|x| x != "\\"); + + if relative_path_components.len() < base_path_components.len() { + //如果请求路径短于基础路径那肯定是非法的 + warn!("路径穿越攻击!"); + return Err(Error::PathTraversal); + } + + for i in 0..(base_path_components.len()) { + if base_path_components[i] != relative_path_components[i] { + return Err(Error::PathTraversal); + } + } + let result: Vec = relative_path_components + .drain((base_path_components.len())..) + .collect(); + Ok(Uri::from_iter(result.iter())) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0eb8df1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,50 @@ +use std::sync::LazyLock; + +use axum::Router; +use axum::routing::get; +use clap::Parser; +use tower_http::trace::TraceLayer; +use tracing::info; + +mod error; +mod filetype; +mod handlers; +mod myclap; +mod template; + +use handlers::handler; +use myclap::Cli; + +use crate::myclap::Config; + +pub static CONFIG: LazyLock = + LazyLock::new(|| Config::from_parser(Cli::parse()).expect("无法读取配置")); + +#[tokio::main] +async fn main() { + //日志 + tracing_subscriber::fmt() + .with_max_level(CONFIG.loglevel) + .init(); + + info!("Log Level: {}",CONFIG.loglevel.to_string()); + info!("Image Width: {}px",CONFIG.width); + + info!("Working Directory: [{}]", CONFIG.directory.display()); + // 设置工作目录 + std::env::set_current_dir(CONFIG.directory.clone()).expect("无法设置工作目录"); + + // 路由 + let app = Router::new() + .route("/", get(handler)) + .route("/{*filename}", get(handler)) + .layer(TraceLayer::new_for_http()); + + let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", CONFIG.port)) + .await + .unwrap(); + info!("Listen on 127.0.0.1:{}", CONFIG.port); + info!("Listen on 0.0.0.0:{}", CONFIG.port); + + axum::serve(listener, app).await.unwrap(); +} diff --git a/src/myclap.rs b/src/myclap.rs new file mode 100644 index 0000000..0cc3ace --- /dev/null +++ b/src/myclap.rs @@ -0,0 +1,54 @@ +use std::path::PathBuf; +use clap::Parser; +use tracing::Level; + +use crate::error::Error; + +#[derive(Parser)] +#[command(version, about, long_about = None)] +pub struct Cli { + /// 设置工作目录 + #[arg(short, long, value_name = "directory")] + pub directory: Option, + + /// 监听的端口号 + #[arg(short, long, value_name = "port",default_value_t = 3000)] + pub port: u16, + + /// 启用调试信息 + #[arg(long,short, action = clap::ArgAction::Count)] + pub loglevel: u8, + + /// 设置图片的宽度,该设置决定了瀑布流的宽度 + #[arg(long,short,default_value_t = 350)] + pub width: usize, + +} + +pub struct Config{ + pub directory:PathBuf, + pub port: u16, + pub loglevel: Level, + pub width: usize, +} + +impl Config { + pub fn from_parser(cli:Cli)->Result{ + let port = cli.port; + let workdir = match cli.directory { + Some(d) => d, + None => match std::env::current_dir() { + Ok(d) => d, + Err(_) => return Err(Error::CannotGetWorkDir), + }, + }; + let loglevel = match cli.loglevel { + 1 => tracing::Level::ERROR, + 2 => tracing::Level::WARN, + 0|3 => tracing::Level::INFO, + 4 => tracing::Level::DEBUG, + 5.. => tracing::Level::TRACE, + }; + Ok(Self { directory: workdir, port, loglevel: loglevel, width: cli.width }) + } +} \ No newline at end of file diff --git a/src/template.rs b/src/template.rs new file mode 100644 index 0000000..5a99e81 --- /dev/null +++ b/src/template.rs @@ -0,0 +1,18 @@ +use askama::Template; +#[derive(Template)] +#[template(path = "index.2.html")] +pub struct ImageFallTemplate { + pub imgs: Vec, + pub column_width:usize, +} +pub struct Imgs { + path: String, +} + +impl Imgs { + pub fn new(path: String) -> Self { + Self { path } + } +} + + diff --git a/templates/NoResources.html b/templates/NoResources.html new file mode 100644 index 0000000..6b77522 --- /dev/null +++ b/templates/NoResources.html @@ -0,0 +1,19 @@ + + + + + + No Resources + + + +

+ No Resources We Can Show You +

+ + \ No newline at end of file diff --git a/templates/index.1.html b/templates/index.1.html new file mode 100644 index 0000000..d0b4c44 --- /dev/null +++ b/templates/index.1.html @@ -0,0 +1,263 @@ + + + + + + + 动态获取图片尺寸的瀑布流 + + + + +
+
加载中,请稍候...
+ + + {% for img in imgs %} +
+
+ +
+
+ {% endfor %} +
+ + + + + + \ No newline at end of file diff --git a/templates/index.2.html b/templates/index.2.html new file mode 100644 index 0000000..1e0c1e4 --- /dev/null +++ b/templates/index.2.html @@ -0,0 +1,203 @@ + + + + + + + Momo + + + + + +
+ {% for img in imgs %} +
+ +
+ {% endfor %} +
+ + + + \ No newline at end of file