{
  "openapi": "3.1.0",
  "info": {
    "title": "BumpAPI",
    "version": "1.0.0",
    "summary": "桌面软件的版本检查与发布接口",
    "description": "给桌面软件用的版本检查 API。客户端用 app_key 查询有没有新版本；开发者用控制台登录后的 JWT 或 API 密钥发布版本、管理应用。\n\n认证有两种：\n1. API 密钥（推荐给程序和 AI 用）：`X-API-Key: bp_xxx` 或 `Authorization: Bearer bp_xxx`，权限在创建时勾选。\n2. 控制台 JWT：`Authorization: Bearer <jwt>`，从 /api/auth/login 获取，默认 7 天过期，拥有全部权限。\n\n查询接口 /api/v1/check 不需要认证，app_key 本身即是标识。",
    "contact": {
      "name": "BumpAPI",
      "url": "https://bump.tinyaii.cn/docs/"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://bump.tinyaii.cn",
      "description": "生产环境"
    }
  ],
  "security": [
    {
      "jwt": []
    },
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "client",
      "description": "客户端调用，无需认证"
    },
    {
      "name": "auth",
      "description": "注册与登录"
    },
    {
      "name": "apps",
      "description": "应用管理"
    },
    {
      "name": "releases",
      "description": "版本发布"
    },
    {
      "name": "stats",
      "description": "统计"
    },
    {
      "name": "keys",
      "description": "API 密钥管理，仅限控制台 JWT"
    }
  ],
  "components": {
    "securitySchemes": {
      "jwt": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "控制台登录后获得的 token，默认 168 小时过期，拥有全部权限"
      },
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "以 bp_ 开头的长期密钥，权限在创建时勾选。也接受 Authorization: Bearer bp_xxx 的写法"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "机器可读的错误码"
          },
          "message": {
            "type": "string",
            "description": "给人看的说明"
          }
        },
        "required": [
          "error",
          "message"
        ]
      },
      "CheckResult": {
        "type": "object",
        "properties": {
          "has_update": {
            "type": "boolean"
          },
          "latest_version": {
            "type": "string"
          },
          "release_notes": {
            "type": "string"
          },
          "force_update": {
            "type": "boolean"
          },
          "publish_time": {
            "type": "string",
            "description": "YYYY-MM-DD，东八区"
          },
          "platform": {
            "type": "string"
          },
          "download_url": {
            "type": "string"
          },
          "sha256": {
            "type": "string",
            "description": "安装包哈希，发布时可选。只用于发现文件损坏，挡不住中间人"
          },
          "channel": {
            "type": "string",
            "description": "这个版本所属的通道"
          }
        },
        "required": [
          "has_update"
        ]
      },
      "App": {
        "type": "object",
        "properties": {
          "app_key": {
            "type": "string",
            "example": "app_8ee7d6c29be378ad4492a072"
          },
          "app_name": {
            "type": "string",
            "example": "我的桌面应用"
          },
          "min_supported_version": {
            "type": "string",
            "description": "强制更新阈值：低于这个版本的客户端会被要求强制升级"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled"
            ]
          },
          "latest_version": {
            "type": "string"
          },
          "release_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "integer",
            "description": "Unix 秒"
          }
        }
      },
      "Release": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string"
          },
          "release_notes": {
            "type": "string"
          },
          "platform": {
            "type": "string"
          },
          "download_url": {
            "type": "string"
          },
          "is_latest": {
            "type": "boolean"
          },
          "publish_time": {
            "type": "string"
          },
          "min_supported_version": {
            "type": "string"
          },
          "sha256": {
            "type": "string"
          },
          "channel": {
            "type": "string"
          }
        }
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string",
            "example": "bp_31ecbad7"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expires_at": {
            "type": "integer",
            "description": "Unix 秒，0 表示永不过期"
          },
          "last_used_at": {
            "type": "integer"
          },
          "created_at": {
            "type": "integer"
          },
          "revoked": {
            "type": "boolean"
          },
          "expired": {
            "type": "boolean"
          }
        }
      }
    }
  },
  "paths": {
    "/api/v1/check": {
      "get": {
        "tags": [
          "client"
        ],
        "summary": "查询是否有新版本",
        "description": "客户端启动时调用一次即可，不要放进循环。任何失败都应静默处理，不要弹错误框。",
        "security": [],
        "parameters": [
          {
            "name": "app_key",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "应用标识，来自控制台"
          },
          {
            "name": "current_version",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "1.0.0"
            },
            "description": "客户端当前版本号，纯数字点分，2-4 段"
          },
          {
            "name": "channel",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "stable"
            },
            "description": "发布通道。不传默认 stable；内测客户端传 beta。每个通道各自维护一个最新版"
          }
        ],
        "responses": {
          "200": {
            "description": "查询成功。无更新时只返回 has_update:false",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckResult"
                },
                "examples": {
                  "noUpdate": {
                    "summary": "已是最新",
                    "value": {
                      "has_update": false
                    }
                  },
                  "hasUpdate": {
                    "summary": "有新版本",
                    "value": {
                      "has_update": true,
                      "latest_version": "1.0.10",
                      "release_notes": "修复了崩溃问题",
                      "force_update": false,
                      "publish_time": "2026-10-28",
                      "platform": "all",
                      "download_url": "https://example.com/app-1.0.10.exe"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "参数缺失或版本号格式不对",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "app_key 不存在",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "请求过于频繁，响应头 Retry-After 给出秒数"
          }
        }
      }
    },
    "/api/auth/register": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "注册开发者账号",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 72
                  }
                },
                "required": [
                  "email",
                  "password"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "注册成功，返回 token 与用户信息"
          },
          "400": {
            "description": "邮箱格式或密码长度不合法"
          },
          "409": {
            "description": "邮箱已注册"
          }
        }
      }
    },
    "/api/auth/login": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "登录",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  }
                },
                "required": [
                  "email",
                  "password"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "登录成功，返回 token"
          },
          "401": {
            "description": "邮箱或密码错误"
          },
          "403": {
            "description": "账号被停用"
          }
        }
      }
    },
    "/api/admin/apps": {
      "get": {
        "tags": [
          "apps"
        ],
        "summary": "列出我的应用",
        "description": "需要 app:read 权限",
        "responses": {
          "200": {
            "description": "应用列表",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "apps": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/App"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "缺少 app:read 权限"
          }
        }
      },
      "post": {
        "tags": [
          "apps"
        ],
        "summary": "创建应用",
        "description": "需要 app:write 权限。创建时不能设强制更新阈值——没有版本时阈值不生效，请在发布第一个版本时用 force_update，或事后 PATCH。传非 0.0.0 的 min_supported_version 会返回 400。",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "app_name": {
                    "type": "string",
                    "maxLength": 60
                  }
                },
                "required": [
                  "app_name"
                ]
              },
              "examples": {
                "simple": {
                  "summary": "只填名字",
                  "value": {
                    "app_name": "我的桌面应用"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "创建成功，返回含 app_key 的应用（latest_version 为空表示还没发布）",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/App"
                }
              }
            }
          },
          "400": {
            "description": "参数不合法，或试图在创建时设置阈值"
          },
          "403": {
            "description": "缺少 app:write 权限"
          }
        }
      }
    },
    "/api/admin/apps/{app_key}": {
      "patch": {
        "tags": [
          "apps"
        ],
        "summary": "修改应用",
        "description": "需要 app:write 权限。min_supported_version 不能高于当前最新版。",
        "parameters": [
          {
            "name": "app_key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "app_name": {
                    "type": "string"
                  },
                  "min_supported_version": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "disabled"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新后的应用"
          },
          "400": {
            "description": "参数不合法"
          },
          "404": {
            "description": "应用不存在或不属于当前账号"
          }
        }
      },
      "delete": {
        "tags": [
          "apps"
        ],
        "summary": "删除应用",
        "description": "需要 app:delete 权限。会连带删除全部版本与请求日志，不可恢复。",
        "parameters": [
          {
            "name": "app_key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "删除成功"
          },
          "404": {
            "description": "应用不存在"
          }
        }
      }
    },
    "/api/admin/apps/{app_key}/rotate": {
      "post": {
        "tags": [
          "apps"
        ],
        "summary": "重置 app_key",
        "description": "需要 app:write 权限。旧 key 立即失效，历史日志会迁移到新 key。",
        "parameters": [
          {
            "name": "app_key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "返回带新 app_key 的应用"
          },
          "404": {
            "description": "应用不存在"
          }
        }
      }
    },
    "/api/admin/release": {
      "post": {
        "tags": [
          "releases"
        ],
        "summary": "发布新版本",
        "description": "需要 release:write 权限。发布后立即生效，客户端下一次请求就能看到。",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "app_key": {
                    "type": "string"
                  },
                  "new_version": {
                    "type": "string",
                    "example": "1.0.2"
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 5000
                  },
                  "force_update": {
                    "type": "boolean",
                    "description": "为 true 时把强制更新阈值提到本次版本，所有旧版本用户被迫升级"
                  },
                  "min_supported_version": {
                    "type": "string",
                    "description": "显式指定阈值，优先级高于 force_update，不能高于 new_version"
                  },
                  "platform": {
                    "type": "string",
                    "default": "all"
                  },
                  "download_url": {
                    "type": "string"
                  },
                  "channel": {
                    "type": "string",
                    "default": "stable",
                    "description": "发布通道，默认 stable。beta 不允许配合 force_update 使用：阈值是全应用共用的，beta 版本号更高，抬阈值会把 stable 用户强更到内测版"
                  },
                  "sha256": {
                    "type": "string",
                    "description": "安装包 sha256，64 位十六进制。可选"
                  }
                },
                "required": [
                  "app_key",
                  "new_version"
                ]
              },
              "examples": {
                "simple": {
                  "summary": "普通发版",
                  "value": {
                    "app_key": "app_xxx",
                    "new_version": "1.0.2",
                    "notes": "修复了崩溃问题"
                  }
                },
                "forced": {
                  "summary": "强制更新",
                  "value": {
                    "app_key": "app_xxx",
                    "new_version": "1.0.3",
                    "notes": "安全修复",
                    "force_update": true
                  }
                },
                "beta": {
                  "summary": "发到内测通道",
                  "value": {
                    "app_key": "app_xxx",
                    "new_version": "1.1.0",
                    "notes": "内测版：新的启动动画",
                    "channel": "beta"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "发布成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Release"
                }
              }
            }
          },
          "400": {
            "description": "参数不合法或阈值高于新版本"
          },
          "404": {
            "description": "应用不存在"
          },
          "409": {
            "description": "该版本号已存在"
          }
        }
      }
    },
    "/api/admin/apps/{app_key}/releases": {
      "get": {
        "tags": [
          "releases"
        ],
        "summary": "发布历史",
        "description": "需要 app:read 权限",
        "parameters": [
          {
            "name": "app_key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "按发布时间倒序",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "app_key": {
                      "type": "string"
                    },
                    "releases": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Release"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/admin/apps/{app_key}/releases/{version}": {
      "patch": {
        "tags": [
          "releases"
        ],
        "summary": "修改版本信息",
        "description": "需要 release:write 权限。版本号本身不可修改，只能改说明、平台、下载地址、哈希和通道。改通道会把该版本设为目标通道的最新版，原通道回退到上一版",
        "parameters": [
          {
            "name": "app_key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "notes": {
                    "type": "string"
                  },
                  "platform": {
                    "type": "string"
                  },
                  "download_url": {
                    "type": "string"
                  },
                  "sha256": {
                    "type": "string",
                    "description": "安装包哈希"
                  },
                  "channel": {
                    "type": "string",
                    "description": "改通道即「转正」（beta → stable）或「撤回内测」。目标通道的最新版会让位给这个版本"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新后的版本"
          },
          "404": {
            "description": "版本不存在"
          }
        }
      },
      "delete": {
        "tags": [
          "releases"
        ],
        "summary": "删除版本",
        "description": "需要 release:delete 权限。删除最新版时，上一版自动接任，强制阈值会一起下调。",
        "parameters": [
          {
            "name": "app_key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "删除结果，含新的最新版与阈值"
          },
          "404": {
            "description": "版本不存在"
          }
        }
      }
    },
    "/api/admin/stats/overview": {
      "get": {
        "tags": [
          "stats"
        ],
        "summary": "概览数字",
        "description": "需要 stats:read 权限。今日数据对比的是昨天同一时段。",
        "responses": {
          "200": {
            "description": "应用数、发布数、今日与累计请求、强制更新应用数",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "apps": {
                      "type": "integer"
                    },
                    "releases": {
                      "type": "integer"
                    },
                    "requests_total": {
                      "type": "integer"
                    },
                    "forced_apps": {
                      "type": "integer"
                    },
                    "requests_today": {
                      "type": "object",
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "delta": {
                          "type": "number"
                        },
                        "has_delta": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/admin/stats/daily": {
      "get": {
        "tags": [
          "stats"
        ],
        "summary": "每日请求趋势",
        "description": "需要 stats:read 权限。缺失日期补 0。",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 30,
              "default": 7
            }
          }
        ],
        "responses": {
          "200": {
            "description": "每日请求数"
          }
        }
      }
    },
    "/api/admin/stats/versions": {
      "get": {
        "tags": [
          "stats"
        ],
        "summary": "客户端版本分布",
        "description": "需要 stats:read 权限。最多返回 8 条。",
        "parameters": [
          {
            "name": "app_key",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "按次数倒序的版本分布"
          }
        }
      }
    },
    "/api/user/keys": {
      "get": {
        "tags": [
          "keys"
        ],
        "summary": "列出 API 密钥",
        "description": "只能用控制台 JWT 调用，API 密钥不能管理密钥。",
        "responses": {
          "200": {
            "description": "密钥列表，不含明文",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ApiKey"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "用 API 密钥调用了这个接口"
          }
        }
      },
      "post": {
        "tags": [
          "keys"
        ],
        "summary": "创建 API 密钥",
        "description": "只能用控制台 JWT 调用。明文只在响应里出现这一次。",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 40
                  },
                  "scopes": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string",
                      "enum": [
                        "app:read",
                        "stats:read",
                        "release:write",
                        "release:delete",
                        "app:write",
                        "app:delete"
                      ]
                    }
                  },
                  "ttl_days": {
                    "type": "integer",
                    "enum": [
                      7,
                      30,
                      90,
                      0
                    ],
                    "default": 30,
                    "description": "0 表示永不过期"
                  }
                },
                "required": [
                  "name",
                  "scopes"
                ]
              },
              "examples": {
                "ci": {
                  "summary": "给 CI 用，只勾发版权限",
                  "value": {
                    "name": "GitHub Actions",
                    "scopes": [
                      "release:write"
                    ],
                    "ttl_days": 90
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "创建成功。plain 字段是完整密钥，只出现这一次"
          },
          "400": {
            "description": "参数不合法"
          },
          "409": {
            "description": "在用的密钥已达 10 个上限"
          }
        }
      }
    },
    "/api/user/keys/scopes": {
      "get": {
        "tags": [
          "keys"
        ],
        "summary": "列出可选权限",
        "responses": {
          "200": {
            "description": "权限清单，含分组与危险标记"
          }
        }
      }
    },
    "/api/user/keys/{id}": {
      "delete": {
        "tags": [
          "keys"
        ],
        "summary": "撤销密钥",
        "description": "撤销后立即失效。",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "撤销成功"
          },
          "404": {
            "description": "密钥不存在或已撤销"
          }
        }
      }
    }
  }
}
