diff --git a/CURRENT_STATUS.md b/CURRENT_STATUS.md index b32243d..cbe0e8c 100644 --- a/CURRENT_STATUS.md +++ b/CURRENT_STATUS.md @@ -1,6 +1,6 @@ # BlueArchiveToolkit 当前工作区状态 -- **更新时间**:2026-09-13 +- **更新时间**:2026-09-19 - **状态来源**:本地工作区盘点、代码验证和最新提交 - **状态分支**:`experiment` - **最新已推送功能提交**:以当前 `git log --oneline -1` 为准 @@ -188,6 +188,12 @@ marker 存在时才保持兼容 key,无法证明归属时拒绝 cleanup;完 - `FileSystemCasRepository` 作为 `bat-core::CasRepository` 适配层。 - `InMemoryResourceRepository`。 - `SqliteResourceRepository`。 +- `ResourceQuery.path_pattern` 使用 `bat-core` 共享的 Unicode scalar 动态规划 matcher; + `*` / `**` / `?`、路径分隔符和字面量特殊字符的语义在 InMemory/SQLite 间一致。 + SQLite 只做候选过滤并由最终 matcher 判定,带路径或 metadata 后置过滤的 + `count()` 与 `list()` 精确相等。 +- Rust `bat` 与 Go `bat-api` 都从 `internal/configtemplate/config.toml.example` + 生成共享配置模板,仓库内 `cmd/bat-api/config.toml.example` 由一致性测试锁定为相同字节。 - 官方 pull plan 构建。 - `OfficialResourcePullService`:官方 URL 拒绝策略、目标路径映射、下载 manifest、下载 quarantine、`.part` 续传、curl 代理配置、403/404/5xx 分类重试、ZIP 结构校验、官方 seed `.hash` 校验、本地全量 verify。 - `OfficialUpdateService`:官方 metadata auto-discover、bootstrap cache、snapshot diff、marker diff、本地 audit/repair、失败 staging 恢复。 diff --git a/docs/reports/GO_STATUS.md b/docs/reports/GO_STATUS.md index 452868a..79617d9 100644 --- a/docs/reports/GO_STATUS.md +++ b/docs/reports/GO_STATUS.md @@ -1,6 +1,6 @@ # Go 侧进度与边界(权威) -- **更新时间**:2026-09-12 +- **更新时间**:2026-09-19 - **用途**:统一 Go module `bat-api` 的产品边界、既有约定和组件进度;其他文档与此冲突时以本文为准。 - **关联缺口**:G-009(资源 bootstrap/分发);相关契约见 `docs/architecture/official-resource-backend.md` §7 和 `docs/guides/bat-api-local-live-smoke.md` @@ -71,7 +71,7 @@ | ID | 约定 | |---|---| -| K | 共享 `config.toml` 的 `[api]` / 进程环境变量 / CLI:端口、public base、RPC socket、RPC 刷新周期;**预留** database/redis。应用不读取二进制旁 `.env` | +| K | 共享 `config.toml` 的 `[api]` / 进程环境变量 / CLI:端口、public base、RPC socket、RPC 刷新周期;模板唯一来源为 `internal/configtemplate/config.toml.example`,`cmd/bat-api/config.toml.example` 由测试校验字节一致;**预留** database/redis。应用不读取二进制旁 `.env` | | L | 管理面 / bootstrap:`/healthz`、`/readyz`、`/v1/bootstrap`、`/v1/release`、`/v1/resources`、`/openapi.yaml`、`/admin/` 控制入口 | | M | CDN:`GET/HEAD /prod-clientpatch.bluearchiveyostar.com/...`,支持 Range、ETag、Last-Modified、长期缓存头 | | N | server-info 可选;**只改 AddressablesCatalogUrlRoot** | diff --git a/infrastructure/src/bin/bat/config_file.rs b/infrastructure/src/bin/bat/config_file.rs index 30265ce..439bf51 100644 --- a/infrastructure/src/bin/bat/config_file.rs +++ b/infrastructure/src/bin/bat/config_file.rs @@ -13,94 +13,8 @@ use std::time::Duration; pub(crate) const CONFIG_FILE_NAME: &str = "config.toml"; pub(crate) const CONFIG_EXAMPLE_FILE_NAME: &str = "config.toml.example"; -const CONFIG_TEMPLATE: &str = r#"# BlueArchive Toolkit 配置文件(bat 首次启动自动生成) -# -# `config.toml` 位于 bat 二进制所在目录。存在时会被读取并生效。 -# 优先级:CLI > 进程环境变量 > config.toml > 内置默认值。 -# `config.toml.example` 只是模板,程序不会自动读取它作为实际配置。 -# -# 字符串建议使用单引号,路径/URL 更容易直接复制。 - -[runtime] -state_dir = '/tmp/bat-pid' -interval_seconds = 3600 -error_retry_seconds = 60 -quiet_up_to_date = false -output_format = 'human' -banner = true -progress = true -tail_lines = 200 - -[resource] -output_root = './bat-resources' -auto_discover = true -app_version = '' -connection_group = '' -launcher_version = '1.7.2' -platforms = ['windows', 'android'] -snapshot_path = '' -dry_run = false -plan = false -force = false -audit_local = true -repair = true - -[resource.server_info] -kind = 'none' -value = '' - -[localized] -output_root = './bat-localized' - -[repository] -import_repository = false -import_cas_root = '' -import_resource_repository_path = '' - -[network] -curl_command = 'curl' -proxy = 'auto' -unzip_command = 'unzip' -zip_command = 'zip' -download_concurrency = 8 - -[translation.worker] -provider = 'mock' -fixture = '' -translation_memory_path = '' -glossary_path = '' -concurrency = 8 -max_attempts = 3 -lease_seconds = 300 -retry_backoff_seconds = 5 -max_tasks = '' -worker_id = '' - -# Go bat-api consumes this shared section. Rust bat ignores it. -[api] -listen = ':18080' -public_base_url = 'http://127.0.0.1:18080' -state_dir = '/tmp/bat-pid' -socket_path = '' -resource_root = '' -server_info_file = '' -require_indexed = true -verify_size = true -rpc_timeout = '30s' -refresh_interval = '1m' -auth_token = '' -auth_query_param = 'bat_token' -auth_exempt_paths = [] -trust_proxy_headers = false -access_log = false -rate_limit_rps = 0 -rate_limit_burst = 0 -max_resource_page_limit = 1000 -database_url = '' -database_password = '' -redis_url = '' -redis_password = '' -"#; +const CONFIG_TEMPLATE: &str = + include_str!("../../../../internal/configtemplate/config.toml.example"); #[derive(Debug, Clone, Default)] pub(crate) struct BatConfigFile { @@ -1068,6 +982,14 @@ mod tests { assert!(options.config.server_info_source.is_none()); } + #[test] + fn shared_template_matches_checked_in_bat_api_example() { + assert_eq!( + CONFIG_TEMPLATE, + include_str!("../../../../cmd/bat-api/config.toml.example") + ); + } + #[test] fn config_file_is_written_when_missing() { let temp = TempDir::new().unwrap(); @@ -1076,10 +998,7 @@ mod tests { let example = temp.path().join(CONFIG_EXAMPLE_FILE_NAME); assert!(example.exists()); let content = fs::read_to_string(example).unwrap(); - let parsed = parse_config_document(&content).unwrap(); - let mut options = CliOptions::default(); - parsed.apply_to_options(&mut options).unwrap(); - assert_eq!(options.config.output_root, PathBuf::from("./bat-resources")); + assert_eq!(content, CONFIG_TEMPLATE); } #[test] diff --git a/internal/api/config_toml.go b/internal/api/config_toml.go index e9f85dc..d131a7b 100644 --- a/internal/api/config_toml.go +++ b/internal/api/config_toml.go @@ -8,100 +8,13 @@ import ( "runtime" "time" + "bat-api/internal/configtemplate" "github.com/pelletier/go-toml/v2" ) -// ConfigTOMLTemplate is the only template generated by bat-api. Rust bat -// accepts the same file and ignores the [api] section. -const ConfigTOMLTemplate = `# BlueArchive Toolkit shared application configuration. -# Priority: CLI flags > process environment > config.toml > built-in defaults. -# This file is the application configuration. The application never reads .env. -# -# Rust bat consumes [runtime], [resource], [localized], [repository], [network] -# and [translation.worker]. Go bat-api consumes [api]. Each binary ignores the -# other application's section. - -[runtime] -state_dir = '/tmp/bat-pid' -interval_seconds = 3600 -error_retry_seconds = 60 -quiet_up_to_date = false -output_format = 'human' -banner = true -progress = true -tail_lines = 200 - -[resource] -output_root = './bat-resources' -auto_discover = true -app_version = '' -connection_group = '' -launcher_version = '1.7.2' -platforms = ['windows', 'android'] -snapshot_path = '' -dry_run = false -plan = false -force = false -audit_local = true -repair = true - -[resource.server_info] -kind = 'none' -value = '' - -[localized] -output_root = './bat-localized' - -[repository] -import_repository = false -import_cas_root = '' -import_resource_repository_path = '' - -[network] -curl_command = 'curl' -proxy = 'auto' -unzip_command = 'unzip' -zip_command = 'zip' -download_concurrency = 8 - -[translation.worker] -provider = 'mock' -fixture = '' -translation_memory_path = '' -glossary_path = '' -concurrency = 8 -max_attempts = 3 -lease_seconds = 300 -retry_backoff_seconds = 5 -max_tasks = '' -worker_id = '' - -[api] -listen = ':18080' -public_base_url = 'http://127.0.0.1:18080' -state_dir = '/tmp/bat-pid' -socket_path = '' -resource_root = '' -server_info_file = '' -require_indexed = true -verify_size = true -rpc_timeout = '30s' -refresh_interval = '1m' -auth_token = '' -auth_query_param = 'bat_token' -auth_exempt_paths = [] -trust_proxy_headers = false -access_log = false -rate_limit_rps = 0 -rate_limit_burst = 0 -max_resource_page_limit = 1000 - -# Reserved for a future API persistence layer. -database_url = '' -database_password = '' -redis_url = '' -redis_password = '' -` +// ConfigTOMLTemplate is the canonical template generated by bat-api. Rust +// bat consumes the same file and ignores the [api] section. +var ConfigTOMLTemplate = configtemplate.Template // LoadConfigFromCurrentExe loads config.toml next to the running binary. func LoadConfigFromCurrentExe(cfg *Config) error { diff --git a/internal/api/config_toml_test.go b/internal/api/config_toml_test.go index d43c581..d7a2114 100644 --- a/internal/api/config_toml_test.go +++ b/internal/api/config_toml_test.go @@ -27,12 +27,31 @@ func TestLoadConfigFromBinaryDirFirstLaunchCreatesExampleOnly(t *testing.T) { if _, err := os.Stat(filepath.Join(dir, ConfigExampleName)); err != nil { t.Fatalf("config.toml.example was not created: %v", err) } + examplePath := filepath.Join(dir, ConfigExampleName) + example, err := os.ReadFile(examplePath) + if err != nil { + t.Fatal(err) + } + if string(example) != ConfigTOMLTemplate { + t.Fatal("first-launch example differs from the canonical runtime template") + } if _, err := os.Stat(filepath.Join(dir, ".env")); !os.IsNotExist(err) { t.Fatalf("unexpected .env after first launch, err=%v", err) } if cfg.Listen != DefaultListen || cfg.RPCTimeout != 30*time.Second { t.Fatalf("first launch changed defaults: %+v", cfg) } + + if err := LoadConfigFromBinaryDir(dir, &cfg); err != nil { + t.Fatal(err) + } + unchanged, err := os.ReadFile(examplePath) + if err != nil { + t.Fatal(err) + } + if string(unchanged) != string(example) { + t.Fatal("existing config.toml.example was modified") + } } func TestCheckedInBatAPIConfigExampleMatchesRuntimeTemplate(t *testing.T) { diff --git a/internal/configtemplate/config.toml.example b/internal/configtemplate/config.toml.example new file mode 100644 index 0000000..13dc0bf --- /dev/null +++ b/internal/configtemplate/config.toml.example @@ -0,0 +1,88 @@ +# BlueArchive Toolkit shared application configuration. +# Priority: CLI flags > process environment > config.toml > built-in defaults. +# This file is the application configuration. The application never reads .env. +# +# Rust bat consumes [runtime], [resource], [localized], [repository], [network] +# and [translation.worker]. Go bat-api consumes [api]. Each binary ignores the +# other application's section. + +[runtime] +state_dir = '/tmp/bat-pid' +interval_seconds = 3600 +error_retry_seconds = 60 +quiet_up_to_date = false +output_format = 'human' +banner = true +progress = true +tail_lines = 200 + +[resource] +output_root = './bat-resources' +auto_discover = true +app_version = '' +connection_group = '' +launcher_version = '1.7.2' +platforms = ['windows', 'android'] +snapshot_path = '' +dry_run = false +plan = false +force = false +audit_local = true +repair = true + +[resource.server_info] +kind = 'none' +value = '' + +[localized] +output_root = './bat-localized' + +[repository] +import_repository = false +import_cas_root = '' +import_resource_repository_path = '' + +[network] +curl_command = 'curl' +proxy = 'auto' +unzip_command = 'unzip' +zip_command = 'zip' +download_concurrency = 8 + +[translation.worker] +provider = 'mock' +fixture = '' +translation_memory_path = '' +glossary_path = '' +concurrency = 8 +max_attempts = 3 +lease_seconds = 300 +retry_backoff_seconds = 5 +max_tasks = '' +worker_id = '' + +[api] +listen = ':18080' +public_base_url = 'http://127.0.0.1:18080' +state_dir = '/tmp/bat-pid' +socket_path = '' +resource_root = '' +server_info_file = '' +require_indexed = true +verify_size = true +rpc_timeout = '30s' +refresh_interval = '1m' +auth_token = '' +auth_query_param = 'bat_token' +auth_exempt_paths = [] +trust_proxy_headers = false +access_log = false +rate_limit_rps = 0 +rate_limit_burst = 0 +max_resource_page_limit = 1000 + +# Reserved for a future API persistence layer. +database_url = '' +database_password = '' +redis_url = '' +redis_password = '' diff --git a/internal/configtemplate/template.go b/internal/configtemplate/template.go new file mode 100644 index 0000000..6089fae --- /dev/null +++ b/internal/configtemplate/template.go @@ -0,0 +1,10 @@ +// Package configtemplate provides the shared runtime configuration template. +package configtemplate + +import _ "embed" + +// Template is the canonical config.toml.example content consumed by both +// Rust bat and Go bat-api. +// +//go:embed config.toml.example +var Template string