Files
moonwell-web/openapi.json
2026-08-17 20:25:40 +04:00

829 lines
40 KiB
JSON

{
"openapi": "3.0.0",
"info": {
"title": "Moonwell Launcher API",
"description": "API для лаунчера WoW-клиента Moonwell. Авторизация, новости, реалмы и их статусы, получение манифеста файлов и скачивание обновлений.",
"version": "1.0.0"
},
"paths": {
"/api/launcher/account": {
"get": {
"tags": [
"Launcher"
],
"summary": "Получить информацию об аккаунте",
"description": "Возвращает имя и текущий баланс авторизованного игрового аккаунта.",
"operationId": "9a18d29372e8de08e1d24237580f19e5",
"responses": {
"200": {
"description": "Информация об аккаунте",
"content": {
"application/json": {
"schema": {
"properties": {
"username": {
"type": "string",
"example": "PLAYER"
},
"balance": {
"type": "string",
"example": "1250.00"
}
},
"type": "object"
}
}
}
},
"401": {
"description": "Не авторизован",
"content": {
"application/json": {
"schema": {
"properties": {
"message": {
"type": "string",
"example": "Unauthenticated."
}
},
"type": "object"
}
}
}
}
},
"security": [
{
"bearerAuth": []
}
]
}
},
"/api/launcher/login": {
"post": {
"tags": [
"Launcher Auth"
],
"summary": "Авторизация лаунчера",
"description": "Авторизация по логину и паролю игрового аккаунта. Возвращает Bearer-токен для доступа к API.",
"operationId": "8b934358769733a6925c1fbc8dc07705",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"required": [
"username",
"password"
],
"properties": {
"username": {
"type": "string",
"pattern": "^[A-Za-z0-9]+$",
"example": "Player",
"maxLength": 32,
"minLength": 3
},
"password": {
"type": "string",
"example": "secret123",
"maxLength": 32,
"minLength": 3
}
},
"type": "object"
}
}
}
},
"responses": {
"200": {
"description": "Успешная авторизация",
"content": {
"application/json": {
"schema": {
"properties": {
"access_token": {
"description": "JWT access token",
"type": "string"
},
"token_type": {
"type": "string",
"example": "Bearer"
},
"expires_at": {
"type": "string",
"format": "date-time",
"example": "2026-04-20T13:00:00+00:00"
}
},
"type": "object"
}
}
}
},
"401": {
"description": "Неверный логин или пароль",
"content": {
"application/json": {
"schema": {
"properties": {
"message": {
"type": "string",
"example": "Неверный логин или пароль."
}
},
"type": "object"
}
}
}
},
"422": {
"description": "Ошибка валидации",
"content": {
"application/json": {
"schema": {
"properties": {
"message": {
"type": "string"
},
"errors": {
"type": "object"
}
},
"type": "object"
}
}
}
},
"429": {
"description": "Слишком много запросов"
}
}
}
},
"/api/launcher/manifest": {
"get": {
"tags": [
"Launcher"
],
"summary": "Получить манифест файлов клиента",
"description": "Возвращает JSON-манифест со списком файлов клиента, их размерами и SHA-256 хешами. Лаунчер сверяет локальные файлы с манифестом и докачивает недостающие.",
"operationId": "2c449e4bb08c2d4c838e36d260a3b61d",
"responses": {
"200": {
"description": "Манифест файлов",
"content": {
"application/json": {
"schema": {
"properties": {
"files": {
"type": "array",
"items": {
"properties": {
"path": {
"type": "string",
"example": "Data/common.MPQ"
},
"size": {
"type": "integer",
"format": "int64",
"example": 2881154862
},
"sha256": {
"type": "string",
"format": "hex",
"example": "d850ffa5efd6a1ba845899a7d9f9ad27f6eea7ac606e95033506c72c86ee0236"
}
},
"type": "object"
}
}
},
"type": "object"
}
}
}
},
"401": {
"description": "Не авторизован",
"content": {
"application/json": {
"schema": {
"properties": {
"message": {
"type": "string",
"example": "Unauthenticated."
}
},
"type": "object"
}
}
}
},
"404": {
"description": "Манифест не найден в хранилище",
"content": {
"application/json": {
"schema": {
"properties": {
"message": {
"type": "string",
"example": "Манифест не найден."
}
},
"type": "object"
}
}
}
}
},
"security": [
{
"bearerAuth": []
}
]
}
},
"/api/launcher/download/{path}": {
"get": {
"tags": [
"Launcher"
],
"summary": "Получить ссылку на скачивание файла",
"description": "Возвращает временную presigned-ссылку на файл в S3. Лаунчер скачивает файл напрямую из хранилища по этой ссылке. Путь может содержать вложенные директории (например `Data/ruRU/patch-ruRU-3.MPQ`).",
"operationId": "9a94331833a7af795c1c57dfa3a77eb3",
"parameters": [
{
"name": "path",
"in": "path",
"description": "Относительный путь к файлу из манифеста",
"required": true,
"schema": {
"type": "string"
},
"example": "Data/common.MPQ"
}
],
"responses": {
"200": {
"description": "Временная ссылка на скачивание",
"content": {
"application/json": {
"schema": {
"properties": {
"url": {
"description": "Presigned URL для скачивания",
"type": "string",
"format": "uri"
},
"expires_in": {
"description": "Время жизни ссылки в секундах",
"type": "integer",
"example": 3600
}
},
"type": "object"
}
}
}
},
"401": {
"description": "Не авторизован",
"content": {
"application/json": {
"schema": {
"properties": {
"message": {
"type": "string",
"example": "Unauthenticated."
}
},
"type": "object"
}
}
}
},
"404": {
"description": "Файл не найден",
"content": {
"application/json": {
"schema": {
"properties": {
"message": {
"type": "string",
"example": "Файл не найден."
}
},
"type": "object"
}
}
}
}
},
"security": [
{
"bearerAuth": []
}
]
}
},
"/api/service/update-appcast": {
"post": {
"tags": [
"Launcher Service"
],
"summary": "Обновить appcast лаунчера",
"description": "Служебный endpoint для разработчика лаунчера. Принимает готовый XML appcast в формате RSS 2.0/Sparkle, который использует Flutter-пакет `upgrader`, и сохраняет его как публичный `/appcast.xml` на сервере. Тело запроса передается как raw XML, без JSON-обертки.",
"operationId": "0b1b8798e0eefc46277891ccc1440afb",
"requestBody": {
"required": true,
"content": {
"application/xml": {
"schema": {
"type": "string",
"example": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<rss version=\"2.0\" xmlns:sparkle=\"http://www.andymatuschak.org/xml-namespaces/sparkle\">\n <channel>\n <title>Moonwell Launcher - Appcast</title>\n <item>\n <title>Version 1.2.0</title>\n <description>Описание изменений для окна обновления.</description>\n <pubDate>Sat, 16 May 2026 12:00:00 +0400</pubDate>\n <enclosure url=\"https://moonwell.su/launcher/download\" sparkle:version=\"1.2.0\" sparkle:os=\"windows\" />\n </item>\n </channel>\n</rss>"
}
}
}
},
"responses": {
"200": {
"description": "Appcast сохранен",
"content": {
"application/json": {
"schema": {
"properties": {
"message": {
"type": "string",
"example": "XML saved"
}
},
"type": "object"
}
}
}
},
"403": {
"description": "Нет доступа или передано пустое тело запроса",
"content": {
"application/json": {
"schema": {
"properties": {
"message": {
"type": "string",
"example": "empty body"
}
},
"type": "object"
}
}
}
},
"500": {
"description": "Не удалось сохранить appcast.xml",
"content": {
"application/json": {
"schema": {
"properties": {
"message": {
"type": "string",
"example": "Failed to save XML"
}
},
"type": "object"
}
}
}
}
},
"security": [
{
"launcherAuth": []
}
]
}
},
"/api/launcher/game-ticket": {
"post": {
"tags": [
"Launcher Auth"
],
"summary": "Issue a single-use game login ticket",
"description": "Creates short-lived SRP credentials for authserver. A new ticket atomically revokes the previous generation. The plaintext ticket is never stored.",
"operationId": "7b54b9b05714077ff61483260a0e64a7",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"required": [
"client_build"
],
"properties": {
"client_build": {
"type": "integer",
"example": 12340
}
},
"type": "object"
}
}
}
},
"responses": {
"200": {
"description": "Ticket issued",
"content": {
"application/json": {
"schema": {
"required": [
"account",
"ticket",
"expires_at"
],
"properties": {
"account": {
"type": "string",
"example": "PLAYERONE"
},
"ticket": {
"type": "string",
"pattern": "^[A-Z0-9]{16}$",
"example": "7W6D4M2Q9K8R3X5Z"
},
"expires_at": {
"type": "string",
"format": "date-time"
}
},
"type": "object"
}
}
}
},
"401": {
"description": "Launcher authentication required"
},
"422": {
"description": "Client build is not allowed"
},
"429": {
"description": "Too many requests"
}
},
"security": [
{
"bearerAuth": []
}
]
}
},
"/api/launcher/news": {
"get": {
"tags": [
"Launcher"
],
"summary": "Список новостей лаунчера",
"description": "Возвращает опубликованные новости для отображения в лаунчере. Отсортированы по sort_order и дате создания.",
"operationId": "69aa09e0c7cca466e8a45a186444245e",
"responses": {
"200": {
"description": "Список новостей",
"content": {
"application/json": {
"schema": {
"properties": {
"data": {
"type": "array",
"items": {
"properties": {
"id": {
"type": "integer",
"example": 1
},
"title": {
"type": "string",
"example": "Обновление 1.2"
},
"body": {
"type": "string",
"example": "Описание обновления..."
},
"image_url": {
"type": "string",
"format": "uri",
"example": "https://storage.yandexcloud.net/warcraft-client/news/update-1.2.jpg",
"nullable": true
},
"created_at": {
"type": "string",
"format": "date-time"
}
},
"type": "object"
}
}
},
"type": "object"
}
}
}
},
"401": {
"description": "Не авторизован",
"content": {
"application/json": {
"schema": {
"properties": {
"message": {
"type": "string",
"example": "Unauthenticated."
}
},
"type": "object"
}
}
}
}
},
"security": [
{
"bearerAuth": []
}
]
}
},
"/api/launcher/realms": {
"get": {
"tags": [
"Launcher"
],
"summary": "Получить список реалмов и их статус",
"description": "Возвращает реалмы из таблицы realmlist. Статус online означает, что игровой порт реалма доступен по TCP.",
"operationId": "6d142b37a7b9a121b7ad02fd6405e054",
"responses": {
"200": {
"description": "Список реалмов",
"content": {
"application/json": {
"schema": {
"properties": {
"data": {
"type": "array",
"items": {
"properties": {
"id": {
"type": "integer",
"example": 1
},
"name": {
"type": "string",
"example": "MoonWell"
},
"address": {
"type": "string",
"example": "logon.moon-well.online"
},
"port": {
"type": "integer",
"example": 8085
},
"icon": {
"type": "integer",
"example": 1
},
"flag": {
"type": "integer",
"example": 0
},
"timezone": {
"type": "integer",
"example": 4
},
"population": {
"type": "number",
"format": "float",
"example": 0.5
},
"online": {
"type": "boolean",
"example": true
},
"status": {
"type": "string",
"example": "online",
"enum": [
"online",
"offline"
]
}
},
"type": "object"
}
},
"checked_at": {
"type": "string",
"format": "date-time"
}
},
"type": "object"
}
}
}
},
"503": {
"description": "Не удалось получить список реалмов",
"content": {
"application/json": {
"schema": {
"properties": {
"message": {
"type": "string",
"example": "Не удалось получить список реалмов."
}
},
"type": "object"
}
}
}
},
"429": {
"description": "Слишком много запросов"
}
}
}
},
"/api/launcher/register": {
"post": {
"tags": [
"Launcher Auth"
],
"summary": "Зарегистрировать игровой аккаунт",
"description": "Создаёт игровой аккаунт AzerothCore. Инвайт-код обязателен, если на сервере включена регистрация по приглашениям.",
"operationId": "bfc1dbd42cbcc339bc8a13dbf189c525",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"required": [
"username",
"email",
"password",
"password_confirmation",
"terms"
],
"properties": {
"username": {
"type": "string",
"pattern": "^[A-Za-z0-9]+$",
"example": "Player",
"maxLength": 32,
"minLength": 3
},
"email": {
"type": "string",
"format": "email",
"example": "player@example.com",
"maxLength": 255
},
"invite_code": {
"type": "string",
"example": "ABCD-EFGH-IJKL",
"nullable": true,
"maxLength": 32,
"minLength": 6
},
"password": {
"type": "string",
"example": "Secret123",
"maxLength": 32,
"minLength": 8
},
"password_confirmation": {
"type": "string",
"example": "Secret123",
"maxLength": 32,
"minLength": 8
},
"terms": {
"description": "Согласие с правилами сервера и пользовательским соглашением",
"type": "boolean",
"example": true
}
},
"type": "object"
}
}
}
},
"responses": {
"201": {
"description": "Аккаунт создан",
"content": {
"application/json": {
"schema": {
"properties": {
"message": {
"type": "string",
"example": "Игровой аккаунт успешно создан."
},
"account": {
"properties": {
"id": {
"type": "integer",
"example": 77
},
"username": {
"type": "string",
"example": "PLAYER"
}
},
"type": "object"
}
},
"type": "object"
}
}
}
},
"422": {
"description": "Ошибка валидации, занятый логин или неверный инвайт-код",
"content": {
"application/json": {
"schema": {
"properties": {
"message": {
"type": "string",
"example": "Такой игровой аккаунт уже существует."
},
"errors": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"type": "object"
}
}
}
},
"500": {
"description": "Внутренняя ошибка регистрации",
"content": {
"application/json": {
"schema": {
"properties": {
"message": {
"type": "string",
"example": "Не удалось создать аккаунт. Попробуй снова позже."
}
},
"type": "object"
}
}
}
},
"429": {
"description": "Слишком много запросов"
}
}
}
}
},
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"description": "Токен, полученный через POST /api/launcher/login",
"bearerFormat": "JWT",
"scheme": "bearer"
},
"launcherAuth": {
"type": "apiKey",
"description": "Сервисный ключ из LAUNCHER_AUTH_KEY для запросов лаунчера к служебным endpoint-ам.",
"name": "Auth",
"in": "header"
}
}
},
"tags": [
{
"name": "Launcher",
"description": "Launcher"
},
{
"name": "Launcher Auth",
"description": "Launcher Auth"
},
{
"name": "Launcher Service",
"description": "Launcher Service"
}
]
}