diff --git a/Dockerfile b/Dockerfile index 73b0bfa..87dc834 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ RUN npm i -g yarn pnpm WORKDIR /src COPY . . -RUN make swagger && make build +RUN make clean && make swagger && make ui && make build ################################# diff --git a/Makefile b/Makefile index ce7c8e6..4acc684 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ GOINSTALL := $(GOCMD) install BIN ?= autoglue MAIN ?= main.go UI_DIR ?= ui +UI_DEST_DIR ?= internal/ui SWAG := $(shell command -v swag 2>/dev/null) GMU := $(shell command -v go-mod-upgrade 2>/dev/null) @@ -68,7 +69,7 @@ build: prepare ui swagger clean: @echo ">> Cleaning artifacts..." - @rm -rf $(BIN) docs/swagger.* docs/docs.go $(UI_DIR)/dist + @rm -rf $(BIN) docs/swagger.* docs/docs.go $(UI_DEST_DIR)/dist $(UI_DIR)/dist $(UI_DIR)/node_modules dev: swagger @echo ">> Starting Vite (frontend) and Go API (backend)..." diff --git a/docker-compose.yml b/docker-compose.yml index 946be74..8821211 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -37,7 +37,7 @@ services: - postgres:postgres env_file: .env environment: - DATABASE_URL: postgres://$DB_USER:$DB_PASSWORD@postgres:5432/$DB_NAME + PGWEB_DATABASE_URL: postgres://$DB_USER:$DB_PASSWORD@postgres:5432/$DB_NAME depends_on: - postgres diff --git a/docs/docs.go b/docs/docs.go deleted file mode 100644 index 075d9ea..0000000 --- a/docs/docs.go +++ /dev/null @@ -1,7455 +0,0 @@ -// Package docs Code generated by swaggo/swag. DO NOT EDIT -package docs - -import "github.com/swaggo/swag" - -const docTemplate = `{ - "schemes": {{ marshal .Schemes }}, - "swagger": "2.0", - "info": { - "description": "{{escape .Description}}", - "title": "{{.Title}}", - "contact": {}, - "version": "{{.Version}}" - }, - "host": "{{.Host}}", - "basePath": "{{.BasePath}}", - "paths": { - "/api/healthz": { - "get": { - "description": "Returns a 200 if the service is up", - "consumes": [ - "application/json" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "health" - ], - "summary": "Basic health check", - "responses": { - "200": { - "description": "ok", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/admin/users": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns paginated list of users (admin only)", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "Admin: list all users", - "parameters": [ - { - "type": "integer", - "description": "Page number (1-based)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size (max 200)", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/authn.ListUsersOut" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "Admin: create user", - "parameters": [ - { - "description": "payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/authn.AdminCreateUserRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/authn.userOut" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - }, - "409": { - "description": "conflict", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/admin/users/{userId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "tags": [ - "admin" - ], - "summary": "Admin: delete user", - "parameters": [ - { - "type": "string", - "description": "User ID", - "name": "userId", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "409": { - "description": "conflict", - "schema": { - "type": "string" - } - } - } - }, - "patch": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "Admin: update user", - "parameters": [ - { - "type": "string", - "description": "User ID", - "name": "userId", - "in": "path", - "required": true - }, - { - "description": "payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/authn.AdminUpdateUserRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/authn.userOut" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "409": { - "description": "conflict", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/annotations": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns annotations for the organization in X-Org-ID. Filters: ` + "`" + `name` + "`" + `, ` + "`" + `value` + "`" + `, and ` + "`" + `q` + "`" + ` (name contains). Add ` + "`" + `include=node_pools` + "`" + ` to include linked node pools.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "List annotations (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Exact name", - "name": "name", - "in": "query" - }, - { - "type": "string", - "description": "Exact value", - "name": "value", - "in": "query" - }, - { - "type": "string", - "description": "name contains (case-insensitive)", - "name": "q", - "in": "query" - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/annotations.annotationResponse" - } - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "organization required", - "schema": { - "type": "string" - } - }, - "500": { - "description": "failed to list annotations", - "schema": { - "type": "string" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Creates an annotation. Optionally link to node pools.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "Create annotation (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "description": "Annotation payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/annotations.createAnnotationRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/annotations.annotationResponse" - } - }, - "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" - } - } - } - } - }, - "/api/v1/annotations/{id}": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns one annotation. Add ` + "`" + `include=node_pools` + "`" + ` to include node pools.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "Get annotation by ID (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Annotation ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/annotations.annotationResponse" - } - }, - "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": [] - } - ], - "description": "Permanently deletes the annotation.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "Delete annotation (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Annotation 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": [] - } - ], - "description": "Partially update annotation fields.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "Update annotation (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Annotation ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Fields to update", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/annotations.updateAnnotationRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/annotations.annotationResponse" - } - }, - "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" - } - } - } - } - }, - "/api/v1/annotations/{id}/node_pools": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns node pools attached to the annotation. Supports ` + "`" + `q` + "`" + ` (name contains, case-insensitive).", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "List node pools linked to an annotation (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Annotation ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Name contains (case-insensitive)", - "name": "q", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/annotations.nodePoolBrief" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Links the annotation to one or more node pools in the same organization.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "Attach annotation to node pools (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Annotation ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "IDs to attach", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/annotations.addAnnotationToNodePool" - } - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/annotations.annotationResponse" - } - }, - "400": { - "description": "invalid id / invalid json / invalid node_pool_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/annotations/{id}/node_pools/{poolId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Unlinks the annotation from the specified node pool.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "Detach annotation from a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Annotation 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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/introspect": { - "post": { - "description": "Returns whether the token is active and basic metadata", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "auth" - ], - "summary": "Introspect a token", - "parameters": [ - { - "description": "token", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": true - } - } - } - } - }, - "/api/v1/auth/login": { - "post": { - "description": "Authenticates a user and returns a JWT bearer token", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "auth" - ], - "summary": "Authenticate and return a token", - "parameters": [ - { - "description": "User login input", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/authn.LoginInput" - } - } - ], - "responses": { - "200": { - "description": "token", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/logout": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Revoke a refresh token", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "auth" - ], - "summary": "Logout user", - "parameters": [ - { - "description": "refresh_token", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/logout_all": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Revokes all active refresh tokens for the authenticated user", - "produces": [ - "text/plain" - ], - "tags": [ - "auth" - ], - "summary": "Logout from all sessions", - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/me": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns the authenticated user's profile and auth context", - "produces": [ - "application/json" - ], - "tags": [ - "auth" - ], - "summary": "Get authenticated user info", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/authn.MeResponse" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/password/change": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Changes the password for the authenticated user", - "consumes": [ - "application/json" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "auth" - ], - "summary": "Change password", - "parameters": [ - { - "description": "current_password, new_password", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/password/forgot": { - "post": { - "description": "Sends a reset token to the user's email address", - "consumes": [ - "application/json" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "auth" - ], - "summary": "Request password reset", - "parameters": [ - { - "description": "email", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/password/reset": { - "post": { - "description": "Resets the password using a valid reset token", - "consumes": [ - "application/json" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "auth" - ], - "summary": "Confirm password reset", - "parameters": [ - { - "description": "token, new_password", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/refresh": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Use a refresh token to obtain a new access token", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "auth" - ], - "summary": "Refresh access token", - "parameters": [ - { - "description": "refresh_token", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "200": { - "description": "new access token", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/refresh/rotate": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Exchanges a valid refresh token for a new access and refresh token, revoking the old one", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "auth" - ], - "summary": "Rotate refresh token", - "parameters": [ - { - "description": "refresh_token", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "200": { - "description": "access_token, refresh_token", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/register": { - "post": { - "description": "Registers a new user and stores credentials", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "auth" - ], - "summary": "Register a new user", - "parameters": [ - { - "description": "User registration input", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/authn.RegisterInput" - } - } - ], - "responses": { - "201": { - "description": "created", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/verify": { - "get": { - "description": "Verifies the user's email using a token (often from an emailed link)", - "produces": [ - "text/plain" - ], - "tags": [ - "auth" - ], - "summary": "Verify email address", - "parameters": [ - { - "type": "string", - "description": "verification token", - "name": "token", - "in": "query", - "required": true - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/verify/resend": { - "post": { - "description": "Sends a new email verification token if needed", - "consumes": [ - "application/json" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "auth" - ], - "summary": "Resend email verification", - "parameters": [ - { - "description": "email", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/clusters": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns clusters for the organization in X-Org-ID. Add ` + "`" + `include=node_pools,bastion` + "`" + ` to expand. Filter by ` + "`" + `q` + "`" + ` (name contains).", - "produces": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "List clusters (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Name contains (case-insensitive)", - "name": "q", - "in": "query" - }, - { - "type": "string", - "description": "Optional: node_pools,bastion", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/clusters.clusterResponse" - } - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "organization required", - "schema": { - "type": "string" - } - }, - "500": { - "description": "failed to list clusters", - "schema": { - "type": "string" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Creates a cluster and optionally links node pools and a bastion server. If ` + "`" + `kubeconfig` + "`" + ` is provided, it will be encrypted per-organization and stored securely (never returned).", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "Create cluster (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "description": "payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/clusters.createClusterRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/clusters.clusterResponse" - } - }, - "400": { - "description": "invalid json / invalid node_pool_ids / invalid bastion_server_id", - "schema": { - "type": "string" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "organization required", - "schema": { - "type": "string" - } - }, - "500": { - "description": "create failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/clusters/{id}": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns one cluster. Add ` + "`" + `include=node_pools,bastion` + "`" + ` to expand.", - "produces": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "Get cluster by ID (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Optional: node_pools,bastion", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/clusters.clusterResponse" - } - }, - "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": [] - } - ], - "tags": [ - "clusters" - ], - "summary": "Delete cluster (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster 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": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "Update cluster (org scoped). If ` + "`" + `kubeconfig` + "`" + ` is provided and non-empty, it will be encrypted per-organization and stored (never returned). Sending an empty string for ` + "`" + `kubeconfig` + "`" + ` is ignored (no change).", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/clusters.updateClusterRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/clusters.clusterResponse" - } - }, - "400": { - "description": "invalid id / invalid json / invalid bastion_server_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" - } - } - } - } - }, - "/api/v1/clusters/{id}/bastion": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "Get cluster bastion (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster ID (UUID)", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/clusters.serverBrief" - } - }, - "204": { - "description": "No Content (no bastion set)", - "schema": { - "type": "string" - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "Set/replace cluster bastion (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "server_id with role=bastion", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/clusters.setBastionRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "invalid id / invalid server_id / server not bastion", - "schema": { - "type": "string" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "organization required", - "schema": { - "type": "string" - } - }, - "404": { - "description": "cluster or server not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "update failed", - "schema": { - "type": "string" - } - } - } - }, - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "tags": [ - "clusters" - ], - "summary": "Clear cluster bastion (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster 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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "update failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/clusters/{id}/node_pools": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "List node pools attached to a cluster (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Name contains (case-insensitive)", - "name": "q", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/clusters.nodePoolBrief" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "Attach node pools to cluster (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "node_pool_ids", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/clusters.attachNodePoolsRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "invalid id / invalid node_pool_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/clusters/{id}/node_pools/{poolId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "tags": [ - "clusters" - ], - "summary": "Detach one node pool from a cluster (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "poolId", - "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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/jobs/active": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Currently running jobs (limit default 100)", - "produces": [ - "application/json" - ], - "tags": [ - "jobs" - ], - "summary": "Active jobs", - "parameters": [ - { - "type": "integer", - "default": 100, - "description": "Max rows", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/jobs.JobListItem" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/jobs/enqueue": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Schedules a job on a queue with optional args/schedule", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "jobs" - ], - "summary": "Manually enqueue a job", - "parameters": [ - { - "description": "Enqueue request", - "name": "payload", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/jobs.EnqueueReq" - } - } - ], - "responses": { - "202": { - "description": "Accepted", - "schema": { - "$ref": "#/definitions/jobs.EnqueueResp" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/jobs/failures": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Failed jobs ordered by most recent (limit default 100)", - "produces": [ - "application/json" - ], - "tags": [ - "jobs" - ], - "summary": "Recent failures", - "parameters": [ - { - "type": "integer", - "default": 100, - "description": "Max rows", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/jobs.JobListItem" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/jobs/kpi": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Aggregated counters across all queues", - "produces": [ - "application/json" - ], - "tags": [ - "jobs" - ], - "summary": "Jobs KPI", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/jobs.KPI" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/jobs/queues": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Counts and avg duration per queue (last 24h)", - "produces": [ - "application/json" - ], - "tags": [ - "jobs" - ], - "summary": "Per-queue rollups", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/jobs.QueueRollup" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/jobs/{id}/cancel": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Cancels running or scheduled jobs", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "jobs" - ], - "summary": "Cancel a job", - "parameters": [ - { - "type": "string", - "description": "Job ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/jobs/{id}/retry": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Calls Archer ScheduleNow on the job id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "jobs" - ], - "summary": "Retry a job immediately", - "parameters": [ - { - "type": "string", - "description": "Job ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/labels": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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" - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/labels.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": [] - } - ], - "description": "Creates a label. Optionally link to node pools.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "labels" - ], - "summary": "Create label (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "description": "Label payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/labels.createLabelRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/labels.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" - } - } - } - } - }, - "/api/v1/labels/{id}": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns one label. Add ` + "`" + `include=node_pools` + "`" + ` to include node groups.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "labels" - ], - "summary": "Get label by ID (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Label ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/labels.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": [] - } - ], - "description": "Permanently deletes the label.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "labels" - ], - "summary": "Delete label (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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": [] - } - ], - "description": "Partially update label fields.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "labels" - ], - "summary": "Update label (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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/labels.updateLabelRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/labels.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" - } - } - } - } - }, - "/api/v1/labels/{id}/node_pools": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns node pools attached to the label. Supports ` + "`" + `q` + "`" + ` (name contains, case-insensitive).", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "labels" - ], - "summary": "List node pools linked to a label (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Label ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Name contains (case-insensitive)", - "name": "q", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/labels.nodePoolBrief" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Links the label to one or more node pools in the same organization.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "labels" - ], - "summary": "Attach label to node pools (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Label ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "IDs to attach", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/labels.addLabelToPoolRequest" - } - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/labels.labelResponse" - } - }, - "400": { - "description": "invalid id / invalid json / invalid node_pool_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/labels/{id}/node_pools/{poolId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Unlinks the label from the specified node pool.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "labels" - ], - "summary": "Detach label from a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Label ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "poolId", - "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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns node pools for the organization in X-Org-ID. Add ` + "`" + `include=servers` + "`" + ` to include attached servers. Filter by ` + "`" + `q` + "`" + ` (name contains).", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "List node pools (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Name contains (case-insensitive)", - "name": "q", - "in": "query" - }, - { - "type": "string", - "description": "Optional: servers", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/nodepools.nodePoolResponse" - } - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "organization required", - "schema": { - "type": "string" - } - }, - "500": { - "description": "failed to list node groups", - "schema": { - "type": "string" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Creates a node group. Optionally attach initial servers.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Create node group (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "description": "NodeGroup payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/nodepools.createNodePoolRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/nodepools.nodePoolResponse" - } - }, - "400": { - "description": "invalid json / missing fields / invalid server_ids", - "schema": { - "type": "string" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "organization required", - "schema": { - "type": "string" - } - }, - "500": { - "description": "create failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns one node group. Add ` + "`" + `include=servers` + "`" + ` to include servers.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Get node group by ID (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Group ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Optional: servers", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/nodepools.nodePoolResponse" - } - }, - "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": [] - } - ], - "description": "Permanently deletes the node pool.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Delete node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Group 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": [] - } - ], - "description": "Partially update node pool fields.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Update node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Fields to update", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/nodepools.updateNodePoolRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/nodepools.nodePoolResponse" - } - }, - "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" - } - } - } - } - }, - "/api/v1/node-pools/{id}/annotations": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "List annotations attached to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/nodepools.annotationBrief" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Attach annotations to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Annotation IDs to attach", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/nodepools.attachAnnotationsRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "invalid id / invalid annotation_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}/annotations/{annotationId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Detach one annotation from a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Annotation ID (UUID)", - "name": "annotationId", - "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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}/labels": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "List labels attached to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/nodepools.labelBrief" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Attach labels to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Label IDs to attach", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/nodepools.attachLabelsRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "invalid id / invalid label_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}/labels/{labelId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Detach one label from a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Label ID (UUID)", - "name": "labelId", - "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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}/servers": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "List servers attached to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Group ID (UUID)", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/nodepools.serverBrief" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Attach servers to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Group ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Server IDs to attach", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/nodepools.attachServersRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "invalid id / invalid server_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}/servers/{serverId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Detach one server from a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Server ID (UUID)", - "name": "serverId", - "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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}/taints": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "List taints attached to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/nodepools.taintBrief" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Attach taints to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Taint IDs to attach", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/nodepools.attachTaintsRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "invalid id / invalid taint_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}/taints/{taintId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Detach one taint from a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Taint ID (UUID)", - "name": "taintId", - "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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/orgs": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "organizations" - ], - "summary": "List organizations for user", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.Organization" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Creates a new organization and assigns the authenticated user as an admin member", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "organizations" - ], - "summary": "Create a new organization", - "parameters": [ - { - "type": "string", - "description": "Optional organization context (ignored for creation)", - "name": "X-Org-ID", - "in": "header" - }, - { - "description": "Organization Input", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/orgs.OrgInput" - } - } - ], - "responses": { - "200": { - "description": "organization_id", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "400": { - "description": "invalid input", - "schema": { - "type": "string" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/orgs/invite": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "organizations" - ], - "summary": "Invite user to organization", - "parameters": [ - { - "description": "Invite input", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/orgs.InviteInput" - } - }, - { - "type": "string", - "description": "Organization context", - "name": "X-Org-ID", - "in": "header", - "required": true - } - ], - "responses": { - "201": { - "description": "invited", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/orgs/members": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns a list of all members in the current organization", - "produces": [ - "application/json" - ], - "tags": [ - "organizations" - ], - "summary": "List organization members", - "parameters": [ - { - "type": "string", - "description": "Organization context", - "name": "X-Org-ID", - "in": "header", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.Member" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/orgs/members/{userId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "tags": [ - "organizations" - ], - "summary": "Remove member from organization", - "parameters": [ - { - "type": "string", - "description": "User ID", - "name": "userId", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "deleted", - "schema": { - "type": "string" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/orgs/{orgId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "tags": [ - "organizations" - ], - "summary": "Delete organization", - "parameters": [ - { - "type": "string", - "description": "Organization ID", - "name": "orgId", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "deleted", - "schema": { - "type": "string" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - } - } - }, - "patch": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "organizations" - ], - "summary": "Update organization metadata", - "parameters": [ - { - "type": "string", - "description": "Org ID", - "name": "orgId", - "in": "path", - "required": true - }, - { - "description": "Organization data", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/orgs.OrgInput" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/models.Organization" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/servers": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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/servers.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": [] - } - ], - "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)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "description": "Server payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/servers.createServerRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/servers.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" - } - } - } - } - }, - "/api/v1/servers/{id}": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns one server in the given organization.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "servers" - ], - "summary": "Get server by ID (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Server ID (UUID)", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/servers.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": [] - } - ], - "description": "Permanently deletes the server.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "servers" - ], - "summary": "Delete server (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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": [] - } - ], - "description": "Partially update fields; changing ssh_key_id validates ownership.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "servers" - ], - "summary": "Update server (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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/servers.updateServerRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/servers.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" - } - } - } - } - }, - "/api/v1/ssh": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/ssh.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": [] - } - ], - "description": "Generates an RSA keypair, saves it, and returns metadata. Optionally set ` + "`" + `download` + "`" + ` to \"public\", \"private\", or \"both\" to download files immediately.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ssh" - ], - "summary": "Create ssh keypair (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "description": "Key generation options", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/ssh.createSSHRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/ssh.sshResponse" - }, - "headers": { - "Content-Disposition": { - "type": "string", - "description": "When download is requested" - } - } - }, - "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" - } - } - } - } - }, - "/api/v1/ssh/{id}": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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)", - "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 - }, - { - "type": "boolean", - "description": "Reveal private key PEM", - "name": "reveal", - "in": "query" - } - ], - "responses": { - "200": { - "description": "When reveal=true", - "schema": { - "$ref": "#/definitions/ssh.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": [] - } - ], - "description": "Permanently deletes a keypair.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ssh" - ], - "summary": "Delete ssh keypair (org scoped)", - "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 - } - ], - "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" - } - } - } - } - }, - "/api/v1/ssh/{id}/download": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Download ` + "`" + `part=public|private|both` + "`" + ` of the keypair. ` + "`" + `both` + "`" + ` returns a zip file.", - "produces": [ - "text/plain" - ], - "tags": [ - "ssh" - ], - "summary": "Download ssh key files by ID (org scoped)", - "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" - } - } - } - } - }, - "/api/v1/taints": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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 taints (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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" - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/taints.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": [] - } - ], - "description": "Creates a taint. Optionally link to node pools.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "taints" - ], - "summary": "Create node taint (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "description": "Taint payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/taints.createTaintRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/taints.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" - } - } - } - } - }, - "/api/v1/taints/{id}": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns one taint. Add ` + "`" + `include=node_pools` + "`" + ` to include node pools.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "taints" - ], - "summary": "Get node taint by ID (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Taint ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/taints.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": [] - } - ], - "description": "Permanently deletes the taint.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "taints" - ], - "summary": "Delete taint (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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": [] - } - ], - "description": "Partially update taint fields.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "taints" - ], - "summary": "Update node taint (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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/taints.updateTaintRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/taints.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" - } - } - } - } - }, - "/api/v1/taints/{id}/node_pools": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns node pools attached to the taint. Supports ` + "`" + `q` + "`" + ` (name contains, case-insensitive).", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "taints" - ], - "summary": "List node pools linked to a taint (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Taint ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Name contains (case-insensitive)", - "name": "q", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/taints.nodePoolResponse" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Links the taint to one or more node pools in the same organization.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "taints" - ], - "summary": "Attach taint to node pools (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Taint ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "IDs to attach", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/taints.addTaintToPoolRequest" - } - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/taints.taintResponse" - } - }, - "400": { - "description": "invalid id / invalid json / invalid node_pool_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/taints/{id}/node_pools/{poolId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Unlinks the taint from the specified node pool.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "taints" - ], - "summary": "Detach taint from a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Taint ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "poolId", - "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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - } - }, - "definitions": { - "annotations.addAnnotationToNodePool": { - "type": "object", - "properties": { - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "annotations.annotationResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "node_pools": { - "type": "array", - "items": { - "$ref": "#/definitions/annotations.nodePoolBrief" - } - }, - "value": { - "type": "string" - } - } - }, - "annotations.createAnnotationRequest": { - "type": "object", - "properties": { - "key": { - "type": "string" - }, - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "value": { - "type": "string" - } - } - }, - "annotations.nodePoolBrief": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - } - } - }, - "annotations.updateAnnotationRequest": { - "type": "object", - "properties": { - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "authn.AdminCreateUserRequest": { - "type": "object", - "properties": { - "email": { - "type": "string", - "example": "jane@example.com" - }, - "name": { - "type": "string", - "example": "Jane Doe" - }, - "password": { - "type": "string", - "example": "Secret123!" - }, - "role": { - "description": "Role allowed values: \"user\" or \"admin\"", - "type": "string", - "enum": [ - "user", - "admin" - ], - "example": "user" - } - } - }, - "authn.AdminUpdateUserRequest": { - "type": "object", - "properties": { - "email": { - "type": "string", - "example": "jane@example.com" - }, - "name": { - "type": "string", - "example": "Jane Doe" - }, - "password": { - "type": "string", - "example": "NewSecret123!" - }, - "role": { - "type": "string", - "enum": [ - "user", - "admin" - ], - "example": "admin" - } - } - }, - "authn.AuthClaimsDTO": { - "type": "object", - "properties": { - "aud": { - "type": "array", - "items": { - "type": "string" - } - }, - "exp": { - "type": "integer" - }, - "iat": { - "type": "integer" - }, - "iss": { - "type": "string" - }, - "nbf": { - "type": "integer" - }, - "orgs": { - "type": "array", - "items": { - "type": "string" - } - }, - "roles": { - "type": "array", - "items": { - "type": "string" - } - }, - "sub": { - "type": "string" - } - } - }, - "authn.ListUsersOut": { - "type": "object", - "properties": { - "page": { - "type": "integer" - }, - "page_size": { - "type": "integer" - }, - "total": { - "type": "integer" - }, - "users": { - "type": "array", - "items": { - "$ref": "#/definitions/authn.UserListItem" - } - } - } - }, - "authn.LoginInput": { - "type": "object", - "properties": { - "email": { - "type": "string", - "example": "me@here.com" - }, - "password": { - "type": "string", - "example": "123456" - } - } - }, - "authn.MeResponse": { - "type": "object", - "properties": { - "claims": { - "$ref": "#/definitions/authn.AuthClaimsDTO" - }, - "org_role": { - "type": "string" - }, - "organization_id": { - "type": "string" - }, - "user_id": { - "$ref": "#/definitions/authn.UserDTO" - } - } - }, - "authn.RegisterInput": { - "type": "object", - "properties": { - "email": { - "type": "string", - "example": "me@here.com" - }, - "name": { - "type": "string", - "example": "My Name" - }, - "password": { - "type": "string", - "example": "123456" - } - } - }, - "authn.UserDTO": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "email": { - "type": "string" - }, - "email_verified": { - "type": "boolean" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "role": { - "$ref": "#/definitions/models.Role" - }, - "updated_at": { - "type": "string" - } - } - }, - "authn.UserListItem": { - "type": "object", - "properties": { - "created_at": {}, - "email": { - "type": "string" - }, - "email_verified": { - "type": "boolean" - }, - "id": {}, - "name": { - "type": "string" - }, - "role": { - "type": "string" - }, - "updated_at": {} - } - }, - "authn.userOut": { - "type": "object", - "properties": { - "created_at": {}, - "email": { - "type": "string" - }, - "email_verified": { - "type": "boolean" - }, - "id": {}, - "name": { - "type": "string" - }, - "role": { - "type": "string" - }, - "updated_at": {} - } - }, - "clusters.annotationBrief": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "clusters.attachNodePoolsRequest": { - "type": "object", - "properties": { - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "clusters.clusterResponse": { - "type": "object", - "properties": { - "bastion_server": { - "$ref": "#/definitions/clusters.serverBrief" - }, - "cluster_load_balancer": { - "type": "string" - }, - "control_load_balancer": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "node_pools": { - "type": "array", - "items": { - "$ref": "#/definitions/clusters.nodePoolBrief" - } - }, - "provider": { - "type": "string" - }, - "region": { - "type": "string" - }, - "status": { - "type": "string" - } - } - }, - "clusters.createClusterRequest": { - "type": "object", - "properties": { - "bastion_server_id": { - "type": "string" - }, - "cluster_load_balancer": { - "type": "string" - }, - "control_load_balancer": { - "type": "string" - }, - "kubeconfig": { - "type": "string" - }, - "name": { - "type": "string" - }, - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "provider": { - "type": "string" - }, - "region": { - "type": "string" - } - } - }, - "clusters.labelBrief": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "clusters.nodePoolBrief": { - "type": "object", - "properties": { - "annotations": { - "type": "array", - "items": { - "$ref": "#/definitions/clusters.annotationBrief" - } - }, - "id": { - "type": "string" - }, - "labels": { - "type": "array", - "items": { - "$ref": "#/definitions/clusters.labelBrief" - } - }, - "name": { - "type": "string" - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/definitions/clusters.serverBrief" - } - }, - "taints": { - "type": "array", - "items": { - "$ref": "#/definitions/clusters.taintBrief" - } - } - } - }, - "clusters.serverBrief": { - "type": "object", - "properties": { - "hostname": { - "type": "string" - }, - "id": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "role": { - "type": "string" - }, - "status": { - "type": "string" - } - } - }, - "clusters.setBastionRequest": { - "type": "object", - "properties": { - "server_id": { - "type": "string" - } - } - }, - "clusters.taintBrief": { - "type": "object", - "properties": { - "effect": { - "type": "string" - }, - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "clusters.updateClusterRequest": { - "type": "object", - "properties": { - "bastion_server_id": { - "type": "string" - }, - "cluster_load_balancer": { - "type": "string" - }, - "control_load_balancer": { - "type": "string" - }, - "kubeconfig": { - "type": "string" - }, - "name": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "region": { - "type": "string" - }, - "status": { - "type": "string" - } - } - }, - "jobs.EnqueueReq": { - "type": "object" - }, - "jobs.EnqueueResp": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - } - }, - "jobs.JobListItem": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "last_error": { - "type": "string" - }, - "max_retry": { - "type": "integer" - }, - "queue_name": { - "type": "string" - }, - "retry_count": { - "type": "integer" - }, - "scheduled_at": { - "type": "string" - }, - "started_at": { - "type": "string" - }, - "status": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "jobs.KPI": { - "type": "object", - "properties": { - "dueNow": { - "type": "integer", - "format": "int64" - }, - "failed24h": { - "type": "integer", - "format": "int64" - }, - "retryable": { - "type": "integer", - "format": "int64" - }, - "runningNow": { - "type": "integer", - "format": "int64" - }, - "scheduledFuture": { - "type": "integer", - "format": "int64" - }, - "succeeded24h": { - "type": "integer", - "format": "int64" - } - } - }, - "jobs.QueueRollup": { - "type": "object", - "properties": { - "avgDurationSecs": { - "type": "number", - "format": "float64" - }, - "failed24h": { - "type": "integer", - "format": "int64" - }, - "queueName": { - "type": "string" - }, - "queuedDue": { - "type": "integer", - "format": "int64" - }, - "queuedFuture": { - "type": "integer", - "format": "int64" - }, - "running": { - "type": "integer", - "format": "int64" - }, - "success24h": { - "type": "integer", - "format": "int64" - } - } - }, - "labels.addLabelToPoolRequest": { - "type": "object", - "properties": { - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "labels.createLabelRequest": { - "type": "object", - "properties": { - "key": { - "type": "string" - }, - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "value": { - "type": "string" - } - } - }, - "labels.labelResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "node_groups": { - "type": "array", - "items": { - "$ref": "#/definitions/labels.nodePoolBrief" - } - }, - "value": { - "type": "string" - } - } - }, - "labels.nodePoolBrief": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - } - } - }, - "labels.updateLabelRequest": { - "type": "object", - "properties": { - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "models.Member": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "id": { - "type": "string" - }, - "organization": { - "$ref": "#/definitions/models.Organization" - }, - "organization_id": { - "type": "string" - }, - "role": { - "description": "e.g. admin, member", - "allOf": [ - { - "$ref": "#/definitions/models.MemberRole" - } - ] - }, - "updated_at": { - "type": "string" - }, - "user": { - "$ref": "#/definitions/models.User" - }, - "user_id": { - "type": "string" - } - } - }, - "models.MemberRole": { - "type": "string", - "enum": [ - "admin", - "member", - "user" - ], - "x-enum-varnames": [ - "MemberRoleAdmin", - "MemberRoleMember", - "MemberRoleUser" - ] - }, - "models.Organization": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "id": { - "type": "string" - }, - "logo": { - "type": "string" - }, - "metadata": { - "type": "string" - }, - "name": { - "type": "string" - }, - "slug": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "models.Role": { - "type": "string", - "enum": [ - "admin", - "user" - ], - "x-enum-varnames": [ - "RoleAdmin", - "RoleUser" - ] - }, - "models.User": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "email": { - "type": "string" - }, - "email_verified": { - "type": "boolean" - }, - "email_verified_at": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "password": { - "type": "string" - }, - "role": { - "$ref": "#/definitions/models.Role" - }, - "updated_at": { - "type": "string" - } - } - }, - "nodepools.annotationBrief": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "nodepools.attachAnnotationsRequest": { - "type": "object", - "properties": { - "annotation_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "nodepools.attachLabelsRequest": { - "type": "object", - "properties": { - "label_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "nodepools.attachServersRequest": { - "type": "object", - "properties": { - "server_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "nodepools.attachTaintsRequest": { - "type": "object", - "properties": { - "taint_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "nodepools.createNodePoolRequest": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "server_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "nodepools.labelBrief": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "nodepools.nodePoolResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/definitions/nodepools.serverBrief" - } - } - } - }, - "nodepools.serverBrief": { - "type": "object", - "properties": { - "hostname": { - "type": "string" - }, - "id": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "role": { - "type": "string" - }, - "status": { - "type": "string" - } - } - }, - "nodepools.taintBrief": { - "type": "object", - "properties": { - "effect": { - "type": "string" - }, - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "nodepools.updateNodePoolRequest": { - "type": "object", - "properties": { - "name": { - "type": "string" - } - } - }, - "orgs.InviteInput": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "role": { - "type": "string" - } - } - }, - "orgs.OrgInput": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "slug": { - "type": "string" - } - } - }, - "servers.createServerRequest": { - "type": "object", - "properties": { - "hostname": { - "type": "string" - }, - "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" - } - } - }, - "servers.serverResponse": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "hostname": { - "type": "string" - }, - "id": { - "type": "string" - }, - "ip_address": { - "type": "string" - }, - "organization_id": { - "type": "string" - }, - "role": { - "type": "string" - }, - "ssh_key_id": { - "type": "string" - }, - "ssh_user": { - "type": "string" - }, - "status": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "servers.updateServerRequest": { - "type": "object", - "properties": { - "hostname": { - "type": "string" - }, - "ip_address": { - "type": "string" - }, - "role": { - "type": "string", - "example": "master|worker|bastion" - }, - "ssh_key_id": { - "type": "string" - }, - "ssh_user": { - "type": "string" - }, - "status": { - "description": "enum: pending,provisioning,ready,failed", - "type": "string", - "example": "pending|provisioning|ready|failed" - } - } - }, - "ssh.createSSHRequest": { - "type": "object", - "properties": { - "bits": { - "type": "integer", - "example": 4096 - }, - "comment": { - "type": "string", - "example": "deploy@autoglue" - }, - "download": { - "type": "string", - "example": "both" - }, - "name": { - "type": "string" - } - } - }, - "ssh.sshResponse": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "fingerprint": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "organization_id": { - "type": "string" - }, - "public_keys": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "ssh.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_keys": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "taints.addTaintToPoolRequest": { - "type": "object", - "properties": { - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "taints.createTaintRequest": { - "type": "object", - "properties": { - "effect": { - "type": "string" - }, - "key": { - "type": "string" - }, - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "value": { - "type": "string" - } - } - }, - "taints.nodePoolBrief": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - } - } - }, - "taints.nodePoolResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/definitions/taints.serverBrief" - } - } - } - }, - "taints.serverBrief": { - "type": "object", - "properties": { - "hostname": { - "type": "string" - }, - "id": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "role": { - "type": "string" - }, - "status": { - "type": "string" - } - } - }, - "taints.taintResponse": { - "type": "object", - "properties": { - "effect": { - "type": "string" - }, - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "node_groups": { - "type": "array", - "items": { - "$ref": "#/definitions/taints.nodePoolBrief" - } - }, - "value": { - "type": "string" - } - } - }, - "taints.updateTaintRequest": { - "type": "object", - "properties": { - "effect": { - "type": "string" - }, - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - } - }, - "securityDefinitions": { - "BearerAuth": { - "type": "apiKey", - "name": "Authorization", - "in": "header" - } - } -}` - -// SwaggerInfo holds exported Swagger Info so clients can modify it -var SwaggerInfo = &swag.Spec{ - Version: "1.0", - Host: "", - BasePath: "/", - Schemes: []string{"http"}, - Title: "AutoGlue API", - Description: "API for managing K3s clusters across cloud providers", - InfoInstanceName: "swagger", - SwaggerTemplate: docTemplate, - LeftDelim: "{{", - RightDelim: "}}", -} - -func init() { - swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) -} diff --git a/docs/swagger.json b/docs/swagger.json deleted file mode 100644 index e259f76..0000000 --- a/docs/swagger.json +++ /dev/null @@ -1,7433 +0,0 @@ -{ - "schemes": [ - "http" - ], - "swagger": "2.0", - "info": { - "description": "API for managing K3s clusters across cloud providers", - "title": "AutoGlue API", - "contact": {}, - "version": "1.0" - }, - "basePath": "/", - "paths": { - "/api/healthz": { - "get": { - "description": "Returns a 200 if the service is up", - "consumes": [ - "application/json" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "health" - ], - "summary": "Basic health check", - "responses": { - "200": { - "description": "ok", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/admin/users": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns paginated list of users (admin only)", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "Admin: list all users", - "parameters": [ - { - "type": "integer", - "description": "Page number (1-based)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size (max 200)", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/authn.ListUsersOut" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "Admin: create user", - "parameters": [ - { - "description": "payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/authn.AdminCreateUserRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/authn.userOut" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - }, - "409": { - "description": "conflict", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/admin/users/{userId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "tags": [ - "admin" - ], - "summary": "Admin: delete user", - "parameters": [ - { - "type": "string", - "description": "User ID", - "name": "userId", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "409": { - "description": "conflict", - "schema": { - "type": "string" - } - } - } - }, - "patch": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "Admin: update user", - "parameters": [ - { - "type": "string", - "description": "User ID", - "name": "userId", - "in": "path", - "required": true - }, - { - "description": "payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/authn.AdminUpdateUserRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/authn.userOut" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "409": { - "description": "conflict", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/annotations": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns annotations for the organization in X-Org-ID. Filters: `name`, `value`, and `q` (name contains). Add `include=node_pools` to include linked node pools.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "List annotations (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Exact name", - "name": "name", - "in": "query" - }, - { - "type": "string", - "description": "Exact value", - "name": "value", - "in": "query" - }, - { - "type": "string", - "description": "name contains (case-insensitive)", - "name": "q", - "in": "query" - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/annotations.annotationResponse" - } - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "organization required", - "schema": { - "type": "string" - } - }, - "500": { - "description": "failed to list annotations", - "schema": { - "type": "string" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Creates an annotation. Optionally link to node pools.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "Create annotation (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "description": "Annotation payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/annotations.createAnnotationRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/annotations.annotationResponse" - } - }, - "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" - } - } - } - } - }, - "/api/v1/annotations/{id}": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns one annotation. Add `include=node_pools` to include node pools.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "Get annotation by ID (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Annotation ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/annotations.annotationResponse" - } - }, - "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": [] - } - ], - "description": "Permanently deletes the annotation.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "Delete annotation (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Annotation 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": [] - } - ], - "description": "Partially update annotation fields.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "Update annotation (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Annotation ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Fields to update", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/annotations.updateAnnotationRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/annotations.annotationResponse" - } - }, - "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" - } - } - } - } - }, - "/api/v1/annotations/{id}/node_pools": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns node pools attached to the annotation. Supports `q` (name contains, case-insensitive).", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "List node pools linked to an annotation (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Annotation ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Name contains (case-insensitive)", - "name": "q", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/annotations.nodePoolBrief" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Links the annotation to one or more node pools in the same organization.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "Attach annotation to node pools (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Annotation ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "IDs to attach", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/annotations.addAnnotationToNodePool" - } - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/annotations.annotationResponse" - } - }, - "400": { - "description": "invalid id / invalid json / invalid node_pool_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/annotations/{id}/node_pools/{poolId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Unlinks the annotation from the specified node pool.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "annotations" - ], - "summary": "Detach annotation from a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Annotation 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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/introspect": { - "post": { - "description": "Returns whether the token is active and basic metadata", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "auth" - ], - "summary": "Introspect a token", - "parameters": [ - { - "description": "token", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": true - } - } - } - } - }, - "/api/v1/auth/login": { - "post": { - "description": "Authenticates a user and returns a JWT bearer token", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "auth" - ], - "summary": "Authenticate and return a token", - "parameters": [ - { - "description": "User login input", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/authn.LoginInput" - } - } - ], - "responses": { - "200": { - "description": "token", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/logout": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Revoke a refresh token", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "auth" - ], - "summary": "Logout user", - "parameters": [ - { - "description": "refresh_token", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/logout_all": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Revokes all active refresh tokens for the authenticated user", - "produces": [ - "text/plain" - ], - "tags": [ - "auth" - ], - "summary": "Logout from all sessions", - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/me": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns the authenticated user's profile and auth context", - "produces": [ - "application/json" - ], - "tags": [ - "auth" - ], - "summary": "Get authenticated user info", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/authn.MeResponse" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/password/change": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Changes the password for the authenticated user", - "consumes": [ - "application/json" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "auth" - ], - "summary": "Change password", - "parameters": [ - { - "description": "current_password, new_password", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/password/forgot": { - "post": { - "description": "Sends a reset token to the user's email address", - "consumes": [ - "application/json" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "auth" - ], - "summary": "Request password reset", - "parameters": [ - { - "description": "email", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/password/reset": { - "post": { - "description": "Resets the password using a valid reset token", - "consumes": [ - "application/json" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "auth" - ], - "summary": "Confirm password reset", - "parameters": [ - { - "description": "token, new_password", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/refresh": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Use a refresh token to obtain a new access token", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "auth" - ], - "summary": "Refresh access token", - "parameters": [ - { - "description": "refresh_token", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "200": { - "description": "new access token", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/refresh/rotate": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Exchanges a valid refresh token for a new access and refresh token, revoking the old one", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "auth" - ], - "summary": "Rotate refresh token", - "parameters": [ - { - "description": "refresh_token", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "200": { - "description": "access_token, refresh_token", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/register": { - "post": { - "description": "Registers a new user and stores credentials", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "auth" - ], - "summary": "Register a new user", - "parameters": [ - { - "description": "User registration input", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/authn.RegisterInput" - } - } - ], - "responses": { - "201": { - "description": "created", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/verify": { - "get": { - "description": "Verifies the user's email using a token (often from an emailed link)", - "produces": [ - "text/plain" - ], - "tags": [ - "auth" - ], - "summary": "Verify email address", - "parameters": [ - { - "type": "string", - "description": "verification token", - "name": "token", - "in": "query", - "required": true - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/auth/verify/resend": { - "post": { - "description": "Sends a new email verification token if needed", - "consumes": [ - "application/json" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "auth" - ], - "summary": "Resend email verification", - "parameters": [ - { - "description": "email", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/clusters": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns clusters for the organization in X-Org-ID. Add `include=node_pools,bastion` to expand. Filter by `q` (name contains).", - "produces": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "List clusters (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Name contains (case-insensitive)", - "name": "q", - "in": "query" - }, - { - "type": "string", - "description": "Optional: node_pools,bastion", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/clusters.clusterResponse" - } - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "organization required", - "schema": { - "type": "string" - } - }, - "500": { - "description": "failed to list clusters", - "schema": { - "type": "string" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Creates a cluster and optionally links node pools and a bastion server. If `kubeconfig` is provided, it will be encrypted per-organization and stored securely (never returned).", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "Create cluster (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "description": "payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/clusters.createClusterRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/clusters.clusterResponse" - } - }, - "400": { - "description": "invalid json / invalid node_pool_ids / invalid bastion_server_id", - "schema": { - "type": "string" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "organization required", - "schema": { - "type": "string" - } - }, - "500": { - "description": "create failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/clusters/{id}": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns one cluster. Add `include=node_pools,bastion` to expand.", - "produces": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "Get cluster by ID (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Optional: node_pools,bastion", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/clusters.clusterResponse" - } - }, - "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": [] - } - ], - "tags": [ - "clusters" - ], - "summary": "Delete cluster (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster 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": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "Update cluster (org scoped). If `kubeconfig` is provided and non-empty, it will be encrypted per-organization and stored (never returned). Sending an empty string for `kubeconfig` is ignored (no change).", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/clusters.updateClusterRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/clusters.clusterResponse" - } - }, - "400": { - "description": "invalid id / invalid json / invalid bastion_server_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" - } - } - } - } - }, - "/api/v1/clusters/{id}/bastion": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "Get cluster bastion (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster ID (UUID)", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/clusters.serverBrief" - } - }, - "204": { - "description": "No Content (no bastion set)", - "schema": { - "type": "string" - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "Set/replace cluster bastion (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "server_id with role=bastion", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/clusters.setBastionRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "invalid id / invalid server_id / server not bastion", - "schema": { - "type": "string" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "organization required", - "schema": { - "type": "string" - } - }, - "404": { - "description": "cluster or server not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "update failed", - "schema": { - "type": "string" - } - } - } - }, - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "tags": [ - "clusters" - ], - "summary": "Clear cluster bastion (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster 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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "update failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/clusters/{id}/node_pools": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "List node pools attached to a cluster (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Name contains (case-insensitive)", - "name": "q", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/clusters.nodePoolBrief" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "tags": [ - "clusters" - ], - "summary": "Attach node pools to cluster (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "node_pool_ids", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/clusters.attachNodePoolsRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "invalid id / invalid node_pool_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/clusters/{id}/node_pools/{poolId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "tags": [ - "clusters" - ], - "summary": "Detach one node pool from a cluster (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Cluster ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "poolId", - "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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/jobs/active": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Currently running jobs (limit default 100)", - "produces": [ - "application/json" - ], - "tags": [ - "jobs" - ], - "summary": "Active jobs", - "parameters": [ - { - "type": "integer", - "default": 100, - "description": "Max rows", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/jobs.JobListItem" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/jobs/enqueue": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Schedules a job on a queue with optional args/schedule", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "jobs" - ], - "summary": "Manually enqueue a job", - "parameters": [ - { - "description": "Enqueue request", - "name": "payload", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/jobs.EnqueueReq" - } - } - ], - "responses": { - "202": { - "description": "Accepted", - "schema": { - "$ref": "#/definitions/jobs.EnqueueResp" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/jobs/failures": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Failed jobs ordered by most recent (limit default 100)", - "produces": [ - "application/json" - ], - "tags": [ - "jobs" - ], - "summary": "Recent failures", - "parameters": [ - { - "type": "integer", - "default": 100, - "description": "Max rows", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/jobs.JobListItem" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/jobs/kpi": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Aggregated counters across all queues", - "produces": [ - "application/json" - ], - "tags": [ - "jobs" - ], - "summary": "Jobs KPI", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/jobs.KPI" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/jobs/queues": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Counts and avg duration per queue (last 24h)", - "produces": [ - "application/json" - ], - "tags": [ - "jobs" - ], - "summary": "Per-queue rollups", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/jobs.QueueRollup" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/jobs/{id}/cancel": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Cancels running or scheduled jobs", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "jobs" - ], - "summary": "Cancel a job", - "parameters": [ - { - "type": "string", - "description": "Job ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/jobs/{id}/retry": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Calls Archer ScheduleNow on the job id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "jobs" - ], - "summary": "Retry a job immediately", - "parameters": [ - { - "type": "string", - "description": "Job ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "no content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/labels": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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" - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/labels.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": [] - } - ], - "description": "Creates a label. Optionally link to node pools.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "labels" - ], - "summary": "Create label (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "description": "Label payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/labels.createLabelRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/labels.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" - } - } - } - } - }, - "/api/v1/labels/{id}": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns one label. Add `include=node_pools` to include node groups.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "labels" - ], - "summary": "Get label by ID (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Label ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/labels.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": [] - } - ], - "description": "Permanently deletes the label.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "labels" - ], - "summary": "Delete label (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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": [] - } - ], - "description": "Partially update label fields.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "labels" - ], - "summary": "Update label (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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/labels.updateLabelRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/labels.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" - } - } - } - } - }, - "/api/v1/labels/{id}/node_pools": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns node pools attached to the label. Supports `q` (name contains, case-insensitive).", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "labels" - ], - "summary": "List node pools linked to a label (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Label ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Name contains (case-insensitive)", - "name": "q", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/labels.nodePoolBrief" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Links the label to one or more node pools in the same organization.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "labels" - ], - "summary": "Attach label to node pools (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Label ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "IDs to attach", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/labels.addLabelToPoolRequest" - } - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/labels.labelResponse" - } - }, - "400": { - "description": "invalid id / invalid json / invalid node_pool_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/labels/{id}/node_pools/{poolId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Unlinks the label from the specified node pool.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "labels" - ], - "summary": "Detach label from a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Label ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "poolId", - "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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns node pools for the organization in X-Org-ID. Add `include=servers` to include attached servers. Filter by `q` (name contains).", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "List node pools (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Name contains (case-insensitive)", - "name": "q", - "in": "query" - }, - { - "type": "string", - "description": "Optional: servers", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/nodepools.nodePoolResponse" - } - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "organization required", - "schema": { - "type": "string" - } - }, - "500": { - "description": "failed to list node groups", - "schema": { - "type": "string" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Creates a node group. Optionally attach initial servers.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Create node group (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "description": "NodeGroup payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/nodepools.createNodePoolRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/nodepools.nodePoolResponse" - } - }, - "400": { - "description": "invalid json / missing fields / invalid server_ids", - "schema": { - "type": "string" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "organization required", - "schema": { - "type": "string" - } - }, - "500": { - "description": "create failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns one node group. Add `include=servers` to include servers.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Get node group by ID (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Group ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Optional: servers", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/nodepools.nodePoolResponse" - } - }, - "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": [] - } - ], - "description": "Permanently deletes the node pool.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Delete node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Group 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": [] - } - ], - "description": "Partially update node pool fields.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Update node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Fields to update", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/nodepools.updateNodePoolRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/nodepools.nodePoolResponse" - } - }, - "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" - } - } - } - } - }, - "/api/v1/node-pools/{id}/annotations": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "List annotations attached to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/nodepools.annotationBrief" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Attach annotations to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Annotation IDs to attach", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/nodepools.attachAnnotationsRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "invalid id / invalid annotation_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}/annotations/{annotationId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Detach one annotation from a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Annotation ID (UUID)", - "name": "annotationId", - "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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}/labels": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "List labels attached to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/nodepools.labelBrief" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Attach labels to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Label IDs to attach", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/nodepools.attachLabelsRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "invalid id / invalid label_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}/labels/{labelId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Detach one label from a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Label ID (UUID)", - "name": "labelId", - "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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}/servers": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "List servers attached to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Group ID (UUID)", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/nodepools.serverBrief" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Attach servers to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Group ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Server IDs to attach", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/nodepools.attachServersRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "invalid id / invalid server_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}/servers/{serverId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Detach one server from a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Server ID (UUID)", - "name": "serverId", - "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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}/taints": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "List taints attached to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/nodepools.taintBrief" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Attach taints to a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Taint IDs to attach", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/nodepools.attachTaintsRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content", - "schema": { - "type": "string" - } - }, - "400": { - "description": "invalid id / invalid taint_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/node-pools/{id}/taints/{taintId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "node-pools" - ], - "summary": "Detach one taint from a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Taint ID (UUID)", - "name": "taintId", - "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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/orgs": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "organizations" - ], - "summary": "List organizations for user", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.Organization" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Creates a new organization and assigns the authenticated user as an admin member", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "organizations" - ], - "summary": "Create a new organization", - "parameters": [ - { - "type": "string", - "description": "Optional organization context (ignored for creation)", - "name": "X-Org-ID", - "in": "header" - }, - { - "description": "Organization Input", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/orgs.OrgInput" - } - } - ], - "responses": { - "200": { - "description": "organization_id", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "400": { - "description": "invalid input", - "schema": { - "type": "string" - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "internal error", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/orgs/invite": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "organizations" - ], - "summary": "Invite user to organization", - "parameters": [ - { - "description": "Invite input", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/orgs.InviteInput" - } - }, - { - "type": "string", - "description": "Organization context", - "name": "X-Org-ID", - "in": "header", - "required": true - } - ], - "responses": { - "201": { - "description": "invited", - "schema": { - "type": "string" - } - }, - "400": { - "description": "bad request", - "schema": { - "type": "string" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/orgs/members": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns a list of all members in the current organization", - "produces": [ - "application/json" - ], - "tags": [ - "organizations" - ], - "summary": "List organization members", - "parameters": [ - { - "type": "string", - "description": "Organization context", - "name": "X-Org-ID", - "in": "header", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.Member" - } - } - }, - "401": { - "description": "unauthorized", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/orgs/members/{userId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "tags": [ - "organizations" - ], - "summary": "Remove member from organization", - "parameters": [ - { - "type": "string", - "description": "User ID", - "name": "userId", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "deleted", - "schema": { - "type": "string" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/orgs/{orgId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "tags": [ - "organizations" - ], - "summary": "Delete organization", - "parameters": [ - { - "type": "string", - "description": "Organization ID", - "name": "orgId", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "deleted", - "schema": { - "type": "string" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - } - } - }, - "patch": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "organizations" - ], - "summary": "Update organization metadata", - "parameters": [ - { - "type": "string", - "description": "Org ID", - "name": "orgId", - "in": "path", - "required": true - }, - { - "description": "Organization data", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/orgs.OrgInput" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/models.Organization" - } - }, - "403": { - "description": "forbidden", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/servers": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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/servers.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": [] - } - ], - "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)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "description": "Server payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/servers.createServerRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/servers.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" - } - } - } - } - }, - "/api/v1/servers/{id}": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns one server in the given organization.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "servers" - ], - "summary": "Get server by ID (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Server ID (UUID)", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/servers.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": [] - } - ], - "description": "Permanently deletes the server.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "servers" - ], - "summary": "Delete server (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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": [] - } - ], - "description": "Partially update fields; changing ssh_key_id validates ownership.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "servers" - ], - "summary": "Update server (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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/servers.updateServerRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/servers.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" - } - } - } - } - }, - "/api/v1/ssh": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/ssh.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": [] - } - ], - "description": "Generates an RSA keypair, saves it, and returns metadata. Optionally set `download` to \"public\", \"private\", or \"both\" to download files immediately.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ssh" - ], - "summary": "Create ssh keypair (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "description": "Key generation options", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/ssh.createSSHRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/ssh.sshResponse" - }, - "headers": { - "Content-Disposition": { - "type": "string", - "description": "When download is requested" - } - } - }, - "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" - } - } - } - } - }, - "/api/v1/ssh/{id}": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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)", - "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 - }, - { - "type": "boolean", - "description": "Reveal private key PEM", - "name": "reveal", - "in": "query" - } - ], - "responses": { - "200": { - "description": "When reveal=true", - "schema": { - "$ref": "#/definitions/ssh.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": [] - } - ], - "description": "Permanently deletes a keypair.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ssh" - ], - "summary": "Delete ssh keypair (org scoped)", - "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 - } - ], - "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" - } - } - } - } - }, - "/api/v1/ssh/{id}/download": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Download `part=public|private|both` of the keypair. `both` returns a zip file.", - "produces": [ - "text/plain" - ], - "tags": [ - "ssh" - ], - "summary": "Download ssh key files by ID (org scoped)", - "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" - } - } - } - } - }, - "/api/v1/taints": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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 taints (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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" - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/taints.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": [] - } - ], - "description": "Creates a taint. Optionally link to node pools.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "taints" - ], - "summary": "Create node taint (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "description": "Taint payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/taints.createTaintRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/taints.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" - } - } - } - } - }, - "/api/v1/taints/{id}": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns one taint. Add `include=node_pools` to include node pools.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "taints" - ], - "summary": "Get node taint by ID (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Node Taint ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/taints.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": [] - } - ], - "description": "Permanently deletes the taint.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "taints" - ], - "summary": "Delete taint (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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": [] - } - ], - "description": "Partially update taint fields.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "taints" - ], - "summary": "Update node taint (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "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/taints.updateTaintRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/taints.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" - } - } - } - } - }, - "/api/v1/taints/{id}/node_pools": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Returns node pools attached to the taint. Supports `q` (name contains, case-insensitive).", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "taints" - ], - "summary": "List node pools linked to a taint (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Taint ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Name contains (case-insensitive)", - "name": "q", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/taints.nodePoolResponse" - } - } - }, - "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" - } - } - } - }, - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Links the taint to one or more node pools in the same organization.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "taints" - ], - "summary": "Attach taint to node pools (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Taint ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "IDs to attach", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/taints.addTaintToPoolRequest" - } - }, - { - "type": "string", - "description": "Optional: node_pools", - "name": "include", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/taints.taintResponse" - } - }, - "400": { - "description": "invalid id / invalid json / invalid node_pool_ids", - "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": "attach failed", - "schema": { - "type": "string" - } - } - } - } - }, - "/api/v1/taints/{id}/node_pools/{poolId}": { - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Unlinks the taint from the specified node pool.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "taints" - ], - "summary": "Detach taint from a node pool (org scoped)", - "parameters": [ - { - "type": "string", - "description": "Organization UUID", - "name": "X-Org-ID", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Taint ID (UUID)", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Node Pool ID (UUID)", - "name": "poolId", - "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" - } - }, - "404": { - "description": "not found", - "schema": { - "type": "string" - } - }, - "500": { - "description": "detach failed", - "schema": { - "type": "string" - } - } - } - } - } - }, - "definitions": { - "annotations.addAnnotationToNodePool": { - "type": "object", - "properties": { - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "annotations.annotationResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "node_pools": { - "type": "array", - "items": { - "$ref": "#/definitions/annotations.nodePoolBrief" - } - }, - "value": { - "type": "string" - } - } - }, - "annotations.createAnnotationRequest": { - "type": "object", - "properties": { - "key": { - "type": "string" - }, - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "value": { - "type": "string" - } - } - }, - "annotations.nodePoolBrief": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - } - } - }, - "annotations.updateAnnotationRequest": { - "type": "object", - "properties": { - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "authn.AdminCreateUserRequest": { - "type": "object", - "properties": { - "email": { - "type": "string", - "example": "jane@example.com" - }, - "name": { - "type": "string", - "example": "Jane Doe" - }, - "password": { - "type": "string", - "example": "Secret123!" - }, - "role": { - "description": "Role allowed values: \"user\" or \"admin\"", - "type": "string", - "enum": [ - "user", - "admin" - ], - "example": "user" - } - } - }, - "authn.AdminUpdateUserRequest": { - "type": "object", - "properties": { - "email": { - "type": "string", - "example": "jane@example.com" - }, - "name": { - "type": "string", - "example": "Jane Doe" - }, - "password": { - "type": "string", - "example": "NewSecret123!" - }, - "role": { - "type": "string", - "enum": [ - "user", - "admin" - ], - "example": "admin" - } - } - }, - "authn.AuthClaimsDTO": { - "type": "object", - "properties": { - "aud": { - "type": "array", - "items": { - "type": "string" - } - }, - "exp": { - "type": "integer" - }, - "iat": { - "type": "integer" - }, - "iss": { - "type": "string" - }, - "nbf": { - "type": "integer" - }, - "orgs": { - "type": "array", - "items": { - "type": "string" - } - }, - "roles": { - "type": "array", - "items": { - "type": "string" - } - }, - "sub": { - "type": "string" - } - } - }, - "authn.ListUsersOut": { - "type": "object", - "properties": { - "page": { - "type": "integer" - }, - "page_size": { - "type": "integer" - }, - "total": { - "type": "integer" - }, - "users": { - "type": "array", - "items": { - "$ref": "#/definitions/authn.UserListItem" - } - } - } - }, - "authn.LoginInput": { - "type": "object", - "properties": { - "email": { - "type": "string", - "example": "me@here.com" - }, - "password": { - "type": "string", - "example": "123456" - } - } - }, - "authn.MeResponse": { - "type": "object", - "properties": { - "claims": { - "$ref": "#/definitions/authn.AuthClaimsDTO" - }, - "org_role": { - "type": "string" - }, - "organization_id": { - "type": "string" - }, - "user_id": { - "$ref": "#/definitions/authn.UserDTO" - } - } - }, - "authn.RegisterInput": { - "type": "object", - "properties": { - "email": { - "type": "string", - "example": "me@here.com" - }, - "name": { - "type": "string", - "example": "My Name" - }, - "password": { - "type": "string", - "example": "123456" - } - } - }, - "authn.UserDTO": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "email": { - "type": "string" - }, - "email_verified": { - "type": "boolean" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "role": { - "$ref": "#/definitions/models.Role" - }, - "updated_at": { - "type": "string" - } - } - }, - "authn.UserListItem": { - "type": "object", - "properties": { - "created_at": {}, - "email": { - "type": "string" - }, - "email_verified": { - "type": "boolean" - }, - "id": {}, - "name": { - "type": "string" - }, - "role": { - "type": "string" - }, - "updated_at": {} - } - }, - "authn.userOut": { - "type": "object", - "properties": { - "created_at": {}, - "email": { - "type": "string" - }, - "email_verified": { - "type": "boolean" - }, - "id": {}, - "name": { - "type": "string" - }, - "role": { - "type": "string" - }, - "updated_at": {} - } - }, - "clusters.annotationBrief": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "clusters.attachNodePoolsRequest": { - "type": "object", - "properties": { - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "clusters.clusterResponse": { - "type": "object", - "properties": { - "bastion_server": { - "$ref": "#/definitions/clusters.serverBrief" - }, - "cluster_load_balancer": { - "type": "string" - }, - "control_load_balancer": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "node_pools": { - "type": "array", - "items": { - "$ref": "#/definitions/clusters.nodePoolBrief" - } - }, - "provider": { - "type": "string" - }, - "region": { - "type": "string" - }, - "status": { - "type": "string" - } - } - }, - "clusters.createClusterRequest": { - "type": "object", - "properties": { - "bastion_server_id": { - "type": "string" - }, - "cluster_load_balancer": { - "type": "string" - }, - "control_load_balancer": { - "type": "string" - }, - "kubeconfig": { - "type": "string" - }, - "name": { - "type": "string" - }, - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "provider": { - "type": "string" - }, - "region": { - "type": "string" - } - } - }, - "clusters.labelBrief": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "clusters.nodePoolBrief": { - "type": "object", - "properties": { - "annotations": { - "type": "array", - "items": { - "$ref": "#/definitions/clusters.annotationBrief" - } - }, - "id": { - "type": "string" - }, - "labels": { - "type": "array", - "items": { - "$ref": "#/definitions/clusters.labelBrief" - } - }, - "name": { - "type": "string" - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/definitions/clusters.serverBrief" - } - }, - "taints": { - "type": "array", - "items": { - "$ref": "#/definitions/clusters.taintBrief" - } - } - } - }, - "clusters.serverBrief": { - "type": "object", - "properties": { - "hostname": { - "type": "string" - }, - "id": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "role": { - "type": "string" - }, - "status": { - "type": "string" - } - } - }, - "clusters.setBastionRequest": { - "type": "object", - "properties": { - "server_id": { - "type": "string" - } - } - }, - "clusters.taintBrief": { - "type": "object", - "properties": { - "effect": { - "type": "string" - }, - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "clusters.updateClusterRequest": { - "type": "object", - "properties": { - "bastion_server_id": { - "type": "string" - }, - "cluster_load_balancer": { - "type": "string" - }, - "control_load_balancer": { - "type": "string" - }, - "kubeconfig": { - "type": "string" - }, - "name": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "region": { - "type": "string" - }, - "status": { - "type": "string" - } - } - }, - "jobs.EnqueueReq": { - "type": "object" - }, - "jobs.EnqueueResp": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - } - }, - "jobs.JobListItem": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "last_error": { - "type": "string" - }, - "max_retry": { - "type": "integer" - }, - "queue_name": { - "type": "string" - }, - "retry_count": { - "type": "integer" - }, - "scheduled_at": { - "type": "string" - }, - "started_at": { - "type": "string" - }, - "status": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "jobs.KPI": { - "type": "object", - "properties": { - "dueNow": { - "type": "integer", - "format": "int64" - }, - "failed24h": { - "type": "integer", - "format": "int64" - }, - "retryable": { - "type": "integer", - "format": "int64" - }, - "runningNow": { - "type": "integer", - "format": "int64" - }, - "scheduledFuture": { - "type": "integer", - "format": "int64" - }, - "succeeded24h": { - "type": "integer", - "format": "int64" - } - } - }, - "jobs.QueueRollup": { - "type": "object", - "properties": { - "avgDurationSecs": { - "type": "number", - "format": "float64" - }, - "failed24h": { - "type": "integer", - "format": "int64" - }, - "queueName": { - "type": "string" - }, - "queuedDue": { - "type": "integer", - "format": "int64" - }, - "queuedFuture": { - "type": "integer", - "format": "int64" - }, - "running": { - "type": "integer", - "format": "int64" - }, - "success24h": { - "type": "integer", - "format": "int64" - } - } - }, - "labels.addLabelToPoolRequest": { - "type": "object", - "properties": { - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "labels.createLabelRequest": { - "type": "object", - "properties": { - "key": { - "type": "string" - }, - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "value": { - "type": "string" - } - } - }, - "labels.labelResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "node_groups": { - "type": "array", - "items": { - "$ref": "#/definitions/labels.nodePoolBrief" - } - }, - "value": { - "type": "string" - } - } - }, - "labels.nodePoolBrief": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - } - } - }, - "labels.updateLabelRequest": { - "type": "object", - "properties": { - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "models.Member": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "id": { - "type": "string" - }, - "organization": { - "$ref": "#/definitions/models.Organization" - }, - "organization_id": { - "type": "string" - }, - "role": { - "description": "e.g. admin, member", - "allOf": [ - { - "$ref": "#/definitions/models.MemberRole" - } - ] - }, - "updated_at": { - "type": "string" - }, - "user": { - "$ref": "#/definitions/models.User" - }, - "user_id": { - "type": "string" - } - } - }, - "models.MemberRole": { - "type": "string", - "enum": [ - "admin", - "member", - "user" - ], - "x-enum-varnames": [ - "MemberRoleAdmin", - "MemberRoleMember", - "MemberRoleUser" - ] - }, - "models.Organization": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "id": { - "type": "string" - }, - "logo": { - "type": "string" - }, - "metadata": { - "type": "string" - }, - "name": { - "type": "string" - }, - "slug": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "models.Role": { - "type": "string", - "enum": [ - "admin", - "user" - ], - "x-enum-varnames": [ - "RoleAdmin", - "RoleUser" - ] - }, - "models.User": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "email": { - "type": "string" - }, - "email_verified": { - "type": "boolean" - }, - "email_verified_at": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "password": { - "type": "string" - }, - "role": { - "$ref": "#/definitions/models.Role" - }, - "updated_at": { - "type": "string" - } - } - }, - "nodepools.annotationBrief": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "nodepools.attachAnnotationsRequest": { - "type": "object", - "properties": { - "annotation_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "nodepools.attachLabelsRequest": { - "type": "object", - "properties": { - "label_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "nodepools.attachServersRequest": { - "type": "object", - "properties": { - "server_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "nodepools.attachTaintsRequest": { - "type": "object", - "properties": { - "taint_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "nodepools.createNodePoolRequest": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "server_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "nodepools.labelBrief": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "nodepools.nodePoolResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/definitions/nodepools.serverBrief" - } - } - } - }, - "nodepools.serverBrief": { - "type": "object", - "properties": { - "hostname": { - "type": "string" - }, - "id": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "role": { - "type": "string" - }, - "status": { - "type": "string" - } - } - }, - "nodepools.taintBrief": { - "type": "object", - "properties": { - "effect": { - "type": "string" - }, - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "nodepools.updateNodePoolRequest": { - "type": "object", - "properties": { - "name": { - "type": "string" - } - } - }, - "orgs.InviteInput": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "role": { - "type": "string" - } - } - }, - "orgs.OrgInput": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "slug": { - "type": "string" - } - } - }, - "servers.createServerRequest": { - "type": "object", - "properties": { - "hostname": { - "type": "string" - }, - "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" - } - } - }, - "servers.serverResponse": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "hostname": { - "type": "string" - }, - "id": { - "type": "string" - }, - "ip_address": { - "type": "string" - }, - "organization_id": { - "type": "string" - }, - "role": { - "type": "string" - }, - "ssh_key_id": { - "type": "string" - }, - "ssh_user": { - "type": "string" - }, - "status": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "servers.updateServerRequest": { - "type": "object", - "properties": { - "hostname": { - "type": "string" - }, - "ip_address": { - "type": "string" - }, - "role": { - "type": "string", - "example": "master|worker|bastion" - }, - "ssh_key_id": { - "type": "string" - }, - "ssh_user": { - "type": "string" - }, - "status": { - "description": "enum: pending,provisioning,ready,failed", - "type": "string", - "example": "pending|provisioning|ready|failed" - } - } - }, - "ssh.createSSHRequest": { - "type": "object", - "properties": { - "bits": { - "type": "integer", - "example": 4096 - }, - "comment": { - "type": "string", - "example": "deploy@autoglue" - }, - "download": { - "type": "string", - "example": "both" - }, - "name": { - "type": "string" - } - } - }, - "ssh.sshResponse": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "fingerprint": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "organization_id": { - "type": "string" - }, - "public_keys": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "ssh.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_keys": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "taints.addTaintToPoolRequest": { - "type": "object", - "properties": { - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "taints.createTaintRequest": { - "type": "object", - "properties": { - "effect": { - "type": "string" - }, - "key": { - "type": "string" - }, - "node_pool_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "value": { - "type": "string" - } - } - }, - "taints.nodePoolBrief": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - } - } - }, - "taints.nodePoolResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/definitions/taints.serverBrief" - } - } - } - }, - "taints.serverBrief": { - "type": "object", - "properties": { - "hostname": { - "type": "string" - }, - "id": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "role": { - "type": "string" - }, - "status": { - "type": "string" - } - } - }, - "taints.taintResponse": { - "type": "object", - "properties": { - "effect": { - "type": "string" - }, - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "node_groups": { - "type": "array", - "items": { - "$ref": "#/definitions/taints.nodePoolBrief" - } - }, - "value": { - "type": "string" - } - } - }, - "taints.updateTaintRequest": { - "type": "object", - "properties": { - "effect": { - "type": "string" - }, - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - } - }, - "securityDefinitions": { - "BearerAuth": { - "type": "apiKey", - "name": "Authorization", - "in": "header" - } - } -} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml deleted file mode 100644 index 4685ac8..0000000 --- a/docs/swagger.yaml +++ /dev/null @@ -1,4844 +0,0 @@ -basePath: / -definitions: - annotations.addAnnotationToNodePool: - properties: - node_pool_ids: - items: - type: string - type: array - type: object - annotations.annotationResponse: - properties: - id: - type: string - key: - type: string - node_pools: - items: - $ref: '#/definitions/annotations.nodePoolBrief' - type: array - value: - type: string - type: object - annotations.createAnnotationRequest: - properties: - key: - type: string - node_pool_ids: - items: - type: string - type: array - value: - type: string - type: object - annotations.nodePoolBrief: - properties: - id: - type: string - name: - type: string - type: object - annotations.updateAnnotationRequest: - properties: - key: - type: string - value: - type: string - type: object - authn.AdminCreateUserRequest: - properties: - email: - example: jane@example.com - type: string - name: - example: Jane Doe - type: string - password: - example: Secret123! - type: string - role: - description: 'Role allowed values: "user" or "admin"' - enum: - - user - - admin - example: user - type: string - type: object - authn.AdminUpdateUserRequest: - properties: - email: - example: jane@example.com - type: string - name: - example: Jane Doe - type: string - password: - example: NewSecret123! - type: string - role: - enum: - - user - - admin - example: admin - type: string - type: object - authn.AuthClaimsDTO: - properties: - aud: - items: - type: string - type: array - exp: - type: integer - iat: - type: integer - iss: - type: string - nbf: - type: integer - orgs: - items: - type: string - type: array - roles: - items: - type: string - type: array - sub: - type: string - type: object - authn.ListUsersOut: - properties: - page: - type: integer - page_size: - type: integer - total: - type: integer - users: - items: - $ref: '#/definitions/authn.UserListItem' - type: array - type: object - authn.LoginInput: - properties: - email: - example: me@here.com - type: string - password: - example: "123456" - type: string - type: object - authn.MeResponse: - properties: - claims: - $ref: '#/definitions/authn.AuthClaimsDTO' - org_role: - type: string - organization_id: - type: string - user_id: - $ref: '#/definitions/authn.UserDTO' - type: object - authn.RegisterInput: - properties: - email: - example: me@here.com - type: string - name: - example: My Name - type: string - password: - example: "123456" - type: string - type: object - authn.UserDTO: - properties: - created_at: - type: string - email: - type: string - email_verified: - type: boolean - id: - type: string - name: - type: string - role: - $ref: '#/definitions/models.Role' - updated_at: - type: string - type: object - authn.UserListItem: - properties: - created_at: {} - email: - type: string - email_verified: - type: boolean - id: {} - name: - type: string - role: - type: string - updated_at: {} - type: object - authn.userOut: - properties: - created_at: {} - email: - type: string - email_verified: - type: boolean - id: {} - name: - type: string - role: - type: string - updated_at: {} - type: object - clusters.annotationBrief: - properties: - id: - type: string - key: - type: string - value: - type: string - type: object - clusters.attachNodePoolsRequest: - properties: - node_pool_ids: - items: - type: string - type: array - type: object - clusters.clusterResponse: - properties: - bastion_server: - $ref: '#/definitions/clusters.serverBrief' - cluster_load_balancer: - type: string - control_load_balancer: - type: string - id: - type: string - name: - type: string - node_pools: - items: - $ref: '#/definitions/clusters.nodePoolBrief' - type: array - provider: - type: string - region: - type: string - status: - type: string - type: object - clusters.createClusterRequest: - properties: - bastion_server_id: - type: string - cluster_load_balancer: - type: string - control_load_balancer: - type: string - kubeconfig: - type: string - name: - type: string - node_pool_ids: - items: - type: string - type: array - provider: - type: string - region: - type: string - type: object - clusters.labelBrief: - properties: - id: - type: string - key: - type: string - value: - type: string - type: object - clusters.nodePoolBrief: - properties: - annotations: - items: - $ref: '#/definitions/clusters.annotationBrief' - type: array - id: - type: string - labels: - items: - $ref: '#/definitions/clusters.labelBrief' - type: array - name: - type: string - servers: - items: - $ref: '#/definitions/clusters.serverBrief' - type: array - taints: - items: - $ref: '#/definitions/clusters.taintBrief' - type: array - type: object - clusters.serverBrief: - properties: - hostname: - type: string - id: - type: string - ip: - type: string - role: - type: string - status: - type: string - type: object - clusters.setBastionRequest: - properties: - server_id: - type: string - type: object - clusters.taintBrief: - properties: - effect: - type: string - id: - type: string - key: - type: string - value: - type: string - type: object - clusters.updateClusterRequest: - properties: - bastion_server_id: - type: string - cluster_load_balancer: - type: string - control_load_balancer: - type: string - kubeconfig: - type: string - name: - type: string - provider: - type: string - region: - type: string - status: - type: string - type: object - jobs.EnqueueReq: - type: object - jobs.EnqueueResp: - properties: - id: - type: string - type: object - jobs.JobListItem: - properties: - id: - type: string - last_error: - type: string - max_retry: - type: integer - queue_name: - type: string - retry_count: - type: integer - scheduled_at: - type: string - started_at: - type: string - status: - type: string - updated_at: - type: string - type: object - jobs.KPI: - properties: - dueNow: - format: int64 - type: integer - failed24h: - format: int64 - type: integer - retryable: - format: int64 - type: integer - runningNow: - format: int64 - type: integer - scheduledFuture: - format: int64 - type: integer - succeeded24h: - format: int64 - type: integer - type: object - jobs.QueueRollup: - properties: - avgDurationSecs: - format: float64 - type: number - failed24h: - format: int64 - type: integer - queueName: - type: string - queuedDue: - format: int64 - type: integer - queuedFuture: - format: int64 - type: integer - running: - format: int64 - type: integer - success24h: - format: int64 - type: integer - type: object - labels.addLabelToPoolRequest: - properties: - node_pool_ids: - items: - type: string - type: array - type: object - labels.createLabelRequest: - properties: - key: - type: string - node_pool_ids: - items: - type: string - type: array - value: - type: string - type: object - labels.labelResponse: - properties: - id: - type: string - key: - type: string - node_groups: - items: - $ref: '#/definitions/labels.nodePoolBrief' - type: array - value: - type: string - type: object - labels.nodePoolBrief: - properties: - id: - type: string - name: - type: string - type: object - labels.updateLabelRequest: - properties: - key: - type: string - value: - type: string - type: object - models.Member: - properties: - created_at: - type: string - id: - type: string - organization: - $ref: '#/definitions/models.Organization' - organization_id: - type: string - role: - allOf: - - $ref: '#/definitions/models.MemberRole' - description: e.g. admin, member - updated_at: - type: string - user: - $ref: '#/definitions/models.User' - user_id: - type: string - type: object - models.MemberRole: - enum: - - admin - - member - - user - type: string - x-enum-varnames: - - MemberRoleAdmin - - MemberRoleMember - - MemberRoleUser - models.Organization: - properties: - created_at: - type: string - id: - type: string - logo: - type: string - metadata: - type: string - name: - type: string - slug: - type: string - updated_at: - type: string - type: object - models.Role: - enum: - - admin - - user - type: string - x-enum-varnames: - - RoleAdmin - - RoleUser - models.User: - properties: - created_at: - type: string - email: - type: string - email_verified: - type: boolean - email_verified_at: - type: string - id: - type: string - name: - type: string - password: - type: string - role: - $ref: '#/definitions/models.Role' - updated_at: - type: string - type: object - nodepools.annotationBrief: - properties: - id: - type: string - key: - type: string - value: - type: string - type: object - nodepools.attachAnnotationsRequest: - properties: - annotation_ids: - items: - type: string - type: array - type: object - nodepools.attachLabelsRequest: - properties: - label_ids: - items: - type: string - type: array - type: object - nodepools.attachServersRequest: - properties: - server_ids: - items: - type: string - type: array - type: object - nodepools.attachTaintsRequest: - properties: - taint_ids: - items: - type: string - type: array - type: object - nodepools.createNodePoolRequest: - properties: - name: - type: string - server_ids: - items: - type: string - type: array - type: object - nodepools.labelBrief: - properties: - id: - type: string - key: - type: string - value: - type: string - type: object - nodepools.nodePoolResponse: - properties: - id: - type: string - name: - type: string - servers: - items: - $ref: '#/definitions/nodepools.serverBrief' - type: array - type: object - nodepools.serverBrief: - properties: - hostname: - type: string - id: - type: string - ip: - type: string - role: - type: string - status: - type: string - type: object - nodepools.taintBrief: - properties: - effect: - type: string - id: - type: string - key: - type: string - value: - type: string - type: object - nodepools.updateNodePoolRequest: - properties: - name: - type: string - type: object - orgs.InviteInput: - properties: - email: - type: string - role: - type: string - type: object - orgs.OrgInput: - properties: - name: - type: string - slug: - type: string - type: object - servers.createServerRequest: - properties: - hostname: - type: string - ip_address: - type: string - role: - example: master|worker|bastion - type: string - ssh_key_id: - type: string - ssh_user: - type: string - status: - example: pending|provisioning|ready|failed - type: string - type: object - servers.serverResponse: - properties: - created_at: - type: string - hostname: - type: string - id: - type: string - ip_address: - type: string - organization_id: - type: string - role: - type: string - ssh_key_id: - type: string - ssh_user: - type: string - status: - type: string - updated_at: - type: string - type: object - servers.updateServerRequest: - properties: - hostname: - type: string - ip_address: - type: string - role: - example: master|worker|bastion - type: string - ssh_key_id: - type: string - ssh_user: - type: string - status: - description: 'enum: pending,provisioning,ready,failed' - example: pending|provisioning|ready|failed - type: string - type: object - ssh.createSSHRequest: - properties: - bits: - example: 4096 - type: integer - comment: - example: deploy@autoglue - type: string - download: - example: both - type: string - name: - type: string - type: object - ssh.sshResponse: - properties: - created_at: - type: string - fingerprint: - type: string - id: - type: string - name: - type: string - organization_id: - type: string - public_keys: - type: string - updated_at: - type: string - type: object - ssh.sshRevealResponse: - properties: - created_at: - type: string - fingerprint: - type: string - id: - type: string - name: - type: string - organization_id: - type: string - private_key: - type: string - public_keys: - type: string - updated_at: - type: string - type: object - taints.addTaintToPoolRequest: - properties: - node_pool_ids: - items: - type: string - type: array - type: object - taints.createTaintRequest: - properties: - effect: - type: string - key: - type: string - node_pool_ids: - items: - type: string - type: array - value: - type: string - type: object - taints.nodePoolBrief: - properties: - id: - type: string - name: - type: string - type: object - taints.nodePoolResponse: - properties: - id: - type: string - name: - type: string - servers: - items: - $ref: '#/definitions/taints.serverBrief' - type: array - type: object - taints.serverBrief: - properties: - hostname: - type: string - id: - type: string - ip: - type: string - role: - type: string - status: - type: string - type: object - taints.taintResponse: - properties: - effect: - type: string - id: - type: string - key: - type: string - node_groups: - items: - $ref: '#/definitions/taints.nodePoolBrief' - type: array - value: - type: string - type: object - taints.updateTaintRequest: - properties: - effect: - type: string - key: - type: string - value: - type: string - type: object -info: - contact: {} - description: API for managing K3s clusters across cloud providers - title: AutoGlue API - version: "1.0" -paths: - /api/healthz: - get: - consumes: - - application/json - description: Returns a 200 if the service is up - produces: - - text/plain - responses: - "200": - description: ok - schema: - type: string - summary: Basic health check - tags: - - health - /api/v1/admin/users: - get: - description: Returns paginated list of users (admin only) - parameters: - - description: Page number (1-based) - in: query - name: page - type: integer - - description: Page size (max 200) - in: query - name: page_size - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/authn.ListUsersOut' - "401": - description: unauthorized - schema: - type: string - "403": - description: forbidden - schema: - type: string - security: - - BearerAuth: [] - summary: 'Admin: list all users' - tags: - - admin - post: - consumes: - - application/json - parameters: - - description: payload - in: body - name: body - required: true - schema: - $ref: '#/definitions/authn.AdminCreateUserRequest' - produces: - - application/json - responses: - "201": - description: Created - schema: - $ref: '#/definitions/authn.userOut' - "400": - description: bad request - schema: - type: string - "401": - description: unauthorized - schema: - type: string - "403": - description: forbidden - schema: - type: string - "409": - description: conflict - schema: - type: string - security: - - BearerAuth: [] - summary: 'Admin: create user' - tags: - - admin - /api/v1/admin/users/{userId}: - delete: - parameters: - - description: User ID - in: path - name: userId - required: true - type: string - responses: - "204": - description: no content - schema: - type: string - "400": - description: bad request - schema: - type: string - "401": - description: unauthorized - schema: - type: string - "403": - description: forbidden - schema: - type: string - "404": - description: not found - schema: - type: string - "409": - description: conflict - schema: - type: string - security: - - BearerAuth: [] - summary: 'Admin: delete user' - tags: - - admin - patch: - consumes: - - application/json - parameters: - - description: User ID - in: path - name: userId - required: true - type: string - - description: payload - in: body - name: body - required: true - schema: - $ref: '#/definitions/authn.AdminUpdateUserRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/authn.userOut' - "400": - description: bad request - schema: - type: string - "401": - description: unauthorized - schema: - type: string - "403": - description: forbidden - schema: - type: string - "404": - description: not found - schema: - type: string - "409": - description: conflict - schema: - type: string - security: - - BearerAuth: [] - summary: 'Admin: update user' - tags: - - admin - /api/v1/annotations: - get: - consumes: - - application/json - description: 'Returns annotations for the organization in X-Org-ID. Filters: - `name`, `value`, and `q` (name contains). Add `include=node_pools` to include - linked node pools.' - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Exact name - in: query - name: name - type: string - - description: Exact value - in: query - name: value - type: string - - description: name contains (case-insensitive) - in: query - name: q - type: string - - description: 'Optional: node_pools' - in: query - name: include - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/annotations.annotationResponse' - type: array - "401": - description: Unauthorized - schema: - type: string - "403": - description: organization required - schema: - type: string - "500": - description: failed to list annotations - schema: - type: string - security: - - BearerAuth: [] - summary: List annotations (org scoped) - tags: - - annotations - post: - consumes: - - application/json - description: Creates an annotation. Optionally link to node pools. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Annotation payload - in: body - name: body - required: true - schema: - $ref: '#/definitions/annotations.createAnnotationRequest' - produces: - - application/json - responses: - "201": - description: Created - schema: - $ref: '#/definitions/annotations.annotationResponse' - "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 - security: - - BearerAuth: [] - summary: Create annotation (org scoped) - tags: - - annotations - /api/v1/annotations/{id}: - delete: - consumes: - - application/json - description: Permanently deletes the annotation. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Annotation ID (UUID) - in: path - name: id - required: true - type: string - produces: - - application/json - 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 - security: - - BearerAuth: [] - summary: Delete annotation (org scoped) - tags: - - annotations - get: - consumes: - - application/json - description: Returns one annotation. Add `include=node_pools` to include node - pools. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Annotation ID (UUID) - in: path - name: id - required: true - type: string - - description: 'Optional: node_pools' - in: query - name: include - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/annotations.annotationResponse' - "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 - security: - - BearerAuth: [] - summary: Get annotation by ID (org scoped) - tags: - - annotations - patch: - consumes: - - application/json - description: Partially update annotation fields. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Annotation ID (UUID) - in: path - name: id - required: true - type: string - - description: Fields to update - in: body - name: body - required: true - schema: - $ref: '#/definitions/annotations.updateAnnotationRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/annotations.annotationResponse' - "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 - security: - - BearerAuth: [] - summary: Update annotation (org scoped) - tags: - - annotations - /api/v1/annotations/{id}/node_pools: - get: - consumes: - - application/json - description: Returns node pools attached to the annotation. Supports `q` (name - contains, case-insensitive). - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Annotation ID (UUID) - in: path - name: id - required: true - type: string - - description: Name contains (case-insensitive) - in: query - name: q - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/annotations.nodePoolBrief' - type: array - "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 - security: - - BearerAuth: [] - summary: List node pools linked to an annotation (org scoped) - tags: - - annotations - post: - consumes: - - application/json - description: Links the annotation to one or more node pools in the same organization. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Annotation ID (UUID) - in: path - name: id - required: true - type: string - - description: IDs to attach - in: body - name: body - required: true - schema: - $ref: '#/definitions/annotations.addAnnotationToNodePool' - - description: 'Optional: node_pools' - in: query - name: include - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/annotations.annotationResponse' - "400": - description: invalid id / invalid json / invalid node_pool_ids - 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: attach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Attach annotation to node pools (org scoped) - tags: - - annotations - /api/v1/annotations/{id}/node_pools/{poolId}: - delete: - consumes: - - application/json - description: Unlinks the annotation from the specified node pool. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Annotation ID (UUID) - in: path - name: id - required: true - type: string - produces: - - application/json - 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 - "404": - description: not found - schema: - type: string - "500": - description: detach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Detach annotation from a node pool (org scoped) - tags: - - annotations - /api/v1/auth/introspect: - post: - consumes: - - application/json - description: Returns whether the token is active and basic metadata - parameters: - - description: token - in: body - name: body - required: true - schema: - additionalProperties: - type: string - type: object - produces: - - application/json - responses: - "200": - description: OK - schema: - additionalProperties: true - type: object - summary: Introspect a token - tags: - - auth - /api/v1/auth/login: - post: - consumes: - - application/json - description: Authenticates a user and returns a JWT bearer token - parameters: - - description: User login input - in: body - name: body - required: true - schema: - $ref: '#/definitions/authn.LoginInput' - produces: - - application/json - responses: - "200": - description: token - schema: - additionalProperties: - type: string - type: object - "401": - description: unauthorized - schema: - type: string - summary: Authenticate and return a token - tags: - - auth - /api/v1/auth/logout: - post: - consumes: - - application/json - description: Revoke a refresh token - parameters: - - description: refresh_token - in: body - name: body - required: true - schema: - additionalProperties: - type: string - type: object - produces: - - application/json - responses: - "204": - description: no content - schema: - type: string - security: - - BearerAuth: [] - summary: Logout user - tags: - - auth - /api/v1/auth/logout_all: - post: - description: Revokes all active refresh tokens for the authenticated user - produces: - - text/plain - responses: - "204": - description: no content - schema: - type: string - security: - - BearerAuth: [] - summary: Logout from all sessions - tags: - - auth - /api/v1/auth/me: - get: - description: Returns the authenticated user's profile and auth context - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/authn.MeResponse' - "401": - description: unauthorized - schema: - type: string - security: - - BearerAuth: [] - summary: Get authenticated user info - tags: - - auth - /api/v1/auth/password/change: - post: - consumes: - - application/json - description: Changes the password for the authenticated user - parameters: - - description: current_password, new_password - in: body - name: body - required: true - schema: - additionalProperties: - type: string - type: object - produces: - - text/plain - responses: - "204": - description: no content - schema: - type: string - "400": - description: bad request - schema: - type: string - security: - - BearerAuth: [] - summary: Change password - tags: - - auth - /api/v1/auth/password/forgot: - post: - consumes: - - application/json - description: Sends a reset token to the user's email address - parameters: - - description: email - in: body - name: body - required: true - schema: - additionalProperties: - type: string - type: object - produces: - - text/plain - responses: - "204": - description: no content - schema: - type: string - summary: Request password reset - tags: - - auth - /api/v1/auth/password/reset: - post: - consumes: - - application/json - description: Resets the password using a valid reset token - parameters: - - description: token, new_password - in: body - name: body - required: true - schema: - additionalProperties: - type: string - type: object - produces: - - text/plain - responses: - "204": - description: no content - schema: - type: string - "400": - description: bad request - schema: - type: string - summary: Confirm password reset - tags: - - auth - /api/v1/auth/refresh: - post: - consumes: - - application/json - description: Use a refresh token to obtain a new access token - parameters: - - description: refresh_token - in: body - name: body - required: true - schema: - additionalProperties: - type: string - type: object - produces: - - application/json - responses: - "200": - description: new access token - schema: - additionalProperties: - type: string - type: object - "401": - description: unauthorized - schema: - type: string - security: - - BearerAuth: [] - summary: Refresh access token - tags: - - auth - /api/v1/auth/refresh/rotate: - post: - consumes: - - application/json - description: Exchanges a valid refresh token for a new access and refresh token, - revoking the old one - parameters: - - description: refresh_token - in: body - name: body - required: true - schema: - additionalProperties: - type: string - type: object - produces: - - application/json - responses: - "200": - description: access_token, refresh_token - schema: - additionalProperties: - type: string - type: object - "401": - description: unauthorized - schema: - type: string - security: - - BearerAuth: [] - summary: Rotate refresh token - tags: - - auth - /api/v1/auth/register: - post: - consumes: - - application/json - description: Registers a new user and stores credentials - parameters: - - description: User registration input - in: body - name: body - required: true - schema: - $ref: '#/definitions/authn.RegisterInput' - produces: - - application/json - responses: - "201": - description: created - schema: - type: string - "400": - description: bad request - schema: - type: string - summary: Register a new user - tags: - - auth - /api/v1/auth/verify: - get: - description: Verifies the user's email using a token (often from an emailed - link) - parameters: - - description: verification token - in: query - name: token - required: true - type: string - produces: - - text/plain - responses: - "204": - description: no content - schema: - type: string - "400": - description: bad request - schema: - type: string - summary: Verify email address - tags: - - auth - /api/v1/auth/verify/resend: - post: - consumes: - - application/json - description: Sends a new email verification token if needed - parameters: - - description: email - in: body - name: body - required: true - schema: - additionalProperties: - type: string - type: object - produces: - - text/plain - responses: - "204": - description: no content - schema: - type: string - summary: Resend email verification - tags: - - auth - /api/v1/clusters: - get: - description: Returns clusters for the organization in X-Org-ID. Add `include=node_pools,bastion` - to expand. Filter by `q` (name contains). - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Name contains (case-insensitive) - in: query - name: q - type: string - - description: 'Optional: node_pools,bastion' - in: query - name: include - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/clusters.clusterResponse' - type: array - "401": - description: Unauthorized - schema: - type: string - "403": - description: organization required - schema: - type: string - "500": - description: failed to list clusters - schema: - type: string - security: - - BearerAuth: [] - summary: List clusters (org scoped) - tags: - - clusters - post: - consumes: - - application/json - description: Creates a cluster and optionally links node pools and a bastion - server. If `kubeconfig` is provided, it will be encrypted per-organization - and stored securely (never returned). - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: payload - in: body - name: body - required: true - schema: - $ref: '#/definitions/clusters.createClusterRequest' - produces: - - application/json - responses: - "201": - description: Created - schema: - $ref: '#/definitions/clusters.clusterResponse' - "400": - description: invalid json / invalid node_pool_ids / invalid bastion_server_id - schema: - type: string - "401": - description: Unauthorized - schema: - type: string - "403": - description: organization required - schema: - type: string - "500": - description: create failed - schema: - type: string - security: - - BearerAuth: [] - summary: Create cluster (org scoped) - tags: - - clusters - /api/v1/clusters/{id}: - delete: - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Cluster ID (UUID) - in: path - name: id - required: true - type: string - 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 - security: - - BearerAuth: [] - summary: Delete cluster (org scoped) - tags: - - clusters - get: - description: Returns one cluster. Add `include=node_pools,bastion` to expand. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Cluster ID (UUID) - in: path - name: id - required: true - type: string - - description: 'Optional: node_pools,bastion' - in: query - name: include - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/clusters.clusterResponse' - "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 - security: - - BearerAuth: [] - summary: Get cluster by ID (org scoped) - tags: - - clusters - patch: - consumes: - - application/json - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Cluster ID (UUID) - in: path - name: id - required: true - type: string - - description: payload - in: body - name: body - required: true - schema: - $ref: '#/definitions/clusters.updateClusterRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/clusters.clusterResponse' - "400": - description: invalid id / invalid json / invalid bastion_server_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 - security: - - BearerAuth: [] - summary: Update cluster (org scoped). If `kubeconfig` is provided and non-empty, - it will be encrypted per-organization and stored (never returned). Sending - an empty string for `kubeconfig` is ignored (no change). - tags: - - clusters - /api/v1/clusters/{id}/bastion: - delete: - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Cluster ID (UUID) - in: path - name: id - required: true - type: string - 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 - "404": - description: not found - schema: - type: string - "500": - description: update failed - schema: - type: string - security: - - BearerAuth: [] - summary: Clear cluster bastion (org scoped) - tags: - - clusters - get: - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Cluster ID (UUID) - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/clusters.serverBrief' - "204": - description: No Content (no bastion set) - schema: - type: string - "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 - security: - - BearerAuth: [] - summary: Get cluster bastion (org scoped) - tags: - - clusters - post: - consumes: - - application/json - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Cluster ID (UUID) - in: path - name: id - required: true - type: string - - description: server_id with role=bastion - in: body - name: body - required: true - schema: - $ref: '#/definitions/clusters.setBastionRequest' - responses: - "204": - description: No Content - schema: - type: string - "400": - description: invalid id / invalid server_id / server not bastion - schema: - type: string - "401": - description: Unauthorized - schema: - type: string - "403": - description: organization required - schema: - type: string - "404": - description: cluster or server not found - schema: - type: string - "500": - description: update failed - schema: - type: string - security: - - BearerAuth: [] - summary: Set/replace cluster bastion (org scoped) - tags: - - clusters - /api/v1/clusters/{id}/node_pools: - get: - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Cluster ID (UUID) - in: path - name: id - required: true - type: string - - description: Name contains (case-insensitive) - in: query - name: q - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/clusters.nodePoolBrief' - type: array - "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 - security: - - BearerAuth: [] - summary: List node pools attached to a cluster (org scoped) - tags: - - clusters - post: - consumes: - - application/json - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Cluster ID (UUID) - in: path - name: id - required: true - type: string - - description: node_pool_ids - in: body - name: body - required: true - schema: - $ref: '#/definitions/clusters.attachNodePoolsRequest' - responses: - "204": - description: No Content - schema: - type: string - "400": - description: invalid id / invalid node_pool_ids - 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: attach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Attach node pools to cluster (org scoped) - tags: - - clusters - /api/v1/clusters/{id}/node_pools/{poolId}: - delete: - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Cluster ID (UUID) - in: path - name: id - required: true - type: string - - description: Node Pool ID (UUID) - in: path - name: poolId - required: true - type: string - 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 - "404": - description: not found - schema: - type: string - "500": - description: detach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Detach one node pool from a cluster (org scoped) - tags: - - clusters - /api/v1/jobs/{id}/cancel: - post: - consumes: - - application/json - description: Cancels running or scheduled jobs - parameters: - - description: Job ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "204": - description: no content - schema: - type: string - "400": - description: bad request - schema: - type: string - "401": - description: unauthorized - schema: - type: string - "404": - description: not found - schema: - type: string - "500": - description: internal error - schema: - type: string - security: - - BearerAuth: [] - summary: Cancel a job - tags: - - jobs - /api/v1/jobs/{id}/retry: - post: - consumes: - - application/json - description: Calls Archer ScheduleNow on the job id - parameters: - - description: Job ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "204": - description: no content - schema: - type: string - "400": - description: bad request - schema: - type: string - "401": - description: unauthorized - schema: - type: string - "404": - description: not found - schema: - type: string - "500": - description: internal error - schema: - type: string - security: - - BearerAuth: [] - summary: Retry a job immediately - tags: - - jobs - /api/v1/jobs/active: - get: - description: Currently running jobs (limit default 100) - parameters: - - default: 100 - description: Max rows - in: query - name: limit - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/jobs.JobListItem' - type: array - "401": - description: unauthorized - schema: - type: string - "500": - description: internal error - schema: - type: string - security: - - BearerAuth: [] - summary: Active jobs - tags: - - jobs - /api/v1/jobs/enqueue: - post: - consumes: - - application/json - description: Schedules a job on a queue with optional args/schedule - parameters: - - description: Enqueue request - in: body - name: payload - required: true - schema: - $ref: '#/definitions/jobs.EnqueueReq' - produces: - - application/json - responses: - "202": - description: Accepted - schema: - $ref: '#/definitions/jobs.EnqueueResp' - "400": - description: bad request - schema: - type: string - "401": - description: unauthorized - schema: - type: string - "500": - description: internal error - schema: - type: string - security: - - BearerAuth: [] - summary: Manually enqueue a job - tags: - - jobs - /api/v1/jobs/failures: - get: - description: Failed jobs ordered by most recent (limit default 100) - parameters: - - default: 100 - description: Max rows - in: query - name: limit - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/jobs.JobListItem' - type: array - "401": - description: unauthorized - schema: - type: string - "500": - description: internal error - schema: - type: string - security: - - BearerAuth: [] - summary: Recent failures - tags: - - jobs - /api/v1/jobs/kpi: - get: - description: Aggregated counters across all queues - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/jobs.KPI' - "401": - description: unauthorized - schema: - type: string - "500": - description: internal error - schema: - type: string - security: - - BearerAuth: [] - summary: Jobs KPI - tags: - - jobs - /api/v1/jobs/queues: - get: - description: Counts and avg duration per queue (last 24h) - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/jobs.QueueRollup' - type: array - "401": - description: unauthorized - schema: - type: string - "500": - description: internal error - schema: - type: string - security: - - BearerAuth: [] - summary: Per-queue rollups - tags: - - jobs - /api/v1/labels: - get: - consumes: - - application/json - 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.' - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Exact key - in: query - name: key - type: string - - description: Exact value - in: query - name: value - type: string - - description: Key contains (case-insensitive) - in: query - name: q - type: string - - description: 'Optional: node_pools' - in: query - name: include - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/labels.labelResponse' - type: array - "401": - description: Unauthorized - schema: - type: string - "403": - description: organization required - schema: - type: string - "500": - description: failed to list node taints - schema: - type: string - security: - - BearerAuth: [] - summary: List node labels (org scoped) - tags: - - labels - post: - consumes: - - application/json - description: Creates a label. Optionally link to node pools. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Label payload - in: body - name: body - required: true - schema: - $ref: '#/definitions/labels.createLabelRequest' - produces: - - application/json - responses: - "201": - description: Created - schema: - $ref: '#/definitions/labels.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 - security: - - BearerAuth: [] - summary: Create label (org scoped) - tags: - - labels - /api/v1/labels/{id}: - delete: - consumes: - - application/json - description: Permanently deletes the label. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Label ID (UUID) - in: path - name: id - required: true - type: string - produces: - - application/json - 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 - security: - - BearerAuth: [] - summary: Delete label (org scoped) - tags: - - labels - get: - consumes: - - application/json - description: Returns one label. Add `include=node_pools` to include node groups. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Label ID (UUID) - in: path - name: id - required: true - type: string - - description: 'Optional: node_pools' - in: query - name: include - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/labels.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 - security: - - BearerAuth: [] - summary: Get label by ID (org scoped) - tags: - - labels - patch: - consumes: - - application/json - description: Partially update label fields. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Label ID (UUID) - in: path - name: id - required: true - type: string - - description: Fields to update - in: body - name: body - required: true - schema: - $ref: '#/definitions/labels.updateLabelRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/labels.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 - security: - - BearerAuth: [] - summary: Update label (org scoped) - tags: - - labels - /api/v1/labels/{id}/node_pools: - get: - consumes: - - application/json - description: Returns node pools attached to the label. Supports `q` (name contains, - case-insensitive). - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Label ID (UUID) - in: path - name: id - required: true - type: string - - description: Name contains (case-insensitive) - in: query - name: q - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/labels.nodePoolBrief' - type: array - "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 - security: - - BearerAuth: [] - summary: List node pools linked to a label (org scoped) - tags: - - labels - post: - consumes: - - application/json - description: Links the label to one or more node pools in the same organization. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Label ID (UUID) - in: path - name: id - required: true - type: string - - description: IDs to attach - in: body - name: body - required: true - schema: - $ref: '#/definitions/labels.addLabelToPoolRequest' - - description: 'Optional: node_pools' - in: query - name: include - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/labels.labelResponse' - "400": - description: invalid id / invalid json / invalid node_pool_ids - 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: attach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Attach label to node pools (org scoped) - tags: - - labels - /api/v1/labels/{id}/node_pools/{poolId}: - delete: - consumes: - - application/json - description: Unlinks the label from the specified node pool. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Label ID (UUID) - in: path - name: id - required: true - type: string - - description: Node Pool ID (UUID) - in: path - name: poolId - required: true - type: string - produces: - - application/json - 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 - "404": - description: not found - schema: - type: string - "500": - description: detach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Detach label from a node pool (org scoped) - tags: - - labels - /api/v1/node-pools: - get: - consumes: - - application/json - description: Returns node pools for the organization in X-Org-ID. Add `include=servers` - to include attached servers. Filter by `q` (name contains). - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Name contains (case-insensitive) - in: query - name: q - type: string - - description: 'Optional: servers' - in: query - name: include - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/nodepools.nodePoolResponse' - type: array - "401": - description: Unauthorized - schema: - type: string - "403": - description: organization required - schema: - type: string - "500": - description: failed to list node groups - schema: - type: string - security: - - BearerAuth: [] - summary: List node pools (org scoped) - tags: - - node-pools - post: - consumes: - - application/json - description: Creates a node group. Optionally attach initial servers. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: NodeGroup payload - in: body - name: body - required: true - schema: - $ref: '#/definitions/nodepools.createNodePoolRequest' - produces: - - application/json - responses: - "201": - description: Created - schema: - $ref: '#/definitions/nodepools.nodePoolResponse' - "400": - description: invalid json / missing fields / invalid server_ids - schema: - type: string - "401": - description: Unauthorized - schema: - type: string - "403": - description: organization required - schema: - type: string - "500": - description: create failed - schema: - type: string - security: - - BearerAuth: [] - summary: Create node group (org scoped) - tags: - - node-pools - /api/v1/node-pools/{id}: - delete: - consumes: - - application/json - description: Permanently deletes the node pool. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Group ID (UUID) - in: path - name: id - required: true - type: string - produces: - - application/json - 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 - security: - - BearerAuth: [] - summary: Delete node pool (org scoped) - tags: - - node-pools - get: - consumes: - - application/json - description: Returns one node group. Add `include=servers` to include servers. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Group ID (UUID) - in: path - name: id - required: true - type: string - - description: 'Optional: servers' - in: query - name: include - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/nodepools.nodePoolResponse' - "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 - security: - - BearerAuth: [] - summary: Get node group by ID (org scoped) - tags: - - node-pools - patch: - consumes: - - application/json - description: Partially update node pool fields. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Pool ID (UUID) - in: path - name: id - required: true - type: string - - description: Fields to update - in: body - name: body - required: true - schema: - $ref: '#/definitions/nodepools.updateNodePoolRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/nodepools.nodePoolResponse' - "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 - security: - - BearerAuth: [] - summary: Update node pool (org scoped) - tags: - - node-pools - /api/v1/node-pools/{id}/annotations: - get: - consumes: - - application/json - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Pool ID (UUID) - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/nodepools.annotationBrief' - type: array - "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 - security: - - BearerAuth: [] - summary: List annotations attached to a node pool (org scoped) - tags: - - node-pools - post: - consumes: - - application/json - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Pool ID (UUID) - in: path - name: id - required: true - type: string - - description: Annotation IDs to attach - in: body - name: body - required: true - schema: - $ref: '#/definitions/nodepools.attachAnnotationsRequest' - produces: - - application/json - responses: - "204": - description: No Content - schema: - type: string - "400": - description: invalid id / invalid annotation_ids - 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: attach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Attach annotations to a node pool (org scoped) - tags: - - node-pools - /api/v1/node-pools/{id}/annotations/{annotationId}: - delete: - consumes: - - application/json - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Pool ID (UUID) - in: path - name: id - required: true - type: string - - description: Annotation ID (UUID) - in: path - name: annotationId - required: true - type: string - produces: - - application/json - 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 - "404": - description: not found - schema: - type: string - "500": - description: detach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Detach one annotation from a node pool (org scoped) - tags: - - node-pools - /api/v1/node-pools/{id}/labels: - get: - consumes: - - application/json - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Pool ID (UUID) - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/nodepools.labelBrief' - type: array - "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 - security: - - BearerAuth: [] - summary: List labels attached to a node pool (org scoped) - tags: - - node-pools - post: - consumes: - - application/json - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Pool ID (UUID) - in: path - name: id - required: true - type: string - - description: Label IDs to attach - in: body - name: body - required: true - schema: - $ref: '#/definitions/nodepools.attachLabelsRequest' - produces: - - application/json - responses: - "204": - description: No Content - schema: - type: string - "400": - description: invalid id / invalid label_ids - 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: attach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Attach labels to a node pool (org scoped) - tags: - - node-pools - /api/v1/node-pools/{id}/labels/{labelId}: - delete: - consumes: - - application/json - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Pool ID (UUID) - in: path - name: id - required: true - type: string - - description: Label ID (UUID) - in: path - name: labelId - required: true - type: string - produces: - - application/json - 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 - "404": - description: not found - schema: - type: string - "500": - description: detach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Detach one label from a node pool (org scoped) - tags: - - node-pools - /api/v1/node-pools/{id}/servers: - get: - consumes: - - application/json - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Group ID (UUID) - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/nodepools.serverBrief' - type: array - "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 - security: - - BearerAuth: [] - summary: List servers attached to a node pool (org scoped) - tags: - - node-pools - post: - consumes: - - application/json - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Group ID (UUID) - in: path - name: id - required: true - type: string - - description: Server IDs to attach - in: body - name: body - required: true - schema: - $ref: '#/definitions/nodepools.attachServersRequest' - produces: - - application/json - responses: - "204": - description: No Content - schema: - type: string - "400": - description: invalid id / invalid server_ids - 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: attach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Attach servers to a node pool (org scoped) - tags: - - node-pools - /api/v1/node-pools/{id}/servers/{serverId}: - delete: - consumes: - - application/json - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Pool ID (UUID) - in: path - name: id - required: true - type: string - - description: Server ID (UUID) - in: path - name: serverId - required: true - type: string - produces: - - application/json - 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 - "404": - description: not found - schema: - type: string - "500": - description: detach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Detach one server from a node pool (org scoped) - tags: - - node-pools - /api/v1/node-pools/{id}/taints: - get: - consumes: - - application/json - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Pool ID (UUID) - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/nodepools.taintBrief' - type: array - "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 - security: - - BearerAuth: [] - summary: List taints attached to a node pool (org scoped) - tags: - - node-pools - post: - consumes: - - application/json - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Pool ID (UUID) - in: path - name: id - required: true - type: string - - description: Taint IDs to attach - in: body - name: body - required: true - schema: - $ref: '#/definitions/nodepools.attachTaintsRequest' - produces: - - application/json - responses: - "204": - description: No Content - schema: - type: string - "400": - description: invalid id / invalid taint_ids - 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: attach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Attach taints to a node pool (org scoped) - tags: - - node-pools - /api/v1/node-pools/{id}/taints/{taintId}: - delete: - consumes: - - application/json - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Pool ID (UUID) - in: path - name: id - required: true - type: string - - description: Taint ID (UUID) - in: path - name: taintId - required: true - type: string - produces: - - application/json - 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 - "404": - description: not found - schema: - type: string - "500": - description: detach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Detach one taint from a node pool (org scoped) - tags: - - node-pools - /api/v1/orgs: - get: - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/models.Organization' - type: array - "401": - description: unauthorized - schema: - type: string - security: - - BearerAuth: [] - summary: List organizations for user - tags: - - organizations - post: - consumes: - - application/json - description: Creates a new organization and assigns the authenticated user as - an admin member - parameters: - - description: Optional organization context (ignored for creation) - in: header - name: X-Org-ID - type: string - - description: Organization Input - in: body - name: body - required: true - schema: - $ref: '#/definitions/orgs.OrgInput' - produces: - - application/json - responses: - "200": - description: organization_id - schema: - additionalProperties: - type: string - type: object - "400": - description: invalid input - schema: - type: string - "401": - description: unauthorized - schema: - type: string - "500": - description: internal error - schema: - type: string - security: - - BearerAuth: [] - summary: Create a new organization - tags: - - organizations - /api/v1/orgs/{orgId}: - delete: - parameters: - - description: Organization ID - in: path - name: orgId - required: true - type: string - responses: - "204": - description: deleted - schema: - type: string - "403": - description: forbidden - schema: - type: string - security: - - BearerAuth: [] - summary: Delete organization - tags: - - organizations - patch: - consumes: - - application/json - parameters: - - description: Org ID - in: path - name: orgId - required: true - type: string - - description: Organization data - in: body - name: body - required: true - schema: - $ref: '#/definitions/orgs.OrgInput' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/models.Organization' - "403": - description: forbidden - schema: - type: string - security: - - BearerAuth: [] - summary: Update organization metadata - tags: - - organizations - /api/v1/orgs/invite: - post: - consumes: - - application/json - parameters: - - description: Invite input - in: body - name: body - required: true - schema: - $ref: '#/definitions/orgs.InviteInput' - - description: Organization context - in: header - name: X-Org-ID - required: true - type: string - produces: - - text/plain - responses: - "201": - description: invited - schema: - type: string - "400": - description: bad request - schema: - type: string - "403": - description: forbidden - schema: - type: string - security: - - BearerAuth: [] - summary: Invite user to organization - tags: - - organizations - /api/v1/orgs/members: - get: - description: Returns a list of all members in the current organization - parameters: - - description: Organization context - in: header - name: X-Org-ID - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/models.Member' - type: array - "401": - description: unauthorized - schema: - type: string - security: - - BearerAuth: [] - summary: List organization members - tags: - - organizations - /api/v1/orgs/members/{userId}: - delete: - parameters: - - description: User ID - in: path - name: userId - required: true - type: string - responses: - "204": - description: deleted - schema: - type: string - "403": - description: forbidden - schema: - type: string - security: - - BearerAuth: [] - summary: Remove member from organization - tags: - - organizations - /api/v1/servers: - get: - consumes: - - application/json - description: 'Returns servers for the organization in X-Org-ID. Optional filters: - status, role.' - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Filter by status (pending|provisioning|ready|failed) - in: query - name: status - type: string - - description: Filter by role - in: query - name: role - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/servers.serverResponse' - type: array - "401": - description: Unauthorized - schema: - type: string - "403": - description: organization required - schema: - type: string - "500": - description: failed to list servers - schema: - type: string - security: - - BearerAuth: [] - summary: List servers (org scoped) - tags: - - servers - post: - consumes: - - application/json - description: Creates a server bound to the org in X-Org-ID. Validates that ssh_key_id - belongs to the org. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Server payload - in: body - name: body - required: true - schema: - $ref: '#/definitions/servers.createServerRequest' - produces: - - application/json - responses: - "201": - description: Created - schema: - $ref: '#/definitions/servers.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 - security: - - BearerAuth: [] - summary: Create server (org scoped) - tags: - - servers - /api/v1/servers/{id}: - delete: - consumes: - - application/json - description: Permanently deletes the server. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Server ID (UUID) - in: path - name: id - required: true - type: string - produces: - - application/json - 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 - security: - - BearerAuth: [] - summary: Delete server (org scoped) - tags: - - servers - get: - consumes: - - application/json - description: Returns one server in the given organization. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Server ID (UUID) - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/servers.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 - security: - - BearerAuth: [] - summary: Get server by ID (org scoped) - tags: - - servers - patch: - consumes: - - application/json - description: Partially update fields; changing ssh_key_id validates ownership. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Server ID (UUID) - in: path - name: id - required: true - type: string - - description: Fields to update - in: body - name: body - required: true - schema: - $ref: '#/definitions/servers.updateServerRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/servers.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 - security: - - BearerAuth: [] - summary: Update server (org scoped) - tags: - - servers - /api/v1/ssh: - get: - consumes: - - application/json - description: Returns ssh keys for the organization in X-Org-ID. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/ssh.sshResponse' - type: array - "401": - description: Unauthorized - schema: - type: string - "403": - description: organization required - schema: - type: string - "500": - description: failed to list keys - schema: - type: string - security: - - BearerAuth: [] - summary: List ssh keys (org scoped) - tags: - - ssh - post: - consumes: - - application/json - description: Generates an RSA keypair, saves it, and returns metadata. Optionally - set `download` to "public", "private", or "both" to download files immediately. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Key generation options - in: body - name: body - required: true - schema: - $ref: '#/definitions/ssh.createSSHRequest' - produces: - - application/json - responses: - "201": - description: Created - headers: - Content-Disposition: - description: When download is requested - type: string - schema: - $ref: '#/definitions/ssh.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 - security: - - BearerAuth: [] - summary: Create ssh keypair (org scoped) - tags: - - ssh - /api/v1/ssh/{id}: - delete: - consumes: - - application/json - description: Permanently deletes a keypair. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: SSH Key ID (UUID) - in: path - name: id - required: true - type: string - produces: - - application/json - 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 - security: - - BearerAuth: [] - summary: Delete ssh keypair (org scoped) - tags: - - ssh - get: - consumes: - - application/json - description: Returns public key fields. Append `?reveal=true` to include the - private key PEM. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: SSH Key ID (UUID) - in: path - name: id - required: true - type: string - - description: Reveal private key PEM - in: query - name: reveal - type: boolean - produces: - - application/json - responses: - "200": - description: When reveal=true - schema: - $ref: '#/definitions/ssh.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 - security: - - BearerAuth: [] - summary: Get ssh key by ID (org scoped) - tags: - - ssh - /api/v1/ssh/{id}/download: - get: - description: Download `part=public|private|both` of the keypair. `both` returns - a zip file. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: SSH Key ID (UUID) - in: path - name: id - required: true - type: string - - description: Which part to download - enum: - - public - - private - - both - in: query - name: part - required: true - type: string - produces: - - text/plain - 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 - security: - - BearerAuth: [] - summary: Download ssh key files by ID (org scoped) - tags: - - ssh - /api/v1/taints: - get: - consumes: - - application/json - 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.' - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Exact key - in: query - name: key - type: string - - description: Exact value - in: query - name: value - type: string - - description: key contains (case-insensitive) - in: query - name: q - type: string - - description: 'Optional: node_pools' - in: query - name: include - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/taints.taintResponse' - type: array - "401": - description: Unauthorized - schema: - type: string - "403": - description: organization required - schema: - type: string - "500": - description: failed to list node taints - schema: - type: string - security: - - BearerAuth: [] - summary: List node taints (org scoped) - tags: - - taints - post: - consumes: - - application/json - description: Creates a taint. Optionally link to node pools. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Taint payload - in: body - name: body - required: true - schema: - $ref: '#/definitions/taints.createTaintRequest' - produces: - - application/json - responses: - "201": - description: Created - schema: - $ref: '#/definitions/taints.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 - security: - - BearerAuth: [] - summary: Create node taint (org scoped) - tags: - - taints - /api/v1/taints/{id}: - delete: - consumes: - - application/json - description: Permanently deletes the taint. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Taint ID (UUID) - in: path - name: id - required: true - type: string - produces: - - application/json - 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 - security: - - BearerAuth: [] - summary: Delete taint (org scoped) - tags: - - taints - get: - consumes: - - application/json - description: Returns one taint. Add `include=node_pools` to include node pools. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Taint ID (UUID) - in: path - name: id - required: true - type: string - - description: 'Optional: node_pools' - in: query - name: include - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/taints.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 - security: - - BearerAuth: [] - summary: Get node taint by ID (org scoped) - tags: - - taints - patch: - consumes: - - application/json - description: Partially update taint fields. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Node Taint ID (UUID) - in: path - name: id - required: true - type: string - - description: Fields to update - in: body - name: body - required: true - schema: - $ref: '#/definitions/taints.updateTaintRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/taints.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 - security: - - BearerAuth: [] - summary: Update node taint (org scoped) - tags: - - taints - /api/v1/taints/{id}/node_pools: - get: - consumes: - - application/json - description: Returns node pools attached to the taint. Supports `q` (name contains, - case-insensitive). - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Taint ID (UUID) - in: path - name: id - required: true - type: string - - description: Name contains (case-insensitive) - in: query - name: q - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/taints.nodePoolResponse' - type: array - "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 - security: - - BearerAuth: [] - summary: List node pools linked to a taint (org scoped) - tags: - - taints - post: - consumes: - - application/json - description: Links the taint to one or more node pools in the same organization. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Taint ID (UUID) - in: path - name: id - required: true - type: string - - description: IDs to attach - in: body - name: body - required: true - schema: - $ref: '#/definitions/taints.addTaintToPoolRequest' - - description: 'Optional: node_pools' - in: query - name: include - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/taints.taintResponse' - "400": - description: invalid id / invalid json / invalid node_pool_ids - 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: attach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Attach taint to node pools (org scoped) - tags: - - taints - /api/v1/taints/{id}/node_pools/{poolId}: - delete: - consumes: - - application/json - description: Unlinks the taint from the specified node pool. - parameters: - - description: Organization UUID - in: header - name: X-Org-ID - required: true - type: string - - description: Taint ID (UUID) - in: path - name: id - required: true - type: string - - description: Node Pool ID (UUID) - in: path - name: poolId - required: true - type: string - produces: - - application/json - 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 - "404": - description: not found - schema: - type: string - "500": - description: detach failed - schema: - type: string - security: - - BearerAuth: [] - summary: Detach taint from a node pool (org scoped) - tags: - - taints -schemes: -- http -securityDefinitions: - BearerAuth: - in: header - name: Authorization - type: apiKey -swagger: "2.0" diff --git a/internal/api/server.go b/internal/api/server.go index 01ca4a4..e51b89b 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -20,10 +20,8 @@ func NewRouter() http.Handler { r.Use(cors.Handler(cors.Options{ AllowedOrigins: []string{ - "http://localhost:5173", - "http://127.0.0.1:5173", - "http://localhost:8080", - "http://127.0.0.1:8080", + "http://*:5173", + "http://*:8080", }, AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}, AllowedHeaders: []string{"Content-Type", "content-type", "Authorization", "authorization", "X-Org-ID", "x-org-id"}, diff --git a/internal/ui/dist/assets/icons-B5E6SSBo.js b/internal/ui/dist/assets/icons-B5E6SSBo.js deleted file mode 100644 index 9a297f6..0000000 --- a/internal/ui/dist/assets/icons-B5E6SSBo.js +++ /dev/null @@ -1,206 +0,0 @@ -import{r as i,R as s}from"./vendor-DvippHRz.js";/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const w=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),b=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(t,a,c)=>c?c.toUpperCase():a.toLowerCase()),u=e=>{const t=b(e);return t.charAt(0).toUpperCase()+t.slice(1)},M=(...e)=>e.filter((t,a,c)=>!!t&&t.trim()!==""&&c.indexOf(t)===a).join(" ").trim(),z=e=>{for(const t in e)if(t.startsWith("aria-")||t==="role"||t==="title")return!0};/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */var C={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const j=i.forwardRef(({color:e="currentColor",size:t=24,strokeWidth:a=2,absoluteStrokeWidth:c,className:n="",children:r,iconNode:p,...d},h)=>i.createElement("svg",{ref:h,...C,width:t,height:t,stroke:e,strokeWidth:c?Number(a)*24/Number(t):a,className:M("lucide",n),...!r&&!z(d)&&{"aria-hidden":"true"},...d},[...p.map(([g,x])=>i.createElement(g,x)),...Array.isArray(r)?r:[r]]));/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const o=(e,t)=>{const a=i.forwardRef(({className:c,...n},r)=>i.createElement(j,{ref:r,iconNode:t,className:M(`lucide-${w(u(e))}`,`lucide-${e}`,c),...n}));return a.displayName=u(e),a};/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const N=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z",key:"3c2336"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],f1=o("badge-check",N);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const $=[["path",{d:"M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z",key:"lc1i9w"}],["path",{d:"m7 16.5-4.74-2.85",key:"1o9zyk"}],["path",{d:"m7 16.5 5-3",key:"va8pkn"}],["path",{d:"M7 16.5v5.17",key:"jnp8gn"}],["path",{d:"M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z",key:"8zsnat"}],["path",{d:"m17 16.5-5-3",key:"8arw3v"}],["path",{d:"m17 16.5 4.74-2.85",key:"8rfmw"}],["path",{d:"M17 16.5v5.17",key:"k6z78m"}],["path",{d:"M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z",key:"1xygjf"}],["path",{d:"M12 8 7.26 5.15",key:"1vbdud"}],["path",{d:"m12 8 4.74-2.85",key:"3rx089"}],["path",{d:"M12 13.5V8",key:"1io7kd"}]],_1=o("boxes",$);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const O=[["path",{d:"m10.852 14.772-.383.923",key:"11vil6"}],["path",{d:"m10.852 9.228-.383-.923",key:"1fjppe"}],["path",{d:"m13.148 14.772.382.924",key:"je3va1"}],["path",{d:"m13.531 8.305-.383.923",key:"18epck"}],["path",{d:"m14.772 10.852.923-.383",key:"k9m8cz"}],["path",{d:"m14.772 13.148.923.383",key:"1xvhww"}],["path",{d:"M17.598 6.5A3 3 0 1 0 12 5a3 3 0 0 0-5.63-1.446 3 3 0 0 0-.368 1.571 4 4 0 0 0-2.525 5.771",key:"jcbbz1"}],["path",{d:"M17.998 5.125a4 4 0 0 1 2.525 5.771",key:"1kkn7e"}],["path",{d:"M19.505 10.294a4 4 0 0 1-1.5 7.706",key:"18bmuc"}],["path",{d:"M4.032 17.483A4 4 0 0 0 11.464 20c.18-.311.892-.311 1.072 0a4 4 0 0 0 7.432-2.516",key:"uozx0d"}],["path",{d:"M4.5 10.291A4 4 0 0 0 6 18",key:"whdemb"}],["path",{d:"M6.002 5.125a3 3 0 0 0 .4 1.375",key:"1kqy2g"}],["path",{d:"m9.228 10.852-.923-.383",key:"1wtb30"}],["path",{d:"m9.228 13.148-.923.383",key:"1a830x"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]],g1=o("brain-cog",O);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const L=[["path",{d:"M12 10h.01",key:"1nrarc"}],["path",{d:"M12 14h.01",key:"1etili"}],["path",{d:"M12 6h.01",key:"1vi96p"}],["path",{d:"M16 10h.01",key:"1m94wz"}],["path",{d:"M16 14h.01",key:"1gbofw"}],["path",{d:"M16 6h.01",key:"1x0f13"}],["path",{d:"M8 10h.01",key:"19clt8"}],["path",{d:"M8 14h.01",key:"6423bh"}],["path",{d:"M8 6h.01",key:"1dz90k"}],["path",{d:"M9 22v-3a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v3",key:"cabbwy"}],["rect",{x:"4",y:"2",width:"16",height:"20",rx:"2",key:"1uxh74"}]],x1=o("building",L);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const q=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],w1=o("check",q);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const P=[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]],b1=o("chevron-down",P);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const A=[["path",{d:"m18 15-6-6-6 6",key:"153udz"}]],z1=o("chevron-up",A);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const V=[["path",{d:"M22 2 2 22",key:"y4kqgn"}],["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]],C1=o("circle-slash-2",V);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const H=[["path",{d:"M12 13v8l-4-4",key:"1f5nwf"}],["path",{d:"m12 21 4-4",key:"1lfcce"}],["path",{d:"M4.393 15.269A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.436 8.284",key:"ui1hmy"}]],j1=o("cloud-download",H);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const S=[["path",{d:"M15.536 11.293a1 1 0 0 0 0 1.414l2.376 2.377a1 1 0 0 0 1.414 0l2.377-2.377a1 1 0 0 0 0-1.414l-2.377-2.377a1 1 0 0 0-1.414 0z",key:"1uwlt4"}],["path",{d:"M2.297 11.293a1 1 0 0 0 0 1.414l2.377 2.377a1 1 0 0 0 1.414 0l2.377-2.377a1 1 0 0 0 0-1.414L6.088 8.916a1 1 0 0 0-1.414 0z",key:"10291m"}],["path",{d:"M8.916 17.912a1 1 0 0 0 0 1.415l2.377 2.376a1 1 0 0 0 1.414 0l2.377-2.376a1 1 0 0 0 0-1.415l-2.377-2.376a1 1 0 0 0-1.414 0z",key:"1tqoq1"}],["path",{d:"M8.916 4.674a1 1 0 0 0 0 1.414l2.377 2.376a1 1 0 0 0 1.414 0l2.377-2.376a1 1 0 0 0 0-1.414l-2.377-2.377a1 1 0 0 0-1.414 0z",key:"1x6lto"}]],N1=o("component",S);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const E=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],$1=o("copy",E);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const B=[["path",{d:"M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v6",key:"rc0qvx"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["circle",{cx:"4",cy:"16",r:"2",key:"1ehqvc"}],["path",{d:"m10 10-4.5 4.5",key:"7fwrp6"}],["path",{d:"m9 11 1 1",key:"wa6s5q"}]],O1=o("file-key-2",B);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const U=[["path",{d:"M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8",key:"5wwlr5"}],["path",{d:"M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",key:"1d0kgt"}]],L1=o("house",U);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const R=[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4",key:"g0fldk"}],["path",{d:"m21 2-9.6 9.6",key:"1j0ho8"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5",key:"yqb3hr"}]],q1=o("key",R);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Z=[["path",{d:"M18 5a2 2 0 0 1 2 2v8.526a2 2 0 0 0 .212.897l1.068 2.127a1 1 0 0 1-.9 1.45H3.62a1 1 0 0 1-.9-1.45l1.068-2.127A2 2 0 0 0 4 15.526V7a2 2 0 0 1 2-2z",key:"1pdavp"}],["path",{d:"M20.054 15.987H3.946",key:"14rxg9"}]],P1=o("laptop",Z);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const D=[["path",{d:"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71",key:"1cjeqo"}],["path",{d:"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71",key:"19qd67"}]],A1=o("link",D);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const T=[["rect",{x:"3",y:"5",width:"6",height:"6",rx:"1",key:"1defrl"}],["path",{d:"m3 17 2 2 4-4",key:"1jhpwq"}],["path",{d:"M13 6h8",key:"15sg57"}],["path",{d:"M13 12h8",key:"h98zly"}],["path",{d:"M13 18h8",key:"oe0vm4"}]],V1=o("list-todo",T);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const W=[["circle",{cx:"12",cy:"16",r:"1",key:"1au0dj"}],["rect",{x:"3",y:"10",width:"18",height:"12",rx:"2",key:"6s8ecr"}],["path",{d:"M7 10V7a5 5 0 0 1 10 0v3",key:"1pqi11"}]],H1=o("lock-keyhole",W);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const I=[["path",{d:"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401",key:"kfwtm"}]],S1=o("moon",I);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const K=[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}],["path",{d:"m15 5 4 4",key:"1mk7zo"}]],E1=o("pencil",K);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const G=[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]],B1=o("plus",G);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const F=[["path",{d:"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"14sxne"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16",key:"1hlbsb"}],["path",{d:"M16 16h5v5",key:"ccwih5"}]],U1=o("refresh-ccw",F);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const X=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]],R1=o("refresh-cw",X);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const J=[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]],Z1=o("search",J);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Q=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]],D1=o("server",Q);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Y=[["path",{d:"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",key:"1i5ecw"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]],T1=o("settings",Y);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const e1=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],W1=o("shield-check",e1);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const t1=[["path",{d:"M3 3h.01",key:"159qn6"}],["path",{d:"M7 5h.01",key:"1hq22a"}],["path",{d:"M11 7h.01",key:"1osv80"}],["path",{d:"M3 7h.01",key:"1xzrh3"}],["path",{d:"M7 9h.01",key:"19b3jx"}],["path",{d:"M3 11h.01",key:"1eifu7"}],["rect",{width:"4",height:"4",x:"15",y:"5",key:"mri9e4"}],["path",{d:"m19 9 2 2v10c0 .6-.4 1-1 1h-6c-.6 0-1-.4-1-1V11l2-2",key:"aib6hk"}],["path",{d:"m13 14 8-2",key:"1d7bmk"}],["path",{d:"m13 19 8-2",key:"1y2vml"}]],I1=o("spray-can",t1);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const a1=[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]],K1=o("sun",a1);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const c1=[["path",{d:"M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z",key:"vktsd0"}],["circle",{cx:"7.5",cy:"7.5",r:".5",fill:"currentColor",key:"kqv944"}]],G1=o("tag",c1);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const o1=[["path",{d:"M13.172 2a2 2 0 0 1 1.414.586l6.71 6.71a2.4 2.4 0 0 1 0 3.408l-4.592 4.592a2.4 2.4 0 0 1-3.408 0l-6.71-6.71A2 2 0 0 1 6 9.172V3a1 1 0 0 1 1-1z",key:"16rjxf"}],["path",{d:"M2 7v6.172a2 2 0 0 0 .586 1.414l6.71 6.71a2.4 2.4 0 0 0 3.191.193",key:"178nd4"}],["circle",{cx:"10.5",cy:"6.5",r:".5",fill:"currentColor",key:"12ikhr"}]],F1=o("tags",o1);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const n1=[["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],X1=o("trash",n1);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const r1=[["path",{d:"m18.84 12.25 1.72-1.71h-.02a5.004 5.004 0 0 0-.12-7.07 5.006 5.006 0 0 0-6.95 0l-1.72 1.71",key:"yqzxt4"}],["path",{d:"m5.17 11.75-1.71 1.71a5.004 5.004 0 0 0 .12 7.07 5.006 5.006 0 0 0 6.95 0l1.71-1.71",key:"4qinb0"}],["line",{x1:"8",x2:"8",y1:"2",y2:"5",key:"1041cp"}],["line",{x1:"2",x2:"5",y1:"8",y2:"8",key:"14m1p5"}],["line",{x1:"16",x2:"16",y1:"19",y2:"22",key:"rzdirn"}],["line",{x1:"19",x2:"22",y1:"16",y2:"16",key:"ox905f"}]],J1=o("unlink",r1);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const h1=[["path",{d:"M2 21a8 8 0 0 1 13.292-6",key:"bjp14o"}],["circle",{cx:"10",cy:"8",r:"5",key:"o932ke"}],["path",{d:"M19 16v6",key:"tddt3s"}],["path",{d:"M22 19h-6",key:"vcuq98"}]],Q1=o("user-round-plus",h1);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const s1=[["path",{d:"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2",key:"975kel"}],["circle",{cx:"12",cy:"7",r:"4",key:"17ys0d"}]],Y1=o("user",s1);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const d1=[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["path",{d:"M16 3.128a4 4 0 0 1 0 7.744",key:"16gr8j"}],["path",{d:"M22 21v-2a4 4 0 0 0-3-3.87",key:"kshegd"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}]],e2=o("users",d1);/** - * @license lucide-react v0.542.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const i1=[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]],t2=o("x",i1);var f={color:void 0,size:void 0,className:void 0,style:void 0,attr:void 0},m=s.createContext&&s.createContext(f),y1=["attr","size","title"];function l1(e,t){if(e==null)return{};var a=p1(e,t),c,n;if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0)&&Object.prototype.propertyIsEnumerable.call(e,c)&&(a[c]=e[c])}return a}function p1(e,t){if(e==null)return{};var a={};for(var c in e)if(Object.prototype.hasOwnProperty.call(e,c)){if(t.indexOf(c)>=0)continue;a[c]=e[c]}return a}function y(){return y=Object.assign?Object.assign.bind():function(e){for(var t=1;ts.createElement(t.tag,l({key:a},t.attr),_(t.child)))}function k(e){return t=>s.createElement(v1,y({attr:l({},e.attr)},t),_(e.child))}function v1(e){var t=a=>{var{attr:c,size:n,title:r}=e,p=l1(e,y1),d=n||a.size||"1em",h;return a.className&&(h=a.className),e.className&&(h=(h?h+" ":"")+e.className),s.createElement("svg",y({stroke:"currentColor",fill:"currentColor",strokeWidth:"0"},a.attr,c,p,{className:h,style:l(l({color:e.color||a.color},a.style),e.style),height:d,width:d,xmlns:"http://www.w3.org/2000/svg"}),r&&s.createElement("title",null,r),e.children)};return m!==void 0?s.createElement(m.Consumer,null,a=>t(a)):t(f)}function a2(e){return k({attr:{viewBox:"0 0 1024 1024"},child:[{tag:"path",attr:{d:"M888 680h-54V540H546v-92h238c8.8 0 16-7.2 16-16V168c0-8.8-7.2-16-16-16H240c-8.8 0-16 7.2-16 16v264c0 8.8 7.2 16 16 16h238v92H190v140h-54c-4.4 0-8 3.6-8 8v176c0 4.4 3.6 8 8 8h176c4.4 0 8-3.6 8-8V688c0-4.4-3.6-8-8-8h-54v-72h220v72h-54c-4.4 0-8 3.6-8 8v176c0 4.4 3.6 8 8 8h176c4.4 0 8-3.6 8-8V688c0-4.4-3.6-8-8-8h-54v-72h220v72h-54c-4.4 0-8 3.6-8 8v176c0 4.4 3.6 8 8 8h176c4.4 0 8-3.6 8-8V688c0-4.4-3.6-8-8-8zM256 805.3c0 1.5-1.2 2.7-2.7 2.7h-58.7c-1.5 0-2.7-1.2-2.7-2.7v-58.7c0-1.5 1.2-2.7 2.7-2.7h58.7c1.5 0 2.7 1.2 2.7 2.7v58.7zm288 0c0 1.5-1.2 2.7-2.7 2.7h-58.7c-1.5 0-2.7-1.2-2.7-2.7v-58.7c0-1.5 1.2-2.7 2.7-2.7h58.7c1.5 0 2.7 1.2 2.7 2.7v58.7zM288 384V216h448v168H288zm544 421.3c0 1.5-1.2 2.7-2.7 2.7h-58.7c-1.5 0-2.7-1.2-2.7-2.7v-58.7c0-1.5 1.2-2.7 2.7-2.7h58.7c1.5 0 2.7 1.2 2.7 2.7v58.7zM360 300a40 40 0 1 0 80 0 40 40 0 1 0-80 0z"},child:[]}]})(e)}function c2(e){return k({attr:{viewBox:"0 0 24 24"},child:[{tag:"path",attr:{fill:"none",strokeWidth:"2",d:"M3,6 L21,6 L3,6 Z M10,2 L10,4 M14,2 L14,4 M16,12 C18.3736719,13.1826446 20,15.6506255 20,19 L20,23 L4,23 L4,19 C4,15.6457258 5.6310898,13.1754259 8,12 M12,16.5 L12,23 M12,13 C15.3137085,13 18,10.3137085 18,7 C18,3.6862915 15.3137085,1 12,1 C8.6862915,1 6,3.6862915 6,7 C6,10.3137085 8.6862915,13 12,13 Z M8,12 C8,14.209139 9.790861,16 12,16 L12,16 C14.209139,16 16,14.209139 16,12"},child:[]}]})(e)}function o2(e){return k({attr:{viewBox:"0 0 496 512"},child:[{tag:"path",attr:{d:"M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"},child:[]}]})(e)}export{a2 as A,_1 as B,w1 as C,O1 as F,c2 as G,L1 as H,q1 as K,P1 as L,S1 as M,B1 as P,R1 as R,K1 as S,F1 as T,e2 as U,t2 as X,N1 as a,I1 as b,D1 as c,g1 as d,H1 as e,V1 as f,x1 as g,Y1 as h,T1 as i,W1 as j,b1 as k,o2 as l,z1 as m,E1 as n,X1 as o,Z1 as p,A1 as q,J1 as r,U1 as s,G1 as t,f1 as u,C1 as v,$1 as w,j1 as x,Q1 as y}; diff --git a/internal/ui/dist/assets/index-CHoyJPs-.css b/internal/ui/dist/assets/index-CHoyJPs-.css deleted file mode 100644 index a27ddb6..0000000 --- a/internal/ui/dist/assets/index-CHoyJPs-.css +++ /dev/null @@ -1 +0,0 @@ -/*! tailwindcss v4.1.12 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:"";--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-300:oklch(80.8% .114 19.571);--color-red-500:oklch(63.7% .237 25.331);--color-red-800:oklch(44.4% .177 26.899);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-900:oklch(20.8% .042 265.755);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-xl:36rem;--container-2xl:42rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-6xl:3.75rem;--text-6xl--line-height:1;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-widest:.1em;--leading-tight:1.25;--leading-loose:2;--radius-xs:.125rem;--radius-2xl:1rem;--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{border-color:var(--border);outline-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){*{outline-color:color-mix(in oklab,var(--ring)50%,transparent)}}body{background-color:var(--background);color:var(--foreground)}}@layer components;@layer utilities{.\@container\/card-header{container:card-header/inline-size}.pointer-events-none{pointer-events:none}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.inset-0{inset:calc(var(--spacing)*0)}.inset-x-0{inset-inline:calc(var(--spacing)*0)}.inset-y-0{inset-block:calc(var(--spacing)*0)}.top-0{top:calc(var(--spacing)*0)}.top-1\.5{top:calc(var(--spacing)*1.5)}.top-2\.5{top:calc(var(--spacing)*2.5)}.top-3\.5{top:calc(var(--spacing)*3.5)}.top-4{top:calc(var(--spacing)*4)}.top-\[50\%\]{top:50%}.right-0{right:calc(var(--spacing)*0)}.right-1{right:calc(var(--spacing)*1)}.right-2{right:calc(var(--spacing)*2)}.right-3{right:calc(var(--spacing)*3)}.right-4{right:calc(var(--spacing)*4)}.bottom-0{bottom:calc(var(--spacing)*0)}.left-0{left:calc(var(--spacing)*0)}.left-2{left:calc(var(--spacing)*2)}.left-\[50\%\]{left:50%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.col-start-2{grid-column-start:2}.row-span-2{grid-row:span 2/span 2}.row-start-1{grid-row-start:1}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.-mx-1{margin-inline:calc(var(--spacing)*-1)}.mx-2{margin-inline:calc(var(--spacing)*2)}.mx-3\.5{margin-inline:calc(var(--spacing)*3.5)}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing)*1)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-auto{margin-top:auto}.mr-1{margin-right:calc(var(--spacing)*1)}.mr-2{margin-right:calc(var(--spacing)*2)}.mr-4{margin-right:calc(var(--spacing)*4)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-auto{margin-left:auto}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.table-row{display:table-row}.field-sizing-content{field-sizing:content}.aspect-square{aspect-ratio:1}.size-2{width:calc(var(--spacing)*2);height:calc(var(--spacing)*2)}.size-2\.5{width:calc(var(--spacing)*2.5);height:calc(var(--spacing)*2.5)}.size-3\.5{width:calc(var(--spacing)*3.5);height:calc(var(--spacing)*3.5)}.size-4{width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.size-7{width:calc(var(--spacing)*7);height:calc(var(--spacing)*7)}.size-9{width:calc(var(--spacing)*9);height:calc(var(--spacing)*9)}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-8{height:calc(var(--spacing)*8)}.h-9{height:calc(var(--spacing)*9)}.h-10{height:calc(var(--spacing)*10)}.h-12{height:calc(var(--spacing)*12)}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.h-svh{height:100svh}.max-h-\(--radix-dropdown-menu-content-available-height\){max-height:var(--radix-dropdown-menu-content-available-height)}.max-h-\(--radix-select-content-available-height\){max-height:var(--radix-select-content-available-height)}.max-h-56{max-height:calc(var(--spacing)*56)}.max-h-64{max-height:calc(var(--spacing)*64)}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-16{min-height:calc(var(--spacing)*16)}.min-h-screen{min-height:100vh}.min-h-svh{min-height:100svh}.w-\(--sidebar-width\){width:var(--sidebar-width)}.w-3{width:calc(var(--spacing)*3)}.w-3\/4{width:75%}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-24{width:calc(var(--spacing)*24)}.w-36{width:calc(var(--spacing)*36)}.w-40{width:calc(var(--spacing)*40)}.w-48{width:calc(var(--spacing)*48)}.w-56{width:calc(var(--spacing)*56)}.w-64{width:calc(var(--spacing)*64)}.w-72{width:calc(var(--spacing)*72)}.w-\[120px\]{width:120px}.w-\[160px\]{width:160px}.w-\[180px\]{width:180px}.w-\[200px\]{width:200px}.w-\[260px\]{width:260px}.w-\[360px\]{width:360px}.w-auto{width:auto}.w-fit{width:fit-content}.w-full{width:100%}.max-w-\(--skeleton-width\){max-width:var(--skeleton-width)}.max-w-\[70vw\]{max-width:70vw}.max-w-\[280px\]{max-width:280px}.max-w-\[calc\(100\%-2rem\)\]{max-width:calc(100% - 2rem)}.max-w-full{max-width:100%}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-5{min-width:calc(var(--spacing)*5)}.min-w-\[8rem\]{min-width:8rem}.min-w-\[360px\]{min-width:360px}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.min-w-full{min-width:100%}.flex-1{flex:1}.shrink-0{flex-shrink:0}.caption-bottom{caption-side:bottom}.origin-\(--radix-dropdown-menu-content-transform-origin\){transform-origin:var(--radix-dropdown-menu-content-transform-origin)}.origin-\(--radix-select-content-transform-origin\){transform-origin:var(--radix-select-content-transform-origin)}.origin-\(--radix-tooltip-content-transform-origin\){transform-origin:var(--radix-tooltip-content-transform-origin)}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-x-px{--tw-translate-x:-1px;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-\[-50\%\]{--tw-translate-x:-50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-px{--tw-translate-x:1px;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-y-\[-50\%\]{--tw-translate-y:-50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-y-\[calc\(-50\%_-_2px\)\]{--tw-translate-y: calc(-50% - 2px) ;translate:var(--tw-translate-x)var(--tw-translate-y)}.rotate-45{rotate:45deg}.animate-in{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.animate-pulse{animation:var(--animate-pulse)}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.scroll-my-1{scroll-margin-block:calc(var(--spacing)*1)}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-rows-\[auto_auto\]{grid-template-rows:auto auto}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-6{gap:calc(var(--spacing)*6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*2)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*4)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-x-reverse)))}.self-start{align-self:flex-start}.justify-self-end{justify-self:flex-end}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\[2px\]{border-radius:2px}.rounded-\[4px\]{border-radius:4px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:calc(var(--radius) + 4px)}.rounded-xs{border-radius:var(--radius-xs)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-input{border-color:var(--input)}.border-red-300{border-color:var(--color-red-300)}.border-sidebar-border{border-color:var(--sidebar-border)}.border-transparent{border-color:#0000}.bg-accent{background-color:var(--accent)}.bg-background{background-color:var(--background)}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black)50%,transparent)}}.bg-border{background-color:var(--border)}.bg-card{background-color:var(--card)}.bg-destructive{background-color:var(--destructive)}.bg-muted,.bg-muted\/50{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\/50{background-color:color-mix(in oklab,var(--muted)50%,transparent)}}.bg-popover{background-color:var(--popover)}.bg-primary{background-color:var(--primary)}.bg-red-50{background-color:var(--color-red-50)}.bg-secondary{background-color:var(--secondary)}.bg-sidebar{background-color:var(--sidebar)}.bg-sidebar-border{background-color:var(--sidebar-border)}.bg-slate-900{background-color:var(--color-slate-900)}.bg-transparent{background-color:#0000}.fill-current{fill:currentColor}.fill-primary{fill:var(--primary)}.p-0{padding:calc(var(--spacing)*0)}.p-1{padding:calc(var(--spacing)*1)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-6{padding:calc(var(--spacing)*6)}.px-1{padding-inline:calc(var(--spacing)*1)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-6{padding-inline:calc(var(--spacing)*6)}.px-8{padding-inline:calc(var(--spacing)*8)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-6{padding-block:calc(var(--spacing)*6)}.py-8{padding-block:calc(var(--spacing)*8)}.py-10{padding-block:calc(var(--spacing)*10)}.pt-4{padding-top:calc(var(--spacing)*4)}.pr-2{padding-right:calc(var(--spacing)*2)}.pr-8{padding-right:calc(var(--spacing)*8)}.pl-2{padding-left:calc(var(--spacing)*2)}.pl-8{padding-left:calc(var(--spacing)*8)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.align-top{vertical-align:top}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-loose{--tw-leading:var(--leading-loose);line-height:var(--leading-loose)}.leading-none{--tw-leading:1;line-height:1}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-balance{text-wrap:balance}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.text-accent-foreground{color:var(--accent-foreground)}.text-card-foreground{color:var(--card-foreground)}.text-current{color:currentColor}.text-destructive{color:var(--destructive)}.text-foreground{color:var(--foreground)}.text-muted-foreground{color:var(--muted-foreground)}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-red-500{color:var(--color-red-500)}.text-red-800{color:var(--color-red-800)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-sidebar-foreground,.text-sidebar-foreground\/70{color:var(--sidebar-foreground)}@supports (color:color-mix(in lab,red,red)){.text-sidebar-foreground\/70{color:color-mix(in oklab,var(--sidebar-foreground)70%,transparent)}}.text-slate-500{color:var(--color-slate-500)}.text-white{color:var(--color-white)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.underline-offset-4{text-underline-offset:4px}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow-\[0_0_0_1px_hsl\(var\(--sidebar-border\)\)\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,hsl(var(--sidebar-border)));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-sidebar-ring{--tw-ring-color:var(--sidebar-ring)}.ring-offset-background{--tw-ring-offset-color:var(--background)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,visibility,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[color\,box-shadow\]{transition-property:color,box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[left\,right\,width\]{transition-property:left,right,width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[margin\,opacity\]{transition-property:margin,opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\,height\,padding\]{transition-property:width,height,padding;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-none{transition-property:none}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-linear{--tw-ease:linear;transition-timing-function:linear}.fade-in-0{--tw-enter-opacity:0}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.zoom-in-95{--tw-enter-scale:.95}.running{animation-play-state:running}.group-focus-within\/menu-item\:opacity-100:is(:where(.group\/menu-item):focus-within *){opacity:1}@media (hover:hover){.group-hover\/menu-item\:opacity-100:is(:where(.group\/menu-item):hover *){opacity:1}}.group-has-data-\[sidebar\=menu-action\]\/menu-item\:pr-8:is(:where(.group\/menu-item):has([data-sidebar=menu-action]) *){padding-right:calc(var(--spacing)*8)}.group-data-\[collapsible\=icon\]\:-mt-8:is(:where(.group)[data-collapsible=icon] *){margin-top:calc(var(--spacing)*-8)}.group-data-\[collapsible\=icon\]\:hidden:is(:where(.group)[data-collapsible=icon] *){display:none}.group-data-\[collapsible\=icon\]\:size-8\!:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--spacing)*8)!important;height:calc(var(--spacing)*8)!important}.group-data-\[collapsible\=icon\]\:w-\(--sidebar-width-icon\):is(:where(.group)[data-collapsible=icon] *){width:var(--sidebar-width-icon)}.group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\)\]:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--spacing)*4)))}.group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\+2px\)\]:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--spacing)*4)) + 2px)}.group-data-\[collapsible\=icon\]\:overflow-hidden:is(:where(.group)[data-collapsible=icon] *){overflow:hidden}.group-data-\[collapsible\=icon\]\:p-0\!:is(:where(.group)[data-collapsible=icon] *){padding:calc(var(--spacing)*0)!important}.group-data-\[collapsible\=icon\]\:p-2\!:is(:where(.group)[data-collapsible=icon] *){padding:calc(var(--spacing)*2)!important}.group-data-\[collapsible\=icon\]\:opacity-0:is(:where(.group)[data-collapsible=icon] *){opacity:0}.group-data-\[collapsible\=offcanvas\]\:right-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible=offcanvas] *){right:calc(var(--sidebar-width)*-1)}.group-data-\[collapsible\=offcanvas\]\:left-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible=offcanvas] *){left:calc(var(--sidebar-width)*-1)}.group-data-\[collapsible\=offcanvas\]\:w-0:is(:where(.group)[data-collapsible=offcanvas] *){width:calc(var(--spacing)*0)}.group-data-\[collapsible\=offcanvas\]\:translate-x-0:is(:where(.group)[data-collapsible=offcanvas] *){--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.group-data-\[disabled\=true\]\:pointer-events-none:is(:where(.group)[data-disabled=true] *){pointer-events:none}.group-data-\[disabled\=true\]\:opacity-50:is(:where(.group)[data-disabled=true] *){opacity:.5}.group-data-\[side\=left\]\:-right-4:is(:where(.group)[data-side=left] *){right:calc(var(--spacing)*-4)}.group-data-\[side\=left\]\:border-r:is(:where(.group)[data-side=left] *){border-right-style:var(--tw-border-style);border-right-width:1px}.group-data-\[side\=right\]\:left-0:is(:where(.group)[data-side=right] *){left:calc(var(--spacing)*0)}.group-data-\[side\=right\]\:rotate-180:is(:where(.group)[data-side=right] *){rotate:180deg}.group-data-\[side\=right\]\:border-l:is(:where(.group)[data-side=right] *){border-left-style:var(--tw-border-style);border-left-width:1px}.group-data-\[state\=open\]\/collapsible\:rotate-180:is(:where(.group\/collapsible)[data-state=open] *){rotate:180deg}.group-data-\[variant\=floating\]\:rounded-lg:is(:where(.group)[data-variant=floating] *){border-radius:var(--radius)}.group-data-\[variant\=floating\]\:border:is(:where(.group)[data-variant=floating] *){border-style:var(--tw-border-style);border-width:1px}.group-data-\[variant\=floating\]\:border-sidebar-border:is(:where(.group)[data-variant=floating] *){border-color:var(--sidebar-border)}.group-data-\[variant\=floating\]\:shadow-sm:is(:where(.group)[data-variant=floating] *){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media (hover:hover){.peer-hover\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button):hover~*){color:var(--sidebar-accent-foreground)}}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-50:is(:where(.peer):disabled~*){opacity:.5}.peer-data-\[active\=true\]\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button)[data-active=true]~*){color:var(--sidebar-accent-foreground)}.peer-data-\[size\=default\]\/menu-button\:top-1\.5:is(:where(.peer\/menu-button)[data-size=default]~*){top:calc(var(--spacing)*1.5)}.peer-data-\[size\=lg\]\/menu-button\:top-2\.5:is(:where(.peer\/menu-button)[data-size=lg]~*){top:calc(var(--spacing)*2.5)}.peer-data-\[size\=sm\]\/menu-button\:top-1:is(:where(.peer\/menu-button)[data-size=sm]~*){top:calc(var(--spacing)*1)}.selection\:bg-primary ::selection{background-color:var(--primary)}.selection\:bg-primary::selection{background-color:var(--primary)}.selection\:text-primary-foreground ::selection{color:var(--primary-foreground)}.selection\:text-primary-foreground::selection{color:var(--primary-foreground)}.file\:inline-flex::file-selector-button{display:inline-flex}.file\:h-7::file-selector-button{height:calc(var(--spacing)*7)}.file\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\:bg-transparent::file-selector-button{background-color:#0000}.file\:text-sm::file-selector-button{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.file\:font-medium::file-selector-button{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.file\:text-foreground::file-selector-button{color:var(--foreground)}.placeholder\:text-muted-foreground::placeholder{color:var(--muted-foreground)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:-inset-2:after{content:var(--tw-content);inset:calc(var(--spacing)*-2)}.after\:inset-y-0:after{content:var(--tw-content);inset-block:calc(var(--spacing)*0)}.after\:left-1\/2:after{content:var(--tw-content);left:50%}.after\:w-\[2px\]:after{content:var(--tw-content);width:2px}.group-data-\[collapsible\=offcanvas\]\:after\:left-full:is(:where(.group)[data-collapsible=offcanvas] *):after{content:var(--tw-content);left:100%}@media (hover:hover){.hover\:bg-accent:hover{background-color:var(--accent)}.hover\:bg-destructive\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab,var(--destructive)90%,transparent)}}.hover\:bg-muted\/50:hover{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-muted\/50:hover{background-color:color-mix(in oklab,var(--muted)50%,transparent)}}.hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--primary)90%,transparent)}}.hover\:bg-secondary\/80:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab,var(--secondary)80%,transparent)}}.hover\:bg-sidebar-accent:hover{background-color:var(--sidebar-accent)}.hover\:text-accent-foreground:hover{color:var(--accent-foreground)}.hover\:text-sidebar-accent-foreground:hover{color:var(--sidebar-accent-foreground)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-90:hover{opacity:.9}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-\[0_0_0_1px_hsl\(var\(--sidebar-accent\)\)\]:hover{--tw-shadow:0 0 0 1px var(--tw-shadow-color,hsl(var(--sidebar-accent)));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:group-data-\[collapsible\=offcanvas\]\:bg-sidebar:hover:is(:where(.group)[data-collapsible=offcanvas] *){background-color:var(--sidebar)}.hover\:after\:bg-sidebar-border:hover:after{content:var(--tw-content);background-color:var(--sidebar-border)}}.focus\:bg-accent:focus{background-color:var(--accent)}.focus\:text-accent-foreground:focus{color:var(--accent-foreground)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-ring:focus{--tw-ring-color:var(--ring)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\:outline-hidden:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.focus\:outline-hidden:focus{outline-offset:2px;outline:2px solid #0000}}.focus-visible\:border-ring:focus-visible{border-color:var(--ring)}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-\[3px\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(3px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:color-mix(in oklab,var(--ring)50%,transparent)}}.active\:bg-sidebar-accent:active{background-color:var(--sidebar-accent)}.active\:text-sidebar-accent-foreground:active{color:var(--sidebar-accent-foreground)}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}:where([data-side=left]) .in-data-\[side\=left\]\:cursor-w-resize{cursor:w-resize}:where([data-side=right]) .in-data-\[side\=right\]\:cursor-e-resize{cursor:e-resize}.has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has([data-slot=card-action]){grid-template-columns:1fr auto}.has-data-\[variant\=inset\]\:bg-sidebar:has([data-variant=inset]){background-color:var(--sidebar)}.has-\[\>svg\]\:px-2\.5:has(>svg){padding-inline:calc(var(--spacing)*2.5)}.has-\[\>svg\]\:px-3:has(>svg){padding-inline:calc(var(--spacing)*3)}.has-\[\>svg\]\:px-4:has(>svg){padding-inline:calc(var(--spacing)*4)}.aria-disabled\:pointer-events-none[aria-disabled=true]{pointer-events:none}.aria-disabled\:opacity-50[aria-disabled=true]{opacity:.5}.aria-invalid\:border-destructive[aria-invalid=true]{border-color:var(--destructive)}.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.data-\[active\=true\]\:bg-sidebar-accent[data-active=true]{background-color:var(--sidebar-accent)}.data-\[active\=true\]\:font-medium[data-active=true]{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.data-\[active\=true\]\:text-sidebar-accent-foreground[data-active=true]{color:var(--sidebar-accent-foreground)}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[error\=true\]\:text-destructive[data-error=true]{color:var(--destructive)}.data-\[inset\]\:pl-8[data-inset]{padding-left:calc(var(--spacing)*8)}.data-\[orientation\=horizontal\]\:h-px[data-orientation=horizontal]{height:1px}.data-\[orientation\=horizontal\]\:w-full[data-orientation=horizontal]{width:100%}.data-\[orientation\=vertical\]\:h-full[data-orientation=vertical]{height:100%}.data-\[orientation\=vertical\]\:w-px[data-orientation=vertical]{width:1px}.data-\[placeholder\]\:text-muted-foreground[data-placeholder]{color:var(--muted-foreground)}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:calc(var(--spacing)*2*-1)}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x:calc(var(--spacing)*-1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:calc(var(--spacing)*2)}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:calc(var(--spacing)*2*-1)}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y:calc(var(--spacing)*-1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:calc(var(--spacing)*2)}.data-\[size\=default\]\:h-9[data-size=default]{height:calc(var(--spacing)*9)}.data-\[size\=sm\]\:h-8[data-size=sm]{height:calc(var(--spacing)*8)}:is(.\*\:data-\[slot\=select-value\]\:line-clamp-1>*)[data-slot=select-value]{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}:is(.\*\:data-\[slot\=select-value\]\:flex>*)[data-slot=select-value]{display:flex}:is(.\*\:data-\[slot\=select-value\]\:items-center>*)[data-slot=select-value]{align-items:center}:is(.\*\:data-\[slot\=select-value\]\:gap-2>*)[data-slot=select-value]{gap:calc(var(--spacing)*2)}.data-\[state\=checked\]\:border-primary[data-state=checked]{border-color:var(--primary)}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:var(--primary)}.data-\[state\=checked\]\:text-primary-foreground[data-state=checked]{color:var(--primary-foreground)}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=closed\]\:duration-300[data-state=closed]{--tw-duration:.3s;transition-duration:.3s}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y:100%}.data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x:-100%}.data-\[state\=closed\]\:slide-out-to-right[data-state=closed]{--tw-exit-translate-x:100%}.data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y:-100%}.data-\[state\=open\]\:animate-in[data-state=open]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:var(--accent)}.data-\[state\=open\]\:bg-secondary[data-state=open]{background-color:var(--secondary)}.data-\[state\=open\]\:text-accent-foreground[data-state=open]{color:var(--accent-foreground)}.data-\[state\=open\]\:text-muted-foreground[data-state=open]{color:var(--muted-foreground)}.data-\[state\=open\]\:opacity-100[data-state=open]{opacity:1}.data-\[state\=open\]\:duration-500[data-state=open]{--tw-duration:.5s;transition-duration:.5s}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity:0}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.data-\[state\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y:100%}.data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x:-100%}.data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x:100%}.data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y:-100%}@media (hover:hover){.data-\[state\=open\]\:hover\:bg-sidebar-accent[data-state=open]:hover{background-color:var(--sidebar-accent)}.data-\[state\=open\]\:hover\:text-sidebar-accent-foreground[data-state=open]:hover{color:var(--sidebar-accent-foreground)}}.data-\[state\=selected\]\:bg-muted[data-state=selected]{background-color:var(--muted)}.data-\[variant\=destructive\]\:text-destructive[data-variant=destructive]{color:var(--destructive)}.data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:color-mix(in oklab,var(--destructive)10%,transparent)}}.data-\[variant\=destructive\]\:focus\:text-destructive[data-variant=destructive]:focus{color:var(--destructive)}@media (min-width:40rem){.sm\:flex{display:flex}.sm\:max-w-2xl{max-width:var(--container-2xl)}.sm\:max-w-\[480px\]{max-width:480px}.sm\:max-w-\[520px\]{max-width:520px}.sm\:max-w-lg{max-width:var(--container-lg)}.sm\:max-w-md{max-width:var(--container-md)}.sm\:max-w-sm{max-width:var(--container-sm)}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:justify-end{justify-content:flex-end}.sm\:text-left{text-align:left}}@media (min-width:48rem){.md\:col-span-2{grid-column:span 2/span 2}.md\:block{display:block}.md\:flex{display:flex}.md\:h-24{height:calc(var(--spacing)*24)}.md\:max-w-\[48ch\]{max-width:48ch}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:justify-between{justify-content:space-between}.md\:justify-end{justify-content:flex-end}.md\:gap-2{gap:calc(var(--spacing)*2)}.md\:truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.md\:px-0{padding-inline:calc(var(--spacing)*0)}.md\:py-0{padding-block:calc(var(--spacing)*0)}.md\:text-left{text-align:left}.md\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.md\:break-normal{overflow-wrap:normal;word-break:normal}.md\:opacity-0{opacity:0}.md\:peer-data-\[variant\=inset\]\:m-2:is(:where(.peer)[data-variant=inset]~*){margin:calc(var(--spacing)*2)}.md\:peer-data-\[variant\=inset\]\:ml-0:is(:where(.peer)[data-variant=inset]~*){margin-left:calc(var(--spacing)*0)}.md\:peer-data-\[variant\=inset\]\:rounded-xl:is(:where(.peer)[data-variant=inset]~*){border-radius:calc(var(--radius) + 4px)}.md\:peer-data-\[variant\=inset\]\:shadow-sm:is(:where(.peer)[data-variant=inset]~*){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.md\:peer-data-\[variant\=inset\]\:peer-data-\[state\=collapsed\]\:ml-2:is(:where(.peer)[data-variant=inset]~*):is(:where(.peer)[data-state=collapsed]~*){margin-left:calc(var(--spacing)*2)}.md\:after\:hidden:after{content:var(--tw-content);display:none}}@media (min-width:64rem){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}.dark\:border-input:is(.dark *){border-color:var(--input)}.dark\:bg-destructive\/60:is(.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-destructive\/60:is(.dark *){background-color:color-mix(in oklab,var(--destructive)60%,transparent)}}.dark\:bg-input\/30:is(.dark *){background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-input\/30:is(.dark *){background-color:color-mix(in oklab,var(--input)30%,transparent)}}@media (hover:hover){.dark\:hover\:bg-accent\/50:is(.dark *):hover{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-accent\/50:is(.dark *):hover{background-color:color-mix(in oklab,var(--accent)50%,transparent)}}.dark\:hover\:bg-input\/50:is(.dark *):hover{background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-input\/50:is(.dark *):hover{background-color:color-mix(in oklab,var(--input)50%,transparent)}}}.dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible{--tw-ring-color:color-mix(in oklab,var(--destructive)40%,transparent)}}.dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid=true]{--tw-ring-color:color-mix(in oklab,var(--destructive)40%,transparent)}}.dark\:data-\[state\=checked\]\:bg-primary:is(.dark *)[data-state=checked]{background-color:var(--primary)}.dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is(.dark *)[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is(.dark *)[data-variant=destructive]:focus{background-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 svg:not([class*=size-]){width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.\[\&_svg\:not\(\[class\*\=\'text-\'\]\)\]\:text-muted-foreground svg:not([class*=text-]){color:var(--muted-foreground)}.\[\&_tr\]\:border-b tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.\[\&_tr\:last-child\]\:border-0 tr:last-child{border-style:var(--tw-border-style);border-width:0}.\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]){padding-right:calc(var(--spacing)*0)}.\[\.border-b\]\:pb-6.border-b{padding-bottom:calc(var(--spacing)*6)}.\[\.border-t\]\:pt-6.border-t{padding-top:calc(var(--spacing)*6)}:is(.\*\:\[span\]\:last\:flex>*):is(span):last-child{display:flex}:is(.\*\:\[span\]\:last\:items-center>*):is(span):last-child{align-items:center}:is(.\*\:\[span\]\:last\:gap-2>*):is(span):last-child{gap:calc(var(--spacing)*2)}:is(.data-\[variant\=destructive\]\:\*\:\[svg\]\:\!text-destructive[data-variant=destructive]>*):is(svg){color:var(--destructive)!important}.\[\&\>\[role\=checkbox\]\]\:translate-y-\[2px\]>[role=checkbox]{--tw-translate-y:2px;translate:var(--tw-translate-x)var(--tw-translate-y)}.\[\&\>button\]\:hidden>button{display:none}.\[\&\>span\:last-child\]\:truncate>span:last-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.\[\&\>svg\]\:pointer-events-none>svg{pointer-events:none}.\[\&\>svg\]\:size-3>svg{width:calc(var(--spacing)*3);height:calc(var(--spacing)*3)}.\[\&\>svg\]\:size-4>svg{width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.\[\&\>svg\]\:shrink-0>svg{flex-shrink:0}.\[\&\>svg\]\:text-sidebar-accent-foreground>svg{color:var(--sidebar-accent-foreground)}.\[\&\>tr\]\:last\:border-b-0>tr:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}[data-side=left][data-collapsible=offcanvas] .\[\[data-side\=left\]\[data-collapsible\=offcanvas\]_\&\]\:-right-2{right:calc(var(--spacing)*-2)}[data-side=left][data-state=collapsed] .\[\[data-side\=left\]\[data-state\=collapsed\]_\&\]\:cursor-e-resize{cursor:e-resize}[data-side=right][data-collapsible=offcanvas] .\[\[data-side\=right\]\[data-collapsible\=offcanvas\]_\&\]\:-left-2{left:calc(var(--spacing)*-2)}[data-side=right][data-state=collapsed] .\[\[data-side\=right\]\[data-state\=collapsed\]_\&\]\:cursor-w-resize{cursor:w-resize}@media (hover:hover){a.\[a\&\]\:hover\:bg-accent:hover{background-color:var(--accent)}a.\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab,var(--destructive)90%,transparent)}}a.\[a\&\]\:hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--primary)90%,transparent)}}a.\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:color-mix(in oklab,var(--secondary)90%,transparent)}}a.\[a\&\]\:hover\:text-accent-foreground:hover{color:var(--accent-foreground)}}}@property --tw-animation-delay{syntax:"*";inherits:false;initial-value:0s}@property --tw-animation-direction{syntax:"*";inherits:false;initial-value:normal}@property --tw-animation-duration{syntax:"*";inherits:false}@property --tw-animation-fill-mode{syntax:"*";inherits:false;initial-value:none}@property --tw-animation-iteration-count{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-translate-y{syntax:"*";inherits:false;initial-value:0}:root{--radius:.625rem;--background:oklch(100% 0 0);--foreground:oklch(14.1% .005 285.823);--card:oklch(100% 0 0);--card-foreground:oklch(14.1% .005 285.823);--popover:oklch(100% 0 0);--popover-foreground:oklch(14.1% .005 285.823);--primary:oklch(21% .006 285.885);--primary-foreground:oklch(98.5% 0 0);--secondary:oklch(96.7% .001 286.375);--secondary-foreground:oklch(21% .006 285.885);--muted:oklch(96.7% .001 286.375);--muted-foreground:oklch(55.2% .016 285.938);--accent:oklch(96.7% .001 286.375);--accent-foreground:oklch(21% .006 285.885);--destructive:oklch(57.7% .245 27.325);--border:oklch(92% .004 286.32);--input:oklch(92% .004 286.32);--ring:oklch(70.5% .015 286.067);--chart-1:oklch(64.6% .222 41.116);--chart-2:oklch(60% .118 184.704);--chart-3:oklch(39.8% .07 227.392);--chart-4:oklch(82.8% .189 84.429);--chart-5:oklch(76.9% .188 70.08);--sidebar:oklch(98.5% 0 0);--sidebar-foreground:oklch(14.1% .005 285.823);--sidebar-primary:oklch(21% .006 285.885);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(96.7% .001 286.375);--sidebar-accent-foreground:oklch(21% .006 285.885);--sidebar-border:oklch(92% .004 286.32);--sidebar-ring:oklch(70.5% .015 286.067)}.dark{--background:oklch(14.1% .005 285.823);--foreground:oklch(98.5% 0 0);--card:oklch(21% .006 285.885);--card-foreground:oklch(98.5% 0 0);--popover:oklch(21% .006 285.885);--popover-foreground:oklch(98.5% 0 0);--primary:oklch(92% .004 286.32);--primary-foreground:oklch(21% .006 285.885);--secondary:oklch(27.4% .006 286.033);--secondary-foreground:oklch(98.5% 0 0);--muted:oklch(27.4% .006 286.033);--muted-foreground:oklch(70.5% .015 286.067);--accent:oklch(27.4% .006 286.033);--accent-foreground:oklch(98.5% 0 0);--destructive:oklch(70.4% .191 22.216);--border:oklch(100% 0 0/.1);--input:oklch(100% 0 0/.15);--ring:oklch(55.2% .016 285.938);--chart-1:oklch(48.8% .243 264.376);--chart-2:oklch(69.6% .17 162.48);--chart-3:oklch(76.9% .188 70.08);--chart-4:oklch(62.7% .265 303.9);--chart-5:oklch(64.5% .246 16.439);--sidebar:oklch(21% .006 285.885);--sidebar-foreground:oklch(98.5% 0 0);--sidebar-primary:oklch(48.8% .243 264.376);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(27.4% .006 286.033);--sidebar-accent-foreground:oklch(98.5% 0 0);--sidebar-border:oklch(100% 0 0/.1);--sidebar-ring:oklch(55.2% .016 285.938)}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes pulse{50%{opacity:.5}}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0)scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1))rotate(var(--tw-enter-rotate,0));filter:blur(var(--tw-enter-blur,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0)scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1))rotate(var(--tw-exit-rotate,0));filter:blur(var(--tw-exit-blur,0))}} diff --git a/internal/ui/dist/assets/index-DSxuk_EI.js b/internal/ui/dist/assets/index-DSxuk_EI.js deleted file mode 100644 index c17962b..0000000 --- a/internal/ui/dist/assets/index-DSxuk_EI.js +++ /dev/null @@ -1 +0,0 @@ -import{t as ia,m as oa,r as o,j as e,n as qs,z as yt,F as la,C as da,p as ca,q as ma,v as re,w as F,_ as Je,x as js,y as St,A as te,B as ae,D as ee,E as Oe,G as us,T as xa,J as ha,H as ua}from"./vendor-DvippHRz.js";import{S as Cs,R as ja,a as Ct,C as _t,b as kt,T as At,D as Et,P as $t,O as Lt,c as fa,d as pa,e as ga,f as va,g as ba,A as Na,h as wa,i as ya,j as Sa,k as Ca,l as _a,m as ka,n as Aa,I as Ea,o as $a,p as La,q as Da,r as Ia,s as Ta,t as Pa,u as Ma,v as Oa,w as za,x as Fa,y as Ra,z as Va,B as Ua,E as Ba,V as Ha,F as Ka,G as qa,H as Ga,J as Ja,K as Qa,L as Wa,M as Ya,N as Xa,Q as Za,U as en}from"./radix-DRmH1vcw.js";import{X as Dt,S as sn,M as tn,L as an,C as bs,H as nn,A as rn,B as on,a as ln,T as It,b as dn,c as hs,d as cn,K as mn,F as xn,e as hn,f as un,G as jn,g as fn,U as mt,h as pn,i as gn,j as vn,k as Gs,l as bn,m as Nn,P as Qe,n as rs,o as Ue,p as Ts,R as Ps,q as ye,r as qe,s as wn,t as $s,u as yn,v as Sn,w as Cn,x as _n,y as xt}from"./icons-B5E6SSBo.js";import{u as Ms,L as as,O as Js,N as Qs,a as _s,b as Tt,R as kn,c as se,B as An}from"./router-CANfZtzM.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const u of document.querySelectorAll('link[rel="modulepreload"]'))d(u);new MutationObserver(u=>{for(const f of u)if(f.type==="childList")for(const $ of f.addedNodes)$.tagName==="LINK"&&$.rel==="modulepreload"&&d($)}).observe(document,{childList:!0,subtree:!0});function r(u){const f={};return u.integrity&&(f.integrity=u.integrity),u.referrerPolicy&&(f.referrerPolicy=u.referrerPolicy),u.crossOrigin==="use-credentials"?f.credentials="include":u.crossOrigin==="anonymous"?f.credentials="omit":f.credentials="same-origin",f}function d(u){if(u.ep)return;u.ep=!0;const f=r(u);fetch(u.href,f)}})();function L(...s){return ia(oa(s))}function ht(s){return s.toLowerCase().trim().replace(/['"]/g,"").replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)+/g,"")}const Us=768;function En(){const[s,t]=o.useState(void 0);return o.useEffect(()=>{const r=window.matchMedia(`(max-width: ${Us-1}px)`),d=()=>{t(window.innerWidthr.removeEventListener("change",d)},[]),!!s}const Ws=qs("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",{variants:{variant:{default:"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",destructive:"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",outline:"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",secondary:"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-9 px-4 py-2 has-[>svg]:px-3",sm:"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",lg:"h-10 rounded-md px-6 has-[>svg]:px-4",icon:"size-9"}},defaultVariants:{variant:"default",size:"default"}});function h({className:s,variant:t,size:r,asChild:d=!1,...u}){const f=d?Cs:"button";return e.jsx(f,{"data-slot":"button",className:L(Ws({variant:t,size:r,className:s})),...u})}function z({className:s,type:t,...r}){return e.jsx("input",{type:t,"data-slot":"input",className:L("file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm","focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]","aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",s),...r})}function Ns({className:s,orientation:t="horizontal",decorative:r=!0,...d}){return e.jsx(ja,{"data-slot":"separator",decorative:r,orientation:t,className:L("bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",s),...d})}function $n({...s}){return e.jsx(Ct,{"data-slot":"sheet",...s})}function Ln({...s}){return e.jsx($t,{"data-slot":"sheet-portal",...s})}function Dn({className:s,...t}){return e.jsx(Lt,{"data-slot":"sheet-overlay",className:L("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",s),...t})}function In({className:s,children:t,side:r="right",...d}){return e.jsxs(Ln,{children:[e.jsx(Dn,{}),e.jsxs(_t,{"data-slot":"sheet-content",className:L("bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",r==="right"&&"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",r==="left"&&"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",r==="top"&&"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",r==="bottom"&&"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",s),...d,children:[t,e.jsxs(kt,{className:"ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none",children:[e.jsx(Dt,{className:"size-4"}),e.jsx("span",{className:"sr-only",children:"Close"})]})]})]})}function Tn({className:s,...t}){return e.jsx("div",{"data-slot":"sheet-header",className:L("flex flex-col gap-1.5 p-4",s),...t})}function Pn({className:s,...t}){return e.jsx(At,{"data-slot":"sheet-title",className:L("text-foreground font-semibold",s),...t})}function Mn({className:s,...t}){return e.jsx(Et,{"data-slot":"sheet-description",className:L("text-muted-foreground text-sm",s),...t})}function Ge({className:s,...t}){return e.jsx("div",{"data-slot":"skeleton",className:L("bg-accent animate-pulse rounded-md",s),...t})}function Os({delayDuration:s=0,...t}){return e.jsx(fa,{"data-slot":"tooltip-provider",delayDuration:s,...t})}function Ys({...s}){return e.jsx(Os,{children:e.jsx(pa,{"data-slot":"tooltip",...s})})}function Xs({...s}){return e.jsx(ga,{"data-slot":"tooltip-trigger",...s})}function Zs({className:s,sideOffset:t=0,children:r,...d}){return e.jsx(va,{children:e.jsxs(ba,{"data-slot":"tooltip-content",sideOffset:t,className:L("bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",s),...d,children:[r,e.jsx(Na,{className:"bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]"})]})})}const On="sidebar_state",zn=3600*24*7,Fn="16rem",Rn="18rem",Vn="3rem",Un="b",Pt=o.createContext(null);function Mt(){const s=o.useContext(Pt);if(!s)throw new Error("useSidebar must be used within a SidebarProvider.");return s}function Bn({defaultOpen:s=!0,open:t,onOpenChange:r,className:d,style:u,children:f,...$}){const C=En(),[O,M]=o.useState(!1),[R,U]=o.useState(s),T=t??R,P=o.useCallback(m=>{const x=typeof m=="function"?m(T):m;r?r(x):U(x),document.cookie=`${On}=${x}; path=/; max-age=${zn}`},[r,T]),j=o.useCallback(()=>C?M(m=>!m):P(m=>!m),[C,P,M]);o.useEffect(()=>{const m=x=>{x.key===Un&&(x.metaKey||x.ctrlKey)&&(x.preventDefault(),j())};return window.addEventListener("keydown",m),()=>window.removeEventListener("keydown",m)},[j]);const V=T?"expanded":"collapsed",H=o.useMemo(()=>({state:V,open:T,setOpen:P,isMobile:C,openMobile:O,setOpenMobile:M,toggleSidebar:j}),[V,T,P,C,O,M,j]);return e.jsx(Pt.Provider,{value:H,children:e.jsx(Os,{delayDuration:0,children:e.jsx("div",{"data-slot":"sidebar-wrapper",style:{"--sidebar-width":Fn,"--sidebar-width-icon":Vn,...u},className:L("group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full",d),...$,children:f})})})}function Hn({side:s="left",variant:t="sidebar",collapsible:r="offcanvas",className:d,children:u,...f}){const{isMobile:$,state:C,openMobile:O,setOpenMobile:M}=Mt();return r==="none"?e.jsx("div",{"data-slot":"sidebar",className:L("bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col",d),...f,children:u}):$?e.jsx($n,{open:O,onOpenChange:M,...f,children:e.jsxs(In,{"data-sidebar":"sidebar","data-slot":"sidebar","data-mobile":"true",className:"bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden",style:{"--sidebar-width":Rn},side:s,children:[e.jsxs(Tn,{className:"sr-only",children:[e.jsx(Pn,{children:"Sidebar"}),e.jsx(Mn,{children:"Displays the mobile sidebar."})]}),e.jsx("div",{className:"flex h-full w-full flex-col",children:u})]})}):e.jsxs("div",{className:"group peer text-sidebar-foreground hidden md:block","data-state":C,"data-collapsible":C==="collapsed"?r:"","data-variant":t,"data-side":s,"data-slot":"sidebar",children:[e.jsx("div",{"data-slot":"sidebar-gap",className:L("relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear","group-data-[collapsible=offcanvas]:w-0","group-data-[side=right]:rotate-180",t==="floating"||t==="inset"?"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]":"group-data-[collapsible=icon]:w-(--sidebar-width-icon)")}),e.jsx("div",{"data-slot":"sidebar-container",className:L("fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",s==="left"?"left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]":"right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",t==="floating"||t==="inset"?"p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]":"group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",d),...f,children:e.jsx("div",{"data-sidebar":"sidebar","data-slot":"sidebar-inner",className:"bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm",children:u})})]})}function Kn({className:s,...t}){return e.jsx("div",{"data-slot":"sidebar-header","data-sidebar":"header",className:L("flex flex-col gap-2 p-2",s),...t})}function qn({className:s,...t}){return e.jsx("div",{"data-slot":"sidebar-footer","data-sidebar":"footer",className:L("flex flex-col gap-2 p-2",s),...t})}function Gn({className:s,...t}){return e.jsx("div",{"data-slot":"sidebar-content","data-sidebar":"content",className:L("flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",s),...t})}function Jn({className:s,...t}){return e.jsx("div",{"data-slot":"sidebar-group","data-sidebar":"group",className:L("relative flex w-full min-w-0 flex-col p-2",s),...t})}function Qn({className:s,asChild:t=!1,...r}){const d=t?Cs:"div";return e.jsx(d,{"data-slot":"sidebar-group-label","data-sidebar":"group-label",className:L("text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0","group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",s),...r})}function Wn({className:s,...t}){return e.jsx("div",{"data-slot":"sidebar-group-content","data-sidebar":"group-content",className:L("w-full text-sm",s),...t})}function Yn({className:s,...t}){return e.jsx("ul",{"data-slot":"sidebar-menu","data-sidebar":"menu",className:L("flex w-full min-w-0 flex-col gap-1",s),...t})}function Xn({className:s,...t}){return e.jsx("li",{"data-slot":"sidebar-menu-item","data-sidebar":"menu-item",className:L("group/menu-item relative",s),...t})}const Zn=qs("peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",{variants:{variant:{default:"hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",outline:"bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]"},size:{default:"h-8 text-sm",sm:"h-7 text-xs",lg:"h-12 text-sm group-data-[collapsible=icon]:p-0!"}},defaultVariants:{variant:"default",size:"default"}});function er({asChild:s=!1,isActive:t=!1,variant:r="default",size:d="default",tooltip:u,className:f,...$}){const C=s?Cs:"button",{isMobile:O,state:M}=Mt(),R=e.jsx(C,{"data-slot":"sidebar-menu-button","data-sidebar":"menu-button","data-size":d,"data-active":t,className:L(Zn({variant:r,size:d}),f),...$});return u?(typeof u=="string"&&(u={children:u}),e.jsxs(Ys,{children:[e.jsx(Xs,{asChild:!0,children:R}),e.jsx(Zs,{side:"right",align:"center",hidden:M!=="collapsed"||O,...u})]})):R}const Ls="";class je extends Error{status;body;constructor(t,r,d){super(r),this.status=t,this.body=d}}function sr(s){const t={};if(!s)return t;if(s instanceof Headers)s.forEach((r,d)=>t[d]=r);else if(Array.isArray(s))for(const[r,d]of s)t[r]=d;else Object.assign(t,s);return t}function tr(){const s={},t=localStorage.getItem("access_token");return t&&(s.Authorization=`Bearer ${t}`),s}function ar(){const s=localStorage.getItem("active_org_id");return s?{"X-Org-ID":s}:{}}async function fs(s,t,r,d={}){const f={...{"Content-Type":"application/json"},...d.auth===!1?{}:tr(),...ar(),...sr(d.headers)},$=await fetch(`${Ls}${s}`,{method:t,headers:f,body:r===void 0?void 0:JSON.stringify(r),...d}),O=($.headers.get("content-type")||"").includes("application/json"),M=O?await $.json().catch(()=>{}):await $.text().catch(()=>"");if(!$.ok){const R=O&&M&&typeof M=="object"&&"error"in M&&M.error||O&&M&&typeof M=="object"&&"message"in M&&M.message||typeof M=="string"&&M||`HTTP ${$.status}`;throw new je($.status,String(R),M)}return console.debug("API ->",t,`${Ls}${s}`,f),O?M:void 0}const g={get:(s,t)=>fs(s,"GET",void 0,t),post:(s,t,r)=>fs(s,"POST",t,r),put:(s,t,r)=>fs(s,"PUT",t,r),patch:(s,t,r)=>fs(s,"PATCH",t,r),delete:(s,t)=>fs(s,"DELETE",void 0,t)};function nr(s){return s&&(s.user||s.user_id)}function Ot(s){return nr(s)?.role==="admin"}function rr(s){return(s?.org_role??"")==="admin"}const Ve={isAuthenticated(){return!!localStorage.getItem("access_token")},async login(s,t){const r=await g.post("/api/v1/auth/login",{email:s,password:t});localStorage.setItem("access_token",r.access_token),localStorage.setItem("refresh_token",r.refresh_token)},async register(s,t,r){await g.post("/api/v1/auth/register",{name:s,email:t,password:r})},async me(){return await g.get("/api/v1/auth/me")},async logout(){const s=localStorage.getItem("refresh_token");if(s)try{await g.post("/api/v1/auth/logout",{refresh_token:s})}catch{}localStorage.removeItem("access_token"),localStorage.removeItem("refresh_token")},async forgot(s){await g.post("/api/v1/auth/password/forgot",{email:s})},async reset(s,t){await g.post("/api/v1/auth/password/reset",{token:s,new_password:t})},async verify(s){const t=await fetch(`${Ls}/api/v1/auth/verify?token=${encodeURIComponent(s)}`);if(!t.ok){const r=await t.text();throw new Error(r)}}};function ir({...s}){return e.jsx(wa,{"data-slot":"collapsible",...s})}function or({...s}){return e.jsx(ya,{"data-slot":"collapsible-trigger",...s})}function lr({...s}){return e.jsx(Sa,{"data-slot":"collapsible-content",...s})}function is({...s}){return e.jsx(Ca,{"data-slot":"dropdown-menu",...s})}function os({...s}){return e.jsx(_a,{"data-slot":"dropdown-menu-trigger",...s})}function ls({className:s,sideOffset:t=4,...r}){return e.jsx(ka,{children:e.jsx(Aa,{"data-slot":"dropdown-menu-content",sideOffset:t,className:L("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",s),...r})})}function Ie({className:s,inset:t,variant:r="default",...d}){return e.jsx(Ea,{"data-slot":"dropdown-menu-item","data-inset":t,"data-variant":r,className:L("focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",s),...d})}function dr(){const{setTheme:s,theme:t}=yt();return e.jsxs(is,{children:[e.jsx(os,{asChild:!0,children:e.jsx(h,{variant:"outline",size:"icon","aria-label":"Toggle theme",children:t==="light"?e.jsx(sn,{className:"h-5 w-5"}):t==="dark"?e.jsx(tn,{className:"h-5 w-5"}):e.jsx(an,{className:"h-5 w-5"})})}),e.jsxs(ls,{align:"end",children:[e.jsxs(Ie,{onClick:()=>s("light"),children:[t==="light"&&e.jsx(bs,{}),"Light"]}),e.jsxs(Ie,{onClick:()=>s("dark"),children:[t==="dark"&&e.jsx(bs,{}),"Dark"]}),e.jsxs(Ie,{onClick:()=>s("system"),children:[t==="system"&&e.jsx(bs,{}),"System"]})]})]})}const Ks="active_org_id",ns="active-org-changed",ys="orgs-changed";function Ke(){return localStorage.getItem(Ks)}function ws(s){s?localStorage.setItem(Ks,s):localStorage.removeItem(Ks),window.dispatchEvent(new CustomEvent(ns,{detail:s}))}function ut(){window.dispatchEvent(new Event(ys))}const cr=()=>{const[s,t]=o.useState([]),[r,d]=o.useState(null);async function u(){try{const C=await g.get("/api/v1/orgs");t(C),!Ke()&&C.length>0&&(ws(C[0].id),d(C[0].id))}catch(C){const O=C instanceof je?C.message:"Failed to load organizations";console.error(O)}}o.useEffect(()=>{d(Ke()),u();const C=R=>{R.key==="active_org_id"&&d(R.newValue)};window.addEventListener("storage",C);const O=R=>d(R.detail??null),M=()=>void u();return window.addEventListener(ns,O),window.addEventListener(ys,M),()=>{window.removeEventListener("storage",C),window.removeEventListener(ns,O),window.removeEventListener(ys,M)}},[]);const f=C=>{ws(C),d(C)},$=s.find(C=>C.id===r)?.name??"Select Org";return e.jsxs(is,{children:[e.jsx(os,{asChild:!0,children:e.jsx(h,{variant:"outline",className:"w-full justify-start",children:$})}),e.jsx(ls,{className:"w-48",children:s.length===0?e.jsx(Ie,{disabled:!0,children:"No organizations"}):s.map(C=>e.jsx(Ie,{onClick:()=>f(C.id),className:C.id===r?"font-semibold":void 0,children:C.name},C.id))})]})},mr=[{label:"Dashboard",icon:nn,to:"/dashboard"},{label:"Core",icon:cn,items:[{label:"Cluster",to:"/core/clusters",icon:rn},{label:"Node Pools",icon:on,to:"/core/nodepools"},{label:"Annotations",icon:ln,to:"/core/annotations"},{label:"Labels",icon:It,to:"/core/labels"},{label:"Taints",icon:dn,to:"/core/taints"},{label:"Servers",icon:hs,to:"/core/servers"}]},{label:"Security",icon:hn,items:[{label:"Keys & Tokens",icon:mn,to:"/security/keys"},{label:"SSH Keys",to:"/security/ssh",icon:xn}]},{label:"Tasks",icon:un,items:[]},{label:"Settings",icon:gn,items:[{label:"Jobs",icon:jn,to:"/settings/jobs"},{label:"Organizations",to:"/settings/orgs",icon:fn},{label:"Members",to:"/settings/members",icon:mt},{label:"Profile",to:"/settings/me",icon:pn}]},{label:"Admin",icon:vn,requiresAdmin:!0,items:[{label:"Users",to:"/admin/users",icon:mt,requiresAdmin:!0}]}];function zt(s,t,r){return s.filter(d=>!(d.requiresAdmin&&!t||d.requiresOrgAdmin&&!r)).map(d=>({...d,items:d.items?zt(d.items,t,r):void 0})).filter(d=>!d.items||d.items.length>0)}const Ft=({item:s})=>{const t=Ms(),r=s.icon;return s.to?e.jsxs(as,{to:s.to,className:`hover:bg-accent hover:text-accent-foreground flex items-center space-x-2 rounded-md px-4 py-2 text-sm ${t.pathname===s.to?"bg-accent text-accent-foreground":""}`,children:[e.jsx(r,{className:"mr-4 h-4 w-4"}),s.label]}):s.items?e.jsx(ir,{defaultOpen:!0,className:"group/collapsible",children:e.jsxs(Jn,{children:[e.jsx(Qn,{asChild:!0,children:e.jsxs(or,{children:[e.jsx(r,{className:"mr-4 h-4 w-4"}),s.label,e.jsx(Gs,{className:"ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180"})]})}),e.jsx(lr,{children:e.jsx(Wn,{children:e.jsx(Yn,{children:s.items.map((d,u)=>e.jsx(Xn,{children:e.jsx(er,{asChild:!0,children:e.jsx(Ft,{item:d})})},u))})})})]})}):null},xr=()=>{const[s,t]=o.useState(null),[r,d]=o.useState(!0);o.useEffect(()=>{let f=!0;return(async()=>{try{const $=await Ve.me();if(!f)return;t($)}catch{}finally{d(!1)}})(),()=>{f=!1}},[]);const u=o.useMemo(()=>{const f=Ot(s),$=rr(s);return zt(mr,f,$)},[s]);return r?e.jsx("div",{className:"p-6",children:"Loading…"}):e.jsxs(Hn,{children:[e.jsx(Kn,{className:"flex items-center justify-between p-4",children:e.jsx("h1",{className:"text-xl font-bold",children:"AutoGlue"})}),e.jsx(Gn,{children:u.map((f,$)=>e.jsx(Ft,{item:f},$))}),e.jsxs(qn,{className:"space-y-2 p-4",children:[e.jsx(cr,{}),e.jsx(dr,{}),e.jsx(h,{onClick:()=>{localStorage.clear(),window.location.reload()},className:"w-full",children:"Logout"})]})]})};function hr(){return e.jsx("footer",{className:"border-t",children:e.jsxs("div",{className:"container flex flex-col items-center justify-between gap-4 py-10 md:h-24 md:flex-row md:py-0",children:[e.jsx("div",{className:"flex flex-col items-center gap-4 px-8 md:flex-row md:gap-2 md:px-0",children:e.jsxs("p",{className:"text-muted-foreground text-center text-sm leading-loose md:text-left",children:["Built for"," ",e.jsx("a",{href:"https://www.glueops.dev/",target:"_blank",rel:"noreferrer",className:"font-medium underline underline-offset-4",children:"GlueOps"}),". The source code is available on"," ",e.jsx("a",{href:"https://github.com/GlueOps/autoglue",target:"_blank",rel:"noreferrer",className:"font-medium underline underline-offset-4",children:"GitHub"}),"."]})}),e.jsx("div",{className:"flex items-center space-x-4",children:e.jsx("a",{href:"https://github.com/GlueOps/autoglue",target:"_blank",rel:"noreferrer",children:e.jsx(bn,{className:"h-5 w-5"})})})]})})}function ur(){return e.jsx("div",{className:"flex h-screen",children:e.jsxs(Bn,{children:[e.jsx(xr,{}),e.jsxs("div",{className:"flex flex-1 flex-col",children:[e.jsx("main",{className:"flex-1 overflow-auto p-4",children:e.jsx(Js,{})}),e.jsx(hr,{})]})]})})}function jr({children:s}){const t=Ms();return Ve.isAuthenticated()?s?e.jsx(e.Fragment,{children:s}):e.jsx(Js,{}):e.jsx(Qs,{to:"/auth/login",state:{from:t},replace:!0})}function fr({children:s}){const[t,r]=o.useState(!0),[d,u]=o.useState(!1),f=Ms();return o.useEffect(()=>{let $=!0;return(async()=>{try{const C=await Ve.me();if(!$)return;u(Ot(C))}catch{if(!$)return;u(!1)}finally{if(r(!1),!$)return}})(),()=>{$=!1}},[]),t?null:d?s?e.jsx(e.Fragment,{children:s}):e.jsx(Js,{}):e.jsx(Qs,{to:"/403",replace:!0,state:{from:f}})}function et({...s}){return e.jsx($a,{"data-slot":"alert-dialog",...s})}function st({...s}){return e.jsx(La,{"data-slot":"alert-dialog-trigger",...s})}function pr({...s}){return e.jsx(Oa,{"data-slot":"alert-dialog-portal",...s})}function gr({className:s,...t}){return e.jsx(za,{"data-slot":"alert-dialog-overlay",className:L("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",s),...t})}function tt({className:s,...t}){return e.jsxs(pr,{children:[e.jsx(gr,{}),e.jsx(Da,{"data-slot":"alert-dialog-content",className:L("bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",s),...t})]})}function at({className:s,...t}){return e.jsx("div",{"data-slot":"alert-dialog-header",className:L("flex flex-col gap-2 text-center sm:text-left",s),...t})}function nt({className:s,...t}){return e.jsx("div",{"data-slot":"alert-dialog-footer",className:L("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",s),...t})}function rt({className:s,...t}){return e.jsx(Ia,{"data-slot":"alert-dialog-title",className:L("text-lg font-semibold",s),...t})}function it({className:s,...t}){return e.jsx(Ta,{"data-slot":"alert-dialog-description",className:L("text-muted-foreground text-sm",s),...t})}function ot({className:s,...t}){return e.jsx(Ma,{className:L(Ws(),s),...t})}function lt({className:s,...t}){return e.jsx(Pa,{className:L(Ws({variant:"outline"}),s),...t})}function Te({className:s,...t}){return e.jsx("div",{"data-slot":"card",className:L("bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",s),...t})}function Pe({className:s,...t}){return e.jsx("div",{"data-slot":"card-header",className:L("@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",s),...t})}function Be({className:s,...t}){return e.jsx("div",{"data-slot":"card-title",className:L("leading-none font-semibold",s),...t})}function Me({className:s,...t}){return e.jsx("div",{"data-slot":"card-content",className:L("px-6",s),...t})}function Ss({className:s,...t}){return e.jsx("div",{"data-slot":"card-footer",className:L("flex items-center px-6 [.border-t]:pt-6",s),...t})}function ie({...s}){return e.jsx(Ct,{"data-slot":"dialog",...s})}function We({...s}){return e.jsx(Fa,{"data-slot":"dialog-trigger",...s})}function vr({...s}){return e.jsx($t,{"data-slot":"dialog-portal",...s})}function br({className:s,...t}){return e.jsx(Lt,{"data-slot":"dialog-overlay",className:L("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",s),...t})}function oe({className:s,children:t,showCloseButton:r=!0,...d}){return e.jsxs(vr,{"data-slot":"dialog-portal",children:[e.jsx(br,{}),e.jsxs(_t,{"data-slot":"dialog-content",className:L("bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",s),...d,children:[t,r&&e.jsxs(kt,{"data-slot":"dialog-close",className:"ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",children:[e.jsx(Dt,{}),e.jsx("span",{className:"sr-only",children:"Close"})]})]})]})}function le({className:s,...t}){return e.jsx("div",{"data-slot":"dialog-header",className:L("flex flex-col gap-2 text-center sm:text-left",s),...t})}function de({className:s,...t}){return e.jsx("div",{"data-slot":"dialog-footer",className:L("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",s),...t})}function ce({className:s,...t}){return e.jsx(At,{"data-slot":"dialog-title",className:L("text-lg leading-none font-semibold",s),...t})}function Ds({className:s,...t}){return e.jsx(Et,{"data-slot":"dialog-description",className:L("text-muted-foreground text-sm",s),...t})}function Nr({className:s,...t}){return e.jsx(Ra,{"data-slot":"label",className:L("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",s),...t})}const ne=la,Rt=o.createContext({}),v=({...s})=>e.jsx(Rt.Provider,{value:{name:s.name},children:e.jsx(da,{...s})}),zs=()=>{const s=o.useContext(Rt),t=o.useContext(Vt),{getFieldState:r}=ca(),d=ma({name:s.name}),u=r(s.name,d);if(!s)throw new Error("useFormField should be used within ");const{id:f}=t;return{id:f,name:s.name,formItemId:`${f}-form-item`,formDescriptionId:`${f}-form-item-description`,formMessageId:`${f}-form-item-message`,...u}},Vt=o.createContext({});function b({className:s,...t}){const r=o.useId();return e.jsx(Vt.Provider,{value:{id:r},children:e.jsx("div",{"data-slot":"form-item",className:L("grid gap-2",s),...t})})}function N({className:s,...t}){const{error:r,formItemId:d}=zs();return e.jsx(Nr,{"data-slot":"form-label","data-error":!!r,className:L("data-[error=true]:text-destructive",s),htmlFor:d,...t})}function E({...s}){const{error:t,formItemId:r,formDescriptionId:d,formMessageId:u}=zs();return e.jsx(Cs,{"data-slot":"form-control",id:r,"aria-describedby":t?`${d} ${u}`:`${d}`,"aria-invalid":!!t,...s})}function jt({className:s,...t}){const{formDescriptionId:r}=zs();return e.jsx("p",{"data-slot":"form-description",id:r,className:L("text-muted-foreground text-sm",s),...t})}function w({className:s,...t}){const{error:r,formMessageId:d}=zs(),u=r?String(r?.message??""):t.children;return u?e.jsx("p",{"data-slot":"form-message",id:d,className:L("text-destructive text-sm",s),...t,children:u}):null}function Ae({...s}){return e.jsx(Va,{"data-slot":"select",...s})}function Ee({...s}){return e.jsx(Ha,{"data-slot":"select-value",...s})}function $e({className:s,size:t="default",children:r,...d}){return e.jsxs(Ua,{"data-slot":"select-trigger","data-size":t,className:L("border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",s),...d,children:[r,e.jsx(Ba,{asChild:!0,children:e.jsx(Gs,{className:"size-4 opacity-50"})})]})}function Le({className:s,children:t,position:r="popper",...d}){return e.jsx(Ka,{children:e.jsxs(qa,{"data-slot":"select-content",className:L("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",r==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",s),position:r,...d,children:[e.jsx(wr,{}),e.jsx(Ga,{className:L("p-1",r==="popper"&&"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"),children:t}),e.jsx(yr,{})]})})}function ve({className:s,children:t,...r}){return e.jsxs(Ja,{"data-slot":"select-item",className:L("focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",s),...r,children:[e.jsx("span",{className:"absolute right-2 flex size-3.5 items-center justify-center",children:e.jsx(Qa,{children:e.jsx(bs,{className:"size-4"})})}),e.jsx(Wa,{children:t})]})}function wr({className:s,...t}){return e.jsx(Ya,{"data-slot":"select-scroll-up-button",className:L("flex cursor-default items-center justify-center py-1",s),...t,children:e.jsx(Nn,{className:"size-4"})})}function yr({className:s,...t}){return e.jsx(Xa,{"data-slot":"select-scroll-down-button",className:L("flex cursor-default items-center justify-center py-1",s),...t,children:e.jsx(Gs,{className:"size-4"})})}const Sr=re({name:F().min(1,"Name required"),email:js("Enter a valid email"),role:Je(["user","admin"]),password:F().min(8,"Min 8 characters")}),Cr=re({name:F().min(1,"Name required"),email:js("Enter a valid email"),role:Je(["user","admin"]),password:F().min(8,"Min 8 characters").optional().or(St(""))});function _r(){const[s,t]=o.useState([]),[r,d]=o.useState(!0),[u,f]=o.useState(!1),[$,C]=o.useState(!1),[O,M]=o.useState(null),[R,U]=o.useState(null),T=te({resolver:ae(Sr),mode:"onChange",defaultValues:{name:"",email:"",role:"user",password:""}}),P=te({resolver:ae(Cr),mode:"onChange",defaultValues:{name:"",email:"",role:"user",password:""}});async function j(){d(!0);try{const c=await g.get("/api/v1/admin/users?page=1&page_size=100");t(c.users??[])}catch(c){ee.error(c instanceof je?c.message:"Failed to load users")}finally{d(!1)}}o.useEffect(()=>{j()},[]);async function V(c){try{const S=await g.post("/api/v1/admin/users",c);t(G=>[S,...G]),f(!1),T.reset({name:"",email:"",role:"user",password:""}),ee.success(`Created ${S.email}`)}catch(S){ee.error(S instanceof je?S.message:"Failed to create user")}}function H(c){M(c),P.reset({name:c.name||"",email:c.email,role:c.role??"user",password:""}),C(!0)}async function m(c){if(!O)return;const S={name:c.name,email:c.email,role:c.role};c.password&&c.password.length>=8&&(S.password=c.password);try{const G=await g.patch(`/api/v1/admin/users/${O.id}`,S);t(K=>K.map(B=>B.id===G.id?G:B)),C(!1),M(null),ee.success(`Updated ${G.email}`)}catch(G){ee.error(G instanceof je?G.message:"Failed to update user")}}async function x(c){try{U(c),await g.delete(`/api/v1/admin/users/${c}`),t(S=>S.filter(G=>G.id!==c)),ee.success("User deleted")}catch(S){ee.error(S instanceof je?S.message:"Failed to delete user")}finally{U(null)}}return e.jsxs("div",{className:"space-y-4 p-6",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("h1",{className:"text-2xl font-bold",children:"Users"}),e.jsxs(h,{onClick:()=>f(!0),children:[e.jsx(Qe,{className:"mr-2 h-4 w-4"}),"New user"]})]}),e.jsx(Ns,{}),r?e.jsx("div",{className:"text-muted-foreground text-sm",children:"Loading…"}):s.length===0?e.jsx("div",{className:"text-muted-foreground text-sm",children:"No users yet."}):e.jsx("div",{className:"grid grid-cols-1 gap-4 pr-2 md:grid-cols-2 lg:grid-cols-3",children:s.map(c=>e.jsxs(Te,{className:"flex flex-col",children:[e.jsx(Pe,{children:e.jsx(Be,{className:"text-base",children:c.name||c.email})}),e.jsxs(Me,{className:"text-muted-foreground space-y-1 text-sm",children:[e.jsxs("div",{children:["Email: ",c.email]}),e.jsxs("div",{children:["Role: ",c.role]}),e.jsxs("div",{children:["Verified: ",c.email_verified?"Yes":"No"]}),e.jsxs("div",{children:["Joined: ",new Date(c.created_at).toLocaleString()]})]}),e.jsxs(Ss,{className:"mt-auto w-full flex-col-reverse gap-2 sm:flex-row sm:items-center sm:justify-between",children:[e.jsxs(h,{variant:"outline",onClick:()=>H(c),children:[e.jsx(rs,{className:"mr-2 h-4 w-4"})," Edit"]}),e.jsxs(et,{children:[e.jsx(st,{asChild:!0,children:e.jsxs(h,{variant:"destructive",disabled:R===c.id,children:[e.jsx(Ue,{className:"mr-2 h-4 w-4"}),R===c.id?"Deleting…":"Delete"]})}),e.jsxs(tt,{children:[e.jsxs(at,{children:[e.jsx(rt,{children:"Delete user?"}),e.jsxs(it,{children:["This will permanently delete ",e.jsx("b",{children:c.email}),"."]})]}),e.jsxs(nt,{className:"sm:justify-between",children:[e.jsx(lt,{disabled:R===c.id,children:"Cancel"}),e.jsx(ot,{asChild:!0,disabled:R===c.id,children:e.jsx(h,{variant:"destructive",onClick:()=>x(c.id),children:"Confirm delete"})})]})]})]})]})]},c.id))}),e.jsx(ie,{open:u,onOpenChange:f,children:e.jsxs(oe,{className:"sm:max-w-[520px]",children:[e.jsxs(le,{children:[e.jsx(ce,{children:"Create user"}),e.jsx(Ds,{children:"Add a new user account."})]}),e.jsx(ne,{...T,children:e.jsxs("form",{onSubmit:T.handleSubmit(V),className:"grid gap-4 py-2",children:[e.jsx(v,{name:"name",control:T.control,render:({field:c})=>e.jsxs(b,{children:[e.jsx(N,{children:"Name"}),e.jsx(E,{children:e.jsx(z,{...c,placeholder:"Jane Doe"})}),e.jsx(w,{})]})}),e.jsx(v,{name:"email",control:T.control,render:({field:c})=>e.jsxs(b,{children:[e.jsx(N,{children:"Email"}),e.jsx(E,{children:e.jsx(z,{type:"email",...c,placeholder:"jane@example.com"})}),e.jsx(w,{})]})}),e.jsx(v,{name:"role",control:T.control,render:({field:c})=>e.jsxs(b,{children:[e.jsx(N,{children:"Role"}),e.jsxs(Ae,{value:c.value,onValueChange:c.onChange,children:[e.jsx(E,{children:e.jsx($e,{className:"w-[200px]",children:e.jsx(Ee,{placeholder:"Select role"})})}),e.jsxs(Le,{children:[e.jsx(ve,{value:"user",children:"User"}),e.jsx(ve,{value:"admin",children:"Admin"})]})]}),e.jsx(w,{})]})}),e.jsx(v,{name:"password",control:T.control,render:({field:c})=>e.jsxs(b,{children:[e.jsx(N,{children:"Password"}),e.jsx(E,{children:e.jsx(z,{type:"password",...c,placeholder:"••••••••"})}),e.jsx(w,{})]})}),e.jsxs(de,{className:"mt-2 flex-col-reverse gap-2 sm:flex-row sm:items-center sm:justify-between",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>f(!1),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:!T.formState.isValid||T.formState.isSubmitting,children:T.formState.isSubmitting?"Creating…":"Create"})]})]})})]})}),e.jsx(ie,{open:$,onOpenChange:C,children:e.jsxs(oe,{className:"sm:max-w-[520px]",children:[e.jsxs(le,{children:[e.jsx(ce,{children:"Edit user"}),e.jsx(Ds,{children:"Update user details. Leave password blank to keep it unchanged."})]}),e.jsx(ne,{...P,children:e.jsxs("form",{onSubmit:P.handleSubmit(m),className:"grid gap-4 py-2",children:[e.jsx(v,{name:"name",control:P.control,render:({field:c})=>e.jsxs(b,{children:[e.jsx(N,{children:"Name"}),e.jsx(E,{children:e.jsx(z,{...c})}),e.jsx(w,{})]})}),e.jsx(v,{name:"email",control:P.control,render:({field:c})=>e.jsxs(b,{children:[e.jsx(N,{children:"Email"}),e.jsx(E,{children:e.jsx(z,{type:"email",...c})}),e.jsx(w,{})]})}),e.jsx(v,{name:"role",control:P.control,render:({field:c})=>e.jsxs(b,{children:[e.jsx(N,{children:"Role"}),e.jsxs(Ae,{value:c.value,onValueChange:c.onChange,children:[e.jsx(E,{children:e.jsx($e,{className:"w-[200px]",children:e.jsx(Ee,{placeholder:"Select role"})})}),e.jsxs(Le,{children:[e.jsx(ve,{value:"user",children:"User"}),e.jsx(ve,{value:"admin",children:"Admin"})]})]}),e.jsx(w,{})]})}),e.jsx(v,{name:"password",control:P.control,render:({field:c})=>e.jsxs(b,{children:[e.jsx(N,{children:"New password (optional)"}),e.jsx(E,{children:e.jsx(z,{type:"password",...c,placeholder:"Leave blank to keep"})}),e.jsx(w,{})]})}),e.jsxs(de,{className:"mt-2 flex-col-reverse gap-2 sm:flex-row sm:items-center sm:justify-between",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>C(!1),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:!P.formState.isValid||P.formState.isSubmitting,children:P.formState.isSubmitting?"Saving…":"Save changes"})]})]})})]})})]})}const kr=re({email:js()});function Ar(){const s=te({resolver:ae(kr),defaultValues:{email:""}});async function t(r){try{await Ve.forgot(r.email),ee.success("If that email exists, we've sent instructions.")}catch(d){ee.error(d.message||"Something went wrong")}}return e.jsx("div",{className:"mx-auto max-w-md",children:e.jsxs(Te,{children:[e.jsx(Pe,{children:e.jsx(Be,{children:"Forgot password"})}),e.jsx(Me,{children:e.jsx(ne,{...s,children:e.jsxs("form",{onSubmit:s.handleSubmit(t),className:"space-y-4",children:[e.jsx(v,{name:"email",control:s.control,render:({field:r})=>e.jsxs(b,{children:[e.jsx(N,{children:"Email"}),e.jsx(E,{children:e.jsx(z,{placeholder:"you@example.com",...r})}),e.jsx(w,{})]})}),e.jsx(h,{type:"submit",className:"w-full",children:"Send reset link"})]})})})]})})}const Er=re({email:js(),password:F().min(6)});function $r(){const s=_s(),t=Ms(),r=te({resolver:ae(Er),defaultValues:{email:"",password:""}});async function d(u){try{await Ve.login(u.email,u.password),ee.success("Welcome back!");const f=t.state?.from?.pathname??"/settings/me";s(f,{replace:!0})}catch(f){ee.error(f.message||"Login failed")}}return e.jsx("div",{className:"mx-auto max-w-md",children:e.jsxs(Te,{children:[e.jsx(Pe,{children:e.jsx(Be,{children:"Sign in"})}),e.jsxs(Me,{children:[e.jsx(ne,{...r,children:e.jsxs("form",{onSubmit:r.handleSubmit(d),className:"space-y-4",children:[e.jsx(v,{name:"email",control:r.control,render:({field:u})=>e.jsxs(b,{children:[e.jsx(N,{children:"Email"}),e.jsx(E,{children:e.jsx(z,{placeholder:"you@example.com",...u})}),e.jsx(w,{})]})}),e.jsx(v,{name:"password",control:r.control,render:({field:u})=>e.jsxs(b,{children:[e.jsx(N,{children:"Password"}),e.jsx(E,{children:e.jsx(z,{type:"password",placeholder:"••••••••",...u})}),e.jsx(w,{})]})}),e.jsx(h,{type:"submit",className:"w-full",children:"Sign in"})]})}),e.jsxs("div",{className:"mt-4 flex justify-between text-sm",children:[e.jsx(as,{to:"/auth/forgot",className:"underline",children:"Forgot password?"}),e.jsx(as,{to:"/auth/register",className:"underline",children:"Create an account"})]})]})]})})}function Lr(){const[s,t]=o.useState(null),r=_s();o.useEffect(()=>{(async()=>{try{const u=await Ve.me();t(u)}catch(u){ee.error(u.message||"Failed to load profile")}})()},[]);async function d(){await Ve.logout(),r("/auth/login")}return e.jsx("div",{className:"mx-auto max-w-xl",children:e.jsxs(Te,{children:[e.jsx(Pe,{children:e.jsx(Be,{children:"My Account"})}),e.jsxs(Me,{className:"space-y-3",children:[s?e.jsx("pre",{className:"bg-muted overflow-auto rounded p-3 text-sm",children:JSON.stringify(s,null,2)}):e.jsx("p",{children:"Loading…"}),e.jsx(h,{onClick:d,children:"Sign out"})]})]})})}const Dr=re({name:F().min(2),email:js(),password:F().min(6)});function Ir(){const s=_s(),t=te({resolver:ae(Dr),defaultValues:{name:"",email:"",password:""}});async function r(d){try{await Ve.register(d.name,d.email,d.password),ee.success("Account created! Check your email to verify."),s("/auth/login")}catch(u){ee.error(u.message||"Registration failed")}}return e.jsx("div",{className:"mx-auto max-w-md",children:e.jsxs(Te,{children:[e.jsx(Pe,{children:e.jsx(Be,{children:"Create account"})}),e.jsxs(Me,{children:[e.jsx(ne,{...t,children:e.jsxs("form",{onSubmit:t.handleSubmit(r),className:"space-y-4",children:[e.jsx(v,{name:"name",control:t.control,render:({field:d})=>e.jsxs(b,{children:[e.jsx(N,{children:"Name"}),e.jsx(E,{children:e.jsx(z,{placeholder:"Jane Doe",...d})}),e.jsx(w,{})]})}),e.jsx(v,{name:"email",control:t.control,render:({field:d})=>e.jsxs(b,{children:[e.jsx(N,{children:"Email"}),e.jsx(E,{children:e.jsx(z,{placeholder:"you@example.com",...d})}),e.jsx(w,{})]})}),e.jsx(v,{name:"password",control:t.control,render:({field:d})=>e.jsxs(b,{children:[e.jsx(N,{children:"Password"}),e.jsx(E,{children:e.jsx(z,{type:"password",placeholder:"••••••••",...d})}),e.jsx(w,{})]})}),e.jsx(h,{type:"submit",className:"w-full",children:"Create account"})]})}),e.jsxs("div",{className:"mt-4 text-sm",children:["Already have an account?"," ",e.jsx(as,{to:"/auth/login",className:"underline",children:"Sign in"})]})]})]})})}const Tr=re({new_password:F().min(6)});function Pr(){const[s]=Tt(),t=s.get("token"),r=te({resolver:ae(Tr),defaultValues:{new_password:""}}),d=_s();async function u(f){if(!t){ee.error("Missing token");return}try{await Ve.reset(t,f.new_password),ee.success("Password updated. Please sign in."),d("/auth/login")}catch($){ee.error($.message||"Reset failed")}}return e.jsx("div",{className:"mx-auto max-w-md",children:e.jsxs(Te,{children:[e.jsx(Pe,{children:e.jsx(Be,{children:"Reset password"})}),e.jsxs(Me,{children:[e.jsx(ne,{...r,children:e.jsxs("form",{onSubmit:r.handleSubmit(u),className:"space-y-4",children:[e.jsx(v,{name:"new_password",control:r.control,render:({field:f})=>e.jsxs(b,{children:[e.jsx(N,{children:"New password"}),e.jsx(E,{children:e.jsx(z,{type:"password",placeholder:"••••••••",...f})}),e.jsx(w,{})]})}),e.jsx(h,{type:"submit",className:"w-full",children:"Update password"})]})}),e.jsx("div",{className:"mt-4 text-sm",children:e.jsx(as,{to:"/auth/login",className:"underline",children:"Back to sign in"})})]})]})})}function Mr(){const[s]=Tt(),t=s.get("token"),[r,d]=o.useState("idle");return o.useEffect(()=>{async function u(){if(!t){d("error");return}try{await Ve.verify(t),d("ok")}catch(f){ee.error(f.message||"Verification failed"),d("error")}}u()},[t]),e.jsx("div",{className:"mx-auto max-w-md",children:e.jsxs(Te,{children:[e.jsx(Pe,{children:e.jsx(Be,{children:"Email verification"})}),e.jsxs(Me,{className:"space-y-3",children:[r==="idle"&&e.jsx("p",{children:"Verifying…"}),r==="ok"&&e.jsxs("div",{children:[e.jsx("p",{children:"Your email has been verified. You can now sign in."}),e.jsx(h,{asChild:!0,className:"mt-3",children:e.jsx(as,{to:"/auth/login",children:"Go to sign in"})})]}),r==="error"&&e.jsxs("div",{children:[e.jsx("p",{children:"Verification failed. Please request a new verification email."}),e.jsx(h,{asChild:!0,className:"mt-3",children:e.jsx(as,{to:"/auth/login",children:"Back to sign in"})})]})]})]})})}const Or=qs("inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",{variants:{variant:{default:"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",secondary:"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",destructive:"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",outline:"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground"}},defaultVariants:{variant:"default"}});function Ce({className:s,variant:t,asChild:r=!1,...d}){const u=r?Cs:"span";return e.jsx(u,{"data-slot":"badge",className:L(Or({variant:t}),s),...d})}function De({className:s,...t}){return e.jsx(Za,{"data-slot":"checkbox",className:L("peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",s),...t,children:e.jsx(en,{"data-slot":"checkbox-indicator",className:"flex items-center justify-center text-current transition-none",children:e.jsx(bs,{className:"size-3.5"})})})}function be({className:s,...t}){return e.jsx("div",{"data-slot":"table-container",className:"relative w-full overflow-x-auto",children:e.jsx("table",{"data-slot":"table",className:L("w-full caption-bottom text-sm",s),...t})})}function Ne({className:s,...t}){return e.jsx("thead",{"data-slot":"table-header",className:L("[&_tr]:border-b",s),...t})}function we({className:s,...t}){return e.jsx("tbody",{"data-slot":"table-body",className:L("[&_tr:last-child]:border-0",s),...t})}function q({className:s,...t}){return e.jsx("tr",{"data-slot":"table-row",className:L("hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",s),...t})}function A({className:s,...t}){return e.jsx("th",{"data-slot":"table-head",className:L("text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",s),...t})}function p({className:s,...t}){return e.jsx("td",{"data-slot":"table-cell",className:L("p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",s),...t})}const zr=re({key:F().trim().min(1,"Key is required").max(120,"Max 120 chars"),value:F().trim().min(1,"Value is required").max(512,"Max 512 chars"),node_pool_ids:Oe(F().uuid()).optional().default([])}),Fr=re({key:F().trim().min(1,"Key is required").max(120,"Max 120 chars"),value:F().trim().min(1,"Value is required").max(512,"Max 512 chars")}),Rr=re({node_pool_ids:Oe(F().uuid()).min(1,"Pick at least one node pool")});function ft(s,t=12){return!s||s.length<=t*2+3?s:`${s.slice(0,t)}…${s.slice(-t)}`}const Vr=()=>{const[s,t]=o.useState(!0),[r,d]=o.useState(null),[u,f]=o.useState([]),[$,C]=o.useState([]),[O,M]=o.useState(""),[R,U]=o.useState(!1),[T,P]=o.useState(null),[j,V]=o.useState(null),[H,m]=o.useState([]),[x,c]=o.useState(!1),[S,G]=o.useState(null);async function K(){t(!0),d(null);try{const[y,Y]=await Promise.all([g.get("/api/v1/annotations?include=node_pools"),g.get("/api/v1/node-pools")]);if(f(y||[]),C(Y||[]),T){const he=(y||[]).find(i=>i.id===T.id)||null;P(he)}if(j){const he=(y||[]).find(i=>i.id===j.id)||null;V(he),he&&B(he.id)}}catch(y){console.error(y);const Y=y instanceof je?y.message:"Failed to load annotations / node pools";d(Y)}finally{t(!1)}}async function B(y){c(!0),G(null);try{const Y=await g.get(`/api/v1/annotations/${y}/node_pools`);m(Y||[])}catch(Y){console.error(Y);const he=Y instanceof je?Y.message:"Failed to load pools for annotation";G(he)}finally{c(!1)}}o.useEffect(()=>{K()},[]);const xe=o.useMemo(()=>{const y=O.trim().toLowerCase();return y?u.filter(Y=>Y.key.toLowerCase().includes(y)||Y.value.toLowerCase().includes(y)||(Y.node_pools||[]).some(he=>he.name.toLowerCase().includes(y))):u},[u,O]);async function W(y){confirm("Delete this annotation? This cannot be undone.")&&(await g.delete(`/api/v1/annotations/${y}`),await K())}const ue=te({resolver:ae(zr),defaultValues:{key:"",value:"",node_pool_ids:[]}}),fe=async y=>{const Y={key:y.key.trim(),value:y.value.trim()};y.node_pool_ids&&y.node_pool_ids.length>0&&(Y.node_pool_ids=y.node_pool_ids),await g.post("/api/v1/annotations",Y),U(!1),ue.reset({key:"",value:"",node_pool_ids:[]}),await K()},a=te({resolver:ae(Fr),defaultValues:{key:"",value:""}});function n(y){P(y),a.reset({key:y.key,value:y.value})}const _=async y=>{T&&(await g.patch(`/api/v1/annotations/${T.id}`,{name:y.key.trim(),value:y.value.trim()}),P(null),await K())},X=te({resolver:ae(Rr),defaultValues:{node_pool_ids:[]}});function me(y){V(y),X.reset({node_pool_ids:[]}),B(y.id)}const ge=async y=>{j&&(await g.post(`/api/v1/annotations/${j.id}/node_pools`,{node_pool_ids:y.node_pool_ids}),X.reset({node_pool_ids:[]}),await B(j.id),await K())};async function Re(y){j&&confirm("Detach this node pool from the annotation?")&&(await g.delete(`/api/v1/annotations/${j.id}/node_pools/${y}`),await B(j.id),await K())}return s?e.jsx("div",{className:"p-6",children:"Loading annotations…"}):r?e.jsx("div",{className:"p-6 text-red-500",children:r}):e.jsxs("div",{className:"space-y-4 p-6",children:[e.jsxs("div",{className:"flex flex-col gap-3 md:flex-row md:items-center md:justify-between",children:[e.jsx("h1",{className:"mb-4 text-2xl font-bold",children:"Annotations"}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("div",{className:"relative",children:[e.jsx(Ts,{className:"absolute top-2.5 left-2 h-4 w-4 opacity-60"}),e.jsx(z,{value:O,onChange:y=>M(y.target.value),placeholder:"Search name, value, pool…",className:"w-72 pl-8"})]}),e.jsxs(h,{variant:"outline",onClick:K,children:[e.jsx(Ps,{className:"mr-2 h-4 w-4"})," Refresh"]}),e.jsxs(ie,{open:R,onOpenChange:U,children:[e.jsx(We,{asChild:!0,children:e.jsxs(h,{onClick:()=>U(!0),children:[e.jsx(Qe,{className:"mr-2 h-4 w-4"})," Create Annotation"]})}),e.jsxs(oe,{className:"sm:max-w-lg",children:[e.jsx(le,{children:e.jsx(ce,{children:"Create annotation"})}),e.jsx(ne,{...ue,children:e.jsxs("form",{onSubmit:ue.handleSubmit(fe),className:"space-y-4",children:[e.jsx(v,{control:ue.control,name:"key",render:({field:y})=>e.jsxs(b,{children:[e.jsx(N,{children:"Key"}),e.jsx(E,{children:e.jsx(z,{placeholder:"cluster-autoscaler.kubernetes.io/safe-to-evict",...y})}),e.jsx(w,{})]})}),e.jsx(v,{control:ue.control,name:"value",render:({field:y})=>e.jsxs(b,{children:[e.jsx(N,{children:"Value"}),e.jsx(E,{children:e.jsx(z,{placeholder:"true",...y})}),e.jsx(w,{})]})}),e.jsx(v,{control:ue.control,name:"node_pool_ids",render:({field:y})=>e.jsxs(b,{children:[e.jsx(N,{children:"Initial node pools (optional)"}),e.jsxs("div",{className:"max-h-56 space-y-2 overflow-auto rounded-xl border p-2",children:[$.length===0&&e.jsx("div",{className:"text-muted-foreground p-2 text-sm",children:"No node pools available"}),$.map(Y=>{const he=y.value?.includes(Y.id)||!1;return e.jsxs("label",{className:"hover:bg-accent flex cursor-pointer items-start gap-2 rounded p-1",children:[e.jsx(De,{checked:he,onCheckedChange:i=>{const D=new Set(y.value||[]);i===!0?D.add(Y.id):D.delete(Y.id),y.onChange(Array.from(D))}}),e.jsxs("div",{className:"leading-tight",children:[e.jsx("div",{className:"text-sm font-medium",children:Y.name}),e.jsx("div",{className:"text-muted-foreground text-xs",children:ft(Y.id,8)})]})]},Y.id)})]}),e.jsx(w,{})]})}),e.jsxs(de,{className:"gap-2",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>U(!1),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:ue.formState.isSubmitting,children:ue.formState.isSubmitting?"Creating…":"Create"})]})]})})]})]})]})]}),e.jsx("div",{className:"bg-background overflow-hidden rounded-2xl border shadow-sm",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs(be,{children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Key"}),e.jsx(A,{children:"Value"}),e.jsx(A,{children:"Node Pools"}),e.jsx(A,{className:"w-[180px] text-right",children:"Actions"})]})}),e.jsxs(we,{children:[xe.map(y=>{const Y=y.node_pools||[];return e.jsxs(q,{children:[e.jsx(p,{className:"font-mono text-sm",children:y.key}),e.jsx(p,{className:"font-mono text-sm",children:y.value}),e.jsxs(p,{children:[e.jsxs("div",{className:"mb-2 flex flex-wrap gap-2",children:[Y.slice(0,6).map(he=>e.jsxs(Ce,{variant:"secondary",className:"gap-1",children:[e.jsx(hs,{className:"h-3 w-3"})," ",he.name]},he.id)),Y.length===0&&e.jsx("span",{className:"text-muted-foreground",children:"No node pools"}),Y.length>6&&e.jsxs("span",{className:"text-muted-foreground",children:["+",Y.length-6," more"]})]}),e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>me(y),children:[e.jsx(ye,{className:"mr-2 h-4 w-4"})," Manage Node Pools"]})]}),e.jsx(p,{children:e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>n(y),children:[e.jsx(rs,{className:"mr-2 h-4 w-4"})," Edit"]}),e.jsxs(is,{children:[e.jsx(os,{asChild:!0,children:e.jsxs(h,{variant:"destructive",size:"sm",children:[e.jsx(Ue,{className:"mr-2 h-4 w-4"})," Delete"]})}),e.jsx(ls,{align:"end",children:e.jsx(Ie,{onClick:()=>W(y.id),children:"Confirm delete"})})]})]})})]},y.id)}),xe.length===0&&e.jsx(q,{children:e.jsx(p,{colSpan:4,className:"text-muted-foreground py-10 text-center",children:"No annotations match your search."})})]})]})})}),e.jsx(ie,{open:!!T,onOpenChange:y=>!y&&P(null),children:e.jsxs(oe,{className:"sm:max-w-md",children:[e.jsx(le,{children:e.jsx(ce,{children:"Edit annotation"})}),e.jsx(ne,{...a,children:e.jsxs("form",{onSubmit:a.handleSubmit(_),className:"space-y-4",children:[e.jsx(v,{control:a.control,name:"key",render:({field:y})=>e.jsxs(b,{children:[e.jsx(N,{children:"Key"}),e.jsx(E,{children:e.jsx(z,{placeholder:"example.com/some",...y})}),e.jsx(w,{})]})}),e.jsx(v,{control:a.control,name:"value",render:({field:y})=>e.jsxs(b,{children:[e.jsx(N,{children:"Value"}),e.jsx(E,{children:e.jsx(z,{placeholder:"true",...y})}),e.jsx(w,{})]})}),e.jsxs(de,{className:"gap-2",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>P(null),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:a.formState.isSubmitting,children:a.formState.isSubmitting?"Saving…":"Save changes"})]})]})})]})}),e.jsx(ie,{open:!!j,onOpenChange:y=>!y&&V(null),children:e.jsxs(oe,{className:"sm:max-w-2xl",children:[e.jsx(le,{children:e.jsxs(ce,{children:["Manage node pools for ",e.jsx("span",{className:"font-mono",children:j?.key})]})}),e.jsxs("div",{className:"space-y-3",children:[e.jsx("div",{className:"text-sm font-medium",children:"Attached node pools"}),x?e.jsx("div",{className:"text-muted-foreground rounded-md border p-3 text-sm",children:"Loading…"}):S?e.jsx("div",{className:"rounded-md border p-3 text-sm text-red-500",children:S}):e.jsx("div",{className:"overflow-hidden rounded-xl border",children:e.jsxs(be,{children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Name"}),e.jsx(A,{className:"w-[120px] text-right",children:"Detach"})]})}),e.jsxs(we,{children:[H.map(y=>e.jsxs(q,{children:[e.jsx(p,{className:"font-medium",children:y.name}),e.jsx(p,{children:e.jsx("div",{className:"flex justify-end",children:e.jsxs(h,{variant:"destructive",size:"sm",onClick:()=>Re(y.id),children:[e.jsx(qe,{className:"mr-2 h-4 w-4"})," Detach"]})})})]},y.id)),H.length===0&&e.jsx(q,{children:e.jsx(p,{colSpan:2,className:"text-muted-foreground py-8 text-center",children:"No node pools attached yet."})})]})]})})]}),e.jsx("div",{className:"pt-4",children:e.jsx(ne,{...X,children:e.jsxs("form",{onSubmit:X.handleSubmit(ge),className:"space-y-3",children:[e.jsx(v,{control:X.control,name:"node_pool_ids",render:({field:y})=>e.jsxs(b,{children:[e.jsx(N,{children:"Attach more node pools"}),e.jsx("div",{className:"grid max-h-64 grid-cols-1 gap-2 overflow-auto rounded-xl border p-2 md:grid-cols-2",children:(()=>{const Y=new Set(H.map(i=>i.id)),he=$.filter(i=>!Y.has(i.id));return he.length===0?e.jsx("div",{className:"text-muted-foreground p-2 text-sm",children:"No more node pools available to attach"}):he.map(i=>{const D=y.value?.includes(i.id)||!1;return e.jsxs("label",{className:"hover:bg-accent flex cursor-pointer items-start gap-2 rounded p-1",children:[e.jsx(De,{checked:D,onCheckedChange:ze=>{const Fe=new Set(y.value||[]);ze===!0?Fe.add(i.id):Fe.delete(i.id),y.onChange(Array.from(Fe))}}),e.jsxs("div",{className:"leading-tight",children:[e.jsx("div",{className:"text-sm font-medium",children:i.name}),e.jsx("div",{className:"text-muted-foreground text-xs",children:ft(i.id,8)})]})]},i.id)})})()}),e.jsx(w,{})]})}),e.jsx(de,{className:"gap-2",children:e.jsxs(h,{type:"submit",disabled:X.formState.isSubmitting,children:[e.jsx(ye,{className:"mr-2 h-4 w-4"})," ",X.formState.isSubmitting?"Attaching…":"Attach selected"]})})]})})})]})})]})};function cs({className:s,...t}){return e.jsx("textarea",{"data-slot":"textarea",className:L("border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",s),...t})}const Ur=re({name:F().trim().min(2,"Name is too short"),provider:F().trim().min(2,"Provider is too short"),region:F().trim().min(1,"Region is required"),node_pool_ids:Oe(us()).default([]).optional(),bastion_server_id:us().optional(),cluster_load_balancer:F().optional(),control_load_balancer:F().optional(),kubeconfig:F().optional()}),Br=re({name:F().trim().min(2,"Name is too short").optional(),provider:F().trim().min(2,"Provider is too short").optional(),region:F().trim().min(1,"Region is required").optional(),status:F().trim().min(1,"Status is required").optional(),bastion_server_id:us().or(St("")).optional(),cluster_load_balancer:F().optional(),control_load_balancer:F().optional(),kubeconfig:F().optional()}).refine(s=>s.name!==void 0||s.provider!==void 0||s.region!==void 0||s.status!==void 0||s.bastion_server_id!==void 0||s.cluster_load_balancer!==void 0||s.control_load_balancer!==void 0||s.kubeconfig!==void 0,{message:"Provide at least one change",path:["name"]}),Hr=re({node_pool_ids:Oe(F().uuid()).min(1,"Pick at least one node pool")}),Kr=re({server_id:us({message:"Enter a valid Server UUID"})});function ps(s,t=8){return!s||s.length<=t*2+3?s:`${s.slice(0,t)}…${s.slice(-t)}`}function qr(){const[s,t]=o.useState([]),[r,d]=o.useState([]),[u,f]=o.useState([]),[$,C]=o.useState(!1),[O,M]=o.useState(null),[R,U]=o.useState(""),[T,P]=o.useState(!1),[j,V]=o.useState(!1),[H,m]=o.useState(null),[x,c]=o.useState(null),[S,G]=o.useState(null);async function K(){C(!0),M(null);try{const i=`/api/v1/clusters?include=node_pools,bastion${R?`&q=${encodeURIComponent(R)}`:""}`,[D,ze,Fe]=await Promise.all([g.get(i),g.get("/api/v1/node-pools"),g.get("/api/v1/servers?role=bastion").catch(()=>[])]),Se=(D||[]).map(pe=>({id:pe.id,name:pe.name,provider:pe.provider,region:pe.region,status:pe.status,cluster_load_balancer:pe.cluster_load_balancer,control_load_balancer:pe.control_load_balancer,node_pools:pe.node_pools??[],bastion_server:pe.bastion_server??null}));if(t(Se),d(ze||[]),f(Fe||[]),x){const pe=Se.find(He=>He.id===x.id)||null;c(pe)}if(S){const pe=Se.find(He=>He.id===S.id)||null;G(pe)}if(H){const pe=Se.find(He=>He.id===H.id)||null;m(pe)}}catch(i){console.error(i);const D=i instanceof je?i.message:"Failed to load clusters";M(D)}finally{C(!1)}}o.useEffect(()=>{K()},[]);const B=te({resolver:ae(Ur),defaultValues:{name:"",provider:"",region:"",node_pool_ids:[],bastion_server_id:void 0,cluster_load_balancer:"",control_load_balancer:"",kubeconfig:""}}),xe=async i=>{const D={name:i.name.trim(),provider:i.provider.trim(),region:i.region.trim(),node_pool_ids:i.node_pool_ids||[]};i.bastion_server_id&&(D.bastion_server_id=i.bastion_server_id),i.cluster_load_balancer&&(D.cluster_load_balancer=i.cluster_load_balancer),i.control_load_balancer&&(D.control_load_balancer=i.control_load_balancer),i.kubeconfig&&i.kubeconfig.trim()&&(D.kubeconfig=i.kubeconfig.trim()),await g.post("/api/v1/clusters",D),P(!1),B.reset(),await K()},W=te({resolver:ae(Br),defaultValues:{name:void 0,provider:void 0,region:void 0,status:void 0,bastion_server_id:void 0,cluster_load_balancer:void 0,control_load_balancer:void 0,kubeconfig:void 0}});function ue(i){m(i),W.reset({name:void 0,provider:void 0,region:void 0,status:void 0,bastion_server_id:void 0,cluster_load_balancer:void 0,control_load_balancer:void 0}),V(!0)}const fe=async i=>{if(!H)return;const D={};i.name!==void 0&&(D.name=i.name.trim()),i.provider!==void 0&&(D.provider=i.provider.trim()),i.region!==void 0&&(D.region=i.region.trim()),i.status!==void 0&&(D.status=i.status.trim()),i.bastion_server_id!==void 0&&(D.bastion_server_id=i.bastion_server_id||""),i.cluster_load_balancer!==void 0&&(D.cluster_load_balancer=i.cluster_load_balancer),i.control_load_balancer!==void 0&&(D.control_load_balancer=i.control_load_balancer),i.kubeconfig!==void 0&&i.kubeconfig.trim()&&(D.kubeconfig=i.kubeconfig.trim()),await g.patch(`/api/v1/clusters/${H.id}`,D),V(!1),m(null),await K()};async function a(i){confirm("Delete this cluster? This cannot be undone.")&&(await g.delete(`/api/v1/clusters/${i}`),await K())}const n=te({resolver:ae(Hr),defaultValues:{node_pool_ids:[]}});function _(i){c(i),n.reset({node_pool_ids:[]})}const X=async i=>{x&&(await g.post(`/api/v1/clusters/${x.id}/node_pools`,{node_pool_ids:i.node_pool_ids}),n.reset({node_pool_ids:[]}),await K())};async function me(i,D){confirm("Detach selected node pool?")&&(await g.delete(`/api/v1/clusters/${i}/node_pools/${D}`),await K())}const ge=o.useMemo(()=>{if(!x)return[];const i=new Set((x.node_pools||[]).map(D=>D.id));return r.filter(D=>!i.has(D.id))},[x,r]),Re=te({resolver:ae(Kr),defaultValues:{server_id:""}});function y(i){G(i),Re.reset({server_id:""})}const Y=async i=>{S&&(await g.post(`/api/v1/clusters/${S.id}/bastion`,{server_id:i.server_id}),await K())};async function he(){S&&confirm("Clear bastion for this cluster?")&&(await g.delete(`/api/v1/clusters/${S.id}/bastion`),await K())}return $?e.jsx("div",{className:"p-6",children:"Loading clusters…"}):O?e.jsx("div",{className:"p-6 text-red-500",children:O}):e.jsxs("div",{className:"space-y-4 p-6",children:[e.jsxs("div",{className:"flex flex-col gap-3 md:flex-row md:items-center md:justify-between",children:[e.jsx("h1",{className:"mb-4 text-2xl font-bold",children:"Clusters"}),e.jsxs("div",{className:"flex flex-1 items-center gap-2 md:justify-end",children:[e.jsx(z,{className:"max-w-xs",placeholder:"Filter by name…",value:R,onChange:i=>U(i.target.value),onKeyDown:i=>{i.key==="Enter"&&K()}}),e.jsxs(h,{variant:"outline",onClick:()=>void K(),children:[e.jsx(wn,{className:"mr-2 h-4 w-4"}),"Apply"]}),e.jsxs(ie,{open:T,onOpenChange:P,children:[e.jsx(We,{asChild:!0,children:e.jsxs(h,{onClick:()=>P(!0),children:[e.jsx(Qe,{className:"mr-2 h-4 w-4"}),"Create Cluster"]})}),e.jsxs(oe,{className:"sm:max-w-2xl",children:[e.jsx(le,{children:e.jsx(ce,{children:"Create Cluster"})}),e.jsx(ne,{...B,children:e.jsxs("form",{onSubmit:B.handleSubmit(xe),className:"grid gap-4 md:grid-cols-2",children:[e.jsx(v,{control:B.control,name:"name",render:({field:i})=>e.jsxs(b,{children:[e.jsx(N,{children:"Name"}),e.jsx(E,{children:e.jsx(z,{placeholder:"my-eks-prod",...i})}),e.jsx(w,{})]})}),e.jsx(v,{control:B.control,name:"provider",render:({field:i})=>e.jsxs(b,{children:[e.jsx(N,{children:"Provider"}),e.jsx(E,{children:e.jsx(z,{placeholder:"aws|gcp|azure|onprem",...i})}),e.jsx(w,{})]})}),e.jsx(v,{control:B.control,name:"region",render:({field:i})=>e.jsxs(b,{children:[e.jsx(N,{children:"Region"}),e.jsx(E,{children:e.jsx(z,{placeholder:"eu-west-1",...i})}),e.jsx(w,{})]})}),e.jsx(v,{control:B.control,name:"bastion_server_id",render:({field:i})=>e.jsxs(b,{children:[e.jsx(N,{children:"Bastion server (UUID, optional)"}),e.jsx(E,{children:e.jsx(z,{placeholder:"paste server UUID",...i})}),u.length>0&&e.jsxs("div",{className:"text-xs text-muted-foreground",children:["Suggestions:",e.jsx("div",{className:"mt-1 flex flex-wrap gap-2",children:u.slice(0,6).map(D=>e.jsxs(h,{type:"button",size:"sm",variant:i.value===D.id?"default":"outline",onClick:()=>i.onChange(D.id),className:"font-normal",children:[e.jsx(hs,{className:"mr-1 h-3 w-3"})," ",D.hostname||ps(D.id,6)]},D.id))})]}),e.jsx(w,{})]})}),e.jsx(v,{control:B.control,name:"cluster_load_balancer",render:({field:i})=>e.jsxs(b,{className:"md:col-span-2",children:[e.jsx(N,{children:"Cluster Load Balancer (optional)"}),e.jsx(E,{children:e.jsx(cs,{placeholder:"e.g. JSON or URL or ARN",rows:2,...i})}),e.jsx(w,{})]})}),e.jsx(v,{control:B.control,name:"control_load_balancer",render:({field:i})=>e.jsxs(b,{className:"md:col-span-2",children:[e.jsx(N,{children:"Control Load Balancer (optional)"}),e.jsx(E,{children:e.jsx(cs,{placeholder:"e.g. JSON or URL or ARN",rows:2,...i})}),e.jsx(w,{})]})}),e.jsx(v,{control:B.control,name:"node_pool_ids",render:({field:i})=>e.jsxs(b,{className:"md:col-span-2",children:[e.jsx(N,{children:"Attach node pools (optional)"}),e.jsxs("div",{className:"grid max-h-64 grid-cols-1 gap-2 overflow-auto rounded-xl border p-2 md:grid-cols-2",children:[r.length===0&&e.jsx("div",{className:"text-muted-foreground p-2 text-sm",children:"No node pools available"}),r.map(D=>{const ze=i.value?.includes(D.id)||!1;return e.jsxs("label",{className:"hover:bg-accent flex cursor-pointer items-start gap-2 rounded p-1",children:[e.jsx(De,{checked:ze,onCheckedChange:Fe=>{const Se=new Set(i.value||[]);Fe===!0?Se.add(D.id):Se.delete(D.id),i.onChange(Array.from(Se))}}),e.jsxs("div",{className:"leading-tight",children:[e.jsx("div",{className:"text-sm font-medium",children:D.name}),e.jsx("div",{className:"text-muted-foreground text-xs",children:ps(D.id,8)})]})]},D.id)})]}),e.jsx(w,{})]})}),e.jsx(v,{control:B.control,name:"kubeconfig",render:({field:i})=>e.jsxs(b,{className:"md:colspan-2",children:[e.jsx(N,{children:"Kubeconfig (optional)"}),e.jsx(E,{children:e.jsx(cs,{placeholder:"Paste full kubeconfig YAML here. It will be encrypted and never returned by the API.",rows:8,className:"font-mono",...i})}),e.jsx(w,{})]})}),e.jsxs(de,{className:"md:col-span-2 gap-2",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>P(!1),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:B.formState.isSubmitting,children:B.formState.isSubmitting?"Creating…":"Create"})]})]})})]})]})]})]}),e.jsx("div",{className:"bg-background overflow-hidden rounded-2xl border shadow-sm",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs(be,{children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Name"}),e.jsx(A,{children:"Provider / Region"}),e.jsx(A,{children:"Status"}),e.jsx(A,{children:"Node Pools"}),e.jsx(A,{children:"Bastion"}),e.jsx(A,{className:"w-[360px] text-right",children:"Actions"})]})}),e.jsxs(we,{children:[s.map(i=>e.jsxs(q,{children:[e.jsx(p,{className:"font-medium",children:i.name}),e.jsxs(p,{children:[e.jsx("div",{className:"text-sm font-medium",children:i.provider||"—"}),e.jsx("div",{className:"text-muted-foreground text-xs",children:i.region||"—"})]}),e.jsx(p,{children:e.jsx(Ce,{variant:i.status==="ready"?"default":i.status==="error"?"destructive":"secondary",children:i.status||"unknown"})}),e.jsx(p,{children:e.jsxs("div",{className:"flex max-w-[280px] flex-wrap gap-2",children:[(i.node_pools||[]).slice(0,4).map(D=>e.jsx(Ce,{variant:"secondary",children:D.name},D.id)),(i.node_pools||[]).length===0&&e.jsx("span",{className:"text-muted-foreground",children:"No pools"}),(i.node_pools||[]).length>4&&e.jsxs("span",{className:"text-muted-foreground",children:["+",(i.node_pools||[]).length-4," more"]})]})}),e.jsx(p,{children:i.bastion_server?e.jsxs("div",{className:"leading-tight",children:[e.jsx("div",{className:"text-sm font-medium",children:i.bastion_server.hostname||ps(i.bastion_server.id,6)}),e.jsx("div",{className:"text-muted-foreground text-xs",children:i.bastion_server.ip})]}):e.jsx("span",{className:"text-muted-foreground",children:"None"})}),e.jsx(p,{children:e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>_(i),children:[e.jsx(ye,{className:"mr-2 h-4 w-4"})," Manage pools"]}),e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>y(i),children:[e.jsx(hs,{className:"mr-2 h-4 w-4"})," Bastion"]}),e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>ue(i),children:[e.jsx(rs,{className:"mr-2 h-4 w-4"})," Edit"]}),e.jsxs(h,{variant:"destructive",size:"sm",onClick:()=>a(i.id),children:[e.jsx(Ue,{className:"mr-2 h-4 w-4"})," Delete"]})]})})]},i.id)),s.length===0&&e.jsx(q,{children:e.jsx(p,{colSpan:6,className:"text-muted-foreground py-10 text-center",children:"No clusters yet."})})]})]})})}),e.jsx(ie,{open:j,onOpenChange:i=>!i&&V(!1),children:e.jsxs(oe,{className:"sm:max-w-2xl",children:[e.jsx(le,{children:e.jsxs(ce,{children:["Edit Cluster",H?e.jsxs("span",{className:"text-muted-foreground ml-2 font-mono text-sm",children:["(",H.name,")"]}):null]})}),e.jsx(ne,{...W,children:e.jsxs("form",{onSubmit:W.handleSubmit(fe),className:"grid gap-4 md:grid-cols-2",children:[e.jsx(v,{control:W.control,name:"name",render:({field:i})=>e.jsxs(b,{children:[e.jsx(N,{children:"New name (optional)"}),e.jsx(E,{children:e.jsx(z,{placeholder:H?.name||"e.g. my-eks-prod",...i})}),e.jsx(w,{})]})}),e.jsx(v,{control:W.control,name:"provider",render:({field:i})=>e.jsxs(b,{children:[e.jsx(N,{children:"New provider (optional)"}),e.jsx(E,{children:e.jsx(z,{placeholder:H?.provider||"aws",...i})}),e.jsx(w,{})]})}),e.jsx(v,{control:W.control,name:"region",render:({field:i})=>e.jsxs(b,{children:[e.jsx(N,{children:"New region (optional)"}),e.jsx(E,{children:e.jsx(z,{placeholder:H?.region||"eu-west-1",...i})}),e.jsx(w,{})]})}),e.jsx(v,{control:W.control,name:"status",render:({field:i})=>e.jsxs(b,{children:[e.jsx(N,{children:"New status (optional)"}),e.jsx(E,{children:e.jsx(z,{placeholder:H?.status||"pending|ready|error",...i})}),e.jsx(w,{})]})}),e.jsx(v,{control:W.control,name:"bastion_server_id",render:({field:i})=>e.jsxs(b,{className:"md:col-span-2",children:[e.jsx(N,{children:"Replace/clear bastion (optional)"}),e.jsx(E,{children:e.jsx(z,{placeholder:"paste new server UUID or leave blank to clear",...i})}),e.jsx(w,{})]})}),e.jsx(v,{control:W.control,name:"cluster_load_balancer",render:({field:i})=>e.jsxs(b,{className:"md:col-span-2",children:[e.jsx(N,{children:"Cluster Load Balancer (optional)"}),e.jsx(E,{children:e.jsx(cs,{placeholder:H?.cluster_load_balancer||"e.g. JSON or URL or ARN",rows:2,...i})}),e.jsx(w,{})]})}),e.jsx(v,{control:W.control,name:"control_load_balancer",render:({field:i})=>e.jsxs(b,{className:"md:col-span-2",children:[e.jsx(N,{children:"Control Load Balancer (optional)"}),e.jsx(E,{children:e.jsx(cs,{placeholder:H?.control_load_balancer||"e.g. JSON or URL or ARN",rows:2,...i})}),e.jsx(w,{})]})}),e.jsx(v,{control:W.control,name:"kubeconfig",render:({field:i})=>e.jsxs(b,{className:"md:colspan-2",children:[e.jsx(N,{children:"Replace Kubeconfig (optional)"}),e.jsx(E,{children:e.jsx(cs,{placeholder:"Paste NEW kubeconfig YAML to replace the stored one. Leave empty for no change.",rows:8,className:"font-mono",...i})}),e.jsx(w,{})]})}),e.jsxs(de,{className:"md:col-span-2 gap-2",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>V(!1),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:W.formState.isSubmitting,children:W.formState.isSubmitting?"Saving…":"Save changes"})]})]})})]})}),e.jsx(ie,{open:!!x,onOpenChange:i=>!i&&c(null),children:e.jsxs(oe,{className:"sm:max-w-2xl",children:[e.jsx(le,{children:e.jsxs(ce,{children:["Manage node pools for ",e.jsx("span",{className:"font-mono",children:x?.name})]})}),e.jsxs("div",{className:"space-y-3",children:[e.jsx("div",{className:"text-sm font-medium",children:"Attached node pools"}),e.jsx("div",{className:"overflow-hidden rounded-xl border",children:e.jsxs(be,{children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Name"}),e.jsx(A,{className:"w-[120px] text-right",children:"Detach"})]})}),e.jsxs(we,{children:[(x?.node_pools||[]).map(i=>e.jsxs(q,{children:[e.jsx(p,{className:"font-medium",children:i.name}),e.jsx(p,{children:e.jsx("div",{className:"flex justify-end",children:e.jsxs(h,{variant:"destructive",size:"sm",onClick:()=>me(x.id,i.id),children:[e.jsx(qe,{className:"mr-2 h-4 w-4"})," Detach"]})})})]},i.id)),(x?.node_pools||[]).length===0&&e.jsx(q,{children:e.jsx(p,{colSpan:2,className:"text-muted-foreground py-8 text-center",children:"No pools attached yet."})})]})]})})]}),e.jsx("div",{className:"pt-4",children:e.jsx(ne,{...n,children:e.jsxs("form",{onSubmit:n.handleSubmit(X),className:"space-y-3",children:[e.jsx(v,{control:n.control,name:"node_pool_ids",render:({field:i})=>e.jsxs(b,{children:[e.jsx(N,{children:"Attach more node pools"}),e.jsxs("div",{className:"grid max-h-64 grid-cols-1 gap-2 overflow-auto rounded-xl border p-2 md:grid-cols-2",children:[ge.length===0&&e.jsx("div",{className:"text-muted-foreground p-2 text-sm",children:"No more node pools available to attach"}),ge.map(D=>{const ze=i.value?.includes(D.id)||!1;return e.jsxs("label",{className:"hover:bg-accent flex cursor-pointer items-start gap-2 rounded p-1",children:[e.jsx(De,{checked:ze,onCheckedChange:Fe=>{const Se=new Set(i.value||[]);Fe===!0?Se.add(D.id):Se.delete(D.id),i.onChange(Array.from(Se))}}),e.jsxs("div",{className:"leading-tight",children:[e.jsx("div",{className:"text-sm font-medium",children:D.name}),e.jsx("div",{className:"text-muted-foreground text-xs",children:ps(D.id,8)})]})]},D.id)})]}),e.jsx(w,{})]})}),e.jsx(de,{className:"gap-2",children:e.jsxs(h,{type:"submit",disabled:n.formState.isSubmitting,children:[e.jsx(ye,{className:"mr-2 h-4 w-4"}),n.formState.isSubmitting?"Attaching…":"Attach selected"]})})]})})})]})}),e.jsx(ie,{open:!!S,onOpenChange:i=>!i&&G(null),children:e.jsxs(oe,{className:"sm:max-w-lg",children:[e.jsx(le,{children:e.jsxs(ce,{children:["Manage bastion for ",e.jsx("span",{className:"font-mono",children:S?.name})]})}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("div",{className:"text-sm font-medium",children:"Current"}),e.jsx("div",{className:"rounded-xl border p-3 text-sm",children:S?.bastion_server?e.jsxs("div",{children:[e.jsx("div",{className:"font-medium",children:S.bastion_server.hostname}),e.jsx("div",{className:"text-muted-foreground",children:S.bastion_server.ip})]}):e.jsx("div",{className:"text-muted-foreground",children:"None"})})]}),e.jsx(ne,{...Re,children:e.jsxs("form",{onSubmit:Re.handleSubmit(Y),className:"space-y-4",children:[e.jsx(v,{control:Re.control,name:"server_id",render:({field:i})=>e.jsxs(b,{children:[e.jsx(N,{children:"New bastion server (UUID)"}),e.jsx(E,{children:e.jsx(z,{placeholder:"paste server UUID",...i})}),u.length>0&&e.jsxs("div",{className:"text-xs text-muted-foreground",children:["Suggestions:",e.jsx("div",{className:"mt-1 flex flex-wrap gap-2",children:u.slice(0,8).map(D=>e.jsxs(h,{type:"button",size:"sm",variant:i.value===D.id?"default":"outline",onClick:()=>i.onChange(D.id),className:"font-normal",children:[e.jsx(hs,{className:"mr-1 h-3 w-3"})," ",D.hostname||ps(D.id,6)]},D.id))})]}),e.jsx(w,{})]})}),e.jsxs(de,{className:"gap-2",children:[e.jsx(h,{type:"button",variant:"secondary",onClick:()=>void he(),children:"Clear bastion"}),e.jsx(h,{type:"submit",disabled:Re.formState.isSubmitting,children:Re.formState.isSubmitting?"Saving…":"Set bastion"})]})]})})]})}),e.jsx("pre",{children:JSON.stringify(s,null,2)})]})}const Gr=re({key:F().trim().min(2,"Key is too short"),value:F().trim().min(2,"Value is too short")}),Jr=re({key:F().trim().min(2,"Key is too short").optional(),value:F().trim().min(2,"Value is too short").optional()}).refine(s=>s.key!==void 0||s.value!==void 0,{message:"Provide a new key or value",path:["key"]}),Qr=re({node_pool_ids:Oe(F().uuid()).min(1,"Pick at least one node pool")});function Wr(s,t=8){return!s||s.length<=t*2+3?s:`${s.slice(0,t)}…${s.slice(-t)}`}const Yr=()=>{const[s,t]=o.useState([]),[r,d]=o.useState([]),[u,f]=o.useState(!1),[$,C]=o.useState(null),[O,M]=o.useState(!1),[R,U]=o.useState(!1),[T,P]=o.useState(null),[j,V]=o.useState(null);async function H(){f(!0),C(null);try{const[a,n]=await Promise.all([g.get("/api/v1/labels?include=node_pools"),g.get("/api/v1/node-pools")]),_=(a||[]).map(X=>({id:X.id,key:X.key,value:X.value,node_pools:X.node_pools??X.node_groups??[]}));if(t(_),d(n||[]),j){const X=_.find(me=>me.id===j.id)||null;V(X)}if(T){const X=_.find(me=>me.id===T.id)||null;P(X)}}catch(a){console.error(a);const n=a instanceof je?a.message:"Failed to load labels/pools";C(n)}finally{f(!1)}}o.useEffect(()=>{H()},[]);const m=te({resolver:ae(Gr),defaultValues:{key:"",value:""}}),x=async a=>{await g.post("/api/v1/labels",{key:a.key.trim(),value:a.value.trim()}),M(!1),m.reset(),await H()},c=te({resolver:ae(Jr),defaultValues:{key:void 0,value:void 0}});function S(a){P(a),c.reset({key:void 0,value:void 0}),U(!0)}const G=async a=>{if(!T)return;const n={};a.key!==void 0&&(n.key=a.key.trim()),a.value!==void 0&&(n.value=a.value.trim()),await g.patch(`/api/v1/labels/${T.id}`,n),U(!1),P(null),await H()};async function K(a){confirm("Delete this label? This cannot be undone.")&&(await g.delete(`/api/v1/labels/${a}`),await H())}const B=te({resolver:ae(Qr),defaultValues:{node_pool_ids:[]}});function xe(a){V(a),B.reset({node_pool_ids:[]})}const W=async a=>{j&&(await g.post(`/api/v1/labels/${j.id}/node_pools`,{node_pool_ids:a.node_pool_ids}),B.reset({node_pool_ids:[]}),await H())};async function ue(a){j&&confirm("Detach this label from the selected node pool?")&&(await g.delete(`/api/v1/labels/${j.id}/node_pools/${a}`),await H())}const fe=o.useMemo(()=>{if(!j)return[];const a=new Set((j.node_pools||[]).map(n=>n.id));return r.filter(n=>!a.has(n.id))},[j,r]);return u?e.jsx("div",{className:"p-6",children:"Loading labels…"}):$?e.jsx("div",{className:"p-6 text-red-500",children:$}):e.jsxs("div",{className:"space-y-4 p-6",children:[e.jsxs("div",{className:"flex flex-col gap-3 md:flex-row md:items-center md:justify-between",children:[e.jsx("h1",{className:"mb-4 text-2xl font-bold",children:"Labels"}),e.jsxs(ie,{open:O,onOpenChange:M,children:[e.jsx(We,{asChild:!0,children:e.jsxs(h,{onClick:()=>M(!0),children:[e.jsx(Qe,{className:"mr-2 h-4 w-4"}),"Create Label"]})}),e.jsxs(oe,{className:"sm:max-w-lg",children:[e.jsx(le,{children:e.jsx(ce,{children:"Create Label"})}),e.jsx(ne,{...m,children:e.jsxs("form",{onSubmit:m.handleSubmit(x),className:"space-y-4",children:[e.jsx(v,{control:m.control,name:"key",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"Key"}),e.jsx(E,{children:e.jsx(z,{placeholder:"app.kubernetes.io/managed-by",...a})}),e.jsx(w,{})]})}),e.jsx(v,{control:m.control,name:"value",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"Value"}),e.jsx(E,{children:e.jsx(z,{placeholder:"GlueOps",...a})}),e.jsx(w,{})]})}),e.jsxs(de,{className:"gap-2",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>M(!1),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:m.formState.isSubmitting,children:m.formState.isSubmitting?"Creating…":"Create"})]})]})})]})]})]}),e.jsx("div",{className:"bg-background overflow-hidden rounded-2xl border shadow-sm",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs(be,{children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Key"}),e.jsx(A,{children:"Value"}),e.jsx(A,{children:"Node Pools"}),e.jsx(A,{className:"w-[260px] text-right",children:"Actions"})]})}),e.jsxs(we,{children:[s.map(a=>e.jsxs(q,{children:[e.jsx(p,{className:"font-medium",children:a.key}),e.jsx(p,{children:a.value}),e.jsx(p,{children:e.jsxs("div",{className:"flex flex-wrap gap-2",children:[(a.node_pools||[]).slice(0,6).map(n=>e.jsx(Ce,{variant:"secondary",children:n.name},n.id)),(a.node_pools||[]).length===0&&e.jsx("span",{className:"text-muted-foreground",children:"No pools"}),(a.node_pools||[]).length>6&&e.jsxs("span",{className:"text-muted-foreground",children:["+",(a.node_pools||[]).length-6," more"]})]})}),e.jsx(p,{children:e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>xe(a),children:[e.jsx(ye,{className:"mr-2 h-4 w-4"}),"Manage node pools"]}),e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>S(a),children:[e.jsx(rs,{className:"mr-2 h-4 w-4"}),"Edit"]}),e.jsxs(h,{variant:"destructive",size:"sm",onClick:()=>K(a.id),children:[e.jsx(Ue,{className:"mr-2 h-4 w-4"}),"Delete"]})]})})]},a.id)),s.length===0&&e.jsx(q,{children:e.jsx(p,{colSpan:4,className:"text-muted-foreground py-10 text-center",children:"No labels yet."})})]})]})})}),e.jsx(ie,{open:R,onOpenChange:a=>!a&&U(!1),children:e.jsxs(oe,{className:"sm:max-w-lg",children:[e.jsx(le,{children:e.jsxs(ce,{children:["Edit Label"," ",T?e.jsxs("span",{className:"text-muted-foreground ml-2 font-mono text-sm",children:["(",T.key," = ",T.value,")"]}):null]})}),e.jsx(ne,{...c,children:e.jsxs("form",{onSubmit:c.handleSubmit(G),className:"space-y-4",children:[e.jsx(v,{control:c.control,name:"key",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"New key (optional)"}),e.jsx(E,{children:e.jsx(z,{placeholder:T?.key||"e.g. app",...a})}),e.jsx(w,{})]})}),e.jsx(v,{control:c.control,name:"value",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"New value (optional)"}),e.jsx(E,{children:e.jsx(z,{placeholder:T?.value||"e.g. GlueOps",...a})}),e.jsx(w,{})]})}),e.jsxs(de,{className:"gap-2",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>U(!1),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:c.formState.isSubmitting,children:c.formState.isSubmitting?"Saving…":"Save changes"})]})]})})]})}),e.jsx(ie,{open:!!j,onOpenChange:a=>!a&&V(null),children:e.jsxs(oe,{className:"sm:max-w-2xl",children:[e.jsx(le,{children:e.jsxs(ce,{children:["Manage node pools for"," ",e.jsx("span",{className:"font-mono",children:j?`${j.key}=${j.value}`:""})]})}),e.jsxs("div",{className:"space-y-3",children:[e.jsx("div",{className:"text-sm font-medium",children:"Attached node pools"}),e.jsx("div",{className:"overflow-hidden rounded-xl border",children:e.jsxs(be,{children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Name"}),e.jsx(A,{className:"w-[120px] text-right",children:"Detach"})]})}),e.jsxs(we,{children:[(j?.node_pools||[]).map(a=>e.jsxs(q,{children:[e.jsx(p,{className:"font-medium",children:a.name}),e.jsx(p,{children:e.jsx("div",{className:"flex justify-end",children:e.jsxs(h,{variant:"destructive",size:"sm",onClick:()=>ue(a.id),children:[e.jsx(qe,{className:"mr-2 h-4 w-4"})," Detach"]})})})]},a.id)),(j?.node_pools||[]).length===0&&e.jsx(q,{children:e.jsx(p,{colSpan:2,className:"text-muted-foreground py-8 text-center",children:"No pools attached yet."})})]})]})})]}),e.jsx("div",{className:"pt-4",children:e.jsx(ne,{...B,children:e.jsxs("form",{onSubmit:B.handleSubmit(W),className:"space-y-3",children:[e.jsx(v,{control:B.control,name:"node_pool_ids",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"Attach more node pools"}),e.jsxs("div",{className:"grid max-h-64 grid-cols-1 gap-2 overflow-auto rounded-xl border p-2 md:grid-cols-2",children:[fe.length===0&&e.jsx("div",{className:"text-muted-foreground p-2 text-sm",children:"No more node pools available to attach"}),fe.map(n=>{const _=a.value?.includes(n.id)||!1;return e.jsxs("label",{className:"hover:bg-accent flex cursor-pointer items-start gap-2 rounded p-1",children:[e.jsx(De,{checked:_,onCheckedChange:X=>{const me=new Set(a.value||[]);X===!0?me.add(n.id):me.delete(n.id),a.onChange(Array.from(me))}}),e.jsxs("div",{className:"leading-tight",children:[e.jsx("div",{className:"text-sm font-medium",children:n.name}),e.jsx("div",{className:"text-muted-foreground text-xs",children:Wr(n.id,8)})]})]},n.id)})]}),e.jsx(w,{})]})}),e.jsx(de,{className:"gap-2",children:e.jsxs(h,{type:"submit",disabled:B.formState.isSubmitting,children:[e.jsx(ye,{className:"mr-2 h-4 w-4"}),B.formState.isSubmitting?"Attaching…":"Attach selected"]})})]})})})]})})]})},Xr=re({name:F().trim().min(1,"Name is required").max(120,"Max 120 chars"),server_ids:Oe(F().uuid()).optional().default([])}),Zr=re({name:F().trim().min(1,"Name is required").max(120,"Max 120 chars")}),ei=re({server_ids:Oe(F().uuid()).min(1,"Pick at least one server")}),si=re({label_ids:Oe(F().uuid()).min(1,"Pick at least one label")}),ti=re({taint_ids:Oe(F().uuid()).min(1,"Pick at least one taint")}),ai=re({annotation_ids:Oe(F().uuid()).min(1,"Pick at least one annotation")});function pt({status:s}){const t=s==="ready"?"default":s==="provisioning"?"secondary":s==="failed"?"destructive":"outline";return e.jsx(Ce,{variant:t,className:"capitalize",children:s||"unknown"})}function ts(s,t=12){return!s||s.length<=t*2+3?s:`${s.slice(0,t)}…${s.slice(-t)}`}function gt(s){const t=s.ip||s.ip_address,r=s.hostname||t||s.id,d=s.role?` · ${s.role}`:"";return`${r}${d}`}function ni(s){return`${s.key}=${s.value}`}function vt(s){return`${s.value?`${s.key}=${s.value}`:s.key}:${s.effect}`}function bt(s){return`${s.name}=${s.value}`}const ri=()=>{const[s,t]=o.useState(!0),[r,d]=o.useState([]),[u,f]=o.useState([]),[$,C]=o.useState([]),[O,M]=o.useState([]),[R,U]=o.useState([]),[T,P]=o.useState(null),[j,V]=o.useState(""),[H,m]=o.useState(!1),[x,c]=o.useState(null),[S,G]=o.useState(null),[K,B]=o.useState(null),[xe,W]=o.useState([]),[ue,fe]=o.useState(!1),[a,n]=o.useState(null),[_,X]=o.useState(null),[me,ge]=o.useState([]),[Re,y]=o.useState(!1),[Y,he]=o.useState(null),[i,D]=o.useState(null),[ze,Fe]=o.useState([]),[Se,pe]=o.useState(!1),[He,dt]=o.useState(null);async function _e(){t(!0),P(null);try{const[l,k,Q,I,Z]=await Promise.all([g.get("/api/v1/node-pools?include=servers"),g.get("/api/v1/servers"),g.get("/api/v1/labels?include=node_pools"),g.get("/api/v1/taints?include=node_pools"),g.get("/api/v1/annotations?include=node_pools")]);if(d(l||[]),f(k||[]),C(Q||[]),M(I||[]),U(Z||[]),S){const ke=(l||[]).find(J=>J.id===S.id)||null;G(ke)}if(x){const ke=(l||[]).find(J=>J.id===x.id)||null;c(ke)}K&&await ks(K.id),_&&await As(_.id),i&&await Es(i.id)}catch(l){console.error(l);const k=l instanceof je?l.message:"Failed to load node pools / servers / labels / taints / annotations";P(k)}finally{t(!1)}}async function ks(l){fe(!0),n(null);try{const k=await g.get(`/api/v1/node-pools/${l}/labels`);W(k||[])}catch(k){console.error(k);const Q=k instanceof je?k.message:"Failed to load labels for pool";n(Q)}finally{fe(!1)}}async function As(l){y(!0),he(null);try{const k=await g.get(`/api/v1/node-pools/${l}/taints`);ge(k||[])}catch(k){console.error(k);const Q=k instanceof je?k.message:"Failed to load taints for pool";he(Q)}finally{y(!1)}}async function Es(l){pe(!0),dt(null);try{const k=await g.get(`/api/v1/node-pools/${l}/annotations`);Fe(k||[])}catch(k){console.error(k);const Q=k instanceof je?k.message:"Failed to load annotations for pool";dt(Q)}finally{pe(!1)}}o.useEffect(()=>{_e()},[]);const Fs=o.useMemo(()=>{const l=new Map;for(const k of $)for(const Q of k.node_groups||[]){const I=l.get(Q.id)||[];I.push({id:k.id,key:k.key,value:k.value}),l.set(Q.id,I)}return l},[$]),Rs=o.useMemo(()=>{const l=new Map;for(const k of O)for(const Q of k.node_groups||[]){const I=l.get(Q.id)||[];I.push({id:k.id,key:k.key,value:k.value,effect:k.effect}),l.set(Q.id,I)}return l},[O]),Vs=o.useMemo(()=>{const l=new Map;for(const k of R)for(const Q of k.node_pools||[]){const I=l.get(Q.id)||[];I.push({id:k.id,name:k.name,value:k.value}),l.set(Q.id,I)}return l},[R]),ct=o.useMemo(()=>{const l=j.trim().toLowerCase();return l?r.filter(k=>{const Q=(k.servers||[]).some(J=>(J.hostname||"").toLowerCase().includes(l)||(J.ip||J.ip_address||"").toLowerCase().includes(l)||(J.role||"").toLowerCase().includes(l)),I=(Fs.get(k.id)||[]).some(J=>J.key.toLowerCase().includes(l)||(J.value||"").toLowerCase().includes(l)),Z=(Rs.get(k.id)||[]).some(J=>{const ra=`${J.key}=${J.value}`.toLowerCase();return J.key.toLowerCase().includes(l)||(J.value||"").toLowerCase().includes(l)||J.effect.toLowerCase().includes(l)||ra.includes(l)}),ke=(Vs.get(k.id)||[]).some(J=>J.name.toLowerCase().includes(l)||(J.value||"").toLowerCase().includes(l)||`${J.name}=${J.value}`.toLowerCase().includes(l));return k.name.toLowerCase().includes(l)||Q||I||Z||ke}):r},[r,j,Fs,Rs,Vs]);async function Bt(l){confirm("Delete this node pool? This cannot be undone.")&&(await g.delete(`/api/v1/node-pools/${l}`),await _e())}const Ye=te({resolver:ae(Xr),defaultValues:{name:"",server_ids:[]}}),Ht=async l=>{const k={name:l.name.trim()};l.server_ids&&l.server_ids.length>0&&(k.server_ids=l.server_ids),await g.post("/api/v1/node-pools",k),m(!1),Ye.reset({name:"",server_ids:[]}),await _e()},ds=te({resolver:ae(Zr),defaultValues:{name:""}});function Kt(l){c(l),ds.reset({name:l.name})}const qt=async l=>{x&&(await g.patch(`/api/v1/node-pools/${x.id}`,{name:l.name.trim()}),c(null),await _e())},Xe=te({resolver:ae(ei),defaultValues:{server_ids:[]}});function Gt(l){G(l),Xe.reset({server_ids:[]})}const Jt=async l=>{S&&(await g.post(`/api/v1/node-pools/${S.id}/servers`,{server_ids:l.server_ids}),Xe.reset({server_ids:[]}),await _e())};async function Qt(l){S&&confirm("Detach this server from the pool?")&&(await g.delete(`/api/v1/node-pools/${S.id}/servers/${l}`),await _e())}const Ze=te({resolver:ae(si),defaultValues:{label_ids:[]}});function Wt(l){B(l),Ze.reset({label_ids:[]}),ks(l.id)}const Yt=async l=>{K&&(await g.post(`/api/v1/node-pools/${K.id}/labels`,{label_ids:l.label_ids}),Ze.reset({label_ids:[]}),await ks(K.id),await _e())};async function Xt(l){K&&confirm("Detach this label from the pool?")&&(await g.delete(`/api/v1/node-pools/${K.id}/labels/${l}`),await ks(K.id),await _e())}const es=te({resolver:ae(ti),defaultValues:{taint_ids:[]}});function Zt(l){X(l),es.reset({taint_ids:[]}),As(l.id)}const ea=async l=>{_&&(await g.post(`/api/v1/node-pools/${_.id}/taints`,{taint_ids:l.taint_ids}),es.reset({taint_ids:[]}),await As(_.id),await _e())};async function sa(l){_&&confirm("Detach this taint from the pool?")&&(await g.delete(`/api/v1/node-pools/${_.id}/taints/${l}`),await As(_.id),await _e())}const ss=te({resolver:ae(ai),defaultValues:{annotation_ids:[]}});function ta(l){D(l),ss.reset({annotation_ids:[]}),Es(l.id)}const aa=async l=>{i&&(await g.post(`/api/v1/node-pools/${i.id}/annotations`,{annotation_ids:l.annotation_ids}),ss.reset({annotation_ids:[]}),await Es(i.id),await _e())};async function na(l){i&&confirm("Detach this annotation from the pool?")&&(await g.delete(`/api/v1/node-pools/${i.id}/annotations/${l}`),await Es(i.id),await _e())}return s?e.jsx("div",{className:"p-6",children:"Loading node pools…"}):T?e.jsx("div",{className:"p-6 text-red-500",children:T}):e.jsxs("div",{className:"space-y-4 p-6",children:[e.jsxs("div",{className:"flex flex-col gap-3 md:flex-row md:items-center md:justify-between",children:[e.jsx("h1",{className:"mb-4 text-2xl font-bold",children:"Node Pools"}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("div",{className:"relative",children:[e.jsx(Ts,{className:"absolute top-2.5 left-2 h-4 w-4 opacity-60"}),e.jsx(z,{value:j,onChange:l=>V(l.target.value),placeholder:"Search pools, servers, labels, taints, annotations…",className:"w-72 pl-8"})]}),e.jsxs(h,{variant:"outline",onClick:_e,children:[e.jsx(Ps,{className:"mr-2 h-4 w-4"})," Refresh"]}),e.jsxs(ie,{open:H,onOpenChange:m,children:[e.jsx(We,{asChild:!0,children:e.jsxs(h,{onClick:()=>m(!0),children:[e.jsx(Qe,{className:"mr-2 h-4 w-4"})," Create Pool"]})}),e.jsxs(oe,{className:"sm:max-w-lg",children:[e.jsx(le,{children:e.jsx(ce,{children:"Create node pool"})}),e.jsx(ne,{...Ye,children:e.jsxs("form",{onSubmit:Ye.handleSubmit(Ht),className:"space-y-4",children:[e.jsx(v,{control:Ye.control,name:"name",render:({field:l})=>e.jsxs(b,{children:[e.jsx(N,{children:"Name"}),e.jsx(E,{children:e.jsx(z,{placeholder:"pool-workers-a",...l})}),e.jsx(w,{})]})}),e.jsx(v,{control:Ye.control,name:"server_ids",render:({field:l})=>e.jsxs(b,{children:[e.jsx(N,{children:"Initial servers (optional)"}),e.jsxs("div",{className:"max-h-56 space-y-2 overflow-auto rounded-xl border p-2",children:[u.length===0&&e.jsx("div",{className:"text-muted-foreground p-2 text-sm",children:"No servers available"}),u.map(k=>{const Q=l.value?.includes(k.id)||!1;return e.jsxs("label",{className:"hover:bg-accent flex cursor-pointer items-start gap-2 rounded p-1",children:[e.jsx(De,{checked:Q,onCheckedChange:I=>{const Z=new Set(l.value||[]);I===!0?Z.add(k.id):Z.delete(k.id),l.onChange(Array.from(Z))}}),e.jsxs("div",{className:"leading-tight",children:[e.jsx("div",{className:"text-sm font-medium",children:gt(k)}),e.jsx("div",{className:"text-muted-foreground text-xs",children:ts(k.id,8)})]})]},k.id)})]}),e.jsx(w,{})]})}),e.jsxs(de,{className:"gap-2",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>m(!1),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:Ye.formState.isSubmitting,children:Ye.formState.isSubmitting?"Creating…":"Create"})]})]})})]})]})]})]}),e.jsx("div",{className:"bg-background overflow-hidden rounded-2xl border shadow-sm",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs(be,{children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Name"}),e.jsx(A,{children:"Servers"}),e.jsx(A,{children:"Annotations"}),e.jsx(A,{children:"Labels"}),e.jsx(A,{children:"Taints"}),e.jsx(A,{className:"w-[180px] text-right",children:"Actions"})]})}),e.jsxs(we,{children:[ct.map(l=>{const k=Fs.get(l.id)||[],Q=Rs.get(l.id)||[],I=Vs.get(l.id)||[];return e.jsxs(q,{children:[e.jsx(p,{className:"font-medium",children:l.name}),e.jsxs(p,{children:[e.jsxs("div",{className:"flex flex-wrap gap-2",children:[(l.servers||[]).slice(0,6).map(Z=>e.jsxs(Ce,{variant:"secondary",className:"gap-1",children:[e.jsx(hs,{className:"h-3 w-3"})," ",Z.hostname||Z.ip||Z.ip_address||ts(Z.id,6),e.jsx("span",{className:"ml-1",children:Z.role}),Z.status&&e.jsx("span",{className:"ml-1",children:e.jsx(pt,{status:Z.status})})]},Z.id)),(l.servers||[]).length===0&&e.jsx("span",{className:"text-muted-foreground",children:"No servers"}),(l.servers||[]).length>6&&e.jsxs("span",{className:"text-muted-foreground",children:["+",(l.servers||[]).length-6," more"]})]}),e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>Gt(l),children:[e.jsx(ye,{className:"mr-2 h-4 w-4"})," Manage servers"]})]}),e.jsxs(p,{children:[e.jsxs("div",{className:"mb-2 flex flex-wrap gap-2",children:[I.slice(0,6).map(Z=>e.jsxs(Ce,{variant:"outline",className:"font-mono",children:[e.jsx($s,{className:"mr-1 h-3 w-3"}),bt(Z)]},Z.id)),I.length===0&&e.jsx("span",{className:"text-muted-foreground",children:"No annotations"}),I.length>6&&e.jsxs("span",{className:"text-muted-foreground",children:["+",I.length-6," more"]})]}),e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>ta(l),children:[e.jsx(ye,{className:"mr-2 h-4 w-4"})," Manage Annotations"]})]}),e.jsxs(p,{children:[e.jsxs("div",{className:"mb-2 flex flex-wrap gap-2",children:[k.slice(0,6).map(Z=>e.jsxs(Ce,{variant:"outline",className:"font-mono",children:[e.jsx($s,{className:"mr-1 h-3 w-3"}),Z.key,"=",Z.value]},Z.id)),k.length===0&&e.jsx("span",{className:"text-muted-foreground",children:"No labels"}),k.length>6&&e.jsxs("span",{className:"text-muted-foreground",children:["+",k.length-6," more"]})]}),e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>Wt(l),children:[e.jsx($s,{className:"mr-2 h-4 w-4"})," Manage Labels"]})]}),e.jsxs(p,{children:[e.jsxs("div",{className:"mb-2 flex flex-wrap gap-2",children:[Q.slice(0,6).map(Z=>e.jsxs(Ce,{variant:"outline",className:"font-mono",children:[e.jsx($s,{className:"mr-1 h-3 w-3"}),vt(Z)]},Z.id)),Q.length===0&&e.jsx("span",{className:"text-muted-foreground",children:"No taints"}),Q.length>6&&e.jsxs("span",{className:"text-muted-foreground",children:["+",Q.length-6," more"]})]}),e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>Zt(l),children:[e.jsx(ye,{className:"mr-2 h-4 w-4"})," Manage Taints"]})]}),e.jsx(p,{children:e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>Kt(l),children:[e.jsx(rs,{className:"mr-2 h-4 w-4"})," Edit"]}),e.jsxs(is,{children:[e.jsx(os,{asChild:!0,children:e.jsxs(h,{variant:"destructive",size:"sm",children:[e.jsx(Ue,{className:"mr-2 h-4 w-4"})," Delete"]})}),e.jsx(ls,{align:"end",children:e.jsx(Ie,{onClick:()=>Bt(l.id),children:"Confirm delete"})})]})]})})]},l.id)}),ct.length===0&&e.jsx(q,{children:e.jsx(p,{colSpan:6,className:"text-muted-foreground py-10 text-center",children:"No node pools match your search."})})]})]})})}),e.jsx(ie,{open:!!x,onOpenChange:l=>!l&&c(null),children:e.jsxs(oe,{className:"sm:max-w-md",children:[e.jsx(le,{children:e.jsx(ce,{children:"Edit node pool"})}),e.jsx(ne,{...ds,children:e.jsxs("form",{onSubmit:ds.handleSubmit(qt),className:"space-y-4",children:[e.jsx(v,{control:ds.control,name:"name",render:({field:l})=>e.jsxs(b,{children:[e.jsx(N,{children:"Name"}),e.jsx(E,{children:e.jsx(z,{placeholder:"pool-workers-a",...l})}),e.jsx(w,{})]})}),e.jsxs(de,{className:"gap-2",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>c(null),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:ds.formState.isSubmitting,children:ds.formState.isSubmitting?"Saving…":"Save changes"})]})]})})]})}),e.jsx(ie,{open:!!S,onOpenChange:l=>!l&&G(null),children:e.jsxs(oe,{className:"sm:max-w-2xl",children:[e.jsx(le,{children:e.jsxs(ce,{children:["Manage servers for ",e.jsx("span",{className:"font-mono",children:S?.name})]})}),e.jsxs("div",{className:"space-y-3",children:[e.jsx("div",{className:"text-sm font-medium",children:"Attached servers"}),e.jsx("div",{className:"overflow-hidden rounded-xl border",children:e.jsxs(be,{children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Server"}),e.jsx(A,{children:"IP"}),e.jsx(A,{children:"Role"}),e.jsx(A,{children:"Status"}),e.jsx(A,{className:"w-[120px] text-right",children:"Detach"})]})}),e.jsxs(we,{children:[(S?.servers||[]).map(l=>e.jsxs(q,{children:[e.jsx(p,{className:"font-medium",children:l.hostname||ts(l.id,8)}),e.jsx(p,{children:e.jsx("code",{className:"font-mono text-sm",children:l.ip||l.ip_address||"—"})}),e.jsx(p,{className:"capitalize",children:l.role||"—"}),e.jsx(p,{children:e.jsx(pt,{status:l.status})}),e.jsx(p,{children:e.jsx("div",{className:"flex justify-end",children:e.jsxs(h,{variant:"destructive",size:"sm",onClick:()=>Qt(l.id),children:[e.jsx(qe,{className:"mr-2 h-4 w-4"})," Detach"]})})})]},l.id)),(S?.servers||[]).length===0&&e.jsx(q,{children:e.jsx(p,{colSpan:5,className:"text-muted-foreground py-8 text-center",children:"No servers attached yet."})})]})]})})]}),e.jsx("div",{className:"pt-4",children:e.jsx(ne,{...Xe,children:e.jsxs("form",{onSubmit:Xe.handleSubmit(Jt),className:"space-y-3",children:[e.jsx(v,{control:Xe.control,name:"server_ids",render:({field:l})=>e.jsxs(b,{children:[e.jsx(N,{children:"Attach more servers"}),e.jsx("div",{className:"grid max-h-64 grid-cols-1 gap-2 overflow-auto rounded-xl border p-2 md:grid-cols-2",children:(()=>{const k=new Set((S?.servers||[]).map(I=>I.id)),Q=u.filter(I=>!k.has(I.id));return Q.length===0?e.jsx("div",{className:"text-muted-foreground p-2 text-sm",children:"No more servers available to attach"}):Q.map(I=>{const Z=l.value?.includes(I.id)||!1;return e.jsxs("label",{className:"hover:bg-accent flex cursor-pointer items-start gap-2 rounded p-1",children:[e.jsx(De,{checked:Z,onCheckedChange:ke=>{const J=new Set(l.value||[]);ke===!0?J.add(I.id):J.delete(I.id),l.onChange(Array.from(J))}}),e.jsxs("div",{className:"leading-tight",children:[e.jsx("div",{className:"text-sm font-medium",children:gt(I)}),e.jsx("div",{className:"text-muted-foreground text-xs",children:ts(I.id,8)})]})]},I.id)})})()}),e.jsx(w,{})]})}),e.jsx(de,{className:"gap-2",children:e.jsxs(h,{type:"submit",disabled:Xe.formState.isSubmitting,children:[e.jsx(ye,{className:"mr-2 h-4 w-4"})," ",Xe.formState.isSubmitting?"Attaching…":"Attach selected"]})})]})})})]})}),e.jsx(ie,{open:!!K,onOpenChange:l=>!l&&B(null),children:e.jsxs(oe,{className:"sm:max-w-2xl",children:[e.jsx(le,{children:e.jsxs(ce,{children:["Manage labels for ",e.jsx("span",{className:"font-mono",children:K?.name})]})}),e.jsxs("div",{className:"space-y-3",children:[e.jsx("div",{className:"text-sm font-medium",children:"Attached labels"}),ue?e.jsx("div",{className:"text-muted-foreground rounded-md border p-3 text-sm",children:"Loading…"}):a?e.jsx("div",{className:"rounded-md border p-3 text-sm text-red-500",children:a}):e.jsx("div",{className:"overflow-hidden rounded-xl border",children:e.jsxs(be,{children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Key"}),e.jsx(A,{children:"Value"}),e.jsx(A,{className:"w-[120px] text-right",children:"Detach"})]})}),e.jsxs(we,{children:[xe.map(l=>e.jsxs(q,{children:[e.jsx(p,{className:"font-mono text-sm",children:l.key}),e.jsx(p,{className:"font-mono text-sm",children:l.value}),e.jsx(p,{children:e.jsx("div",{className:"flex justify-end",children:e.jsxs(h,{variant:"destructive",size:"sm",onClick:()=>Xt(l.id),children:[e.jsx(qe,{className:"mr-2 h-4 w-4"})," Detach"]})})})]},l.id)),xe.length===0&&e.jsx(q,{children:e.jsx(p,{colSpan:3,className:"text-muted-foreground py-8 text-center",children:"No labels attached yet."})})]})]})})]}),e.jsx("div",{className:"pt-4",children:e.jsx(ne,{...Ze,children:e.jsxs("form",{onSubmit:Ze.handleSubmit(Yt),className:"space-y-3",children:[e.jsx(v,{control:Ze.control,name:"label_ids",render:({field:l})=>e.jsxs(b,{children:[e.jsx(N,{children:"Attach more labels"}),e.jsx("div",{className:"grid max-h-64 grid-cols-1 gap-2 overflow-auto rounded-xl border p-2 md:grid-cols-2",children:(()=>{const k=new Set(xe.map(I=>I.id)),Q=$.filter(I=>!k.has(I.id));return Q.length===0?e.jsx("div",{className:"text-muted-foreground p-2 text-sm",children:"No more labels available to attach"}):Q.map(I=>{const Z=l.value?.includes(I.id)||!1;return e.jsxs("label",{className:"hover:bg-accent flex cursor-pointer items-start gap-2 rounded p-1",children:[e.jsx(De,{checked:Z,onCheckedChange:ke=>{const J=new Set(l.value||[]);ke===!0?J.add(I.id):J.delete(I.id),l.onChange(Array.from(J))}}),e.jsxs("div",{className:"leading-tight",children:[e.jsx("div",{className:"text-sm font-medium",children:ni(I)}),e.jsx("div",{className:"text-muted-foreground text-xs",children:ts(I.id,8)})]})]},I.id)})})()}),e.jsx(w,{})]})}),e.jsx(de,{className:"gap-2",children:e.jsxs(h,{type:"submit",disabled:Ze.formState.isSubmitting,children:[e.jsx(ye,{className:"mr-2 h-4 w-4"})," ",Ze.formState.isSubmitting?"Attaching…":"Attach selected"]})})]})})})]})}),e.jsx(ie,{open:!!_,onOpenChange:l=>!l&&X(null),children:e.jsxs(oe,{className:"sm:max-w-2xl",children:[e.jsx(le,{children:e.jsxs(ce,{children:["Manage taints for ",e.jsx("span",{className:"font-mono",children:_?.name})]})}),e.jsxs("div",{className:"space-y-3",children:[e.jsx("div",{className:"text-sm font-medium",children:"Attached taints"}),Re?e.jsx("div",{className:"text-muted-foreground rounded-md border p-3 text-sm",children:"Loading…"}):Y?e.jsx("div",{className:"rounded-md border p-3 text-sm text-red-500",children:Y}):e.jsx("div",{className:"overflow-hidden rounded-xl border",children:e.jsxs(be,{children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Key"}),e.jsx(A,{children:"Value"}),e.jsx(A,{children:"Effect"}),e.jsx(A,{className:"w-[120px] text-right",children:"Detach"})]})}),e.jsxs(we,{children:[me.map(l=>e.jsxs(q,{children:[e.jsx(p,{className:"font-mono text-sm",children:l.key}),e.jsx(p,{className:"font-mono text-sm",children:l.value}),e.jsx(p,{className:"font-mono text-sm",children:l.effect}),e.jsx(p,{children:e.jsx("div",{className:"flex justify-end",children:e.jsxs(h,{variant:"destructive",size:"sm",onClick:()=>sa(l.id),children:[e.jsx(qe,{className:"mr-2 h-4 w-4"})," Detach"]})})})]},l.id)),me.length===0&&e.jsx(q,{children:e.jsx(p,{colSpan:4,className:"text-muted-foreground py-8 text-center",children:"No taints attached yet."})})]})]})})]}),e.jsx("div",{className:"pt-4",children:e.jsx(ne,{...es,children:e.jsxs("form",{onSubmit:es.handleSubmit(ea),className:"space-y-3",children:[e.jsx(v,{control:es.control,name:"taint_ids",render:({field:l})=>e.jsxs(b,{children:[e.jsx(N,{children:"Attach more taints"}),e.jsx("div",{className:"grid max-h-64 grid-cols-1 gap-2 overflow-auto rounded-xl border p-2 md:grid-cols-2",children:(()=>{const k=new Set(me.map(I=>I.id)),Q=O.filter(I=>!k.has(I.id));return Q.length===0?e.jsx("div",{className:"text-muted-foreground p-2 text-sm",children:"No more taints available to attach"}):Q.map(I=>{const Z=l.value?.includes(I.id)||!1;return e.jsxs("label",{className:"hover:bg-accent flex cursor-pointer items-start gap-2 rounded p-1",children:[e.jsx(De,{checked:Z,onCheckedChange:ke=>{const J=new Set(l.value||[]);ke===!0?J.add(I.id):J.delete(I.id),l.onChange(Array.from(J))}}),e.jsxs("div",{className:"leading-tight",children:[e.jsx("div",{className:"text-sm font-medium",children:vt(I)}),e.jsx("div",{className:"text-muted-foreground text-xs",children:ts(I.id,8)})]})]},I.id)})})()}),e.jsx(w,{})]})}),e.jsx(de,{className:"gap-2",children:e.jsxs(h,{type:"submit",disabled:es.formState.isSubmitting,children:[e.jsx(ye,{className:"mr-2 h-4 w-4"})," ",es.formState.isSubmitting?"Attaching…":"Attach selected"]})})]})})})]})}),e.jsx(ie,{open:!!i,onOpenChange:l=>!l&&D(null),children:e.jsxs(oe,{className:"sm:max-w-2xl",children:[e.jsx(le,{children:e.jsxs(ce,{children:["Manage annotations for"," ",e.jsx("span",{className:"font-mono",children:i?.name})]})}),e.jsxs("div",{className:"space-y-3",children:[e.jsx("div",{className:"text-sm font-medium",children:"Attached annotations"}),Se?e.jsx("div",{className:"text-muted-foreground rounded-md border p-3 text-sm",children:"Loading…"}):He?e.jsx("div",{className:"rounded-md border p-3 text-sm text-red-500",children:He}):e.jsx("div",{className:"overflow-hidden rounded-xl border",children:e.jsxs(be,{children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Name"}),e.jsx(A,{children:"Value"}),e.jsx(A,{className:"w-[120px] text-right",children:"Detach"})]})}),e.jsxs(we,{children:[ze.map(l=>e.jsxs(q,{children:[e.jsx(p,{className:"font-mono text-sm",children:l.name}),e.jsx(p,{className:"font-mono text-sm",children:l.value}),e.jsx(p,{children:e.jsx("div",{className:"flex justify-end",children:e.jsxs(h,{variant:"destructive",size:"sm",onClick:()=>na(l.id),children:[e.jsx(qe,{className:"mr-2 h-4 w-4"})," Detach"]})})})]},l.id)),ze.length===0&&e.jsx(q,{children:e.jsx(p,{colSpan:3,className:"text-muted-foreground py-8 text-center",children:"No annotations attached yet."})})]})]})})]}),e.jsx("div",{className:"pt-4",children:e.jsx(ne,{...ss,children:e.jsxs("form",{onSubmit:ss.handleSubmit(aa),className:"space-y-3",children:[e.jsx(v,{control:ss.control,name:"annotation_ids",render:({field:l})=>e.jsxs(b,{children:[e.jsx(N,{children:"Attach more annotations"}),e.jsx("div",{className:"grid max-h-64 grid-cols-1 gap-2 overflow-auto rounded-xl border p-2 md:grid-cols-2",children:(()=>{const k=new Set(ze.map(I=>I.id)),Q=R.filter(I=>!k.has(I.id));return Q.length===0?e.jsx("div",{className:"text-muted-foreground p-2 text-sm",children:"No more annotations available to attach"}):Q.map(I=>{const Z=l.value?.includes(I.id)||!1;return e.jsxs("label",{className:"hover:bg-accent flex cursor-pointer items-start gap-2 rounded p-1",children:[e.jsx(De,{checked:Z,onCheckedChange:ke=>{const J=new Set(l.value||[]);ke===!0?J.add(I.id):J.delete(I.id),l.onChange(Array.from(J))}}),e.jsxs("div",{className:"leading-tight",children:[e.jsx("div",{className:"text-sm font-medium",children:bt(I)}),e.jsx("div",{className:"text-muted-foreground text-xs",children:ts(I.id,8)})]})]},I.id)})})()}),e.jsx(w,{})]})}),e.jsx(de,{className:"gap-2",children:e.jsxs(h,{type:"submit",disabled:ss.formState.isSubmitting,children:[e.jsx(ye,{className:"mr-2 h-4 w-4"})," ",ss.formState.isSubmitting?"Attaching…":"Attach selected"]})})]})})})]})})]})},gs=["pending","provisioning","ready","failed"],vs=["master","worker","bastion"],Ut=re({hostname:F().trim().max(120,"Max 120 chars").optional(),ip_address:F().trim().min(1,"IP address is required"),role:Je(vs),ssh_key_id:us("Pick a valid SSH key"),ssh_user:F().trim().min(1,"SSH user is required"),status:Je(gs).default("pending")}),ii=Ut.partial();function oi({status:s}){const t=s==="ready"?"default":s==="provisioning"?"secondary":s==="failed"?"destructive":"outline";return e.jsx(Ce,{variant:t,className:"capitalize",children:s})}function Bs(s,t=16){return!s||s.length<=t*2+3?s:`${s.slice(0,t)}…${s.slice(-t)}`}const li=()=>{const[s,t]=o.useState([]),[r,d]=o.useState([]),[u,f]=o.useState(!1),[$,C]=o.useState(null),[O,M]=o.useState(""),[R,U]=o.useState(""),[T,P]=o.useState(""),[j,V]=o.useState(!1),[H,m]=o.useState(null);function x(){const a=new URLSearchParams;return R&&a.set("status",R),T&&a.set("role",T),`/api/v1/servers${a.toString()?`?${a.toString()}`:""}`}async function c(){f(!0),C(null);try{const[a,n]=await Promise.all([g.get(x()),g.get("/api/v1/ssh")]);t(a??[]),d(n??[])}catch(a){console.error(a),C("Failed to load servers or SSH keys")}finally{f(!1)}}o.useEffect(()=>{c();const a=n=>{n.key==="active_org_id"&&c()};return window.addEventListener("storage",a),()=>window.removeEventListener("storage",a)},[]),o.useEffect(()=>{c()},[R,T]);const S=o.useMemo(()=>{const a=new Map;return r.forEach(n=>a.set(n.id,n)),a},[r]),G=o.useMemo(()=>{const a=O.trim().toLowerCase();return a?s.filter(n=>(n.hostname??"").toLowerCase().includes(a)||n.ip_address.toLowerCase().includes(a)||n.role.toLowerCase().includes(a)||n.ssh_user.toLowerCase().includes(a)):s},[s,O]);async function K(a){confirm("Delete this server? This cannot be undone.")&&(await g.delete(`/api/v1/servers/${encodeURIComponent(a)}`),await c())}const B=te({resolver:ae(Ut),defaultValues:{hostname:"",ip_address:"",role:"worker",ssh_key_id:"",ssh_user:"ubuntu",status:"pending"}}),xe=async a=>{const n={ip_address:a.ip_address.trim(),role:a.role,ssh_key_id:a.ssh_key_id,ssh_user:a.ssh_user.trim(),status:a.status};a.hostname&&a.hostname.trim()&&(n.hostname=a.hostname.trim()),await g.post("/api/v1/servers",n),V(!1),B.reset(),await c()},W=te({resolver:ae(ii),defaultValues:{}});function ue(a){m(a),W.reset({hostname:a.hostname??"",ip_address:a.ip_address,role:vs.includes(a.role)?a.role:"worker",ssh_key_id:a.ssh_key_id,ssh_user:a.ssh_user,status:gs.includes(a.status)?a.status:"pending"})}const fe=async a=>{if(!H)return;const n={};a.hostname!==void 0&&(n.hostname=a.hostname?.trim()||""),a.ip_address!==void 0&&(n.ip_address=a.ip_address.trim()),a.role!==void 0&&(n.role=a.role),a.ssh_key_id!==void 0&&(n.ssh_key_id=a.ssh_key_id),a.ssh_user!==void 0&&(n.ssh_user=a.ssh_user.trim()),a.status!==void 0&&(n.status=a.status),await g.patch(`/api/v1/servers/${encodeURIComponent(H.id)}`,n),m(null),await c()};return u?e.jsx("div",{className:"p-6",children:"Loading servers…"}):$?e.jsx("div",{className:"p-6 text-red-500",children:$}):e.jsxs(Os,{children:[e.jsxs("div",{className:"space-y-4 p-6",children:[e.jsxs("div",{className:"flex flex-col gap-3 md:flex-row md:items-center md:justify-between",children:[e.jsx("h1",{className:"mb-4 text-2xl font-bold",children:"Servers"}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("div",{className:"relative",children:[e.jsx(Ts,{className:"absolute top-2.5 left-2 h-4 w-4 opacity-60"}),e.jsx(z,{value:O,onChange:a=>M(a.target.value),placeholder:"Search hostname, IP, role, user…",className:"w-64 pl-8"})]}),e.jsxs(Ae,{value:T,onValueChange:a=>P(a),children:[e.jsx($e,{className:"w-36",children:e.jsx(Ee,{placeholder:"Role (all)"})}),e.jsx(Le,{children:vs.map(a=>e.jsx(ve,{value:a,children:a},a))})]}),e.jsxs(Ae,{value:R,onValueChange:a=>U(a),children:[e.jsx($e,{className:"w-40",children:e.jsx(Ee,{placeholder:"Status (all)"})}),e.jsx(Le,{children:gs.map(a=>e.jsx(ve,{value:a,children:a},a))})]}),e.jsxs(h,{variant:"outline",onClick:c,children:[e.jsx(Ps,{className:"mr-2 h-4 w-4"}),"Refresh"]}),e.jsxs(ie,{open:j,onOpenChange:V,children:[e.jsx(We,{asChild:!0,children:e.jsxs(h,{onClick:()=>V(!0),children:[e.jsx(Qe,{className:"mr-2 h-4 w-4"}),"Create Server"]})}),e.jsxs(oe,{className:"sm:max-w-lg",children:[e.jsx(le,{children:e.jsx(ce,{children:"Create server"})}),e.jsx(ne,{...B,children:e.jsxs("form",{onSubmit:B.handleSubmit(xe),className:"space-y-4",children:[e.jsx(v,{control:B.control,name:"hostname",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"Hostname"}),e.jsx(E,{children:e.jsx(z,{placeholder:"worker-01",...a})}),e.jsx(w,{})]})}),e.jsx(v,{control:B.control,name:"ip_address",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"IP address"}),e.jsx(E,{children:e.jsx(z,{placeholder:"10.0.1.23",...a})}),e.jsx(w,{})]})}),e.jsxs("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2",children:[e.jsx(v,{control:B.control,name:"role",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"Role"}),e.jsxs(Ae,{onValueChange:a.onChange,value:a.value,children:[e.jsx(E,{children:e.jsx($e,{children:e.jsx(Ee,{placeholder:"Select role"})})}),e.jsx(Le,{children:vs.map(n=>e.jsx(ve,{value:n,children:n},n))})]}),e.jsx(w,{})]})}),e.jsx(v,{control:B.control,name:"ssh_user",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"SSH user"}),e.jsx(E,{children:e.jsx(z,{placeholder:"ubuntu",...a})}),e.jsx(w,{})]})})]}),e.jsx(v,{control:B.control,name:"ssh_key_id",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"SSH key"}),e.jsxs(Ae,{onValueChange:a.onChange,value:a.value,children:[e.jsx(E,{children:e.jsx($e,{children:e.jsx(Ee,{placeholder:r.length?"Select SSH key":"No SSH keys found"})})}),e.jsx(Le,{children:r.map(n=>e.jsxs(ve,{value:n.id,children:[n.name?n.name:"Unnamed key"," —"," ",Bs(n.fingerprint,8)]},n.id))})]}),e.jsx(w,{})]})}),e.jsx(v,{control:B.control,name:"status",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"Initial status"}),e.jsxs(Ae,{onValueChange:a.onChange,value:a.value,children:[e.jsx(E,{children:e.jsx($e,{children:e.jsx(Ee,{placeholder:"pending"})})}),e.jsx(Le,{children:gs.map(n=>e.jsx(ve,{value:n,children:n},n))})]}),e.jsx(w,{})]})}),e.jsxs(de,{className:"gap-2",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>V(!1),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:B.formState.isSubmitting,children:B.formState.isSubmitting?"Creating…":"Create"})]})]})})]})]})]})]}),e.jsx("div",{className:"bg-background overflow-hidden rounded-2xl border shadow-sm",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs(be,{children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Hostname"}),e.jsx(A,{children:"IP address"}),e.jsx(A,{children:"Role"}),e.jsx(A,{children:"SSH user"}),e.jsx(A,{children:"SSH key"}),e.jsx(A,{children:"Status"}),e.jsx(A,{children:"Created"}),e.jsx(A,{className:"w-[180px] text-right",children:"Actions"})]})}),e.jsxs(we,{children:[G.map(a=>{const n=S.get(a.ssh_key_id);return e.jsxs(q,{children:[e.jsx(p,{className:"font-medium",children:a.hostname||"—"}),e.jsx(p,{children:e.jsx("code",{className:"font-mono text-sm",children:a.ip_address})}),e.jsx(p,{className:"capitalize",children:a.role}),e.jsx(p,{children:e.jsx("code",{className:"font-mono text-sm",children:a.ssh_user})}),e.jsx(p,{children:n?e.jsxs(Ys,{children:[e.jsx(Xs,{asChild:!0,children:e.jsxs("span",{className:"inline-flex items-center gap-2",children:[e.jsx(Ce,{variant:"secondary",children:n.name||"SSH key"}),e.jsx("code",{className:"font-mono text-xs",children:Bs(n.fingerprint,8)})]})}),e.jsx(Zs,{className:"max-w-[70vw]",children:e.jsx("p",{className:"font-mono text-xs break-all",children:n.public_keys})})]}):e.jsx("span",{className:"text-muted-foreground",children:"Unknown"})}),e.jsx(p,{children:e.jsx(oi,{status:a.status})}),e.jsx(p,{children:new Date(a.created_at).toLocaleString()}),e.jsx(p,{children:e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>ue(a),children:[e.jsx(rs,{className:"mr-2 h-4 w-4"})," Edit"]}),e.jsxs(is,{children:[e.jsx(os,{asChild:!0,children:e.jsxs(h,{variant:"destructive",size:"sm",children:[e.jsx(Ue,{className:"mr-2 h-4 w-4"})," Delete"]})}),e.jsx(ls,{align:"end",children:e.jsx(Ie,{onClick:()=>K(a.id),children:"Confirm delete"})})]})]})})]},a.id)}),G.length===0&&e.jsx(q,{children:e.jsx(p,{colSpan:8,className:"text-muted-foreground py-10 text-center",children:"No servers match your filters."})})]})]})})})]}),e.jsx(ie,{open:!!H,onOpenChange:a=>!a&&m(null),children:e.jsxs(oe,{className:"sm:max-w-lg",children:[e.jsx(le,{children:e.jsx(ce,{children:"Edit server"})}),e.jsx(ne,{...W,children:e.jsxs("form",{onSubmit:W.handleSubmit(fe),className:"space-y-4",children:[e.jsx(v,{control:W.control,name:"hostname",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"Hostname"}),e.jsx(E,{children:e.jsx(z,{placeholder:"worker-01",...a})}),e.jsx(w,{})]})}),e.jsx(v,{control:W.control,name:"ip_address",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"IP address"}),e.jsx(E,{children:e.jsx(z,{placeholder:"10.0.1.23",...a})}),e.jsx(w,{})]})}),e.jsxs("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2",children:[e.jsx(v,{control:W.control,name:"role",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"Role"}),e.jsxs(Ae,{onValueChange:a.onChange,value:a.value,children:[e.jsx(E,{children:e.jsx($e,{children:e.jsx(Ee,{placeholder:"Select role"})})}),e.jsx(Le,{children:vs.map(n=>e.jsx(ve,{value:n,children:n},n))})]}),e.jsx(w,{})]})}),e.jsx(v,{control:W.control,name:"ssh_user",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"SSH user"}),e.jsx(E,{children:e.jsx(z,{placeholder:"ubuntu",...a})}),e.jsx(w,{})]})})]}),e.jsx(v,{control:W.control,name:"ssh_key_id",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"SSH key"}),e.jsxs(Ae,{onValueChange:a.onChange,value:a.value,children:[e.jsx(E,{children:e.jsx($e,{children:e.jsx(Ee,{placeholder:r.length?"Select SSH key":"No SSH keys found"})})}),e.jsx(Le,{children:r.map(n=>e.jsxs(ve,{value:n.id,children:[n.name?n.name:"SSH key"," — ",Bs(n.fingerprint,8)]},n.id))})]}),e.jsx(w,{})]})}),e.jsx(v,{control:W.control,name:"status",render:({field:a})=>e.jsxs(b,{children:[e.jsx(N,{children:"Status"}),e.jsxs(Ae,{onValueChange:a.onChange,value:a.value,children:[e.jsx(E,{children:e.jsx($e,{children:e.jsx(Ee,{placeholder:"pending"})})}),e.jsx(Le,{children:gs.map(n=>e.jsx(ve,{value:n,children:n},n))})]}),e.jsx(w,{})]})}),e.jsxs(de,{className:"gap-2",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>m(null),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:W.formState.isSubmitting,children:W.formState.isSubmitting?"Saving…":"Save changes"})]})]})})]})})]})},Is=["NoSchedule","PreferNoSchedule","NoExecute"],di=re({key:F().trim().min(1,"Key is required").max(120,"Max 120 chars"),value:F().trim().optional(),effect:Je(Is),node_pool_ids:Oe(us()).optional().default([])}),ci=re({key:F().trim().min(1,"Key is required").max(120).optional(),value:F().trim().optional(),effect:Je(Is).optional()}),mi=re({node_pool_ids:Oe(F().uuid()).min(1,"Pick at least one node pool")});function Hs(s,t=12){return s?s.length<=t*2+3?s:`${s.slice(0,t)}…${s.slice(-t)}`:""}function xi({t:s}){const t=`${s.key}${s.value?`=${s.value}`:""}${s.effect?`:${s.effect}`:""}`;return e.jsxs(Ce,{variant:"secondary",className:"font-mono text-xs",children:[e.jsx(It,{className:"mr-1 h-3 w-3"}),t]})}const hi=()=>{const[s,t]=o.useState(!0),[r,d]=o.useState(null),[u,f]=o.useState([]),[$,C]=o.useState([]),[O,M]=o.useState(""),[R,U]=o.useState(!1),[T,P]=o.useState(null),[j,V]=o.useState(null);async function H(){t(!0),d(null);try{const[n,_]=await Promise.all([g.get("/api/v1/taints?include=node_groups"),g.get("/api/v1/node-pools")]);if(f(n||[]),C(_||[]),j){const X=(n||[]).find(me=>me.id===j.id)||null;V(X)}if(T){const X=(n||[]).find(me=>me.id===T.id)||null;P(X)}}catch(n){console.error(n);const _=n instanceof je?n.message:"Failed to load taints or node pools";d(_)}finally{t(!1)}}o.useEffect(()=>{H()},[]);const m=o.useMemo(()=>{const n=O.trim().toLowerCase();return n?u.filter(_=>{const X=`${_.key}${_.value?`=${_.value}`:""}${_.effect?`:${_.effect}`:""}`.toLowerCase(),me=(_.node_groups||[]).some(ge=>ge.name.toLowerCase().includes(n));return X.includes(n)||me}):u},[u,O]);async function x(n){confirm("Delete this taint? This cannot be undone.")&&(await g.delete(`/api/v1/taints/${n}`),await H())}const c=te({resolver:ae(di),defaultValues:{key:"",value:"",effect:void 0,node_pool_ids:[]}}),S=async n=>{const _={key:n.key.trim(),effect:n.effect};n.value&&(_.value=n.value.trim()),n.node_pool_ids&&n.node_pool_ids.length>0&&(_.node_pool_ids=n.node_pool_ids),await g.post("/api/v1/taints",_),U(!1),c.reset({key:"",value:"",effect:void 0,node_pool_ids:[]}),await H()},G=te({resolver:ae(ci),defaultValues:{}});function K(n){P(n),G.reset({key:n.key,value:n.value||"",effect:n.effect||void 0})}const B=async n=>{if(!T)return;const _={};n.key!==void 0&&(_.key=n.key.trim()),n.value!==void 0&&(_.value=n.value?.trim()??""),n.effect!==void 0&&(_.effect=n.effect),await g.patch(`/api/v1/taints/${T.id}`,_),P(null),await H()},xe=te({resolver:ae(mi),defaultValues:{node_pool_ids:[]}});function W(n){V(n),xe.reset({node_pool_ids:[]})}const ue=async n=>{j&&(await g.post(`/api/v1/taints/${j.id}/node_pools`,{node_pool_ids:n.node_pool_ids}),xe.reset({node_pool_ids:[]}),await H())};async function fe(n){j&&confirm("Detach this taint from the node pool?")&&(await g.delete(`/api/v1/taints/${j.id}/node_pools/${n}`),await H())}const a=o.useMemo(()=>{if(!j)return[];const n=new Set((j.node_groups||[]).map(_=>_.id));return $.filter(_=>!n.has(_.id))},[j,$]);return s?e.jsx("div",{className:"p-6",children:"Loading taints…"}):r?e.jsx("div",{className:"p-6 text-red-500",children:r}):e.jsxs("div",{className:"space-y-4 p-6",children:[e.jsxs("div",{className:"flex flex-col gap-3 md:flex-row md:items-center md:justify-between",children:[e.jsx("h1",{className:"mb-4 text-2xl font-bold",children:"Taints"}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("div",{className:"relative",children:[e.jsx(Ts,{className:"absolute top-2.5 left-2 h-4 w-4 opacity-60"}),e.jsx(z,{value:O,onChange:n=>M(n.target.value),placeholder:"Search taints or attached pools…",className:"w-72 pl-8"})]}),e.jsxs(h,{variant:"outline",onClick:H,children:[e.jsx(Ps,{className:"mr-2 h-4 w-4"})," Refresh"]}),e.jsxs(ie,{open:R,onOpenChange:U,children:[e.jsx(We,{asChild:!0,children:e.jsxs(h,{onClick:()=>U(!0),children:[e.jsx(Qe,{className:"mr-2 h-4 w-4"})," Create Taint"]})}),e.jsxs(oe,{className:"sm:max-w-lg",children:[e.jsx(le,{children:e.jsx(ce,{children:"Create taint"})}),e.jsx(ne,{...c,children:e.jsxs("form",{onSubmit:c.handleSubmit(S),className:"space-y-4",children:[e.jsx(v,{control:c.control,name:"key",render:({field:n})=>e.jsxs(b,{children:[e.jsx(N,{children:"Key"}),e.jsx(E,{children:e.jsx(z,{placeholder:"dedicated",...n})}),e.jsx(w,{})]})}),e.jsx(v,{control:c.control,name:"value",render:({field:n})=>e.jsxs(b,{children:[e.jsx(N,{children:"Value (optional)"}),e.jsx(E,{children:e.jsx(z,{placeholder:"gpu",...n})}),e.jsx(w,{})]})}),e.jsx(v,{control:c.control,name:"effect",render:({field:n})=>e.jsxs(b,{children:[e.jsx(N,{children:"Effect"}),e.jsxs(Ae,{onValueChange:n.onChange,value:n.value,children:[e.jsx(E,{children:e.jsx($e,{children:e.jsx(Ee,{placeholder:"Select effect"})})}),e.jsx(Le,{children:Is.map(_=>e.jsx(ve,{value:_,children:_},_))})]}),e.jsx(w,{})]})}),e.jsx(v,{control:c.control,name:"node_pool_ids",render:({field:n})=>e.jsxs(b,{children:[e.jsx(N,{children:"Attach to node pools (optional)"}),e.jsxs("div",{className:"max-h-56 space-y-2 overflow-auto rounded-xl border p-2",children:[$.length===0&&e.jsx("div",{className:"text-muted-foreground p-2 text-sm",children:"No node pools available"}),$.map(_=>{const X=n.value?.includes(_.id)||!1;return e.jsxs("label",{className:"hover:bg-accent flex cursor-pointer items-start gap-2 rounded p-1",children:[e.jsx(De,{checked:X,onCheckedChange:me=>{const ge=new Set(n.value||[]);me===!0?ge.add(_.id):ge.delete(_.id),n.onChange(Array.from(ge))}}),e.jsxs("div",{className:"leading-tight",children:[e.jsx("div",{className:"text-sm font-medium",children:_.name}),e.jsx("div",{className:"text-muted-foreground text-xs",children:Hs(_.id,8)})]})]},_.id)})]}),e.jsx(w,{})]})}),e.jsxs(de,{className:"gap-2",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>U(!1),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:c.formState.isSubmitting,children:c.formState.isSubmitting?"Creating…":"Create"})]})]})})]})]})]})]}),e.jsx("div",{className:"bg-background overflow-hidden rounded-2xl border shadow-sm",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs(be,{children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Taint"}),e.jsx(A,{children:"Attached Node Pools"}),e.jsx(A,{className:"w-[180px] text-right",children:"Actions"})]})}),e.jsxs(we,{children:[m.map(n=>e.jsxs(q,{children:[e.jsx(p,{className:"font-medium",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(xi,{t:n}),e.jsx("code",{className:"text-muted-foreground text-xs",children:Hs(n.id,6)})]})}),e.jsxs(p,{children:[e.jsxs("div",{className:"mb-2 flex flex-wrap gap-2",children:[(n.node_groups||[]).slice(0,6).map(_=>e.jsxs(Ce,{variant:"outline",className:"gap-1",children:[e.jsx(yn,{className:"h-3 w-3"}),_.name]},_.id)),(n.node_groups||[]).length===0&&e.jsx("span",{className:"text-muted-foreground",children:"No node pools"}),(n.node_groups||[]).length>6&&e.jsxs("span",{className:"text-muted-foreground",children:["+",(n.node_groups||[]).length-6," more"]})]}),e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>W(n),children:[e.jsx(ye,{className:"mr-2 h-4 w-4"})," Manage node pools"]})]}),e.jsx(p,{children:e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>K(n),children:[e.jsx(rs,{className:"mr-2 h-4 w-4"})," Edit"]}),e.jsxs(is,{children:[e.jsx(os,{asChild:!0,children:e.jsxs(h,{variant:"destructive",size:"sm",children:[e.jsx(Ue,{className:"mr-2 h-4 w-4"})," Delete"]})}),e.jsx(ls,{align:"end",children:e.jsx(Ie,{onClick:()=>x(n.id),children:"Confirm delete"})})]})]})})]},n.id)),m.length===0&&e.jsx(q,{children:e.jsxs(p,{colSpan:3,className:"text-muted-foreground py-10 text-center",children:[e.jsx(Sn,{className:"mx-auto mb-2 h-6 w-6 opacity-60"}),"No taints match your search."]})})]})]})})}),e.jsx(ie,{open:!!T,onOpenChange:n=>!n&&P(null),children:e.jsxs(oe,{className:"sm:max-w-md",children:[e.jsx(le,{children:e.jsx(ce,{children:"Edit taint"})}),e.jsx(ne,{...G,children:e.jsxs("form",{onSubmit:G.handleSubmit(B),className:"space-y-4",children:[e.jsx(v,{control:G.control,name:"key",render:({field:n})=>e.jsxs(b,{children:[e.jsx(N,{children:"Key"}),e.jsx(E,{children:e.jsx(z,{placeholder:"dedicated",...n})}),e.jsx(w,{})]})}),e.jsx(v,{control:G.control,name:"value",render:({field:n})=>e.jsxs(b,{children:[e.jsx(N,{children:"Value (optional)"}),e.jsx(E,{children:e.jsx(z,{placeholder:"gpu",...n})}),e.jsx(w,{})]})}),e.jsx(v,{control:G.control,name:"effect",render:({field:n})=>e.jsxs(b,{children:[e.jsx(N,{children:"Effect"}),e.jsxs(Ae,{onValueChange:n.onChange,value:n.value,children:[e.jsx(E,{children:e.jsx($e,{children:e.jsx(Ee,{placeholder:"Select effect"})})}),e.jsx(Le,{children:Is.map(_=>e.jsx(ve,{value:_,children:_},_))})]}),e.jsx(w,{})]})}),e.jsxs(de,{className:"gap-2",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>P(null),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:G.formState.isSubmitting,children:G.formState.isSubmitting?"Saving…":"Save changes"})]})]})})]})}),e.jsx(ie,{open:!!j,onOpenChange:n=>!n&&V(null),children:e.jsxs(oe,{className:"sm:max-w-2xl",children:[e.jsx(le,{children:e.jsxs(ce,{children:["Manage pools for"," ",e.jsx("span",{className:"font-mono",children:j?`${j.key}${j.value?`=${j.value}`:""}${j.effect?`:${j.effect}`:""}`:""})]})}),e.jsxs("div",{className:"space-y-3",children:[e.jsx("div",{className:"text-sm font-medium",children:"Attached node pools"}),e.jsx("div",{className:"overflow-hidden rounded-xl border",children:e.jsxs(be,{children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Name"}),e.jsx(A,{className:"w-[120px] text-right",children:"Detach"})]})}),e.jsxs(we,{children:[(j?.node_groups||[]).map(n=>e.jsxs(q,{children:[e.jsx(p,{className:"font-medium",children:n.name}),e.jsx(p,{children:e.jsx("div",{className:"flex justify-end",children:e.jsxs(h,{variant:"destructive",size:"sm",onClick:()=>fe(n.id),children:[e.jsx(qe,{className:"mr-2 h-4 w-4"})," Detach"]})})})]},n.id)),(j?.node_groups||[]).length===0&&e.jsx(q,{children:e.jsx(p,{colSpan:2,className:"text-muted-foreground py-8 text-center",children:"No node pools attached yet."})})]})]})})]}),e.jsx("div",{className:"pt-4",children:e.jsx(ne,{...xe,children:e.jsxs("form",{onSubmit:xe.handleSubmit(ue),className:"space-y-3",children:[e.jsx(v,{control:xe.control,name:"node_pool_ids",render:({field:n})=>e.jsxs(b,{children:[e.jsx(N,{children:"Attach more node pools"}),e.jsxs("div",{className:"grid max-h-64 grid-cols-1 gap-2 overflow-auto rounded-xl border p-2 md:grid-cols-2",children:[a.length===0&&e.jsx("div",{className:"text-muted-foreground p-2 text-sm",children:"No more node pools available to attach"}),a.map(_=>{const X=n.value?.includes(_.id)||!1;return e.jsxs("label",{className:"hover:bg-accent flex cursor-pointer items-start gap-2 rounded p-1",children:[e.jsx(De,{checked:X,onCheckedChange:me=>{const ge=new Set(n.value||[]);me===!0?ge.add(_.id):ge.delete(_.id),n.onChange(Array.from(ge))}}),e.jsxs("div",{className:"leading-tight",children:[e.jsx("div",{className:"text-sm font-medium",children:_.name}),e.jsx("div",{className:"text-muted-foreground text-xs",children:Hs(_.id,8)})]})]},_.id)})]}),e.jsx(w,{})]})}),e.jsx(de,{className:"gap-2",children:e.jsxs(h,{type:"submit",disabled:xe.formState.isSubmitting,children:[e.jsx(ye,{className:"mr-2 h-4 w-4"})," ",xe.formState.isSubmitting?"Attaching…":"Attach selected"]})})]})})})]})})]})};function Nt(){return e.jsxs("div",{className:"p-6",children:[e.jsx("h1",{className:"text-2xl font-bold",children:"403 — Forbidden"}),e.jsx("p",{className:"text-muted-foreground text-sm",children:"You don’t have access to this area."})]})}const wt=()=>{const s=_s();return e.jsxs("div",{className:"bg-background text-foreground flex min-h-screen flex-col items-center justify-center",children:[e.jsx("h1",{className:"mb-4 text-6xl font-bold",children:"404"}),e.jsx("p",{className:"mb-8 text-2xl",children:"Oops! Page not found"}),e.jsx(h,{onClick:()=>s("/dashboard"),children:"Go back to Dashboard"})]})},ui=re({name:F().min(1,"Name is required").max(100,"Max 100 characters"),comment:F().trim().max(100,"Max 100 characters").default(""),bits:Je(["2048","3072","4096"])});function ji(s,t="download.bin"){if(!s)return t;const r=/filename\*=UTF-8''([^;]+)/i.exec(s);return r?.[1]?decodeURIComponent(r[1]):/filename="?([^"]+)"?/i.exec(s)?.[1]??t}function fi(s,t=24){return!s||s.length<=t*2+3?s:`${s.slice(0,t)}…${s.slice(-t)}`}function pi(s){return s?.split(/\s+/)?.[0]??"ssh-key"}async function gi(s){try{await navigator.clipboard.writeText(s)}catch{const t=document.createElement("textarea");t.value=s,t.setAttribute("readonly",""),t.style.position="absolute",t.style.left="-9999px",document.body.appendChild(t),t.select(),document.execCommand("copy"),document.body.removeChild(t)}}const vi=()=>{const[s,t]=o.useState([]),[r,d]=o.useState(null),[u,f]=o.useState(!0),[$,C]=o.useState(""),[O,M]=o.useState(!1),R=o.useMemo(()=>!!localStorage.getItem("active_org_id"),[]);async function U(){f(!0),d(null);try{if(!R){t([]),d("Select an organization first.");return}const m=await g.get("/api/v1/ssh");t(m??[])}catch(m){console.error(m),d("Failed to fetch SSH keys")}finally{f(!1)}}o.useEffect(()=>{U();const m=x=>{x.key==="active_org_id"&&U()};return window.addEventListener("storage",m),()=>window.removeEventListener("storage",m)},[]);const T=s.filter(m=>`${m.name} ${m.public_keys} ${m.fingerprint}`.toLowerCase().includes($.toLowerCase()));async function P(m,x="both"){const c=localStorage.getItem("access_token"),S=localStorage.getItem("active_org_id"),G=`${Ls}/api/v1/ssh/${encodeURIComponent(m)}/download?part=${encodeURIComponent(x)}`;try{const K=await fetch(G,{method:"GET",headers:{...c?{Authorization:`Bearer ${c}`}:{},...S?{"X-Org-ID":S}:{}}});if(!K.ok){const a=await K.text().catch(()=>"");throw new Error(a||`HTTP ${K.status}`)}const B=await K.blob(),xe=x==="both"?`ssh_key_${m}.zip`:x==="public"?`id_rsa_${m}.pub`:`id_rsa_${m}.pem`,W=ji(K.headers.get("content-disposition")??void 0,xe),ue=URL.createObjectURL(B),fe=document.createElement("a");fe.href=ue,fe.download=W,document.body.appendChild(fe),fe.click(),fe.remove(),URL.revokeObjectURL(ue)}catch(K){console.error(K),alert(K instanceof Error?K.message:"Download failed")}}async function j(m){try{await g.delete(`/api/v1/ssh/${encodeURIComponent(m)}`),await U()}catch(x){console.error(x),alert("Failed to delete key")}}const V=te({resolver:ae(ui),defaultValues:{name:"",comment:"deploy@autoglue",bits:"4096"}});async function H(m){try{await g.post("/api/v1/ssh",{bits:Number(m.bits),comment:m.comment?.trim()??"",name:m.name.trim(),download:"none"}),M(!1),V.reset(),await U()}catch(x){console.error(x),alert("Failed to create key")}}return u?e.jsx("div",{className:"p-6",children:"Loading SSH Keys…"}):r?e.jsx("div",{className:"p-6 text-red-500",children:r}):e.jsx(Os,{children:e.jsxs("div",{className:"space-y-4 p-6",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsx("h1",{className:"text-2xl font-bold",children:"SSH Keys"}),e.jsx("div",{className:"w-full max-w-sm",children:e.jsx(z,{value:$,onChange:m=>C(m.target.value),placeholder:"Search by name, fingerprint or key"})}),e.jsxs(ie,{open:O,onOpenChange:M,children:[e.jsx(We,{asChild:!0,children:e.jsxs(h,{onClick:()=>M(!0),children:[e.jsx(Qe,{className:"mr-2 h-4 w-4"}),"Create New Keypair"]})}),e.jsxs(oe,{className:"sm:max-w-lg",children:[e.jsx(le,{children:e.jsx(ce,{children:"Create SSH Keypair"})}),e.jsx(ne,{...V,children:e.jsxs("form",{onSubmit:V.handleSubmit(H),className:"space-y-4",children:[e.jsx(v,{control:V.control,name:"name",render:({field:m})=>e.jsxs(b,{children:[e.jsx(N,{children:"Name"}),e.jsx(E,{children:e.jsx(z,{placeholder:"e.g., CI deploy key",...m})}),e.jsx(w,{})]})}),e.jsx(v,{control:V.control,name:"comment",render:({field:m})=>e.jsxs(b,{children:[e.jsx(N,{children:"Comment"}),e.jsx(E,{children:e.jsx(z,{placeholder:"e.g., deploy@autoglue",...m})}),e.jsx(w,{})]})}),e.jsx(v,{control:V.control,name:"bits",render:({field:m})=>e.jsxs(b,{children:[e.jsx(N,{children:"Key size"}),e.jsx(E,{children:e.jsxs("select",{className:"bg-background w-full rounded-md border px-3 py-2 text-sm",value:m.value,onChange:m.onChange,children:[e.jsx("option",{value:"2048",children:"2048"}),e.jsx("option",{value:"3072",children:"3072"}),e.jsx("option",{value:"4096",children:"4096"})]})}),e.jsx(w,{})]})}),e.jsxs(de,{className:"gap-2",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>M(!1),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:V.formState.isSubmitting,children:V.formState.isSubmitting?"Creating…":"Create"})]})]})})]})]})]}),e.jsx("div",{className:"bg-background overflow-hidden rounded-2xl border shadow-sm",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs(be,{children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Name"}),e.jsx(A,{className:"min-w-[360px]",children:"Public Key"}),e.jsx(A,{children:"Fingerprint"}),e.jsx(A,{children:"Created"}),e.jsx(A,{className:"w-[160px] text-right",children:"Actions"})]})}),e.jsx(we,{children:T.map(m=>{const x=pi(m.public_keys),c=fi(m.public_keys,18);return e.jsxs(q,{children:[e.jsx(p,{className:"align-top",children:m.name}),e.jsx(p,{className:"align-top",children:e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx(Ce,{variant:"secondary",className:"whitespace-nowrap",children:x}),e.jsxs(Ys,{children:[e.jsx(Xs,{asChild:!0,children:e.jsx("code",{className:"font-mono text-sm break-all md:max-w-[48ch] md:truncate md:break-normal",children:c})}),e.jsx(Zs,{className:"max-w-[70vw]",children:e.jsx("div",{className:"max-w-full",children:e.jsx("p",{className:"font-mono text-xs break-all",children:m.public_keys})})})]})]})}),e.jsx(p,{className:"align-top",children:e.jsx("code",{className:"font-mono text-sm",children:m.fingerprint})}),e.jsx(p,{className:"align-top",children:new Date(m.created_at).toLocaleString(void 0,{year:"numeric",month:"short",day:"2-digit",hour:"2-digit",minute:"2-digit"})}),e.jsx(p,{className:"align-top",children:e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsxs(h,{variant:"outline",size:"sm",onClick:()=>gi(m.public_keys),title:"Copy public key",children:[e.jsx(Cn,{className:"mr-2 h-4 w-4"}),"Copy"]}),e.jsxs(is,{children:[e.jsx(os,{asChild:!0,children:e.jsxs(h,{variant:"outline",size:"sm",children:[e.jsx(_n,{className:"mr-2 h-4 w-4"}),"Download"]})}),e.jsxs(ls,{align:"end",children:[e.jsx(Ie,{onClick:()=>P(m.id,"both"),children:"Public + Private (.zip)"}),e.jsx(Ie,{onClick:()=>P(m.id,"public"),children:"Public only (.pub)"}),e.jsx(Ie,{onClick:()=>P(m.id,"private"),children:"Private only (.pem)"})]})]}),e.jsxs(h,{variant:"destructive",size:"sm",onClick:()=>j(m.id),children:[e.jsx(Ue,{className:"mr-2 h-4 w-4"}),"Delete"]})]})})]},m.id)})})]})})})]})})};function bi(s){const t=s?.user_id??s?.UserID??s?.user?.id??s?.User?.ID??"",r=s?.email??s?.Email??s?.user?.email??s?.User?.Email,d=s?.name??s?.Name??s?.user?.name??s?.User?.Name,u=s?.role??s?.Role??"member",f=s?.created_at??s?.CreatedAt;return{userId:String(t),email:r,name:d,role:String(u),joinedAt:f}}const Ni=re({email:js("Enter a valid email"),role:Je(["member","admin"])}),wi=()=>{const[s,t]=o.useState(!0),[r,d]=o.useState([]),[u,f]=o.useState(null),[$,C]=o.useState(!1),[O,M]=o.useState(!1),[R,U]=o.useState(null),T=o.useMemo(()=>Ke(),[]),P=te({resolver:ae(Ni),defaultValues:{email:"",role:"member"},mode:"onChange"});async function j(){try{const x=await g.get("/api/v1/auth/me");f(x)}catch{}}async function V(x){if(!x){d([]),t(!1);return}t(!0);try{const c=await g.get("/api/v1/orgs/members");d((c??[]).map(bi))}catch(c){const S=c instanceof je?c.message:"Failed to load members";ee.error(S)}finally{t(!1)}}o.useEffect(()=>{j(),V(T)},[T]),o.useEffect(()=>{const x=()=>void V(Ke()),c=S=>{S.key==="active_org_id"&&x()};return window.addEventListener(ns,x),window.addEventListener("storage",c),()=>{window.removeEventListener(ns,x),window.removeEventListener("storage",c)}},[]);async function H(x){const c=Ke();if(!c){ee.error("Select an organization first");return}try{M(!0),await g.post("/api/v1/orgs/invite",x),ee.success(`Invited ${x.email}`),C(!1),P.reset({email:"",role:"member"}),V(c)}catch(S){const G=S instanceof je?S.message:"Failed to invite member";ee.error(G)}finally{M(!1)}}async function m(x){const c=Ke();if(!c){ee.error("Select an organization first");return}try{U(x),await g.delete(`/api/v1/orgs/members/${x}`,{headers:{"X-Org-ID":c}}),d(S=>S.filter(G=>G.userId!==x)),ee.success("Member removed")}catch(S){const G=S instanceof je?S.message:"Failed to remove member";ee.error(G)}finally{U(null)}}return s?e.jsxs("div",{className:"space-y-4 p-6",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("h1",{className:"text-2xl font-bold",children:"Members"}),e.jsxs(h,{disabled:!0,children:[e.jsx(xt,{className:"mr-2 h-4 w-4"}),"Invite"]})]}),e.jsx(Ns,{}),e.jsx("div",{className:"grid gap-4 sm:grid-cols-2 lg:grid-cols-3",children:[...Array(6)].map((x,c)=>e.jsxs(Te,{children:[e.jsx(Pe,{children:e.jsx(Ge,{className:"h-5 w-40"})}),e.jsxs(Me,{className:"space-y-2",children:[e.jsx(Ge,{className:"h-4 w-56"}),e.jsx(Ge,{className:"h-4 w-40"})]}),e.jsx(Ss,{children:e.jsx(Ge,{className:"h-9 w-24"})})]},c))})]}):Ke()?e.jsxs("div",{className:"space-y-4 p-6",children:[e.jsxs("div",{className:"flex flex-col gap-3 md:flex-row md:items-center md:justify-between",children:[e.jsx("h1",{className:"text-2xl font-bold",children:"Members"}),e.jsxs(ie,{open:$,onOpenChange:C,children:[e.jsx(We,{asChild:!0,children:e.jsxs(h,{children:[e.jsx(xt,{className:"mr-2 h-4 w-4"}),"Invite"]})}),e.jsxs(oe,{className:"sm:max-w-[520px]",children:[e.jsxs(le,{children:[e.jsx(ce,{children:"Invite member"}),e.jsx(Ds,{children:"Send an invite to join this organization."})]}),e.jsx(ne,{...P,children:e.jsxs("form",{onSubmit:P.handleSubmit(H),className:"grid gap-4 py-2",children:[e.jsx(v,{control:P.control,name:"email",render:({field:x})=>e.jsxs(b,{children:[e.jsx(N,{children:"Email"}),e.jsx(E,{children:e.jsx(z,{type:"email",placeholder:"jane@example.com",...x})}),e.jsx(w,{})]})}),e.jsx(v,{control:P.control,name:"role",render:({field:x})=>e.jsxs(b,{children:[e.jsx(N,{children:"Role"}),e.jsxs(Ae,{onValueChange:x.onChange,defaultValue:x.value,children:[e.jsx(E,{children:e.jsx($e,{className:"w-[200px]",children:e.jsx(Ee,{placeholder:"Select role"})})}),e.jsxs(Le,{children:[e.jsx(ve,{value:"member",children:"Member"}),e.jsx(ve,{value:"admin",children:"Admin"})]})]}),e.jsx(w,{})]})}),e.jsxs(de,{className:"mt-2 flex-col-reverse gap-2 sm:flex-row sm:items-center sm:justify-between",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>C(!1),children:"Cancel"}),e.jsx(h,{type:"submit",disabled:!P.formState.isValid||O,children:O?"Sending…":"Send invite"})]})]})})]})]})]}),e.jsx(Ns,{}),r.length===0?e.jsx("div",{className:"text-muted-foreground text-sm",children:"No members yet."}):e.jsx("div",{className:"grid grid-cols-1 gap-4 pr-2 sm:grid-cols-2 lg:grid-cols-3",children:r.map(x=>{const c=u?.id&&x.userId===u.id;return e.jsxs(Te,{className:"flex flex-col",children:[e.jsx(Pe,{children:e.jsx(Be,{className:"text-base",children:x.name||x.email||x.userId})}),e.jsxs(Me,{className:"text-muted-foreground space-y-1 text-sm",children:[x.email&&e.jsxs("div",{children:["Email: ",x.email]}),e.jsxs("div",{children:["Role: ",x.role]}),x.joinedAt&&e.jsxs("div",{children:["Joined: ",new Date(x.joinedAt).toLocaleString()]})]}),e.jsxs(Ss,{className:"mt-auto w-full flex-col-reverse gap-2 sm:flex-row sm:items-center sm:justify-between",children:[e.jsx("div",{}),e.jsxs(et,{children:[e.jsx(st,{asChild:!0,children:e.jsxs(h,{variant:"destructive",disabled:c||R===x.userId,className:"ml-auto",children:[e.jsx(Ue,{className:"mr-2 h-5 w-5"}),R===x.userId?"Removing…":"Remove"]})}),e.jsxs(tt,{children:[e.jsxs(at,{children:[e.jsx(rt,{children:"Remove member?"}),e.jsxs(it,{children:["This will remove ",e.jsx("b",{children:x.name||x.email||x.userId})," from the organization."]})]}),e.jsxs(nt,{className:"sm:justify-between",children:[e.jsx(lt,{disabled:R===x.userId,children:"Cancel"}),e.jsx(ot,{asChild:!0,disabled:R===x.userId,children:e.jsx(h,{variant:"destructive",onClick:()=>m(x.userId),children:"Confirm remove"})})]})]})]})]})]},x.userId)})})]}):e.jsxs("div",{className:"space-y-4 p-6",children:[e.jsx("div",{className:"flex items-center justify-between",children:e.jsx("h1",{className:"text-2xl font-bold",children:"Members"})}),e.jsx(Ns,{}),e.jsx("p",{className:"text-muted-foreground text-sm",children:"No organization selected. Choose an organization to manage its members."})]})},yi=re({name:F().min(2).max(100),slug:F().min(2).max(50).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/,"Use lowercase letters, numbers, and hyphens.")}),Si=()=>{const[s,t]=o.useState([]),[r,d]=o.useState(!0),[u,f]=o.useState(!1),[$,C]=o.useState(null),[O,M]=o.useState(null),R=o.useRef(!1),U=te({resolver:ae(yi),mode:"onChange",defaultValues:{name:"",slug:""}}),T=U.watch("name");o.useEffect(()=>{R.current||U.setValue("slug",ht(T||""),{shouldValidate:!0})},[T,U]);const P=async()=>{d(!0);try{const m=await g.get("/api/v1/orgs");t(m),f(m.length===0)}catch(m){const x=m instanceof je?m.message:"Failed to load organizations";ee.error(x)}finally{d(!1)}};o.useEffect(()=>{C(Ke()),P();const m=S=>{S.key==="active_org_id"&&C(S.newValue)};window.addEventListener("storage",m);const x=S=>{const G=S.detail??null;C(G)};window.addEventListener(ns,x);const c=()=>void P();return window.addEventListener(ys,c),()=>{window.removeEventListener("storage",m),window.removeEventListener(ns,x),window.removeEventListener(ys,c)}},[]);async function j(m){try{const x=await g.post("/api/v1/orgs",m);t(c=>[x,...c]),ws(x.id),C(x.id),ut(),ee.success(`Created ${x.name}`),f(!1),U.reset({name:"",slug:""}),R.current=!1}catch(x){const c=x instanceof je?x.message:"Failed to create organization";ee.error(c)}}function V(m){ws(m.id),C(m.id),ee.success(`Switched to ${m.name}`)}async function H(m){try{M(m.id),await g.delete(`/api/v1/orgs/${m.id}`),t(x=>{const c=x.filter(S=>S.id!==m.id);if($===m.id){const S=c[0]?.id??null;ws(S),C(S)}return c}),ut(),ee.success(`Deleted ${m.name}`)}catch(x){const c=x instanceof je?x.message:"Failed to delete organization";ee.error(c)}finally{M(null)}}return r?e.jsxs("div",{className:"space-y-4 p-6",children:[e.jsx("div",{className:"flex flex-col gap-3 md:flex-row md:items-center md:justify-between",children:e.jsx("h1",{className:"mb-4 text-2xl font-bold",children:"Organizations"})}),e.jsx("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3",children:[...Array(6)].map((m,x)=>e.jsxs(Te,{children:[e.jsx(Pe,{children:e.jsx(Ge,{className:"h-5 w-40"})}),e.jsxs(Me,{children:[e.jsx(Ge,{className:"mb-2 h-4 w-24"}),e.jsx(Ge,{className:"h-4 w-48"})]}),e.jsx(Ss,{children:e.jsx(Ge,{className:"h-9 w-24"})})]},x))})]}):e.jsxs("div",{className:"space-y-4 p-6",children:[e.jsxs("div",{className:"flex flex-col gap-3 md:flex-row md:items-center md:justify-between",children:[e.jsx("h1",{className:"mb-4 text-2xl font-bold",children:"Organizations"}),e.jsx(h,{onClick:()=>f(!0),children:"New organization"})]}),e.jsx(Ns,{}),s.length===0?e.jsx("div",{className:"text-muted-foreground text-sm",children:"No organizations yet."}):e.jsx("div",{className:"grid grid-cols-1 gap-4 pr-2 sm:grid-cols-2 lg:grid-cols-3",children:s.map(m=>e.jsxs(Te,{className:"flex flex-col",children:[e.jsx(Pe,{children:e.jsx(Be,{className:"text-base",children:m.name})}),e.jsxs(Me,{className:"text-muted-foreground text-sm",children:[e.jsxs("div",{children:["Slug: ",m.slug]}),e.jsxs("div",{className:"mt-1",children:["ID: ",m.id]})]}),e.jsxs(Ss,{className:"mt-auto w-full flex-col-reverse gap-2 sm:flex-row sm:items-center sm:justify-between",children:[e.jsx(h,{onClick:()=>V(m),children:m.id===$?"Selected":"Select"}),e.jsxs(et,{children:[e.jsx(st,{asChild:!0,children:e.jsxs(h,{variant:"destructive",className:"ml-auto",disabled:O===m.id,children:[e.jsx(Ue,{className:"mr-2 h-5 w-5"}),O===m.id?"Deleting…":"Delete"]})}),e.jsxs(tt,{children:[e.jsxs(at,{children:[e.jsx(rt,{children:"Delete organization?"}),e.jsxs(it,{children:["This will permanently delete ",e.jsx("b",{children:m.name}),". This action cannot be undone."]})]}),e.jsxs(nt,{className:"sm:justify-between",children:[e.jsx(lt,{disabled:O===m.id,children:"Cancel"}),e.jsx(ot,{asChild:!0,disabled:O===m.id,children:e.jsx(h,{variant:"destructive",onClick:()=>H(m),children:"Confirm delete"})})]})]})]})]})]},m.id))}),e.jsx(ie,{open:u,onOpenChange:f,children:e.jsxs(oe,{className:"sm:max-w-[480px]",children:[e.jsxs(le,{children:[e.jsx(ce,{children:"Create organization"}),e.jsx(Ds,{children:"Set a name and a URL-friendly slug."})]}),e.jsx(ne,{...U,children:e.jsxs("form",{onSubmit:U.handleSubmit(j),className:"space-y-4",children:[e.jsx(v,{control:U.control,name:"name",render:({field:m})=>e.jsxs(b,{children:[e.jsx(N,{children:"Name"}),e.jsx(E,{children:e.jsx(z,{placeholder:"Acme Inc",autoFocus:!0,...m})}),e.jsx(jt,{children:"This is your organization’s display name."}),e.jsx(w,{})]})}),e.jsx(v,{control:U.control,name:"slug",render:({field:m})=>e.jsxs(b,{children:[e.jsx(N,{children:"Slug"}),e.jsx(E,{children:e.jsx(z,{placeholder:"acme-inc",...m,onChange:x=>{R.current=!0,m.onChange(x)},onBlur:x=>{const c=ht(x.target.value);U.setValue("slug",c,{shouldValidate:!0}),m.onBlur()}})}),e.jsx(jt,{children:"Lowercase, numbers and hyphens only."}),e.jsx(w,{})]})}),e.jsxs(de,{className:"flex-col-reverse gap-2 sm:flex-row sm:items-center sm:justify-between",children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>{U.reset(),f(!1),R.current=!1},children:"Cancel"}),e.jsx(h,{type:"submit",disabled:!U.formState.isValid||U.formState.isSubmitting,children:U.formState.isSubmitting?"Creating...":"Create"})]})]})})]})})]})},ms=s=>(s??0).toLocaleString(),Ci=s=>{if(!isFinite(s)||s<=0)return"–";if(s<60)return`${s.toFixed(0)}s`;if(s<3600)return`${Math.floor(s/60)}m ${Math.floor(s%60)}s`;const t=Math.floor(s/3600),r=Math.floor(s%3600/60);return`${t}h ${r}m`};function _i(){const[s,t]=o.useState(null),[r,d]=o.useState([]),[u,f]=o.useState(!1),[$,C]=o.useState(null),[O,M]=o.useState(!0),[R,U]=o.useState(5e3),T=o.useCallback(async()=>{f(!0),C(null);try{const[j,V]=await Promise.all([g.get("/api/v1/jobs/kpi"),g.get("/api/v1/jobs/queues")]);t(j),d(V)}catch(j){C(j.message||String(j))}finally{f(!1)}},[]);o.useEffect(()=>{T()},[T]),o.useEffect(()=>{if(!O)return;const j=setInterval(T,R);return()=>clearInterval(j)},[O,R,T]);const P=o.useMemo(()=>({queues:r.length,running:r.reduce((j,V)=>j+V.Running,0),due:r.reduce((j,V)=>j+V.QueuedDue,0),future:r.reduce((j,V)=>j+V.QueuedFuture,0)}),[r]);return e.jsxs("div",{className:"p-6 space-y-6",children:[e.jsxs("header",{className:"flex items-center justify-between gap-3",children:[e.jsx("h1",{className:"text-2xl font-semibold tracking-tight",children:"Jobs Dashboard"}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("label",{className:"flex items-center gap-2 text-sm",children:[e.jsx("input",{type:"checkbox",className:"h-4 w-4",checked:O,onChange:j=>M(j.target.checked)}),"Auto refresh"]}),e.jsxs("select",{className:"border rounded px-2 py-1 text-sm",value:R,onChange:j=>U(parseInt(j.target.value)),children:[e.jsx("option",{value:3e3,children:"3s"}),e.jsx("option",{value:5e3,children:"5s"}),e.jsx("option",{value:1e4,children:"10s"}),e.jsx("option",{value:3e4,children:"30s"})]}),e.jsx("button",{className:"px-3 py-1.5 rounded bg-slate-900 text-white text-sm hover:opacity-90",onClick:T,disabled:u,children:u?"Refreshing…":"Refresh"})]})]}),$&&e.jsx("div",{className:"rounded border border-red-300 bg-red-50 text-red-800 p-3 text-sm",children:$}),e.jsxs("section",{className:"grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-6",children:[e.jsx(xs,{label:"Running",value:ms(s?.RunningNow)}),e.jsx(xs,{label:"Due now",value:ms(s?.DueNow)}),e.jsx(xs,{label:"Scheduled",value:ms(s?.ScheduledFuture)}),e.jsx(xs,{label:"Succeeded (24h)",value:ms(s?.Succeeded24h)}),e.jsx(xs,{label:"Failed (24h)",value:ms(s?.Failed24h)}),e.jsx(xs,{label:"Retryable",value:ms(s?.Retryable)})]}),e.jsxs("section",{className:"space-y-2",children:[e.jsx("div",{className:"flex items-center justify-between",children:e.jsxs("h2",{className:"text-lg font-medium",children:["Queues ",e.jsxs("span",{className:"text-slate-500 text-sm",children:["(",P.queues,")"]})]})}),e.jsx("div",{className:"overflow-x-auto rounded border",children:e.jsxs(be,{className:"min-w-full text-sm",children:[e.jsx(Ne,{children:e.jsxs(q,{children:[e.jsx(A,{children:"Queue"}),e.jsx(A,{className:"text-right",children:"Running"}),e.jsx(A,{className:"text-right",children:"Due"}),e.jsx(A,{className:"text-right",children:"Future"}),e.jsx(A,{className:"text-right",children:"Success 24h"}),e.jsx(A,{className:"text-right",children:"Failed 24h"}),e.jsx(A,{className:"text-right",children:"Avg Duration"})]})}),e.jsx(we,{children:r.map(j=>e.jsxs(q,{className:"border-t",children:[e.jsx(p,{children:j.QueueName}),e.jsx(p,{className:"text-right",children:j.Running}),e.jsx(p,{className:"text-right",children:j.QueuedDue}),e.jsx(p,{className:"text-right",children:j.QueuedFuture}),e.jsx(p,{className:"text-right",children:j.Success24h}),e.jsx(p,{className:"text-right",children:j.Failed24h}),e.jsx(p,{className:"text-right",children:Ci(j.AvgDurationSecs)})]},j.QueueName))})]})})]})]})}function xs({label:s,value:t}){return e.jsxs(Te,{children:[e.jsx(Pe,{children:e.jsx(Be,{children:s})}),e.jsx(Me,{children:e.jsx("div",{className:"mt-1 text-2xl font-semibold",children:t})})]})}function ki(){return e.jsxs(kn,{children:[e.jsx(se,{path:"/",element:e.jsx(Qs,{to:"/auth/login",replace:!0})}),e.jsxs(se,{path:"/auth",children:[e.jsx(se,{path:"login",element:e.jsx($r,{})}),e.jsx(se,{path:"register",element:e.jsx(Ir,{})}),e.jsx(se,{path:"forgot",element:e.jsx(Ar,{})}),e.jsx(se,{path:"reset",element:e.jsx(Pr,{})}),e.jsx(se,{path:"verify",element:e.jsx(Mr,{})})]}),e.jsx(se,{element:e.jsx(jr,{}),children:e.jsxs(se,{element:e.jsx(ur,{}),children:[e.jsx(se,{element:e.jsx(fr,{}),children:e.jsx(se,{path:"/admin",children:e.jsx(se,{path:"users",element:e.jsx(_r,{})})})}),e.jsxs(se,{path:"/core",children:[e.jsx(se,{path:"annotations",element:e.jsx(Vr,{})}),e.jsx(se,{path:"clusters",element:e.jsx(qr,{})}),e.jsx(se,{path:"labels",element:e.jsx(Yr,{})}),e.jsx(se,{path:"nodepools",element:e.jsx(ri,{})}),e.jsx(se,{path:"servers",element:e.jsx(li,{})}),e.jsx(se,{path:"taints",element:e.jsx(hi,{})})]}),e.jsx(se,{path:"/security",children:e.jsx(se,{path:"ssh",element:e.jsx(vi,{})})}),e.jsxs(se,{path:"/settings",children:[e.jsx(se,{path:"jobs",element:e.jsx(_i,{})}),e.jsx(se,{path:"orgs",element:e.jsx(Si,{})}),e.jsx(se,{path:"members",element:e.jsx(wi,{})}),e.jsx(se,{path:"me",element:e.jsx(Lr,{})})]}),e.jsx(se,{path:"/403",element:e.jsx(Nt,{})}),e.jsx(se,{path:"*",element:e.jsx(wt,{})})]})}),e.jsx(se,{path:"/403",element:e.jsx(Nt,{})}),e.jsx(se,{path:"*",element:e.jsx(wt,{})})]})}const Ai=({...s})=>{const{theme:t="system"}=yt();return e.jsx(xa,{theme:t,className:"toaster group",style:{"--normal-bg":"var(--popover)","--normal-text":"var(--popover-foreground)","--normal-border":"var(--border)"},...s})};function Ei({children:s,defaultTheme:t="system",storageKey:r="vite-ui-theme"}){return e.jsx(ha,{attribute:"class",defaultTheme:t,enableSystem:!0,storageKey:r,disableTransitionOnChange:!0,children:s})}ua.createRoot(document.getElementById("root")).render(e.jsx(o.StrictMode,{children:e.jsx(An,{children:e.jsxs(Ei,{defaultTheme:"system",storageKey:"dragon-theme",children:[e.jsx(ki,{}),e.jsx(Ai,{richColors:!0,position:"top-right"})]})})})); diff --git a/internal/ui/dist/assets/radix-DRmH1vcw.js b/internal/ui/dist/assets/radix-DRmH1vcw.js deleted file mode 100644 index 2bdc690..0000000 --- a/internal/ui/dist/assets/radix-DRmH1vcw.js +++ /dev/null @@ -1,11 +0,0 @@ -import{r as s,j as l,a as wt,b as an,c as Or,h as yt,d as Et,u as jr,o as kr,s as Lr,f as Fr,e as $r,g as Br,i as Ur,l as Hr,k as Gr,R as ge}from"./vendor-DvippHRz.js";function Xt(e,n){if(typeof e=="function")return e(n);e!=null&&(e.current=n)}function Ke(...e){return n=>{let t=!1;const o=e.map(r=>{const a=Xt(r,n);return!t&&typeof a=="function"&&(t=!0),a});if(t)return()=>{for(let r=0;r{const{children:a,...i}=o,c=s.Children.toArray(a),u=c.find(Wr);if(u){const d=u.props.children,f=c.map(p=>p===u?s.Children.count(d)>1?s.Children.only(null):s.isValidElement(d)?d.props.children:null:p);return l.jsx(n,{...i,ref:r,children:s.isValidElement(d)?s.cloneElement(d,void 0,f):null})}return l.jsx(n,{...i,ref:r,children:a})});return t.displayName=`${e}.Slot`,t}var Ic=xe("Slot");function Kr(e){const n=s.forwardRef((t,o)=>{const{children:r,...a}=t;if(s.isValidElement(r)){const i=zr(r),c=Vr(a,r.props);return r.type!==s.Fragment&&(c.ref=o?Ke(o,i):i),s.cloneElement(r,c)}return s.Children.count(r)>1?s.Children.only(null):null});return n.displayName=`${e}.SlotClone`,n}var cn=Symbol("radix.slottable");function ln(e){const n=({children:t})=>l.jsx(l.Fragment,{children:t});return n.displayName=`${e}.Slottable`,n.__radixId=cn,n}function Wr(e){return s.isValidElement(e)&&typeof e.type=="function"&&"__radixId"in e.type&&e.type.__radixId===cn}function Vr(e,n){const t={...n};for(const o in n){const r=e[o],a=n[o];/^on[A-Z]/.test(o)?r&&a?t[o]=(...c)=>{const u=a(...c);return r(...c),u}:r&&(t[o]=r):o==="style"?t[o]={...r,...a}:o==="className"&&(t[o]=[r,a].filter(Boolean).join(" "))}return{...e,...t}}function zr(e){let n=Object.getOwnPropertyDescriptor(e.props,"ref")?.get,t=n&&"isReactWarning"in n&&n.isReactWarning;return t?e.ref:(n=Object.getOwnPropertyDescriptor(e,"ref")?.get,t=n&&"isReactWarning"in n&&n.isReactWarning,t?e.props.ref:e.props.ref||e.ref)}var Yr=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","select","span","svg","ul"],_=Yr.reduce((e,n)=>{const t=xe(`Primitive.${n}`),o=s.forwardRef((r,a)=>{const{asChild:i,...c}=r,u=i?t:n;return typeof window<"u"&&(window[Symbol.for("radix-ui")]=!0),l.jsx(u,{...c,ref:a})});return o.displayName=`Primitive.${n}`,{...e,[n]:o}},{});function un(e,n){e&&wt.flushSync(()=>e.dispatchEvent(n))}var Xr="Separator",qt="horizontal",qr=["horizontal","vertical"],dn=s.forwardRef((e,n)=>{const{decorative:t,orientation:o=qt,...r}=e,a=Zr(o)?o:qt,c=t?{role:"none"}:{"aria-orientation":a==="vertical"?a:void 0,role:"separator"};return l.jsx(_.div,{"data-orientation":a,...c,...r,ref:n})});dn.displayName=Xr;function Zr(e){return qr.includes(e)}var Mc=dn;function E(e,n,{checkForDefaultPrevented:t=!0}={}){return function(r){if(e?.(r),t===!1||!r.defaultPrevented)return n?.(r)}}function Jr(e,n){const t=s.createContext(n),o=a=>{const{children:i,...c}=a,u=s.useMemo(()=>c,Object.values(c));return l.jsx(t.Provider,{value:u,children:i})};o.displayName=e+"Provider";function r(a){const i=s.useContext(t);if(i)return i;if(n!==void 0)return n;throw new Error(`\`${a}\` must be used within \`${e}\``)}return[o,r]}function se(e,n=[]){let t=[];function o(a,i){const c=s.createContext(i),u=t.length;t=[...t,i];const d=p=>{const{scope:v,children:x,...C}=p,m=v?.[e]?.[u]||c,h=s.useMemo(()=>C,Object.values(C));return l.jsx(m.Provider,{value:h,children:x})};d.displayName=a+"Provider";function f(p,v){const x=v?.[e]?.[u]||c,C=s.useContext(x);if(C)return C;if(i!==void 0)return i;throw new Error(`\`${p}\` must be used within \`${a}\``)}return[d,f]}const r=()=>{const a=t.map(i=>s.createContext(i));return function(c){const u=c?.[e]||a;return s.useMemo(()=>({[`__scope${e}`]:{...c,[e]:u}}),[c,u])}};return r.scopeName=e,[o,Qr(r,...n)]}function Qr(...e){const n=e[0];if(e.length===1)return n;const t=()=>{const o=e.map(r=>({useScope:r(),scopeName:r.scopeName}));return function(a){const i=o.reduce((c,{useScope:u,scopeName:d})=>{const p=u(a)[`__scope${d}`];return{...c,...p}},{});return s.useMemo(()=>({[`__scope${n.scopeName}`]:i}),[i])}};return t.scopeName=n.scopeName,t}var K=globalThis?.document?s.useLayoutEffect:()=>{},es=an[" useId ".trim().toString()]||(()=>{}),ts=0;function re(e){const[n,t]=s.useState(es());return K(()=>{t(o=>o??String(ts++))},[e]),e||(n?`radix-${n}`:"")}var ns=an[" useInsertionEffect ".trim().toString()]||K;function pe({prop:e,defaultProp:n,onChange:t=()=>{},caller:o}){const[r,a,i]=os({defaultProp:n,onChange:t}),c=e!==void 0,u=c?e:r;{const f=s.useRef(e!==void 0);s.useEffect(()=>{const p=f.current;p!==c&&console.warn(`${o} is changing from ${p?"controlled":"uncontrolled"} to ${c?"controlled":"uncontrolled"}. Components should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled value for the lifetime of the component.`),f.current=c},[c,o])}const d=s.useCallback(f=>{if(c){const p=rs(f)?f(e):f;p!==e&&i.current?.(p)}else a(f)},[c,e,a,i]);return[u,d]}function os({defaultProp:e,onChange:n}){const[t,o]=s.useState(e),r=s.useRef(t),a=s.useRef(n);return ns(()=>{a.current=n},[n]),s.useEffect(()=>{r.current!==t&&(a.current?.(t),r.current=t)},[t,r]),[t,o,a]}function rs(e){return typeof e=="function"}function ie(e){const n=s.useRef(e);return s.useEffect(()=>{n.current=e}),s.useMemo(()=>(...t)=>n.current?.(...t),[])}function ss(e,n=globalThis?.document){const t=ie(e);s.useEffect(()=>{const o=r=>{r.key==="Escape"&&t(r)};return n.addEventListener("keydown",o,{capture:!0}),()=>n.removeEventListener("keydown",o,{capture:!0})},[t,n])}var as="DismissableLayer",ut="dismissableLayer.update",is="dismissableLayer.pointerDownOutside",cs="dismissableLayer.focusOutside",Zt,fn=s.createContext({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set}),De=s.forwardRef((e,n)=>{const{disableOutsidePointerEvents:t=!1,onEscapeKeyDown:o,onPointerDownOutside:r,onFocusOutside:a,onInteractOutside:i,onDismiss:c,...u}=e,d=s.useContext(fn),[f,p]=s.useState(null),v=f?.ownerDocument??globalThis?.document,[,x]=s.useState({}),C=M(n,P=>p(P)),m=Array.from(d.layers),[h]=[...d.layersWithOutsidePointerEventsDisabled].slice(-1),w=m.indexOf(h),g=f?m.indexOf(f):-1,y=d.layersWithOutsidePointerEventsDisabled.size>0,S=g>=w,T=ds(P=>{const I=P.target,F=[...d.branches].some(L=>L.contains(I));!S||F||(r?.(P),i?.(P),P.defaultPrevented||c?.())},v),A=fs(P=>{const I=P.target;[...d.branches].some(L=>L.contains(I))||(a?.(P),i?.(P),P.defaultPrevented||c?.())},v);return ss(P=>{g===d.layers.size-1&&(o?.(P),!P.defaultPrevented&&c&&(P.preventDefault(),c()))},v),s.useEffect(()=>{if(f)return t&&(d.layersWithOutsidePointerEventsDisabled.size===0&&(Zt=v.body.style.pointerEvents,v.body.style.pointerEvents="none"),d.layersWithOutsidePointerEventsDisabled.add(f)),d.layers.add(f),Jt(),()=>{t&&d.layersWithOutsidePointerEventsDisabled.size===1&&(v.body.style.pointerEvents=Zt)}},[f,v,t,d]),s.useEffect(()=>()=>{f&&(d.layers.delete(f),d.layersWithOutsidePointerEventsDisabled.delete(f),Jt())},[f,d]),s.useEffect(()=>{const P=()=>x({});return document.addEventListener(ut,P),()=>document.removeEventListener(ut,P)},[]),l.jsx(_.div,{...u,ref:C,style:{pointerEvents:y?S?"auto":"none":void 0,...e.style},onFocusCapture:E(e.onFocusCapture,A.onFocusCapture),onBlurCapture:E(e.onBlurCapture,A.onBlurCapture),onPointerDownCapture:E(e.onPointerDownCapture,T.onPointerDownCapture)})});De.displayName=as;var ls="DismissableLayerBranch",us=s.forwardRef((e,n)=>{const t=s.useContext(fn),o=s.useRef(null),r=M(n,o);return s.useEffect(()=>{const a=o.current;if(a)return t.branches.add(a),()=>{t.branches.delete(a)}},[t.branches]),l.jsx(_.div,{...e,ref:r})});us.displayName=ls;function ds(e,n=globalThis?.document){const t=ie(e),o=s.useRef(!1),r=s.useRef(()=>{});return s.useEffect(()=>{const a=c=>{if(c.target&&!o.current){let u=function(){pn(is,t,d,{discrete:!0})};const d={originalEvent:c};c.pointerType==="touch"?(n.removeEventListener("click",r.current),r.current=u,n.addEventListener("click",r.current,{once:!0})):u()}else n.removeEventListener("click",r.current);o.current=!1},i=window.setTimeout(()=>{n.addEventListener("pointerdown",a)},0);return()=>{window.clearTimeout(i),n.removeEventListener("pointerdown",a),n.removeEventListener("click",r.current)}},[n,t]),{onPointerDownCapture:()=>o.current=!0}}function fs(e,n=globalThis?.document){const t=ie(e),o=s.useRef(!1);return s.useEffect(()=>{const r=a=>{a.target&&!o.current&&pn(cs,t,{originalEvent:a},{discrete:!1})};return n.addEventListener("focusin",r),()=>n.removeEventListener("focusin",r)},[n,t]),{onFocusCapture:()=>o.current=!0,onBlurCapture:()=>o.current=!1}}function Jt(){const e=new CustomEvent(ut);document.dispatchEvent(e)}function pn(e,n,t,{discrete:o}){const r=t.originalEvent.target,a=new CustomEvent(e,{bubbles:!1,cancelable:!0,detail:t});n&&r.addEventListener(e,n,{once:!0}),o?un(r,a):r.dispatchEvent(a)}var at="focusScope.autoFocusOnMount",it="focusScope.autoFocusOnUnmount",Qt={bubbles:!1,cancelable:!0},ps="FocusScope",We=s.forwardRef((e,n)=>{const{loop:t=!1,trapped:o=!1,onMountAutoFocus:r,onUnmountAutoFocus:a,...i}=e,[c,u]=s.useState(null),d=ie(r),f=ie(a),p=s.useRef(null),v=M(n,m=>u(m)),x=s.useRef({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;s.useEffect(()=>{if(o){let m=function(y){if(x.paused||!c)return;const S=y.target;c.contains(S)?p.current=S:de(p.current,{select:!0})},h=function(y){if(x.paused||!c)return;const S=y.relatedTarget;S!==null&&(c.contains(S)||de(p.current,{select:!0}))},w=function(y){if(document.activeElement===document.body)for(const T of y)T.removedNodes.length>0&&de(c)};document.addEventListener("focusin",m),document.addEventListener("focusout",h);const g=new MutationObserver(w);return c&&g.observe(c,{childList:!0,subtree:!0}),()=>{document.removeEventListener("focusin",m),document.removeEventListener("focusout",h),g.disconnect()}}},[o,c,x.paused]),s.useEffect(()=>{if(c){tn.add(x);const m=document.activeElement;if(!c.contains(m)){const w=new CustomEvent(at,Qt);c.addEventListener(at,d),c.dispatchEvent(w),w.defaultPrevented||(ms(Cs(mn(c)),{select:!0}),document.activeElement===m&&de(c))}return()=>{c.removeEventListener(at,d),setTimeout(()=>{const w=new CustomEvent(it,Qt);c.addEventListener(it,f),c.dispatchEvent(w),w.defaultPrevented||de(m??document.body,{select:!0}),c.removeEventListener(it,f),tn.remove(x)},0)}}},[c,d,f,x]);const C=s.useCallback(m=>{if(!t&&!o||x.paused)return;const h=m.key==="Tab"&&!m.altKey&&!m.ctrlKey&&!m.metaKey,w=document.activeElement;if(h&&w){const g=m.currentTarget,[y,S]=vs(g);y&&S?!m.shiftKey&&w===S?(m.preventDefault(),t&&de(y,{select:!0})):m.shiftKey&&w===y&&(m.preventDefault(),t&&de(S,{select:!0})):w===g&&m.preventDefault()}},[t,o,x.paused]);return l.jsx(_.div,{tabIndex:-1,...i,ref:v,onKeyDown:C})});We.displayName=ps;function ms(e,{select:n=!1}={}){const t=document.activeElement;for(const o of e)if(de(o,{select:n}),document.activeElement!==t)return}function vs(e){const n=mn(e),t=en(n,e),o=en(n.reverse(),e);return[t,o]}function mn(e){const n=[],t=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:o=>{const r=o.tagName==="INPUT"&&o.type==="hidden";return o.disabled||o.hidden||r?NodeFilter.FILTER_SKIP:o.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;t.nextNode();)n.push(t.currentNode);return n}function en(e,n){for(const t of e)if(!hs(t,{upTo:n}))return t}function hs(e,{upTo:n}){if(getComputedStyle(e).visibility==="hidden")return!0;for(;e;){if(n!==void 0&&e===n)return!1;if(getComputedStyle(e).display==="none")return!0;e=e.parentElement}return!1}function gs(e){return e instanceof HTMLInputElement&&"select"in e}function de(e,{select:n=!1}={}){if(e&&e.focus){const t=document.activeElement;e.focus({preventScroll:!0}),e!==t&&gs(e)&&n&&e.select()}}var tn=xs();function xs(){let e=[];return{add(n){const t=e[0];n!==t&&t?.pause(),e=nn(e,n),e.unshift(n)},remove(n){e=nn(e,n),e[0]?.resume()}}}function nn(e,n){const t=[...e],o=t.indexOf(n);return o!==-1&&t.splice(o,1),t}function Cs(e){return e.filter(n=>n.tagName!=="A")}var ws="Portal",Oe=s.forwardRef((e,n)=>{const{container:t,...o}=e,[r,a]=s.useState(!1);K(()=>a(!0),[]);const i=t||r&&globalThis?.document?.body;return i?Or.createPortal(l.jsx(_.div,{...o,ref:n}),i):null});Oe.displayName=ws;function ys(e,n){return s.useReducer((t,o)=>n[t][o]??t,e)}var te=e=>{const{present:n,children:t}=e,o=Es(n),r=typeof t=="function"?t({present:o.isPresent}):s.Children.only(t),a=M(o.ref,bs(r));return typeof t=="function"||o.isPresent?s.cloneElement(r,{ref:a}):null};te.displayName="Presence";function Es(e){const[n,t]=s.useState(),o=s.useRef(null),r=s.useRef(e),a=s.useRef("none"),i=e?"mounted":"unmounted",[c,u]=ys(i,{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return s.useEffect(()=>{const d=$e(o.current);a.current=c==="mounted"?d:"none"},[c]),K(()=>{const d=o.current,f=r.current;if(f!==e){const v=a.current,x=$e(d);e?u("MOUNT"):x==="none"||d?.display==="none"?u("UNMOUNT"):u(f&&v!==x?"ANIMATION_OUT":"UNMOUNT"),r.current=e}},[e,u]),K(()=>{if(n){let d;const f=n.ownerDocument.defaultView??window,p=x=>{const m=$e(o.current).includes(CSS.escape(x.animationName));if(x.target===n&&m&&(u("ANIMATION_END"),!r.current)){const h=n.style.animationFillMode;n.style.animationFillMode="forwards",d=f.setTimeout(()=>{n.style.animationFillMode==="forwards"&&(n.style.animationFillMode=h)})}},v=x=>{x.target===n&&(a.current=$e(o.current))};return n.addEventListener("animationstart",v),n.addEventListener("animationcancel",p),n.addEventListener("animationend",p),()=>{f.clearTimeout(d),n.removeEventListener("animationstart",v),n.removeEventListener("animationcancel",p),n.removeEventListener("animationend",p)}}else u("ANIMATION_END")},[n,u]),{isPresent:["mounted","unmountSuspended"].includes(c),ref:s.useCallback(d=>{o.current=d?getComputedStyle(d):null,t(d)},[])}}function $e(e){return e?.animationName||"none"}function bs(e){let n=Object.getOwnPropertyDescriptor(e.props,"ref")?.get,t=n&&"isReactWarning"in n&&n.isReactWarning;return t?e.ref:(n=Object.getOwnPropertyDescriptor(e,"ref")?.get,t=n&&"isReactWarning"in n&&n.isReactWarning,t?e.props.ref:e.props.ref||e.ref)}var ct=0;function bt(){s.useEffect(()=>{const e=document.querySelectorAll("[data-radix-focus-guard]");return document.body.insertAdjacentElement("afterbegin",e[0]??on()),document.body.insertAdjacentElement("beforeend",e[1]??on()),ct++,()=>{ct===1&&document.querySelectorAll("[data-radix-focus-guard]").forEach(n=>n.remove()),ct--}},[])}function on(){const e=document.createElement("span");return e.setAttribute("data-radix-focus-guard",""),e.tabIndex=0,e.style.outline="none",e.style.opacity="0",e.style.position="fixed",e.style.pointerEvents="none",e}var Ve="Dialog",[vn,hn]=se(Ve),[Ss,ae]=vn(Ve),gn=e=>{const{__scopeDialog:n,children:t,open:o,defaultOpen:r,onOpenChange:a,modal:i=!0}=e,c=s.useRef(null),u=s.useRef(null),[d,f]=pe({prop:o,defaultProp:r??!1,onChange:a,caller:Ve});return l.jsx(Ss,{scope:n,triggerRef:c,contentRef:u,contentId:re(),titleId:re(),descriptionId:re(),open:d,onOpenChange:f,onOpenToggle:s.useCallback(()=>f(p=>!p),[f]),modal:i,children:t})};gn.displayName=Ve;var xn="DialogTrigger",Cn=s.forwardRef((e,n)=>{const{__scopeDialog:t,...o}=e,r=ae(xn,t),a=M(n,r.triggerRef);return l.jsx(_.button,{type:"button","aria-haspopup":"dialog","aria-expanded":r.open,"aria-controls":r.contentId,"data-state":Pt(r.open),...o,ref:a,onClick:E(e.onClick,r.onOpenToggle)})});Cn.displayName=xn;var St="DialogPortal",[Rs,wn]=vn(St,{forceMount:void 0}),yn=e=>{const{__scopeDialog:n,forceMount:t,children:o,container:r}=e,a=ae(St,n);return l.jsx(Rs,{scope:n,forceMount:t,children:s.Children.map(o,i=>l.jsx(te,{present:t||a.open,children:l.jsx(Oe,{asChild:!0,container:r,children:i})}))})};yn.displayName=St;var Be="DialogOverlay",En=s.forwardRef((e,n)=>{const t=wn(Be,e.__scopeDialog),{forceMount:o=t.forceMount,...r}=e,a=ae(Be,e.__scopeDialog);return a.modal?l.jsx(te,{present:o||a.open,children:l.jsx(_s,{...r,ref:n})}):null});En.displayName=Be;var Ps=xe("DialogOverlay.RemoveScroll"),_s=s.forwardRef((e,n)=>{const{__scopeDialog:t,...o}=e,r=ae(Be,t);return l.jsx(Et,{as:Ps,allowPinchZoom:!0,shards:[r.contentRef],children:l.jsx(_.div,{"data-state":Pt(r.open),...o,ref:n,style:{pointerEvents:"auto",...o.style}})})}),Ce="DialogContent",bn=s.forwardRef((e,n)=>{const t=wn(Ce,e.__scopeDialog),{forceMount:o=t.forceMount,...r}=e,a=ae(Ce,e.__scopeDialog);return l.jsx(te,{present:o||a.open,children:a.modal?l.jsx(Ts,{...r,ref:n}):l.jsx(Is,{...r,ref:n})})});bn.displayName=Ce;var Ts=s.forwardRef((e,n)=>{const t=ae(Ce,e.__scopeDialog),o=s.useRef(null),r=M(n,t.contentRef,o);return s.useEffect(()=>{const a=o.current;if(a)return yt(a)},[]),l.jsx(Sn,{...e,ref:r,trapFocus:t.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:E(e.onCloseAutoFocus,a=>{a.preventDefault(),t.triggerRef.current?.focus()}),onPointerDownOutside:E(e.onPointerDownOutside,a=>{const i=a.detail.originalEvent,c=i.button===0&&i.ctrlKey===!0;(i.button===2||c)&&a.preventDefault()}),onFocusOutside:E(e.onFocusOutside,a=>a.preventDefault())})}),Is=s.forwardRef((e,n)=>{const t=ae(Ce,e.__scopeDialog),o=s.useRef(!1),r=s.useRef(!1);return l.jsx(Sn,{...e,ref:n,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:a=>{e.onCloseAutoFocus?.(a),a.defaultPrevented||(o.current||t.triggerRef.current?.focus(),a.preventDefault()),o.current=!1,r.current=!1},onInteractOutside:a=>{e.onInteractOutside?.(a),a.defaultPrevented||(o.current=!0,a.detail.originalEvent.type==="pointerdown"&&(r.current=!0));const i=a.target;t.triggerRef.current?.contains(i)&&a.preventDefault(),a.detail.originalEvent.type==="focusin"&&r.current&&a.preventDefault()}})}),Sn=s.forwardRef((e,n)=>{const{__scopeDialog:t,trapFocus:o,onOpenAutoFocus:r,onCloseAutoFocus:a,...i}=e,c=ae(Ce,t),u=s.useRef(null),d=M(n,u);return bt(),l.jsxs(l.Fragment,{children:[l.jsx(We,{asChild:!0,loop:!0,trapped:o,onMountAutoFocus:r,onUnmountAutoFocus:a,children:l.jsx(De,{role:"dialog",id:c.contentId,"aria-describedby":c.descriptionId,"aria-labelledby":c.titleId,"data-state":Pt(c.open),...i,ref:d,onDismiss:()=>c.onOpenChange(!1)})}),l.jsxs(l.Fragment,{children:[l.jsx(As,{titleId:c.titleId}),l.jsx(Ds,{contentRef:u,descriptionId:c.descriptionId})]})]})}),Rt="DialogTitle",Rn=s.forwardRef((e,n)=>{const{__scopeDialog:t,...o}=e,r=ae(Rt,t);return l.jsx(_.h2,{id:r.titleId,...o,ref:n})});Rn.displayName=Rt;var Pn="DialogDescription",_n=s.forwardRef((e,n)=>{const{__scopeDialog:t,...o}=e,r=ae(Pn,t);return l.jsx(_.p,{id:r.descriptionId,...o,ref:n})});_n.displayName=Pn;var Tn="DialogClose",In=s.forwardRef((e,n)=>{const{__scopeDialog:t,...o}=e,r=ae(Tn,t);return l.jsx(_.button,{type:"button",...o,ref:n,onClick:E(e.onClick,()=>r.onOpenChange(!1))})});In.displayName=Tn;function Pt(e){return e?"open":"closed"}var Mn="DialogTitleWarning",[Ms,An]=Jr(Mn,{contentName:Ce,titleName:Rt,docsSlug:"dialog"}),As=({titleId:e})=>{const n=An(Mn),t=`\`${n.contentName}\` requires a \`${n.titleName}\` for the component to be accessible for screen reader users. - -If you want to hide the \`${n.titleName}\`, you can wrap it with our VisuallyHidden component. - -For more information, see https://radix-ui.com/primitives/docs/components/${n.docsSlug}`;return s.useEffect(()=>{e&&(document.getElementById(e)||console.error(t))},[t,e]),null},Ns="DialogDescriptionWarning",Ds=({contentRef:e,descriptionId:n})=>{const o=`Warning: Missing \`Description\` or \`aria-describedby={undefined}\` for {${An(Ns).contentName}}.`;return s.useEffect(()=>{const r=e.current?.getAttribute("aria-describedby");n&&r&&(document.getElementById(n)||console.warn(o))},[o,e,n]),null},Os=gn,js=Cn,ks=yn,Ls=En,Fs=bn,$s=Rn,Bs=_n,Nn=In,Us="Arrow",Dn=s.forwardRef((e,n)=>{const{children:t,width:o=10,height:r=5,...a}=e;return l.jsx(_.svg,{...a,ref:n,width:o,height:r,viewBox:"0 0 30 10",preserveAspectRatio:"none",children:e.asChild?t:l.jsx("polygon",{points:"0,0 30,0 15,10"})})});Dn.displayName=Us;var Hs=Dn;function On(e){const[n,t]=s.useState(void 0);return K(()=>{if(e){t({width:e.offsetWidth,height:e.offsetHeight});const o=new ResizeObserver(r=>{if(!Array.isArray(r)||!r.length)return;const a=r[0];let i,c;if("borderBoxSize"in a){const u=a.borderBoxSize,d=Array.isArray(u)?u[0]:u;i=d.inlineSize,c=d.blockSize}else i=e.offsetWidth,c=e.offsetHeight;t({width:i,height:c})});return o.observe(e,{box:"border-box"}),()=>o.unobserve(e)}else t(void 0)},[e]),n}var _t="Popper",[jn,Pe]=se(_t),[Gs,kn]=jn(_t),Ln=e=>{const{__scopePopper:n,children:t}=e,[o,r]=s.useState(null);return l.jsx(Gs,{scope:n,anchor:o,onAnchorChange:r,children:t})};Ln.displayName=_t;var Fn="PopperAnchor",$n=s.forwardRef((e,n)=>{const{__scopePopper:t,virtualRef:o,...r}=e,a=kn(Fn,t),i=s.useRef(null),c=M(n,i),u=s.useRef(null);return s.useEffect(()=>{const d=u.current;u.current=o?.current||i.current,d!==u.current&&a.onAnchorChange(u.current)}),o?null:l.jsx(_.div,{...r,ref:c})});$n.displayName=Fn;var Tt="PopperContent",[Ks,Ws]=jn(Tt),Bn=s.forwardRef((e,n)=>{const{__scopePopper:t,side:o="bottom",sideOffset:r=0,align:a="center",alignOffset:i=0,arrowPadding:c=0,avoidCollisions:u=!0,collisionBoundary:d=[],collisionPadding:f=0,sticky:p="partial",hideWhenDetached:v=!1,updatePositionStrategy:x="optimized",onPlaced:C,...m}=e,h=kn(Tt,t),[w,g]=s.useState(null),y=M(n,b=>g(b)),[S,T]=s.useState(null),A=On(S),P=A?.width??0,I=A?.height??0,F=o+(a!=="center"?"-"+a:""),L=typeof f=="number"?f:{top:0,right:0,bottom:0,left:0,...f},B=Array.isArray(d)?d:[d],H=B.length>0,U={padding:L,boundary:B.filter(zs),altBoundary:H},{refs:W,floatingStyles:V,placement:N,isPositioned:G,middlewareData:k}=jr({strategy:"fixed",placement:F,whileElementsMounted:(...b)=>Gr(...b,{animationFrame:x==="always"}),elements:{reference:h.anchor},middleware:[kr({mainAxis:r+I,alignmentAxis:i}),u&&Lr({mainAxis:!0,crossAxis:!1,limiter:p==="partial"?Hr():void 0,...U}),u&&Fr({...U}),$r({...U,apply:({elements:b,rects:D,availableWidth:Y,availableHeight:O})=>{const{width:j,height:$}=D.reference,Q=b.floating.style;Q.setProperty("--radix-popper-available-width",`${Y}px`),Q.setProperty("--radix-popper-available-height",`${O}px`),Q.setProperty("--radix-popper-anchor-width",`${j}px`),Q.setProperty("--radix-popper-anchor-height",`${$}px`)}}),S&&Br({element:S,padding:c}),Ys({arrowWidth:P,arrowHeight:I}),v&&Ur({strategy:"referenceHidden",...U})]}),[R,Z]=Gn(N),z=ie(C);K(()=>{G&&z?.()},[G,z]);const ne=k.arrow?.x,ce=k.arrow?.y,J=k.arrow?.centerOffset!==0,[le,q]=s.useState();return K(()=>{w&&q(window.getComputedStyle(w).zIndex)},[w]),l.jsx("div",{ref:W.setFloating,"data-radix-popper-content-wrapper":"",style:{...V,transform:G?V.transform:"translate(0, -200%)",minWidth:"max-content",zIndex:le,"--radix-popper-transform-origin":[k.transformOrigin?.x,k.transformOrigin?.y].join(" "),...k.hide?.referenceHidden&&{visibility:"hidden",pointerEvents:"none"}},dir:e.dir,children:l.jsx(Ks,{scope:t,placedSide:R,onArrowChange:T,arrowX:ne,arrowY:ce,shouldHideArrow:J,children:l.jsx(_.div,{"data-side":R,"data-align":Z,...m,ref:y,style:{...m.style,animation:G?void 0:"none"}})})})});Bn.displayName=Tt;var Un="PopperArrow",Vs={top:"bottom",right:"left",bottom:"top",left:"right"},Hn=s.forwardRef(function(n,t){const{__scopePopper:o,...r}=n,a=Ws(Un,o),i=Vs[a.placedSide];return l.jsx("span",{ref:a.onArrowChange,style:{position:"absolute",left:a.arrowX,top:a.arrowY,[i]:0,transformOrigin:{top:"",right:"0 0",bottom:"center 0",left:"100% 0"}[a.placedSide],transform:{top:"translateY(100%)",right:"translateY(50%) rotate(90deg) translateX(-50%)",bottom:"rotate(180deg)",left:"translateY(50%) rotate(-90deg) translateX(50%)"}[a.placedSide],visibility:a.shouldHideArrow?"hidden":void 0},children:l.jsx(Hs,{...r,ref:t,style:{...r.style,display:"block"}})})});Hn.displayName=Un;function zs(e){return e!==null}var Ys=e=>({name:"transformOrigin",options:e,fn(n){const{placement:t,rects:o,middlewareData:r}=n,i=r.arrow?.centerOffset!==0,c=i?0:e.arrowWidth,u=i?0:e.arrowHeight,[d,f]=Gn(t),p={start:"0%",center:"50%",end:"100%"}[f],v=(r.arrow?.x??0)+c/2,x=(r.arrow?.y??0)+u/2;let C="",m="";return d==="bottom"?(C=i?p:`${v}px`,m=`${-u}px`):d==="top"?(C=i?p:`${v}px`,m=`${o.floating.height+u}px`):d==="right"?(C=`${-u}px`,m=i?p:`${x}px`):d==="left"&&(C=`${o.floating.width+u}px`,m=i?p:`${x}px`),{data:{x:C,y:m}}}});function Gn(e){const[n,t="center"]=e.split("-");return[n,t]}var It=Ln,Mt=$n,At=Bn,Nt=Hn,Kn=Object.freeze({position:"absolute",border:0,width:1,height:1,padding:0,margin:-1,overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",wordWrap:"normal"}),Xs="VisuallyHidden",Wn=s.forwardRef((e,n)=>l.jsx(_.span,{...e,ref:n,style:{...Kn,...e.style}}));Wn.displayName=Xs;var qs=Wn,[ze,Ac]=se("Tooltip",[Pe]),Ye=Pe(),Vn="TooltipProvider",Zs=700,dt="tooltip.open",[Js,Dt]=ze(Vn),zn=e=>{const{__scopeTooltip:n,delayDuration:t=Zs,skipDelayDuration:o=300,disableHoverableContent:r=!1,children:a}=e,i=s.useRef(!0),c=s.useRef(!1),u=s.useRef(0);return s.useEffect(()=>{const d=u.current;return()=>window.clearTimeout(d)},[]),l.jsx(Js,{scope:n,isOpenDelayedRef:i,delayDuration:t,onOpen:s.useCallback(()=>{window.clearTimeout(u.current),i.current=!1},[]),onClose:s.useCallback(()=>{window.clearTimeout(u.current),u.current=window.setTimeout(()=>i.current=!0,o)},[o]),isPointerInTransitRef:c,onPointerInTransitChange:s.useCallback(d=>{c.current=d},[]),disableHoverableContent:r,children:a})};zn.displayName=Vn;var Me="Tooltip",[Qs,je]=ze(Me),Yn=e=>{const{__scopeTooltip:n,children:t,open:o,defaultOpen:r,onOpenChange:a,disableHoverableContent:i,delayDuration:c}=e,u=Dt(Me,e.__scopeTooltip),d=Ye(n),[f,p]=s.useState(null),v=re(),x=s.useRef(0),C=i??u.disableHoverableContent,m=c??u.delayDuration,h=s.useRef(!1),[w,g]=pe({prop:o,defaultProp:r??!1,onChange:P=>{P?(u.onOpen(),document.dispatchEvent(new CustomEvent(dt))):u.onClose(),a?.(P)},caller:Me}),y=s.useMemo(()=>w?h.current?"delayed-open":"instant-open":"closed",[w]),S=s.useCallback(()=>{window.clearTimeout(x.current),x.current=0,h.current=!1,g(!0)},[g]),T=s.useCallback(()=>{window.clearTimeout(x.current),x.current=0,g(!1)},[g]),A=s.useCallback(()=>{window.clearTimeout(x.current),x.current=window.setTimeout(()=>{h.current=!0,g(!0),x.current=0},m)},[m,g]);return s.useEffect(()=>()=>{x.current&&(window.clearTimeout(x.current),x.current=0)},[]),l.jsx(It,{...d,children:l.jsx(Qs,{scope:n,contentId:v,open:w,stateAttribute:y,trigger:f,onTriggerChange:p,onTriggerEnter:s.useCallback(()=>{u.isOpenDelayedRef.current?A():S()},[u.isOpenDelayedRef,A,S]),onTriggerLeave:s.useCallback(()=>{C?T():(window.clearTimeout(x.current),x.current=0)},[T,C]),onOpen:S,onClose:T,disableHoverableContent:C,children:t})})};Yn.displayName=Me;var ft="TooltipTrigger",Xn=s.forwardRef((e,n)=>{const{__scopeTooltip:t,...o}=e,r=je(ft,t),a=Dt(ft,t),i=Ye(t),c=s.useRef(null),u=M(n,c,r.onTriggerChange),d=s.useRef(!1),f=s.useRef(!1),p=s.useCallback(()=>d.current=!1,[]);return s.useEffect(()=>()=>document.removeEventListener("pointerup",p),[p]),l.jsx(Mt,{asChild:!0,...i,children:l.jsx(_.button,{"aria-describedby":r.open?r.contentId:void 0,"data-state":r.stateAttribute,...o,ref:u,onPointerMove:E(e.onPointerMove,v=>{v.pointerType!=="touch"&&!f.current&&!a.isPointerInTransitRef.current&&(r.onTriggerEnter(),f.current=!0)}),onPointerLeave:E(e.onPointerLeave,()=>{r.onTriggerLeave(),f.current=!1}),onPointerDown:E(e.onPointerDown,()=>{r.open&&r.onClose(),d.current=!0,document.addEventListener("pointerup",p,{once:!0})}),onFocus:E(e.onFocus,()=>{d.current||r.onOpen()}),onBlur:E(e.onBlur,r.onClose),onClick:E(e.onClick,r.onClose)})})});Xn.displayName=ft;var Ot="TooltipPortal",[ea,ta]=ze(Ot,{forceMount:void 0}),qn=e=>{const{__scopeTooltip:n,forceMount:t,children:o,container:r}=e,a=je(Ot,n);return l.jsx(ea,{scope:n,forceMount:t,children:l.jsx(te,{present:t||a.open,children:l.jsx(Oe,{asChild:!0,container:r,children:o})})})};qn.displayName=Ot;var Re="TooltipContent",Zn=s.forwardRef((e,n)=>{const t=ta(Re,e.__scopeTooltip),{forceMount:o=t.forceMount,side:r="top",...a}=e,i=je(Re,e.__scopeTooltip);return l.jsx(te,{present:o||i.open,children:i.disableHoverableContent?l.jsx(Jn,{side:r,...a,ref:n}):l.jsx(na,{side:r,...a,ref:n})})}),na=s.forwardRef((e,n)=>{const t=je(Re,e.__scopeTooltip),o=Dt(Re,e.__scopeTooltip),r=s.useRef(null),a=M(n,r),[i,c]=s.useState(null),{trigger:u,onClose:d}=t,f=r.current,{onPointerInTransitChange:p}=o,v=s.useCallback(()=>{c(null),p(!1)},[p]),x=s.useCallback((C,m)=>{const h=C.currentTarget,w={x:C.clientX,y:C.clientY},g=aa(w,h.getBoundingClientRect()),y=ia(w,g),S=ca(m.getBoundingClientRect()),T=ua([...y,...S]);c(T),p(!0)},[p]);return s.useEffect(()=>()=>v(),[v]),s.useEffect(()=>{if(u&&f){const C=h=>x(h,f),m=h=>x(h,u);return u.addEventListener("pointerleave",C),f.addEventListener("pointerleave",m),()=>{u.removeEventListener("pointerleave",C),f.removeEventListener("pointerleave",m)}}},[u,f,x,v]),s.useEffect(()=>{if(i){const C=m=>{const h=m.target,w={x:m.clientX,y:m.clientY},g=u?.contains(h)||f?.contains(h),y=!la(w,i);g?v():y&&(v(),d())};return document.addEventListener("pointermove",C),()=>document.removeEventListener("pointermove",C)}},[u,f,i,d,v]),l.jsx(Jn,{...e,ref:a})}),[oa,ra]=ze(Me,{isInside:!1}),sa=ln("TooltipContent"),Jn=s.forwardRef((e,n)=>{const{__scopeTooltip:t,children:o,"aria-label":r,onEscapeKeyDown:a,onPointerDownOutside:i,...c}=e,u=je(Re,t),d=Ye(t),{onClose:f}=u;return s.useEffect(()=>(document.addEventListener(dt,f),()=>document.removeEventListener(dt,f)),[f]),s.useEffect(()=>{if(u.trigger){const p=v=>{v.target?.contains(u.trigger)&&f()};return window.addEventListener("scroll",p,{capture:!0}),()=>window.removeEventListener("scroll",p,{capture:!0})}},[u.trigger,f]),l.jsx(De,{asChild:!0,disableOutsidePointerEvents:!1,onEscapeKeyDown:a,onPointerDownOutside:i,onFocusOutside:p=>p.preventDefault(),onDismiss:f,children:l.jsxs(At,{"data-state":u.stateAttribute,...d,...c,ref:n,style:{...c.style,"--radix-tooltip-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-tooltip-content-available-width":"var(--radix-popper-available-width)","--radix-tooltip-content-available-height":"var(--radix-popper-available-height)","--radix-tooltip-trigger-width":"var(--radix-popper-anchor-width)","--radix-tooltip-trigger-height":"var(--radix-popper-anchor-height)"},children:[l.jsx(sa,{children:o}),l.jsx(oa,{scope:t,isInside:!0,children:l.jsx(qs,{id:u.contentId,role:"tooltip",children:r||o})})]})})});Zn.displayName=Re;var Qn="TooltipArrow",eo=s.forwardRef((e,n)=>{const{__scopeTooltip:t,...o}=e,r=Ye(t);return ra(Qn,t).isInside?null:l.jsx(Nt,{...r,...o,ref:n})});eo.displayName=Qn;function aa(e,n){const t=Math.abs(n.top-e.y),o=Math.abs(n.bottom-e.y),r=Math.abs(n.right-e.x),a=Math.abs(n.left-e.x);switch(Math.min(t,o,r,a)){case a:return"left";case r:return"right";case t:return"top";case o:return"bottom";default:throw new Error("unreachable")}}function ia(e,n,t=5){const o=[];switch(n){case"top":o.push({x:e.x-t,y:e.y+t},{x:e.x+t,y:e.y+t});break;case"bottom":o.push({x:e.x-t,y:e.y-t},{x:e.x+t,y:e.y-t});break;case"left":o.push({x:e.x+t,y:e.y-t},{x:e.x+t,y:e.y+t});break;case"right":o.push({x:e.x-t,y:e.y-t},{x:e.x-t,y:e.y+t});break}return o}function ca(e){const{top:n,right:t,bottom:o,left:r}=e;return[{x:r,y:n},{x:t,y:n},{x:t,y:o},{x:r,y:o}]}function la(e,n){const{x:t,y:o}=e;let r=!1;for(let a=0,i=n.length-1;ao!=v>o&&t<(p-d)*(o-f)/(v-f)+d&&(r=!r)}return r}function ua(e){const n=e.slice();return n.sort((t,o)=>t.xo.x?1:t.yo.y?1:0),da(n)}function da(e){if(e.length<=1)return e.slice();const n=[];for(let o=0;o=2;){const a=n[n.length-1],i=n[n.length-2];if((a.x-i.x)*(r.y-i.y)>=(a.y-i.y)*(r.x-i.x))n.pop();else break}n.push(r)}n.pop();const t=[];for(let o=e.length-1;o>=0;o--){const r=e[o];for(;t.length>=2;){const a=t[t.length-1],i=t[t.length-2];if((a.x-i.x)*(r.y-i.y)>=(a.y-i.y)*(r.x-i.x))t.pop();else break}t.push(r)}return t.pop(),n.length===1&&t.length===1&&n[0].x===t[0].x&&n[0].y===t[0].y?n:n.concat(t)}var Nc=zn,Dc=Yn,Oc=Xn,jc=qn,kc=Zn,Lc=eo,Xe="Collapsible",[fa,Fc]=se(Xe),[pa,jt]=fa(Xe),to=s.forwardRef((e,n)=>{const{__scopeCollapsible:t,open:o,defaultOpen:r,disabled:a,onOpenChange:i,...c}=e,[u,d]=pe({prop:o,defaultProp:r??!1,onChange:i,caller:Xe});return l.jsx(pa,{scope:t,disabled:a,contentId:re(),open:u,onOpenToggle:s.useCallback(()=>d(f=>!f),[d]),children:l.jsx(_.div,{"data-state":Lt(u),"data-disabled":a?"":void 0,...c,ref:n})})});to.displayName=Xe;var no="CollapsibleTrigger",ma=s.forwardRef((e,n)=>{const{__scopeCollapsible:t,...o}=e,r=jt(no,t);return l.jsx(_.button,{type:"button","aria-controls":r.contentId,"aria-expanded":r.open||!1,"data-state":Lt(r.open),"data-disabled":r.disabled?"":void 0,disabled:r.disabled,...o,ref:n,onClick:E(e.onClick,r.onOpenToggle)})});ma.displayName=no;var kt="CollapsibleContent",va=s.forwardRef((e,n)=>{const{forceMount:t,...o}=e,r=jt(kt,e.__scopeCollapsible);return l.jsx(te,{present:t||r.open,children:({present:a})=>l.jsx(ha,{...o,ref:n,present:a})})});va.displayName=kt;var ha=s.forwardRef((e,n)=>{const{__scopeCollapsible:t,present:o,children:r,...a}=e,i=jt(kt,t),[c,u]=s.useState(o),d=s.useRef(null),f=M(n,d),p=s.useRef(0),v=p.current,x=s.useRef(0),C=x.current,m=i.open||c,h=s.useRef(m),w=s.useRef(void 0);return s.useEffect(()=>{const g=requestAnimationFrame(()=>h.current=!1);return()=>cancelAnimationFrame(g)},[]),K(()=>{const g=d.current;if(g){w.current=w.current||{transitionDuration:g.style.transitionDuration,animationName:g.style.animationName},g.style.transitionDuration="0s",g.style.animationName="none";const y=g.getBoundingClientRect();p.current=y.height,x.current=y.width,h.current||(g.style.transitionDuration=w.current.transitionDuration,g.style.animationName=w.current.animationName),u(o)}},[i.open,o]),l.jsx(_.div,{"data-state":Lt(i.open),"data-disabled":i.disabled?"":void 0,id:i.contentId,hidden:!m,...a,ref:f,style:{"--radix-collapsible-content-height":v?`${v}px`:void 0,"--radix-collapsible-content-width":C?`${C}px`:void 0,...e.style},children:m&&r})});function Lt(e){return e?"open":"closed"}var $c=to;function Ft(e){const n=e+"CollectionProvider",[t,o]=se(n),[r,a]=t(n,{collectionRef:{current:null},itemMap:new Map}),i=m=>{const{scope:h,children:w}=m,g=ge.useRef(null),y=ge.useRef(new Map).current;return l.jsx(r,{scope:h,itemMap:y,collectionRef:g,children:w})};i.displayName=n;const c=e+"CollectionSlot",u=xe(c),d=ge.forwardRef((m,h)=>{const{scope:w,children:g}=m,y=a(c,w),S=M(h,y.collectionRef);return l.jsx(u,{ref:S,children:g})});d.displayName=c;const f=e+"CollectionItemSlot",p="data-radix-collection-item",v=xe(f),x=ge.forwardRef((m,h)=>{const{scope:w,children:g,...y}=m,S=ge.useRef(null),T=M(h,S),A=a(f,w);return ge.useEffect(()=>(A.itemMap.set(S,{ref:S,...y}),()=>void A.itemMap.delete(S))),l.jsx(v,{[p]:"",ref:T,children:g})});x.displayName=f;function C(m){const h=a(e+"CollectionConsumer",m);return ge.useCallback(()=>{const g=h.collectionRef.current;if(!g)return[];const y=Array.from(g.querySelectorAll(`[${p}]`));return Array.from(h.itemMap.values()).sort((A,P)=>y.indexOf(A.ref.current)-y.indexOf(P.ref.current))},[h.collectionRef,h.itemMap])}return[{Provider:i,Slot:d,ItemSlot:x},C,o]}var ga=s.createContext(void 0);function $t(e){const n=s.useContext(ga);return e||n||"ltr"}var lt="rovingFocusGroup.onEntryFocus",xa={bubbles:!1,cancelable:!0},ke="RovingFocusGroup",[pt,oo,Ca]=Ft(ke),[wa,ro]=se(ke,[Ca]),[ya,Ea]=wa(ke),so=s.forwardRef((e,n)=>l.jsx(pt.Provider,{scope:e.__scopeRovingFocusGroup,children:l.jsx(pt.Slot,{scope:e.__scopeRovingFocusGroup,children:l.jsx(ba,{...e,ref:n})})}));so.displayName=ke;var ba=s.forwardRef((e,n)=>{const{__scopeRovingFocusGroup:t,orientation:o,loop:r=!1,dir:a,currentTabStopId:i,defaultCurrentTabStopId:c,onCurrentTabStopIdChange:u,onEntryFocus:d,preventScrollOnEntryFocus:f=!1,...p}=e,v=s.useRef(null),x=M(n,v),C=$t(a),[m,h]=pe({prop:i,defaultProp:c??null,onChange:u,caller:ke}),[w,g]=s.useState(!1),y=ie(d),S=oo(t),T=s.useRef(!1),[A,P]=s.useState(0);return s.useEffect(()=>{const I=v.current;if(I)return I.addEventListener(lt,y),()=>I.removeEventListener(lt,y)},[y]),l.jsx(ya,{scope:t,orientation:o,dir:C,loop:r,currentTabStopId:m,onItemFocus:s.useCallback(I=>h(I),[h]),onItemShiftTab:s.useCallback(()=>g(!0),[]),onFocusableItemAdd:s.useCallback(()=>P(I=>I+1),[]),onFocusableItemRemove:s.useCallback(()=>P(I=>I-1),[]),children:l.jsx(_.div,{tabIndex:w||A===0?-1:0,"data-orientation":o,...p,ref:x,style:{outline:"none",...e.style},onMouseDown:E(e.onMouseDown,()=>{T.current=!0}),onFocus:E(e.onFocus,I=>{const F=!T.current;if(I.target===I.currentTarget&&F&&!w){const L=new CustomEvent(lt,xa);if(I.currentTarget.dispatchEvent(L),!L.defaultPrevented){const B=S().filter(N=>N.focusable),H=B.find(N=>N.active),U=B.find(N=>N.id===m),V=[H,U,...B].filter(Boolean).map(N=>N.ref.current);co(V,f)}}T.current=!1}),onBlur:E(e.onBlur,()=>g(!1))})})}),ao="RovingFocusGroupItem",io=s.forwardRef((e,n)=>{const{__scopeRovingFocusGroup:t,focusable:o=!0,active:r=!1,tabStopId:a,children:i,...c}=e,u=re(),d=a||u,f=Ea(ao,t),p=f.currentTabStopId===d,v=oo(t),{onFocusableItemAdd:x,onFocusableItemRemove:C,currentTabStopId:m}=f;return s.useEffect(()=>{if(o)return x(),()=>C()},[o,x,C]),l.jsx(pt.ItemSlot,{scope:t,id:d,focusable:o,active:r,children:l.jsx(_.span,{tabIndex:p?0:-1,"data-orientation":f.orientation,...c,ref:n,onMouseDown:E(e.onMouseDown,h=>{o?f.onItemFocus(d):h.preventDefault()}),onFocus:E(e.onFocus,()=>f.onItemFocus(d)),onKeyDown:E(e.onKeyDown,h=>{if(h.key==="Tab"&&h.shiftKey){f.onItemShiftTab();return}if(h.target!==h.currentTarget)return;const w=Pa(h,f.orientation,f.dir);if(w!==void 0){if(h.metaKey||h.ctrlKey||h.altKey||h.shiftKey)return;h.preventDefault();let y=v().filter(S=>S.focusable).map(S=>S.ref.current);if(w==="last")y.reverse();else if(w==="prev"||w==="next"){w==="prev"&&y.reverse();const S=y.indexOf(h.currentTarget);y=f.loop?_a(y,S+1):y.slice(S+1)}setTimeout(()=>co(y))}}),children:typeof i=="function"?i({isCurrentTabStop:p,hasTabStop:m!=null}):i})})});io.displayName=ao;var Sa={ArrowLeft:"prev",ArrowUp:"prev",ArrowRight:"next",ArrowDown:"next",PageUp:"first",Home:"first",PageDown:"last",End:"last"};function Ra(e,n){return n!=="rtl"?e:e==="ArrowLeft"?"ArrowRight":e==="ArrowRight"?"ArrowLeft":e}function Pa(e,n,t){const o=Ra(e.key,t);if(!(n==="vertical"&&["ArrowLeft","ArrowRight"].includes(o))&&!(n==="horizontal"&&["ArrowUp","ArrowDown"].includes(o)))return Sa[o]}function co(e,n=!1){const t=document.activeElement;for(const o of e)if(o===t||(o.focus({preventScroll:n}),document.activeElement!==t))return}function _a(e,n){return e.map((t,o)=>e[(n+o)%e.length])}var Ta=so,Ia=io,mt=["Enter"," "],Ma=["ArrowDown","PageUp","Home"],lo=["ArrowUp","PageDown","End"],Aa=[...Ma,...lo],Na={ltr:[...mt,"ArrowRight"],rtl:[...mt,"ArrowLeft"]},Da={ltr:["ArrowLeft"],rtl:["ArrowRight"]},Le="Menu",[Ae,Oa,ja]=Ft(Le),[Ee,uo]=se(Le,[ja,Pe,ro]),qe=Pe(),fo=ro(),[ka,be]=Ee(Le),[La,Fe]=Ee(Le),po=e=>{const{__scopeMenu:n,open:t=!1,children:o,dir:r,onOpenChange:a,modal:i=!0}=e,c=qe(n),[u,d]=s.useState(null),f=s.useRef(!1),p=ie(a),v=$t(r);return s.useEffect(()=>{const x=()=>{f.current=!0,document.addEventListener("pointerdown",C,{capture:!0,once:!0}),document.addEventListener("pointermove",C,{capture:!0,once:!0})},C=()=>f.current=!1;return document.addEventListener("keydown",x,{capture:!0}),()=>{document.removeEventListener("keydown",x,{capture:!0}),document.removeEventListener("pointerdown",C,{capture:!0}),document.removeEventListener("pointermove",C,{capture:!0})}},[]),l.jsx(It,{...c,children:l.jsx(ka,{scope:n,open:t,onOpenChange:p,content:u,onContentChange:d,children:l.jsx(La,{scope:n,onClose:s.useCallback(()=>p(!1),[p]),isUsingKeyboardRef:f,dir:v,modal:i,children:o})})})};po.displayName=Le;var Fa="MenuAnchor",Bt=s.forwardRef((e,n)=>{const{__scopeMenu:t,...o}=e,r=qe(t);return l.jsx(Mt,{...r,...o,ref:n})});Bt.displayName=Fa;var Ut="MenuPortal",[$a,mo]=Ee(Ut,{forceMount:void 0}),vo=e=>{const{__scopeMenu:n,forceMount:t,children:o,container:r}=e,a=be(Ut,n);return l.jsx($a,{scope:n,forceMount:t,children:l.jsx(te,{present:t||a.open,children:l.jsx(Oe,{asChild:!0,container:r,children:o})})})};vo.displayName=Ut;var ee="MenuContent",[Ba,Ht]=Ee(ee),ho=s.forwardRef((e,n)=>{const t=mo(ee,e.__scopeMenu),{forceMount:o=t.forceMount,...r}=e,a=be(ee,e.__scopeMenu),i=Fe(ee,e.__scopeMenu);return l.jsx(Ae.Provider,{scope:e.__scopeMenu,children:l.jsx(te,{present:o||a.open,children:l.jsx(Ae.Slot,{scope:e.__scopeMenu,children:i.modal?l.jsx(Ua,{...r,ref:n}):l.jsx(Ha,{...r,ref:n})})})})}),Ua=s.forwardRef((e,n)=>{const t=be(ee,e.__scopeMenu),o=s.useRef(null),r=M(n,o);return s.useEffect(()=>{const a=o.current;if(a)return yt(a)},[]),l.jsx(Gt,{...e,ref:r,trapFocus:t.open,disableOutsidePointerEvents:t.open,disableOutsideScroll:!0,onFocusOutside:E(e.onFocusOutside,a=>a.preventDefault(),{checkForDefaultPrevented:!1}),onDismiss:()=>t.onOpenChange(!1)})}),Ha=s.forwardRef((e,n)=>{const t=be(ee,e.__scopeMenu);return l.jsx(Gt,{...e,ref:n,trapFocus:!1,disableOutsidePointerEvents:!1,disableOutsideScroll:!1,onDismiss:()=>t.onOpenChange(!1)})}),Ga=xe("MenuContent.ScrollLock"),Gt=s.forwardRef((e,n)=>{const{__scopeMenu:t,loop:o=!1,trapFocus:r,onOpenAutoFocus:a,onCloseAutoFocus:i,disableOutsidePointerEvents:c,onEntryFocus:u,onEscapeKeyDown:d,onPointerDownOutside:f,onFocusOutside:p,onInteractOutside:v,onDismiss:x,disableOutsideScroll:C,...m}=e,h=be(ee,t),w=Fe(ee,t),g=qe(t),y=fo(t),S=Oa(t),[T,A]=s.useState(null),P=s.useRef(null),I=M(n,P,h.onContentChange),F=s.useRef(0),L=s.useRef(""),B=s.useRef(0),H=s.useRef(null),U=s.useRef("right"),W=s.useRef(0),V=C?Et:s.Fragment,N=C?{as:Ga,allowPinchZoom:!0}:void 0,G=R=>{const Z=L.current+R,z=S().filter(b=>!b.disabled),ne=document.activeElement,ce=z.find(b=>b.ref.current===ne)?.textValue,J=z.map(b=>b.textValue),le=ti(J,Z,ce),q=z.find(b=>b.textValue===le)?.ref.current;(function b(D){L.current=D,window.clearTimeout(F.current),D!==""&&(F.current=window.setTimeout(()=>b(""),1e3))})(Z),q&&setTimeout(()=>q.focus())};s.useEffect(()=>()=>window.clearTimeout(F.current),[]),bt();const k=s.useCallback(R=>U.current===H.current?.side&&oi(R,H.current?.area),[]);return l.jsx(Ba,{scope:t,searchRef:L,onItemEnter:s.useCallback(R=>{k(R)&&R.preventDefault()},[k]),onItemLeave:s.useCallback(R=>{k(R)||(P.current?.focus(),A(null))},[k]),onTriggerLeave:s.useCallback(R=>{k(R)&&R.preventDefault()},[k]),pointerGraceTimerRef:B,onPointerGraceIntentChange:s.useCallback(R=>{H.current=R},[]),children:l.jsx(V,{...N,children:l.jsx(We,{asChild:!0,trapped:r,onMountAutoFocus:E(a,R=>{R.preventDefault(),P.current?.focus({preventScroll:!0})}),onUnmountAutoFocus:i,children:l.jsx(De,{asChild:!0,disableOutsidePointerEvents:c,onEscapeKeyDown:d,onPointerDownOutside:f,onFocusOutside:p,onInteractOutside:v,onDismiss:x,children:l.jsx(Ta,{asChild:!0,...y,dir:w.dir,orientation:"vertical",loop:o,currentTabStopId:T,onCurrentTabStopIdChange:A,onEntryFocus:E(u,R=>{w.isUsingKeyboardRef.current||R.preventDefault()}),preventScrollOnEntryFocus:!0,children:l.jsx(At,{role:"menu","aria-orientation":"vertical","data-state":No(h.open),"data-radix-menu-content":"",dir:w.dir,...g,...m,ref:I,style:{outline:"none",...m.style},onKeyDown:E(m.onKeyDown,R=>{const z=R.target.closest("[data-radix-menu-content]")===R.currentTarget,ne=R.ctrlKey||R.altKey||R.metaKey,ce=R.key.length===1;z&&(R.key==="Tab"&&R.preventDefault(),!ne&&ce&&G(R.key));const J=P.current;if(R.target!==J||!Aa.includes(R.key))return;R.preventDefault();const q=S().filter(b=>!b.disabled).map(b=>b.ref.current);lo.includes(R.key)&&q.reverse(),Qa(q)}),onBlur:E(e.onBlur,R=>{R.currentTarget.contains(R.target)||(window.clearTimeout(F.current),L.current="")}),onPointerMove:E(e.onPointerMove,Ne(R=>{const Z=R.target,z=W.current!==R.clientX;if(R.currentTarget.contains(Z)&&z){const ne=R.clientX>W.current?"right":"left";U.current=ne,W.current=R.clientX}}))})})})})})})});ho.displayName=ee;var Ka="MenuGroup",Kt=s.forwardRef((e,n)=>{const{__scopeMenu:t,...o}=e;return l.jsx(_.div,{role:"group",...o,ref:n})});Kt.displayName=Ka;var Wa="MenuLabel",go=s.forwardRef((e,n)=>{const{__scopeMenu:t,...o}=e;return l.jsx(_.div,{...o,ref:n})});go.displayName=Wa;var Ue="MenuItem",rn="menu.itemSelect",Ze=s.forwardRef((e,n)=>{const{disabled:t=!1,onSelect:o,...r}=e,a=s.useRef(null),i=Fe(Ue,e.__scopeMenu),c=Ht(Ue,e.__scopeMenu),u=M(n,a),d=s.useRef(!1),f=()=>{const p=a.current;if(!t&&p){const v=new CustomEvent(rn,{bubbles:!0,cancelable:!0});p.addEventListener(rn,x=>o?.(x),{once:!0}),un(p,v),v.defaultPrevented?d.current=!1:i.onClose()}};return l.jsx(xo,{...r,ref:u,disabled:t,onClick:E(e.onClick,f),onPointerDown:p=>{e.onPointerDown?.(p),d.current=!0},onPointerUp:E(e.onPointerUp,p=>{d.current||p.currentTarget?.click()}),onKeyDown:E(e.onKeyDown,p=>{const v=c.searchRef.current!=="";t||v&&p.key===" "||mt.includes(p.key)&&(p.currentTarget.click(),p.preventDefault())})})});Ze.displayName=Ue;var xo=s.forwardRef((e,n)=>{const{__scopeMenu:t,disabled:o=!1,textValue:r,...a}=e,i=Ht(Ue,t),c=fo(t),u=s.useRef(null),d=M(n,u),[f,p]=s.useState(!1),[v,x]=s.useState("");return s.useEffect(()=>{const C=u.current;C&&x((C.textContent??"").trim())},[a.children]),l.jsx(Ae.ItemSlot,{scope:t,disabled:o,textValue:r??v,children:l.jsx(Ia,{asChild:!0,...c,focusable:!o,children:l.jsx(_.div,{role:"menuitem","data-highlighted":f?"":void 0,"aria-disabled":o||void 0,"data-disabled":o?"":void 0,...a,ref:d,onPointerMove:E(e.onPointerMove,Ne(C=>{o?i.onItemLeave(C):(i.onItemEnter(C),C.defaultPrevented||C.currentTarget.focus({preventScroll:!0}))})),onPointerLeave:E(e.onPointerLeave,Ne(C=>i.onItemLeave(C))),onFocus:E(e.onFocus,()=>p(!0)),onBlur:E(e.onBlur,()=>p(!1))})})})}),Va="MenuCheckboxItem",Co=s.forwardRef((e,n)=>{const{checked:t=!1,onCheckedChange:o,...r}=e;return l.jsx(So,{scope:e.__scopeMenu,checked:t,children:l.jsx(Ze,{role:"menuitemcheckbox","aria-checked":He(t)?"mixed":t,...r,ref:n,"data-state":Vt(t),onSelect:E(r.onSelect,()=>o?.(He(t)?!0:!t),{checkForDefaultPrevented:!1})})})});Co.displayName=Va;var wo="MenuRadioGroup",[za,Ya]=Ee(wo,{value:void 0,onValueChange:()=>{}}),yo=s.forwardRef((e,n)=>{const{value:t,onValueChange:o,...r}=e,a=ie(o);return l.jsx(za,{scope:e.__scopeMenu,value:t,onValueChange:a,children:l.jsx(Kt,{...r,ref:n})})});yo.displayName=wo;var Eo="MenuRadioItem",bo=s.forwardRef((e,n)=>{const{value:t,...o}=e,r=Ya(Eo,e.__scopeMenu),a=t===r.value;return l.jsx(So,{scope:e.__scopeMenu,checked:a,children:l.jsx(Ze,{role:"menuitemradio","aria-checked":a,...o,ref:n,"data-state":Vt(a),onSelect:E(o.onSelect,()=>r.onValueChange?.(t),{checkForDefaultPrevented:!1})})})});bo.displayName=Eo;var Wt="MenuItemIndicator",[So,Xa]=Ee(Wt,{checked:!1}),Ro=s.forwardRef((e,n)=>{const{__scopeMenu:t,forceMount:o,...r}=e,a=Xa(Wt,t);return l.jsx(te,{present:o||He(a.checked)||a.checked===!0,children:l.jsx(_.span,{...r,ref:n,"data-state":Vt(a.checked)})})});Ro.displayName=Wt;var qa="MenuSeparator",Po=s.forwardRef((e,n)=>{const{__scopeMenu:t,...o}=e;return l.jsx(_.div,{role:"separator","aria-orientation":"horizontal",...o,ref:n})});Po.displayName=qa;var Za="MenuArrow",_o=s.forwardRef((e,n)=>{const{__scopeMenu:t,...o}=e,r=qe(t);return l.jsx(Nt,{...r,...o,ref:n})});_o.displayName=Za;var Ja="MenuSub",[Bc,To]=Ee(Ja),Te="MenuSubTrigger",Io=s.forwardRef((e,n)=>{const t=be(Te,e.__scopeMenu),o=Fe(Te,e.__scopeMenu),r=To(Te,e.__scopeMenu),a=Ht(Te,e.__scopeMenu),i=s.useRef(null),{pointerGraceTimerRef:c,onPointerGraceIntentChange:u}=a,d={__scopeMenu:e.__scopeMenu},f=s.useCallback(()=>{i.current&&window.clearTimeout(i.current),i.current=null},[]);return s.useEffect(()=>f,[f]),s.useEffect(()=>{const p=c.current;return()=>{window.clearTimeout(p),u(null)}},[c,u]),l.jsx(Bt,{asChild:!0,...d,children:l.jsx(xo,{id:r.triggerId,"aria-haspopup":"menu","aria-expanded":t.open,"aria-controls":r.contentId,"data-state":No(t.open),...e,ref:Ke(n,r.onTriggerChange),onClick:p=>{e.onClick?.(p),!(e.disabled||p.defaultPrevented)&&(p.currentTarget.focus(),t.open||t.onOpenChange(!0))},onPointerMove:E(e.onPointerMove,Ne(p=>{a.onItemEnter(p),!p.defaultPrevented&&!e.disabled&&!t.open&&!i.current&&(a.onPointerGraceIntentChange(null),i.current=window.setTimeout(()=>{t.onOpenChange(!0),f()},100))})),onPointerLeave:E(e.onPointerLeave,Ne(p=>{f();const v=t.content?.getBoundingClientRect();if(v){const x=t.content?.dataset.side,C=x==="right",m=C?-5:5,h=v[C?"left":"right"],w=v[C?"right":"left"];a.onPointerGraceIntentChange({area:[{x:p.clientX+m,y:p.clientY},{x:h,y:v.top},{x:w,y:v.top},{x:w,y:v.bottom},{x:h,y:v.bottom}],side:x}),window.clearTimeout(c.current),c.current=window.setTimeout(()=>a.onPointerGraceIntentChange(null),300)}else{if(a.onTriggerLeave(p),p.defaultPrevented)return;a.onPointerGraceIntentChange(null)}})),onKeyDown:E(e.onKeyDown,p=>{const v=a.searchRef.current!=="";e.disabled||v&&p.key===" "||Na[o.dir].includes(p.key)&&(t.onOpenChange(!0),t.content?.focus(),p.preventDefault())})})})});Io.displayName=Te;var Mo="MenuSubContent",Ao=s.forwardRef((e,n)=>{const t=mo(ee,e.__scopeMenu),{forceMount:o=t.forceMount,...r}=e,a=be(ee,e.__scopeMenu),i=Fe(ee,e.__scopeMenu),c=To(Mo,e.__scopeMenu),u=s.useRef(null),d=M(n,u);return l.jsx(Ae.Provider,{scope:e.__scopeMenu,children:l.jsx(te,{present:o||a.open,children:l.jsx(Ae.Slot,{scope:e.__scopeMenu,children:l.jsx(Gt,{id:c.contentId,"aria-labelledby":c.triggerId,...r,ref:d,align:"start",side:i.dir==="rtl"?"left":"right",disableOutsidePointerEvents:!1,disableOutsideScroll:!1,trapFocus:!1,onOpenAutoFocus:f=>{i.isUsingKeyboardRef.current&&u.current?.focus(),f.preventDefault()},onCloseAutoFocus:f=>f.preventDefault(),onFocusOutside:E(e.onFocusOutside,f=>{f.target!==c.trigger&&a.onOpenChange(!1)}),onEscapeKeyDown:E(e.onEscapeKeyDown,f=>{i.onClose(),f.preventDefault()}),onKeyDown:E(e.onKeyDown,f=>{const p=f.currentTarget.contains(f.target),v=Da[i.dir].includes(f.key);p&&v&&(a.onOpenChange(!1),c.trigger?.focus(),f.preventDefault())})})})})})});Ao.displayName=Mo;function No(e){return e?"open":"closed"}function He(e){return e==="indeterminate"}function Vt(e){return He(e)?"indeterminate":e?"checked":"unchecked"}function Qa(e){const n=document.activeElement;for(const t of e)if(t===n||(t.focus(),document.activeElement!==n))return}function ei(e,n){return e.map((t,o)=>e[(n+o)%e.length])}function ti(e,n,t){const r=n.length>1&&Array.from(n).every(d=>d===n[0])?n[0]:n,a=t?e.indexOf(t):-1;let i=ei(e,Math.max(a,0));r.length===1&&(i=i.filter(d=>d!==t));const u=i.find(d=>d.toLowerCase().startsWith(r.toLowerCase()));return u!==t?u:void 0}function ni(e,n){const{x:t,y:o}=e;let r=!1;for(let a=0,i=n.length-1;ao!=v>o&&t<(p-d)*(o-f)/(v-f)+d&&(r=!r)}return r}function oi(e,n){if(!n)return!1;const t={x:e.clientX,y:e.clientY};return ni(t,n)}function Ne(e){return n=>n.pointerType==="mouse"?e(n):void 0}var ri=po,si=Bt,ai=vo,ii=ho,ci=Kt,li=go,ui=Ze,di=Co,fi=yo,pi=bo,mi=Ro,vi=Po,hi=_o,gi=Io,xi=Ao,Je="DropdownMenu",[Ci,Uc]=se(Je,[uo]),X=uo(),[wi,Do]=Ci(Je),Oo=e=>{const{__scopeDropdownMenu:n,children:t,dir:o,open:r,defaultOpen:a,onOpenChange:i,modal:c=!0}=e,u=X(n),d=s.useRef(null),[f,p]=pe({prop:r,defaultProp:a??!1,onChange:i,caller:Je});return l.jsx(wi,{scope:n,triggerId:re(),triggerRef:d,contentId:re(),open:f,onOpenChange:p,onOpenToggle:s.useCallback(()=>p(v=>!v),[p]),modal:c,children:l.jsx(ri,{...u,open:f,onOpenChange:p,dir:o,modal:c,children:t})})};Oo.displayName=Je;var jo="DropdownMenuTrigger",ko=s.forwardRef((e,n)=>{const{__scopeDropdownMenu:t,disabled:o=!1,...r}=e,a=Do(jo,t),i=X(t);return l.jsx(si,{asChild:!0,...i,children:l.jsx(_.button,{type:"button",id:a.triggerId,"aria-haspopup":"menu","aria-expanded":a.open,"aria-controls":a.open?a.contentId:void 0,"data-state":a.open?"open":"closed","data-disabled":o?"":void 0,disabled:o,...r,ref:Ke(n,a.triggerRef),onPointerDown:E(e.onPointerDown,c=>{!o&&c.button===0&&c.ctrlKey===!1&&(a.onOpenToggle(),a.open||c.preventDefault())}),onKeyDown:E(e.onKeyDown,c=>{o||(["Enter"," "].includes(c.key)&&a.onOpenToggle(),c.key==="ArrowDown"&&a.onOpenChange(!0),["Enter"," ","ArrowDown"].includes(c.key)&&c.preventDefault())})})})});ko.displayName=jo;var yi="DropdownMenuPortal",Lo=e=>{const{__scopeDropdownMenu:n,...t}=e,o=X(n);return l.jsx(ai,{...o,...t})};Lo.displayName=yi;var Fo="DropdownMenuContent",$o=s.forwardRef((e,n)=>{const{__scopeDropdownMenu:t,...o}=e,r=Do(Fo,t),a=X(t),i=s.useRef(!1);return l.jsx(ii,{id:r.contentId,"aria-labelledby":r.triggerId,...a,...o,ref:n,onCloseAutoFocus:E(e.onCloseAutoFocus,c=>{i.current||r.triggerRef.current?.focus(),i.current=!1,c.preventDefault()}),onInteractOutside:E(e.onInteractOutside,c=>{const u=c.detail.originalEvent,d=u.button===0&&u.ctrlKey===!0,f=u.button===2||d;(!r.modal||f)&&(i.current=!0)}),style:{...e.style,"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"}})});$o.displayName=Fo;var Ei="DropdownMenuGroup",bi=s.forwardRef((e,n)=>{const{__scopeDropdownMenu:t,...o}=e,r=X(t);return l.jsx(ci,{...r,...o,ref:n})});bi.displayName=Ei;var Si="DropdownMenuLabel",Ri=s.forwardRef((e,n)=>{const{__scopeDropdownMenu:t,...o}=e,r=X(t);return l.jsx(li,{...r,...o,ref:n})});Ri.displayName=Si;var Pi="DropdownMenuItem",Bo=s.forwardRef((e,n)=>{const{__scopeDropdownMenu:t,...o}=e,r=X(t);return l.jsx(ui,{...r,...o,ref:n})});Bo.displayName=Pi;var _i="DropdownMenuCheckboxItem",Ti=s.forwardRef((e,n)=>{const{__scopeDropdownMenu:t,...o}=e,r=X(t);return l.jsx(di,{...r,...o,ref:n})});Ti.displayName=_i;var Ii="DropdownMenuRadioGroup",Mi=s.forwardRef((e,n)=>{const{__scopeDropdownMenu:t,...o}=e,r=X(t);return l.jsx(fi,{...r,...o,ref:n})});Mi.displayName=Ii;var Ai="DropdownMenuRadioItem",Ni=s.forwardRef((e,n)=>{const{__scopeDropdownMenu:t,...o}=e,r=X(t);return l.jsx(pi,{...r,...o,ref:n})});Ni.displayName=Ai;var Di="DropdownMenuItemIndicator",Oi=s.forwardRef((e,n)=>{const{__scopeDropdownMenu:t,...o}=e,r=X(t);return l.jsx(mi,{...r,...o,ref:n})});Oi.displayName=Di;var ji="DropdownMenuSeparator",ki=s.forwardRef((e,n)=>{const{__scopeDropdownMenu:t,...o}=e,r=X(t);return l.jsx(vi,{...r,...o,ref:n})});ki.displayName=ji;var Li="DropdownMenuArrow",Fi=s.forwardRef((e,n)=>{const{__scopeDropdownMenu:t,...o}=e,r=X(t);return l.jsx(hi,{...r,...o,ref:n})});Fi.displayName=Li;var $i="DropdownMenuSubTrigger",Bi=s.forwardRef((e,n)=>{const{__scopeDropdownMenu:t,...o}=e,r=X(t);return l.jsx(gi,{...r,...o,ref:n})});Bi.displayName=$i;var Ui="DropdownMenuSubContent",Hi=s.forwardRef((e,n)=>{const{__scopeDropdownMenu:t,...o}=e,r=X(t);return l.jsx(xi,{...r,...o,ref:n,style:{...e.style,"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"}})});Hi.displayName=Ui;var Hc=Oo,Gc=ko,Kc=Lo,Wc=$o,Vc=Bo,Uo="AlertDialog",[Gi,zc]=se(Uo,[hn]),ue=hn(),Ho=e=>{const{__scopeAlertDialog:n,...t}=e,o=ue(n);return l.jsx(Os,{...o,...t,modal:!0})};Ho.displayName=Uo;var Ki="AlertDialogTrigger",Go=s.forwardRef((e,n)=>{const{__scopeAlertDialog:t,...o}=e,r=ue(t);return l.jsx(js,{...r,...o,ref:n})});Go.displayName=Ki;var Wi="AlertDialogPortal",Ko=e=>{const{__scopeAlertDialog:n,...t}=e,o=ue(n);return l.jsx(ks,{...o,...t})};Ko.displayName=Wi;var Vi="AlertDialogOverlay",Wo=s.forwardRef((e,n)=>{const{__scopeAlertDialog:t,...o}=e,r=ue(t);return l.jsx(Ls,{...r,...o,ref:n})});Wo.displayName=Vi;var Se="AlertDialogContent",[zi,Yi]=Gi(Se),Xi=ln("AlertDialogContent"),Vo=s.forwardRef((e,n)=>{const{__scopeAlertDialog:t,children:o,...r}=e,a=ue(t),i=s.useRef(null),c=M(n,i),u=s.useRef(null);return l.jsx(Ms,{contentName:Se,titleName:zo,docsSlug:"alert-dialog",children:l.jsx(zi,{scope:t,cancelRef:u,children:l.jsxs(Fs,{role:"alertdialog",...a,...r,ref:c,onOpenAutoFocus:E(r.onOpenAutoFocus,d=>{d.preventDefault(),u.current?.focus({preventScroll:!0})}),onPointerDownOutside:d=>d.preventDefault(),onInteractOutside:d=>d.preventDefault(),children:[l.jsx(Xi,{children:o}),l.jsx(Zi,{contentRef:i})]})})})});Vo.displayName=Se;var zo="AlertDialogTitle",Yo=s.forwardRef((e,n)=>{const{__scopeAlertDialog:t,...o}=e,r=ue(t);return l.jsx($s,{...r,...o,ref:n})});Yo.displayName=zo;var Xo="AlertDialogDescription",qo=s.forwardRef((e,n)=>{const{__scopeAlertDialog:t,...o}=e,r=ue(t);return l.jsx(Bs,{...r,...o,ref:n})});qo.displayName=Xo;var qi="AlertDialogAction",Zo=s.forwardRef((e,n)=>{const{__scopeAlertDialog:t,...o}=e,r=ue(t);return l.jsx(Nn,{...r,...o,ref:n})});Zo.displayName=qi;var Jo="AlertDialogCancel",Qo=s.forwardRef((e,n)=>{const{__scopeAlertDialog:t,...o}=e,{cancelRef:r}=Yi(Jo,t),a=ue(t),i=M(n,r);return l.jsx(Nn,{...a,...o,ref:i})});Qo.displayName=Jo;var Zi=({contentRef:e})=>{const n=`\`${Se}\` requires a description for the component to be accessible for screen reader users. - -You can add a description to the \`${Se}\` by passing a \`${Xo}\` component as a child, which also benefits sighted users by adding visible context to the dialog. - -Alternatively, you can use your own component as a description by assigning it an \`id\` and passing the same value to the \`aria-describedby\` prop in \`${Se}\`. If the description is confusing or duplicative for sighted users, you can use the \`@radix-ui/react-visually-hidden\` primitive as a wrapper around your description component. - -For more information, see https://radix-ui.com/primitives/docs/components/alert-dialog`;return s.useEffect(()=>{document.getElementById(e.current?.getAttribute("aria-describedby"))||console.warn(n)},[n,e]),null},Yc=Ho,Xc=Go,qc=Ko,Zc=Wo,Jc=Vo,Qc=Zo,el=Qo,tl=Yo,nl=qo,Ji="Label",er=s.forwardRef((e,n)=>l.jsx(_.label,{...e,ref:n,onMouseDown:t=>{t.target.closest("button, input, select, textarea")||(e.onMouseDown?.(t),!t.defaultPrevented&&t.detail>1&&t.preventDefault())}}));er.displayName=Ji;var ol=er;function sn(e,[n,t]){return Math.min(t,Math.max(n,e))}function tr(e){const n=s.useRef({value:e,previous:e});return s.useMemo(()=>(n.current.value!==e&&(n.current.previous=n.current.value,n.current.value=e),n.current.previous),[e])}var Qi=[" ","Enter","ArrowUp","ArrowDown"],ec=[" ","Enter"],we="Select",[Qe,et,tc]=Ft(we),[_e,rl]=se(we,[tc,Pe]),tt=Pe(),[nc,me]=_e(we),[oc,rc]=_e(we),nr=e=>{const{__scopeSelect:n,children:t,open:o,defaultOpen:r,onOpenChange:a,value:i,defaultValue:c,onValueChange:u,dir:d,name:f,autoComplete:p,disabled:v,required:x,form:C}=e,m=tt(n),[h,w]=s.useState(null),[g,y]=s.useState(null),[S,T]=s.useState(!1),A=$t(d),[P,I]=pe({prop:o,defaultProp:r??!1,onChange:a,caller:we}),[F,L]=pe({prop:i,defaultProp:c,onChange:u,caller:we}),B=s.useRef(null),H=h?C||!!h.closest("form"):!0,[U,W]=s.useState(new Set),V=Array.from(U).map(N=>N.props.value).join(";");return l.jsx(It,{...m,children:l.jsxs(nc,{required:x,scope:n,trigger:h,onTriggerChange:w,valueNode:g,onValueNodeChange:y,valueNodeHasChildren:S,onValueNodeHasChildrenChange:T,contentId:re(),value:F,onValueChange:L,open:P,onOpenChange:I,dir:A,triggerPointerDownPosRef:B,disabled:v,children:[l.jsx(Qe.Provider,{scope:n,children:l.jsx(oc,{scope:e.__scopeSelect,onNativeOptionAdd:s.useCallback(N=>{W(G=>new Set(G).add(N))},[]),onNativeOptionRemove:s.useCallback(N=>{W(G=>{const k=new Set(G);return k.delete(N),k})},[]),children:t})}),H?l.jsxs(Sr,{"aria-hidden":!0,required:x,tabIndex:-1,name:f,autoComplete:p,value:F,onChange:N=>L(N.target.value),disabled:v,form:C,children:[F===void 0?l.jsx("option",{value:""}):null,Array.from(U)]},V):null]})})};nr.displayName=we;var or="SelectTrigger",rr=s.forwardRef((e,n)=>{const{__scopeSelect:t,disabled:o=!1,...r}=e,a=tt(t),i=me(or,t),c=i.disabled||o,u=M(n,i.onTriggerChange),d=et(t),f=s.useRef("touch"),[p,v,x]=Pr(m=>{const h=d().filter(y=>!y.disabled),w=h.find(y=>y.value===i.value),g=_r(h,m,w);g!==void 0&&i.onValueChange(g.value)}),C=m=>{c||(i.onOpenChange(!0),x()),m&&(i.triggerPointerDownPosRef.current={x:Math.round(m.pageX),y:Math.round(m.pageY)})};return l.jsx(Mt,{asChild:!0,...a,children:l.jsx(_.button,{type:"button",role:"combobox","aria-controls":i.contentId,"aria-expanded":i.open,"aria-required":i.required,"aria-autocomplete":"none",dir:i.dir,"data-state":i.open?"open":"closed",disabled:c,"data-disabled":c?"":void 0,"data-placeholder":Rr(i.value)?"":void 0,...r,ref:u,onClick:E(r.onClick,m=>{m.currentTarget.focus(),f.current!=="mouse"&&C(m)}),onPointerDown:E(r.onPointerDown,m=>{f.current=m.pointerType;const h=m.target;h.hasPointerCapture(m.pointerId)&&h.releasePointerCapture(m.pointerId),m.button===0&&m.ctrlKey===!1&&m.pointerType==="mouse"&&(C(m),m.preventDefault())}),onKeyDown:E(r.onKeyDown,m=>{const h=p.current!=="";!(m.ctrlKey||m.altKey||m.metaKey)&&m.key.length===1&&v(m.key),!(h&&m.key===" ")&&Qi.includes(m.key)&&(C(),m.preventDefault())})})})});rr.displayName=or;var sr="SelectValue",ar=s.forwardRef((e,n)=>{const{__scopeSelect:t,className:o,style:r,children:a,placeholder:i="",...c}=e,u=me(sr,t),{onValueNodeHasChildrenChange:d}=u,f=a!==void 0,p=M(n,u.onValueNodeChange);return K(()=>{d(f)},[d,f]),l.jsx(_.span,{...c,ref:p,style:{pointerEvents:"none"},children:Rr(u.value)?l.jsx(l.Fragment,{children:i}):a})});ar.displayName=sr;var sc="SelectIcon",ir=s.forwardRef((e,n)=>{const{__scopeSelect:t,children:o,...r}=e;return l.jsx(_.span,{"aria-hidden":!0,...r,ref:n,children:o||"▼"})});ir.displayName=sc;var ac="SelectPortal",cr=e=>l.jsx(Oe,{asChild:!0,...e});cr.displayName=ac;var ye="SelectContent",lr=s.forwardRef((e,n)=>{const t=me(ye,e.__scopeSelect),[o,r]=s.useState();if(K(()=>{r(new DocumentFragment)},[]),!t.open){const a=o;return a?wt.createPortal(l.jsx(ur,{scope:e.__scopeSelect,children:l.jsx(Qe.Slot,{scope:e.__scopeSelect,children:l.jsx("div",{children:e.children})})}),a):null}return l.jsx(dr,{...e,ref:n})});lr.displayName=ye;var oe=10,[ur,ve]=_e(ye),ic="SelectContentImpl",cc=xe("SelectContent.RemoveScroll"),dr=s.forwardRef((e,n)=>{const{__scopeSelect:t,position:o="item-aligned",onCloseAutoFocus:r,onEscapeKeyDown:a,onPointerDownOutside:i,side:c,sideOffset:u,align:d,alignOffset:f,arrowPadding:p,collisionBoundary:v,collisionPadding:x,sticky:C,hideWhenDetached:m,avoidCollisions:h,...w}=e,g=me(ye,t),[y,S]=s.useState(null),[T,A]=s.useState(null),P=M(n,b=>S(b)),[I,F]=s.useState(null),[L,B]=s.useState(null),H=et(t),[U,W]=s.useState(!1),V=s.useRef(!1);s.useEffect(()=>{if(y)return yt(y)},[y]),bt();const N=s.useCallback(b=>{const[D,...Y]=H().map($=>$.ref.current),[O]=Y.slice(-1),j=document.activeElement;for(const $ of b)if($===j||($?.scrollIntoView({block:"nearest"}),$===D&&T&&(T.scrollTop=0),$===O&&T&&(T.scrollTop=T.scrollHeight),$?.focus(),document.activeElement!==j))return},[H,T]),G=s.useCallback(()=>N([I,y]),[N,I,y]);s.useEffect(()=>{U&&G()},[U,G]);const{onOpenChange:k,triggerPointerDownPosRef:R}=g;s.useEffect(()=>{if(y){let b={x:0,y:0};const D=O=>{b={x:Math.abs(Math.round(O.pageX)-(R.current?.x??0)),y:Math.abs(Math.round(O.pageY)-(R.current?.y??0))}},Y=O=>{b.x<=10&&b.y<=10?O.preventDefault():y.contains(O.target)||k(!1),document.removeEventListener("pointermove",D),R.current=null};return R.current!==null&&(document.addEventListener("pointermove",D),document.addEventListener("pointerup",Y,{capture:!0,once:!0})),()=>{document.removeEventListener("pointermove",D),document.removeEventListener("pointerup",Y,{capture:!0})}}},[y,k,R]),s.useEffect(()=>{const b=()=>k(!1);return window.addEventListener("blur",b),window.addEventListener("resize",b),()=>{window.removeEventListener("blur",b),window.removeEventListener("resize",b)}},[k]);const[Z,z]=Pr(b=>{const D=H().filter(j=>!j.disabled),Y=D.find(j=>j.ref.current===document.activeElement),O=_r(D,b,Y);O&&setTimeout(()=>O.ref.current.focus())}),ne=s.useCallback((b,D,Y)=>{const O=!V.current&&!Y;(g.value!==void 0&&g.value===D||O)&&(F(b),O&&(V.current=!0))},[g.value]),ce=s.useCallback(()=>y?.focus(),[y]),J=s.useCallback((b,D,Y)=>{const O=!V.current&&!Y;(g.value!==void 0&&g.value===D||O)&&B(b)},[g.value]),le=o==="popper"?vt:fr,q=le===vt?{side:c,sideOffset:u,align:d,alignOffset:f,arrowPadding:p,collisionBoundary:v,collisionPadding:x,sticky:C,hideWhenDetached:m,avoidCollisions:h}:{};return l.jsx(ur,{scope:t,content:y,viewport:T,onViewportChange:A,itemRefCallback:ne,selectedItem:I,onItemLeave:ce,itemTextRefCallback:J,focusSelectedItem:G,selectedItemText:L,position:o,isPositioned:U,searchRef:Z,children:l.jsx(Et,{as:cc,allowPinchZoom:!0,children:l.jsx(We,{asChild:!0,trapped:g.open,onMountAutoFocus:b=>{b.preventDefault()},onUnmountAutoFocus:E(r,b=>{g.trigger?.focus({preventScroll:!0}),b.preventDefault()}),children:l.jsx(De,{asChild:!0,disableOutsidePointerEvents:!0,onEscapeKeyDown:a,onPointerDownOutside:i,onFocusOutside:b=>b.preventDefault(),onDismiss:()=>g.onOpenChange(!1),children:l.jsx(le,{role:"listbox",id:g.contentId,"data-state":g.open?"open":"closed",dir:g.dir,onContextMenu:b=>b.preventDefault(),...w,...q,onPlaced:()=>W(!0),ref:P,style:{display:"flex",flexDirection:"column",outline:"none",...w.style},onKeyDown:E(w.onKeyDown,b=>{const D=b.ctrlKey||b.altKey||b.metaKey;if(b.key==="Tab"&&b.preventDefault(),!D&&b.key.length===1&&z(b.key),["ArrowUp","ArrowDown","Home","End"].includes(b.key)){let O=H().filter(j=>!j.disabled).map(j=>j.ref.current);if(["ArrowUp","End"].includes(b.key)&&(O=O.slice().reverse()),["ArrowUp","ArrowDown"].includes(b.key)){const j=b.target,$=O.indexOf(j);O=O.slice($+1)}setTimeout(()=>N(O)),b.preventDefault()}})})})})})})});dr.displayName=ic;var lc="SelectItemAlignedPosition",fr=s.forwardRef((e,n)=>{const{__scopeSelect:t,onPlaced:o,...r}=e,a=me(ye,t),i=ve(ye,t),[c,u]=s.useState(null),[d,f]=s.useState(null),p=M(n,P=>f(P)),v=et(t),x=s.useRef(!1),C=s.useRef(!0),{viewport:m,selectedItem:h,selectedItemText:w,focusSelectedItem:g}=i,y=s.useCallback(()=>{if(a.trigger&&a.valueNode&&c&&d&&m&&h&&w){const P=a.trigger.getBoundingClientRect(),I=d.getBoundingClientRect(),F=a.valueNode.getBoundingClientRect(),L=w.getBoundingClientRect();if(a.dir!=="rtl"){const j=L.left-I.left,$=F.left-j,Q=P.left-$,he=P.width+Q,ot=Math.max(he,I.width),rt=window.innerWidth-oe,st=sn($,[oe,Math.max(oe,rt-ot)]);c.style.minWidth=he+"px",c.style.left=st+"px"}else{const j=I.right-L.right,$=window.innerWidth-F.right-j,Q=window.innerWidth-P.right-$,he=P.width+Q,ot=Math.max(he,I.width),rt=window.innerWidth-oe,st=sn($,[oe,Math.max(oe,rt-ot)]);c.style.minWidth=he+"px",c.style.right=st+"px"}const B=v(),H=window.innerHeight-oe*2,U=m.scrollHeight,W=window.getComputedStyle(d),V=parseInt(W.borderTopWidth,10),N=parseInt(W.paddingTop,10),G=parseInt(W.borderBottomWidth,10),k=parseInt(W.paddingBottom,10),R=V+N+U+k+G,Z=Math.min(h.offsetHeight*5,R),z=window.getComputedStyle(m),ne=parseInt(z.paddingTop,10),ce=parseInt(z.paddingBottom,10),J=P.top+P.height/2-oe,le=H-J,q=h.offsetHeight/2,b=h.offsetTop+q,D=V+N+b,Y=R-D;if(D<=J){const j=B.length>0&&h===B[B.length-1].ref.current;c.style.bottom="0px";const $=d.clientHeight-m.offsetTop-m.offsetHeight,Q=Math.max(le,q+(j?ce:0)+$+G),he=D+Q;c.style.height=he+"px"}else{const j=B.length>0&&h===B[0].ref.current;c.style.top="0px";const Q=Math.max(J,V+m.offsetTop+(j?ne:0)+q)+Y;c.style.height=Q+"px",m.scrollTop=D-J+m.offsetTop}c.style.margin=`${oe}px 0`,c.style.minHeight=Z+"px",c.style.maxHeight=H+"px",o?.(),requestAnimationFrame(()=>x.current=!0)}},[v,a.trigger,a.valueNode,c,d,m,h,w,a.dir,o]);K(()=>y(),[y]);const[S,T]=s.useState();K(()=>{d&&T(window.getComputedStyle(d).zIndex)},[d]);const A=s.useCallback(P=>{P&&C.current===!0&&(y(),g?.(),C.current=!1)},[y,g]);return l.jsx(dc,{scope:t,contentWrapper:c,shouldExpandOnScrollRef:x,onScrollButtonChange:A,children:l.jsx("div",{ref:u,style:{display:"flex",flexDirection:"column",position:"fixed",zIndex:S},children:l.jsx(_.div,{...r,ref:p,style:{boxSizing:"border-box",maxHeight:"100%",...r.style}})})})});fr.displayName=lc;var uc="SelectPopperPosition",vt=s.forwardRef((e,n)=>{const{__scopeSelect:t,align:o="start",collisionPadding:r=oe,...a}=e,i=tt(t);return l.jsx(At,{...i,...a,ref:n,align:o,collisionPadding:r,style:{boxSizing:"border-box",...a.style,"--radix-select-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-select-content-available-width":"var(--radix-popper-available-width)","--radix-select-content-available-height":"var(--radix-popper-available-height)","--radix-select-trigger-width":"var(--radix-popper-anchor-width)","--radix-select-trigger-height":"var(--radix-popper-anchor-height)"}})});vt.displayName=uc;var[dc,zt]=_e(ye,{}),ht="SelectViewport",pr=s.forwardRef((e,n)=>{const{__scopeSelect:t,nonce:o,...r}=e,a=ve(ht,t),i=zt(ht,t),c=M(n,a.onViewportChange),u=s.useRef(0);return l.jsxs(l.Fragment,{children:[l.jsx("style",{dangerouslySetInnerHTML:{__html:"[data-radix-select-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-select-viewport]::-webkit-scrollbar{display:none}"},nonce:o}),l.jsx(Qe.Slot,{scope:t,children:l.jsx(_.div,{"data-radix-select-viewport":"",role:"presentation",...r,ref:c,style:{position:"relative",flex:1,overflow:"hidden auto",...r.style},onScroll:E(r.onScroll,d=>{const f=d.currentTarget,{contentWrapper:p,shouldExpandOnScrollRef:v}=i;if(v?.current&&p){const x=Math.abs(u.current-f.scrollTop);if(x>0){const C=window.innerHeight-oe*2,m=parseFloat(p.style.minHeight),h=parseFloat(p.style.height),w=Math.max(m,h);if(w0?S:0,p.style.justifyContent="flex-end")}}}u.current=f.scrollTop})})})]})});pr.displayName=ht;var mr="SelectGroup",[fc,pc]=_e(mr),mc=s.forwardRef((e,n)=>{const{__scopeSelect:t,...o}=e,r=re();return l.jsx(fc,{scope:t,id:r,children:l.jsx(_.div,{role:"group","aria-labelledby":r,...o,ref:n})})});mc.displayName=mr;var vr="SelectLabel",vc=s.forwardRef((e,n)=>{const{__scopeSelect:t,...o}=e,r=pc(vr,t);return l.jsx(_.div,{id:r.id,...o,ref:n})});vc.displayName=vr;var Ge="SelectItem",[hc,hr]=_e(Ge),gr=s.forwardRef((e,n)=>{const{__scopeSelect:t,value:o,disabled:r=!1,textValue:a,...i}=e,c=me(Ge,t),u=ve(Ge,t),d=c.value===o,[f,p]=s.useState(a??""),[v,x]=s.useState(!1),C=M(n,g=>u.itemRefCallback?.(g,o,r)),m=re(),h=s.useRef("touch"),w=()=>{r||(c.onValueChange(o),c.onOpenChange(!1))};if(o==="")throw new Error("A must have a value prop that is not an empty string. This is because the Select value can be set to an empty string to clear the selection and show the placeholder.");return l.jsx(hc,{scope:t,value:o,disabled:r,textId:m,isSelected:d,onItemTextChange:s.useCallback(g=>{p(y=>y||(g?.textContent??"").trim())},[]),children:l.jsx(Qe.ItemSlot,{scope:t,value:o,disabled:r,textValue:f,children:l.jsx(_.div,{role:"option","aria-labelledby":m,"data-highlighted":v?"":void 0,"aria-selected":d&&v,"data-state":d?"checked":"unchecked","aria-disabled":r||void 0,"data-disabled":r?"":void 0,tabIndex:r?void 0:-1,...i,ref:C,onFocus:E(i.onFocus,()=>x(!0)),onBlur:E(i.onBlur,()=>x(!1)),onClick:E(i.onClick,()=>{h.current!=="mouse"&&w()}),onPointerUp:E(i.onPointerUp,()=>{h.current==="mouse"&&w()}),onPointerDown:E(i.onPointerDown,g=>{h.current=g.pointerType}),onPointerMove:E(i.onPointerMove,g=>{h.current=g.pointerType,r?u.onItemLeave?.():h.current==="mouse"&&g.currentTarget.focus({preventScroll:!0})}),onPointerLeave:E(i.onPointerLeave,g=>{g.currentTarget===document.activeElement&&u.onItemLeave?.()}),onKeyDown:E(i.onKeyDown,g=>{u.searchRef?.current!==""&&g.key===" "||(ec.includes(g.key)&&w(),g.key===" "&&g.preventDefault())})})})})});gr.displayName=Ge;var Ie="SelectItemText",xr=s.forwardRef((e,n)=>{const{__scopeSelect:t,className:o,style:r,...a}=e,i=me(Ie,t),c=ve(Ie,t),u=hr(Ie,t),d=rc(Ie,t),[f,p]=s.useState(null),v=M(n,w=>p(w),u.onItemTextChange,w=>c.itemTextRefCallback?.(w,u.value,u.disabled)),x=f?.textContent,C=s.useMemo(()=>l.jsx("option",{value:u.value,disabled:u.disabled,children:x},u.value),[u.disabled,u.value,x]),{onNativeOptionAdd:m,onNativeOptionRemove:h}=d;return K(()=>(m(C),()=>h(C)),[m,h,C]),l.jsxs(l.Fragment,{children:[l.jsx(_.span,{id:u.textId,...a,ref:v}),u.isSelected&&i.valueNode&&!i.valueNodeHasChildren?wt.createPortal(a.children,i.valueNode):null]})});xr.displayName=Ie;var Cr="SelectItemIndicator",wr=s.forwardRef((e,n)=>{const{__scopeSelect:t,...o}=e;return hr(Cr,t).isSelected?l.jsx(_.span,{"aria-hidden":!0,...o,ref:n}):null});wr.displayName=Cr;var gt="SelectScrollUpButton",yr=s.forwardRef((e,n)=>{const t=ve(gt,e.__scopeSelect),o=zt(gt,e.__scopeSelect),[r,a]=s.useState(!1),i=M(n,o.onScrollButtonChange);return K(()=>{if(t.viewport&&t.isPositioned){let c=function(){const d=u.scrollTop>0;a(d)};const u=t.viewport;return c(),u.addEventListener("scroll",c),()=>u.removeEventListener("scroll",c)}},[t.viewport,t.isPositioned]),r?l.jsx(br,{...e,ref:i,onAutoScroll:()=>{const{viewport:c,selectedItem:u}=t;c&&u&&(c.scrollTop=c.scrollTop-u.offsetHeight)}}):null});yr.displayName=gt;var xt="SelectScrollDownButton",Er=s.forwardRef((e,n)=>{const t=ve(xt,e.__scopeSelect),o=zt(xt,e.__scopeSelect),[r,a]=s.useState(!1),i=M(n,o.onScrollButtonChange);return K(()=>{if(t.viewport&&t.isPositioned){let c=function(){const d=u.scrollHeight-u.clientHeight,f=Math.ceil(u.scrollTop)u.removeEventListener("scroll",c)}},[t.viewport,t.isPositioned]),r?l.jsx(br,{...e,ref:i,onAutoScroll:()=>{const{viewport:c,selectedItem:u}=t;c&&u&&(c.scrollTop=c.scrollTop+u.offsetHeight)}}):null});Er.displayName=xt;var br=s.forwardRef((e,n)=>{const{__scopeSelect:t,onAutoScroll:o,...r}=e,a=ve("SelectScrollButton",t),i=s.useRef(null),c=et(t),u=s.useCallback(()=>{i.current!==null&&(window.clearInterval(i.current),i.current=null)},[]);return s.useEffect(()=>()=>u(),[u]),K(()=>{c().find(f=>f.ref.current===document.activeElement)?.ref.current?.scrollIntoView({block:"nearest"})},[c]),l.jsx(_.div,{"aria-hidden":!0,...r,ref:n,style:{flexShrink:0,...r.style},onPointerDown:E(r.onPointerDown,()=>{i.current===null&&(i.current=window.setInterval(o,50))}),onPointerMove:E(r.onPointerMove,()=>{a.onItemLeave?.(),i.current===null&&(i.current=window.setInterval(o,50))}),onPointerLeave:E(r.onPointerLeave,()=>{u()})})}),gc="SelectSeparator",xc=s.forwardRef((e,n)=>{const{__scopeSelect:t,...o}=e;return l.jsx(_.div,{"aria-hidden":!0,...o,ref:n})});xc.displayName=gc;var Ct="SelectArrow",Cc=s.forwardRef((e,n)=>{const{__scopeSelect:t,...o}=e,r=tt(t),a=me(Ct,t),i=ve(Ct,t);return a.open&&i.position==="popper"?l.jsx(Nt,{...r,...o,ref:n}):null});Cc.displayName=Ct;var wc="SelectBubbleInput",Sr=s.forwardRef(({__scopeSelect:e,value:n,...t},o)=>{const r=s.useRef(null),a=M(o,r),i=tr(n);return s.useEffect(()=>{const c=r.current;if(!c)return;const u=window.HTMLSelectElement.prototype,f=Object.getOwnPropertyDescriptor(u,"value").set;if(i!==n&&f){const p=new Event("change",{bubbles:!0});f.call(c,n),c.dispatchEvent(p)}},[i,n]),l.jsx(_.select,{...t,style:{...Kn,...t.style},ref:a,defaultValue:n})});Sr.displayName=wc;function Rr(e){return e===""||e===void 0}function Pr(e){const n=ie(e),t=s.useRef(""),o=s.useRef(0),r=s.useCallback(i=>{const c=t.current+i;n(c),(function u(d){t.current=d,window.clearTimeout(o.current),d!==""&&(o.current=window.setTimeout(()=>u(""),1e3))})(c)},[n]),a=s.useCallback(()=>{t.current="",window.clearTimeout(o.current)},[]);return s.useEffect(()=>()=>window.clearTimeout(o.current),[]),[t,r,a]}function _r(e,n,t){const r=n.length>1&&Array.from(n).every(d=>d===n[0])?n[0]:n,a=t?e.indexOf(t):-1;let i=yc(e,Math.max(a,0));r.length===1&&(i=i.filter(d=>d!==t));const u=i.find(d=>d.textValue.toLowerCase().startsWith(r.toLowerCase()));return u!==t?u:void 0}function yc(e,n){return e.map((t,o)=>e[(n+o)%e.length])}var sl=nr,al=rr,il=ar,cl=ir,ll=cr,ul=lr,dl=pr,fl=gr,pl=xr,ml=wr,vl=yr,hl=Er,nt="Checkbox",[Ec,gl]=se(nt),[bc,Yt]=Ec(nt);function Sc(e){const{__scopeCheckbox:n,checked:t,children:o,defaultChecked:r,disabled:a,form:i,name:c,onCheckedChange:u,required:d,value:f="on",internal_do_not_use_render:p}=e,[v,x]=pe({prop:t,defaultProp:r??!1,onChange:u,caller:nt}),[C,m]=s.useState(null),[h,w]=s.useState(null),g=s.useRef(!1),y=C?!!i||!!C.closest("form"):!0,S={checked:v,disabled:a,setChecked:x,control:C,setControl:m,name:c,form:i,value:f,hasConsumerStoppedPropagationRef:g,required:d,defaultChecked:fe(r)?!1:r,isFormControl:y,bubbleInput:h,setBubbleInput:w};return l.jsx(bc,{scope:n,...S,children:_c(p)?p(S):o})}var Tr="CheckboxTrigger",Ir=s.forwardRef(({__scopeCheckbox:e,onKeyDown:n,onClick:t,...o},r)=>{const{control:a,value:i,disabled:c,checked:u,required:d,setControl:f,setChecked:p,hasConsumerStoppedPropagationRef:v,isFormControl:x,bubbleInput:C}=Yt(Tr,e),m=M(r,f),h=s.useRef(u);return s.useEffect(()=>{const w=a?.form;if(w){const g=()=>p(h.current);return w.addEventListener("reset",g),()=>w.removeEventListener("reset",g)}},[a,p]),l.jsx(_.button,{type:"button",role:"checkbox","aria-checked":fe(u)?"mixed":u,"aria-required":d,"data-state":Dr(u),"data-disabled":c?"":void 0,disabled:c,value:i,...o,ref:m,onKeyDown:E(n,w=>{w.key==="Enter"&&w.preventDefault()}),onClick:E(t,w=>{p(g=>fe(g)?!0:!g),C&&x&&(v.current=w.isPropagationStopped(),v.current||w.stopPropagation())})})});Ir.displayName=Tr;var Rc=s.forwardRef((e,n)=>{const{__scopeCheckbox:t,name:o,checked:r,defaultChecked:a,required:i,disabled:c,value:u,onCheckedChange:d,form:f,...p}=e;return l.jsx(Sc,{__scopeCheckbox:t,checked:r,defaultChecked:a,disabled:c,required:i,onCheckedChange:d,name:o,form:f,value:u,internal_do_not_use_render:({isFormControl:v})=>l.jsxs(l.Fragment,{children:[l.jsx(Ir,{...p,ref:n,__scopeCheckbox:t}),v&&l.jsx(Nr,{__scopeCheckbox:t})]})})});Rc.displayName=nt;var Mr="CheckboxIndicator",Pc=s.forwardRef((e,n)=>{const{__scopeCheckbox:t,forceMount:o,...r}=e,a=Yt(Mr,t);return l.jsx(te,{present:o||fe(a.checked)||a.checked===!0,children:l.jsx(_.span,{"data-state":Dr(a.checked),"data-disabled":a.disabled?"":void 0,...r,ref:n,style:{pointerEvents:"none",...e.style}})})});Pc.displayName=Mr;var Ar="CheckboxBubbleInput",Nr=s.forwardRef(({__scopeCheckbox:e,...n},t)=>{const{control:o,hasConsumerStoppedPropagationRef:r,checked:a,defaultChecked:i,required:c,disabled:u,name:d,value:f,form:p,bubbleInput:v,setBubbleInput:x}=Yt(Ar,e),C=M(t,x),m=tr(a),h=On(o);s.useEffect(()=>{const g=v;if(!g)return;const y=window.HTMLInputElement.prototype,T=Object.getOwnPropertyDescriptor(y,"checked").set,A=!r.current;if(m!==a&&T){const P=new Event("click",{bubbles:A});g.indeterminate=fe(a),T.call(g,fe(a)?!1:a),g.dispatchEvent(P)}},[v,m,a,r]);const w=s.useRef(fe(a)?!1:a);return l.jsx(_.input,{type:"checkbox","aria-hidden":!0,defaultChecked:i??w.current,required:c,disabled:u,name:d,value:f,form:p,...n,tabIndex:-1,ref:C,style:{...n.style,...h,position:"absolute",pointerEvents:"none",opacity:0,margin:0,transform:"translateX(-100%)"}})});Nr.displayName=Ar;function _c(e){return typeof e=="function"}function fe(e){return e==="indeterminate"}function Dr(e){return fe(e)?"indeterminate":e?"checked":"unchecked"}export{Lc as A,al as B,Fs as C,Bs as D,cl as E,ll as F,ul as G,dl as H,Vc as I,fl as J,ml as K,pl as L,vl as M,hl as N,Ls as O,ks as P,Rc as Q,Mc as R,Ic as S,$s as T,Pc as U,il as V,Os as a,Nn as b,Nc as c,Dc as d,Oc as e,jc as f,kc as g,$c as h,ma as i,va as j,Hc as k,Gc as l,Kc as m,Wc as n,Yc as o,Xc as p,Jc as q,tl as r,nl as s,el as t,Qc as u,qc as v,Zc as w,js as x,ol as y,sl as z}; diff --git a/internal/ui/dist/assets/router-CANfZtzM.js b/internal/ui/dist/assets/router-CANfZtzM.js deleted file mode 100644 index 918aad1..0000000 --- a/internal/ui/dist/assets/router-CANfZtzM.js +++ /dev/null @@ -1,12 +0,0 @@ -import{r as i}from"./vendor-DvippHRz.js";function Zt(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}/** - * react-router v7.8.2 - * - * Copyright (c) Remix Software Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE.md file in the root directory of this source tree. - * - * @license MIT - */var ue="popstate";function ke(e={}){function t(n,a){let{pathname:o,search:l,hash:s}=n.location;return G("",{pathname:o,search:l,hash:s},a.state&&a.state.usr||null,a.state&&a.state.key||"default")}function r(n,a){return typeof a=="string"?a:B(a)}return Fe(t,r,null,e)}function E(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function x(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function $e(){return Math.random().toString(36).substring(2,10)}function se(e,t){return{usr:e.state,key:e.key,idx:t}}function G(e,t,r=null,n){return{pathname:typeof e=="string"?e:e.pathname,search:"",hash:"",...typeof t=="string"?I(t):t,state:r,key:t&&t.key||n||$e()}}function B({pathname:e="/",search:t="",hash:r=""}){return t&&t!=="?"&&(e+=t.charAt(0)==="?"?t:"?"+t),r&&r!=="#"&&(e+=r.charAt(0)==="#"?r:"#"+r),e}function I(e){let t={};if(e){let r=e.indexOf("#");r>=0&&(t.hash=e.substring(r),e=e.substring(0,r));let n=e.indexOf("?");n>=0&&(t.search=e.substring(n),e=e.substring(0,n)),e&&(t.pathname=e)}return t}function Fe(e,t,r,n={}){let{window:a=document.defaultView,v5Compat:o=!1}=n,l=a.history,s="POP",u=null,c=f();c==null&&(c=0,l.replaceState({...l.state,idx:c},""));function f(){return(l.state||{idx:null}).idx}function h(){s="POP";let p=f(),m=p==null?null:p-c;c=p,u&&u({action:s,location:w.location,delta:m})}function y(p,m){s="PUSH";let d=G(w.location,p,m);c=f()+1;let R=se(d,c),C=w.createHref(d);try{l.pushState(R,"",C)}catch(b){if(b instanceof DOMException&&b.name==="DataCloneError")throw b;a.location.assign(C)}o&&u&&u({action:s,location:w.location,delta:1})}function g(p,m){s="REPLACE";let d=G(w.location,p,m);c=f();let R=se(d,c),C=w.createHref(d);l.replaceState(R,"",C),o&&u&&u({action:s,location:w.location,delta:0})}function v(p){return Ne(p)}let w={get action(){return s},get location(){return e(a,l)},listen(p){if(u)throw new Error("A history only accepts one active listener");return a.addEventListener(ue,h),u=p,()=>{a.removeEventListener(ue,h),u=null}},createHref(p){return t(a,p)},createURL:v,encodeLocation(p){let m=v(p);return{pathname:m.pathname,search:m.search,hash:m.hash}},push:y,replace:g,go(p){return l.go(p)}};return w}function Ne(e,t=!1){let r="http://localhost";typeof window<"u"&&(r=window.location.origin!=="null"?window.location.origin:window.location.href),E(r,"No window.location.(origin|href) available to create URL");let n=typeof e=="string"?e:B(e);return n=n.replace(/ $/,"%20"),!t&&n.startsWith("//")&&(n=r+n),new URL(n,r)}function de(e,t,r="/"){return Ie(e,t,r,!1)}function Ie(e,t,r,n){let a=typeof t=="string"?I(t):t,o=$(a.pathname||"/",r);if(o==null)return null;let l=pe(e);Te(l);let s=null;for(let u=0;s==null&&u{let f={relativePath:c===void 0?l.path||"":c,caseSensitive:l.caseSensitive===!0,childrenIndex:s,route:l};if(f.relativePath.startsWith("/")){if(!f.relativePath.startsWith(n)&&u)return;E(f.relativePath.startsWith(n),`Absolute route path "${f.relativePath}" nested under path "${n}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),f.relativePath=f.relativePath.slice(n.length)}let h=k([n,f.relativePath]),y=r.concat(f);l.children&&l.children.length>0&&(E(l.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${h}".`),pe(l.children,t,y,h,u)),!(l.path==null&&!l.index)&&t.push({path:h,score:We(h,l.index),routesMeta:y})};return e.forEach((l,s)=>{if(l.path===""||!l.path?.includes("?"))o(l,s);else for(let u of me(l.path))o(l,s,!0,u)}),t}function me(e){let t=e.split("/");if(t.length===0)return[];let[r,...n]=t,a=r.endsWith("?"),o=r.replace(/\?$/,"");if(n.length===0)return a?[o,""]:[o];let l=me(n.join("/")),s=[];return s.push(...l.map(u=>u===""?o:[o,u].join("/"))),a&&s.push(...l),s.map(u=>e.startsWith("/")&&u===""?"/":u)}function Te(e){e.sort((t,r)=>t.score!==r.score?r.score-t.score:He(t.routesMeta.map(n=>n.childrenIndex),r.routesMeta.map(n=>n.childrenIndex)))}var De=/^:[\w-]+$/,Me=3,Oe=2,Be=1,Ae=10,Ue=-2,ce=e=>e==="*";function We(e,t){let r=e.split("/"),n=r.length;return r.some(ce)&&(n+=Ue),t&&(n+=Oe),r.filter(a=>!ce(a)).reduce((a,o)=>a+(De.test(o)?Me:o===""?Be:Ae),n)}function He(e,t){return e.length===t.length&&e.slice(0,-1).every((n,a)=>n===t[a])?e[e.length-1]-t[t.length-1]:0}function _e(e,t,r=!1){let{routesMeta:n}=e,a={},o="/",l=[];for(let s=0;s{if(f==="*"){let v=s[y]||"";l=o.slice(0,o.length-v.length).replace(/(.)\/+$/,"$1")}const g=s[y];return h&&!g?c[f]=void 0:c[f]=(g||"").replace(/%2F/g,"/"),c},{}),pathname:o,pathnameBase:l,pattern:e}}function je(e,t=!1,r=!0){x(e==="*"||!e.endsWith("*")||e.endsWith("/*"),`Route path "${e}" will be treated as if it were "${e.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${e.replace(/\*$/,"/*")}".`);let n=[],a="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(l,s,u)=>(n.push({paramName:s,isOptional:u!=null}),u?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return e.endsWith("*")?(n.push({paramName:"*"}),a+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):r?a+="\\/*$":e!==""&&e!=="/"&&(a+="(?:(?=\\/|$))"),[new RegExp(a,t?void 0:"i"),n]}function Ve(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return x(!1,`The URL path "${e}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${t}).`),e}}function $(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let r=t.endsWith("/")?t.length-1:t.length,n=e.charAt(r);return n&&n!=="/"?null:e.slice(r)||"/"}function ze(e,t="/"){let{pathname:r,search:n="",hash:a=""}=typeof e=="string"?I(e):e;return{pathname:r?r.startsWith("/")?r:Je(r,t):t,search:qe(n),hash:Ge(a)}}function Je(e,t){let r=t.replace(/\/+$/,"").split("/");return e.split("/").forEach(a=>{a===".."?r.length>1&&r.pop():a!=="."&&r.push(a)}),r.length>1?r.join("/"):"/"}function Y(e,t,r,n){return`Cannot include a '${e}' character in a manually specified \`to.${t}\` field [${JSON.stringify(n)}]. Please separate it out to the \`to.${r}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function Ke(e){return e.filter((t,r)=>r===0||t.route.path&&t.route.path.length>0)}function Z(e){let t=Ke(e);return t.map((r,n)=>n===t.length-1?r.pathname:r.pathnameBase)}function ee(e,t,r,n=!1){let a;typeof e=="string"?a=I(e):(a={...e},E(!a.pathname||!a.pathname.includes("?"),Y("?","pathname","search",a)),E(!a.pathname||!a.pathname.includes("#"),Y("#","pathname","hash",a)),E(!a.search||!a.search.includes("#"),Y("#","search","hash",a)));let o=e===""||a.pathname==="",l=o?"/":a.pathname,s;if(l==null)s=r;else{let h=t.length-1;if(!n&&l.startsWith("..")){let y=l.split("/");for(;y[0]==="..";)y.shift(),h-=1;a.pathname=y.join("/")}s=h>=0?t[h]:"/"}let u=ze(a,s),c=l&&l!=="/"&&l.endsWith("/"),f=(o||l===".")&&r.endsWith("/");return!u.pathname.endsWith("/")&&(c||f)&&(u.pathname+="/"),u}var k=e=>e.join("/").replace(/\/\/+/g,"/"),Ye=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),qe=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,Ge=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e;function Xe(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}var ye=["POST","PUT","PATCH","DELETE"];new Set(ye);var Qe=["GET",...ye];new Set(Qe);var T=i.createContext(null);T.displayName="DataRouter";var J=i.createContext(null);J.displayName="DataRouterState";i.createContext(!1);var ge=i.createContext({isTransitioning:!1});ge.displayName="ViewTransition";var Ze=i.createContext(new Map);Ze.displayName="Fetchers";var et=i.createContext(null);et.displayName="Await";var S=i.createContext(null);S.displayName="Navigation";var A=i.createContext(null);A.displayName="Location";var L=i.createContext({outlet:null,matches:[],isDataRoute:!1});L.displayName="Route";var te=i.createContext(null);te.displayName="RouteError";function tt(e,{relative:t}={}){E(D(),"useHref() may be used only in the context of a component.");let{basename:r,navigator:n}=i.useContext(S),{hash:a,pathname:o,search:l}=U(e,{relative:t}),s=o;return r!=="/"&&(s=o==="/"?r:k([r,o])),n.createHref({pathname:s,search:l,hash:a})}function D(){return i.useContext(A)!=null}function F(){return E(D(),"useLocation() may be used only in the context of a component."),i.useContext(A).location}var ve="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function we(e){i.useContext(S).static||i.useLayoutEffect(e)}function re(){let{isDataRoute:e}=i.useContext(L);return e?yt():rt()}function rt(){E(D(),"useNavigate() may be used only in the context of a component.");let e=i.useContext(T),{basename:t,navigator:r}=i.useContext(S),{matches:n}=i.useContext(L),{pathname:a}=F(),o=JSON.stringify(Z(n)),l=i.useRef(!1);return we(()=>{l.current=!0}),i.useCallback((u,c={})=>{if(x(l.current,ve),!l.current)return;if(typeof u=="number"){r.go(u);return}let f=ee(u,JSON.parse(o),a,c.relative==="path");e==null&&t!=="/"&&(f.pathname=f.pathname==="/"?t:k([t,f.pathname])),(c.replace?r.replace:r.push)(f,c.state,c)},[t,r,o,a,e])}var nt=i.createContext(null);function at(e){let t=i.useContext(L).outlet;return t&&i.createElement(nt.Provider,{value:e},t)}function U(e,{relative:t}={}){let{matches:r}=i.useContext(L),{pathname:n}=F(),a=JSON.stringify(Z(r));return i.useMemo(()=>ee(e,JSON.parse(a),n,t==="path"),[e,a,n,t])}function ot(e,t){return Re(e,t)}function Re(e,t,r,n,a){E(D(),"useRoutes() may be used only in the context of a component.");let{navigator:o}=i.useContext(S),{matches:l}=i.useContext(L),s=l[l.length-1],u=s?s.params:{},c=s?s.pathname:"/",f=s?s.pathnameBase:"/",h=s&&s.route;{let d=h&&h.path||"";Ee(c,!h||d.endsWith("*")||d.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${c}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. - -Please change the parent to .`)}let y=F(),g;if(t){let d=typeof t=="string"?I(t):t;E(f==="/"||d.pathname?.startsWith(f),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${f}" but pathname "${d.pathname}" was given in the \`location\` prop.`),g=d}else g=y;let v=g.pathname||"/",w=v;if(f!=="/"){let d=f.replace(/^\//,"").split("/");w="/"+v.replace(/^\//,"").split("/").slice(d.length).join("/")}let p=de(e,{pathname:w});x(h||p!=null,`No routes matched location "${g.pathname}${g.search}${g.hash}" `),x(p==null||p[p.length-1].route.element!==void 0||p[p.length-1].route.Component!==void 0||p[p.length-1].route.lazy!==void 0,`Matched leaf route at location "${g.pathname}${g.search}${g.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let m=ct(p&&p.map(d=>Object.assign({},d,{params:Object.assign({},u,d.params),pathname:k([f,o.encodeLocation?o.encodeLocation(d.pathname).pathname:d.pathname]),pathnameBase:d.pathnameBase==="/"?f:k([f,o.encodeLocation?o.encodeLocation(d.pathnameBase).pathname:d.pathnameBase])})),l,r,n,a);return t&&m?i.createElement(A.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",...g},navigationType:"POP"}},m):m}function lt(){let e=mt(),t=Xe(e)?`${e.status} ${e.statusText}`:e instanceof Error?e.message:JSON.stringify(e),r=e instanceof Error?e.stack:null,n="rgba(200,200,200, 0.5)",a={padding:"0.5rem",backgroundColor:n},o={padding:"2px 4px",backgroundColor:n},l=null;return console.error("Error handled by React Router default ErrorBoundary:",e),l=i.createElement(i.Fragment,null,i.createElement("p",null,"💿 Hey developer 👋"),i.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",i.createElement("code",{style:o},"ErrorBoundary")," or"," ",i.createElement("code",{style:o},"errorElement")," prop on your route.")),i.createElement(i.Fragment,null,i.createElement("h2",null,"Unexpected Application Error!"),i.createElement("h3",{style:{fontStyle:"italic"}},t),r?i.createElement("pre",{style:a},r):null,l)}var it=i.createElement(lt,null),ut=class extends i.Component{constructor(e){super(e),this.state={location:e.location,revalidation:e.revalidation,error:e.error}}static getDerivedStateFromError(e){return{error:e}}static getDerivedStateFromProps(e,t){return t.location!==e.location||t.revalidation!=="idle"&&e.revalidation==="idle"?{error:e.error,location:e.location,revalidation:e.revalidation}:{error:e.error!==void 0?e.error:t.error,location:t.location,revalidation:e.revalidation||t.revalidation}}componentDidCatch(e,t){this.props.unstable_onError?this.props.unstable_onError(e,t):console.error("React Router caught the following error during render",e)}render(){return this.state.error!==void 0?i.createElement(L.Provider,{value:this.props.routeContext},i.createElement(te.Provider,{value:this.state.error,children:this.props.component})):this.props.children}};function st({routeContext:e,match:t,children:r}){let n=i.useContext(T);return n&&n.static&&n.staticContext&&(t.route.errorElement||t.route.ErrorBoundary)&&(n.staticContext._deepestRenderedBoundaryId=t.route.id),i.createElement(L.Provider,{value:e},r)}function ct(e,t=[],r=null,n=null,a=null){if(e==null){if(!r)return null;if(r.errors)e=r.matches;else if(t.length===0&&!r.initialized&&r.matches.length>0)e=r.matches;else return null}let o=e,l=r?.errors;if(l!=null){let c=o.findIndex(f=>f.route.id&&l?.[f.route.id]!==void 0);E(c>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(l).join(",")}`),o=o.slice(0,Math.min(o.length,c+1))}let s=!1,u=-1;if(r)for(let c=0;c=0?o=o.slice(0,u+1):o=[o[0]];break}}}return o.reduceRight((c,f,h)=>{let y,g=!1,v=null,w=null;r&&(y=l&&f.route.id?l[f.route.id]:void 0,v=f.route.errorElement||it,s&&(u<0&&h===0?(Ee("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),g=!0,w=null):u===h&&(g=!0,w=f.route.hydrateFallbackElement||null)));let p=t.concat(o.slice(0,h+1)),m=()=>{let d;return y?d=v:g?d=w:f.route.Component?d=i.createElement(f.route.Component,null):f.route.element?d=f.route.element:d=c,i.createElement(st,{match:f,routeContext:{outlet:c,matches:p,isDataRoute:r!=null},children:d})};return r&&(f.route.ErrorBoundary||f.route.errorElement||h===0)?i.createElement(ut,{location:r.location,revalidation:r.revalidation,component:v,error:y,children:m(),routeContext:{outlet:null,matches:p,isDataRoute:!0},unstable_onError:n}):m()},null)}function ne(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function ft(e){let t=i.useContext(T);return E(t,ne(e)),t}function ht(e){let t=i.useContext(J);return E(t,ne(e)),t}function dt(e){let t=i.useContext(L);return E(t,ne(e)),t}function ae(e){let t=dt(e),r=t.matches[t.matches.length-1];return E(r.route.id,`${e} can only be used on routes that contain a unique "id"`),r.route.id}function pt(){return ae("useRouteId")}function mt(){let e=i.useContext(te),t=ht("useRouteError"),r=ae("useRouteError");return e!==void 0?e:t.errors?.[r]}function yt(){let{router:e}=ft("useNavigate"),t=ae("useNavigate"),r=i.useRef(!1);return we(()=>{r.current=!0}),i.useCallback(async(a,o={})=>{x(r.current,ve),r.current&&(typeof a=="number"?e.navigate(a):await e.navigate(a,{fromRouteId:t,...o}))},[e,t])}var fe={};function Ee(e,t,r){!t&&!fe[e]&&(fe[e]=!0,x(!1,r))}i.memo(gt);function gt({routes:e,future:t,state:r,unstable_onError:n}){return Re(e,void 0,r,n,t)}function er({to:e,replace:t,state:r,relative:n}){E(D()," may be used only in the context of a component.");let{static:a}=i.useContext(S);x(!a," must not be used on the initial render in a . This is a no-op, but you should modify your code so the is only ever rendered in response to some user interaction or state change.");let{matches:o}=i.useContext(L),{pathname:l}=F(),s=re(),u=ee(e,Z(o),l,n==="path"),c=JSON.stringify(u);return i.useEffect(()=>{s(JSON.parse(c),{replace:t,state:r,relative:n})},[s,c,n,t,r]),null}function tr(e){return at(e.context)}function vt(e){E(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function wt({basename:e="/",children:t=null,location:r,navigationType:n="POP",navigator:a,static:o=!1}){E(!D(),"You cannot render a inside another . You should never have more than one in your app.");let l=e.replace(/^\/*/,"/"),s=i.useMemo(()=>({basename:l,navigator:a,static:o,future:{}}),[l,a,o]);typeof r=="string"&&(r=I(r));let{pathname:u="/",search:c="",hash:f="",state:h=null,key:y="default"}=r,g=i.useMemo(()=>{let v=$(u,l);return v==null?null:{location:{pathname:v,search:c,hash:f,state:h,key:y},navigationType:n}},[l,u,c,f,h,y,n]);return x(g!=null,` is not able to match the URL "${u}${c}${f}" because it does not start with the basename, so the won't render anything.`),g==null?null:i.createElement(S.Provider,{value:s},i.createElement(A.Provider,{children:t,value:g}))}function rr({children:e,location:t}){return ot(X(e),t)}function X(e,t=[]){let r=[];return i.Children.forEach(e,(n,a)=>{if(!i.isValidElement(n))return;let o=[...t,a];if(n.type===i.Fragment){r.push.apply(r,X(n.props.children,o));return}E(n.type===vt,`[${typeof n.type=="string"?n.type:n.type.name}] is not a component. All component children of must be a or `),E(!n.props.index||!n.props.children,"An index route cannot have child routes.");let l={id:n.props.id||o.join("-"),caseSensitive:n.props.caseSensitive,element:n.props.element,Component:n.props.Component,index:n.props.index,path:n.props.path,loader:n.props.loader,action:n.props.action,hydrateFallbackElement:n.props.hydrateFallbackElement,HydrateFallback:n.props.HydrateFallback,errorElement:n.props.errorElement,ErrorBoundary:n.props.ErrorBoundary,hasErrorBoundary:n.props.hasErrorBoundary===!0||n.props.ErrorBoundary!=null||n.props.errorElement!=null,shouldRevalidate:n.props.shouldRevalidate,handle:n.props.handle,lazy:n.props.lazy};n.props.children&&(l.children=X(n.props.children,o)),r.push(l)}),r}var j="get",V="application/x-www-form-urlencoded";function K(e){return e!=null&&typeof e.tagName=="string"}function Rt(e){return K(e)&&e.tagName.toLowerCase()==="button"}function Et(e){return K(e)&&e.tagName.toLowerCase()==="form"}function xt(e){return K(e)&&e.tagName.toLowerCase()==="input"}function Ct(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function bt(e,t){return e.button===0&&(!t||t==="_self")&&!Ct(e)}function Q(e=""){return new URLSearchParams(typeof e=="string"||Array.isArray(e)||e instanceof URLSearchParams?e:Object.keys(e).reduce((t,r)=>{let n=e[r];return t.concat(Array.isArray(n)?n.map(a=>[r,a]):[[r,n]])},[]))}function Pt(e,t){let r=Q(e);return t&&t.forEach((n,a)=>{r.has(a)||t.getAll(a).forEach(o=>{r.append(a,o)})}),r}var _=null;function St(){if(_===null)try{new FormData(document.createElement("form"),0),_=!1}catch{_=!0}return _}var Lt=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function q(e){return e!=null&&!Lt.has(e)?(x(!1,`"${e}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${V}"`),null):e}function kt(e,t){let r,n,a,o,l;if(Et(e)){let s=e.getAttribute("action");n=s?$(s,t):null,r=e.getAttribute("method")||j,a=q(e.getAttribute("enctype"))||V,o=new FormData(e)}else if(Rt(e)||xt(e)&&(e.type==="submit"||e.type==="image")){let s=e.form;if(s==null)throw new Error('Cannot submit a