openapi: 3.0.3
info:
  title: Sirius Scrape API
  version: "1.0.0"
  description: >
    HTTP API micro-SaaS untuk scraping & data-extraction: metadata halaman,
    ekstraksi via CSS-selector, dan fetch JSON API dengan JSONPath-lite.
    Auth via header x-api-key, rate-limit per key, mitigasi anti-SSRF.
servers:
  - url: https://api.siriusx.id
    description: Produksi (ganti dengan domain Anda)
  - url: http://localhost:8787
    description: Lokal / dev
security:
  - ApiKeyAuth: []
tags:
  - name: system
  - name: scrape
paths:
  /health:
    get:
      tags: [system]
      summary: Health check (publik, tanpa auth)
      security: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean, example: true }
                  service: { type: string, example: api-server }
                  endpoints:
                    type: array
                    items: { type: string }
  /v1/scrape:
    post:
      tags: [scrape]
      summary: Ambil metadata terstruktur dari sebuah URL
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url: { type: string, format: uri, example: "https://example.com" }
      responses:
        "200":
          description: Sukses
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  data:
                    type: object
                    properties:
                      status: { type: integer, example: 200 }
                      metadata:
                        type: object
                        properties:
                          title: { type: string, nullable: true }
                          description: { type: string, nullable: true }
                          canonical: { type: string, nullable: true }
                          og: { type: object, additionalProperties: { type: string } }
                          h1: { type: array, items: { type: string } }
                          h2: { type: array, items: { type: string } }
                          h3: { type: array, items: { type: string } }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "502": { $ref: "#/components/responses/UpstreamError" }
  /v1/extract:
    post:
      tags: [scrape]
      summary: Ekstrak elemen dari HTML via CSS-selector ringan
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [selector]
              properties:
                selector: { type: string, example: "a.titlelink" }
                url: { type: string, format: uri }
                html: { type: string }
                limit: { type: integer, example: 10 }
      responses:
        "200":
          description: Sukses
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  data:
                    type: object
                    properties:
                      count: { type: integer }
                      items:
                        type: array
                        items:
                          type: object
                          properties:
                            tag: { type: string }
                            text: { type: string }
                            attrs: { type: object, additionalProperties: { type: string } }
                            html: { type: string }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "502": { $ref: "#/components/responses/UpstreamError" }
  /v1/json:
    post:
      tags: [scrape]
      summary: Fetch JSON API eksternal + pilih bagian via JSONPath-lite
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url: { type: string, format: uri }
                method: { type: string, example: GET }
                headers: { type: object, additionalProperties: { type: string } }
                body: {}
                path: { type: string, example: "stargazers_count" }
      responses:
        "200":
          description: Sukses
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  data:
                    type: object
                    properties:
                      status: { type: integer }
                      result: {}
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "502": { $ref: "#/components/responses/UpstreamError" }
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
  responses:
    BadRequest:
      description: Request tidak valid
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    Unauthorized:
      description: API key hilang/salah
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    RateLimited:
      description: Rate limit terlampaui
      headers:
        retry-after:
          schema: { type: integer }
      content:
        application/json:
          schema:
            allOf:
              - $ref: "#/components/schemas/Error"
              - type: object
                properties:
                  retryAfterSec: { type: integer }
    UpstreamError:
      description: Error upstream / diblokir (SSRF)
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
  schemas:
    Error:
      type: object
      properties:
        ok: { type: boolean, example: false }
        error: { type: string }
