Milracle-v2
    • Milracle API 接口清单
    • 认证 (Auth)
      • 认证 (Auth) 端点(POST)
        POST
      • 认证 (Auth) 端点(GET)
        GET
    • 谱面 (Charts)
      • 谱面 (Charts) 端点(GET)
        GET
      • 谱面 (Charts) 端点(POST)
        POST
      • 谱面 (Charts) 端点(DELETE)
        DELETE
      • 审核台 (Studio) 端点(GET)
        GET
      • 审核台 (Studio) 端点(POST)
        POST
    • 成绩 (Scores)
      • 成绩 (Scores) 端点(GET)
        GET
      • 成绩 (Scores) 端点(POST)
        POST
    • 游玩记录 (Records)
      • 游玩记录 (Records) 端点(GET)
        GET
      • 游玩记录 (Records) 端点(POST)
        POST
    • 资源包 (Resource Packs)
      • 资源包 (Resource Packs) 端点(GET)
        GET
      • 资源包 (Resource Packs) 端点(POST)
        POST
    • 用户 (User)
      • 用户 (User) 端点(GET)
        GET
      • 用户 (User) 端点(POST)
        POST
    • 管理员 (Admin)
      • 管理员 (Admin) 端点(GET)
        GET
      • 管理员 (Admin) 端点(PUT)
        PUT
    • 内容 (Content)
      • 内容 (Content) 端点(GET)
        GET
      • 内容 (Content) 端点(POST)
        POST
      • 内容 (Content) 端点(PUT)
        PUT
      • 内容 (Content) 端点(DELETE)
        DELETE
    • 上传 (Upload)
      • 上传 (Upload) 端点(GET)
      • 上传 (Upload) 端点(POST)
    • 工具 (Utils)
      • 工具 (Utils) 端点(GET)
    • 工单 (Tickets)
      • 工单 (Tickets) 端点(GET)
      • 工单 (Tickets) 端点(POST)
    • 数据模型
      • Error
      • User
      • Chart
      • ChartAsset
      • Score
      • Record
      • RecordProperties
      • ResourcePack
      • Ticket

    Milracle API 接口清单

    由 scripts/gen-api-docs.mjs 自动生成,请勿手改——改该文件后重新运行 node scripts/gen-api-docs.mjs。

    • Base URL:https://api.xhsr.org.cn(Vercel Node 24,区域 hkg1)
    • 鉴权:Authorization: Bearer <jwt>。API 已独立部署,跨域场景必须用 Bearer(Cookie 仅同源有效)。
    • 错误约定:所有非 2xx 返回 { "error": "文案", "code": "机器码" }。生产环境 error 为固定文案,不泄露内部信息;前端请按 code 分支。
    • 函数上限:maxDuration 30 秒。

    快速上手

    三步走:登录拿 token → 带 token 调接口 → 过期用 refresh 换新。下面每条命令都可直接复制运行。

    # 1) 登录(公开接口,无需 token)
    curl -X POST 'https://api.xhsr.org.cn/api/v2/auth?action=login' \
      -H 'Content-Type: application/json' \
      -d '{"login":"demo","password":"********"}'
    # → { "token": "eyJ...", "refreshToken": "...", "user": { ... } }
    
    # 2) 带 token 调需要登录的接口
    TOKEN='第 1 步拿到的 token'
    curl 'https://api.xhsr.org.cn/api/v2/user?action=me' -H "Authorization: Bearer $TOKEN"
    
    # 3) token 过期(401)时用 refreshToken 换新
    curl -X POST 'https://api.xhsr.org.cn/api/v2/auth?action=refresh' \
      -H 'Content-Type: application/json' \
      -d '{"refreshToken":"<refreshToken>"}'
    

    错误响应示例(所有非 2xx 统一此结构,前端按 code 分支):

    { "error": "Internal Server Error", "code": "internal_error" }
    

    鉴权级别

    标记含义
    public公开
    user需登录
    self需登录(仅本人)
    privileged需 reviewer / moderator
    moderator需 admin / moderator
    admin需 admin

    目录

    • 认证 (Auth) —— 13 个接口 /api/v2/auth
    • 用户 (User) —— 11 个接口 /api/v2/user
    • 谱面 (Charts) —— 7 个接口 /api/v2/charts
    • 成绩 (Scores) —— 4 个接口 /api/v2/scores
    • 游玩记录 (Records) —— 4 个接口 /api/v2/records
    • 资源包 (Resource Packs) —— 7 个接口 /api/v2/resource_packs
    • 审核台 (Studio) —— 6 个接口 /api/v2/studio
    • 工单 (Tickets) —— 8 个接口 /api/v2/tickets
    • 内容 (Content) —— 11 个接口 /api/v2/content
    • 上传 (Upload) —— 4 个接口 /api/v2/upload
    • 工具 (Utils) —— 1 个接口 /api/v2/convert-zip
    • 管理员 (Admin) —— 5 个接口 /api/v2/admin

    共 81 个接口,分布在 12 个端点。


    认证 (Auth)

    /api/v2/auth

    POST /api/v2/auth?action=login

    账号密码登录 · 鉴权:公开

    参数说明
    login邮箱或用户名
    password密码

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/auth?action=login' \
      -H 'Content-Type: application/json' \
      -d '{"login":"demo","password":"********"}'
    

    成功响应:{ token, refreshToken, user }

    响应示例

    {
      "token": "<JWT,7 天有效>",
      "refreshToken": "<refreshToken>",
      "user": {
        "id": 1,
        "username": "星鸿",
        "role": "user",
        "status": "active"
      }
    }
    

    账号未激活时返回 403 email_unverified 并补发验证码;被封禁返回 403。

    POST /api/v2/auth?action=register

    注册新账号 · 鉴权:公开

    参数说明
    email邮箱
    username用户名
    password密码

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/auth?action=register' \
      -H 'Content-Type: application/json' \
      -d '{"email":"user@example.com","username":"demo","password":"********"}'
    

    成功响应:{ message, user }

    创建后状态为 pending,需经 verify/activate 激活。

    POST /api/v2/auth?action=verify

    发送邮箱验证码 · 鉴权:公开

    参数说明
    email邮箱

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/auth?action=verify' \
      -H 'Content-Type: application/json' \
      -d '{"email":"user@example.com"}'
    

    成功响应:{ message }

    同一邮箱 30 秒一次(429)。非生产环境响应会附带 code 方便本地调试。

    POST /api/v2/auth?action=activate

    校验验证码并激活账号 · 鉴权:公开

    参数说明
    email邮箱
    code验证码

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/auth?action=activate' \
      -H 'Content-Type: application/json' \
      -d '{"email":"user@example.com","code":"123456"}'
    

    成功响应:{ message }

    同一邮箱 10 分钟内最多尝试 10 次,超出返回 429。

    POST /api/v2/auth?action=reset-password

    重置密码 · 鉴权:公开

    参数说明
    email邮箱
    code验证码(第二步)
    newPassword新密码(第二步)

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/auth?action=reset-password' \
      -H 'Content-Type: application/json' \
      -d '{"email":"user@example.com","code":"123456","newPassword":"********"}'
    

    成功响应:{ message }

    两阶段:只传 email = 发送验证码;传 email+code+newPassword = 完成重置。邮箱不存在时也返回 200,避免账号枚举。

    POST /api/v2/auth?action=refresh

    用 refreshToken 换新 token · 鉴权:公开

    参数说明
    refreshToken刷新令牌(body 或 cookie)

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/auth?action=refresh' \
      -H 'Content-Type: application/json' \
      -d '{"refreshToken":"<refreshToken>"}'
    

    成功响应:{ token, refreshToken }

    原子消费:旧 refreshToken 用后即失效,并发重放会拿到 401。

    POST /api/v2/auth?action=logout

    登出并撤销 refreshToken · 鉴权:需登录

    参数说明
    refreshToken可选

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/auth?action=logout' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"refreshToken":"<refreshToken>"}'
    

    成功响应:{ message }

    POST /api/v2/auth?action=oauth-token

    OAuth 授权码换 token(PKCE S256) · 鉴权:公开

    参数说明
    grant_typeauthorization_code
    code授权码
    redirect_uri回调地址
    code_verifierPKCE 校验串
    client_id客户端 ID

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/auth?action=oauth-token' \
      -H 'Content-Type: application/json' \
      -d '{"grant_type":"authorization_code","code":"123456","redirect_uri":"https://app.example.com/callback","code_verifier":"<code_verifier>","client_id":"<client_id>"}'
    

    成功响应:{ access_token, token_type, expires_in, user }

    POST /api/v2/auth?action=github-callback

    GitHub OAuth 回调换 token / 绑定 · 鉴权:公开

    参数说明
    codeGitHub 授权码
    statestate

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/auth?action=github-callback' \
      -H 'Content-Type: application/json' \
      -d '{"code":"123456","state":"<state>"}'
    

    成功响应:{ token, refreshToken, user, created }

    携带本站 JWT 时为「绑定」,未携带时为「登录/自动注册」。

    GET /api/v2/auth?action=oauth-authorize

    OAuth 授权端点(PKCE S256) · 鉴权:需登录

    参数说明
    client_id客户端 ID
    redirect_uri回调地址
    response_typecode
    code_challengePKCE 挑战
    code_challenge_methodS256
    state状态串

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/auth?action=oauth-authorize&client_id=<client_id>&redirect_uri=https://app.example.com/callback&response_type=code&code_challenge=<code_challenge>&code_challenge_method=S256&state=<state>' \
      -H "Authorization: Bearer $TOKEN"
    

    成功响应:302 重定向到 redirect_uri?code=...

    GET /api/v2/auth?action=milkloud-login

    发起 Milkloud 登录 · 鉴权:公开

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/auth?action=milkloud-login'
    

    成功响应:302 重定向到 Milkloud 授权页

    GET /api/v2/auth?action=milkloud-callback

    Milkloud OAuth 回调 · 鉴权:公开

    参数说明
    code授权码
    statestate

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/auth?action=milkloud-callback&code=123456&state=<state>'
    

    成功响应:{ token, refreshToken, user }

    GET /api/v2/auth?action=github-login

    发起 GitHub 登录 · 鉴权:公开

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/auth?action=github-login'
    

    成功响应:302 重定向到 GitHub 授权页

    未配置 GITHUB_CLIENT_ID/SECRET 时返回 500 github_not_configured。


    用户 (User)

    /api/v2/user

    GET /api/v2/user?action=me

    获取当前登录用户 · 鉴权:需登录

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/user?action=me' \
      -H "Authorization: Bearer $TOKEN"
    

    成功响应:{ user }

    GET /api/v2/user?action=list

    用户列表(分页/搜索/排序) · 鉴权:公开

    参数说明
    page页码
    limit每页条数(≤200)
    sortcreated_at
    orderasc
    q搜索关键字
    role角色筛选(仅管理员)
    status状态筛选(仅管理员)

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/user?action=list&page=1&limit=20&sort=newest&order=desc&q=关键词&role=user&status=active'
    

    成功响应:{ data, page, limit, total }

    公开请求绝不返回 email/role/status,防止全站邮箱枚举。

    GET /api/v2/user?action=detail

    用户详情(含其谱面) · 鉴权:公开

    参数说明
    id用户 ID

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/user?action=detail&id=1'
    

    成功响应:{ data: { ...user, charts[] } }

    公开视角只展示已上架谱面。

    GET /api/v2/user?action=get

    用户简要信息 · 鉴权:公开

    参数说明
    id用户 ID

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/user?action=get&id=1'
    

    成功响应:{ data }

    响应示例

    {
      "data": {
        "id": 1,
        "username": "星鸿",
        "avatar_url": "https://file.xhsr.org.cn/avatar-1-….png",
        "bio": "你好喵~",
        "created_at": "2025-12-08T21:38:48.004Z",
        "followers_count": 4,
        "following_count": 0,
        "is_following": false
      }
    }
    

    GET /api/v2/user?action=followers

    粉丝列表 · 鉴权:公开

    参数说明
    id用户 ID
    page页码
    limit每页条数

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/user?action=followers&id=1&page=1&limit=20'
    

    成功响应:{ data, page, limit, total }

    GET /api/v2/user?action=following

    关注列表 · 鉴权:公开

    参数说明
    id用户 ID
    page页码
    limit每页条数

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/user?action=following&id=1&page=1&limit=20'
    

    成功响应:{ data, page, limit, total }

    POST /api/v2/user?action=update

    更新资料 · 鉴权:需登录(仅本人)

    参数说明
    username用户名
    bio简介
    avatar_url头像
    id目标用户 ID(仅管理员)

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/user?action=update' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"username":"demo","bio":"<bio>","avatar_url":"<avatar_url>","id":"1"}'
    

    成功响应:{ message, user, usernameChange }

    用户名每 365 天只能改一次(管理员可绕过);github_id 只能置空,绑定必须走 GitHub 授权。

    POST /api/v2/user?action=change-password

    修改密码 · 鉴权:需登录(仅本人)

    参数说明
    oldPassword旧密码
    newPassword新密码
    id目标用户 ID(仅管理员)

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/user?action=change-password' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"oldPassword":"********","newPassword":"********","id":"1"}'
    

    成功响应:{ message }

    POST /api/v2/user?action=follow

    关注 / 取关 · 鉴权:需登录

    参数说明
    userId目标用户 ID
    actionfollow

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/user?action=follow' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"userId":"1"}'
    

    成功响应:{ message, followers_count, following_count, is_following }

    POST /api/v2/user?action=send-email-code

    向新邮箱发送换绑验证码 · 鉴权:需登录

    参数说明
    email新邮箱
    password当前密码(身份确认)

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/user?action=send-email-code' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"email":"user@example.com","password":"********"}'
    

    成功响应:{ message, email }

    必须校验当前密码,防止会话被盗后直接改绑邮箱。

    POST /api/v2/user?action=change-email

    校验验证码并完成换绑 · 鉴权:需登录

    参数说明
    email新邮箱
    code验证码

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/user?action=change-email' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"email":"user@example.com","code":"123456"}'
    

    成功响应:{ message, user }


    谱面 (Charts)

    /api/v2/charts

    GET /api/v2/charts?action=list

    谱面列表(分页/搜索/筛选) · 鉴权:公开

    参数说明
    page页码
    limit每页条数(≤200)
    sortoldest
    q搜索(标题/艺术家/谱师/上传者)
    uploader_id按上传者过滤
    statusapproved
    published1

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/charts?action=list&page=1&limit=20&sort=newest&q=关键词&uploader_id=1&status=active&published=1'
    

    成功响应:{ data: Chart[] }

    响应示例

    {
      "data": [
        {
          "id": 8,
          "title": "Endtime",
          "artist": "Cres.",
          "charter": "Heronmony",
          "difficulty": "Cloudburst",
          "level": "11+",
          "bpm": "180",
          "cover_url": "https://file.xhsr.org.cn/charts/1771149808591-image.png",
          "chart_url": "https://file.xhsr.org.cn/charts/1771149806393-chart.json",
          "audio_url": "https://file.xhsr.org.cn/charts/1771149807796-audio.mp3",
          "meta_url": "https://file.xhsr.org.cn/charts/recovered/8/meta.json",
          "status": "approved",
          "uploader_id": 68,
          "like_count": 0,
          "created_at": "2026-02-15T02:03:30.005Z"
        }
      ]
    }
    

    普通用户默认只看已上架 + 自己的;hidden 谱面一律不可见。

    GET /api/v2/charts?action=detail

    谱面详情 · 鉴权:公开

    参数说明
    id谱面 ID

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/charts?action=detail&id=1'
    

    成功响应:{ data: Chart }

    响应示例

    {
      "data": {
        "id": 8,
        "title": "Endtime",
        "artist": "Cres.",
        "charter": "Heronmony",
        "difficulty": "Cloudburst",
        "level": "11+",
        "bpm": "180",
        "offset_val": "-40.000",
        "cover_url": "https://file.xhsr.org.cn/charts/1771149808591-image.png",
        "chart_url": "https://file.xhsr.org.cn/charts/1771149806393-chart.json",
        "audio_url": "https://file.xhsr.org.cn/charts/1771149807796-audio.mp3",
        "meta_url": "https://file.xhsr.org.cn/charts/recovered/8/meta.json",
        "status": "approved",
        "uploader_id": 68,
        "description": "本人第一张Milthm自制谱",
        "play_count": 0,
        "like_count": 0,
        "created_at": "2026-02-15T02:03:30.005Z"
      }
    }
    

    未上架谱面仅上传者本人与特权角色可见;hidden 仅特权角色可见(对外 404)。

    GET /api/v2/charts?action=history

    谱面流转历史(投票/上架申请/审计) · 鉴权:公开

    参数说明
    id谱面 ID

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/charts?action=history&id=1'
    

    成功响应:{ data: Event[] }

    POST /api/v2/charts?action=like

    点赞 / 取消点赞 · 鉴权:需登录

    参数说明
    chartId谱面 ID
    actionlike

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/charts?action=like' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"chartId":"1"}'
    

    成功响应:{ message, is_liked, like_count }

    POST /api/v2/charts?action=moderate

    管理谱面状态 · 鉴权:需 reviewer / moderator

    参数说明
    chartId谱面 ID
    actionhide

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/charts?action=moderate' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"chartId":"1"}'
    

    成功响应:{ message, status }

    re-review 会清空该谱面历史投票。

    POST /api/v2/charts?action=delete

    软删除谱面(置为 hidden) · 鉴权:需登录(仅本人)

    参数说明
    id谱面 ID

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/charts?action=delete' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"id":"1"}'
    

    成功响应:{ message, status }

    仅隐藏,投票/成绩/点赞/评论等关联数据全部保留,可恢复。

    DELETE /api/v2/charts?action=delete

    软删除谱面(同 POST action=delete) · 鉴权:需登录(仅本人)

    参数说明
    id谱面 ID

    请求示例

    curl -X DELETE 'https://api.xhsr.org.cn/api/v2/charts?action=delete' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"id":"1"}'
    

    成功响应:{ message, status }


    成绩 (Scores)

    /api/v2/scores

    GET /api/v2/scores?action=recent

    最近成绩 · 鉴权:公开

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/scores?action=recent'
    

    成功响应:{ data }

    GET /api/v2/scores?action=leaderboard

    谱面排行榜 · 鉴权:公开

    参数说明
    chartId谱面 ID

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/scores?action=leaderboard&chartId=1'
    

    成功响应:{ data }

    GET /api/v2/scores?action=user

    某用户的成绩列表 · 鉴权:公开

    参数说明
    user_id用户 ID

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/scores?action=user&user_id=1'
    

    成功响应:{ data }

    POST /api/v2/scores?action=upload

    上传成绩 · 鉴权:需登录

    参数说明
    chartId谱面 ID
    score0–1010000
    acc准确率
    perfect_big—
    perfect_small—
    great—
    good—
    bad—
    miss—
    max_combo—

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/scores?action=upload' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"chartId":1,"score":986500,"acc":99.42,"perfect_big":812,"perfect_small":64,"great":3,"good":0,"bad":0,"miss":0,"max_combo":512}'
    

    成功响应:{ data, achievement }

    rank/reality/achievement(R/AP/FC)由服务端按分数与谱面等级计算,不接受客户端传入。


    游玩记录 (Records)

    /api/v2/records

    GET /api/v2/records?action=getsong

    我的某个谱面的记录(按分数排序) · 鉴权:需登录

    参数说明
    chartId谱面 ID 或 chart hash
    page—
    limit≤100

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/records?action=getsong&chartId=1&page=1&limit=20' \
      -H "Authorization: Bearer $TOKEN"
    

    成功响应:{ data: { items, total } }

    响应示例

    {
      "data": {
        "items": [
          {
            "id": 101,
            "props": {
              "chart_hash": "<hash>",
              "score": 986500,
              "score_accuracy": 0.9942,
              "grade": "S",
              "played_at": "2026-10-08T00:00:00Z"
            },
            "file_url": "https://file.xhsr.org.cn/records/…",
            "create_time": 1791400000
          }
        ],
        "total": 4
      }
    }
    

    GET /api/v2/records?action=getall

    我的全部记录(按时间排序) · 鉴权:需登录

    参数说明
    page—
    limit≤100

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/records?action=getall&page=1&limit=20' \
      -H "Authorization: Bearer $TOKEN"
    

    成功响应:{ data: { items, total } }

    GET /api/v2/records?action=download

    下载记录文件 · 鉴权:公开

    参数说明
    id记录 ID

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/records?action=download&id=1'
    

    成功响应:二进制流或 302

    明文记录直出;仍是密文的用本地 RSA 私钥解密后返回,并回写明文。

    POST /api/v2/records?action=upload

    上传游玩记录 · 鉴权:需登录

    参数说明
    chartId谱面 ID(query)
    body原始二进制(RSA 加密的 asar)

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/records?action=upload&chartId=1' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/octet-stream' \
      --data-binary @record.asar
    

    成功响应:{ data: { id } }

    请求体是 RSA 加密的 asar 原始二进制(Content-Type: application/octet-stream),不是 JSON;chartId 走 query。

    每个 (用户, 谱面) 最多保留 4 条,超出按分数从低到高清理。


    资源包 (Resource Packs)

    /api/v2/resource_packs

    GET /api/v2/resource_packs?action=list

    资源包列表 · 鉴权:公开

    参数说明
    page—
    limit—
    q搜索

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/resource_packs?action=list&page=1&limit=20&q=关键词'
    

    成功响应:{ data }

    响应示例

    {
      "data": {
        "items": [
          {
            "id": 1,
            "meta": {
              "name": "Bruh",
              "description": "…",
              "version": "0.1.0",
              "author": "qaqFei",
              "license": "",
              "createTime": null,
              "updateTime": null
            },
            "file_url": "https://file.xhsr.org.cn/resource-packs/1787361512490-11-bruh.mrp",
            "status": "approved",
            "download_count": 6,
            "created_at": "2026-08-21T17:18:33.625Z"
          }
        ],
        "total": 1,
        "page": 1,
        "limit": 20
      }
    }
    

    meta 是读取时由扁平列(name/description/version/author/license/other_information/create_time/update_time)现拼的对象,库里没有 meta 列。

    GET /api/v2/resource_packs?action=detail

    资源包详情 · 鉴权:公开

    参数说明
    id资源包 ID

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/resource_packs?action=detail&id=1'
    

    成功响应:{ data }

    GET /api/v2/resource_packs?action=download

    下载资源包 · 鉴权:公开

    参数说明
    id资源包 ID

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/resource_packs?action=download&id=1'
    

    成功响应:二进制流或 302

    POST /api/v2/resource_packs?action=init

    初始化上传(获取直传签名) · 鉴权:需登录

    参数说明
    filename—
    size—

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/resource_packs?action=init' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"filename":"pack.mrp","size":"1048576"}'
    

    成功响应:{ data }

    POST /api/v2/resource_packs?action=finalize

    完成上传并登记 · 鉴权:需登录

    参数说明
    key对象键
    meta资源包元信息

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/resource_packs?action=finalize' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"key":"<对象键>","meta":"<meta>"}'
    

    成功响应:{ data }

    POST /api/v2/resource_packs?action=upload

    上传资源包 · 鉴权:需登录

    参数说明
    file文件

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/resource_packs?action=upload' \
      -H "Authorization: Bearer $TOKEN" \
      -F 'file=@pack.mrp'
    

    成功响应:{ data }

    multipart 上传,字段名 file。服务端解析 MRP0 元数据后登记,form 里的其他字段一律忽略;成功后状态为 pending。

    POST /api/v2/resource_packs?action=review

    审核资源包 · 鉴权:需 reviewer / moderator

    参数说明
    id—
    approveboolean

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/resource_packs?action=review' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"id":1,"status":"approved"}'
    

    成功响应:{ data }

    不能审核自己上传的资源包(403)。


    审核台 (Studio)

    /api/v2/studio

    GET /api/v2/studio?action=pending

    待审核谱面(含投票数) · 鉴权:需 reviewer / moderator

    参数说明
    page—
    limit≤200
    q搜索
    sortoldest

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/studio?action=pending&page=1&limit=20&q=关键词&sort=newest' \
      -H "Authorization: Bearer $TOKEN"
    

    成功响应:{ data, page, limit, total }

    GET /api/v2/studio?action=publish-requests

    待处理上架申请 · 鉴权:需 reviewer / moderator

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/studio?action=publish-requests' \
      -H "Authorization: Bearer $TOKEN"
    

    成功响应:{ data }

    GET /api/v2/studio?action=queue

    统一审核队列 · 鉴权:需 reviewer / moderator

    参数说明
    filterpending_queue
    page—
    limit—
    q—

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/studio?action=queue&filter=pending&page=1&limit=20&q=关键词' \
      -H "Authorization: Bearer $TOKEN"
    

    成功响应:{ data, page, limit }

    POST /api/v2/studio?action=vote

    谱面审核投票 · 鉴权:需 admin / moderator

    参数说明
    chartId—
    voteapprove

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/studio?action=vote' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"chartId":8,"vote":"approve"}'
    

    成功响应:{ message, approves, rejects, status }

    双人通过制:approve≥2 → approved;reject≥1 → rejected。

    POST /api/v2/studio?action=approve-publish

    上架申请投票 · 鉴权:需 admin / moderator

    参数说明
    requestId—
    approveboolean

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/studio?action=approve-publish' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"requestId":1,"approve":true}'
    

    成功响应:{ message, approves, rejects, processed, approved }

    POST /api/v2/studio?action=request-publish

    申请上架自己的谱面 · 鉴权:需登录(仅本人)

    参数说明
    chartId谱面 ID

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/studio?action=request-publish' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"chartId":"1"}'
    

    成功响应:{ message }

    仅上传者本人,且谱面状态须已 approved。


    工单 (Tickets)

    /api/v2/tickets

    GET /api/v2/tickets?action=list

    工单列表 · 鉴权:需登录

    参数说明
    scopemine
    status—
    type—
    page—
    limit≤200

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/tickets?action=list&scope=mine&status=active&page=1&limit=20' \
      -H "Authorization: Bearer $TOKEN"
    

    成功响应:{ data, page, limit, total }

    GET /api/v2/tickets?action=detail

    工单详情(含消息) · 鉴权:需登录

    参数说明
    id工单 ID

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/tickets?action=detail&id=1' \
      -H "Authorization: Bearer $TOKEN"
    

    成功响应:{ data: { ticket, messages, can_manage } }

    非版主只能看自己的单,且看不到 is_internal 内部备注。

    GET /api/v2/tickets?action=stats

    工单统计 · 鉴权:需 admin / moderator

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/tickets?action=stats' \
      -H "Authorization: Bearer $TOKEN"
    

    成功响应:{ data: counts }

    POST /api/v2/tickets?action=create

    创建工单 / 举报 · 鉴权:需登录

    参数说明
    typereport_user
    subject标题(general 必填)
    content正文(≤5000)
    targetType—
    targetId—
    attachmentsURL 数组(≤10)

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/tickets?action=create' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"type":"report_chart","content":"描述问题…","targetType":"chart","targetId":8,"attachments":["https://example.com/evidence.png"]}'
    

    成功响应:{ message, data }

    每用户每天 10 条;同一目标未关闭的重复举报返回 409。

    POST /api/v2/tickets?action=reply

    回复工单 · 鉴权:需登录

    参数说明
    id工单 ID
    content正文
    internal内部备注(仅版主)
    attachments—

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/tickets?action=reply' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"id":"1","content":"正文内容","internal":false,"attachments":["https://example.com/evidence.png"]}'
    

    成功响应:{ message }

    POST /api/v2/tickets?action=claim

    认领工单 · 鉴权:需 admin / moderator

    参数说明
    id工单 ID

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/tickets?action=claim' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"id":"1"}'
    

    成功响应:{ message, data }

    POST /api/v2/tickets?action=resolve

    标记已解决 · 鉴权:需 admin / moderator

    参数说明
    id—
    note备注

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/tickets?action=resolve' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"id":"1","note":"处理备注"}'
    

    成功响应:{ message, data }

    POST /api/v2/tickets?action=reject

    驳回工单 · 鉴权:需 admin / moderator

    参数说明
    id—
    note备注

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/tickets?action=reject' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"id":"1","note":"处理备注"}'
    

    成功响应:{ message, data }


    内容 (Content)

    /api/v2/content

    GET /api/v2/content

    公告列表 / 详情 · 鉴权:公开

    参数说明
    typeannouncements
    id按 ID 查
    slug按 slug 查
    admin1 时需管理员且返回正文

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/content?type=announcements'
    

    成功响应:{ data }

    GET /api/v2/content

    团队成员(按分组) · 鉴权:公开

    参数说明
    typeteam

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/content?type=team'
    

    成功响应:{ data }

    GET /api/v2/content

    作品列表 / 详情 · 鉴权:公开

    参数说明
    typecreations
    id—
    author按用户名
    authorId按用户 ID

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/content?type=creations&author=demo'
    

    成功响应:{ data }

    GET /api/v2/content

    客户端下载列表 · 鉴权:公开

    参数说明
    typedownloads

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/content?type=downloads'
    

    成功响应:{ data }

    GET /api/v2/content

    评论列表 · 鉴权:公开

    参数说明
    typecomments
    targetchart
    targetId—
    user_id查某人的评论
    page—
    limit—

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/content?type=comments&target=chart&targetId=8&page=1&limit=20'
    

    成功响应:{ data, page, limit, total }

    POST /api/v2/content

    发表评论 · 鉴权:需登录

    参数说明
    target—
    targetId—
    content—

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/content?type=comments' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"target":"chart","targetId":8,"content":"写得真好!"}'
    

    成功响应:{ message }

    邮箱未验证或封禁返回 403。

    POST /api/v2/content

    新建内容 · 鉴权:需 admin

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/content?type=announcements' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{…按 type 对应的字段…}'
    

    成功响应:{ message }

    PUT /api/v2/content

    编辑评论 · 鉴权:需登录(仅本人)

    参数说明
    id—
    content—

    请求示例

    curl -X PUT 'https://api.xhsr.org.cn/api/v2/content?type=comments' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"id":1,"content":"编辑后的内容"}'
    

    成功响应:{ message }

    PUT /api/v2/content

    更新内容 · 鉴权:需 admin

    参数说明
    id—

    请求示例

    curl -X PUT 'https://api.xhsr.org.cn/api/v2/content' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"id":"1"}'
    

    成功响应:{ message }

    DELETE /api/v2/content

    删除评论(软删除) · 鉴权:需登录(仅本人)

    参数说明
    id—

    请求示例

    curl -X DELETE 'https://api.xhsr.org.cn/api/v2/content?type=comments&id=1' \
      -H "Authorization: Bearer $TOKEN"
    

    成功响应:{ message }

    DELETE /api/v2/content

    删除内容 · 鉴权:需 admin

    参数说明
    id—

    请求示例

    curl -X DELETE 'https://api.xhsr.org.cn/api/v2/content?type=announcements&id=1' \
      -H "Authorization: Bearer $TOKEN"
    

    成功响应:{ message }


    上传 (Upload)

    /api/v2/upload

    按 query.type 分发:avatar / evidence / file / chart-zip / chart。注意区分:POST 上传用 ?type=,GET 拉头像用 ?action=avatar。

    GET /api/v2/upload?action=avatar

    获取用户头像(公开) · 鉴权:公开

    参数说明
    userId用户 ID

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/upload?action=avatar&userId=1'
    

    成功响应:图片二进制直出 / 302 跳 CDN

    默认头像从磁盘直出并缓存 7 天;自定义头像 302 重定向到 CDN 地址。

    POST /api/v2/upload

    上传头像(multipart) · 鉴权:需登录

    参数说明
    typeavatar
    file图片文件(≤8MB,multipart 字段名 file)

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/upload?type=avatar' \
      -H "Authorization: Bearer $TOKEN" \
      -F 'file=@avatar.png'
    

    成功响应:{ url }

    multipart 上传,字段名 file,≤8MB。成功返回 { "url": "https://file.xhsr.org.cn/…" }。

    POST /api/v2/upload?action=init

    初始化谱面包直传(分资源预签名) · 鉴权:需登录

    参数说明
    typechart-zip
    resources资源数组 [{path, size, contentType?}]

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/upload?action=init' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"resources":[{"path":"chart.json","size":12345},{"path":"audio.mp3","size":6789012}]}'
    

    成功响应:{ packageId, prefix, resources: [{path, key, size, contentType, inline, presignedUrl?}], expiresAt }

    响应示例

    {
      "packageId": "1791401113381-abc123def456",
      "prefix": "charts/1791401113381-abc123def456",
      "resources": [
        {
          "path": "chart.json",
          "key": "charts/1791401113381-abc123def456/chart.json",
          "size": 12345,
          "contentType": "application/json",
          "inline": true
        },
        {
          "path": "audio.mp3",
          "key": "charts/1791401113381-abc123def456/audio.mp3",
          "size": 6789012,
          "contentType": "audio/mpeg",
          "inline": false,
          "presignedUrl": "https://…<预签名 PUT 地址>"
        }
      ],
      "expiresAt": 1791401713381
    }
    

    inline: true 的资源走 finalize 的 multipart 内嵌;inline: false 的资源客户端拿 presignedUrl 直传 R2(绕过 Vercel 4.5MB 限制),签名 10 分钟有效。

    走 R2 预签名直传,绕过 Vercel 4.5MB 请求体限制;签名 10 分钟有效。

    POST /api/v2/upload?action=finalize

    合并资源并完成谱面登记 · 鉴权:需登录

    参数说明
    packageIdinit 返回的 packageId
    resources资源清单(与 init 返回一致)
    title/artist/charter/difficulty/level/bpm/offset/description谱面元数据(multipart 字段)
    allow_ai_learningboolean
    inline_<base64url(path)>小文件内嵌(multipart)

    请求示例

    curl -X POST 'https://api.xhsr.org.cn/api/v2/upload?action=finalize' \
      -H "Authorization: Bearer $TOKEN" \
      -F 'packageId=1791401113381-abc123def456' \
      -F 'resources=[{"path":"chart.json","key":"charts/1791401113381-abc123def456/chart.json","size":12345},{"path":"audio.mp3","key":"charts/1791401113381-abc123def456/audio.mp3","size":6789012}]' \
      -F 'title=Endtime' -F 'artist=Cres.' -F 'charter=Heronmony' \
      -F 'difficulty=Cloudburst' -F 'level=11+' -F 'bpm=180' \
      -F 'allow_ai_learning=false' \
      -F 'inline_YXVkaW8ubXAz=@audio.mp3'
    

    成功响应:{ data: Chart }

    multipart 表单。inline_<base64url(path)> 字段名内嵌小文件(例中 YXVkaW8ubXAz 即 base64url("audio.mp3"));直传资源由服务端回拉核对 size。成功后谱面状态为 pending,进入审核。


    工具 (Utils)

    /api/v2/convert-zip

    GET /api/v2/convert-zip

    获取谱面游玩元数据(本地库或 PhiZone) · 鉴权:公开

    参数说明
    chartId谱面 ID,PhiZone 用 pz-
    sourcelocal
    includeChartJson1 时附带谱面 JSON(较慢)

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/convert-zip?chartId=1&source=local&includeChartJson=<includeChartJson>'
    

    成功响应:{ data: { chartUrl, audioUrl, coverUrl, metaUrl, extraUrl, title, isZip } }

    响应示例

    {
      "data": {
        "chartUrl": "https://file.xhsr.org.cn/charts/1771149806393-chart.json",
        "chartJson": null,
        "audioUrl": "https://file.xhsr.org.cn/charts/1771149807796-audio.mp3",
        "coverUrl": "https://file.xhsr.org.cn/charts/1771149808591-image.png",
        "coverData": null,
        "metaUrl": "https://file.xhsr.org.cn/charts/recovered/8/meta.json",
        "extraUrl": null,
        "title": "Endtime",
        "isZip": false
      }
    }
    

    未上架谱面仅上传者本人与特权角色可查,防止枚举 pending 谱面的下载地址;PhiZone 上游异常返回 502。


    管理员 (Admin)

    /api/v2/admin

    GET /api/v2/admin?action=stats

    站点统计 · 鉴权:需 admin

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/admin?action=stats' \
      -H "Authorization: Bearer $TOKEN"
    

    成功响应:{ data }

    GET /api/v2/admin?action=users

    用户管理列表 · 鉴权:需 admin

    参数说明
    page—
    limit≤500
    sort—
    order—
    role—
    status—
    q—

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/admin?action=users&page=1&limit=20&sort=newest&order=desc&role=user&status=active&q=关键词' \
      -H "Authorization: Bearer $TOKEN"
    

    成功响应:{ data, page, limit, total }

    GET /api/v2/admin?action=charts

    谱面管理列表 · 鉴权:需 admin

    参数说明
    statuspending

    请求示例

    curl 'https://api.xhsr.org.cn/api/v2/admin?action=charts&status=active' \
      -H "Authorization: Bearer $TOKEN"
    

    成功响应:{ data }

    PUT /api/v2/admin?action=users

    修改用户角色 / 状态 · 鉴权:需 admin

    参数说明
    userId—
    roleuser
    statusactive

    请求示例

    curl -X PUT 'https://api.xhsr.org.cn/api/v2/admin?action=users' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"userId":"1","role":"user","status":"active"}'
    

    成功响应:{ message, user }

    PUT /api/v2/admin?action=charts

    修改谱面状态 · 鉴权:需 admin

    参数说明
    chartId或 id
    statuspending

    请求示例

    curl -X PUT 'https://api.xhsr.org.cn/api/v2/admin?action=charts' \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"chartId":"1","status":"active"}'
    

    成功响应:{ message }


    通用错误码

    HTTPcode 示例含义
    400invalid_request参数缺失或不合法
    401unauthorized未登录 / token 无效或过期
    403account_banned / email_unverified / forbidden权限不足或账号状态受限
    404not_found资源不存在
    409duplicate_report / ticket_closed资源冲突
    422—上传内容未通过校验(仅 records)
    429rate_limited触发频率限制
    500internal_error服务端错误(生产不返回内部细节)
    502github_upstream_error上游(GitHub / Milkloud)异常
    503—依赖功能未就绪(如未执行数据库迁移)
    修改于 2026-10-08 01:17:40
    下一页
    认证 (Auth) 端点(POST)
    Built with