366 lines
13 KiB
Plaintext
366 lines
13 KiB
Plaintext
![]() |
#!/usr/bin/env nu
|
||
|
|
||
|
# API Routes and handlers for Provisioning System
|
||
|
# Defines all REST API endpoints and their handlers
|
||
|
|
||
|
use ../lib_provisioning/utils/settings.nu *
|
||
|
use ../main_provisioning/query.nu *
|
||
|
|
||
|
# Route definitions for the API server
|
||
|
export def get_route_definitions []: nothing -> list {
|
||
|
[
|
||
|
{
|
||
|
method: "GET"
|
||
|
path: "/api/v1/health"
|
||
|
handler: "health_check"
|
||
|
description: "Health check endpoint"
|
||
|
parameters: []
|
||
|
}
|
||
|
{
|
||
|
method: "GET"
|
||
|
path: "/api/v1/query"
|
||
|
handler: "query_infrastructure"
|
||
|
description: "Query infrastructure state"
|
||
|
parameters: [
|
||
|
{ name: "target", type: "string", required: false, default: "servers", description: "Query target (servers, metrics, logs)" }
|
||
|
{ name: "infra", type: "string", required: false, description: "Infrastructure name" }
|
||
|
{ name: "provider", type: "string", required: false, description: "Provider filter" }
|
||
|
{ name: "find", type: "string", required: false, description: "Search filter" }
|
||
|
{ name: "format", type: "string", required: false, default: "json", description: "Output format" }
|
||
|
]
|
||
|
}
|
||
|
{
|
||
|
method: "POST"
|
||
|
path: "/api/v1/query"
|
||
|
handler: "complex_query"
|
||
|
description: "Execute complex queries with request body"
|
||
|
body_schema: {
|
||
|
type: "object"
|
||
|
properties: {
|
||
|
query_type: { type: "string", enum: ["infrastructure", "metrics", "logs", "ai"] }
|
||
|
target: { type: "string" }
|
||
|
filters: { type: "object" }
|
||
|
ai_query: { type: "string", description: "Natural language query" }
|
||
|
aggregations: { type: "array" }
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
{
|
||
|
method: "GET"
|
||
|
path: "/api/v1/metrics"
|
||
|
handler: "get_metrics"
|
||
|
description: "Retrieve system metrics"
|
||
|
parameters: [
|
||
|
{ name: "timerange", type: "string", default: "1h", description: "Time range (1m, 5m, 1h, 1d)" }
|
||
|
{ name: "metric_type", type: "string", description: "Metric type filter" }
|
||
|
{ name: "aggregation", type: "string", default: "avg", description: "Aggregation method" }
|
||
|
]
|
||
|
}
|
||
|
{
|
||
|
method: "GET"
|
||
|
path: "/api/v1/logs"
|
||
|
handler: "get_logs"
|
||
|
description: "Retrieve system logs"
|
||
|
parameters: [
|
||
|
{ name: "level", type: "string", default: "info", description: "Log level filter" }
|
||
|
{ name: "service", type: "string", description: "Service name filter" }
|
||
|
{ name: "since", type: "string", default: "1h", description: "Time since" }
|
||
|
{ name: "limit", type: "integer", default: 100, description: "Number of entries" }
|
||
|
]
|
||
|
}
|
||
|
{
|
||
|
method: "GET"
|
||
|
path: "/api/v1/dashboard"
|
||
|
handler: "get_dashboard_data"
|
||
|
description: "Dashboard data endpoint"
|
||
|
parameters: [
|
||
|
{ name: "view", type: "string", default: "overview", description: "Dashboard view" }
|
||
|
{ name: "refresh", type: "boolean", default: false, description: "Force refresh" }
|
||
|
]
|
||
|
}
|
||
|
{
|
||
|
method: "GET"
|
||
|
path: "/api/v1/servers"
|
||
|
handler: "list_servers"
|
||
|
description: "List all servers"
|
||
|
parameters: [
|
||
|
{ name: "status", type: "string", description: "Status filter" }
|
||
|
{ name: "provider", type: "string", description: "Provider filter" }
|
||
|
{ name: "infra", type: "string", description: "Infrastructure filter" }
|
||
|
]
|
||
|
}
|
||
|
{
|
||
|
method: "GET"
|
||
|
path: "/api/v1/servers/{id}"
|
||
|
handler: "get_server"
|
||
|
description: "Get specific server details"
|
||
|
path_params: [
|
||
|
{ name: "id", type: "string", required: true, description: "Server ID" }
|
||
|
]
|
||
|
}
|
||
|
{
|
||
|
method: "GET"
|
||
|
path: "/api/v1/servers/{id}/status"
|
||
|
handler: "get_server_status"
|
||
|
description: "Get server status and metrics"
|
||
|
path_params: [
|
||
|
{ name: "id", type: "string", required: true, description: "Server ID" }
|
||
|
]
|
||
|
}
|
||
|
{
|
||
|
method: "GET"
|
||
|
path: "/api/v1/servers/{id}/logs"
|
||
|
handler: "get_server_logs"
|
||
|
description: "Get server-specific logs"
|
||
|
path_params: [
|
||
|
{ name: "id", type: "string", required: true, description: "Server ID" }
|
||
|
]
|
||
|
}
|
||
|
{
|
||
|
method: "POST"
|
||
|
path: "/api/v1/servers"
|
||
|
handler: "create_server"
|
||
|
description: "Create new server"
|
||
|
body_schema: {
|
||
|
type: "object"
|
||
|
required: ["name", "provider"]
|
||
|
properties: {
|
||
|
name: { type: "string" }
|
||
|
provider: { type: "string" }
|
||
|
infra: { type: "string" }
|
||
|
instance_type: { type: "string" }
|
||
|
count: { type: "integer", default: 1 }
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
{
|
||
|
method: "DELETE"
|
||
|
path: "/api/v1/servers/{id}"
|
||
|
handler: "delete_server"
|
||
|
description: "Delete server"
|
||
|
path_params: [
|
||
|
{ name: "id", type: "string", required: true, description: "Server ID" }
|
||
|
]
|
||
|
}
|
||
|
{
|
||
|
method: "GET"
|
||
|
path: "/api/v1/ai/query"
|
||
|
handler: "ai_query"
|
||
|
description: "Natural language infrastructure queries"
|
||
|
parameters: [
|
||
|
{ name: "q", type: "string", required: true, description: "Natural language query" }
|
||
|
{ name: "context", type: "string", description: "Context for the query" }
|
||
|
]
|
||
|
}
|
||
|
{
|
||
|
method: "POST"
|
||
|
path: "/api/v1/ai/analyze"
|
||
|
handler: "ai_analyze"
|
||
|
description: "AI-powered infrastructure analysis"
|
||
|
body_schema: {
|
||
|
type: "object"
|
||
|
properties: {
|
||
|
analysis_type: { type: "string", enum: ["cost", "performance", "security", "optimization"] }
|
||
|
timerange: { type: "string", default: "24h" }
|
||
|
target: { type: "string" }
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
{
|
||
|
method: "GET"
|
||
|
path: "/api/v1/dataframes/query"
|
||
|
handler: "dataframe_query"
|
||
|
description: "Query infrastructure data using dataframes"
|
||
|
parameters: [
|
||
|
{ name: "source", type: "string", required: true, description: "Data source (logs, metrics, events)" }
|
||
|
{ name: "query", type: "string", required: true, description: "Polars/SQL-like query" }
|
||
|
{ name: "format", type: "string", default: "json", description: "Output format" }
|
||
|
]
|
||
|
}
|
||
|
{
|
||
|
method: "WebSocket"
|
||
|
path: "/ws/stream"
|
||
|
handler: "websocket_stream"
|
||
|
description: "Real-time updates via WebSocket"
|
||
|
parameters: [
|
||
|
{ name: "subscribe", type: "array", description: "Subscription topics" }
|
||
|
]
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
|
||
|
# Generate OpenAPI/Swagger specification
|
||
|
export def generate_api_spec []: nothing -> record {
|
||
|
let routes = get_route_definitions
|
||
|
|
||
|
{
|
||
|
openapi: "3.0.3"
|
||
|
info: {
|
||
|
title: "Provisioning System API"
|
||
|
description: "REST API for infrastructure provisioning and management"
|
||
|
version: "1.0.0"
|
||
|
contact: {
|
||
|
name: "Provisioning Team"
|
||
|
url: "https://github.com/provisioning-rs"
|
||
|
}
|
||
|
}
|
||
|
servers: [
|
||
|
{
|
||
|
url: "http://localhost:8080"
|
||
|
description: "Development server"
|
||
|
}
|
||
|
]
|
||
|
paths: ($routes | generate_paths)
|
||
|
components: {
|
||
|
schemas: (generate_schemas)
|
||
|
securitySchemes: {
|
||
|
BearerAuth: {
|
||
|
type: "http"
|
||
|
scheme: "bearer"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
security: [
|
||
|
{ BearerAuth: [] }
|
||
|
]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
def generate_paths []: list -> record {
|
||
|
let paths = {}
|
||
|
|
||
|
$in | each { |route|
|
||
|
let path_key = ($route.path | str replace -a "{id}" "{id}")
|
||
|
|
||
|
$paths | insert $path_key {
|
||
|
($route.method | str downcase): {
|
||
|
summary: $route.description
|
||
|
parameters: ($route.parameters? | default [] | each { |param|
|
||
|
{
|
||
|
name: $param.name
|
||
|
in: "query"
|
||
|
required: ($param.required? | default false)
|
||
|
schema: { type: $param.type }
|
||
|
description: $param.description?
|
||
|
}
|
||
|
})
|
||
|
responses: {
|
||
|
"200": {
|
||
|
description: "Successful response"
|
||
|
content: {
|
||
|
"application/json": {
|
||
|
schema: { type: "object" }
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
"400": {
|
||
|
description: "Bad request"
|
||
|
}
|
||
|
"500": {
|
||
|
description: "Internal server error"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} | last
|
||
|
}
|
||
|
|
||
|
def generate_schemas []: nothing -> record {
|
||
|
{
|
||
|
Error: {
|
||
|
type: "object"
|
||
|
properties: {
|
||
|
error: { type: "string" }
|
||
|
message: { type: "string" }
|
||
|
code: { type: "integer" }
|
||
|
}
|
||
|
}
|
||
|
HealthCheck: {
|
||
|
type: "object"
|
||
|
properties: {
|
||
|
status: { type: "string" }
|
||
|
service: { type: "string" }
|
||
|
version: { type: "string" }
|
||
|
timestamp: { type: "string" }
|
||
|
}
|
||
|
}
|
||
|
Server: {
|
||
|
type: "object"
|
||
|
properties: {
|
||
|
id: { type: "string" }
|
||
|
name: { type: "string" }
|
||
|
provider: { type: "string" }
|
||
|
status: { type: "string" }
|
||
|
ip_address: { type: "string" }
|
||
|
created_at: { type: "string" }
|
||
|
}
|
||
|
}
|
||
|
Metrics: {
|
||
|
type: "object"
|
||
|
properties: {
|
||
|
timestamp: { type: "string" }
|
||
|
cpu_usage: { type: "number" }
|
||
|
memory_usage: { type: "number" }
|
||
|
disk_usage: { type: "number" }
|
||
|
network_io: { type: "object" }
|
||
|
}
|
||
|
}
|
||
|
LogEntry: {
|
||
|
type: "object"
|
||
|
properties: {
|
||
|
timestamp: { type: "string" }
|
||
|
level: { type: "string" }
|
||
|
service: { type: "string" }
|
||
|
message: { type: "string" }
|
||
|
metadata: { type: "object" }
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Generate route documentation
|
||
|
export def generate_route_docs []: nothing -> str {
|
||
|
let routes = get_route_definitions
|
||
|
|
||
|
let header = "# Provisioning API Routes\n\nThis document describes all available API endpoints.\n\n"
|
||
|
|
||
|
let route_docs = ($routes | each { |route|
|
||
|
let params_doc = if ($route.parameters? | length) > 0 {
|
||
|
"\n**Parameters:**\n" + ($route.parameters | each { |p|
|
||
|
$"- `($p.name)` \\(($p.type)\\): ($p.description? | default 'No description')"
|
||
|
} | str join "\n")
|
||
|
} else { "" }
|
||
|
|
||
|
let body_doc = if ($route.body_schema? | is-not-empty) {
|
||
|
$"\n**Request Body:**\n```json\n($route.body_schema | to json)\n```"
|
||
|
} else { "" }
|
||
|
|
||
|
$"## ($route.method) ($route.path)\n\n($route.description)($params_doc)($body_doc)\n"
|
||
|
} | str join "\n")
|
||
|
|
||
|
$header + $route_docs
|
||
|
}
|
||
|
|
||
|
# Validate route configuration
|
||
|
export def validate_routes []: nothing -> record {
|
||
|
let routes = get_route_definitions
|
||
|
let validation_results = []
|
||
|
|
||
|
let path_conflicts = ($routes | group-by path | each { |path, group|
|
||
|
if ($group | length) > 1 {
|
||
|
let methods = ($group | get method)
|
||
|
let duplicate_methods = ($methods | uniq | length) != ($methods | length)
|
||
|
|
||
|
if $duplicate_methods {
|
||
|
{ path: $path, issue: "duplicate_methods", methods: $methods }
|
||
|
}
|
||
|
}
|
||
|
} | compact)
|
||
|
|
||
|
{
|
||
|
total_routes: ($routes | length)
|
||
|
unique_paths: ($routes | get path | uniq | length)
|
||
|
path_conflicts: $path_conflicts
|
||
|
validation_passed: ($path_conflicts | length) == 0
|
||
|
}
|
||
|
}
|