{
  "openapi": "3.0.3",
  "info": {
    "title": "auth.yxlist.cn 独立软件认证服务",
    "version": "1.2.0",
    "description": "独立部署、只读复用主站账号；管理端使用独立saa1令牌，不共享主站JWT。R2未配置时日志仅本地入队。"
  },
  "servers": [
    {
      "url": "https://auth.yxlist.cn/api/software-auth/v1"
    }
  ],
  "paths": {
    "/login": {
      "post": {
        "summary": "各软件用现有账号密码独立登录；不做SSO",
        "security": [],
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "default": {
            "description": "失败；心跳任何非200结果必须退出",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            }
          }
        }
      }
    },
    "/heartbeat": {
      "post": {
        "summary": "检查用户/应用/会话并续租；失败立即退出",
        "security": [
          {
            "SoftwareSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "default": {
            "description": "失败；心跳任何非200结果必须退出",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "appId": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9._-]{0,63}$"
                  }
                },
                "required": [
                  "appId"
                ],
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/logout": {
      "post": {
        "summary": "幂等退出当前外部软件会话",
        "security": [
          {
            "SoftwareSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "auditQueued": {
                      "type": "boolean",
                      "nullable": true
                    },
                    "alreadyRevoked": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "ok",
                    "auditQueued",
                    "alreadyRevoked"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "default": {
            "description": "失败；心跳任何非200结果必须退出",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {},
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/events": {
      "post": {
        "summary": "持久入队并去重；202不代表已上传R2，不续租",
        "security": [
          {
            "SoftwareSession": []
          }
        ],
        "responses": {
          "202": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accepted": {
                      "type": "integer"
                    },
                    "duplicates": {
                      "type": "integer"
                    },
                    "archived": {
                      "type": "boolean",
                      "enum": [
                        false
                      ]
                    }
                  },
                  "required": [
                    "accepted",
                    "duplicates",
                    "archived"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "default": {
            "description": "失败；心跳任何非200结果必须退出",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "events": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 50,
                    "items": {
                      "$ref": "#/components/schemas/Event"
                    }
                  }
                },
                "required": [
                  "events"
                ],
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/admin/users/{id}/kick": {
      "post": {
        "summary": "仅撤销外部软件已有会话；不封禁重登，不退出旧站/插件",
        "security": [
          {
            "AdminSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "revokedCount": {
                      "type": "integer"
                    },
                    "scope": {
                      "type": "string",
                      "enum": [
                        "external-software"
                      ]
                    },
                    "auditQueued": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "ok",
                    "revokedCount",
                    "scope",
                    "auditQueued"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "default": {
            "description": "失败；心跳任何非200结果必须退出",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "appId": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9._-]{0,63}$"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ]
      }
    },
    "/admin/sessions": {
      "get": {
        "summary": "分页列出有效租约会话摘要，不返回token/hash",
        "security": [
          {
            "AdminSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "sessionId": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "userId": {
                            "type": "integer"
                          },
                          "appId": {
                            "type": "string",
                            "pattern": "^[a-z0-9][a-z0-9._-]{0,63}$"
                          },
                          "clientVersion": {
                            "type": "string"
                          },
                          "createdAt": {
                            "type": "integer"
                          },
                          "lastSeenAt": {
                            "type": "integer"
                          },
                          "leaseExpiresAt": {
                            "type": "integer"
                          },
                          "absoluteExpiresAt": {
                            "type": "integer"
                          },
                          "loginIp": {
                            "type": "string",
                            "nullable": true
                          },
                          "lastIp": {
                            "type": "string",
                            "nullable": true
                          },
                          "loginIpSource": {
                            "type": "string",
                            "nullable": true,
                            "enum": [
                              "socket",
                              "trusted_proxy",
                              "unavailable",
                              null
                            ]
                          },
                          "lastIpSource": {
                            "type": "string",
                            "nullable": true,
                            "enum": [
                              "socket",
                              "trusted_proxy",
                              "unavailable",
                              null
                            ]
                          }
                        },
                        "required": [
                          "sessionId",
                          "userId",
                          "appId",
                          "clientVersion",
                          "createdAt",
                          "lastSeenAt",
                          "leaseExpiresAt",
                          "absoluteExpiresAt",
                          "loginIp",
                          "lastIp",
                          "loginIpSource",
                          "lastIpSource"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "items",
                    "total",
                    "offset",
                    "limit"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "default": {
            "description": "失败；心跳任何非200结果必须退出",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "userId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "appId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9._-]{0,63}$"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ]
      }
    },
    "/admin/status": {
      "get": {
        "summary": "应用登记、租约策略、日志积压与R2运行状态",
        "security": [
          {
            "AdminSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enabled": {
                      "type": "boolean"
                    },
                    "apps": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "pattern": "^[a-z0-9][a-z0-9._-]{0,63}$"
                      }
                    },
                    "policy": {
                      "type": "object"
                    },
                    "queue": {
                      "type": "object"
                    },
                    "r2": {
                      "type": "object"
                    },
                    "appIdMode": {
                      "type": "string",
                      "enum": [
                        "self_report",
                        "allowlist"
                      ]
                    }
                  },
                  "required": [
                    "enabled",
                    "apps",
                    "policy",
                    "queue",
                    "r2",
                    "appIdMode"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "default": {
            "description": "失败；心跳任何非200结果必须退出",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/admin/login": {
      "post": {
        "servers": [
          {
            "url": "https://auth.yxlist.cn"
          }
        ],
        "summary": "现有管理员密码登录独立管理台",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "username",
                  "password"
                ],
                "additionalProperties": false,
                "properties": {
                  "username": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string",
                    "writeOnly": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "token",
                    "admin",
                    "serverTime",
                    "expiresAt"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "token": {
                      "type": "string"
                    },
                    "admin": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "username": {
                          "type": "string"
                        },
                        "displayName": {
                          "type": "string"
                        }
                      }
                    },
                    "serverTime": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "auditQueued": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "description": "失败",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/admin/logout": {
      "post": {
        "servers": [
          {
            "url": "https://auth.yxlist.cn"
          }
        ],
        "summary": "退出独立管理会话",
        "security": [
          {
            "AdminSession": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "description": "失败",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "SoftwareSession": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "opaque sa1 token",
        "description": "POST /login返回，仅保存在内存"
      },
      "AdminSession": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "opaque saa1 token",
        "description": "POST /admin/login取得，内存保存，30分钟绝对有效"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "error": {
            "type": "string",
            "description": "兼容旧网站api()的message别名"
          }
        },
        "required": [
          "code",
          "message",
          "requestId"
        ],
        "additionalProperties": false
      },
      "Session": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "sessionId": {
            "type": "string",
            "format": "uuid"
          },
          "user": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "username": {
                "type": "string"
              },
              "displayName": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "username",
              "displayName"
            ],
            "additionalProperties": false
          },
          "serverTime": {
            "type": "string",
            "format": "date-time"
          },
          "leaseExpiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "absoluteExpiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "heartbeatIntervalMs": {
            "type": "integer",
            "enum": [
              30000
            ]
          },
          "requestTimeoutMs": {
            "type": "integer",
            "enum": [
              5000
            ]
          }
        },
        "required": [
          "ok",
          "sessionId",
          "user",
          "serverTime",
          "leaseExpiresAt",
          "absoluteExpiresAt",
          "heartbeatIntervalMs",
          "requestTimeoutMs"
        ],
        "additionalProperties": false
      },
      "LoginResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "sessionId": {
            "type": "string",
            "format": "uuid"
          },
          "user": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "username": {
                "type": "string"
              },
              "displayName": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "username",
              "displayName"
            ],
            "additionalProperties": false
          },
          "serverTime": {
            "type": "string",
            "format": "date-time"
          },
          "leaseExpiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "absoluteExpiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "heartbeatIntervalMs": {
            "type": "integer",
            "enum": [
              30000
            ]
          },
          "requestTimeoutMs": {
            "type": "integer",
            "enum": [
              5000
            ]
          },
          "token": {
            "type": "string",
            "pattern": "^sa1_[A-Za-z0-9_-]{43}$"
          }
        },
        "required": [
          "ok",
          "sessionId",
          "user",
          "serverTime",
          "leaseExpiresAt",
          "absoluteExpiresAt",
          "heartbeatIntervalMs",
          "requestTimeoutMs",
          "token"
        ],
        "additionalProperties": false
      },
      "Event": {
        "type": "object",
        "properties": {
          "eventId": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "enum": [
              "app.open",
              "feature.use",
              "operation.complete",
              "app.error"
            ]
          },
          "feature": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_.:-]{1,80}$"
          },
          "result": {
            "type": "string",
            "enum": [
              "success",
              "failure",
              "info"
            ]
          },
          "durationMs": {
            "type": "integer",
            "minimum": 0,
            "maximum": 86400000
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "eventId",
          "type"
        ],
        "additionalProperties": false
      },
      "LoginRequest": {
        "type": "object",
        "properties": {
          "appId": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9._-]{0,63}$",
            "description": "软件自行提交的固定编号；默认自报模式无需预登记，可选白名单另行限制。不是密码或客户端真实性证明。"
          },
          "username": {
            "type": "string",
            "minLength": 2,
            "maxLength": 20
          },
          "password": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1024,
            "writeOnly": true
          },
          "clientVersion": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_.+-]{1,40}$"
          }
        },
        "required": [
          "appId",
          "username",
          "password"
        ],
        "additionalProperties": false
      }
    }
  }
}
