From 3ca77c097d88a732b3f8f949fdb3b3a089a7700f Mon Sep 17 00:00:00 2001 From: allanice001 Date: Mon, 3 Nov 2025 19:07:15 +0000 Subject: [PATCH] feat: add labels to terraform provider --- docs/docs.go | 2 +- docs/swagger.json | 2 +- docs/swagger.yaml | 6 + internal/models/node_pool.go | 2 +- sdk/go/api/openapi.yaml | 9 + sdk/go/docs/DtoLabelResponse.md | 78 ++++++ sdk/go/model_dto_label_response.go | 114 ++++++++- sdk/ts/docs/DtoLabelResponse.md | 16 +- sdk/ts/src/models/DtoLabelResponse.ts | 25 ++ .../internal/provider/datasource_labels.go | 104 ++++++++ .../internal/provider/provider.go | 2 + .../internal/provider/resource_label.go | 235 ++++++++++++++++++ terraform/envs/dev/terraform.tfvars | 3 +- terraform/envs/dev/variables.tf | 1 + terraform/modules/taints/main.tf | 4 + terraform/modules/taints/outputs.tf | 4 + terraform/modules/taints/variables.tf | 11 + terraform/modules/taints/versions.tf | 9 + ui/src/sdk/docs/DtoLabelResponse.md | 6 + ui/src/sdk/models/DtoLabelResponse.ts | 24 ++ 20 files changed, 644 insertions(+), 13 deletions(-) create mode 100644 terraform-provider-autoglue/internal/provider/datasource_labels.go create mode 100644 terraform-provider-autoglue/internal/provider/resource_label.go create mode 100644 terraform/modules/taints/main.tf create mode 100644 terraform/modules/taints/outputs.tf create mode 100644 terraform/modules/taints/variables.tf create mode 100644 terraform/modules/taints/versions.tf diff --git a/docs/docs.go b/docs/docs.go index 7f96a50..f6254d5 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -5,7 +5,7 @@ package docs import "github.com/swaggo/swag/v2" const docTemplate = `{ - "schemes": {{ marshal .Schemes }},"swagger":"2.0","info":{"description":"{{escape .Description}}","title":"{{.Title}}","contact":{"name":"GlueOps"},"version":"{{.Version}}"},"host":"{{.Host}}","basePath":"{{.BasePath}}","paths":{"/.well-known/jwks.json":{"get":{"description":"Returns the JSON Web Key Set for token verification","produces":["application/json"],"tags":["Auth"],"summary":"Get JWKS","operationId":"getJWKS","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.JWKS"}}}}},"/auth/logout":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Revoke refresh token family (logout everywhere)","operationId":"Logout","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.LogoutRequest"}}],"responses":{"204":{"description":"No Content"}}}},"/auth/refresh":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Rotate refresh token","operationId":"Refresh","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.RefreshRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/callback":{"get":{"produces":["application/json"],"tags":["Auth"],"summary":"Handle social login callback","operationId":"AuthCallback","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/start":{"post":{"description":"Returns provider authorization URL for the frontend to redirect","produces":["application/json"],"tags":["Auth"],"summary":"Begin social login","operationId":"AuthStart","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.AuthStartResponse"}}}}},"/labels":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns node labels for the organization in X-Org-ID. Filters: ` + "`" + `key` + "`" + `, ` + "`" + `value` + "`" + `, and ` + "`" + `q` + "`" + ` (key contains). Add ` + "`" + `include=node_pools` + "`" + ` to include linked node groups.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"List node labels (org scoped)","operationId":"ListLabels","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Exact key","name":"key","in":"query"},{"type":"string","description":"Exact value","name":"value","in":"query"},{"type":"string","description":"Key contains (case-insensitive)","name":"q","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.LabelResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list node taints","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Create label (org scoped)","operationId":"CreateLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Label payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateLabelRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid json / missing fields / invalid node_pool_ids","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/labels/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns one label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Get label by ID (org scoped)","operationId":"GetLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Delete label (org scoped)","operationId":"DeleteLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update label fields.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Update label (org scoped)","operationId":"UpdateLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateLabelRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid id / invalid json","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}},"/me":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me"],"summary":"Get current user profile","operationId":"GetMe","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.meResponse"}}}},"patch":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Me"],"summary":"Update current user profile","operationId":"UpdateMe","parameters":[{"description":"Patch profile","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.updateMeRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.User"}}}}},"/me/api-keys":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"List my API keys","operationId":"ListUserAPIKeys","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"post":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"description":"Returns the plaintext key once. Store it securely on the client side.","consumes":["application/json"],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Create a new user API key","operationId":"CreateUserAPIKey","parameters":[{"description":"Key options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.createUserKeyRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"/me/api-keys/{id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Delete a user API key","operationId":"DeleteUserAPIKey","parameters":[{"type":"string","description":"Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content"}}}},"/orgs":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List organizations I belong to","operationId":"listMyOrgs","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create organization","operationId":"createOrg","parameters":[{"description":"Org payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/models.Organization"}},"400":{"description":"Bad Request","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"409":{"description":"Conflict","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Get organization","operationId":"getOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete organization (owner)","operationId":"deleteOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"patch":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Update organization (owner/admin)","operationId":"updateOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Update payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgUpdateReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List org-scoped API keys (no secrets)","operationId":"listOrgKeys","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.APIKey"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create org key/secret pair (owner/admin)","operationId":"createOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Key name + optional expiry","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgKeyCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.orgKeyCreateResp"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys/{key_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete org key (owner/admin)","operationId":"deleteOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"Key ID (UUID)","name":"key_id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List members in org","operationId":"listMembers","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.memberOut"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Add or update a member (owner/admin)","operationId":"addOrUpdateMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"User \u0026 role","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.memberUpsertReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.memberOut"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members/{user_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Remove a member (owner/admin)","operationId":"removeMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"User ID (UUID)","name":"user_id","in":"path","required":true}],"responses":{"204":{"description":"Removed"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/servers":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns servers for the organization in X-Org-ID. Optional filters: status, role.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"List servers (org scoped)","operationId":"ListServers","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Filter by status (pending|provisioning|ready|failed)","name":"status","in":"query"},{"type":"string","description":"Filter by role","name":"role","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.ServerResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list servers","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a server bound to the org in X-Org-ID. Validates that ssh_key_id belongs to the org.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Create server (org scoped)","operationId":"CreateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Server payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateServerRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid json / missing fields / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/servers/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns one server in the given organization.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Get server by ID (org scoped)","operationId":"GetServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the server.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Delete server (org scoped)","operationId":"DeleteServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update fields; changing ssh_key_id validates ownership.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Update server (org scoped)","operationId":"UpdateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateServerRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id / invalid json / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}},"/ssh":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns ssh keys for the organization in X-Org-ID.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"List ssh keys (org scoped)","operationId":"ListPublicSshKeys","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.SshResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list keys","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Generates an RSA or ED25519 keypair, saves it, and returns metadata. For RSA you may set bits (2048/3072/4096). Default is 4096. ED25519 ignores bits.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Create ssh keypair (org scoped)","operationId":"CreateSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Key generation options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateSSHRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.SshResponse"}},"400":{"description":"invalid json / invalid bits","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"generation/create failed","schema":{"type":"string"}}}}},"/ssh/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns public key fields. Append ` + "`" + `?reveal=true` + "`" + ` to include the private key PEM.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Get ssh key by ID (org scoped)","operationId":"GetSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"type":"boolean","description":"Reveal private key PEM","name":"reveal","in":"query"}],"responses":{"200":{"description":"When reveal=true","schema":{"$ref":"#/definitions/dto.SshRevealResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes a keypair.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Delete ssh keypair (org scoped)","operationId":"DeleteSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}}},"/ssh/{id}/download":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Download ` + "`" + `part=public|private|both` + "`" + ` of the keypair. ` + "`" + `both` + "`" + ` returns a zip file.","produces":["application/json"],"tags":["Ssh"],"summary":"Download ssh key files by ID (org scoped)","operationId":"DownloadSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header","required":true},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"enum":["public","private","both"],"type":"string","description":"Which part to download","name":"part","in":"query","required":true}],"responses":{"200":{"description":"file content","schema":{"type":"string"}},"400":{"description":"invalid id / invalid part","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"download failed","schema":{"type":"string"}}}}},"/taints":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns node taints for the organization in X-Org-ID. Filters: ` + "`" + `key` + "`" + `, ` + "`" + `value` + "`" + `, and ` + "`" + `q` + "`" + ` (key contains). Add ` + "`" + `include=node_pools` + "`" + ` to include linked node pools.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"List node pool taints (org scoped)","operationId":"ListTaints","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Exact key","name":"key","in":"query"},{"type":"string","description":"Exact value","name":"value","in":"query"},{"type":"string","description":"key contains (case-insensitive)","name":"q","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.TaintResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list node taints","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Create node taint (org scoped)","operationId":"CreateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Taint payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateTaintRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid json / missing fields / invalid node_pool_ids","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/taints/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Get node taint by ID (org scoped)","operationId":"GetTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Delete taint (org scoped)","operationId":"DeleteTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update taint fields.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Update node taint (org scoped)","operationId":"UpdateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateTaintRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id / invalid json","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}}},"definitions":{"dto.AuthStartResponse":{"type":"object","properties":{"auth_url":{"type":"string","example":"https://accounts.google.com/o/oauth2/v2/auth?client_id=..."}}},"dto.CreateLabelRequest":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"dto.CreateSSHRequest":{"type":"object","properties":{"bits":{"description":"Only for RSA","type":"integer"},"comment":{"type":"string","example":"deploy@autoglue"},"name":{"type":"string"},"type":{"description":"\"rsa\" (default) or \"ed25519\"","type":"string"}}},"dto.CreateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.CreateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.JWK":{"type":"object","properties":{"alg":{"type":"string","example":"RS256"},"e":{"type":"string","example":"AQAB"},"kid":{"type":"string","example":"7c6f1d0a-7a98-4e6a-9dbf-6b1af4b9f345"},"kty":{"type":"string","example":"RSA"},"n":{"type":"string"},"use":{"type":"string","example":"sig"},"x":{"type":"string"}}},"dto.JWKS":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/definitions/dto.JWK"}}}},"dto.LabelResponse":{"type":"object","properties":{"id":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.LogoutRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.RefreshRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.ServerResponse":{"type":"object","properties":{"created_at":{"type":"string"},"hostname":{"type":"string"},"id":{"type":"string"},"organization_id":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshRevealResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"private_key":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.TaintResponse":{"type":"object","properties":{"created_at":{"type":"string"},"effect":{"type":"string"},"id":{"type":"string"},"key":{"type":"string"},"organization_id":{"type":"string"},"updated_at":{"type":"string"},"value":{"type":"string"}}},"dto.TokenPair":{"type":"object","properties":{"access_token":{"type":"string","example":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ij..."},"expires_in":{"type":"integer","example":3600},"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf...."},"token_type":{"type":"string","example":"Bearer"}}},"dto.UpdateLabelRequest":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"dto.UpdateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.UpdateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"handlers.createUserKeyRequest":{"type":"object","properties":{"expires_in_hours":{"description":"optional TTL","type":"integer"},"name":{"type":"string"}}},"handlers.meResponse":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"emails":{"type":"array","items":{"$ref":"#/definitions/models.UserEmail"}},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"organizations":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"handlers.memberOut":{"type":"object","properties":{"email":{"type":"string"},"role":{"description":"owner/admin/member","type":"string"},"user_id":{"type":"string","format":"uuid"}}},"handlers.memberUpsertReq":{"type":"object","properties":{"role":{"type":"string","example":"member"},"user_id":{"type":"string","format":"uuid"}}},"handlers.orgCreateReq":{"type":"object","properties":{"domain":{"type":"string","example":"acme.com"},"name":{"type":"string","example":"Acme Corp"}}},"handlers.orgKeyCreateReq":{"type":"object","properties":{"expires_in_hours":{"type":"integer","example":720},"name":{"type":"string","example":"automation-bot"}}},"handlers.orgKeyCreateResp":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"org_key":{"description":"shown once:","type":"string"},"org_secret":{"description":"shown once:","type":"string"},"scope":{"description":"\"org\"","type":"string"}}},"handlers.orgUpdateReq":{"type":"object","properties":{"domain":{"type":"string"},"name":{"type":"string"}}},"handlers.updateMeRequest":{"type":"object","properties":{"display_name":{"type":"string"}}},"handlers.userAPIKeyOut":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string"},"name":{"type":"string"},"plain":{"description":"Shown only on create:","type":"string"},"scope":{"description":"\"user\"","type":"string"}}},"models.APIKey":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string","format":"date-time"},"name":{"type":"string"},"org_id":{"type":"string","format":"uuid"},"prefix":{"type":"string"},"revoked":{"type":"boolean"},"scope":{"type":"string"},"updated_at":{"type":"string","format":"date-time"},"user_id":{"type":"string","format":"uuid"}}},"models.Organization":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"domain":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"name":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.User":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.UserEmail":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"email":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_primary":{"type":"boolean"},"is_verified":{"type":"boolean"},"updated_at":{"type":"string","format":"date-time"},"user":{"$ref":"#/definitions/models.User"},"user_id":{"type":"string","format":"uuid"}}},"utils.ErrorResponse":{"type":"object","properties":{"code":{"description":"A machine-readable error code, e.g. \"validation_error\"\nexample: validation_error","type":"string"},"message":{"description":"Human-readable message\nexample: slug is required","type":"string"}}}},"securityDefinitions":{"ApiKeyAuth":{"description":"User API key","type":"apiKey","name":"X-API-KEY","in":"header"},"BearerAuth":{"description":"Bearer token authentication","type":"apiKey","name":"Authorization","in":"header"},"OrgKeyAuth":{"description":"Org-level key/secret authentication","type":"apiKey","name":"X-ORG-KEY","in":"header"},"OrgSecretAuth":{"description":"Org-level secret","type":"apiKey","name":"X-ORG-SECRET","in":"header"}}}` + "schemes": {{ marshal .Schemes }},"swagger":"2.0","info":{"description":"{{escape .Description}}","title":"{{.Title}}","contact":{"name":"GlueOps"},"version":"{{.Version}}"},"host":"{{.Host}}","basePath":"{{.BasePath}}","paths":{"/.well-known/jwks.json":{"get":{"description":"Returns the JSON Web Key Set for token verification","produces":["application/json"],"tags":["Auth"],"summary":"Get JWKS","operationId":"getJWKS","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.JWKS"}}}}},"/auth/logout":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Revoke refresh token family (logout everywhere)","operationId":"Logout","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.LogoutRequest"}}],"responses":{"204":{"description":"No Content"}}}},"/auth/refresh":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Rotate refresh token","operationId":"Refresh","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.RefreshRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/callback":{"get":{"produces":["application/json"],"tags":["Auth"],"summary":"Handle social login callback","operationId":"AuthCallback","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/start":{"post":{"description":"Returns provider authorization URL for the frontend to redirect","produces":["application/json"],"tags":["Auth"],"summary":"Begin social login","operationId":"AuthStart","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.AuthStartResponse"}}}}},"/labels":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns node labels for the organization in X-Org-ID. Filters: ` + "`" + `key` + "`" + `, ` + "`" + `value` + "`" + `, and ` + "`" + `q` + "`" + ` (key contains). Add ` + "`" + `include=node_pools` + "`" + ` to include linked node groups.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"List node labels (org scoped)","operationId":"ListLabels","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Exact key","name":"key","in":"query"},{"type":"string","description":"Exact value","name":"value","in":"query"},{"type":"string","description":"Key contains (case-insensitive)","name":"q","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.LabelResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list node taints","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Create label (org scoped)","operationId":"CreateLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Label payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateLabelRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid json / missing fields / invalid node_pool_ids","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/labels/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns one label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Get label by ID (org scoped)","operationId":"GetLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Delete label (org scoped)","operationId":"DeleteLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update label fields.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Update label (org scoped)","operationId":"UpdateLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateLabelRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid id / invalid json","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}},"/me":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me"],"summary":"Get current user profile","operationId":"GetMe","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.meResponse"}}}},"patch":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Me"],"summary":"Update current user profile","operationId":"UpdateMe","parameters":[{"description":"Patch profile","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.updateMeRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.User"}}}}},"/me/api-keys":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"List my API keys","operationId":"ListUserAPIKeys","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"post":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"description":"Returns the plaintext key once. Store it securely on the client side.","consumes":["application/json"],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Create a new user API key","operationId":"CreateUserAPIKey","parameters":[{"description":"Key options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.createUserKeyRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"/me/api-keys/{id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Delete a user API key","operationId":"DeleteUserAPIKey","parameters":[{"type":"string","description":"Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content"}}}},"/orgs":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List organizations I belong to","operationId":"listMyOrgs","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create organization","operationId":"createOrg","parameters":[{"description":"Org payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/models.Organization"}},"400":{"description":"Bad Request","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"409":{"description":"Conflict","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Get organization","operationId":"getOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete organization (owner)","operationId":"deleteOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"patch":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Update organization (owner/admin)","operationId":"updateOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Update payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgUpdateReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List org-scoped API keys (no secrets)","operationId":"listOrgKeys","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.APIKey"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create org key/secret pair (owner/admin)","operationId":"createOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Key name + optional expiry","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgKeyCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.orgKeyCreateResp"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys/{key_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete org key (owner/admin)","operationId":"deleteOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"Key ID (UUID)","name":"key_id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List members in org","operationId":"listMembers","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.memberOut"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Add or update a member (owner/admin)","operationId":"addOrUpdateMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"User \u0026 role","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.memberUpsertReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.memberOut"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members/{user_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Remove a member (owner/admin)","operationId":"removeMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"User ID (UUID)","name":"user_id","in":"path","required":true}],"responses":{"204":{"description":"Removed"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/servers":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns servers for the organization in X-Org-ID. Optional filters: status, role.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"List servers (org scoped)","operationId":"ListServers","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Filter by status (pending|provisioning|ready|failed)","name":"status","in":"query"},{"type":"string","description":"Filter by role","name":"role","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.ServerResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list servers","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a server bound to the org in X-Org-ID. Validates that ssh_key_id belongs to the org.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Create server (org scoped)","operationId":"CreateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Server payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateServerRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid json / missing fields / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/servers/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns one server in the given organization.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Get server by ID (org scoped)","operationId":"GetServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the server.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Delete server (org scoped)","operationId":"DeleteServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update fields; changing ssh_key_id validates ownership.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Update server (org scoped)","operationId":"UpdateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateServerRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id / invalid json / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}},"/ssh":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns ssh keys for the organization in X-Org-ID.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"List ssh keys (org scoped)","operationId":"ListPublicSshKeys","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.SshResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list keys","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Generates an RSA or ED25519 keypair, saves it, and returns metadata. For RSA you may set bits (2048/3072/4096). Default is 4096. ED25519 ignores bits.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Create ssh keypair (org scoped)","operationId":"CreateSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Key generation options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateSSHRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.SshResponse"}},"400":{"description":"invalid json / invalid bits","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"generation/create failed","schema":{"type":"string"}}}}},"/ssh/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns public key fields. Append ` + "`" + `?reveal=true` + "`" + ` to include the private key PEM.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Get ssh key by ID (org scoped)","operationId":"GetSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"type":"boolean","description":"Reveal private key PEM","name":"reveal","in":"query"}],"responses":{"200":{"description":"When reveal=true","schema":{"$ref":"#/definitions/dto.SshRevealResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes a keypair.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Delete ssh keypair (org scoped)","operationId":"DeleteSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}}},"/ssh/{id}/download":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Download ` + "`" + `part=public|private|both` + "`" + ` of the keypair. ` + "`" + `both` + "`" + ` returns a zip file.","produces":["application/json"],"tags":["Ssh"],"summary":"Download ssh key files by ID (org scoped)","operationId":"DownloadSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header","required":true},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"enum":["public","private","both"],"type":"string","description":"Which part to download","name":"part","in":"query","required":true}],"responses":{"200":{"description":"file content","schema":{"type":"string"}},"400":{"description":"invalid id / invalid part","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"download failed","schema":{"type":"string"}}}}},"/taints":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns node taints for the organization in X-Org-ID. Filters: ` + "`" + `key` + "`" + `, ` + "`" + `value` + "`" + `, and ` + "`" + `q` + "`" + ` (key contains). Add ` + "`" + `include=node_pools` + "`" + ` to include linked node pools.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"List node pool taints (org scoped)","operationId":"ListTaints","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Exact key","name":"key","in":"query"},{"type":"string","description":"Exact value","name":"value","in":"query"},{"type":"string","description":"key contains (case-insensitive)","name":"q","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.TaintResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list node taints","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Create node taint (org scoped)","operationId":"CreateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Taint payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateTaintRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid json / missing fields / invalid node_pool_ids","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/taints/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Get node taint by ID (org scoped)","operationId":"GetTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Delete taint (org scoped)","operationId":"DeleteTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update taint fields.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Update node taint (org scoped)","operationId":"UpdateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateTaintRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id / invalid json","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}}},"definitions":{"dto.AuthStartResponse":{"type":"object","properties":{"auth_url":{"type":"string","example":"https://accounts.google.com/o/oauth2/v2/auth?client_id=..."}}},"dto.CreateLabelRequest":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"dto.CreateSSHRequest":{"type":"object","properties":{"bits":{"description":"Only for RSA","type":"integer"},"comment":{"type":"string","example":"deploy@autoglue"},"name":{"type":"string"},"type":{"description":"\"rsa\" (default) or \"ed25519\"","type":"string"}}},"dto.CreateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.CreateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.JWK":{"type":"object","properties":{"alg":{"type":"string","example":"RS256"},"e":{"type":"string","example":"AQAB"},"kid":{"type":"string","example":"7c6f1d0a-7a98-4e6a-9dbf-6b1af4b9f345"},"kty":{"type":"string","example":"RSA"},"n":{"type":"string"},"use":{"type":"string","example":"sig"},"x":{"type":"string"}}},"dto.JWKS":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/definitions/dto.JWK"}}}},"dto.LabelResponse":{"type":"object","properties":{"created_at":{"type":"string"},"id":{"type":"string"},"key":{"type":"string"},"organization_id":{"type":"string"},"updated_at":{"type":"string"},"value":{"type":"string"}}},"dto.LogoutRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.RefreshRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.ServerResponse":{"type":"object","properties":{"created_at":{"type":"string"},"hostname":{"type":"string"},"id":{"type":"string"},"organization_id":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshRevealResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"private_key":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.TaintResponse":{"type":"object","properties":{"created_at":{"type":"string"},"effect":{"type":"string"},"id":{"type":"string"},"key":{"type":"string"},"organization_id":{"type":"string"},"updated_at":{"type":"string"},"value":{"type":"string"}}},"dto.TokenPair":{"type":"object","properties":{"access_token":{"type":"string","example":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ij..."},"expires_in":{"type":"integer","example":3600},"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf...."},"token_type":{"type":"string","example":"Bearer"}}},"dto.UpdateLabelRequest":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"dto.UpdateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.UpdateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"handlers.createUserKeyRequest":{"type":"object","properties":{"expires_in_hours":{"description":"optional TTL","type":"integer"},"name":{"type":"string"}}},"handlers.meResponse":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"emails":{"type":"array","items":{"$ref":"#/definitions/models.UserEmail"}},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"organizations":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"handlers.memberOut":{"type":"object","properties":{"email":{"type":"string"},"role":{"description":"owner/admin/member","type":"string"},"user_id":{"type":"string","format":"uuid"}}},"handlers.memberUpsertReq":{"type":"object","properties":{"role":{"type":"string","example":"member"},"user_id":{"type":"string","format":"uuid"}}},"handlers.orgCreateReq":{"type":"object","properties":{"domain":{"type":"string","example":"acme.com"},"name":{"type":"string","example":"Acme Corp"}}},"handlers.orgKeyCreateReq":{"type":"object","properties":{"expires_in_hours":{"type":"integer","example":720},"name":{"type":"string","example":"automation-bot"}}},"handlers.orgKeyCreateResp":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"org_key":{"description":"shown once:","type":"string"},"org_secret":{"description":"shown once:","type":"string"},"scope":{"description":"\"org\"","type":"string"}}},"handlers.orgUpdateReq":{"type":"object","properties":{"domain":{"type":"string"},"name":{"type":"string"}}},"handlers.updateMeRequest":{"type":"object","properties":{"display_name":{"type":"string"}}},"handlers.userAPIKeyOut":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string"},"name":{"type":"string"},"plain":{"description":"Shown only on create:","type":"string"},"scope":{"description":"\"user\"","type":"string"}}},"models.APIKey":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string","format":"date-time"},"name":{"type":"string"},"org_id":{"type":"string","format":"uuid"},"prefix":{"type":"string"},"revoked":{"type":"boolean"},"scope":{"type":"string"},"updated_at":{"type":"string","format":"date-time"},"user_id":{"type":"string","format":"uuid"}}},"models.Organization":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"domain":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"name":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.User":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.UserEmail":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"email":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_primary":{"type":"boolean"},"is_verified":{"type":"boolean"},"updated_at":{"type":"string","format":"date-time"},"user":{"$ref":"#/definitions/models.User"},"user_id":{"type":"string","format":"uuid"}}},"utils.ErrorResponse":{"type":"object","properties":{"code":{"description":"A machine-readable error code, e.g. \"validation_error\"\nexample: validation_error","type":"string"},"message":{"description":"Human-readable message\nexample: slug is required","type":"string"}}}},"securityDefinitions":{"ApiKeyAuth":{"description":"User API key","type":"apiKey","name":"X-API-KEY","in":"header"},"BearerAuth":{"description":"Bearer token authentication","type":"apiKey","name":"Authorization","in":"header"},"OrgKeyAuth":{"description":"Org-level key/secret authentication","type":"apiKey","name":"X-ORG-KEY","in":"header"},"OrgSecretAuth":{"description":"Org-level secret","type":"apiKey","name":"X-ORG-SECRET","in":"header"}}}` // SwaggerInfo holds exported Swagger Info so clients can modify it var SwaggerInfo = &swag.Spec{ diff --git a/docs/swagger.json b/docs/swagger.json index 6c09e0c..00d7c0f 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1 +1 @@ -{"schemes":["http","https"],"swagger":"2.0","info":{"description":"API for managing K3s clusters across cloud providers","title":"AutoGlue API","contact":{"name":"GlueOps"},"version":"1.0"},"host":"localhost:8080","basePath":"/api/v1","paths":{"/.well-known/jwks.json":{"get":{"description":"Returns the JSON Web Key Set for token verification","produces":["application/json"],"tags":["Auth"],"summary":"Get JWKS","operationId":"getJWKS","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.JWKS"}}}}},"/auth/logout":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Revoke refresh token family (logout everywhere)","operationId":"Logout","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.LogoutRequest"}}],"responses":{"204":{"description":"No Content"}}}},"/auth/refresh":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Rotate refresh token","operationId":"Refresh","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.RefreshRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/callback":{"get":{"produces":["application/json"],"tags":["Auth"],"summary":"Handle social login callback","operationId":"AuthCallback","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/start":{"post":{"description":"Returns provider authorization URL for the frontend to redirect","produces":["application/json"],"tags":["Auth"],"summary":"Begin social login","operationId":"AuthStart","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.AuthStartResponse"}}}}},"/labels":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns node labels for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node groups.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"List node labels (org scoped)","operationId":"ListLabels","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Exact key","name":"key","in":"query"},{"type":"string","description":"Exact value","name":"value","in":"query"},{"type":"string","description":"Key contains (case-insensitive)","name":"q","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.LabelResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list node taints","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Create label (org scoped)","operationId":"CreateLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Label payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateLabelRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid json / missing fields / invalid node_pool_ids","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/labels/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns one label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Get label by ID (org scoped)","operationId":"GetLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Delete label (org scoped)","operationId":"DeleteLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update label fields.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Update label (org scoped)","operationId":"UpdateLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateLabelRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid id / invalid json","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}},"/me":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me"],"summary":"Get current user profile","operationId":"GetMe","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.meResponse"}}}},"patch":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Me"],"summary":"Update current user profile","operationId":"UpdateMe","parameters":[{"description":"Patch profile","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.updateMeRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.User"}}}}},"/me/api-keys":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"List my API keys","operationId":"ListUserAPIKeys","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"post":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"description":"Returns the plaintext key once. Store it securely on the client side.","consumes":["application/json"],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Create a new user API key","operationId":"CreateUserAPIKey","parameters":[{"description":"Key options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.createUserKeyRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"/me/api-keys/{id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Delete a user API key","operationId":"DeleteUserAPIKey","parameters":[{"type":"string","description":"Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content"}}}},"/orgs":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List organizations I belong to","operationId":"listMyOrgs","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create organization","operationId":"createOrg","parameters":[{"description":"Org payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/models.Organization"}},"400":{"description":"Bad Request","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"409":{"description":"Conflict","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Get organization","operationId":"getOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete organization (owner)","operationId":"deleteOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"patch":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Update organization (owner/admin)","operationId":"updateOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Update payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgUpdateReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List org-scoped API keys (no secrets)","operationId":"listOrgKeys","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.APIKey"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create org key/secret pair (owner/admin)","operationId":"createOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Key name + optional expiry","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgKeyCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.orgKeyCreateResp"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys/{key_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete org key (owner/admin)","operationId":"deleteOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"Key ID (UUID)","name":"key_id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List members in org","operationId":"listMembers","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.memberOut"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Add or update a member (owner/admin)","operationId":"addOrUpdateMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"User \u0026 role","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.memberUpsertReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.memberOut"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members/{user_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Remove a member (owner/admin)","operationId":"removeMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"User ID (UUID)","name":"user_id","in":"path","required":true}],"responses":{"204":{"description":"Removed"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/servers":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns servers for the organization in X-Org-ID. Optional filters: status, role.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"List servers (org scoped)","operationId":"ListServers","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Filter by status (pending|provisioning|ready|failed)","name":"status","in":"query"},{"type":"string","description":"Filter by role","name":"role","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.ServerResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list servers","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a server bound to the org in X-Org-ID. Validates that ssh_key_id belongs to the org.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Create server (org scoped)","operationId":"CreateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Server payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateServerRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid json / missing fields / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/servers/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns one server in the given organization.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Get server by ID (org scoped)","operationId":"GetServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the server.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Delete server (org scoped)","operationId":"DeleteServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update fields; changing ssh_key_id validates ownership.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Update server (org scoped)","operationId":"UpdateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateServerRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id / invalid json / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}},"/ssh":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns ssh keys for the organization in X-Org-ID.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"List ssh keys (org scoped)","operationId":"ListPublicSshKeys","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.SshResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list keys","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Generates an RSA or ED25519 keypair, saves it, and returns metadata. For RSA you may set bits (2048/3072/4096). Default is 4096. ED25519 ignores bits.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Create ssh keypair (org scoped)","operationId":"CreateSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Key generation options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateSSHRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.SshResponse"}},"400":{"description":"invalid json / invalid bits","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"generation/create failed","schema":{"type":"string"}}}}},"/ssh/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns public key fields. Append `?reveal=true` to include the private key PEM.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Get ssh key by ID (org scoped)","operationId":"GetSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"type":"boolean","description":"Reveal private key PEM","name":"reveal","in":"query"}],"responses":{"200":{"description":"When reveal=true","schema":{"$ref":"#/definitions/dto.SshRevealResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes a keypair.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Delete ssh keypair (org scoped)","operationId":"DeleteSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}}},"/ssh/{id}/download":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Download `part=public|private|both` of the keypair. `both` returns a zip file.","produces":["application/json"],"tags":["Ssh"],"summary":"Download ssh key files by ID (org scoped)","operationId":"DownloadSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header","required":true},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"enum":["public","private","both"],"type":"string","description":"Which part to download","name":"part","in":"query","required":true}],"responses":{"200":{"description":"file content","schema":{"type":"string"}},"400":{"description":"invalid id / invalid part","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"download failed","schema":{"type":"string"}}}}},"/taints":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns node taints for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node pools.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"List node pool taints (org scoped)","operationId":"ListTaints","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Exact key","name":"key","in":"query"},{"type":"string","description":"Exact value","name":"value","in":"query"},{"type":"string","description":"key contains (case-insensitive)","name":"q","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.TaintResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list node taints","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Create node taint (org scoped)","operationId":"CreateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Taint payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateTaintRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid json / missing fields / invalid node_pool_ids","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/taints/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Get node taint by ID (org scoped)","operationId":"GetTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Delete taint (org scoped)","operationId":"DeleteTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update taint fields.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Update node taint (org scoped)","operationId":"UpdateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateTaintRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id / invalid json","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}}},"definitions":{"dto.AuthStartResponse":{"type":"object","properties":{"auth_url":{"type":"string","example":"https://accounts.google.com/o/oauth2/v2/auth?client_id=..."}}},"dto.CreateLabelRequest":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"dto.CreateSSHRequest":{"type":"object","properties":{"bits":{"description":"Only for RSA","type":"integer"},"comment":{"type":"string","example":"deploy@autoglue"},"name":{"type":"string"},"type":{"description":"\"rsa\" (default) or \"ed25519\"","type":"string"}}},"dto.CreateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.CreateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.JWK":{"type":"object","properties":{"alg":{"type":"string","example":"RS256"},"e":{"type":"string","example":"AQAB"},"kid":{"type":"string","example":"7c6f1d0a-7a98-4e6a-9dbf-6b1af4b9f345"},"kty":{"type":"string","example":"RSA"},"n":{"type":"string"},"use":{"type":"string","example":"sig"},"x":{"type":"string"}}},"dto.JWKS":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/definitions/dto.JWK"}}}},"dto.LabelResponse":{"type":"object","properties":{"id":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.LogoutRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.RefreshRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.ServerResponse":{"type":"object","properties":{"created_at":{"type":"string"},"hostname":{"type":"string"},"id":{"type":"string"},"organization_id":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshRevealResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"private_key":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.TaintResponse":{"type":"object","properties":{"created_at":{"type":"string"},"effect":{"type":"string"},"id":{"type":"string"},"key":{"type":"string"},"organization_id":{"type":"string"},"updated_at":{"type":"string"},"value":{"type":"string"}}},"dto.TokenPair":{"type":"object","properties":{"access_token":{"type":"string","example":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ij..."},"expires_in":{"type":"integer","example":3600},"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf...."},"token_type":{"type":"string","example":"Bearer"}}},"dto.UpdateLabelRequest":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"dto.UpdateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.UpdateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"handlers.createUserKeyRequest":{"type":"object","properties":{"expires_in_hours":{"description":"optional TTL","type":"integer"},"name":{"type":"string"}}},"handlers.meResponse":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"emails":{"type":"array","items":{"$ref":"#/definitions/models.UserEmail"}},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"organizations":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"handlers.memberOut":{"type":"object","properties":{"email":{"type":"string"},"role":{"description":"owner/admin/member","type":"string"},"user_id":{"type":"string","format":"uuid"}}},"handlers.memberUpsertReq":{"type":"object","properties":{"role":{"type":"string","example":"member"},"user_id":{"type":"string","format":"uuid"}}},"handlers.orgCreateReq":{"type":"object","properties":{"domain":{"type":"string","example":"acme.com"},"name":{"type":"string","example":"Acme Corp"}}},"handlers.orgKeyCreateReq":{"type":"object","properties":{"expires_in_hours":{"type":"integer","example":720},"name":{"type":"string","example":"automation-bot"}}},"handlers.orgKeyCreateResp":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"org_key":{"description":"shown once:","type":"string"},"org_secret":{"description":"shown once:","type":"string"},"scope":{"description":"\"org\"","type":"string"}}},"handlers.orgUpdateReq":{"type":"object","properties":{"domain":{"type":"string"},"name":{"type":"string"}}},"handlers.updateMeRequest":{"type":"object","properties":{"display_name":{"type":"string"}}},"handlers.userAPIKeyOut":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string"},"name":{"type":"string"},"plain":{"description":"Shown only on create:","type":"string"},"scope":{"description":"\"user\"","type":"string"}}},"models.APIKey":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string","format":"date-time"},"name":{"type":"string"},"org_id":{"type":"string","format":"uuid"},"prefix":{"type":"string"},"revoked":{"type":"boolean"},"scope":{"type":"string"},"updated_at":{"type":"string","format":"date-time"},"user_id":{"type":"string","format":"uuid"}}},"models.Organization":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"domain":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"name":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.User":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.UserEmail":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"email":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_primary":{"type":"boolean"},"is_verified":{"type":"boolean"},"updated_at":{"type":"string","format":"date-time"},"user":{"$ref":"#/definitions/models.User"},"user_id":{"type":"string","format":"uuid"}}},"utils.ErrorResponse":{"type":"object","properties":{"code":{"description":"A machine-readable error code, e.g. \"validation_error\"\nexample: validation_error","type":"string"},"message":{"description":"Human-readable message\nexample: slug is required","type":"string"}}}},"securityDefinitions":{"ApiKeyAuth":{"description":"User API key","type":"apiKey","name":"X-API-KEY","in":"header"},"BearerAuth":{"description":"Bearer token authentication","type":"apiKey","name":"Authorization","in":"header"},"OrgKeyAuth":{"description":"Org-level key/secret authentication","type":"apiKey","name":"X-ORG-KEY","in":"header"},"OrgSecretAuth":{"description":"Org-level secret","type":"apiKey","name":"X-ORG-SECRET","in":"header"}}} \ No newline at end of file +{"schemes":["http","https"],"swagger":"2.0","info":{"description":"API for managing K3s clusters across cloud providers","title":"AutoGlue API","contact":{"name":"GlueOps"},"version":"1.0"},"host":"localhost:8080","basePath":"/api/v1","paths":{"/.well-known/jwks.json":{"get":{"description":"Returns the JSON Web Key Set for token verification","produces":["application/json"],"tags":["Auth"],"summary":"Get JWKS","operationId":"getJWKS","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.JWKS"}}}}},"/auth/logout":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Revoke refresh token family (logout everywhere)","operationId":"Logout","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.LogoutRequest"}}],"responses":{"204":{"description":"No Content"}}}},"/auth/refresh":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Rotate refresh token","operationId":"Refresh","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.RefreshRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/callback":{"get":{"produces":["application/json"],"tags":["Auth"],"summary":"Handle social login callback","operationId":"AuthCallback","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/start":{"post":{"description":"Returns provider authorization URL for the frontend to redirect","produces":["application/json"],"tags":["Auth"],"summary":"Begin social login","operationId":"AuthStart","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.AuthStartResponse"}}}}},"/labels":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns node labels for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node groups.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"List node labels (org scoped)","operationId":"ListLabels","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Exact key","name":"key","in":"query"},{"type":"string","description":"Exact value","name":"value","in":"query"},{"type":"string","description":"Key contains (case-insensitive)","name":"q","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.LabelResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list node taints","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Create label (org scoped)","operationId":"CreateLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Label payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateLabelRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid json / missing fields / invalid node_pool_ids","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/labels/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns one label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Get label by ID (org scoped)","operationId":"GetLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Delete label (org scoped)","operationId":"DeleteLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update label fields.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Update label (org scoped)","operationId":"UpdateLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateLabelRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid id / invalid json","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}},"/me":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me"],"summary":"Get current user profile","operationId":"GetMe","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.meResponse"}}}},"patch":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Me"],"summary":"Update current user profile","operationId":"UpdateMe","parameters":[{"description":"Patch profile","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.updateMeRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.User"}}}}},"/me/api-keys":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"List my API keys","operationId":"ListUserAPIKeys","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"post":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"description":"Returns the plaintext key once. Store it securely on the client side.","consumes":["application/json"],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Create a new user API key","operationId":"CreateUserAPIKey","parameters":[{"description":"Key options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.createUserKeyRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"/me/api-keys/{id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Delete a user API key","operationId":"DeleteUserAPIKey","parameters":[{"type":"string","description":"Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content"}}}},"/orgs":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List organizations I belong to","operationId":"listMyOrgs","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create organization","operationId":"createOrg","parameters":[{"description":"Org payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/models.Organization"}},"400":{"description":"Bad Request","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"409":{"description":"Conflict","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Get organization","operationId":"getOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete organization (owner)","operationId":"deleteOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"patch":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Update organization (owner/admin)","operationId":"updateOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Update payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgUpdateReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List org-scoped API keys (no secrets)","operationId":"listOrgKeys","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.APIKey"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create org key/secret pair (owner/admin)","operationId":"createOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Key name + optional expiry","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgKeyCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.orgKeyCreateResp"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys/{key_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete org key (owner/admin)","operationId":"deleteOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"Key ID (UUID)","name":"key_id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List members in org","operationId":"listMembers","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.memberOut"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Add or update a member (owner/admin)","operationId":"addOrUpdateMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"User \u0026 role","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.memberUpsertReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.memberOut"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members/{user_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Remove a member (owner/admin)","operationId":"removeMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"User ID (UUID)","name":"user_id","in":"path","required":true}],"responses":{"204":{"description":"Removed"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/servers":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns servers for the organization in X-Org-ID. Optional filters: status, role.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"List servers (org scoped)","operationId":"ListServers","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Filter by status (pending|provisioning|ready|failed)","name":"status","in":"query"},{"type":"string","description":"Filter by role","name":"role","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.ServerResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list servers","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a server bound to the org in X-Org-ID. Validates that ssh_key_id belongs to the org.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Create server (org scoped)","operationId":"CreateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Server payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateServerRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid json / missing fields / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/servers/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns one server in the given organization.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Get server by ID (org scoped)","operationId":"GetServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the server.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Delete server (org scoped)","operationId":"DeleteServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update fields; changing ssh_key_id validates ownership.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Update server (org scoped)","operationId":"UpdateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateServerRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id / invalid json / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}},"/ssh":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns ssh keys for the organization in X-Org-ID.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"List ssh keys (org scoped)","operationId":"ListPublicSshKeys","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.SshResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list keys","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Generates an RSA or ED25519 keypair, saves it, and returns metadata. For RSA you may set bits (2048/3072/4096). Default is 4096. ED25519 ignores bits.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Create ssh keypair (org scoped)","operationId":"CreateSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Key generation options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateSSHRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.SshResponse"}},"400":{"description":"invalid json / invalid bits","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"generation/create failed","schema":{"type":"string"}}}}},"/ssh/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns public key fields. Append `?reveal=true` to include the private key PEM.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Get ssh key by ID (org scoped)","operationId":"GetSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"type":"boolean","description":"Reveal private key PEM","name":"reveal","in":"query"}],"responses":{"200":{"description":"When reveal=true","schema":{"$ref":"#/definitions/dto.SshRevealResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes a keypair.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Delete ssh keypair (org scoped)","operationId":"DeleteSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}}},"/ssh/{id}/download":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Download `part=public|private|both` of the keypair. `both` returns a zip file.","produces":["application/json"],"tags":["Ssh"],"summary":"Download ssh key files by ID (org scoped)","operationId":"DownloadSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header","required":true},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"enum":["public","private","both"],"type":"string","description":"Which part to download","name":"part","in":"query","required":true}],"responses":{"200":{"description":"file content","schema":{"type":"string"}},"400":{"description":"invalid id / invalid part","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"download failed","schema":{"type":"string"}}}}},"/taints":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns node taints for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node pools.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"List node pool taints (org scoped)","operationId":"ListTaints","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Exact key","name":"key","in":"query"},{"type":"string","description":"Exact value","name":"value","in":"query"},{"type":"string","description":"key contains (case-insensitive)","name":"q","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.TaintResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list node taints","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Create node taint (org scoped)","operationId":"CreateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Taint payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateTaintRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid json / missing fields / invalid node_pool_ids","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/taints/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Get node taint by ID (org scoped)","operationId":"GetTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Delete taint (org scoped)","operationId":"DeleteTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update taint fields.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Update node taint (org scoped)","operationId":"UpdateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateTaintRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id / invalid json","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}}},"definitions":{"dto.AuthStartResponse":{"type":"object","properties":{"auth_url":{"type":"string","example":"https://accounts.google.com/o/oauth2/v2/auth?client_id=..."}}},"dto.CreateLabelRequest":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"dto.CreateSSHRequest":{"type":"object","properties":{"bits":{"description":"Only for RSA","type":"integer"},"comment":{"type":"string","example":"deploy@autoglue"},"name":{"type":"string"},"type":{"description":"\"rsa\" (default) or \"ed25519\"","type":"string"}}},"dto.CreateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.CreateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.JWK":{"type":"object","properties":{"alg":{"type":"string","example":"RS256"},"e":{"type":"string","example":"AQAB"},"kid":{"type":"string","example":"7c6f1d0a-7a98-4e6a-9dbf-6b1af4b9f345"},"kty":{"type":"string","example":"RSA"},"n":{"type":"string"},"use":{"type":"string","example":"sig"},"x":{"type":"string"}}},"dto.JWKS":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/definitions/dto.JWK"}}}},"dto.LabelResponse":{"type":"object","properties":{"created_at":{"type":"string"},"id":{"type":"string"},"key":{"type":"string"},"organization_id":{"type":"string"},"updated_at":{"type":"string"},"value":{"type":"string"}}},"dto.LogoutRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.RefreshRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.ServerResponse":{"type":"object","properties":{"created_at":{"type":"string"},"hostname":{"type":"string"},"id":{"type":"string"},"organization_id":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshRevealResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"private_key":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.TaintResponse":{"type":"object","properties":{"created_at":{"type":"string"},"effect":{"type":"string"},"id":{"type":"string"},"key":{"type":"string"},"organization_id":{"type":"string"},"updated_at":{"type":"string"},"value":{"type":"string"}}},"dto.TokenPair":{"type":"object","properties":{"access_token":{"type":"string","example":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ij..."},"expires_in":{"type":"integer","example":3600},"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf...."},"token_type":{"type":"string","example":"Bearer"}}},"dto.UpdateLabelRequest":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"dto.UpdateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.UpdateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"handlers.createUserKeyRequest":{"type":"object","properties":{"expires_in_hours":{"description":"optional TTL","type":"integer"},"name":{"type":"string"}}},"handlers.meResponse":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"emails":{"type":"array","items":{"$ref":"#/definitions/models.UserEmail"}},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"organizations":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"handlers.memberOut":{"type":"object","properties":{"email":{"type":"string"},"role":{"description":"owner/admin/member","type":"string"},"user_id":{"type":"string","format":"uuid"}}},"handlers.memberUpsertReq":{"type":"object","properties":{"role":{"type":"string","example":"member"},"user_id":{"type":"string","format":"uuid"}}},"handlers.orgCreateReq":{"type":"object","properties":{"domain":{"type":"string","example":"acme.com"},"name":{"type":"string","example":"Acme Corp"}}},"handlers.orgKeyCreateReq":{"type":"object","properties":{"expires_in_hours":{"type":"integer","example":720},"name":{"type":"string","example":"automation-bot"}}},"handlers.orgKeyCreateResp":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"org_key":{"description":"shown once:","type":"string"},"org_secret":{"description":"shown once:","type":"string"},"scope":{"description":"\"org\"","type":"string"}}},"handlers.orgUpdateReq":{"type":"object","properties":{"domain":{"type":"string"},"name":{"type":"string"}}},"handlers.updateMeRequest":{"type":"object","properties":{"display_name":{"type":"string"}}},"handlers.userAPIKeyOut":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string"},"name":{"type":"string"},"plain":{"description":"Shown only on create:","type":"string"},"scope":{"description":"\"user\"","type":"string"}}},"models.APIKey":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string","format":"date-time"},"name":{"type":"string"},"org_id":{"type":"string","format":"uuid"},"prefix":{"type":"string"},"revoked":{"type":"boolean"},"scope":{"type":"string"},"updated_at":{"type":"string","format":"date-time"},"user_id":{"type":"string","format":"uuid"}}},"models.Organization":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"domain":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"name":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.User":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.UserEmail":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"email":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_primary":{"type":"boolean"},"is_verified":{"type":"boolean"},"updated_at":{"type":"string","format":"date-time"},"user":{"$ref":"#/definitions/models.User"},"user_id":{"type":"string","format":"uuid"}}},"utils.ErrorResponse":{"type":"object","properties":{"code":{"description":"A machine-readable error code, e.g. \"validation_error\"\nexample: validation_error","type":"string"},"message":{"description":"Human-readable message\nexample: slug is required","type":"string"}}}},"securityDefinitions":{"ApiKeyAuth":{"description":"User API key","type":"apiKey","name":"X-API-KEY","in":"header"},"BearerAuth":{"description":"Bearer token authentication","type":"apiKey","name":"Authorization","in":"header"},"OrgKeyAuth":{"description":"Org-level key/secret authentication","type":"apiKey","name":"X-ORG-KEY","in":"header"},"OrgSecretAuth":{"description":"Org-level secret","type":"apiKey","name":"X-ORG-SECRET","in":"header"}}} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 26e1f9e..49fe3bb 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -86,10 +86,16 @@ definitions: type: object dto.LabelResponse: properties: + created_at: + type: string id: type: string key: type: string + organization_id: + type: string + updated_at: + type: string value: type: string type: object diff --git a/internal/models/node_pool.go b/internal/models/node_pool.go index 4663522..cbdb3ab 100644 --- a/internal/models/node_pool.go +++ b/internal/models/node_pool.go @@ -17,7 +17,7 @@ type NodePool struct { Taints []Taint `gorm:"many2many:node_taints;constraint:OnDelete:CASCADE" json:"taints,omitempty"` //Clusters []Cluster `gorm:"many2many:cluster_node_pools;constraint:OnDelete:CASCADE" json:"clusters,omitempty"` Topology string `gorm:"not null" json:"topology,omitempty"` // stacked or external - Role string `gorm:"not null" json:"role,omitempty"` // master, worker, ort etcd (etcd only if topology = external + Role string `gorm:"not null" json:"role,omitempty"` // master, worker, or etcd (etcd only if topology = external CreatedAt time.Time `gorm:"not null;default:now()" json:"created_at" format:"date-time"` UpdatedAt time.Time `gorm:"not null;default:now()" json:"updated_at" format:"date-time"` } diff --git a/sdk/go/api/openapi.yaml b/sdk/go/api/openapi.yaml index 006cab4..7b261d9 100644 --- a/sdk/go/api/openapi.yaml +++ b/sdk/go/api/openapi.yaml @@ -1852,14 +1852,23 @@ components: type: object dto.LabelResponse: example: + updated_at: updated_at + organization_id: organization_id + created_at: created_at id: id value: value key: key properties: + created_at: + type: string id: type: string key: type: string + organization_id: + type: string + updated_at: + type: string value: type: string type: object diff --git a/sdk/go/docs/DtoLabelResponse.md b/sdk/go/docs/DtoLabelResponse.md index 83a2b9a..d96ed9d 100644 --- a/sdk/go/docs/DtoLabelResponse.md +++ b/sdk/go/docs/DtoLabelResponse.md @@ -4,8 +4,11 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- +**CreatedAt** | Pointer to **string** | | [optional] **Id** | Pointer to **string** | | [optional] **Key** | Pointer to **string** | | [optional] +**OrganizationId** | Pointer to **string** | | [optional] +**UpdatedAt** | Pointer to **string** | | [optional] **Value** | Pointer to **string** | | [optional] ## Methods @@ -27,6 +30,31 @@ NewDtoLabelResponseWithDefaults instantiates a new DtoLabelResponse object This constructor will only assign default values to properties that have it defined, but it doesn't guarantee that properties required by API are set +### GetCreatedAt + +`func (o *DtoLabelResponse) GetCreatedAt() string` + +GetCreatedAt returns the CreatedAt field if non-nil, zero value otherwise. + +### GetCreatedAtOk + +`func (o *DtoLabelResponse) GetCreatedAtOk() (*string, bool)` + +GetCreatedAtOk returns a tuple with the CreatedAt field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetCreatedAt + +`func (o *DtoLabelResponse) SetCreatedAt(v string)` + +SetCreatedAt sets CreatedAt field to given value. + +### HasCreatedAt + +`func (o *DtoLabelResponse) HasCreatedAt() bool` + +HasCreatedAt returns a boolean if a field has been set. + ### GetId `func (o *DtoLabelResponse) GetId() string` @@ -77,6 +105,56 @@ SetKey sets Key field to given value. HasKey returns a boolean if a field has been set. +### GetOrganizationId + +`func (o *DtoLabelResponse) GetOrganizationId() string` + +GetOrganizationId returns the OrganizationId field if non-nil, zero value otherwise. + +### GetOrganizationIdOk + +`func (o *DtoLabelResponse) GetOrganizationIdOk() (*string, bool)` + +GetOrganizationIdOk returns a tuple with the OrganizationId field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetOrganizationId + +`func (o *DtoLabelResponse) SetOrganizationId(v string)` + +SetOrganizationId sets OrganizationId field to given value. + +### HasOrganizationId + +`func (o *DtoLabelResponse) HasOrganizationId() bool` + +HasOrganizationId returns a boolean if a field has been set. + +### GetUpdatedAt + +`func (o *DtoLabelResponse) GetUpdatedAt() string` + +GetUpdatedAt returns the UpdatedAt field if non-nil, zero value otherwise. + +### GetUpdatedAtOk + +`func (o *DtoLabelResponse) GetUpdatedAtOk() (*string, bool)` + +GetUpdatedAtOk returns a tuple with the UpdatedAt field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetUpdatedAt + +`func (o *DtoLabelResponse) SetUpdatedAt(v string)` + +SetUpdatedAt sets UpdatedAt field to given value. + +### HasUpdatedAt + +`func (o *DtoLabelResponse) HasUpdatedAt() bool` + +HasUpdatedAt returns a boolean if a field has been set. + ### GetValue `func (o *DtoLabelResponse) GetValue() string` diff --git a/sdk/go/model_dto_label_response.go b/sdk/go/model_dto_label_response.go index 24dc623..bb37d76 100644 --- a/sdk/go/model_dto_label_response.go +++ b/sdk/go/model_dto_label_response.go @@ -19,9 +19,12 @@ var _ MappedNullable = &DtoLabelResponse{} // DtoLabelResponse struct for DtoLabelResponse type DtoLabelResponse struct { - Id *string `json:"id,omitempty"` - Key *string `json:"key,omitempty"` - Value *string `json:"value,omitempty"` + CreatedAt *string `json:"created_at,omitempty"` + Id *string `json:"id,omitempty"` + Key *string `json:"key,omitempty"` + OrganizationId *string `json:"organization_id,omitempty"` + UpdatedAt *string `json:"updated_at,omitempty"` + Value *string `json:"value,omitempty"` } // NewDtoLabelResponse instantiates a new DtoLabelResponse object @@ -41,6 +44,38 @@ func NewDtoLabelResponseWithDefaults() *DtoLabelResponse { return &this } +// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise. +func (o *DtoLabelResponse) GetCreatedAt() string { + if o == nil || IsNil(o.CreatedAt) { + var ret string + return ret + } + return *o.CreatedAt +} + +// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DtoLabelResponse) GetCreatedAtOk() (*string, bool) { + if o == nil || IsNil(o.CreatedAt) { + return nil, false + } + return o.CreatedAt, true +} + +// HasCreatedAt returns a boolean if a field has been set. +func (o *DtoLabelResponse) HasCreatedAt() bool { + if o != nil && !IsNil(o.CreatedAt) { + return true + } + + return false +} + +// SetCreatedAt gets a reference to the given string and assigns it to the CreatedAt field. +func (o *DtoLabelResponse) SetCreatedAt(v string) { + o.CreatedAt = &v +} + // GetId returns the Id field value if set, zero value otherwise. func (o *DtoLabelResponse) GetId() string { if o == nil || IsNil(o.Id) { @@ -105,6 +140,70 @@ func (o *DtoLabelResponse) SetKey(v string) { o.Key = &v } +// GetOrganizationId returns the OrganizationId field value if set, zero value otherwise. +func (o *DtoLabelResponse) GetOrganizationId() string { + if o == nil || IsNil(o.OrganizationId) { + var ret string + return ret + } + return *o.OrganizationId +} + +// GetOrganizationIdOk returns a tuple with the OrganizationId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DtoLabelResponse) GetOrganizationIdOk() (*string, bool) { + if o == nil || IsNil(o.OrganizationId) { + return nil, false + } + return o.OrganizationId, true +} + +// HasOrganizationId returns a boolean if a field has been set. +func (o *DtoLabelResponse) HasOrganizationId() bool { + if o != nil && !IsNil(o.OrganizationId) { + return true + } + + return false +} + +// SetOrganizationId gets a reference to the given string and assigns it to the OrganizationId field. +func (o *DtoLabelResponse) SetOrganizationId(v string) { + o.OrganizationId = &v +} + +// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise. +func (o *DtoLabelResponse) GetUpdatedAt() string { + if o == nil || IsNil(o.UpdatedAt) { + var ret string + return ret + } + return *o.UpdatedAt +} + +// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DtoLabelResponse) GetUpdatedAtOk() (*string, bool) { + if o == nil || IsNil(o.UpdatedAt) { + return nil, false + } + return o.UpdatedAt, true +} + +// HasUpdatedAt returns a boolean if a field has been set. +func (o *DtoLabelResponse) HasUpdatedAt() bool { + if o != nil && !IsNil(o.UpdatedAt) { + return true + } + + return false +} + +// SetUpdatedAt gets a reference to the given string and assigns it to the UpdatedAt field. +func (o *DtoLabelResponse) SetUpdatedAt(v string) { + o.UpdatedAt = &v +} + // GetValue returns the Value field value if set, zero value otherwise. func (o *DtoLabelResponse) GetValue() string { if o == nil || IsNil(o.Value) { @@ -147,12 +246,21 @@ func (o DtoLabelResponse) MarshalJSON() ([]byte, error) { func (o DtoLabelResponse) ToMap() (map[string]interface{}, error) { toSerialize := map[string]interface{}{} + if !IsNil(o.CreatedAt) { + toSerialize["created_at"] = o.CreatedAt + } if !IsNil(o.Id) { toSerialize["id"] = o.Id } if !IsNil(o.Key) { toSerialize["key"] = o.Key } + if !IsNil(o.OrganizationId) { + toSerialize["organization_id"] = o.OrganizationId + } + if !IsNil(o.UpdatedAt) { + toSerialize["updated_at"] = o.UpdatedAt + } if !IsNil(o.Value) { toSerialize["value"] = o.Value } diff --git a/sdk/ts/docs/DtoLabelResponse.md b/sdk/ts/docs/DtoLabelResponse.md index d7e8102..4710307 100644 --- a/sdk/ts/docs/DtoLabelResponse.md +++ b/sdk/ts/docs/DtoLabelResponse.md @@ -2,11 +2,14 @@ ## Properties -| Name | Type | -| ------- | ------ | -| `id` | string | -| `key` | string | -| `value` | string | +| Name | Type | +| ----------------- | ------ | +| `created_at` | string | +| `id` | string | +| `key` | string | +| `organization_id` | string | +| `updated_at` | string | +| `value` | string | ## Example @@ -15,8 +18,11 @@ import type { DtoLabelResponse } from "@glueops/autoglue-sdk-go"; // TODO: Update the object below with actual values const example = { + created_at: null, id: null, key: null, + organization_id: null, + updated_at: null, value: null, } satisfies DtoLabelResponse; diff --git a/sdk/ts/src/models/DtoLabelResponse.ts b/sdk/ts/src/models/DtoLabelResponse.ts index 6188a57..a02910b 100644 --- a/sdk/ts/src/models/DtoLabelResponse.ts +++ b/sdk/ts/src/models/DtoLabelResponse.ts @@ -19,6 +19,12 @@ import { mapValues } from "../runtime"; * @interface DtoLabelResponse */ export interface DtoLabelResponse { + /** + * + * @type {string} + * @memberof DtoLabelResponse + */ + created_at?: string; /** * * @type {string} @@ -31,6 +37,18 @@ export interface DtoLabelResponse { * @memberof DtoLabelResponse */ key?: string; + /** + * + * @type {string} + * @memberof DtoLabelResponse + */ + organization_id?: string; + /** + * + * @type {string} + * @memberof DtoLabelResponse + */ + updated_at?: string; /** * * @type {string} @@ -60,8 +78,12 @@ export function DtoLabelResponseFromJSONTyped( return json; } return { + created_at: json["created_at"] == null ? undefined : json["created_at"], id: json["id"] == null ? undefined : json["id"], key: json["key"] == null ? undefined : json["key"], + organization_id: + json["organization_id"] == null ? undefined : json["organization_id"], + updated_at: json["updated_at"] == null ? undefined : json["updated_at"], value: json["value"] == null ? undefined : json["value"], }; } @@ -79,8 +101,11 @@ export function DtoLabelResponseToJSONTyped( } return { + created_at: value["created_at"], id: value["id"], key: value["key"], + organization_id: value["organization_id"], + updated_at: value["updated_at"], value: value["value"], }; } diff --git a/terraform-provider-autoglue/internal/provider/datasource_labels.go b/terraform-provider-autoglue/internal/provider/datasource_labels.go new file mode 100644 index 0000000..3a67348 --- /dev/null +++ b/terraform-provider-autoglue/internal/provider/datasource_labels.go @@ -0,0 +1,104 @@ +package provider + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + dschema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var _ datasource.DataSource = &LabelsDataSource{} +var _ datasource.DataSourceWithConfigure = &LabelsDataSource{} + +type LabelsDataSource struct{ client *Client } + +func NewLabelsDataSource() datasource.DataSource { return &LabelsDataSource{} } + +type labelsDSModel struct { + Items []labelItem `tfsdk:"items"` +} + +type labelItem struct { + ID types.String `tfsdk:"id"` + OrganizationID types.String `tfsdk:"organization_id"` + CreatedAt types.String `tfsdk:"created_at"` + UpdatedAt types.String `tfsdk:"updated_at"` + Key types.String `tfsdk:"key"` + Value types.String `tfsdk:"value"` + Raw types.String `tfsdk:"raw"` +} + +func (d *LabelsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_labels" +} + +func (d *LabelsDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = dschema.Schema{ + Description: "List labels for the organization (org-scoped).", + Attributes: map[string]dschema.Attribute{ + "items": dschema.ListNestedAttribute{ + Computed: true, + Description: "Labels returned by the API.", + NestedObject: dschema.NestedAttributeObject{ + Attributes: map[string]dschema.Attribute{ + "id": dschema.StringAttribute{Computed: true, Description: "Taint ID (UUID)."}, + "organization_id": dschema.StringAttribute{Computed: true}, + "key": dschema.StringAttribute{Computed: true}, + "value": dschema.StringAttribute{Computed: true}, + "created_at": dschema.StringAttribute{Computed: true, Description: "RFC3339, UTC."}, + "updated_at": dschema.StringAttribute{Computed: true, Description: "RFC3339, UTC."}, + "raw": dschema.StringAttribute{Computed: true, Description: "Full JSON for the item."}, + }, + }, + }, + }, + } +} + +func (d *LabelsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, _ *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + d.client = req.ProviderData.(*Client) +} + +func (d *LabelsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + if d.client == nil || d.client.SDK == nil { + resp.Diagnostics.AddError("Client not configured", "Provider configuration missing") + return + } + + var conf labelsDSModel + resp.Diagnostics.Append(req.Config.Get(ctx, &conf)...) + if resp.Diagnostics.HasError() { + return + } + + call := d.client.SDK.TaintsAPI.ListTaints(ctx) + items, httpResp, err := call.Execute() + if err != nil { + resp.Diagnostics.AddError("List labels failed", fmt.Sprintf("%v", httpErr(err, httpResp))) + return + } + + out := labelsDSModel{ + Items: make([]labelItem, 0, len(items)), + } + + for _, item := range items { + raw, _ := json.Marshal(item) + out.Items = append(out.Items, labelItem{ + ID: types.StringPointerValue(item.Id), + OrganizationID: types.StringPointerValue(item.OrganizationId), + Key: types.StringPointerValue(item.Key), + Value: types.StringPointerValue(item.Value), + CreatedAt: types.StringPointerValue(item.CreatedAt), + UpdatedAt: types.StringPointerValue(item.UpdatedAt), + Raw: types.StringValue(string(raw)), + }) + } + resp.Diagnostics.Append(resp.State.Set(ctx, &out)...) +} diff --git a/terraform-provider-autoglue/internal/provider/provider.go b/terraform-provider-autoglue/internal/provider/provider.go index b9e83fa..5f08869 100644 --- a/terraform-provider-autoglue/internal/provider/provider.go +++ b/terraform-provider-autoglue/internal/provider/provider.go @@ -50,6 +50,7 @@ func (p *AutoglueProvider) DataSources(_ context.Context) []func() datasource.Da NewSshDataSource, NewServersDataSource, NewTaintsDataSource, + NewLabelsDataSource, } } @@ -58,5 +59,6 @@ func (p *AutoglueProvider) Resources(_ context.Context) []func() resource.Resour NewSshResource, NewServerResource, NewTaintResource, + NewLabelResource, } } diff --git a/terraform-provider-autoglue/internal/provider/resource_label.go b/terraform-provider-autoglue/internal/provider/resource_label.go new file mode 100644 index 0000000..0413a0b --- /dev/null +++ b/terraform-provider-autoglue/internal/provider/resource_label.go @@ -0,0 +1,235 @@ +package provider + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/glueops/autoglue-sdk-go" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + rschema "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var _ resource.Resource = &LabelResource{} +var _ resource.ResourceWithConfigure = &LabelResource{} +var _ resource.ResourceWithImportState = &LabelResource{} + +type LabelResource struct{ client *Client } + +func NewLabelResource() resource.Resource { return &LabelResource{} } + +type labelResModel struct { + ID types.String `tfsdk:"id"` + OrganizationID types.String `tfsdk:"organization_id"` + CreatedAt types.String `tfsdk:"created_at"` + UpdatedAt types.String `tfsdk:"updated_at"` + Key types.String `tfsdk:"key"` + Value types.String `tfsdk:"value"` + Raw types.String `tfsdk:"raw"` +} + +func (r *LabelResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_label" +} + +func (r *LabelResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = rschema.Schema{ + Description: "Create and manage a label (org-scoped).", + Attributes: map[string]rschema.Attribute{ + "id": rschema.StringAttribute{ + Computed: true, + Description: "Server ID (UUID).", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "organization_id": rschema.StringAttribute{Computed: true}, + "created_at": rschema.StringAttribute{Computed: true}, + "updated_at": rschema.StringAttribute{Computed: true}, + "key": rschema.StringAttribute{ + Required: true, + Description: "Key.", + }, + "value": rschema.StringAttribute{ + Required: true, + Description: "Value.", + }, + "raw": rschema.StringAttribute{ + Computed: true, + Description: "Full server JSON from API.", + }, + }, + } +} + +func (r *LabelResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + r.client = req.ProviderData.(*Client) +} + +func (r *LabelResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + if r.client == nil || r.client.SDK == nil { + resp.Diagnostics.AddError("Client not configured", "Provider configuration missing") + return + } + + var plan labelResModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + payload := autoglue.NewDtoCreateLabelRequest() + if !plan.Key.IsNull() { + k := plan.Key.ValueString() + payload.SetKey(k) + } + if !plan.Value.IsNull() { + v := plan.Value.ValueString() + payload.SetValue(v) + } + + call := r.client.SDK.LabelsAPI.CreateLabel(ctx).Body(*payload) + + out, httpResp, err := call.Execute() + if err != nil { + resp.Diagnostics.AddError("Create label failed", fmt.Sprintf("%v", httpErr(err, httpResp))) + return + } + + raw, _ := json.Marshal(out) + + var state labelResModel + r.mapRespToState(out, &state) + state.Raw = types.StringValue(string(raw)) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *LabelResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + if r.client == nil || r.client.SDK == nil { + resp.Diagnostics.AddError("Client not configured", "Provider configuration missing") + return + } + + var state labelResModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if state.ID.IsNull() || state.ID.ValueString() == "" { + // Nothing to read; treat as gone + resp.State.RemoveResource(ctx) + return + } + + call := r.client.SDK.LabelsAPI.GetLabel(ctx, state.ID.ValueString()) + + out, httpResp, err := call.Execute() + if err != nil { + if httpResp != nil && httpResp.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError("Read label failed", fmt.Sprintf("%v", httpErr(err, httpResp))) + return + } + + raw, _ := json.Marshal(out) + r.mapRespToState(out, &state) + state.Raw = types.StringValue(string(raw)) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *LabelResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + if r.client == nil || r.client.SDK == nil { + resp.Diagnostics.AddError("Client not configured", "Provider configuration missing") + return + } + + var plan labelResModel + var prior labelResModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &prior)...) + if resp.Diagnostics.HasError() { + return + } + + body := autoglue.NewDtoUpdateLabelRequest() + if !plan.Key.IsNull() { + k := plan.Key.ValueString() + body.SetKey(k) + } + if !plan.Value.IsNull() { + v := plan.Value.ValueString() + body.SetValue(v) + } + + call := r.client.SDK.LabelsAPI.UpdateLabel(ctx, prior.ID.ValueString()).Body(*body) + + out, httpResp, err := call.Execute() + if err != nil { + // If 404 on update, treat as gone + if httpResp != nil && httpResp.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError("Update label failed", fmt.Sprintf("%v", httpErr(err, httpResp))) + return + } + + raw, _ := json.Marshal(out) + + var newState labelResModel + r.mapRespToState(out, &newState) + newState.Raw = types.StringValue(string(raw)) + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *LabelResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + if r.client == nil || r.client.SDK == nil { + resp.Diagnostics.AddError("Client not configured", "Provider configuration missing") + return + } + + var state labelResModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + call := r.client.SDK.LabelsAPI.DeleteLabel(ctx, state.ID.ValueString()) + + _, httpResp, err := call.Execute() + if err != nil { + // If already gone, that's fine + if httpResp != nil && httpResp.StatusCode == http.StatusNotFound { + return + } + resp.Diagnostics.AddError("Delete label failed", fmt.Sprintf("%v", httpErr(err, httpResp))) + return + } +} + +func (r *LabelResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), req.ID)...) +} + +func (r *LabelResource) mapRespToState(s *autoglue.DtoLabelResponse, out *labelResModel) { + out.ID = types.StringPointerValue(s.Id) + out.OrganizationID = types.StringPointerValue(s.OrganizationId) + out.Key = types.StringPointerValue(s.Key) + out.Value = types.StringPointerValue(s.Value) + out.CreatedAt = types.StringPointerValue(s.CreatedAt) + out.UpdatedAt = types.StringPointerValue(s.UpdatedAt) +} diff --git a/terraform/envs/dev/terraform.tfvars b/terraform/envs/dev/terraform.tfvars index 7c23a11..e859025 100644 --- a/terraform/envs/dev/terraform.tfvars +++ b/terraform/envs/dev/terraform.tfvars @@ -19,5 +19,4 @@ ssh_keys = { download_part = "both" download_dir = "out/clusterKey" } -} - +} \ No newline at end of file diff --git a/terraform/envs/dev/variables.tf b/terraform/envs/dev/variables.tf index b475b21..eb4cf4e 100644 --- a/terraform/envs/dev/variables.tf +++ b/terraform/envs/dev/variables.tf @@ -28,3 +28,4 @@ variable "ssh_keys" { download_dir = optional(string, "out") })) } + diff --git a/terraform/modules/taints/main.tf b/terraform/modules/taints/main.tf new file mode 100644 index 0000000..6ff070a --- /dev/null +++ b/terraform/modules/taints/main.tf @@ -0,0 +1,4 @@ +resource "autoglue_taints" "this" { + key = var.key + +} \ No newline at end of file diff --git a/terraform/modules/taints/outputs.tf b/terraform/modules/taints/outputs.tf new file mode 100644 index 0000000..a7c8281 --- /dev/null +++ b/terraform/modules/taints/outputs.tf @@ -0,0 +1,4 @@ +output "ids" { + description = "Map of taint IDs by key." + value = { for k, r in autoglue_taint.this : k => r.id } +} diff --git a/terraform/modules/taints/variables.tf b/terraform/modules/taints/variables.tf new file mode 100644 index 0000000..3d30b15 --- /dev/null +++ b/terraform/modules/taints/variables.tf @@ -0,0 +1,11 @@ +variable "key" { + type = string +} + +variable "value" { + type = string +} + +variable "effect" { + type = string +} \ No newline at end of file diff --git a/terraform/modules/taints/versions.tf b/terraform/modules/taints/versions.tf new file mode 100644 index 0000000..60ae39d --- /dev/null +++ b/terraform/modules/taints/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.5.0" + + required_providers { + autoglue = { + source = "glueops/autoglue/autoglue" + } + } +} \ No newline at end of file diff --git a/ui/src/sdk/docs/DtoLabelResponse.md b/ui/src/sdk/docs/DtoLabelResponse.md index 32cf8db..3d54003 100644 --- a/ui/src/sdk/docs/DtoLabelResponse.md +++ b/ui/src/sdk/docs/DtoLabelResponse.md @@ -6,8 +6,11 @@ Name | Type ------------ | ------------- +`created_at` | string `id` | string `key` | string +`organization_id` | string +`updated_at` | string `value` | string ## Example @@ -17,8 +20,11 @@ import type { DtoLabelResponse } from '@glueops/autoglue-sdk-go' // TODO: Update the object below with actual values const example = { + "created_at": null, "id": null, "key": null, + "organization_id": null, + "updated_at": null, "value": null, } satisfies DtoLabelResponse diff --git a/ui/src/sdk/models/DtoLabelResponse.ts b/ui/src/sdk/models/DtoLabelResponse.ts index 3fb20fb..5d92808 100644 --- a/ui/src/sdk/models/DtoLabelResponse.ts +++ b/ui/src/sdk/models/DtoLabelResponse.ts @@ -19,6 +19,12 @@ import { mapValues } from '../runtime'; * @interface DtoLabelResponse */ export interface DtoLabelResponse { + /** + * + * @type {string} + * @memberof DtoLabelResponse + */ + created_at?: string; /** * * @type {string} @@ -31,6 +37,18 @@ export interface DtoLabelResponse { * @memberof DtoLabelResponse */ key?: string; + /** + * + * @type {string} + * @memberof DtoLabelResponse + */ + organization_id?: string; + /** + * + * @type {string} + * @memberof DtoLabelResponse + */ + updated_at?: string; /** * * @type {string} @@ -56,8 +74,11 @@ export function DtoLabelResponseFromJSONTyped(json: any, ignoreDiscriminator: bo } return { + 'created_at': json['created_at'] == null ? undefined : json['created_at'], 'id': json['id'] == null ? undefined : json['id'], 'key': json['key'] == null ? undefined : json['key'], + 'organization_id': json['organization_id'] == null ? undefined : json['organization_id'], + 'updated_at': json['updated_at'] == null ? undefined : json['updated_at'], 'value': json['value'] == null ? undefined : json['value'], }; } @@ -73,8 +94,11 @@ export function DtoLabelResponseToJSONTyped(value?: DtoLabelResponse | null, ign return { + 'created_at': value['created_at'], 'id': value['id'], 'key': value['key'], + 'organization_id': value['organization_id'], + 'updated_at': value['updated_at'], 'value': value['value'], }; }