Consolidate workspace deps and migrate sha1 to sha2
CI / verify (push) Has been cancelled

This commit is contained in:
2026-05-07 21:11:14 +08:00
parent 9146e5b8ec
commit df80876f60
41 changed files with 949 additions and 342 deletions
+8
View File
@@ -16,6 +16,14 @@
---
## 2026-05-07 server-rs Cargo 依赖集中到 workspace
- 背景:`server-rs` 多 crate 已稳定成 DDD workspace,成员 `Cargo.toml` 中重复散写第三方版本和本地 path 依赖,升级 SpacetimeDB SDK、`serde``reqwest``tokio` 等依赖时容易漂移。
- 决策:`server-rs/Cargo.toml``[workspace.dependencies]` 统一维护第三方依赖版本和 workspace 内部 crate path;成员 crate 默认使用 `{ workspace = true }`,只保留自身 feature、optional 或 target-specific 差异;不再新增 `sha1`OSS 与阿里云 OpenAPI 签名统一走 `sha2::Sha256` 对应的 V4/V3 口径。
- 影响范围:`server-rs/Cargo.toml`、所有 `server-rs/crates/*/Cargo.toml``platform-oss``platform-auth`、后续新增 Rust crate 或新增 Rust 依赖的开发流程。
- 验证方式:修改 Cargo 配置后先执行 `cargo metadata --manifest-path server-rs\Cargo.toml --format-version 1 --no-deps`,再按影响范围执行 `cargo check`、DDD 边界检查和编码检查。
- 关联文档:`docs/technical/RUST_WORKSPACE_DEPENDENCY_CONSOLIDATION_2026-05-07.md`
## 2026-05-06 Maincloud 历史残留引用禁止再使用
- 背景:项目已经全面移除 Maincloud 运行口径,但历史脚本、测试名和文档仍可能让后续开发误用 `api-server:maincloud``GENARRATIVE_SPACETIME_MAINCLOUD_*`
@@ -81,6 +81,10 @@ DDD 分层边界以 `docs/technical/SERVER_RS_DDD_FULL_REFACTOR_2026-04-28.md`
注意:`server-rs` 的默认 `cargo build` 只构建 `crates/api-server`SpacetimeDB 模块产物继续走 `spacetime build` / 发布链路。
Cargo 依赖口径:第三方依赖版本和 workspace 内部 crate path 统一维护在 `server-rs/Cargo.toml``[workspace.dependencies]`,成员 crate 默认继承 workspace 依赖,只保留自身 `features``optional` 或 target-specific 差异。
Rust 加密摘要依赖口径:新代码不再引入 `sha1`OSS V4 签名、阿里云 OpenAPI V3 签名和 refresh session token 摘要统一使用 `sha2::Sha256`
## SpacetimeDB 表域总览
`docs/technical/SPACETIMEDB_TABLE_CATALOG.md` 为持续维护入口。当前表域包括:
@@ -135,4 +139,5 @@ DDD 分层边界以 `docs/technical/SERVER_RS_DDD_FULL_REFACTOR_2026-04-28.md`
- 契约与路由矩阵:`docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md`
- SpacetimeDB 表结构变更约束:`docs/technical/SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md`
- SpacetimeDB 表目录:`docs/technical/SPACETIMEDB_TABLE_CATALOG.md`
- Rust workspace 依赖集中配置:`docs/technical/RUST_WORKSPACE_DEPENDENCY_CONSOLIDATION_2026-05-07.md`
- 生产部署计划:`docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md`
@@ -104,9 +104,11 @@
1. 发送验证码调用 `SendSmsVerifyCode`
2. 校验验证码调用 `CheckSmsVerifyCode`
3. 使用阿里云 RPC 签名口径:
- `SignatureMethod=HMAC-SHA1`
- `SignatureVersion=1.0`
3. 使用阿里云 OpenAPI V3 请求头签名口径:
- `Authorization: ACS3-HMAC-SHA256 ...`
- `x-acs-action`
- `x-acs-version`
- `x-acs-content-sha256`
4. 当前仍只支持中国大陆手机号。
## 7. 状态与快照
+1
View File
@@ -4,6 +4,7 @@
## 文档列表
- [RUST_WORKSPACE_DEPENDENCY_CONSOLIDATION_2026-05-07.md](./RUST_WORKSPACE_DEPENDENCY_CONSOLIDATION_2026-05-07.md):记录 `server-rs` Cargo 依赖集中配置口径,第三方版本和 workspace 内部 crate path 统一维护在根 `server-rs/Cargo.toml`,成员 crate 只保留 feature/optional 差异。
- [API_SERVER_EXTERNAL_SERVICE_ENV_CONFIG_2026-05-07.md](./API_SERVER_EXTERNAL_SERVICE_ENV_CONFIG_2026-05-07.md):冻结 api-server 外部服务配置边界,公共服务 URL 可保留代码默认值,非公共模型名和私有网关 URL 统一通过环境变量注入。
- [PROFILE_TASK_AND_TRACKING_SYSTEM_2026-05-03.md](./PROFILE_TASK_AND_TRACKING_SYSTEM_2026-05-03.md):冻结个人任务与埋点系统首版方案,明确 `tracking_event``tracking_daily_stat``profile_task_config`、任务进度、领奖记录和光点钱包流水的边界。
- [SQUARE_HOLE_IMAGE_SLOT_AND_RUNTIME_INTERACTION_FIX_2026-05-06.md](./SQUARE_HOLE_IMAGE_SLOT_AND_RUNTIME_INTERACTION_FIX_2026-05-06.md):记录方洞挑战结果页图片槽位局部生成、洞口图历史素材、运行态拖拽与点击投放交互的修正口径。
@@ -0,0 +1,55 @@
# Rust workspace 依赖集中配置记录
日期:`2026-05-07`
## 1. 背景
`server-rs` workspace 已经包含 `api-server``spacetime-module``spacetime-client`、多个 `module-*` 领域 crate、`platform-*` 适配 crate 和共享 crate。随着 DDD 收口推进,成员 `Cargo.toml` 中重复散写了第三方 crate 版本和本地 path 依赖,后续升级 `serde``reqwest``tokio``time`、SpacetimeDB SDK 或内部 crate 路径时容易出现漂移。
本次只做 Cargo 配置收敛,不改变业务代码、表结构、reducer/procedure 签名、HTTP contract 或前端绑定。
## 2. 配置规则
1. 共享第三方依赖版本统一维护在 `server-rs/Cargo.toml``[workspace.dependencies]`
2. workspace 内部 crate 的 `path` 也统一维护在根 `server-rs/Cargo.toml`
3. 成员 crate 默认使用 `{ workspace = true }` 继承依赖。
4. 成员 crate 只保留自身需要表达的差异,例如 `features``optional = true` 或 target-specific dependency。
5. 需要关闭 default features 的依赖,应优先在 workspace 根依赖中声明;成员 crate 不再重复覆盖同一项。
6. `module-assets` 这类有默认服务端 feature 的领域 crate,在 workspace 根内按 `default-features = false` 维护;需要服务端 OSS/HTTP 能力的 adapter crate 显式启用 `features = ["server-service"]`
## 3. 本次收敛范围
已上提到 workspace 根的依赖包括:
1. 本地路径依赖:`module-*``platform-*``shared-*``spacetime-client`
2. 常用第三方依赖:`serde``serde_json``serde_urlencoded``reqwest``tokio``time``tracing``base64``hmac``sha2``uuid``url` 等。
3. SpacetimeDB 相关依赖:`spacetimedb``spacetimedb-sdk``spacetimedb-lib`
`spacetimedb-lib` 在 workspace 根统一关闭 default features`spacetime-module` 只继承并补充 `features = ["serde"]`。这样避免成员 crate 尝试覆盖 workspace default-feature 设定导致 manifest 解析失败。
阿里云 OSS 相关签名不再依赖不推荐的 `sha1` crate,统一使用 `sha2::Sha256`
1. 浏览器直传 ticket 使用 OSS V4 表单签名字段:`x-oss-signature-version=OSS4-HMAC-SHA256``x-oss-credential``x-oss-date``x-oss-signature`
2. 服务端 OSS 读写请求和测试辅助签名统一使用 `OSS4-HMAC-SHA256` Authorization。
3. 阿里云短信 OpenAPI 请求统一使用 `ACS3-HMAC-SHA256` 请求头签名,不再在表单中传旧 `SignatureMethod=HMAC-SHA1` / `SignatureVersion=1.0`
## 4. 不在本次范围
1. 不新增或删除 crate。
2. 不修改 `server-rs` workspace `members` / `default-members` 语义。
3. 不修改 SpacetimeDB 表、reducer、procedure、migration 白名单或生成绑定。
4. 不改变 `module-*` 的 DDD 依赖方向。
## 5. 验收口径
配置改动后至少执行:
```powershell
cargo metadata --manifest-path server-rs\Cargo.toml --format-version 1 --no-deps
cargo check -p api-server --manifest-path server-rs\Cargo.toml
cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml
npm.cmd run check:server-rs-ddd
npm.cmd run check:encoding -- docs/technical/RUST_WORKSPACE_DEPENDENCY_CONSOLIDATION_2026-05-07.md docs/technical/README.md server-rs/README.md .hermes/shared-memory/decision-log.md .hermes/shared-memory/project-overview.md
```
若仅改 Cargo 依赖配置且未触碰 API smoke 相关代码,不强制启动 `npm run api-server`;若后续改动同时涉及 API 路由、SpacetimeDB facade 或运行时行为,仍按 `AGENTS.md` 和 DDD 文档执行后端 smoke。
@@ -1,6 +1,6 @@
pipeline {
agent {
label 'built-in && windows'
label 'windows'
}
options {
@@ -16,7 +16,7 @@ pipeline {
CARGO_INCREMENTAL = '0'
RUSTC_WRAPPER = 'sccache'
SCCACHE_DIR = '${env.USERPROFILE}\\.cache\\sccache-stdb-module'
SCCACHE_CACHE_SIZE = '30G'
SCCACHE_CACHE_SIZE = '30G'o
}
parameters {
+3
View File
@@ -1,3 +1,6 @@
[build]
rustc-wrapper = "sccache"
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=lld"]
+31 -34
View File
@@ -75,7 +75,6 @@ dependencies = [
"dotenvy",
"hmac",
"http-body-util",
"httpdate",
"image",
"module-ai",
"module-assets",
@@ -98,7 +97,7 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
"sha1",
"sha2",
"shared-contracts",
"shared-kernel",
"shared-logging",
@@ -1872,14 +1871,13 @@ name = "platform-auth"
version = "0.1.0"
dependencies = [
"argon2",
"base64 0.22.1",
"hmac",
"jsonwebtoken",
"rand_core 0.6.4",
"reqwest",
"serde",
"serde_json",
"sha1",
"serde_urlencoded",
"sha2",
"shared-kernel",
"time",
@@ -1906,11 +1904,10 @@ version = "0.1.0"
dependencies = [
"base64 0.22.1",
"hmac",
"httpdate",
"reqwest",
"serde",
"serde_json",
"sha1",
"sha2",
"time",
"tokio",
]
@@ -2730,9 +2727,9 @@ dependencies = [
[[package]]
name = "spacetimedb"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "591f9068644aab6808e7612a869dedde7eeb26df78027a19bc9dc597cc649678"
checksum = "1306cc3a9ed9c89f43b263614a529357cc53a067e3d06c1cbb485e3b577b118b"
dependencies = [
"anyhow",
"bytemuck",
@@ -2753,9 +2750,9 @@ dependencies = [
[[package]]
name = "spacetimedb-bindings-macro"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f68bf4810d838be622c13efd4cd64e0a9ce8cd340deaa730f0c92caee845f9"
checksum = "51567ec01cd323438a00c134c16f26ffcde5f9dbe6a42a52e54578285bf49d73"
dependencies = [
"heck 0.4.1",
"humantime",
@@ -2767,18 +2764,18 @@ dependencies = [
[[package]]
name = "spacetimedb-bindings-sys"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c2fe9f4124a599c9deae8f8231be3ae5a49bc5b2eef5e04c04b2632cf4cc0b4"
checksum = "3b40fa1bea26664085febe2b4455568c8b47dea2cb0245406b27e30963df2ba1"
dependencies = [
"spacetimedb-primitives",
]
[[package]]
name = "spacetimedb-client-api-messages"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02a18c2e145f61ad498f8094a2231e09f8d39a3dde09defa716075dbcb8c7e85"
checksum = "dfc9eeb20a555bad07029cbee4efe3a305cb5c1e40e21a07cbbbbed16a106014"
dependencies = [
"bytes",
"bytestring",
@@ -2798,9 +2795,9 @@ dependencies = [
[[package]]
name = "spacetimedb-data-structures"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4035c17ddbfc8c49a659bd6fb265b0a2a11115d1b4ad1963bccfad75cdfb4b"
checksum = "748fd5850a757823c5b8948065d9e4dc5092968a051aa3f34f170e91d95e493b"
dependencies = [
"ahash",
"crossbeam-queue",
@@ -2813,9 +2810,9 @@ dependencies = [
[[package]]
name = "spacetimedb-lib"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "672c0dd16feced67155a0dee7bd38d30f7725321c8177cb871a21c3d8749ae97"
checksum = "5612611d09d358f535438275d2a0d6a5e2fa56fa583dcfdbeddd623974df1d5e"
dependencies = [
"anyhow",
"bitflags",
@@ -2837,9 +2834,9 @@ dependencies = [
[[package]]
name = "spacetimedb-memory-usage"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c00614eb981354ee6b31661ec47002d3fc274f9d4543279dd6ee8692cdd8266"
checksum = "1c3a0d08fc5d8688a47e3ffcb803275519663b7ea1fba7ad25e608182de4ec6d"
dependencies = [
"decorum",
"ethnum",
@@ -2847,9 +2844,9 @@ dependencies = [
[[package]]
name = "spacetimedb-metrics"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef6f7f6b24932505a696b75b7e5e60646ab1d76eeb8d2f95f04948562c965b5e"
checksum = "ca2d647201339aa17ba438a07463e96ed64ba214fb0c182588e262b055efa7f3"
dependencies = [
"arrayvec",
"itertools",
@@ -2859,9 +2856,9 @@ dependencies = [
[[package]]
name = "spacetimedb-primitives"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dba5d7497d54aa8d4254f78a0bef12606bb05e62f8dea8b69abc9b241508e8b7"
checksum = "9b668b51e7318207ae7eebcd4cae0c5d43bf713e7f229ac309ea2614a486ffde"
dependencies = [
"bitflags",
"either",
@@ -2873,18 +2870,18 @@ dependencies = [
[[package]]
name = "spacetimedb-query-builder"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d04c6e41e05273f14405ac6f429477626677d46528b561a509b7b78b45128f30"
checksum = "0186b1a2b3bf25bdd0f2676b61801fd754013ca6a58e1e24cc5148945388bc9d"
dependencies = [
"spacetimedb-lib",
]
[[package]]
name = "spacetimedb-sats"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfde33ec86d80881da8b00c42096bf0382bef8e1bc35e9b6faaa42d77cbf503c"
checksum = "11780ed69f178bf3784b7599da5171450e4b7ac6fd66b79e2e1861c867cef1a6"
dependencies = [
"anyhow",
"arrayvec",
@@ -2915,9 +2912,9 @@ dependencies = [
[[package]]
name = "spacetimedb-schema"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b03b34a38bd39f3f60a0687efafb942355bd9f6026b88a38c7c9ec904e944f1"
checksum = "1e4e9f8aa596e0e7034f0c8b3649d3fa3cc7bde340761519c3a3c60f10ec8888"
dependencies = [
"anyhow",
"convert_case 0.6.0",
@@ -2946,9 +2943,9 @@ dependencies = [
[[package]]
name = "spacetimedb-sdk"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e7302851fec72929ffef976125f51971b0ec76be2730e27705a8e544f2ce159"
checksum = "41e82f20034b8aaeaa081871b07895aab45be1f0fc35e114ab64ae8e7e5c1a54"
dependencies = [
"anymap3",
"base64 0.21.7",
@@ -2978,9 +2975,9 @@ dependencies = [
[[package]]
name = "spacetimedb-sql-parser"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7cbb9a837ac5f1ddb0cfb745159dea276dcf456244452f5a90684e5184f1f31"
checksum = "ec5c77a2d4e3f42ede59598c56cb81a0fe54fd1974e2707f7140d1d5f41d08a7"
dependencies = [
"derive_more",
"spacetimedb-lib",
+55 -1
View File
@@ -42,8 +42,62 @@ version = "0.1.0"
license = "UNLICENSED"
[workspace.dependencies]
# 本地 workspace crate 路径统一在这里维护,成员 crate 只声明 feature 差异。
module-ai = { path = "crates/module-ai", default-features = false }
module-assets = { path = "crates/module-assets", default-features = false }
module-auth = { path = "crates/module-auth", default-features = false }
module-big-fish = { path = "crates/module-big-fish", default-features = false }
module-combat = { path = "crates/module-combat", default-features = false }
module-custom-world = { path = "crates/module-custom-world", default-features = false }
module-inventory = { path = "crates/module-inventory", default-features = false }
module-match3d = { path = "crates/module-match3d", default-features = false }
module-npc = { path = "crates/module-npc", default-features = false }
module-progression = { path = "crates/module-progression", default-features = false }
module-puzzle = { path = "crates/module-puzzle", default-features = false }
module-quest = { path = "crates/module-quest", default-features = false }
module-runtime = { path = "crates/module-runtime", default-features = false }
module-runtime-item = { path = "crates/module-runtime-item", default-features = false }
module-runtime-story = { path = "crates/module-runtime-story", default-features = false }
module-square-hole = { path = "crates/module-square-hole", default-features = false }
module-story = { path = "crates/module-story", default-features = false }
platform-auth = { path = "crates/platform-auth", default-features = false }
platform-llm = { path = "crates/platform-llm", default-features = false }
platform-oss = { path = "crates/platform-oss", default-features = false }
shared-contracts = { path = "crates/shared-contracts", default-features = false }
shared-kernel = { path = "crates/shared-kernel", default-features = false }
shared-logging = { path = "crates/shared-logging", default-features = false }
spacetime-client = { path = "crates/spacetime-client", default-features = false }
argon2 = "0.5"
async-stream = "0.3"
axum = "0.8"
base64 = "0.22"
dotenvy = "0.15"
hmac = "0.12"
http-body-util = "0.1"
image = { version = "0.25", default-features = false }
jsonwebtoken = "9"
log = "0.4"
spacetimedb = "2.1.0"
rand_core = "0.6"
reqwest = { version = "0.12", default-features = false }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_urlencoded = "0.7"
sha2 = "0.10"
spacetimedb = "2.2.0"
spacetimedb-sdk = "2.2.0"
spacetimedb-lib = { version = "2.2.0", default-features = false }
time = "0.3"
tokio = "1"
tokio-stream = "0.1"
tower = "0.5"
tower-http = "0.6"
tracing = "0.1"
tracing-subscriber = "0.3"
url = "2"
urlencoding = "2"
uuid = "1"
webp = "0.3"
[profile.dev]
opt-level = 0 # 默认 0,有人手滑改 1/2 会慢
+9 -1
View File
@@ -14,7 +14,7 @@
## 2. 当前阶段说明
当前目录已经完成以下三十项初始化:
当前目录已经完成以下三十项初始化:
1. 为新后端预留正式目录并把路径固定到仓库结构中。
2. 创建虚拟 workspace `Cargo.toml`,后续 crate 会逐项挂入。
@@ -53,6 +53,7 @@
35. 创建 `scripts/spacetime-dev.sh`,固定 Unix-like 本地 SpacetimeDB 启动入口。
36. 创建 `scripts/oss-smoke.ps1`,固定 Windows 本地阿里云 OSS 真实联调入口。
37. 固定 Vite dev proxy 的 Rust `api-server` 默认目标与 `GENARRATIVE_RUNTIME_SERVER_TARGET` 覆盖开关。
38. 固定 `Cargo.toml` 依赖集中配置口径,第三方版本和 workspace 内部 crate path 统一维护在根 `server-rs/Cargo.toml`
后续任务会继续在本目录内按顺序补齐:
@@ -109,8 +110,15 @@
4. `spacetime-module` 新增业务入口前先确认是否已有对应上下文目录,禁止继续把大段业务流程堆回 `src/lib.rs`
5. 根目录可执行 `npm run check:server-rs-ddd` 检查第一阶段 DDD 骨架与绝对边界。
## 7. Cargo 依赖配置口径
`2026-05-07` 起,`server-rs` 的依赖版本和 workspace 内部 crate path 统一维护在根 `Cargo.toml``[workspace.dependencies]`,完整记录见 [../docs/technical/RUST_WORKSPACE_DEPENDENCY_CONSOLIDATION_2026-05-07.md](../docs/technical/RUST_WORKSPACE_DEPENDENCY_CONSOLIDATION_2026-05-07.md)。
成员 crate 的 `Cargo.toml` 默认使用 `{ workspace = true }` 继承依赖;只在成员 crate 内保留本 crate 的 feature、optional、target-specific dependency 等差异。新增 crate 或新增依赖时,应优先补根 workspace 依赖,再在成员 crate 中继承。
## 5. 关联文档
1. [../docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md](../docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md)
2. [../backend-rewrite-tasklist/M0_REPOSITORY_BOUNDARY_DECISIONS_2026-04-20.md](../backend-rewrite-tasklist/M0_REPOSITORY_BOUNDARY_DECISIONS_2026-04-20.md)
3. [../backend-rewrite-tasklist/01_M0_M2_FOUNDATION_AND_AUTH.md](../backend-rewrite-tasklist/01_M0_M2_FOUNDATION_AND_AUTH.md)
4. [../docs/technical/RUST_WORKSPACE_DEPENDENCY_CONSOLIDATION_2026-05-07.md](../docs/technical/RUST_WORKSPACE_DEPENDENCY_CONSOLIDATION_2026-05-07.md)
+45 -46
View File
@@ -5,51 +5,50 @@ version.workspace = true
license.workspace = true
[dependencies]
async-stream = "0.3"
axum = "0.8"
base64 = "0.22"
dotenvy = "0.15"
image = { version = "0.25", default-features = false, features = ["jpeg", "png", "webp"] }
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
webp = "0.3"
module-ai = { path = "../module-ai" }
module-assets = { path = "../module-assets" }
module-auth = { path = "../module-auth" }
module-big-fish = { path = "../module-big-fish" }
module-combat = { path = "../module-combat" }
module-custom-world = { path = "../module-custom-world" }
module-inventory = { path = "../module-inventory" }
module-match3d = { path = "../module-match3d" }
module-npc = { path = "../module-npc" }
module-puzzle = { path = "../module-puzzle" }
module-runtime = { path = "../module-runtime" }
module-runtime-story = { path = "../module-runtime-story" }
module-runtime-item = { path = "../module-runtime-item" }
module-square-hole = { path = "../module-square-hole" }
module-story = { path = "../module-story" }
platform-auth = { path = "../platform-auth" }
platform-llm = { path = "../platform-llm" }
platform-oss = { path = "../platform-oss" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
shared-contracts = { path = "../shared-contracts" }
shared-kernel = { path = "../shared-kernel" }
shared-logging = { path = "../shared-logging" }
spacetime-client = { path = "../spacetime-client" }
tokio = { version = "1", features = ["macros", "rt-multi-thread", "net", "time"] }
tokio-stream = "0.1"
time = { version = "0.3", features = ["formatting"] }
tower-http = { version = "0.6", features = ["trace"] }
tracing = "0.1"
url = "2"
urlencoding = "2"
uuid = { version = "1", features = ["v4"] }
async-stream = { workspace = true }
axum = { workspace = true }
base64 = { workspace = true }
dotenvy = { workspace = true }
image = { workspace = true, features = ["jpeg", "png", "webp"] }
reqwest = { workspace = true, features = ["json", "rustls-tls"] }
webp = { workspace = true }
module-ai = { workspace = true }
module-assets = { workspace = true, features = ["server-service"] }
module-auth = { workspace = true }
module-big-fish = { workspace = true }
module-combat = { workspace = true }
module-custom-world = { workspace = true }
module-inventory = { workspace = true }
module-match3d = { workspace = true }
module-npc = { workspace = true }
module-puzzle = { workspace = true }
module-runtime = { workspace = true }
module-runtime-story = { workspace = true }
module-runtime-item = { workspace = true }
module-square-hole = { workspace = true }
module-story = { workspace = true }
platform-auth = { workspace = true }
platform-llm = { workspace = true }
platform-oss = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
shared-contracts = { workspace = true }
shared-kernel = { workspace = true }
shared-logging = { workspace = true }
spacetime-client = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "net", "time"] }
tokio-stream = { workspace = true }
time = { workspace = true, features = ["formatting"] }
tower-http = { workspace = true, features = ["trace"] }
tracing = { workspace = true }
url = { workspace = true }
urlencoding = { workspace = true }
uuid = { workspace = true, features = ["v4"] }
[dev-dependencies]
base64 = "0.22"
hmac = "0.12"
httpdate = "1"
http-body-util = "0.1"
reqwest = { version = "0.12", default-features = false, features = ["json", "multipart", "rustls-tls"] }
sha1 = "0.10"
tower = { version = "0.5", features = ["util"] }
base64 = { workspace = true }
hmac = { workspace = true }
http-body-util = { workspace = true }
reqwest = { workspace = true, features = ["json", "multipart", "rustls-tls"] }
sha2 = { workspace = true }
tower = { workspace = true, features = ["util"] }
+164 -20
View File
@@ -473,26 +473,23 @@ mod tests {
error::Error,
fs,
path::{Path, PathBuf},
time::SystemTime,
};
use axum::{
body::Body,
http::{Request, StatusCode},
};
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64_STANDARD};
use hmac::{Hmac, Mac};
use http_body_util::BodyExt;
use httpdate::fmt_http_date;
use reqwest::{Method, multipart};
use serde_json::{Value, json};
use sha1::Sha1;
use sha2::{Digest, Sha256};
use shared_kernel::new_uuid_simple_string;
use tower::ServiceExt;
use crate::{app::build_router, config::AppConfig, state::AppState};
type HmacSha1 = Hmac<Sha1>;
type HmacSha256 = Hmac<Sha256>;
#[test]
fn asset_history_kind_support_includes_puzzle_cover_image() {
@@ -653,8 +650,13 @@ mod tests {
Value::String("private".to_string())
);
assert_eq!(
payload["data"]["upload"]["formFields"]["OSSAccessKeyId"],
Value::String("test-access-key-id".to_string())
payload["data"]["upload"]["formFields"]["x-oss-signature-version"],
Value::String("OSS4-HMAC-SHA256".to_string())
);
assert!(
payload["data"]["upload"]["formFields"]["x-oss-credential"]
.as_str()
.is_some_and(|value| value.starts_with("test-access-key-id/"))
);
assert!(payload["data"]["upload"].get("publicUrl").is_none());
}
@@ -702,7 +704,7 @@ mod tests {
assert!(
payload["data"]["read"]["signedUrl"]
.as_str()
.is_some_and(|value| value.contains("OSSAccessKeyId=test-access-key-id"))
.is_some_and(|value| value.contains("x-oss-signature-version=OSS4-HMAC-SHA256"))
);
}
@@ -1410,13 +1412,26 @@ mod tests {
.oss_access_key_secret
.as_deref()
.ok_or_else(|| std::io::Error::other("缺少 oss access key secret"))?;
let date = fmt_http_date(SystemTime::now());
let canonical_resource = match object_key.map(str::trim).filter(|value| !value.is_empty()) {
Some(object_key) => format!("/{bucket}/{}", object_key.trim_start_matches('/')),
None => format!("/{bucket}/"),
};
let string_to_sign = format!("{}\n\n\n{}\n{}", method.as_str(), date, canonical_resource);
let signature = sign_oss_string(access_key_secret, &string_to_sign)?;
let signed_at = time::OffsetDateTime::now_utc();
let signed_at_text = build_oss_v4_signature_date(signed_at);
let signature_scope = build_oss_v4_signature_scope(endpoint, signed_at)?;
let object_path = object_key.map(str::trim).filter(|value| !value.is_empty());
let canonical_uri = build_oss_v4_canonical_uri(bucket, object_path);
let payload_hash = "UNSIGNED-PAYLOAD";
let canonical_headers =
format!("host:{bucket}.{endpoint}\nx-oss-content-sha256:{payload_hash}\nx-oss-date:{signed_at_text}\n");
let additional_headers = "host";
let canonical_request = format!(
"{}\n{}\n\n{}\n{}\n{}",
method.as_str(),
canonical_uri,
canonical_headers,
additional_headers,
payload_hash
);
let string_to_sign =
build_oss_v4_string_to_sign(&signed_at_text, &signature_scope, &canonical_request);
let signature = sign_oss_v4_content(access_key_secret, &signature_scope, &string_to_sign)?;
let target_url = match object_key.map(str::trim).filter(|value| !value.is_empty()) {
Some(object_key) => build_object_url(config, object_key)?,
None => reqwest::Url::parse(&format!("https://{bucket}.{endpoint}/"))?,
@@ -1424,18 +1439,147 @@ mod tests {
let response = client
.request(method, target_url)
.header("Date", date)
.header("Authorization", format!("OSS {access_key_id}:{signature}"))
.header("x-oss-content-sha256", payload_hash)
.header("x-oss-date", signed_at_text)
.header(
"Authorization",
format!(
"OSS4-HMAC-SHA256 Credential={access_key_id}/{signature_scope},AdditionalHeaders={additional_headers},Signature={signature}"
),
)
.send()
.await?;
Ok(response)
}
fn sign_oss_string(secret: &str, content: &str) -> Result<String, Box<dyn Error>> {
let mut signer = HmacSha1::new_from_slice(secret.as_bytes())?;
fn build_oss_v4_signature_scope(
endpoint: &str,
signed_at: time::OffsetDateTime,
) -> Result<String, Box<dyn Error>> {
let date = signed_at.date().to_string().replace('-', "");
let region = endpoint
.trim()
.split('.')
.next()
.and_then(|segment| segment.strip_prefix("oss-"))
.ok_or_else(|| std::io::Error::other("OSS endpoint 无法解析 region"))?;
Ok(format!("{date}/{region}/oss/aliyun_v4_request"))
}
fn build_oss_v4_signature_date(signed_at: time::OffsetDateTime) -> String {
let date = signed_at.date().to_string().replace('-', "");
let time = signed_at
.time()
.to_string()
.split('.')
.next()
.unwrap_or("00:00:00")
.replace(':', "");
debug_assert_eq!(time.len(), 6);
format!("{date}T{time}Z")
}
fn build_oss_v4_canonical_uri(bucket: &str, object_key: Option<&str>) -> String {
match object_key.map(str::trim).filter(|value| !value.is_empty()) {
Some(object_key) => format!(
"/{}/{}",
encode_oss_url_query_value(bucket),
encode_oss_url_path(object_key.trim_start_matches('/'))
),
None => format!("/{}/", encode_oss_url_query_value(bucket)),
}
}
fn build_oss_v4_string_to_sign(
signature_date: &str,
signature_scope: &str,
canonical_request: &str,
) -> String {
format!(
"OSS4-HMAC-SHA256\n{signature_date}\n{signature_scope}\n{}",
sha256_hex(canonical_request.as_bytes())
)
}
fn sign_oss_v4_content(
secret: &str,
signature_scope: &str,
content: &str,
) -> Result<String, Box<dyn Error>> {
let signing_key = build_oss_v4_signing_key(secret, signature_scope)?;
let mut signer = HmacSha256::new_from_slice(&signing_key)?;
signer.update(content.as_bytes());
Ok(BASE64_STANDARD.encode(signer.finalize().into_bytes()))
Ok(hex_lower(&signer.finalize().into_bytes()))
}
fn build_oss_v4_signing_key(
secret: &str,
signature_scope: &str,
) -> Result<Vec<u8>, Box<dyn Error>> {
let mut parts = signature_scope.split('/');
let date = parts
.next()
.ok_or_else(|| std::io::Error::other("OSS V4 scope 缺少日期"))?;
let region = parts
.next()
.ok_or_else(|| std::io::Error::other("OSS V4 scope 缺少 region"))?;
let service = parts
.next()
.ok_or_else(|| std::io::Error::other("OSS V4 scope 缺少 service"))?;
let request = parts
.next()
.ok_or_else(|| std::io::Error::other("OSS V4 scope 缺少 request"))?;
let date_key = hmac_sha256_raw(format!("aliyun_v4{secret}").as_bytes(), date)?;
let region_key = hmac_sha256_raw(&date_key, region)?;
let service_key = hmac_sha256_raw(&region_key, service)?;
hmac_sha256_raw(&service_key, request)
}
fn hmac_sha256_raw(key: &[u8], content: &str) -> Result<Vec<u8>, Box<dyn Error>> {
let mut signer = HmacSha256::new_from_slice(key)?;
signer.update(content.as_bytes());
Ok(signer.finalize().into_bytes().to_vec())
}
fn sha256_hex(content: &[u8]) -> String {
let mut hasher = Sha256::new();
hasher.update(content);
hex_lower(&hasher.finalize())
}
fn hex_lower(bytes: &[u8]) -> String {
bytes
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>()
}
fn encode_oss_url_path(path: &str) -> String {
path.split('/')
.map(encode_oss_url_query_value)
.collect::<Vec<_>>()
.join("/")
}
fn encode_oss_url_query_value(value: &str) -> String {
let mut encoded = String::with_capacity(value.len());
for byte in value.bytes() {
match byte {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
encoded.push(byte as char)
}
_ => {
use std::fmt::Write as _;
let _ = write!(&mut encoded, "%{byte:02X}");
}
}
}
encoded
}
fn ensure_success_status(status: u16, message: &str) -> Result<(), Box<dyn Error>> {
+2 -2
View File
@@ -9,6 +9,6 @@ default = []
spacetime-types = ["dep:spacetimedb"]
[dependencies]
serde = { version = "1", features = ["derive"] }
shared-kernel = { path = "../shared-kernel" }
serde = { workspace = true }
shared-kernel = { workspace = true }
spacetimedb = { workspace = true, optional = true }
+4 -4
View File
@@ -10,8 +10,8 @@ server-service = ["dep:platform-oss", "dep:reqwest"]
spacetime-types = ["dep:spacetimedb"]
[dependencies]
serde = { version = "1", features = ["derive"] }
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"], optional = true }
serde = { workspace = true }
reqwest = { workspace = true, features = ["rustls-tls"], optional = true }
spacetimedb = { workspace = true, optional = true }
platform-oss = { path = "../platform-oss", optional = true }
shared-kernel = { path = "../shared-kernel" }
platform-oss = { workspace = true, optional = true }
shared-kernel = { workspace = true }
+7 -7
View File
@@ -5,12 +5,12 @@ version.workspace = true
license.workspace = true
[dependencies]
platform-auth = { path = "../platform-auth" }
shared-kernel = { path = "../shared-kernel" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
time = { version = "0.3", features = ["formatting", "parsing"] }
tracing = "0.1"
platform-auth = { workspace = true }
shared-kernel = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
time = { workspace = true, features = ["formatting", "parsing"] }
tracing = { workspace = true }
[dev-dependencies]
tokio = { version = "1", features = ["macros", "rt"] }
tokio = { workspace = true, features = ["macros", "rt"] }
+3 -3
View File
@@ -9,7 +9,7 @@ default = []
spacetime-types = ["dep:spacetimedb"]
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
shared-kernel = { path = "../shared-kernel" }
serde = { workspace = true }
serde_json = { workspace = true }
shared-kernel = { workspace = true }
spacetimedb = { workspace = true, optional = true }
+3 -3
View File
@@ -9,7 +9,7 @@ default = []
spacetime-types = ["dep:spacetimedb", "module-runtime-item/spacetime-types"]
[dependencies]
module-runtime-item = { path = "../module-runtime-item", default-features = false }
serde = { version = "1", features = ["derive"] }
shared-kernel = { path = "../shared-kernel" }
module-runtime-item = { workspace = true }
serde = { workspace = true }
shared-kernel = { workspace = true }
spacetimedb = { workspace = true, optional = true }
@@ -9,6 +9,6 @@ default = []
spacetime-types = ["dep:spacetimedb"]
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde = { workspace = true }
serde_json = { workspace = true }
spacetimedb = { workspace = true, optional = true }
+2 -2
View File
@@ -9,6 +9,6 @@ default = []
spacetime-types = ["dep:spacetimedb"]
[dependencies]
serde = { version = "1", features = ["derive"] }
shared-kernel = { path = "../shared-kernel" }
serde = { workspace = true }
shared-kernel = { workspace = true }
spacetimedb = { workspace = true, optional = true }
+2 -2
View File
@@ -9,6 +9,6 @@ default = []
spacetime-types = ["dep:spacetimedb"]
[dependencies]
serde = { version = "1", features = ["derive"] }
shared-kernel = { path = "../shared-kernel" }
serde = { workspace = true }
shared-kernel = { workspace = true }
spacetimedb = { workspace = true, optional = true }
+2 -2
View File
@@ -9,6 +9,6 @@ default = []
spacetime-types = ["dep:spacetimedb"]
[dependencies]
serde = { version = "1", features = ["derive"] }
shared-kernel = { path = "../shared-kernel" }
serde = { workspace = true }
shared-kernel = { workspace = true }
spacetimedb = { workspace = true, optional = true }
@@ -9,6 +9,6 @@ default = []
spacetime-types = ["dep:spacetimedb"]
[dependencies]
serde = { version = "1", features = ["derive"] }
shared-kernel = { path = "../shared-kernel" }
serde = { workspace = true }
shared-kernel = { workspace = true }
spacetimedb = { workspace = true, optional = true }
+2 -2
View File
@@ -9,6 +9,6 @@ default = []
spacetime-types = ["dep:spacetimedb"]
[dependencies]
serde = { version = "1", features = ["derive"] }
shared-kernel = { path = "../shared-kernel" }
serde = { workspace = true }
shared-kernel = { workspace = true }
spacetimedb = { workspace = true, optional = true }
+2 -2
View File
@@ -9,6 +9,6 @@ default = []
spacetime-types = ["dep:spacetimedb"]
[dependencies]
serde = { version = "1", features = ["derive"] }
shared-kernel = { path = "../shared-kernel" }
serde = { workspace = true }
shared-kernel = { workspace = true }
spacetimedb = { workspace = true, optional = true }
@@ -9,7 +9,7 @@ default = []
spacetime-types = ["dep:spacetimedb"]
[dependencies]
module-inventory = { path = "../module-inventory", default-features = false }
serde = { version = "1", features = ["derive"] }
shared-kernel = { path = "../shared-kernel" }
module-inventory = { workspace = true }
serde = { workspace = true }
shared-kernel = { workspace = true }
spacetimedb = { workspace = true, optional = true }

Some files were not shown because too many files have changed in this diff Show More