{
  "openapi": "3.0.3",
  "info": {
    "title": "NoackHub API",
    "version": "v2",
    "description": "REST API för NoackHub — git hosting + CI inom No Ack Infrastruktur AB.\n\nAlla endpoints kräver `Authorization: token <api-token>` förutom `/auth/login`.\nHämta token via POST /auth/login eller skapa via Inställningar → API-tokens (kommer).",
    "contact": {"name": "No-Ack", "url": "https://noackhosting.se"}
  },
  "servers": [
    {"url": "https://noackhub.noackhosting.net/api/v2", "description": "Produktion"},
    {"url": "https://dev.noackhub.noackhosting.net/api/v2", "description": "Dev"}
  ],
  "components": {
    "securitySchemes": {
      "tokenAuth": {"type": "apiKey", "in": "header", "name": "Authorization", "description": "Format: token <din-api-token>"}
    },
    "schemas": {
      "User": {"type": "object", "properties": {"id": {"type": "integer"}, "login": {"type": "string"}, "email": {"type": "string"}, "full_name": {"type": "string"}, "is_admin": {"type": "boolean"}}},
      "Repo": {"type": "object", "properties": {"id": {"type": "integer"}, "name": {"type": "string"}, "full_name": {"type": "string"}, "description": {"type": "string"}, "private": {"type": "boolean"}, "clone_url": {"type": "string"}, "ssh_url": {"type": "string"}, "default_branch": {"type": "string"}}},
      "Issue": {"type": "object", "properties": {"id": {"type": "integer"}, "number": {"type": "integer"}, "title": {"type": "string"}, "body": {"type": "string"}, "state": {"type": "string"}}},
      "Error": {"type": "object", "properties": {"error": {"type": "string"}}}
    }
  },
  "security": [{"tokenAuth": []}],
  "paths": {
    "/auth/login": {"post": {"summary": "Logga in", "security": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "required": ["username","password"], "properties": {"username": {"type":"string"}, "password": {"type":"string"}}}}}}, "responses": {"200": {"description": "OK med token + user"}, "401": {"description": "Fel credentials"}}}},
    "/me": {"get": {"summary": "Egen användar-info", "responses": {"200": {"description": "User", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/User"}}}}}}},
    "/me/state": {"get": {"summary": "Onboarding-status (2FA, SSH-key)", "responses": {"200": {"description": "UserState"}}}},
    "/me/keys": {
      "get": {"summary": "Lista SSH-nycklar", "responses": {"200": {"description": "[]SSHKey"}}},
      "post": {"summary": "Lägg till SSH-nyckel", "requestBody": {"required": true, "content": {"application/json": {"schema": {"type":"object", "required":["title","key"], "properties":{"title":{"type":"string"},"key":{"type":"string"}}}}}}, "responses": {"201": {"description": "Skapad"}}}
    },
    "/me/keys/{id}": {"delete": {"summary": "Ta bort SSH-nyckel", "parameters": [{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}], "responses": {"200": {"description":"OK"}}}},
    "/me/profile": {"patch": {"summary": "Uppdatera profil", "requestBody": {"required": true, "content": {"application/json": {"schema": {"type":"object","properties":{"full_name":{"type":"string"},"email":{"type":"string"},"location":{"type":"string"},"website":{"type":"string"},"description":{"type":"string"}}}}}}, "responses": {"200":{"description":"OK"}}}},
    "/me/password": {"post": {"summary": "Byt lösenord", "requestBody": {"required":true, "content":{"application/json":{"schema":{"type":"object","required":["old_password","new_password"],"properties":{"old_password":{"type":"string"},"new_password":{"type":"string","minLength":12}}}}}}, "responses": {"200":{"description":"OK"}}}},
    "/me/2fa/setup": {"post": {"summary": "Generera 2FA-secret (returnerar QR base64)", "responses": {"200":{"description":"OK med secret + qr_base64"}}}},
    "/me/2fa/confirm": {"post": {"summary": "Bekräfta 2FA-enrollment med TOTP-kod", "requestBody": {"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code"],"properties":{"code":{"type":"string"}}}}}}, "responses": {"200":{"description":"OK"}}}},
    "/me/2fa/verify": {"post": {"summary": "Verifiera TOTP-kod vid login", "responses": {"200":{"description":"OK"}}}},
    "/me/2fa/disable": {"post": {"summary": "Stäng av 2FA (kräver TOTP-kod)", "responses": {"200":{"description":"OK"}}}},
    "/repos": {
      "get": {"summary": "Lista mina repos", "responses": {"200":{"description":"[]Repo"}}},
      "post": {"summary": "Skapa repo", "requestBody": {"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"private":{"type":"boolean"},"auto_init":{"type":"boolean"}}}}}}, "responses":{"201":{"description":"Skapad"}}}
    },
    "/repos/{owner}/{repo}": {
      "get": {"summary": "Hämta repo", "parameters": [{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}}], "responses":{"200":{"description":"Repo"}}},
      "patch": {"summary": "Redigera repo", "parameters": [{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}}], "responses":{"200":{"description":"OK"}}},
      "delete": {"summary": "Radera repo", "parameters": [{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}}], "responses":{"200":{"description":"OK"}}}
    },
    "/repos/{owner}/{repo}/contents/{path}": {"get": {"summary": "Lista filer eller hämta fil-innehåll", "parameters": [{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}},{"in":"path","name":"path","required":true,"schema":{"type":"string"}}], "responses":{"200":{"description":"[]File eller File"}}}},
    "/repos/{owner}/{repo}/commits": {"get": {"summary": "Senaste commits", "parameters": [{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}}], "responses":{"200":{"description":"[]Commit"}}}},
    "/repos/{owner}/{repo}/issues": {
      "get": {"summary": "Lista issues", "parameters": [{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}},{"in":"query","name":"state","schema":{"type":"string","enum":["open","closed","all"]}}], "responses":{"200":{"description":"[]Issue"}}},
      "post": {"summary": "Skapa issue", "parameters": [{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}}], "requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title"],"properties":{"title":{"type":"string"},"body":{"type":"string"}}}}}}, "responses":{"201":{"description":"Skapad"}}}
    },
    "/repos/{owner}/{repo}/issues/{number}": {"get": {"summary": "Hämta issue + kommentarer", "parameters": [{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}},{"in":"path","name":"number","required":true,"schema":{"type":"integer"}}], "responses":{"200":{"description":"Issue + comments"}}}},
    "/repos/{owner}/{repo}/issues/{number}/comments": {"post": {"summary": "Kommentera issue", "parameters": [{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}},{"in":"path","name":"number","required":true,"schema":{"type":"integer"}}], "responses":{"201":{"description":"Skapad"}}}},
    "/repos/{owner}/{repo}/issues/{number}/state": {"post": {"summary": "Stäng/öppna issue", "parameters": [{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}},{"in":"path","name":"number","required":true,"schema":{"type":"integer"}}], "responses":{"200":{"description":"OK"}}}},
    "/repos/{owner}/{repo}/pulls": {"get": {"summary": "Lista pull requests", "parameters":[{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}}], "responses":{"200":{"description":"[]PR"}}}},
    "/repos/{owner}/{repo}/pulls/{number}": {"get": {"summary": "Hämta PR-detalj", "parameters":[{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}},{"in":"path","name":"number","required":true,"schema":{"type":"integer"}}], "responses":{"200":{"description":"PR"}}}},
    "/repos/{owner}/{repo}/releases": {"get": {"summary": "Lista releases","parameters":[{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}}], "responses":{"200":{"description":"[]Release"}}}},
    "/repos/{owner}/{repo}/actions/runs": {"get": {"summary": "Lista workflow runs","parameters":[{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}}], "responses":{"200":{"description":"[]WorkflowRun"}}}},
    "/repos/{owner}/{repo}/actions/runs/{id}": {"get": {"summary": "Workflow run-detalj","parameters":[{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}},{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}], "responses":{"200":{"description":"WorkflowRun"}}}},
    "/repos/{owner}/{repo}/actions/runs/{id}/jobs": {"get": {"summary": "Jobs i en run","parameters":[{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}},{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}], "responses":{"200":{"description":"[]Job"}}}},
    "/repos/{owner}/{repo}/actions/jobs/{id}/logs": {"get": {"summary": "Plain text-loggar för ett job","parameters":[{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}},{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}], "responses":{"200":{"description":"text/plain"}}}},
    "/repos/{owner}/{repo}/collaborators": {
      "get": {"summary": "Lista collaborators","parameters":[{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}}], "responses":{"200":{"description":"[]User"}}},
      "post": {"summary": "Lägg till collaborator","parameters":[{"in":"path","name":"owner","required":true,"schema":{"type":"string"}},{"in":"path","name":"repo","required":true,"schema":{"type":"string"}}], "requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["username"],"properties":{"username":{"type":"string"},"permission":{"type":"string","enum":["read","write","admin"]}}}}}}, "responses":{"201":{"description":"OK"}}}
    },
    "/admin/invites": {
      "get": {"summary": "Lista invites (admin)","responses":{"200":{"description":"[]Invite"}}},
      "post": {"summary": "Skapa invite (admin)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["username","email"],"properties":{"username":{"type":"string"},"email":{"type":"string"},"fullName":{"type":"string"},"role":{"type":"string","enum":["user","customer_admin","site_admin"]}}}}}}, "responses":{"201":{"description":"Skapad"}}}
    },
    "/admin/users": {"get": {"summary": "Lista alla användare (admin)","responses":{"200":{"description":"[]User"}}}},
    "/admin/users/{user}/disable": {"post": {"summary": "Inaktivera användare","parameters":[{"in":"path","name":"user","required":true,"schema":{"type":"string"}}], "responses":{"200":{"description":"OK"}}}},
    "/admin/users/{user}/enable": {"post": {"summary": "Aktivera användare","parameters":[{"in":"path","name":"user","required":true,"schema":{"type":"string"}}], "responses":{"200":{"description":"OK"}}}},
    "/admin/users/{user}": {"delete": {"summary": "Radera användare","parameters":[{"in":"path","name":"user","required":true,"schema":{"type":"string"}}], "responses":{"200":{"description":"OK"}}}}
  }
}
