openapi: 3.0.3
info:
  title: rate-gate
  version: 0.1.0
  description: >
    Сервис ограничения частоты. Алгоритмы: токен-бакет (целочисленные милли-токены)
    и скользящее окно (счётчики текущего и предыдущего окна). Состояние в памяти.
servers:
  - url: http://localhost:8112
security:
  - {}
  - bearer: []
paths:
  /v1/check:
    post:
      summary: Проверить и списать лимит
      description: Если задан API_TOKEN, нужен заголовок Authorization Bearer. При отказе ответ 200 с allowed=false и заголовком Retry-After.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [key]
              properties:
                key: { type: string, minLength: 1, maxLength: 256, example: "ip:203.0.113.7" }
                cost: { type: integer, minimum: 1, default: 1 }
                rule: { type: string, description: Имя правила; иначе выбирается по префиксу ключа }
      responses:
        "200":
          description: Решение
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Decision" }
        "400": { description: Некорректный ввод }
        "401": { description: Нужен токен }
        "422": { description: cost больше лимита правила }
        "503": { description: Хранилище ключей заполнено }
  /v1/demo/check:
    post:
      summary: Открытая проверка для страницы-демо (правило demo)
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                key: { type: string, maxLength: 32 }
      responses:
        "200":
          description: Решение
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Decision" }
  /v1/rules:
    get:
      summary: Список правил
      responses:
        "200": { description: Правила }
  /v1/stats:
    get:
      summary: Счётчики и последние проверки (ключи хэшируются)
      responses:
        "200": { description: Статистика }
  /metrics:
    get:
      summary: Метрики Prometheus
      responses:
        "200":
          description: Текстовый формат
          content:
            text/plain: { schema: { type: string } }
  /healthz:
    get:
      summary: Проверка живости
      responses:
        "200": { description: ok }
components:
  securitySchemes:
    bearer: { type: http, scheme: bearer }
  schemas:
    Decision:
      type: object
      properties:
        allowed: { type: boolean }
        remaining: { type: integer }
        limit: { type: integer }
        reset_ms: { type: integer, description: Мс до полного восстановления }
        retry_after_ms: { type: integer, description: Мс до следующей попытки (0, если разрешено) }
        rule: { type: string }
