diff --git a/.github/workflows/release.yml.bak b/.github/workflows/release.yml.bak new file mode 100644 index 0000000..aebd6c0 --- /dev/null +++ b/.github/workflows/release.yml.bak @@ -0,0 +1,621 @@ +name: release + +on: + workflow_dispatch: + inputs: + release_tag: + description: "Release tag to publish, for example v0.3.7. Leave empty for artifact-only manual builds." + required: false + type: string + publish_release: + description: "Create/update the GitHub Release and Docker image. Requires release_tag." + required: false + default: false + type: boolean + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref_name || inputs.release_tag || github.run_id }} + cancel-in-progress: false + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + +jobs: + prepare: + name: Prepare release + runs-on: ubuntu-latest + outputs: + publish: ${{ steps.release.outputs.publish }} + tag: ${{ steps.release.outputs.tag }} + steps: + - name: Resolve release mode + id: release + shell: bash + run: | + publish="false" + tag="" + + # push tag 触发 → 自动发布;手动触发 → 读取 inputs + if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then + publish="true" + tag="${GITHUB_REF_NAME}" + else + publish="${{ inputs.publish_release }}" + tag="${{ inputs.release_tag }}" + fi + + # 发布时校验 tag 格式:v + 语义版本,允许 pre-release 后缀如 -rc1, +build123 + if [[ "${publish}" == "true" ]]; then + if [[ ! "${tag}" =~ ^v[0-9]+(\.[0-9]+)*([-+][0-9A-Za-z.-]+)?$ ]]; then + echo "release_tag must look like v0.3.7 when publishing" >&2 + exit 1 + fi + fi + + echo "publish=${publish}" >> "${GITHUB_OUTPUT}" + echo "tag=${tag}" >> "${GITHUB_OUTPUT}" + + build-linux-server: + name: Build Server - ${{ matrix.platform.release_for }} + needs: prepare + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + platform: + - release_for: linux_x86_64_musl + target: x86_64-unknown-linux-musl + binary_name: nodeget-server-linux-x86_64-musl + - release_for: linux_x86_64_gnu + target: x86_64-unknown-linux-gnu + binary_name: nodeget-server-linux-x86_64-gnu + - release_for: linux_aarch64_gnu + target: aarch64-unknown-linux-gnu + binary_name: nodeget-server-linux-aarch64-gnu + - release_for: linux_aarch64_musl + target: aarch64-unknown-linux-musl + binary_name: nodeget-server-linux-aarch64-musl + - release_for: linux_armv7_gnueabihf + target: armv7-unknown-linux-gnueabihf + binary_name: nodeget-server-linux-armv7-gnueabihf + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Rust + uses: ./.github/actions/setup-rust + with: + cache-shared-key: release-linux-${{ matrix.platform.target }} + + - name: Install cross + env: + CROSS_VERSION: v0.2.5 + GH_TOKEN: ${{ github.token }} + run: | + mkdir -p "${HOME}/.local/bin" + gh release download "${CROSS_VERSION}" \ + --repo cross-rs/cross \ + --pattern cross-x86_64-unknown-linux-gnu.tar.gz \ + -O - \ + | tar -xz -C "${HOME}/.local/bin" + echo "${HOME}/.local/bin" >> "${GITHUB_PATH}" + + - name: Build binary + run: cross build --package nodeget-server --target ${{ matrix.platform.target }} --profile minimal --jobs 32 + env: + CROSS_NO_WARNINGS: 0 + + - name: Compress binary + run: upx -9 ./target/${{ matrix.platform.target }}/minimal/nodeget-server + + - name: Rename binary + run: cp target/${{ matrix.platform.target }}/minimal/nodeget-server target/${{ matrix.platform.target }}/minimal/${{ matrix.platform.binary_name }} + + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: server-${{ matrix.platform.target }} + path: target/${{ matrix.platform.target }}/minimal/${{ matrix.platform.binary_name }} + if-no-files-found: error + retention-days: 14 + + build-linux-agent: + name: Build Agent - ${{ matrix.platform.release_for }} + needs: prepare + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + platform: + # ── x86_64 ── + - release_for: linux_x86_64_musl + target: x86_64-unknown-linux-musl + binary_name: nodeget-agent-linux-x86_64-musl + - release_for: linux_x86_64_gnu + target: x86_64-unknown-linux-gnu + binary_name: nodeget-agent-linux-x86_64-gnu + # Android:NDK 缺少 libunwind,必须用 build-std 编译 panic_abort 版 std + - release_for: linux_x86_64_android + target: x86_64-linux-android + binary_name: nodeget-agent-linux-x86_64-android + build_std: true + # ── i686 ── + - release_for: linux_i686_gnu + target: i686-unknown-linux-gnu + binary_name: nodeget-agent-linux-i686-gnu + - release_for: linux_i686_musl + target: i686-unknown-linux-musl + binary_name: nodeget-agent-linux-i686-musl + - release_for: linux_i686_android + target: i686-linux-android + binary_name: nodeget-agent-linux-i686-android + build_std: true + # ── aarch64 ── + - release_for: linux_aarch64_gnu + target: aarch64-unknown-linux-gnu + binary_name: nodeget-agent-linux-aarch64-gnu + - release_for: linux_aarch64_musl + target: aarch64-unknown-linux-musl + binary_name: nodeget-agent-linux-aarch64-musl + - release_for: linux_aarch64_android + target: aarch64-linux-android + binary_name: nodeget-agent-linux-aarch64-android + build_std: true + # ── arm (generic) ── + - release_for: linux_arm_gnueabi + target: arm-unknown-linux-gnueabi + binary_name: nodeget-agent-linux-arm-gnueabi + - release_for: linux_arm_gnueabihf + target: arm-unknown-linux-gnueabihf + binary_name: nodeget-agent-linux-arm-gnueabihf + - release_for: linux_arm_musleabi + target: arm-unknown-linux-musleabi + binary_name: nodeget-agent-linux-arm-musleabi + - release_for: linux_arm_musleabihf + target: arm-unknown-linux-musleabihf + binary_name: nodeget-agent-linux-arm-musleabihf + # Android:NDK 缺少 libunwind + - release_for: linux_arm_androideabi + target: arm-linux-androideabi + binary_name: nodeget-agent-linux-arm-androideabi + build_std: true + # ── armv7 ── + - release_for: linux_armv7_gnueabi + target: armv7-unknown-linux-gnueabi + binary_name: nodeget-agent-linux-armv7-gnueabi + - release_for: linux_armv7_gnueabihf + target: armv7-unknown-linux-gnueabihf + binary_name: nodeget-agent-linux-armv7-gnueabihf + - release_for: linux_armv7_musleabi + target: armv7-unknown-linux-musleabi + binary_name: nodeget-agent-linux-armv7-musleabi + - release_for: linux_armv7_musleabihf + target: armv7-unknown-linux-musleabihf + binary_name: nodeget-agent-linux-armv7-musleabihf + # Android:NDK 缺少 libunwind + - release_for: linux_armv7_androideabi + target: armv7-linux-androideabi + binary_name: nodeget-agent-linux-armv7-androideabi + build_std: true + # ── armv5te (tier 3):无预编译 std,必须 build-std ── + - release_for: linux_armv5te_gnueabi + target: armv5te-unknown-linux-gnueabi + binary_name: nodeget-agent-linux-armv5te-gnueabi + build_std: true + - release_for: linux_armv5te_musleabi + target: armv5te-unknown-linux-musleabi + binary_name: nodeget-agent-linux-armv5te-musleabi + build_std: true + # ── thumb ── + - release_for: linux_thumbv7neon_gnueabihf + target: thumbv7neon-unknown-linux-gnueabihf + binary_name: nodeget-agent-linux-thumbv7neon-gnueabihf + # Android:stable 无预编译 std,必须 nightly + build-std + - release_for: linux_thumbv7neon_androideabi + target: thumbv7neon-linux-androideabi + binary_name: nodeget-agent-linux-thumbv7neon-androideabi + build_std: true + # mips (tier 3):无预编译 std,必须 build-std + - release_for: linux_mips_gnu + target: mips-unknown-linux-gnu + binary_name: nodeget-agent-linux-mips-gnu + build_std: true + - release_for: linux_mips_musl + target: mips-unknown-linux-musl + binary_name: nodeget-agent-linux-mips-musl + build_std: true + # mipsel (tier 3) + - release_for: linux_mipsel_gnu + target: mipsel-unknown-linux-gnu + binary_name: nodeget-agent-linux-mipsel-gnu + build_std: true + - release_for: linux_mipsel_musl + target: mipsel-unknown-linux-musl + binary_name: nodeget-agent-linux-mipsel-musl + build_std: true + # mips64 (tier 3) + - release_for: linux_mips64_gnu + target: mips64-unknown-linux-gnuabi64 + binary_name: nodeget-agent-linux-mips64-gnu + build_std: true + - release_for: linux_mips64_musl + target: mips64-unknown-linux-muslabi64 + binary_name: nodeget-agent-linux-mips64-musl + build_std: true + # mips64el (tier 3) + - release_for: linux_mips64el_gnu + target: mips64el-unknown-linux-gnuabi64 + binary_name: nodeget-agent-linux-mips64el-gnu + build_std: true + - release_for: linux_mips64el_musl + target: mips64el-unknown-linux-muslabi64 + binary_name: nodeget-agent-linux-mips64el-musl + build_std: true + # powerpc + - release_for: linux_powerpc_gnu + target: powerpc-unknown-linux-gnu + binary_name: nodeget-agent-linux-powerpc-gnu + build_std: true + - release_for: linux_powerpc64_gnu + target: powerpc64-unknown-linux-gnu + binary_name: nodeget-agent-linux-powerpc64-gnu + no_upx: true + - release_for: linux_powerpc64le_gnu + target: powerpc64le-unknown-linux-gnu + binary_name: nodeget-agent-linux-powerpc64le-gnu + no_upx: true + # s390x (IBM Z) + - release_for: linux_s390x_gnu + target: s390x-unknown-linux-gnu + binary_name: nodeget-agent-linux-s390x-gnu + no_upx: true + # LoongArch64 + - release_for: linux_loongarch64_gnu + target: loongarch64-unknown-linux-gnu + binary_name: nodeget-agent-linux-loongarch64-gnu + build_std: true + - release_for: linux_loongarch64_musl + target: loongarch64-unknown-linux-musl + binary_name: nodeget-agent-linux-loongarch64-musl + build_std: true + # riscv64 + - release_for: linux_riscv64gc_gnu + target: riscv64gc-unknown-linux-gnu + binary_name: nodeget-agent-linux-riscv64gc-gnu + no_upx: true + - release_for: linux_riscv64gc_musl + target: riscv64gc-unknown-linux-musl + binary_name: nodeget-agent-linux-riscv64gc-musl + no_upx: true + build_std: true + # sparc64 + - release_for: linux_sparc64_gnu + target: sparc64-unknown-linux-gnu + binary_name: nodeget-agent-linux-sparc64-gnu + no_upx: true + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Rust + uses: ./.github/actions/setup-rust + with: + cache-shared-key: release-agent-linux-${{ matrix.platform.target }} + + - name: Install cross + env: + CROSS_VERSION: v0.2.5 + GH_TOKEN: ${{ github.token }} + run: | + mkdir -p "${HOME}/.local/bin" + gh release download "${CROSS_VERSION}" \ + --repo cross-rs/cross \ + --pattern cross-x86_64-unknown-linux-gnu.tar.gz \ + -O - \ + | tar -xz -C "${HOME}/.local/bin" + echo "${HOME}/.local/bin" >> "${GITHUB_PATH}" + + - name: Install nightly toolchain for build-std + if: ${{ matrix.platform.build_std }} + run: rustup install nightly && rustup component add rust-src --toolchain nightly + + # 清除 CI 缓存中残留的预编译 target std,防止与 -Z build-std 冲突 + # 产生 "duplicate lang item in crate 'core'" 错误 + - name: Clean cached target std for build-std + if: ${{ matrix.platform.build_std }} + run: rustup target remove --toolchain nightly ${{ matrix.platform.target }} 2>/dev/null || true + + - name: Build binary (stable) + if: ${{ !matrix.platform.build_std }} + run: cross build --package nodeget-agent --target ${{ matrix.platform.target }} --profile minimal --jobs 32 + env: + CROSS_NO_WARNINGS: 0 + + # cross v0.2.5 的 Cross.toml build-std 只支持布尔值(true), + # 不支持字符串 "std,panic_abort",因此 -Z build-std=std,panic_abort + # 必须在命令行显式传递以包含 panic_abort。 + # Cross.toml 中的 build-std = true 让 cross 跳过 rustup target add。 + - name: Build binary (nightly, build-std) + if: ${{ matrix.platform.build_std }} + run: cross +nightly build --package nodeget-agent --target ${{ matrix.platform.target }} --profile minimal --jobs 32 -Z build-std=std,panic_abort + env: + CROSS_NO_WARNINGS: 0 + + - name: Compress binary + if: ${{ !matrix.platform.no_upx }} + run: upx -9 ./target/${{ matrix.platform.target }}/minimal/nodeget-agent + + - name: Rename binary + run: cp target/${{ matrix.platform.target }}/minimal/nodeget-agent target/${{ matrix.platform.target }}/minimal/${{ matrix.platform.binary_name }} + + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: agent-${{ matrix.platform.target }} + path: target/${{ matrix.platform.target }}/minimal/${{ matrix.platform.binary_name }} + if-no-files-found: error + retention-days: 14 + + build-windows-server: + name: Build Server - ${{ matrix.platform.release_for }} + needs: prepare + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + platform: + - release_for: windows_x86_64 + target: x86_64-pc-windows-msvc + binary_name: nodeget-server-windows-x86_64.exe + - release_for: windows_aarch64 + target: aarch64-pc-windows-msvc + binary_name: nodeget-server-windows-aarch64.exe + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Rust + uses: ./.github/actions/setup-rust + with: + targets: ${{ matrix.platform.target }} + cache-shared-key: release-windows-${{ matrix.platform.target }} + + - name: Build binary + run: cargo build --package nodeget-server --target ${{ matrix.platform.target }} --profile minimal --jobs 32 + env: + CROSS_NO_WARNINGS: 0 + + - name: Rename binary + run: copy target\${{ matrix.platform.target }}\minimal\nodeget-server.exe target\${{ matrix.platform.target }}\minimal\${{ matrix.platform.binary_name }} + + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: server-${{ matrix.platform.target }} + path: target/${{ matrix.platform.target }}/minimal/${{ matrix.platform.binary_name }} + if-no-files-found: error + retention-days: 14 + + build-windows-agent: + name: Build Agent - ${{ matrix.platform.release_for }} + needs: prepare + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + platform: + - release_for: windows_x86_64 + target: x86_64-pc-windows-msvc + binary_name: nodeget-agent-windows-x86_64.exe + - release_for: windows_aarch64 + target: aarch64-pc-windows-msvc + binary_name: nodeget-agent-windows-aarch64.exe + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Rust + uses: ./.github/actions/setup-rust + with: + targets: ${{ matrix.platform.target }} + cache-shared-key: release-agent-windows-${{ matrix.platform.target }} + + - name: Build binary + run: cargo build --package nodeget-agent --target ${{ matrix.platform.target }} --profile minimal --jobs 32 + env: + CROSS_NO_WARNINGS: 0 + + - name: Rename binary + run: copy target\${{ matrix.platform.target }}\minimal\nodeget-agent.exe target\${{ matrix.platform.target }}\minimal\${{ matrix.platform.binary_name }} + + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: agent-${{ matrix.platform.target }} + path: target/${{ matrix.platform.target }}/minimal/${{ matrix.platform.binary_name }} + if-no-files-found: error + retention-days: 14 + + build-macos-server: + name: Build Server - ${{ matrix.platform.release_for }} + needs: prepare + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + platform: + - release_for: macos_aarch64 + target: aarch64-apple-darwin + binary_name: nodeget-server-macos-aarch64 + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Rust + uses: ./.github/actions/setup-rust + with: + targets: ${{ matrix.platform.target }} + cache-shared-key: release-macos-${{ matrix.platform.target }} + + - name: Build binary + run: cargo build --package nodeget-server --target ${{ matrix.platform.target }} --profile minimal --jobs 32 + env: + CROSS_NO_WARNINGS: 0 + + - name: Rename binary + run: cp target/${{ matrix.platform.target }}/minimal/nodeget-server target/${{ matrix.platform.target }}/minimal/${{ matrix.platform.binary_name }} + + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: server-${{ matrix.platform.target }} + path: target/${{ matrix.platform.target }}/minimal/${{ matrix.platform.binary_name }} + if-no-files-found: error + retention-days: 14 + + build-macos-agent: + name: Build Agent - ${{ matrix.platform.release_for }} + needs: prepare + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + platform: + - release_for: macos_aarch64 + target: aarch64-apple-darwin + binary_name: nodeget-agent-macos-aarch64 + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Rust + uses: ./.github/actions/setup-rust + with: + targets: ${{ matrix.platform.target }} + cache-shared-key: release-agent-macos-${{ matrix.platform.target }} + + - name: Build binary + run: cargo build --package nodeget-agent --target ${{ matrix.platform.target }} --profile minimal --jobs 32 + env: + CROSS_NO_WARNINGS: 0 + + - name: Rename binary + run: cp target/${{ matrix.platform.target }}/minimal/nodeget-agent target/${{ matrix.platform.target }}/minimal/${{ matrix.platform.binary_name }} + + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: agent-${{ matrix.platform.target }} + path: target/${{ matrix.platform.target }}/minimal/${{ matrix.platform.binary_name }} + if-no-files-found: error + retention-days: 14 + + publish-release: + name: Publish GitHub Release + needs: + - prepare + - build-linux-server + - build-windows-server + - build-macos-server + - build-linux-agent + - build-windows-agent + - build-macos-agent + if: needs.prepare.outputs.publish == 'true' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Download artifacts + uses: actions/download-artifact@v8 + with: + path: dist + merge-multiple: true + + - name: Upload release assets + uses: softprops/action-gh-release@v3 + with: + tag_name: ${{ needs.prepare.outputs.tag }} + name: NodeGet ${{ needs.prepare.outputs.tag }} + make_latest: true + files: dist/* + + publish-docker-server: + name: Publish Docker image + needs: + - prepare + - build-linux-server + if: needs.prepare.outputs.publish == 'true' + runs-on: ubuntu-latest + permissions: + actions: write + contents: read + packages: write + env: + REGISTRY: docker.io + IMAGE_NAME: genshinmc/nodeget + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Check Docker Hub credentials + shell: bash + run: | + test -n "${DOCKERHUB_USERNAME}" + test -n "${DOCKERHUB_TOKEN}" + + - name: Download Linux musl binaries + uses: actions/download-artifact@v8 + with: + pattern: server-*-musl + path: downloads + merge-multiple: true + + - name: Prepare bin directory + shell: bash + run: | + mkdir -p bin + cp downloads/nodeget-server-linux-x86_64-musl bin/nodeget-server-amd64 + cp downloads/nodeget-server-linux-aarch64-musl bin/nodeget-server-arm64 + chmod 0755 bin/nodeget-server-amd64 bin/nodeget-server-arm64 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Login to Docker Hub + uses: docker/login-action@v4 + with: + registry: ${{ env.REGISTRY }} + username: ${{ env.DOCKERHUB_USERNAME }} + password: ${{ env.DOCKERHUB_TOKEN }} + + - name: Docker metadata + id: meta + uses: docker/metadata-action@v6 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest + type=raw,value=${{ needs.prepare.outputs.tag }} + + - name: Build and push + uses: docker/build-push-action@v7 + with: + context: . + file: ./Dockerfile + target: runtime + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index d53da41..e9d22a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2667,7 +2667,7 @@ dependencies = [ [[package]] name = "ng-config" -version = "0.5.10" +version = "0.5.11" dependencies = [ "anyhow", "jsonrpsee", @@ -2683,10 +2683,11 @@ dependencies = [ [[package]] name = "ng-core" -version = "0.5.10" +version = "0.5.11" dependencies = [ "anyhow", "libc", + "portable-atomic", "rand 0.10.1", "serde", "serde_json", @@ -2697,7 +2698,7 @@ dependencies = [ [[package]] name = "ng-crontab" -version = "0.5.10" +version = "0.5.11" dependencies = [ "anyhow", "chrono", @@ -2719,7 +2720,7 @@ dependencies = [ [[package]] name = "ng-db" -version = "0.5.10" +version = "0.5.11" dependencies = [ "anyhow", "hex", @@ -2735,7 +2736,7 @@ dependencies = [ [[package]] name = "ng-infra" -version = "0.5.10" +version = "0.5.11" dependencies = [ "anyhow", "ng-core", @@ -2747,7 +2748,7 @@ dependencies = [ [[package]] name = "ng-js-runtime" -version = "0.5.10" +version = "0.5.11" dependencies = [ "anyhow", "llrt_buffer", @@ -2769,7 +2770,7 @@ dependencies = [ [[package]] name = "ng-js-worker" -version = "0.5.10" +version = "0.5.11" dependencies = [ "anyhow", "base64", @@ -2787,7 +2788,7 @@ dependencies = [ [[package]] name = "ng-kv" -version = "0.5.10" +version = "0.5.11" dependencies = [ "anyhow", "jsonrpsee", @@ -2810,7 +2811,7 @@ dependencies = [ [[package]] name = "ng-monitoring" -version = "0.5.10" +version = "0.5.11" dependencies = [ "anyhow", "futures-util", @@ -2831,7 +2832,7 @@ dependencies = [ [[package]] name = "ng-static" -version = "0.5.10" +version = "0.5.11" dependencies = [ "anyhow", "arc-swap", @@ -2852,7 +2853,7 @@ dependencies = [ [[package]] name = "ng-task" -version = "0.5.10" +version = "0.5.11" dependencies = [ "anyhow", "futures-util", @@ -2870,7 +2871,7 @@ dependencies = [ [[package]] name = "ng-terminal" -version = "0.5.10" +version = "0.5.11" dependencies = [ "anyhow", "axum", @@ -2889,7 +2890,7 @@ dependencies = [ [[package]] name = "ng-token" -version = "0.5.10" +version = "0.5.11" dependencies = [ "anyhow", "hex", @@ -2939,7 +2940,7 @@ checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" [[package]] name = "nodeget-agent" -version = "0.5.10" +version = "0.5.11" dependencies = [ "anyhow", "base64", @@ -2976,7 +2977,7 @@ dependencies = [ [[package]] name = "nodeget-server" -version = "0.5.10" +version = "0.5.11" dependencies = [ "anyhow", "arc-swap", @@ -3460,6 +3461,12 @@ dependencies = [ "pnet_macros_support", ] +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + [[package]] name = "portable-pty" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 4363a46..c2a0d87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ members = [ resolver = "2" [workspace.package] -version = "0.5.10" +version = "0.5.11" license = "AGPL-3" edition = "2024" repository = "https://github.com/NodeSeekDev/NodeGet" @@ -63,6 +63,7 @@ tower = { version = "0.5.3", default-features = false } axum-server = { version = "0.8.0", features = ["tls-rustls-no-provider"] } dhat = { version = "0.3.3"} arc-swap = { version = "1.9", default-features = false } +portable-atomic = { version = "1.13.1", default-features = false, features = ["fallback"] } # Internal crates ng-core = { path = "crates/ng-core" } diff --git a/Cross.toml b/Cross.toml index e7489f2..6b82679 100644 --- a/Cross.toml +++ b/Cross.toml @@ -1,17 +1,61 @@ -[target.arm-unknown-linux-gnueabi] -rustflags = ["-C", "link-arg=-latomic"] - -[target.arm-unknown-linux-gnueabihf] -rustflags = ["-C", "link-arg=-latomic"] - -[target.armv7-unknown-linux-gnueabi] -rustflags = ["-C", "link-arg=-latomic"] - -[target.armv7-unknown-linux-gnueabihf] -rustflags = ["-C", "link-arg=-latomic"] - -[target.armv7-unknown-linux-musleabi] -rustflags = ["-C", "link-arg=-latomic"] - -[target.armv7-unknown-linux-musleabihf] -rustflags = ["-C", "link-arg=-latomic"] +## Android +#[target.x86_64-linux-android] +#build-std = true +# +#[target.i686-linux-android] +#build-std = true +# +#[target.aarch64-linux-android] +#build-std = true +# +#[target.arm-linux-androideabi] +#build-std = true +# +#[target.armv7-linux-androideabi] +#build-std = true +# +#[target.thumbv7neon-linux-androideabi] +#build-std = true +# +## ARM 老款 (tier 3):无预编译 std,必须从源码构建 +#[target.armv5te-unknown-linux-gnueabi] +#build-std = true +# +#[target.armv5te-unknown-linux-musleabi] +#build-std = true +# +## 极老 x86 (tier 3) +#[target.i586-unknown-linux-gnu] +#build-std = true +# +#[target.i586-unknown-linux-musl] +#build-std = true +# +## 32 位 PowerPC (tier 3) +#[target.powerpc-unknown-linux-gnu] +#build-std = true +# +## MIPS 系列 (tier 3):无预编译 std,必须从源码构建 +#[target.mips-unknown-linux-gnu] +#build-std = true +# +#[target.mips-unknown-linux-musl] +#build-std = true +# +#[target.mipsel-unknown-linux-gnu] +#build-std = true +# +#[target.mipsel-unknown-linux-musl] +#build-std = true +# +#[target.mips64-unknown-linux-gnuabi64] +#build-std = true +# +#[target.mips64-unknown-linux-muslabi64] +#build-std = true +# +#[target.mips64el-unknown-linux-gnuabi64] +#build-std = true +# +#[target.mips64el-unknown-linux-muslabi64] +#build-std = true diff --git a/crates/ng-core/Cargo.toml b/crates/ng-core/Cargo.toml index 10d68d1..d4f1349 100644 --- a/crates/ng-core/Cargo.toml +++ b/crates/ng-core/Cargo.toml @@ -14,6 +14,9 @@ uuid = { workspace = true } rand = { workspace = true } tracing = { workspace = true } +# 32 位平台兼容:portable-atomic 在不支持原生 64 位原子操作的平台提供回退实现 +portable-atomic = { workspace = true } + # for-server / for-agent feature dependencies libc = { version = "0.2.175", default-features = false, optional = true } diff --git a/crates/ng-core/src/utils/mod.rs b/crates/ng-core/src/utils/mod.rs index b0d6ace..9c14de4 100644 --- a/crates/ng-core/src/utils/mod.rs +++ b/crates/ng-core/src/utils/mod.rs @@ -8,7 +8,7 @@ use rand::distr::Alphanumeric; use rand::{RngExt, rng}; use serde::Deserialize; use serde::Serialize; -use std::sync::atomic::{AtomicI64, Ordering}; +use portable_atomic::{AtomicI64, Ordering}; #[cfg(feature = "for-server")] pub mod error_message; diff --git a/crates/ng-crontab/src/rpc/crontab/mod.rs b/crates/ng-crontab/src/rpc/crontab/mod.rs index 3ce035c..6a2189c 100644 --- a/crates/ng-crontab/src/rpc/crontab/mod.rs +++ b/crates/ng-crontab/src/rpc/crontab/mod.rs @@ -22,20 +22,22 @@ mod set_enable; /// 校验 crontab 名称合法性。 /// -/// 只允许字母、数字、下划线、短横线。禁止路径分隔符及控制字符。 +/// 允许 Unicode 字母、数字、下划线、短横线(支持中文等多语言命名)。 +/// 禁止路径分隔符、控制字符及可能造成问题的特殊符号。 fn validate_name(name: &str) -> anyhow::Result<()> { if name.is_empty() { return Err(ng_core::error::NodegetError::InvalidInput("name cannot be empty".to_owned()).into()); } - if name.len() > 128 { + if name.chars().count() > 128 { return Err(ng_core::error::NodegetError::InvalidInput("name too long (max 128 chars)".to_owned()).into()); } - let valid = name - .chars() - .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-'); + let valid = name.chars().all(|c| { + // 允许:Unicode 字母/数字(含中文)、下划线、短横线 + c.is_alphanumeric() || c == '_' || c == '-' + }); if !valid { return Err(ng_core::error::NodegetError::InvalidInput( - "name contains invalid characters (only [A-Za-z0-9_-] are allowed)".to_owned(), + "name contains invalid characters (alphanumeric, underscore, hyphen allowed)".to_owned(), ) .into()); } @@ -208,6 +210,16 @@ mod tests { assert!(validate_name(&name).is_ok()); } + #[test] + fn validate_name_accepts_chinese() { + assert!(validate_name("电信ping").is_ok()); + } + + #[test] + fn validate_name_accepts_mixed_chinese_ascii() { + assert!(validate_name("定时任务_1").is_ok()); + } + // ── validate_name: empty ────────────────────────────────────── #[test] @@ -259,8 +271,9 @@ mod tests { } #[test] - fn validate_name_rejects_unicode() { - assert!(validate_name("定时任务").is_err()); + fn validate_name_rejects_unicode_symbols() { + // Unicode 符号(非字母数字)仍应被拒绝 + assert!(validate_name("任务❌").is_err()); } #[test] diff --git a/crates/ng-token/src/super_token.rs b/crates/ng-token/src/super_token.rs index 464948c..4ce1d7b 100644 --- a/crates/ng-token/src/super_token.rs +++ b/crates/ng-token/src/super_token.rs @@ -27,7 +27,7 @@ use crate::hash_to_bytes; /// - 错误:数据库插入失败(通常因 ID=1 唯一约束冲突) async fn insert_new_super_token( db: &sea_orm::DatabaseConnection, -) -> anyhow::Result<(String, String)> { +) -> Result<(String, String), sea_orm::DbErr> { let token_key = generate_random_string(16); let token_secret = generate_random_string(32); let full_token = format!("{token_key}:{token_secret}"); @@ -50,12 +50,7 @@ async fn insert_new_super_token( password_hash: Set(Some(password_hash)), }; - token::Entity::insert(super_token_model) - .exec(db) - .await - .map_err(|e| { - NodegetError::DatabaseError(format!("Failed to initialize super token: {e}")) - })?; + token::Entity::insert(super_token_model).exec(db).await?; debug!(target: "token", "Super token inserted into database"); Ok((full_token, raw_password)) @@ -82,15 +77,22 @@ pub async fn generate_super_token() -> anyhow::Result> } Ok(Some(result)) } - Err(e) => { - // 判断是否为唯一约束冲突(SQLite 和 PostgreSQL 的错误消息不同) - let error_msg = format!("{e}"); - if error_msg.contains("UNIQUE constraint failed") || error_msg.contains("duplicate key") - { + Err(db_err) => { + // 使用 SeaORM 的 sql_err() 精确判断唯一约束冲突, + // 不依赖错误消息字符串(PostgreSQL 中文 locale 下消息不含 "duplicate key") + let is_unique_violation = matches!( + db_err.sql_err(), + Some(sea_orm::SqlErr::UniqueConstraintViolation(_)) + ); + + if is_unique_violation { debug!(target: "token", "Super token already exists, skipping generation"); Ok(None) } else { - Err(e) + Err(NodegetError::DatabaseError(format!( + "Failed to initialize super token: {db_err}" + )) + .into()) } } }