# 整体框架 ## 数据文件 ### `secrets.json` 存放 API 密钥,位于项目根目录。 ```json { "curseforge": "", "modrinth": "" } ``` ### `configs/{config_name}.json` 存放单个 mod 项目的配置,位于 `configs/` 目录下。文件名即为配置名,用户通过 WebUI 下拉选择。 ```jsonc { "name": "模组名称", "modrinth": { "project_id": "AABBCCDD", "version_name": "ModName v${version} for Minecraft ${mc_version_range}", "version": "${version}-mc${mc_version}", "loaders": ["fabric"], "environment": "client_and_server", "dependencies": [ { "dependency_type": "required", "project_id": "P7dR8mSH" } ] }, "curseforge": { "project_id": 123456, "version_name": "#{filename_format}", "environment": ["Client", "Server"], "loaders": ["fabric"], "relations": { "projects": [] } }, "project_dir": "/path/to/project", "minecraft_properties_dir": "./properties", // 目录内为 ${mc_version}.properties 文件,文件名即对应游戏版本 "project_properties_path": "./gradle.properties", // 项目版本配置文件路径 "mod_version_field": "mod_version", // 版本号在 project_properties_path 文件中的键名 "filename_format": "modname-${version}-mc${mc_version}.jar", "source_filename_format": "modname-${version}-mc${mc_version}-sources.jar" } ``` | 字段 | 类型 | 说明 | |------|------|------| | `name` | string | 配置文件显示名称(模组名称) | | `modrinth` | object | Modrinth 配置 | | `modrinth.project_id` | string | Modrinth 项目 ID(8 位 base62) | | `modrinth.version_name` | string | 版本名称模板,`${mc_version_range}` 为 `"1.19"` 或 `"1.20-1.20.2"` | | `modrinth.version` | string | 版本号模板 | | `modrinth.loaders` | string[] | 加载器列表 | | `modrinth.environment` | string | 运行环境,必填。可选值:`client_and_server`、`client_only`、`server_only` 等 | | `modrinth.dependencies` | object[] | 依赖列表,无依赖填 `[]` | | `curseforge` | object | CurseForge 配置 | | `curseforge.project_id` | number | CurseForge 项目 ID | | `curseforge.version_name` | string | 版本名称模板,如 `"#{filename_format}"` | | `curseforge.environment` | string[] | 运行环境 | | `curseforge.loaders` | string[] | 加载器列表 | | `curseforge.relations` | object | 关联项目,格式 `{ projects: [...] }` | | `project_dir` | string | 项目目录绝对路径 | | `minecraft_properties_dir` | string | 相对项目目录的 MC 版本配置目录,默认 `./properties`。目录内为 `${mc_version}.properties` 文件,文件名即为可用游戏版本 | | `project_properties_path` | string | 相对项目目录的项目版本配置文件路径,默认 `./gradle.properties` | | `mod_version_field` | string | 版本号在 `project_properties_path` 文件中的键名,默认 `"mod_version"` | | `filename_format` | string | 构建产物文件名模板,用于反向解析 `${version}` 和 `${mc_version}` | | `source_filename_format` | string | 源码 jar 文件名模板,用于正向生成文件名 | > **递归解析规则**:所有含占位符的模板字符串,正向 format 或反向解析后,若结果仍包含占位符,则需继续解析,直至结果中不再有占位符为止。 > > - `#{...}` — 引用占位符,如 `#{filename_format}` 表示此处将引用解析后的值 > - `${...}` — 模板变量,由运行时填入实际值,如 `${version}`、`${mc_version}` ## 运行时流程 ### 1. 启动 - 启动 Next.js 开发服务器 - 后端读取 `configs/` 目录,列出所有可用配置文件 - 后端读取 `secrets.json`,加载两个平台的 API 密钥到内存 ### 2. 选择配置 - 前端 ConfigSelector 下拉框展示所有配置(按 `name` 显示) - 用户选择一个配置后,前端请求后端解析项目信息 ### 3. 解析项目 后端收到选中配置后: 1. 读取 `{project_dir}/{project_properties_path}`,提取 `mod_version_field` 对应的值 → `version` 2. 扫描 `{project_dir}/{minecraft_properties_dir}/` 目录,获取所有 `${mc_version}.properties` 文件名 → `mc_version[]` 3. 对 `mc_version[]` 排序后计算每个版本的兼容范围: - 非最后一个版本:范围为 `[当前版本, 下一个版本)`,如 `"1.19.1"` → `"1.19.1-1.19.2"` - 最后一个版本:`< 26` 的版本用 `~` 匹配次版本号(如 `1.21.x`);`≥ 26` 的版本用 `^` 匹配主版本号(如 `26.x`),截止版本由用户在下拉框中选择 4. 遍历每个 `mc_version`: - 用 `version` 和 `mc_version` 填入 `filename_format` 模板,生成构建产物文件名 - 用 `version` 和 `mc_version` 填入 `source_filename_format` 模板,生成源码文件名 - 检查 `{project_dir}/build/libs/` 下是否存在对应文件 5. 将所有匹配结果返回前端,由 VersionTable 展示(MC 版本 | 兼容范围 | 构建产物 | 源码) ### 4. 版本确认与发布 #### 第一页:版本选择 1. 用户点击 `[一键发布]`,弹出 PublishModal 2. 前端请求两个平台的已有版本列表: - Modrinth:`GET /project/{id}/version` - CurseForge:通过对应 API 获取 3. 后端比对已有版本与待发布版本(按 `version` 模板生成的值匹配) 4. 前端左右两栏展示(待发布版本与已有版本合并后,按 `mod_version` 降序、`mc_version` 降序排列): - 顶部绿色行 = 待发布版本,复选框默认勾选 - 下方灰色行 = 已有版本,复选框禁用 - 已存在的待发布版本显示为已有版本颜色,复选框禁用 5. 用户调整勾选后点击"下一步" #### 第二页:发布设置 - 用户填写 changelog(Markdown,Code / Preview 切换) - 选择 version_type(默认 `release`) - 点击"确认发布";可点击"上一步"返回修改 ### 5. 执行发布 1. 前端调用 `trpc.release.publish.useMutation()` 发起发布 2. 前端通过 `trpc.release.progress.useSubscription()` 监听进度,实时更新进度条 3. 进度条格式:`{平台图标} 平台名: [======== ] 0 / N`,仅显示有勾选任务的平台 4. 单个平台内按 MC 版本顺序逐个提交,**不可并行**;两个平台之间可以并行执行 5. 每个版本的发布流程: - 解析模板:用 `version` 和 `mc_version` 填入各平台 `version_name`、`version` 模板(含递归解析) - Modrinth:构造 multipart/form-data 请求,含 `data` JSON + 构建产物文件 + 源码文件 - CurseForge:构造 multipart/form-data 请求,含 `metadata` JSON + 文件 - 通过 tRPC subscription 推送进度更新 6. **任一请求失败则取消当前平台的后续所有任务**,不影响另一个平台 7. 全部完成或失败后,弹出结果 Modal 显示最终状态(成功数 / 失败数)