openapi: 3.1.0
info:
  # Do not change the title, if the title changes, the import paths will be broken
  title: Api
  version: 0.1.0
  description: BulkMailer Pro API specification
servers:
  - url: /api
    description: Base API path
tags:
  - name: health
    description: Health operations
  - name: auth
    description: Authentication
  - name: settings
    description: Public settings
  - name: user
    description: User profile and credentials
  - name: mailer
    description: Email sending
  - name: payments
    description: License payments
  - name: admin
    description: Admin panel
paths:
  /healthz:
    get:
      operationId: healthCheck
      tags: [health]
      summary: Health check
      responses:
        "200":
          description: Healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthStatus"

  /auth/register:
    post:
      operationId: register
      tags: [auth]
      summary: Register a new user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UserRegistration"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthResponse"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /auth/login:
    post:
      operationId: login
      tags: [auth]
      summary: Log in
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UserLogin"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /auth/logout:
    post:
      operationId: logout
      tags: [auth]
      summary: Log out
      responses:
        "204":
          description: Logged out

  /auth/me:
    get:
      operationId: getMe
      tags: [auth]
      summary: Get current user
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserProfile"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /auth/forgot-password:
    post:
      operationId: forgotPassword
      tags: [auth]
      summary: Request password reset
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ForgotPasswordRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"

  /auth/reset-password:
    post:
      operationId: resetPassword
      tags: [auth]
      summary: Reset password with token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PasswordReset"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"
        "400":
          description: Invalid token
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /settings/public:
    get:
      operationId: getPublicSettings
      tags: [settings]
      summary: Get public settings (wallet, prices, telegram)
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicSettings"

  /user/credentials:
    get:
      operationId: getUserCredentials
      tags: [user]
      summary: Get user mailer credentials (masked)
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserCredentials"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    put:
      operationId: saveUserCredentials
      tags: [user]
      summary: Save user mailer credentials
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CredentialUpdate"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserCredentials"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /mailer/send:
    post:
      operationId: sendEmails
      tags: [mailer]
      summary: Send bulk emails
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BulkEmailSend"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SendResult"
        "403":
          description: No active license
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /mailer/logs:
    get:
      operationId: getEmailLogs
      tags: [mailer]
      summary: Get send history
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/EmailLog"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /payments:
    post:
      operationId: submitPayment
      tags: [payments]
      summary: Submit a payment claim
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PaymentSubmission"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentRecord"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    get:
      operationId: getMyPayments
      tags: [payments]
      summary: Get my payment history
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/PaymentRecord"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /admin/login:
    post:
      operationId: adminLogin
      tags: [admin]
      summary: Admin login
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AdminCredentials"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AdminAuthResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /admin/logout:
    post:
      operationId: adminLogout
      tags: [admin]
      summary: Admin logout
      responses:
        "204":
          description: Logged out

  /admin/settings:
    get:
      operationId: getAdminSettings
      tags: [admin]
      summary: Get all admin settings
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AdminSettings"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    put:
      operationId: updateAdminSettings
      tags: [admin]
      summary: Update admin settings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SettingsUpdate"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AdminSettings"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /admin/users:
    get:
      operationId: getAdminUsers
      tags: [admin]
      summary: Get all users
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/AdminUser"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /admin/users/{id}/activate:
    post:
      operationId: activateUserLicense
      tags: [admin]
      summary: Force-activate user license
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LicenseActivation"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AdminUser"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /admin/users/{id}/revoke:
    post:
      operationId: revokeUserLicense
      tags: [admin]
      summary: Revoke user license
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AdminUser"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /admin/users/{id}:
    delete:
      operationId: deleteAdminUser
      tags: [admin]
      summary: Delete a user
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "204":
          description: Deleted
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /admin/payments:
    get:
      operationId: getAdminPayments
      tags: [admin]
      summary: Get all payment submissions
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/AdminPayment"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /admin/payments/{id}/approve:
    post:
      operationId: approvePayment
      tags: [admin]
      summary: Approve a payment and activate license
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AdminPayment"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /admin/payments/{id}/deny:
    post:
      operationId: denyPayment
      tags: [admin]
      summary: Deny a payment
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AdminPayment"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

components:
  schemas:
    HealthStatus:
      type: object
      required: [status]
      properties:
        status:
          type: string

    ErrorResponse:
      type: object
      required: [error]
      properties:
        error:
          type: string

    MessageResponse:
      type: object
      required: [message]
      properties:
        message:
          type: string

    UserRegistration:
      type: object
      required: [username, email, password]
      properties:
        username:
          type: string
          minLength: 3
        email:
          type: string
        password:
          type: string
          minLength: 8

    UserLogin:
      type: object
      required: [email, password]
      properties:
        email:
          type: string
        password:
          type: string

    AuthResponse:
      type: object
      required: [user, token]
      properties:
        user:
          $ref: "#/components/schemas/UserProfile"
        token:
          type: string

    UserProfile:
      type: object
      required: [id, username, email, licenseStatus, createdAt]
      properties:
        id:
          type: integer
        username:
          type: string
        email:
          type: string
        licenseStatus:
          type: string
        licenseExpiresAt:
          type: ["string", "null"]
        createdAt:
          type: string

    ForgotPasswordRequest:
      type: object
      required: [email]
      properties:
        email:
          type: string

    PasswordReset:
      type: object
      required: [token, password]
      properties:
        token:
          type: string
        password:
          type: string
          minLength: 8

    PublicSettings:
      type: object
      required: [walletAddress, btcWalletAddress, ltcWalletAddress, ethWalletAddress, monthlyPrice, biannualPrice, yearlyPrice, lifetimePrice, telegramSupport, telegramChannel]
      properties:
        walletAddress:
          type: string
        btcWalletAddress:
          type: string
        ltcWalletAddress:
          type: string
        ethWalletAddress:
          type: string
        monthlyPrice:
          type: string
        biannualPrice:
          type: string
        yearlyPrice:
          type: string
        lifetimePrice:
          type: string
        telegramSupport:
          type: string
        telegramChannel:
          type: string

    UserCredentials:
      type: object
      required: [hasApiKey, sendingDomain, provider]
      properties:
        hasApiKey:
          type: boolean
        sendingDomain:
          type: ["string", "null"]
        provider:
          type: ["string", "null"]

    CredentialUpdate:
      type: object
      required: [apiKey, sendingDomain, provider]
      properties:
        apiKey:
          type: string
        sendingDomain:
          type: string
        provider:
          type: string

    BulkEmailSend:
      type: object
      required: [fromName, subject, recipients, body]
      properties:
        fromName:
          type: string
        senderEmail:
          type: ["string", "null"]
        replyTo:
          type: ["string", "null"]
        subject:
          type: string
        recipients:
          type: array
          items:
            type: string
        body:
          type: string
        isHtml:
          type: boolean
        batchDelay:
          type: integer

    SendResult:
      type: object
      required: [sent, failed, total]
      properties:
        sent:
          type: integer
        failed:
          type: integer
        total:
          type: integer
        logs:
          type: array
          items:
            $ref: "#/components/schemas/SendLogEntry"

    SendLogEntry:
      type: object
      required: [recipient, status]
      properties:
        recipient:
          type: string
        status:
          type: string
        provider:
          type: ["string", "null"]
        error:
          type: ["string", "null"]

    EmailLog:
      type: object
      required: [id, recipient, subject, status, provider, createdAt]
      properties:
        id:
          type: integer
        recipient:
          type: string
        subject:
          type: string
        status:
          type: string
        provider:
          type: ["string", "null"]
        error:
          type: ["string", "null"]
        createdAt:
          type: string

    PaymentSubmission:
      type: object
      required: [tier, txHash, coin]
      properties:
        tier:
          type: string
        coin:
          type: string
        txHash:
          type: string
        amount:
          type: ["string", "null"]

    PaymentRecord:
      type: object
      required: [id, tier, coin, txHash, status, createdAt]
      properties:
        id:
          type: integer
        tier:
          type: string
        coin:
          type: string
        txHash:
          type: string
        status:
          type: string
        amount:
          type: ["string", "null"]
        createdAt:
          type: string

    AdminCredentials:
      type: object
      required: [password]
      properties:
        password:
          type: string

    AdminAuthResponse:
      type: object
      required: [token]
      properties:
        token:
          type: string

    AdminSettings:
      type: object
      required: [walletAddress, btcWalletAddress, ltcWalletAddress, ethWalletAddress, monthlyPrice, biannualPrice, yearlyPrice, lifetimePrice, telegramSupport, telegramChannel, telegramBotToken, adminChatId, platformResendKey, platformEmailFrom, siteUrl]
      properties:
        walletAddress:
          type: string
        btcWalletAddress:
          type: string
        ltcWalletAddress:
          type: string
        ethWalletAddress:
          type: string
        monthlyPrice:
          type: string
        biannualPrice:
          type: string
        yearlyPrice:
          type: string
        lifetimePrice:
          type: string
        telegramSupport:
          type: string
        telegramChannel:
          type: string
        telegramBotToken:
          type: string
        adminChatId:
          type: string
        platformResendKey:
          type: string
        platformEmailFrom:
          type: string
        siteUrl:
          type: string

    SettingsUpdate:
      type: object
      properties:
        walletAddress:
          type: string
        btcWalletAddress:
          type: string
        ltcWalletAddress:
          type: string
        ethWalletAddress:
          type: string
        monthlyPrice:
          type: string
        biannualPrice:
          type: string
        yearlyPrice:
          type: string
        lifetimePrice:
          type: string
        telegramSupport:
          type: string
        telegramChannel:
          type: string
        telegramBotToken:
          type: string
        adminChatId:
          type: string
        platformResendKey:
          type: string
        platformEmailFrom:
          type: string
        siteUrl:
          type: string

    AdminUser:
      type: object
      required: [id, username, email, licenseStatus, createdAt]
      properties:
        id:
          type: integer
        username:
          type: string
        email:
          type: string
        licenseStatus:
          type: string
        licenseExpiresAt:
          type: ["string", "null"]
        createdAt:
          type: string

    LicenseActivation:
      type: object
      required: [tier]
      properties:
        tier:
          type: string

    AdminPayment:
      type: object
      required: [id, userId, username, email, tier, coin, txHash, status, createdAt]
      properties:
        id:
          type: integer
        userId:
          type: integer
        username:
          type: string
        email:
          type: string
        tier:
          type: string
        coin:
          type: string
        txHash:
          type: string
        status:
          type: string
        amount:
          type: ["string", "null"]
        createdAt:
          type: string
