Add documentation for Mod Releaser project
This commit is contained in:
parent
7dff3d0f3f
commit
346b0783d8
130
docs/curseforge/get.md
Normal file
130
docs/curseforge/get.md
Normal file
@ -0,0 +1,130 @@
|
||||
# CurseForge Get Mod Files API
|
||||
|
||||
> 来源:https://docs.curseforge.com/rest-api/
|
||||
>
|
||||
> API 基础地址:`https://api.curseforge.com`,认证方式:Header `x-api-key: <token>`
|
||||
|
||||
---
|
||||
|
||||
## Get Mod Files
|
||||
|
||||
```
|
||||
GET https://api.curseforge.com/v1/mods/{modId}/files
|
||||
```
|
||||
|
||||
获取指定项目的所有文件。
|
||||
|
||||
### 参数
|
||||
|
||||
| 参数 | 位置 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|------|
|
||||
| `modId` | path | int32 | 是 | 项目 ID |
|
||||
| `gameVersion` | query | string | 否 | 按游戏版本过滤 |
|
||||
| `modLoaderType` | query | int | 否 | 按 Mod Loader 类型过滤(见下表) |
|
||||
| `gameVersionTypeId` | query | int32 | 否 | 按 `gameVersionTypeId` 过滤 |
|
||||
| `index` | query | int32 | 否 | 分页起始索引(从 0 开始,限制 `index + pageSize <= 10000`) |
|
||||
| `pageSize` | query | int32 | 否 | 每页数量,默认/最大 50 |
|
||||
|
||||
**modLoaderType 枚举值:**
|
||||
|
||||
| 值 | 含义 |
|
||||
|----|------|
|
||||
| 0 | Any |
|
||||
| 1 | Forge |
|
||||
| 2 | Cauldron |
|
||||
| 3 | LiteLoader |
|
||||
| 4 | Fabric |
|
||||
| 5 | Quilt |
|
||||
| 6 | NeoForge |
|
||||
|
||||
### 成功响应
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 12345, // 文件 ID
|
||||
"gameId": 432, // 游戏 ID
|
||||
"modId": 789, // 项目 ID
|
||||
"isAvailable": true, // 是否可下载
|
||||
"displayName": "My Mod v1.0", // 显示名称
|
||||
"fileName": "my-mod-1.0.jar", // 文件名
|
||||
"releaseType": 1, // 1=Release, 2=Beta, 3=Alpha
|
||||
"fileStatus": 10, // 文件状态(见附表)
|
||||
"hashes": [
|
||||
{ "value": "abc123...", "algo": 1 } // algo: 1=Sha1, 2=Md5
|
||||
],
|
||||
"fileDate": "2025-01-01T00:00:00Z", // 文件时间戳
|
||||
"fileLength": 1048576, // 文件大小(字节)
|
||||
"downloadCount": 5000, // 下载次数
|
||||
"fileSizeOnDisk": 1048576, // 磁盘占用
|
||||
"downloadUrl": "https://...", // 下载地址
|
||||
"gameVersions": ["1.21.5", "1.21.4"], // 关联的游戏版本(字符串列表)
|
||||
"sortableGameVersions": [ // 排序用版本信息
|
||||
{
|
||||
"gameVersionName": "1.21.5",
|
||||
"gameVersionPadded": "0000000001.0000000021.0000000005",
|
||||
"gameVersion": "1.21.5",
|
||||
"gameVersionReleaseDate": "2025-04-01T00:00:00Z",
|
||||
"gameVersionTypeId": 83806
|
||||
}
|
||||
],
|
||||
"dependencies": [ // 依赖关系
|
||||
{
|
||||
"modId": 123, // 依赖的项目 ID
|
||||
"relationType": 3 // 关系类型(见附表)
|
||||
}
|
||||
],
|
||||
"exposeAsAlternative": false,
|
||||
"parentProjectFileId": null,
|
||||
"alternateFileId": null,
|
||||
"isServerPack": false,
|
||||
"serverPackFileId": null,
|
||||
"isEarlyAccessContent": false,
|
||||
"earlyAccessEndDate": null,
|
||||
"fileFingerprint": 1234567890,
|
||||
"modules": [
|
||||
{ "name": "main", "fingerprint": 1234567890 }
|
||||
]
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"index": 0, // 本次返回的起始索引
|
||||
"pageSize": 50, // 请求的页大小
|
||||
"resultCount": 1, // 本次实际返回数量
|
||||
"totalCount": 10 // 总文件数
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 附表
|
||||
|
||||
### releaseType
|
||||
|
||||
| 值 | 含义 |
|
||||
|----|------|
|
||||
| 1 | Release |
|
||||
| 2 | Beta |
|
||||
| 3 | Alpha |
|
||||
|
||||
### relationType
|
||||
|
||||
| 值 | 含义 |
|
||||
|----|------|
|
||||
| 1 | EmbeddedLibrary |
|
||||
| 2 | OptionalDependency |
|
||||
| 3 | RequiredDependency |
|
||||
| 4 | Tool |
|
||||
| 5 | Incompatible |
|
||||
| 6 | Include |
|
||||
|
||||
### fileStatus(常用值)
|
||||
|
||||
| 值 | 含义 |
|
||||
|----|------|
|
||||
| 1 | Processing |
|
||||
| 4 | Approved |
|
||||
| 10 | Released |
|
||||
| 12 | Deprecated |
|
||||
200
docs/curseforge/upload.md
Normal file
200
docs/curseforge/upload.md
Normal file
@ -0,0 +1,200 @@
|
||||
# CurseForge Upload API
|
||||
|
||||
> 基于 CurseForge 官方 API 文档及实测整理。
|
||||
>
|
||||
> API 基础地址:`https://minecraft.curseforge.com`,**必须带浏览器 `User-Agent` 请求头**,否则 Cloudflare 会拦截返回 403。
|
||||
>
|
||||
> 推荐 User-Agent:`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36`
|
||||
|
||||
---
|
||||
|
||||
## 生成 Token
|
||||
|
||||
在 [API Tokens](https://authors.curseforge.com/account/api-tokens) 页面生成 API Token。
|
||||
|
||||
## 认证
|
||||
|
||||
通过以下两种方式之一传递 Token:
|
||||
|
||||
- HTTP Header:`X-Api-Token: <token>`
|
||||
- Query String:`?token=<token>`
|
||||
|
||||
---
|
||||
|
||||
## Game Version Types API
|
||||
|
||||
```
|
||||
GET https://minecraft.curseforge.com/api/game/version-types
|
||||
```
|
||||
|
||||
> **注意:** 此端点必须带浏览器 `User-Agent` 请求头,否则会被 Cloudflare 拦截返回 403。
|
||||
>
|
||||
> 推荐值:`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36`
|
||||
|
||||
返回版本类型列表,直接返回 JSON 数组,每个对象包含:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | int | 版本类型 ID(即 `gameVersionTypeID`) |
|
||||
| `name` | string | 类型名称 |
|
||||
| `slug` | string | 类型标识 |
|
||||
|
||||
其中 `id: 68441, name: "Modloader"` 对应 Modloader 分类。
|
||||
|
||||
`id: 75208, name: "Environment"` 对应运行环境,包含 `name: "Client"` 和 `name: "Server"` 两个 Game Version。
|
||||
|
||||
## Game Versions API
|
||||
|
||||
```
|
||||
GET https://minecraft.curseforge.com/api/game/versions
|
||||
```
|
||||
|
||||
> **注意:** 此端点必须带浏览器 `User-Agent` 请求头,否则会被 Cloudflare 拦截返回 403。
|
||||
>
|
||||
> 推荐值:`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36`
|
||||
|
||||
返回 Minecraft 版本列表,直接返回 JSON 数组(无 `{"data": ...}` 包装),每个对象包含:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | int | 版本 ID(用于上传时的 `gameVersions` 字段) |
|
||||
| `gameVersionTypeID` | int | 版本类型 ID |
|
||||
| `name` | string | 版本名称(如 `1.21.5`) |
|
||||
| `slug` | string | 版本标识(如 `1-21-5`) |
|
||||
| `apiVersion` | int \| null | API 版本号 |
|
||||
|
||||
> **注意:** Game Versions 列表并非仅包含游戏版本号,而是将 Mod Loader(Forge、Fabric 等)、Environment(Client、Server)甚至 Java 版本都混在一起。具体某个版本属于哪个类型,看它的 `gameVersionTypeID`,对应 [Game Version Types API](#game-version-types-api) 中的 `id`。
|
||||
|
||||
---
|
||||
|
||||
## Project Upload File API
|
||||
|
||||
```
|
||||
POST https://minecraft.curseforge.com/api/projects/{projectId}/upload-file
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
上传文件到项目。`projectId` 可在项目概览页面的 URL 中找到。
|
||||
|
||||
### 请求字段
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `metadata` | string (JSON) | 文件元数据(见下方) |
|
||||
| `file` | file | 要上传的实际文件 |
|
||||
|
||||
### metadata JSON 结构
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"changelog": "更新日志内容", // 必填。支持 HTML / Markdown(需设置 changelogType)
|
||||
"changelogType": "markdown", // 可选,默认 "text"。可选值: "text", "html", "markdown"
|
||||
"displayName": "Foo", // 可选,站点上显示的文件友好名称
|
||||
"parentFileID": 42, // 可选,父文件 ID
|
||||
"gameVersions": [157, 158], // 可选,支持的 Game Version ID 列表(通过 Game Versions API 获取)。若提供 parentFileID 则不支持此字段
|
||||
"releaseType": "release", // 必填。可选值: "alpha", "beta", "release"
|
||||
"isMarkedForManualRelease": false, // 可选,若为 true,审核通过后不会立即发布,可手动选择发布时间
|
||||
"relations": {
|
||||
"projects": [{
|
||||
"slug": "mantle", // 关联项目的 slug
|
||||
"projectID": "74924", // 可选,用于精确匹配项目
|
||||
"type": "requiredDependency" // 关联类型,可选值见下方
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**relations.type 可选值:**
|
||||
|
||||
- `embeddedLibrary` — 内嵌库
|
||||
- `incompatible` — 不兼容
|
||||
- `optionalDependency` — 可选依赖
|
||||
- `requiredDependency` — 必需依赖
|
||||
- `tool` — 工具
|
||||
|
||||
### 成功响应
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"id": 20402 // 新建文件的 ID
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Project File Management API
|
||||
|
||||
```
|
||||
POST https://minecraft.curseforge.com/api/projects/{projectId}/update-file
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
更新已上传的文件信息。
|
||||
|
||||
### 请求字段
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `metadata` | string (JSON) | 文件更新元数据(见下方) |
|
||||
|
||||
### metadata JSON 结构
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"fileID": 20402, // 必填,要更新的文件 ID
|
||||
|
||||
// 以下字段均为可选,可任意组合,但至少要包含一个
|
||||
"changelog": "更新日志内容",
|
||||
"changelogType": "markdown", // 可选值: "text", "html", "markdown",默认 "text"
|
||||
"displayName": "Foo",
|
||||
"gameVersions": [157, 158],
|
||||
"releaseType": "release", // 可选值: "alpha", "beta", "release"
|
||||
"relations": {
|
||||
"projects": [{
|
||||
"slug": "mantle",
|
||||
"projectID": "74924",
|
||||
"type": "requiredDependency"
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **注意:** 若未填写任何可选字段,API 将返回失败。
|
||||
|
||||
### 成功响应
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"id": 20402 // 被更新文件的 ID
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Maven
|
||||
|
||||
CurseForge 提供 Maven 端点,可在构建脚本中引用依赖。
|
||||
|
||||
```
|
||||
https://www.curseforge.com/api/maven/{projectSlug}/{mavenArtifact}/{mavenVersion}/{projectFileNameArtifact}-{projectFileNameVersion}-{projectFileNameTag}.jar
|
||||
```
|
||||
|
||||
**URL 参数说明:**
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| `{projectSlug}` | 项目的 slug |
|
||||
| `{mavenArtifact}` | 文件名 artifact |
|
||||
| `{mavenVersion}` | 版本号,或使用 `release` 获取最新版 |
|
||||
| `{projectFileNameArtifact}` | 同 `mavenArtifact` |
|
||||
| `{projectFileNameVersion}` | 同 `mavenVersion` |
|
||||
| `{projectFileNameTag}` | 文件名标签(如 `universal`、`dev`) |
|
||||
|
||||
> **注意:** Maven 认证不支持 Header 方式,需将 API Key 直接编入 Maven URL。
|
||||
|
||||
---
|
||||
|
||||
## 参考链接
|
||||
|
||||
- [CurseForge API Tokens](https://authors.curseforge.com/account/api-tokens)
|
||||
- [官方文档原文](https://support.curseforge.com/support/solutions/articles/9000197321-curseforge-upload-api)
|
||||
118
docs/frontend.md
Normal file
118
docs/frontend.md
Normal file
@ -0,0 +1,118 @@
|
||||
# 前端页面
|
||||
|
||||
## 布局
|
||||
|
||||
单页面应用,无需路由。自上而下:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ ConfigSelector │
|
||||
│ [选择配置 ▼] [+ 添加配置] │
|
||||
├─────────────────────────────────────────┤
|
||||
│ VersionTable │
|
||||
│ ┌──────────┬────────────────┬────────┐ │
|
||||
│ │ MC 版本 │ 构建产物 │ 源码 │ │
|
||||
│ ├──────────┼────────────────┼────────┤ │
|
||||
│ │ 1.21 │ xxx-1.0.0-... │ xxx.. │ │
|
||||
│ │ 1.21.1 │ xxx-1.0.0-... │ xxx.. │ │
|
||||
│ └──────────┴────────────────┴────────┘ │
|
||||
├─────────────────────────────────────────┤
|
||||
│ [一键发布] │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 组件
|
||||
|
||||
### ConfigSelector
|
||||
|
||||
- 下拉框列出 `configs/` 目录下所有配置(按 `name` 显示)
|
||||
- 选中后触发后端解析,刷新 VersionTable
|
||||
- 右侧 `[修改配置]` `[+ 添加配置]` 两个按钮,图标使用 Ant Design 图标库
|
||||
- 修改配置:弹出 ConfigModal,预填当前配置数据,保存时覆盖原文件
|
||||
- 添加配置:弹出 ConfigModal,空白表单,保存时创建新文件
|
||||
- ConfigModal 添加 / 修改共用同一个组件,通过是否传入已有配置数据区分模式
|
||||
|
||||
### VersionTable
|
||||
|
||||
- 每行:MC 版本 | 构建产物文件名 | 源码文件名
|
||||
- MC 版本来自 `minecraft_properties_dir` 目录文件名
|
||||
- 构建产物和源码分别匹配 `filename_format` 和 `source_filename_format`
|
||||
- 全部只读展示
|
||||
- 切换 config 后后台解析期间,Table 使用 Ant Design 自带的加载状态
|
||||
|
||||
### PublishModal
|
||||
|
||||
点击 `[一键发布]` 后弹出,分两页。
|
||||
|
||||
#### 第一页:版本选择
|
||||
|
||||
1. 弹出后加载 Modrinth 和 CurseForge 已有版本数据,Table 使用 Ant Design 自带的加载状态
|
||||
2. 加载完成后,左右两栏布局:
|
||||
- 左栏:Modrinth 版本列表
|
||||
- 右栏:CurseForge 版本列表
|
||||
3. 每栏顶部为**待发布版本**(绿色高亮),按顺序排列;下方为已有版本(另一颜色)
|
||||
4. 每个版本行最左侧有复选框:
|
||||
- 待发布版本:默认勾选,可选择取消
|
||||
- 已有版本:复选框禁用
|
||||
- 若某待发布版本在平台上已存在(版本号完全一致),显示为已有版本颜色,复选框禁用
|
||||
5. 点击"下一步"进入第二页
|
||||
|
||||
#### 第二页:发布设置
|
||||
|
||||
- **Changelog**:Markdown 编辑器,带 Code / Preview 切换(类似 GitHub 评论框)
|
||||
- **version_type**:下拉选择 `release`(默认) / `beta` / `alpha`
|
||||
- 平台不再勾选(第一页已通过版本复选框隐式确定:某平台无任何勾选的待发布版本即不发布该平台)
|
||||
- 点击"确认发布"开始执行
|
||||
- 提供"上一步"按钮返回第一页修改选择
|
||||
|
||||
#### 发布执行与进度
|
||||
|
||||
- 一个 mod version 对应多个 MC version 变体,按顺序逐个提交,**不可并行**
|
||||
- 任一请求失败则取消后续所有任务
|
||||
- 点击确认后显示两个进度条(仅显示有发布任务的平台):
|
||||
- `平台名: 已发布数 / 总发布数`
|
||||
- 全部完成后或失败后,弹出结果 Modal 显示最终状态
|
||||
|
||||
### ConfigModal
|
||||
|
||||
添加 / 修改共用一个 Modal 组件。添加模式为空表单;修改模式预填当前配置数据,保存时覆盖原文件。
|
||||
|
||||
弹出后需请求 Modrinth 和 CurseForge API 获取可选值,Table / Select 使用 Ant Design 自带的加载状态。加载完成后显示表单:
|
||||
|
||||
**通用字段**
|
||||
|
||||
| 字段 | 控件 | 说明 |
|
||||
|------|------|------|
|
||||
| `name` | Input | 配置文件显示名称 |
|
||||
| `project_dir` | Input + 目录选择 | 项目目录绝对路径 |
|
||||
| `minecraft_properties_dir` | Input | 默认 `./properties` |
|
||||
| `project_properties_path` | Input | 默认 `./gradle.properties` |
|
||||
| `mod_version_field` | Input | 默认 `mod_version` |
|
||||
| `filename_format` | Input | 含 `${version}` `${mc_version}` |
|
||||
| `source_filename_format` | Input | 含 `${version}` `${mc_version}` |
|
||||
|
||||
**Modrinth 字段**
|
||||
|
||||
| 字段 | 控件 | 说明 |
|
||||
|------|------|------|
|
||||
| `project_id` | Input | 项目 ID(8 位 base62)。输入后使用 Ant Design 防抖 hook 自动查询项目名称并显示在输入框下方以供确认 |
|
||||
| `version_name` | Input | 模板字符串,含占位符。默认 `"ModName v${version} for Minecraft ${mc_version_range}"`。使用防抖自动显示匹配到的版本数量 |
|
||||
| `version` | Input | 模板字符串,含占位符 |
|
||||
| `loaders` | Select(多选) | 选项从 `GET /tag/loader` 获取 |
|
||||
| `environment` | Select | 运行环境,必填。选项使用 Ant Design OptGroup 分组,具体分组参考 `docs/modrinth/upload.md` 中的 `environment` 枚举值章节 |
|
||||
| `dependencies` | 动态列表 | 每项含 `dependency_type`(Select:required/optional/incompatible/embedded)和 `project_id`(Input) |
|
||||
|
||||
**CurseForge 字段**
|
||||
|
||||
| 字段 | 控件 | 说明 |
|
||||
|------|------|------|
|
||||
| `project_id` | InputNumber | 项目 ID(数字)。输入后使用 Ant Design 防抖 hook 自动查询项目名称并显示在输入框下方以供确认 |
|
||||
| `version_name` | Input | 模板字符串,含占位符。默认 `"${filename_format}"`。使用防抖自动显示匹配到的版本数量 |
|
||||
| `environment` | Select(多选) | 选项从 Game Version Types API(Environment 类型)获取 |
|
||||
| `loaders` | Select(多选) | 选项从 Game Version Types API(Modloader 类型)获取 |
|
||||
| `relations` | 动态列表 | 每项含 `slug`(Input)和 `type`(Select:embeddedLibrary/incompatible/optionalDependency/requiredDependency/tool) |
|
||||
|
||||
> **防抖策略**:`project_id` 和 `version_name` 的防抖延迟需设置较长(建议 800ms~1s),避免触发两个平台 API 的速率限制。
|
||||
|
||||
底部:保存按钮,写入 `configs/{name}.json`。
|
||||
|
||||
92
docs/index.md
Normal file
92
docs/index.md
Normal file
@ -0,0 +1,92 @@
|
||||
# Mod Releaser
|
||||
|
||||
一个基于 Web 界面的本地工具,用于通过 API 向 Modrinth 和 CurseForge 一键发布 mod 新版本。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **框架**:Next.js(前后端不分离,WebUI 作为本地界面)
|
||||
- **UI 库**:Ant Design
|
||||
- **平台 API**:Modrinth API、CurseForge API
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
/
|
||||
├── secrets.json # API 密钥(curseforge / modrinth)
|
||||
├── configs/
|
||||
│ └── {config_name}.json # 各 mod 的配置
|
||||
├── public/ # 静态资源(图标、图片等)
|
||||
├── src/
|
||||
│ ├── app/ # Next.js App Router(页面、布局)
|
||||
│ ├── components/ # 可复用 UI 组件
|
||||
│ ├── lib/ # 后端逻辑(与前端直接调用,不创建 API Route)
|
||||
│ └── types/ # TypeScript 类型定义
|
||||
├── docs/ # 本文档
|
||||
├── package.json
|
||||
└── ...
|
||||
```
|
||||
|
||||
## 开发规范
|
||||
|
||||
### 前后端通信
|
||||
|
||||
使用 Next.js Server Actions 或直接在 Server Component 中调用后端函数,**不创建 API Route**。前端通过 `"use server"` 指令的函数或 Server Component 的 `async` 逻辑直接调用 `src/lib/` 中的后端代码,避免前后端分离的冗余 API 层。
|
||||
|
||||
### `src/` 目录说明
|
||||
|
||||
| 目录 | 职责 |
|
||||
|------|------|
|
||||
| `app/` | Next.js App Router 页面与布局 |
|
||||
| `components/` | 可复用的 Ant Design 组件 |
|
||||
| `lib/` | 纯后端逻辑(读取配置、调用 API、模板解析等),可供前端直接引用 |
|
||||
| `types/` | TypeScript 类型/接口定义 |
|
||||
|
||||
### 代码质量
|
||||
|
||||
- 每个文件行数不宜过多,单一职责,低耦合高内聚
|
||||
- 尽可能减少技术债,写出可维护性高的代码
|
||||
- 组件拆分粒度合理,逻辑与 UI 分离
|
||||
|
||||
## 文档目录
|
||||
|
||||
- [frontend.md](./frontend.md) — 前端页面与组件(WebUI)
|
||||
- [modrinth/](./modrinth/) — Modrinth API 参考
|
||||
- [curseforge/](./curseforge/) — CurseForge API 参考
|
||||
- [workflow.md](./workflow.md) — 整体发布流程、状态管理、错误处理
|
||||
|
||||
## 参考文档
|
||||
|
||||
编写代码时,如需查阅框架或组件库文档,通过 fetch 工具按以下体系获取:
|
||||
|
||||
### Modrinth API
|
||||
|
||||
参考 `docs/modrinth/` 目录(忽略 `openapi.yml`):
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `upload.md` | 新建版本 API(`POST /version`) |
|
||||
| `get.md` | 获取版本信息 API(列出项目版本、获取单个版本等) |
|
||||
| `get-meta.md` | 元数据查询 API(加载器列表、游戏版本列表等) |
|
||||
|
||||
### CurseForge API
|
||||
|
||||
参考 `docs/curseforge/` 目录:
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `upload.md` | 上传文件 API(含 Game Version Types / Game Versions 查询) |
|
||||
| `get.md` | 获取文件列表 API(`GET /mods/{modId}/files`) |
|
||||
|
||||
### 前端框架与组件库
|
||||
|
||||
#### Next.js
|
||||
|
||||
- `nextjs.org/docs/llms.txt` — 目录索引(带描述)
|
||||
- 每页 `.md` — 逐页获取正文
|
||||
|
||||
#### Ant Design
|
||||
|
||||
- `ant.design/llms.txt` — 目录索引(纯链接)
|
||||
- `ant.design/design.md` — 设计语言上下文
|
||||
- 每页 `.md` — 逐页获取正文
|
||||
- 每组件 `semantic.md` — 单个组件语义结构
|
||||
284
docs/modrinth/get-meta.md
Normal file
284
docs/modrinth/get-meta.md
Normal file
@ -0,0 +1,284 @@
|
||||
# Modrinth 元数据查询 API 使用文档
|
||||
|
||||
本文档描述如何获取 Modrinth 平台支持的**所有加载器(loaders)** 和 **Minecraft 游戏版本(game versions)**,以及项目类型、分类等元数据。这些接口均为**公开接口,无需认证**。
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **生产环境** | `https://api.modrinth.com/v2` |
|
||||
| **认证** | 无需认证(全部公开) |
|
||||
| **Rate Limit** | 每 IP 每分钟 300 请求 |
|
||||
|
||||
---
|
||||
|
||||
## 接口一:获取所有加载器
|
||||
|
||||
获取 Modrinth 支持的全部模组加载器列表。
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **端点** | `GET /tag/loader` |
|
||||
| **认证** | 否 |
|
||||
| **成功响应码** | `200` |
|
||||
| **响应体** | `LoaderTag[]` — 加载器对象数组 |
|
||||
|
||||
### 响应字段(LoaderTag)
|
||||
|
||||
| 字段 | 类型 | 说明 | 示例 |
|
||||
|------|------|------|------|
|
||||
| `name` | `string` | 加载器名称 | `"fabric"` |
|
||||
| `icon` | `string` | 加载器的 SVG 图标 | `"<svg>...</svg>"` |
|
||||
| `supported_project_types` | `string[]` | 该加载器适用的项目类型 | `["mod", "modpack"]` |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl "https://api.modrinth.com/v2/tag/loader"
|
||||
```
|
||||
|
||||
```js
|
||||
const response = await fetch("https://api.modrinth.com/v2/tag/loader");
|
||||
const loaders = await response.json();
|
||||
|
||||
for (const loader of loaders) {
|
||||
console.log(`${loader.name} — 适用于: ${loader.supported_project_types.join(", ")}`);
|
||||
}
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"icon": "<svg>...</svg>",
|
||||
"name": "fabric",
|
||||
"supported_project_types": ["mod", "modpack"]
|
||||
},
|
||||
{
|
||||
"icon": "<svg>...</svg>",
|
||||
"name": "forge",
|
||||
"supported_project_types": ["mod", "modpack"]
|
||||
},
|
||||
{
|
||||
"icon": "<svg>...</svg>",
|
||||
"name": "quilt",
|
||||
"supported_project_types": ["mod", "modpack"]
|
||||
},
|
||||
{
|
||||
"icon": "<svg>...</svg>",
|
||||
"name": "neoforge",
|
||||
"supported_project_types": ["mod", "modpack"]
|
||||
},
|
||||
{
|
||||
"icon": "<svg>...</svg>",
|
||||
"name": "minecraft",
|
||||
"supported_project_types": ["resourcepack", "shader"]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
> **注意:** `"minecraft"` 加载器用于资源包(resourcepacks)、光影等不需要模组加载器的项目类型。
|
||||
|
||||
---
|
||||
|
||||
## 接口二:获取所有游戏版本
|
||||
|
||||
获取 Modrinth 支持的全部 Minecraft 版本列表,包含版本类型和发布日期。
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **端点** | `GET /tag/game_version` |
|
||||
| **认证** | 否 |
|
||||
| **成功响应码** | `200` |
|
||||
| **响应体** | `GameVersionTag[]` — 游戏版本对象数组 |
|
||||
|
||||
### 响应字段(GameVersionTag)
|
||||
|
||||
| 字段 | 类型 | 说明 | 示例 |
|
||||
|------|------|------|------|
|
||||
| `version` | `string` | 游戏版本号 | `"1.18.1"` |
|
||||
| `version_type` | `string` | 版本类型:`release`、`snapshot`、`alpha`、`beta` | `"release"` |
|
||||
| `date` | `string` | 版本发布日期(ISO-8601) | `"2021-12-10T00:00:00Z"` |
|
||||
| `major` | `boolean` | 是否为主要版本,用于 Featured Versions 标记 | `true` |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl "https://api.modrinth.com/v2/tag/game_version"
|
||||
```
|
||||
|
||||
```js
|
||||
const response = await fetch("https://api.modrinth.com/v2/tag/game_version");
|
||||
const versions = await response.json();
|
||||
|
||||
// 仅列出正式发布版
|
||||
for (const v of versions.filter(v => v.version_type === "release")) {
|
||||
console.log(`${v.version} (${v.version_type})${v.major ? " [主要版本]" : ""}`);
|
||||
}
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"version": "1.21.4",
|
||||
"version_type": "release",
|
||||
"date": "2025-04-04T00:00:00Z",
|
||||
"major": true
|
||||
},
|
||||
{
|
||||
"version": "1.21.3",
|
||||
"version_type": "release",
|
||||
"date": "2024-11-02T00:00:00Z",
|
||||
"major": false
|
||||
},
|
||||
{
|
||||
"version": "1.20.1",
|
||||
"version_type": "release",
|
||||
"date": "2023-06-12T00:00:00Z",
|
||||
"major": true
|
||||
},
|
||||
{
|
||||
"version": "24w45a",
|
||||
"version_type": "snapshot",
|
||||
"date": "2024-11-06T00:00:00Z",
|
||||
"major": false
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 辅助接口:项目类型
|
||||
|
||||
获取 Modrinth 支持的项目类型,用于了解加载器适用哪些类型的项目。
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **端点** | `GET /tag/project_type` |
|
||||
| **响应体** | `string[]` |
|
||||
|
||||
```bash
|
||||
curl "https://api.modrinth.com/v2/tag/project_type"
|
||||
```
|
||||
|
||||
```json
|
||||
["mod", "modpack", "resourcepack", "shader"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 辅助接口:分类标签
|
||||
|
||||
获取 Modrinth 的项目分类列表(含图标和适用项目类型)。
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **端点** | `GET /tag/category` |
|
||||
| **响应体** | `CategoryTag[]` |
|
||||
|
||||
```bash
|
||||
curl "https://api.modrinth.com/v2/tag/category"
|
||||
```
|
||||
|
||||
响应字段:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `icon` | `string` | 分类的 SVG 图标 |
|
||||
| `name` | `string` | 分类名称 |
|
||||
| `project_type` | `string` | 该分类适用的项目类型 |
|
||||
| `header` | `string` | 该分类所属的标题分组 |
|
||||
|
||||
---
|
||||
|
||||
## 常见使用场景
|
||||
|
||||
### 场景 1:构建上传表单的下拉选项
|
||||
|
||||
在发布模组的 UI 中,动态获取可选加载器和游戏版本:
|
||||
|
||||
```js
|
||||
const [loaders, gameVersions] = await Promise.all([
|
||||
fetch("https://api.modrinth.com/v2/tag/loader").then(r => r.json()),
|
||||
fetch("https://api.modrinth.com/v2/tag/game_version").then(r => r.json())
|
||||
]);
|
||||
|
||||
// 加载器下拉
|
||||
const loaderNames = loaders.map(l => l.name);
|
||||
// → ["fabric", "forge", "quilt", "neoforge", "minecraft", ...]
|
||||
|
||||
// 仅正式版游戏版本
|
||||
const releaseVersions = gameVersions
|
||||
.filter(v => v.version_type === "release")
|
||||
.map(v => v.version);
|
||||
// → ["1.21.4", "1.21.3", "1.20.4", ...]
|
||||
```
|
||||
|
||||
### 场景 2:验证用户输入是否合法
|
||||
|
||||
在上传版本前,校验 `loaders` 和 `game_versions` 中填的值是否被 Modrinth 接受:
|
||||
|
||||
```js
|
||||
const validLoaders = new Set(loaders.map(l => l.name));
|
||||
const validVersions = new Set(gameVersions.map(v => v.version));
|
||||
|
||||
const invalidLoaders = userInputLoaders.filter(l => !validLoaders.has(l));
|
||||
const invalidVersions = userInputVersions.filter(v => !validVersions.has(v));
|
||||
|
||||
if (invalidLoaders.length > 0 || invalidVersions.length > 0) {
|
||||
console.error("不合法的值:", { invalidLoaders, invalidVersions });
|
||||
}
|
||||
```
|
||||
|
||||
### 场景 3:获取某个加载器支持的 project_type
|
||||
|
||||
用于判断某个加载器是否适用于 mod、modpack 等类型:
|
||||
|
||||
```js
|
||||
const loaders = await fetch("https://api.modrinth.com/v2/tag/loader").then(r => r.json());
|
||||
|
||||
const fabricInfo = loaders.find(l => l.name === "fabric");
|
||||
console.log(fabricInfo.supported_project_types); // → ["mod", "modpack"]
|
||||
|
||||
// 判断 fabric 是否支持 mod 类型
|
||||
const supportsMod = fabricInfo.supported_project_types.includes("mod"); // → true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 缓存建议
|
||||
|
||||
这些元数据接口的返回值在短期内不会频繁变化,强烈建议在客户端做缓存:
|
||||
|
||||
- **加载器列表**:以天为单位缓存(加载器新增频率极低)。
|
||||
- **游戏版本列表**:以小时或天为单位缓存(仅在 Minecraft 新版本发布时需要刷新)。
|
||||
|
||||
```js
|
||||
// 简单的内存缓存示例
|
||||
let cachedLoaders = null;
|
||||
let cacheTime = 0;
|
||||
const CACHE_TTL = 86400000; // 24 小时
|
||||
|
||||
async function getLoaders() {
|
||||
if (cachedLoaders && Date.now() - cacheTime < CACHE_TTL) {
|
||||
return cachedLoaders;
|
||||
}
|
||||
cachedLoaders = await fetch("https://api.modrinth.com/v2/tag/loader").then(r => r.json());
|
||||
cacheTime = Date.now();
|
||||
return cachedLoaders;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 参考
|
||||
|
||||
- [Modrinth API 文档](https://docs.modrinth.com)
|
||||
- [OpenAPI 规范文件](openapi.yml)
|
||||
- [获取版本信息 API](get.md)
|
||||
- [新建版本 API](upload.md)
|
||||
402
docs/modrinth/get.md
Normal file
402
docs/modrinth/get.md
Normal file
@ -0,0 +1,402 @@
|
||||
# Modrinth 获取版本信息 API 使用文档
|
||||
|
||||
本文档描述如何通过 Modrinth API 获取项目的版本列表以及每个版本的详细信息,包括版本名(`name`)、版本号(`version_number`)、文件名(`filename`)、下载地址等。
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
|
||||
所有版本读取接口均为 **公开接口,无需认证**。但如果项目包含 `draft`(草稿)状态的版本,获取它们可能需要认证。
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **生产环境** | `https://api.modrinth.com/v2` |
|
||||
| **测试环境** | `https://staging-api.modrinth.com/v2` |
|
||||
| **认证** | 公开接口无需认证;获取草稿版本需要 PAT |
|
||||
| **Rate Limit** | 每 IP 每分钟 300 请求 |
|
||||
|
||||
---
|
||||
|
||||
## 接口一:列出项目的所有版本
|
||||
|
||||
获取指定项目下的全部版本列表。这是获取版本信息最常用的入口。
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **端点** | `GET /project/{id\|slug}/version` |
|
||||
| **认证** | 否(公开) |
|
||||
| **成功响应码** | `200` |
|
||||
| **响应体** | `Version[]` — Version 对象数组 |
|
||||
|
||||
### 路径参数
|
||||
|
||||
| 参数 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `id\|slug` | 项目的 8 位 base62 ID 或 slug(别名) | `AABBCCDD` 或 `my-mod` |
|
||||
|
||||
### 查询参数(可选,用于过滤)
|
||||
|
||||
| 参数 | 类型 | 说明 | 示例 |
|
||||
|------|------|------|------|
|
||||
| `loaders` | `string` | 按加载器过滤,JSON 数组字符串 | `'["fabric"]'` |
|
||||
| `game_versions` | `string` | 按 Minecraft 版本过滤,JSON 数组字符串 | `'["1.20.4"]'` |
|
||||
| `featured` | `boolean` | 按是否精选过滤 | `true` |
|
||||
| `include_changelog` | `boolean` | 是否在响应中包含 `changelog` 字段。**强烈建议设为 `false`** 以减小响应体,除非确实需要更新日志内容 | `false` |
|
||||
|
||||
### 请求示例
|
||||
|
||||
**获取某项目的全部版本(不含 changelog):**
|
||||
|
||||
```bash
|
||||
curl "https://api.modrinth.com/v2/project/AABBCCDD/version?include_changelog=false"
|
||||
```
|
||||
|
||||
**按加载器和游戏版本过滤:**
|
||||
|
||||
```bash
|
||||
curl "https://api.modrinth.com/v2/project/AABBCCDD/version?loaders=%5B%22fabric%22%5D&game_versions=%5B%221.20.4%22%5D"
|
||||
```
|
||||
|
||||
> 注意:`loaders` 和 `game_versions` 的值需要是 JSON 数组字符串(如 `["fabric"]`),在 URL 中需要做 URL 编码。`[...]` 编码后为 `%5B...%5D`。
|
||||
|
||||
**Node.js (fetch) 示例:**
|
||||
|
||||
```js
|
||||
const projectId = "AABBCCDD";
|
||||
|
||||
const url = new URL(`https://api.modrinth.com/v2/project/${projectId}/version`);
|
||||
url.searchParams.set("include_changelog", "false");
|
||||
|
||||
const response = await fetch(url);
|
||||
const versions = await response.json();
|
||||
|
||||
for (const v of versions) {
|
||||
console.log(`版本名: ${v.name}`);
|
||||
console.log(`版本号: ${v.version_number}`);
|
||||
for (const f of v.files) {
|
||||
console.log(` 文件: ${f.filename} (${f.size} bytes) ${f.primary ? "[主文件]" : ""}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 接口二:获取单个版本
|
||||
|
||||
根据版本 ID 获取某个版本的完整信息。
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **端点** | `GET /version/{id}` |
|
||||
| **认证** | 否(公开) |
|
||||
| **成功响应码** | `200` |
|
||||
| **响应体** | `Version` 对象 |
|
||||
|
||||
### 路径参数
|
||||
|
||||
| 参数 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `id` | 版本 ID(8 位 base62 字符串) | `IIJJKKLL` |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl "https://api.modrinth.com/v2/version/IIJJKKLL"
|
||||
```
|
||||
|
||||
```js
|
||||
const versionId = "IIJJKKLL";
|
||||
const response = await fetch(`https://api.modrinth.com/v2/version/${versionId}`);
|
||||
const version = await response.json();
|
||||
console.log(version.name, version.version_number);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 接口三:通过项目 + 版本号获取版本
|
||||
|
||||
如果你知道项目 ID 和版本号(而非版本 ID),可以用此接口直接获取。
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **端点** | `GET /project/{id\|slug}/version/{id\|number}` |
|
||||
| **认证** | 否(公开) |
|
||||
| **成功响应码** | `200` |
|
||||
| **响应体** | `Version` 对象 |
|
||||
|
||||
### 路径参数
|
||||
|
||||
| 参数 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `id\|slug` | 项目 ID 或 slug | `AABBCCDD` |
|
||||
| `id\|number` | 版本 ID 或版本号字符串 | `1.0.0` 或 `IIJJKKLL` |
|
||||
|
||||
> **注意:** 如果版本号匹配到多个版本,只会返回 **最旧的** 那个。建议使用版本 ID 获得精确结果。
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl "https://api.modrinth.com/v2/project/AABBCCDD/version/1.0.0"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 接口四:批量获取多个版本
|
||||
|
||||
一次请求获取多个指定版本的信息。
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **端点** | `GET /versions` |
|
||||
| **认证** | 否(公开) |
|
||||
| **成功响应码** | `200` |
|
||||
| **响应体** | `Version[]` — Version 对象数组 |
|
||||
|
||||
### 查询参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 | 示例 |
|
||||
|------|------|------|------|------|
|
||||
| `ids` | `string` | ✅ | JSON 数组字符串,包含版本 ID 列表 | `'["IIJJKKLL", "MMNNOOPP"]'` |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl "https://api.modrinth.com/v2/versions?ids=%5B%22IIJJKKLL%22%2C%22MMNNOOPP%22%5D"
|
||||
```
|
||||
|
||||
```js
|
||||
const versionIds = ["IIJJKKLL", "MMNNOOPP"];
|
||||
const url = new URL("https://api.modrinth.com/v2/versions");
|
||||
url.searchParams.set("ids", JSON.stringify(versionIds));
|
||||
const response = await fetch(url);
|
||||
const versions = await response.json();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 响应格式
|
||||
|
||||
### Version 对象结构
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "IIJJKKLL",
|
||||
"project_id": "AABBCCDD",
|
||||
"author_id": "EEFFGGHH",
|
||||
"name": "Version 1.0.0",
|
||||
"version_number": "1.0.0",
|
||||
"changelog": "## 更新内容\n\n- 修复了若干 Bug",
|
||||
"date_published": "2025-01-01T00:00:00Z",
|
||||
"downloads": 1024,
|
||||
"version_type": "release",
|
||||
"status": "listed",
|
||||
"requested_status": null,
|
||||
"game_versions": ["1.20.1", "1.20.4"],
|
||||
"loaders": ["fabric"],
|
||||
"featured": false,
|
||||
"dependencies": [
|
||||
{
|
||||
"dependency_type": "required",
|
||||
"project_id": "P7dR8mSH",
|
||||
"version_id": null,
|
||||
"file_name": null
|
||||
}
|
||||
],
|
||||
"files": [
|
||||
{
|
||||
"hashes": {
|
||||
"sha512": "93ecf5fe...",
|
||||
"sha1": "c84dd4b3..."
|
||||
},
|
||||
"url": "https://cdn.modrinth.com/data/AABBCCDD/versions/1.0.0/my-mod-1.0.0.jar",
|
||||
"filename": "my-mod-1.0.0.jar",
|
||||
"primary": true,
|
||||
"size": 1097270,
|
||||
"file_type": null
|
||||
},
|
||||
{
|
||||
"hashes": {
|
||||
"sha512": "ab12cd34...",
|
||||
"sha1": "ef56gh78..."
|
||||
},
|
||||
"url": "https://cdn.modrinth.com/data/AABBCCDD/versions/1.0.0/my-mod-1.0.0-sources.jar",
|
||||
"filename": "my-mod-1.0.0-sources.jar",
|
||||
"primary": false,
|
||||
"size": 543210,
|
||||
"file_type": "sources-jar"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 关键字段说明
|
||||
|
||||
#### Version 顶层字段
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | `string` | 版本唯一 ID(8 位 base62) |
|
||||
| `project_id` | `string` | 所属项目 ID |
|
||||
| `author_id` | `string` | 发布者用户 ID |
|
||||
| `name` | `string` | **版本名称**(如 `"Version 1.0.0"`) |
|
||||
| `version_number` | `string` | **版本号**(如 `"1.0.0"`,建议遵循语义化版本) |
|
||||
| `changelog` | `string \| null` | 更新日志文本(若使用 `include_changelog=false` 则为 `null`) |
|
||||
| `date_published` | `string` | 发布时间(ISO-8601 格式) |
|
||||
| `downloads` | `integer` | 下载次数 |
|
||||
| `version_type` | `string` | 发布渠道:`release`、`beta`、`alpha` |
|
||||
| `status` | `string` | 版本状态:`listed`、`archived`、`draft`、`unlisted`、`scheduled` |
|
||||
| `game_versions` | `string[]` | 支持的 Minecraft 版本列表 |
|
||||
| `loaders` | `string[]` | 支持的加载器列表 |
|
||||
| `featured` | `boolean` | 是否为精选版本 |
|
||||
| `dependencies` | `object[]` | 依赖列表(结构见下方) |
|
||||
| `files` | `object[]` | 该版本的所有文件列表 |
|
||||
|
||||
#### dependencies 数组项
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `dependency_type` | `string` | `required`、`optional`、`incompatible`、`embedded` |
|
||||
| `project_id` | `string \| null` | 依赖的项目 ID |
|
||||
| `version_id` | `string \| null` | 依赖的具体版本 ID |
|
||||
| `file_name` | `string \| null` | 依赖文件名 |
|
||||
|
||||
#### files 数组项(最常需要关注的字段)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `filename` | `string` | **文件名**(如 `"my-mod-1.0.0.jar"`) |
|
||||
| `url` | `string` | 文件直链下载地址 |
|
||||
| `size` | `integer` | 文件大小(字节) |
|
||||
| `primary` | `boolean` | 是否为主文件。每个版本最多一个主文件;若没有一个标记为 `true`,则推断第一个文件为主文件 |
|
||||
| `hashes.sha1` | `string` | 文件的 SHA-1 哈希 |
|
||||
| `hashes.sha512` | `string` | 文件的 SHA-512 哈希 |
|
||||
| `file_type` | `string \| null` | 文件类型:`sources-jar`、`dev-jar`、`javadoc-jar`、`required-resource-pack`、`optional-resource-pack`、`unknown`、`signature`。普通主 jar 为 `null` |
|
||||
|
||||
---
|
||||
|
||||
## 常见使用场景
|
||||
|
||||
### 场景 1:获取项目的最新版本
|
||||
|
||||
从版本列表中取第一个(通常是时间最新的),再拿到文件信息:
|
||||
|
||||
```js
|
||||
const projectSlug = "my-mod";
|
||||
const response = await fetch(
|
||||
`https://api.modrinth.com/v2/project/${projectSlug}/version?include_changelog=false`
|
||||
);
|
||||
const versions = await response.json();
|
||||
|
||||
const latest = versions[0];
|
||||
console.log(`版本: ${latest.name} (${latest.version_number})`);
|
||||
console.log(`主文件: ${latest.files.find(f => f.primary).filename}`);
|
||||
```
|
||||
|
||||
### 场景 2:列出某版本的所有文件及其类型
|
||||
|
||||
```js
|
||||
const versionId = "IIJJKKLL";
|
||||
const response = await fetch(`https://api.modrinth.com/v2/version/${versionId}`);
|
||||
const version = await response.json();
|
||||
|
||||
for (const file of version.files) {
|
||||
const typeLabel = file.file_type ?? "主 jar";
|
||||
console.log(`[${typeLabel}] ${file.filename} — ${file.url}`);
|
||||
}
|
||||
```
|
||||
|
||||
输出示例:
|
||||
|
||||
```
|
||||
[主 jar] my-mod-1.0.0.jar — https://cdn.modrinth.com/.../my-mod-1.0.0.jar
|
||||
[sources-jar] my-mod-1.0.0-sources.jar — https://cdn.modrinth.com/.../my-mod-1.0.0-sources.jar
|
||||
```
|
||||
|
||||
### 场景 3:下载版本的主文件(通过直链)
|
||||
|
||||
```js
|
||||
const version = await fetch("https://api.modrinth.com/v2/version/IIJJKKLL").then(r => r.json());
|
||||
const primaryFile = version.files.find(f => f.primary) ?? version.files[0];
|
||||
|
||||
const fileBuffer = await fetch(primaryFile.url).then(r => r.arrayBuffer());
|
||||
require("fs").writeFileSync(primaryFile.filename, Buffer.from(fileBuffer));
|
||||
```
|
||||
|
||||
### 场景 4:仅获取特定加载器 + 游戏版本的版本
|
||||
|
||||
```js
|
||||
const projectId = "AABBCCDD";
|
||||
const url = new URL(`https://api.modrinth.com/v2/project/${projectId}/version`);
|
||||
url.searchParams.set("loaders", JSON.stringify(["fabric"]));
|
||||
url.searchParams.set("game_versions", JSON.stringify(["1.20.4"]));
|
||||
url.searchParams.set("include_changelog", "false");
|
||||
|
||||
const versions = await fetch(url).then(r => r.json());
|
||||
for (const v of versions) {
|
||||
console.log(`${v.name} (${v.version_number}) — ${v.files.find(f => f.primary)?.filename}`);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 错误码
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| `200` | 成功 |
|
||||
| `404` | 项目不存在、版本不存在,或无权访问(如草稿版本) |
|
||||
|
||||
---
|
||||
|
||||
## 注意事项与最佳实践
|
||||
|
||||
### 1. 始终使用 `include_changelog=false`
|
||||
|
||||
除非你真的需要每个版本的 changelog 文本,否则务必传入 `include_changelog=false`。changelog 可能非常长,会显著增大响应体、降低请求速度。Modrinth 官方也强烈建议这样做。
|
||||
|
||||
### 2. 主文件的判断规则
|
||||
|
||||
`files` 数组中若有一个 `primary: true`,那就是主文件。如果一个都没有,则**推断第一个文件为主文件**。不要假定 `files[0]` 一定是主 jar——sources jar 也可能排在首位。
|
||||
|
||||
### 3. 版本列表的排序
|
||||
|
||||
`GET /project/{id|slug}/version` 返回的列表**按发布日期降序排列**(最新的在前)。若需确定顺序,请以 `date_published` 字段为准。
|
||||
|
||||
### 4. 别忘了 User-Agent
|
||||
|
||||
即使是公开 GET 请求,Modrinth 也要求设置 `User-Agent` 请求头:
|
||||
|
||||
```
|
||||
User-Agent: your_username/your_project/1.0.0 (contact@example.com)
|
||||
```
|
||||
|
||||
### 5. 通过 slug 访问更易读
|
||||
|
||||
项目标识符支持 slug(如 `my-mod`)而非只能用 ID(如 `AABBCCDD`),对于调试和可读性更有帮助。但 slug 可能被项目所有者修改,长期存储建议使用 ID。
|
||||
|
||||
### 6. 版本号可能不唯一
|
||||
|
||||
`GET /project/{id|slug}/version/{id\|number}` 在使用版本号查找时,若遇到重名只返回最旧的那个。精确获取请使用版本 ID。
|
||||
|
||||
### 7. 分页
|
||||
|
||||
`GET /project/{id|slug}/version` **不支持分页参数**,会一次性返回该项目所有版本。对于版本数量极多的项目,响应体可能较大,此时推荐结合 `loaders` 和 `game_versions` 过滤。
|
||||
|
||||
---
|
||||
|
||||
## 可用加载器与游戏版本查询
|
||||
|
||||
如果你不确定 `loaders` 或 `game_versions` 过滤参数应填什么值,可以通过标签接口查询所有合法值:
|
||||
|
||||
```bash
|
||||
curl "https://api.modrinth.com/v2/tag/loader"
|
||||
curl "https://api.modrinth.com/v2/tag/game_version"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 参考
|
||||
|
||||
- [Modrinth API 文档](https://docs.modrinth.com)
|
||||
- [OpenAPI 规范文件](openapi.yml)
|
||||
- [新建版本 API 文档](upload.md)
|
||||
3984
docs/modrinth/openapi.yml
Normal file
3984
docs/modrinth/openapi.yml
Normal file
File diff suppressed because it is too large
Load Diff
407
docs/modrinth/upload.md
Normal file
407
docs/modrinth/upload.md
Normal file
@ -0,0 +1,407 @@
|
||||
# Modrinth 新建版本 API 使用文档
|
||||
|
||||
本文档详细描述如何通过 Modrinth API 创建一个新版本,包括上传模组主文件(`.jar`)和一个 `-sources.jar` 附属文件。
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **端点** | `POST /version` |
|
||||
| **生产环境** | `https://api.modrinth.com/v2/version` |
|
||||
| **测试环境** | `https://staging-api.modrinth.com/v2/version` |
|
||||
| **Content-Type** | `multipart/form-data` |
|
||||
| **认证** | 必需 — Personal Access Token (PAT) |
|
||||
| **所需 Scope** | `VERSION_CREATE` |
|
||||
| **成功响应码** | `200` |
|
||||
| **成功响应体** | [Version](#响应格式) 对象 |
|
||||
|
||||
---
|
||||
|
||||
## 认证
|
||||
|
||||
所有创建数据的请求都需要认证。你需要一个 **Personal Access Token**(PAT),在 [Modrinth 用户设置](https://modrinth.com/settings/account) 中生成。
|
||||
|
||||
创建版本需要 PAT 具有 `VERSION_CREATE` 权限范围。
|
||||
|
||||
将 Token 放在 `Authorization` 请求头中:
|
||||
|
||||
```
|
||||
Authorization: mrp_RNtLRSPmGj2pd1v1ubi52nX7TJJM9sznrmwhAuj511oe4t1jAqAQ3D6Wc8Ic
|
||||
```
|
||||
|
||||
> **注意:** 需要在 `upload.md` 中提供有效的 Token。
|
||||
|
||||
同时,Modrinth **要求**提供一个可唯一标识的 `User-Agent` 请求头:
|
||||
|
||||
```
|
||||
User-Agent: github_username/project_name/1.0.0 (contact@example.com)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 请求格式
|
||||
|
||||
请求是一个 **multipart/form-data** 请求,必须包含至少两个表单字段:
|
||||
|
||||
- **`data`**:一个 JSON 字符串,包含版本元数据(见下方 [data JSON 结构](#data-json-结构))。
|
||||
- **一个或多个文件字段**:包含要上传的 `.jar`、`.mrpack`、`.zip` 或 `.litemod` 文件。文件字段名可自定义。
|
||||
|
||||
### 重要概念区分:multipart 字段名 vs 文件名
|
||||
|
||||
在 multipart 请求中,每个文件字段都有**两个不同的名字**:
|
||||
|
||||
```
|
||||
-F "main-file=@./build/libs/my-mod-1.0.0.jar"
|
||||
^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
|
||||
multipart 字段名 (field name) 文件名 (filename)
|
||||
```
|
||||
|
||||
- **multipart 字段名**(如 `main-file`):在请求体中标识这一部分数据的 key,由你自定义。
|
||||
- **文件名**(如 `my-mod-1.0.0.jar`):被上传文件的原始名称。
|
||||
|
||||
> **关键规则:`file_parts`、`primary_file`、`file_types` 的 key 全部使用 multipart 字段名,而不是文件名。**
|
||||
|
||||
### `file_parts` — 声明有哪些文件
|
||||
|
||||
在 `data` JSON 的 `file_parts` 数组中列出所有承载文件的 **multipart 字段名**,并在 `primary_file` 中指定哪个是主文件。
|
||||
|
||||
```json
|
||||
{
|
||||
"file_parts": ["main-file", "sources-file", "dev-file"],
|
||||
"primary_file": "main-file"
|
||||
}
|
||||
```
|
||||
|
||||
### `file_types` — 标记附属文件类型
|
||||
|
||||
`file_types` 是一个 **对象(map)**,**key 是 multipart 字段名**(与 `file_parts` 中的值一致),value 是该文件的类型枚举值。用于标记 `-sources.jar`、`-dev.jar` 等附属文件。
|
||||
|
||||
可用的文件类型(`FileTypeEnum`):
|
||||
|
||||
| 值 | 含义 |
|
||||
|----|------|
|
||||
| `required-resource-pack` | 必需资源包 |
|
||||
| `optional-resource-pack` | 可选资源包 |
|
||||
| `sources-jar` | 源代码 JAR |
|
||||
| `dev-jar` | 开发版 JAR(包含未混淆的类) |
|
||||
| `javadoc-jar` | Javadoc JAR |
|
||||
| `unknown` | 未知类型 |
|
||||
| `signature` | 签名文件 |
|
||||
|
||||
**标记 sources.jar 的示例:**
|
||||
|
||||
```json
|
||||
{
|
||||
"file_types": {
|
||||
"sources-file": "sources-jar"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 上例中 `"sources-file"` 是 multipart 字段名,需要在 `file_parts` 中同步出现。
|
||||
|
||||
---
|
||||
|
||||
## data JSON 结构
|
||||
|
||||
`data` 字段的值是一个 JSON 字符串,对应 `CreatableVersion` schema,它扩展了 `BaseVersion`。
|
||||
|
||||
### 顶层结构(`CreateVersionBody`)
|
||||
|
||||
```json
|
||||
{
|
||||
"data": "{ ... CreatableVersion JSON ... }"
|
||||
}
|
||||
```
|
||||
|
||||
### CreatableVersion 字段一览
|
||||
|
||||
#### 必填字段
|
||||
|
||||
| 字段 | 类型 | 说明 | 示例 |
|
||||
|------|------|------|------|
|
||||
| `project_id` | `string` | 目标项目的 ID(8 位 base62) | `"AABBCCDD"` |
|
||||
| `file_parts` | `string[]` | 所有承载文件的 **multipart 字段名** 列表 | `["main-file", "sources-file"]` |
|
||||
| `name` | `string` | 版本名称 | `"Version 1.0.0"` |
|
||||
| `version_number` | `string` | 版本号,建议遵循语义化版本 | `"1.0.0"` |
|
||||
| `game_versions` | `string[]` | 支持的 Minecraft 版本列表 | `["1.20.1", "1.20.4"]` |
|
||||
| `version_type` | `string` | 发布渠道:`release`、`beta`、`alpha` | `"release"` |
|
||||
| `loaders` | `string[]` | 支持的模组加载器列表 | `["fabric", "forge"]` |
|
||||
| `featured` | `boolean` | 是否为精选版本 | `false` |
|
||||
| `dependencies` | `object[]` | 依赖列表(可为空数组 `[]`) | 见下方 [依赖结构](#依赖结构) |
|
||||
|
||||
#### 可选字段
|
||||
|
||||
| 字段 | 类型 | 说明 | 示例 |
|
||||
|------|------|------|------|
|
||||
| `primary_file` | `string` | 主文件的 **multipart 字段名**。不提供时推断第一个文件为主文件 | `"main-file"` |
|
||||
| `changelog` | `string \| null` | 更新日志(Markdown/纯文本) | `"修复了若干 Bug"` |
|
||||
| `status` | `string` | 版本状态:`listed`、`archived`、`draft`、`unlisted`、`scheduled`、`unknown`。默认为 `listed` | `"listed"` |
|
||||
| `requested_status` | `string \| null` | 请求的状态(用于审核流程):`listed`、`archived`、`draft`、`unlisted` | `"listed"` |
|
||||
| `environment` | `string` | 目标环境,见下方枚举 | `"client_and_server"` |
|
||||
| `file_types` | `object` | **multipart 字段名** → 文件类型的映射 | `{"sources-file": "sources-jar"}` |
|
||||
|
||||
#### `environment` 枚举值
|
||||
|
||||
所有值来自 Modrinth 前端源码 [environments.ts](https://github.com/modrinth/code/blob/main/packages/ui/src/components/project/settings/environment/environments.ts)。以下按前端 UI 的层级结构排列,只有 **Server-side only** 和 **Client and server** 是带子选项的大类,其余为单选:
|
||||
|
||||
| 大类(前端 UI) | 子项标题 | API 值 | 说明 |
|
||||
|----------------|---------|--------|------|
|
||||
| *(无分类,单选)* | Unknown environment | `unknown` | 未指定或无法确定环境 |
|
||||
| Client-side only | — | `client_only` | 所有功能在客户端运行,兼容原版服务端 |
|
||||
| **Server-side only** | Works in singleplayer too | `server_only` | 所有功能在服务端运行,兼容原版客户端;也支持单人模式的内置服务端 |
|
||||
| | Dedicated server only | `dedicated_server_only` | 所有功能在服务端运行,兼容原版客户端;仅在专用服务器上工作 |
|
||||
| **Client and server** | Required on both | `client_and_server` | 客户端和服务端都必须安装 |
|
||||
| | Optional on client | `server_only_client_optional` | 主要为服务端功能,客户端安装可增强体验 |
|
||||
| | Optional on server | `client_only_server_optional` | 主要为客户端功能,服务端安装可增强体验 |
|
||||
| | Optional on both, works best when installed on both sides | `client_or_server_prefers_both` | 双方都装体验最佳 |
|
||||
| | Optional on both, works the same if installed on either side | `client_or_server` | 单独安装任一侧效果相同 |
|
||||
| Singleplayer only | — | `singleplayer_only` | 仅在单人模式或未连接多人服务器时可用 |
|
||||
|
||||
### 依赖结构
|
||||
|
||||
`dependencies` 数组中每个依赖对象至少需要 `dependency_type` 字段:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 | 示例 |
|
||||
|------|------|------|------|------|
|
||||
| `dependency_type` | `string` | ✅ | 依赖类型:`required`、`optional`、`incompatible`、`embedded` | `"required"` |
|
||||
| `project_id` | `string \| null` | ❌ | 依赖的项目 ID | `"P7dR8mSH"` (Fabric API) |
|
||||
| `version_id` | `string \| null` | ❌ | 依赖的具体版本 ID;为 `null` 时匹配最新版 | `"IIJJKKLL"` |
|
||||
| `file_name` | `string \| null` | ❌ | 依赖文件名,主要用于整合包中的外部依赖 | `"fabric-api-0.92.0.jar"` |
|
||||
|
||||
> **注意:** `project_id` 和 `version_id` 二者至少提供一个,否则依赖关系无法解析。
|
||||
|
||||
**Fabric API 依赖示例:**
|
||||
|
||||
```json
|
||||
{
|
||||
"dependency_type": "required",
|
||||
"project_id": "P7dR8mSH"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完整请求示例
|
||||
|
||||
以下是一个使用 cURL 上传一个主 jar 和一个 sources.jar 的完整请求。
|
||||
|
||||
### 准备
|
||||
|
||||
假设:
|
||||
- 项目 ID 为 `AABBCCDD`
|
||||
- 主文件为 `my-mod-1.0.0.jar`
|
||||
- 源代码文件为 `my-mod-1.0.0-sources.jar`
|
||||
|
||||
### cURL 命令
|
||||
|
||||
```bash
|
||||
curl -X POST "https://api.modrinth.com/v2/version" \
|
||||
-H "Authorization: YOUR_PAT_TOKEN" \
|
||||
-H "User-Agent: your_username/your_project/1.0.0" \
|
||||
-F "data={
|
||||
\"project_id\": \"AABBCCDD\",
|
||||
\"file_parts\": [\"main-file\", \"sources-file\"],
|
||||
\"primary_file\": \"main-file\",
|
||||
\"name\": \"Version 1.0.0\",
|
||||
\"version_number\": \"1.0.0\",
|
||||
\"changelog\": \"## 更新内容\n\n- 新增了某某功能\n- 修复了某某 Bug\",
|
||||
\"dependencies\": [
|
||||
{
|
||||
\"dependency_type\": \"required\",
|
||||
\"project_id\": \"P7dR8mSH\"
|
||||
}
|
||||
],
|
||||
\"game_versions\": [\"1.20.1\", \"1.20.4\"],
|
||||
\"version_type\": \"release\",
|
||||
\"loaders\": [\"fabric\"],
|
||||
\"featured\": false,
|
||||
\"status\": \"listed\",
|
||||
\"environment\": \"client_and_server\",
|
||||
\"file_types\": {
|
||||
\"sources-file\": \"sources-jar\"
|
||||
}
|
||||
}" \
|
||||
-F "main-file=@./build/libs/my-mod-1.0.0.jar" \
|
||||
-F "sources-file=@./build/libs/my-mod-1.0.0-sources.jar"
|
||||
```
|
||||
|
||||
### Node.js (fetch) 示例
|
||||
|
||||
```js
|
||||
const fs = require("fs");
|
||||
|
||||
const TOKEN = "YOUR_PAT_TOKEN";
|
||||
const PROJECT_ID = "AABBCCDD";
|
||||
|
||||
const form = new FormData();
|
||||
|
||||
const metadata = {
|
||||
project_id: PROJECT_ID,
|
||||
file_parts: ["main-file", "sources-file"],
|
||||
primary_file: "main-file",
|
||||
name: "Version 1.0.0",
|
||||
version_number: "1.0.0",
|
||||
changelog: "## 更新内容\n\n- 新增了某某功能\n- 修复了某某 Bug",
|
||||
dependencies: [
|
||||
{ dependency_type: "required", project_id: "P7dR8mSH" }
|
||||
],
|
||||
game_versions: ["1.20.1", "1.20.4"],
|
||||
version_type: "release",
|
||||
loaders: ["fabric"],
|
||||
featured: false,
|
||||
status: "listed",
|
||||
environment: "client_and_server",
|
||||
file_types: {
|
||||
"sources-file": "sources-jar"
|
||||
}
|
||||
};
|
||||
|
||||
form.append("data", JSON.stringify(metadata));
|
||||
form.append("main-file", new Blob([fs.readFileSync("./build/libs/my-mod-1.0.0.jar")]), "my-mod-1.0.0.jar");
|
||||
form.append("sources-file", new Blob([fs.readFileSync("./build/libs/my-mod-1.0.0-sources.jar")]), "my-mod-1.0.0-sources.jar");
|
||||
|
||||
const response = await fetch("https://api.modrinth.com/v2/version", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Authorization": TOKEN,
|
||||
"User-Agent": "your_username/your_project/1.0.0"
|
||||
},
|
||||
body: form
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
console.log(result);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 响应格式
|
||||
|
||||
成功创建后返回一个 [Version](#version-对象) 对象,HTTP 状态码 `200`。
|
||||
|
||||
### Version 对象
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "IIJJKKLL",
|
||||
"project_id": "AABBCCDD",
|
||||
"author_id": "EEFFGGHH",
|
||||
"name": "Version 1.0.0",
|
||||
"version_number": "1.0.0",
|
||||
"changelog": "## 更新内容\n\n- 新增了某某功能\n- 修复了某某 Bug",
|
||||
"date_published": "2025-01-01T00:00:00Z",
|
||||
"downloads": 0,
|
||||
"version_type": "release",
|
||||
"status": "listed",
|
||||
"requested_status": null,
|
||||
"game_versions": ["1.20.1", "1.20.4"],
|
||||
"loaders": ["fabric"],
|
||||
"featured": false,
|
||||
"files": [
|
||||
{
|
||||
"hashes": {
|
||||
"sha512": "93ecf5fe...",
|
||||
"sha1": "c84dd4b3..."
|
||||
},
|
||||
"url": "https://cdn.modrinth.com/data/AABBCCDD/versions/1.0.0/my-mod-1.0.0.jar",
|
||||
"filename": "my-mod-1.0.0.jar",
|
||||
"primary": true,
|
||||
"size": 1097270,
|
||||
"file_type": null
|
||||
},
|
||||
{
|
||||
"hashes": {
|
||||
"sha512": "ab12cd34...",
|
||||
"sha1": "ef56gh78..."
|
||||
},
|
||||
"url": "https://cdn.modrinth.com/data/AABBCCDD/versions/1.0.0/my-mod-1.0.0-sources.jar",
|
||||
"filename": "my-mod-1.0.0-sources.jar",
|
||||
"primary": false,
|
||||
"size": 543210,
|
||||
"file_type": "sources-jar"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
响应中 `files` 数组的每个文件对象包含:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `hashes` | 文件哈希,包含 `sha512` 和 `sha1` |
|
||||
| `url` | 文件直链 |
|
||||
| `filename` | 文件名 |
|
||||
| `primary` | 是否为主文件 |
|
||||
| `size` | 文件大小(字节) |
|
||||
| `file_type` | 文件类型(对于 sources.jar 为 `"sources-jar"`) |
|
||||
|
||||
---
|
||||
|
||||
## 错误码
|
||||
|
||||
| 状态码 | 错误类型 | 说明 |
|
||||
|--------|----------|------|
|
||||
| `400` | `InvalidInputError` | 请求参数无效,详见响应中的 `description`。常见原因:缺少必填字段、`game_versions` 或 `loaders` 值不合法、文件格式不被接受 |
|
||||
| `401` | `AuthError` | Token 无效、未提供 Token,或 Token 不包含 `VERSION_CREATE` 权限范围 |
|
||||
| `404` | — | 目标项目 `project_id` 不存在 |
|
||||
|
||||
---
|
||||
|
||||
## 注意事项与最佳实践
|
||||
|
||||
### 1. 必须至少上传一个文件
|
||||
|
||||
除非版本 `status` 设置为 `draft`,否则每个新版本必须附带至少一个文件。接受的格式:`.mrpack`、`.jar`、`.zip`、`.litemod`。
|
||||
|
||||
### 2. `file_parts` / `file_types` / `primary_file` 全部使用 multipart 字段名
|
||||
|
||||
这三处字符串和实际表单字段名必须**严格匹配**,且它们指向的都是 **multipart 字段名**(如 `"main-file"`),不是文件名(如 `"my-mod-1.0.0.jar"`)。否则上传的文件不会被正确关联。
|
||||
|
||||
### 3. sources.jar 的正确做法
|
||||
|
||||
- 给 sources 文件随意命名 multipart 字段名(如 `"sources-file"`),将其列入 `file_parts`。
|
||||
- 在 `file_types` 中映射该 **multipart 字段名** → `"sources-jar"`。
|
||||
- **不要**将 sources 文件设为主文件(`primary_file` 应指向主 jar 的 multipart 字段名)。
|
||||
|
||||
### 4. `dependencies` 可以是空数组
|
||||
|
||||
如果该模组没有依赖(极少数情况),直接传 `[]` 即可。但 `dependencies` 字段本身是必填的。
|
||||
|
||||
### 5. 文件大小限制
|
||||
|
||||
Modrinth 对上传文件有大小限制。如果文件过大,可能收到 `400` 错误。建议保持在合理范围内(一般单个文件不超过 100MB)。
|
||||
|
||||
### 6. 版本号建议
|
||||
|
||||
`version_number` 理想上应遵循[语义化版本规范](https://semver.org/lang/zh-CN/),便于 Modrinth 进行版本比较和用户理解。
|
||||
|
||||
### 7. 草稿版本
|
||||
|
||||
如果你想先上传版本而不立即公开,可将 `status` 设为 `draft`。草稿版本可以不附带文件。后续可通过 PATCH `/version/{id}` 修改状态为 `listed` 来发布。
|
||||
|
||||
### 8. 已有版本追加文件
|
||||
|
||||
如果版本已创建但需要追加文件,应使用 `POST /version/{id}/file` 端点(需要 `VERSION_WRITE` 权限范围)。
|
||||
|
||||
### 9. 环境与加载器的校验
|
||||
|
||||
`loaders` 和 `game_versions` 必须使用 Modrinth 认可的值。你可以通过以下端口查询可用值:
|
||||
- `GET /tag/loader` — 可用加载器
|
||||
- `GET /tag/game_version` — 可用 Minecraft 版本
|
||||
|
||||
### 10. Rate Limit
|
||||
|
||||
Modrinth API 有频率限制:每 IP 每分钟最多 **300** 个请求。响应头中包含 `X-Ratelimit-Limit`、`X-Ratelimit-Remaining`、`X-Ratelimit-Reset`。
|
||||
|
||||
---
|
||||
|
||||
## 参考
|
||||
|
||||
- [Modrinth API 文档](https://docs.modrinth.com)
|
||||
- [Modrinth 创建 PAT](https://modrinth.com/settings/account)
|
||||
- [OpenAPI 规范文件](openapi.yml)
|
||||
- [Modrinth Labrinth 源码](https://github.com/modrinth/labrinth)
|
||||
- [语义化版本规范](https://semver.org)
|
||||
132
docs/workflow.md
Normal file
132
docs/workflow.md
Normal file
@ -0,0 +1,132 @@
|
||||
# 整体框架
|
||||
|
||||
## 数据文件
|
||||
|
||||
### `secrets.json`
|
||||
|
||||
存放 API 密钥,位于项目根目录。
|
||||
|
||||
```json
|
||||
{
|
||||
"curseforge": "<CurseForge API Token>",
|
||||
"modrinth": "<Modrinth Personal Access Token>"
|
||||
}
|
||||
```
|
||||
|
||||
### `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}` 引用 config 中 `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 或反向解析后,若结果仍包含 `${...}` 占位符,则需继续解析,直至结果中不再有占位符为止。`${...}` 中的变量首先从 config 同级字段取值,例如 `${filename_format}` 引用 config 中的 `filename_format` 字段。
|
||||
|
||||
## 运行时流程
|
||||
|
||||
### 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`:
|
||||
- 用 `version` 和 `mc_version` 填入 `filename_format` 模板,生成构建产物文件名
|
||||
- 用 `version` 和 `mc_version` 填入 `source_filename_format` 模板,生成源码文件名
|
||||
- 检查 `{project_dir}/build/libs/` 下是否存在对应文件
|
||||
4. 将所有匹配结果返回前端,由 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. 前端显示两个进度条(仅显示有勾选任务的平台),格式:`{平台图标} 平台名: [======== ] 0 / N`
|
||||
2. 单个平台内按 MC 版本顺序逐个提交,**不可并行**;两个平台之间可以并行执行
|
||||
3. 每个版本的发布流程:
|
||||
- 解析模板:用 `version` 和 `mc_version` 填入各平台 `version_name`、`version` 模板(含递归解析)
|
||||
- Modrinth:构造 multipart/form-data 请求,含 `data` JSON + 构建产物文件 + 源码文件
|
||||
- CurseForge:构造 multipart/form-data 请求,含 `metadata` JSON + 文件
|
||||
- 更新对应进度条
|
||||
4. **任一请求失败则取消当前平台的后续所有任务**,不影响另一个平台
|
||||
5. 全部完成或失败后,弹出结果 Modal 显示最终状态(成功数 / 失败数)
|
||||
Loading…
Reference in New Issue
Block a user