commit 40d5e8161acb18322f5c65d54f2ea7d56dce6ecf Author: Andrew Date: Sun Mar 15 23:34:48 2026 -0400 feat: initial implementation of VirtFusion MCP server Complete MCP server wrapping all 84 VirtFusion Admin API endpoints: - Core HTTP client with Bearer auth and error handling - 17 tool modules organized by API category - Endpoint drift detection scripts and Gitea Actions CI/CD - Comprehensive README with configuration examples - CLAUDE.md for AI assistant onboarding Co-Authored-By: Claude Opus 4.6 (1M context) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml new file mode 100644 index 0000000..d58ee7b --- /dev/null +++ b/.gitea/workflows/ci.yaml @@ -0,0 +1,25 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build diff --git a/.gitea/workflows/endpoint-sync.yaml b/.gitea/workflows/endpoint-sync.yaml new file mode 100644 index 0000000..85ffd6f --- /dev/null +++ b/.gitea/workflows/endpoint-sync.yaml @@ -0,0 +1,27 @@ +name: Endpoint Sync Check + +on: + schedule: + - cron: '0 9 * * 1' # Every Monday at 9am + push: + paths: + - 'openapi.yaml' + workflow_dispatch: + +jobs: + check-drift: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Check for endpoint drift + run: npm run check-endpoint-drift diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad51b91 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +dist/ +*.js.map +*.d.ts.map +.env +.env.* +!.env.example diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..72cb7a9 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,81 @@ +# VirtFusion MCP Server + +## Overview + +MCP server wrapping the VirtFusion virtualization control panel REST API (84 endpoints). Built with TypeScript, `@modelcontextprotocol/sdk`, and Zod for input validation. + +## Architecture + +``` +src/ + index.ts — Entry point: env validation, McpServer + StdioServerTransport setup + client.ts — VirtFusionClient: fetch wrapper with Bearer auth, query param filtering + errors.ts — VirtFusionApiError class + formatErrorResponse() for MCP error results + types.ts — Shared interfaces (PaginatedResponse, QueryParams) + tools/ + index.ts — Barrel: registerAllTools() wires all 17 modules to McpServer + general.ts — 1 tool (test connection) + hypervisors.ts — 2 tools + hypervisor-groups.ts — 3 tools + servers.ts — 18 tools (largest module) + servers-power.ts — 4 tools + servers-network.ts — 5 tools + servers-firewall.ts — 4 tools + servers-traffic.ts — 4 tools + ip-blocks.ts — 3 tools + backups.ts — 1 tool + dns.ts — 1 tool + media.ts — 2 tools + packages.ts — 2 tools + queue.ts — 1 tool + ssh-keys.ts — 4 tools + users.ts — 7 tools + self-service.ts — 19 tools +``` + +## Conventions + +### Tool naming +- `snake_case`: `{category}_{action}_{noun}` (e.g., `servers_power_boot`, `ip_blocks_list`) +- Every tool gets a human-readable description as second arg to `server.tool()` + +### Module pattern +Each `src/tools/*.ts` file exports one function: +```typescript +export function registerXxxTools(server: McpServer, client: VirtFusionClient): void { + server.tool('tool_name', 'Description', { ...zodSchema }, async (params) => { ... }); +} +``` + +### Error handling +- All tool handlers wrap logic in try/catch +- Catch block returns `formatErrorResponse(error)` which produces `{ isError: true, content: [...] }` +- `VirtFusionApiError` carries statusCode, statusText, and errorBody + +### HTTP client +- `VirtFusionClient` methods: `get()`, `post()`, `put()`, `delete()` +- All accept `(path, body?, query?)` — query params with `undefined` values are filtered out +- 204 responses return `{ success: true }` + +## How to add a new tool + +1. Identify the API endpoint in `openapi.yaml` +2. Find (or create) the matching `src/tools/*.ts` module by tag +3. Add a `server.tool()` call following the existing pattern +4. Register it in `src/tools/index.ts` if it's a new module +5. Update `endpoint-manifest.json`: `npm run extract-endpoints > endpoint-manifest.json` +6. Update the tool count in README.md + +## Commands + +```bash +npm run build # Compile TypeScript to dist/ +npm run dev # Run with tsx (development) +npm run extract-endpoints # Parse openapi.yaml → stdout JSON +npm run check-endpoint-drift # Compare spec vs manifest +``` + +## Environment variables + +- `VIRTFUSION_API_URL` — Required. e.g., `https://cp.example.com/api/v1` +- `VIRTFUSION_API_TOKEN` — Required. Bearer token from VirtFusion admin panel. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7671145 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 EZSCALE Hosting, LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..dfdb63a --- /dev/null +++ b/README.md @@ -0,0 +1,301 @@ +# VirtFusion MCP Server + +A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that wraps the [VirtFusion](https://virtfusion.com) virtualization control panel API. This enables AI assistants like Claude to manage VirtFusion infrastructure through natural language. + +## Features + +- **84 tools** covering the entire VirtFusion Admin API +- Server lifecycle management (create, build, delete, suspend, unsuspend) +- Power control (boot, shutdown, restart, poweroff) +- Network management (IPv4, whitelist, firewall, traffic) +- User management with external relation ID support +- Self-service operations (credit, resource packs, hourly profiles) +- Infrastructure management (hypervisors, IP blocks, packages, DNS) +- Backup and media management +- SSH key management +- Automatic API endpoint drift detection + +## Prerequisites + +- **Node.js** 22 or later +- **VirtFusion** panel with Admin API access +- An API token generated from your VirtFusion admin panel + +## Installation + +### From source + +```bash +git clone https://git.ezscale.cloud/EZSCALE/virtfusion-mcp.git +cd virtfusion-mcp +npm install +npm run build +``` + +## Configuration + +The server requires two environment variables: + +| Variable | Description | Example | +|---|---|---| +| `VIRTFUSION_API_URL` | Base URL of your VirtFusion API | `https://cp.example.com/api/v1` | +| `VIRTFUSION_API_TOKEN` | Admin API bearer token | `your-api-token-here` | + +### Claude Desktop + +Add to your `claude_desktop_config.json`: + +```json +{ + "mcpServers": { + "virtfusion": { + "command": "node", + "args": ["/path/to/virtfusion-mcp/dist/index.js"], + "env": { + "VIRTFUSION_API_URL": "https://cp.example.com/api/v1", + "VIRTFUSION_API_TOKEN": "your-api-token" + } + } + } +} +``` + +### Claude Code + +```bash +claude mcp add virtfusion -- node /path/to/virtfusion-mcp/dist/index.js \ + -e VIRTFUSION_API_URL=https://cp.example.com/api/v1 \ + -e VIRTFUSION_API_TOKEN=your-api-token +``` + +### VS Code / Cursor + +Add to your `.vscode/mcp.json`: + +```json +{ + "servers": { + "virtfusion": { + "command": "node", + "args": ["/path/to/virtfusion-mcp/dist/index.js"], + "env": { + "VIRTFUSION_API_URL": "https://cp.example.com/api/v1", + "VIRTFUSION_API_TOKEN": "your-api-token" + } + } + } +} +``` + +## Tool Reference + +### General (1 tool) + +| Tool | Description | +|---|---| +| `general_test_connection` | Test the API connection to VirtFusion | + +### Servers (18 tools) + +| Tool | Description | +|---|---| +| `servers_list` | List all servers with optional filtering | +| `servers_create` | Create a new server | +| `servers_get` | Retrieve details of a specific server | +| `servers_delete` | Delete a server | +| `servers_build` | Build/rebuild a server with an OS template | +| `servers_list_templates` | List OS templates available for a server | +| `servers_modify_name` | Change a server's display name | +| `servers_modify_cpu` | Modify CPU cores for a server | +| `servers_modify_memory` | Modify memory allocation for a server | +| `servers_throttle_cpu` | Throttle a server's CPU | +| `servers_get_traffic` | Retrieve traffic statistics | +| `servers_suspend` | Suspend a server | +| `servers_unsuspend` | Unsuspend a server | +| `servers_reset_password` | Reset a server's root/admin password | +| `servers_enable_vnc` | Enable or disable VNC | +| `servers_get_vnc` | Retrieve VNC connection details | +| `servers_set_custom_xml` | Set custom XML configuration | +| `servers_change_owner` | Change the owner of a server | +| `servers_change_package` | Change a server's package | +| `servers_set_backup_plan` | Set a server's backup plan | +| `servers_list_by_user` | List servers owned by a user | + +### Servers — Power (4 tools) + +| Tool | Description | +|---|---| +| `servers_power_boot` | Boot a server | +| `servers_power_shutdown` | Gracefully shutdown a server | +| `servers_power_restart` | Restart a server | +| `servers_power_poweroff` | Force power off a server | + +### Servers — Network (5 tools) + +| Tool | Description | +|---|---| +| `servers_network_whitelist_add` | Add an IP to a server's network whitelist | +| `servers_network_whitelist_remove` | Remove an IP from a server's whitelist | +| `servers_network_ipv4_add` | Add specific IPv4 addresses | +| `servers_network_ipv4_remove` | Remove IPv4 addresses | +| `servers_network_ipv4_add_qty` | Add a quantity of IPv4 addresses | + +### Servers — Firewall (4 tools) + +| Tool | Description | +|---|---| +| `servers_firewall_get` | Retrieve firewall configuration | +| `servers_firewall_enable` | Enable the firewall | +| `servers_firewall_disable` | Disable the firewall | +| `servers_firewall_set_rules` | Apply firewall rulesets | + +### Servers — Traffic (4 tools) + +| Tool | Description | +|---|---| +| `servers_traffic_list_blocks` | List traffic blocks | +| `servers_traffic_add_block` | Add a traffic block | +| `servers_traffic_remove_block` | Remove a traffic block | +| `servers_traffic_modify` | Modify primary traffic allowance | + +### Hypervisors (2 tools) + +| Tool | Description | +|---|---| +| `hypervisors_list` | List all hypervisors | +| `hypervisors_get` | Retrieve hypervisor details | + +### Hypervisor Groups (3 tools) + +| Tool | Description | +|---|---| +| `hypervisor_groups_list` | List all hypervisor groups | +| `hypervisor_groups_get` | Retrieve group details | +| `hypervisor_groups_get_resources` | Retrieve group resources | + +### IP Blocks (3 tools) + +| Tool | Description | +|---|---| +| `ip_blocks_list` | List all IP blocks | +| `ip_blocks_get` | Retrieve IP block details | +| `ip_blocks_add_ipv4_range` | Add an IPv4 range to a block | + +### Packages (2 tools) + +| Tool | Description | +|---|---| +| `packages_list` | List all packages | +| `packages_get` | Retrieve package details | + +### Queue (1 tool) + +| Tool | Description | +|---|---| +| `queue_get` | Retrieve queue item status | + +### Backups (1 tool) + +| Tool | Description | +|---|---| +| `backups_list_by_server` | List backups for a server | + +### DNS (1 tool) + +| Tool | Description | +|---|---| +| `dns_get_service` | Retrieve a DNS service | + +### Media (2 tools) + +| Tool | Description | +|---|---| +| `media_get_iso` | Retrieve ISO image details | +| `media_get_templates_by_package` | List OS templates for a package | + +### SSH Keys (4 tools) + +| Tool | Description | +|---|---| +| `ssh_keys_create` | Add an SSH key to a user | +| `ssh_keys_get` | Retrieve SSH key details | +| `ssh_keys_delete` | Delete an SSH key | +| `ssh_keys_list_by_user` | List SSH keys for a user | + +### Users (7 tools) + +| Tool | Description | +|---|---| +| `users_create` | Create a new user | +| `users_get` | Retrieve a user by external relation ID | +| `users_modify` | Modify a user | +| `users_delete` | Delete a user | +| `users_reset_password` | Reset a user's password | +| `users_generate_auth_tokens` | Generate login tokens | +| `users_generate_server_auth_tokens` | Generate server-scoped login tokens | + +### Self Service (19 tools) + +| Tool | Description | +|---|---| +| `self_service_add_credit` | Add credit to a user | +| `self_service_cancel_credit` | Cancel applied credit | +| `self_service_get_currencies` | Retrieve available currencies | +| `self_service_add_hourly_group_profile` | Add hourly group profile to user | +| `self_service_remove_hourly_group_profile` | Remove hourly group profile | +| `self_service_add_resource_group_profile` | Add resource group profile | +| `self_service_remove_resource_group_profile` | Remove resource group profile | +| `self_service_add_resource_pack` | Add resource pack to user | +| `self_service_get_resource_pack` | Retrieve a resource pack | +| `self_service_modify_resource_pack` | Modify a resource pack | +| `self_service_delete_resource_pack` | Delete a resource pack | +| `self_service_get_hourly_stats` | Retrieve hourly statistics | +| `self_service_set_hourly_resource_pack` | Set hourly resource pack | +| `self_service_modify_access` | Modify self-service access | +| `self_service_get_report` | Generate a user report | +| `self_service_get_usage` | Retrieve user usage data | +| `self_service_delete_pack_servers` | Delete all servers in a pack | +| `self_service_suspend_pack_servers` | Suspend all servers in a pack | +| `self_service_unsuspend_pack_servers` | Unsuspend all servers in a pack | + +## Development + +### Build + +```bash +npm run build +``` + +### Run in development mode + +```bash +VIRTFUSION_API_URL=https://cp.example.com/api/v1 \ +VIRTFUSION_API_TOKEN=your-token \ +npm run dev +``` + +### API Endpoint Sync + +This project includes tooling to detect when VirtFusion adds new API endpoints: + +```bash +# Extract endpoints from the current OpenAPI spec +npm run extract-endpoints > endpoint-manifest.json + +# Check for drift between spec and manifest +npm run check-endpoint-drift +``` + +The `endpoint-sync` workflow runs weekly and on any changes to `openapi.yaml`, creating an issue if drift is detected. + +#### Adding new tools when VirtFusion updates their API + +1. Download the updated `openapi.yaml` from VirtFusion docs +2. Run `npm run check-endpoint-drift` to see what changed +3. Implement new tools in the appropriate `src/tools/*.ts` module +4. Update the manifest: `npm run extract-endpoints > endpoint-manifest.json` +5. Commit everything together + +## License + +MIT — see [LICENSE](LICENSE) diff --git a/endpoint-manifest.json b/endpoint-manifest.json new file mode 100644 index 0000000..f588bca --- /dev/null +++ b/endpoint-manifest.json @@ -0,0 +1,506 @@ +[ + { + "method": "GET", + "path": "/backups/server/{serverId}", + "summary": "Retrieve a server backups", + "tag": "Backups" + }, + { + "method": "GET", + "path": "/compute/hypervisors", + "summary": "Retrieve hypervisors", + "tag": "Hypervisors" + }, + { + "method": "GET", + "path": "/compute/hypervisors/{hypervisorId}", + "summary": "Retrive a Hypervisor", + "tag": "Hypervisors" + }, + { + "method": "GET", + "path": "/compute/hypervisors/groups", + "summary": "Retrieve hypervisor groups", + "tag": "Hypervisor Groups" + }, + { + "method": "GET", + "path": "/compute/hypervisors/groups/{hypervisorGroupId}", + "summary": "Retrieve a hypervisor group", + "tag": "Hypervisor Groups" + }, + { + "method": "GET", + "path": "/compute/hypervisors/groups/{hypervisorGroupId}/resources", + "summary": "Retrieve a hypervisor groups resources", + "tag": "Hypervisor Groups" + }, + { + "method": "GET", + "path": "/connect", + "summary": "Test connection", + "tag": "General" + }, + { + "method": "GET", + "path": "/connectivity/ipblocks", + "summary": "Retrieve IP blocks", + "tag": "IP Blocks" + }, + { + "method": "GET", + "path": "/connectivity/ipblocks/{blockId}", + "summary": "Retrieve an IP block", + "tag": "IP Blocks" + }, + { + "method": "POST", + "path": "/connectivity/ipblocks/{blockId}/ipv4", + "summary": "Add an IPv4 range to an IP block", + "tag": "IP Blocks" + }, + { + "method": "GET", + "path": "/dns/services/{serviceId}", + "summary": "Retrieve a DNS service", + "tag": "DNS" + }, + { + "method": "GET", + "path": "/media/iso/{isoId}", + "summary": "Retrieve an ISO", + "tag": "Media" + }, + { + "method": "GET", + "path": "/media/templates/fromServerPackageSpec/{serverPackageId}", + "summary": "Retrieve operating system templates that are available for a package", + "tag": "Media" + }, + { + "method": "GET", + "path": "/packages", + "summary": "Retrieve packages", + "tag": "Packages" + }, + { + "method": "GET", + "path": "/packages/{packageId}", + "summary": "Retrieve a packge", + "tag": "Packages" + }, + { + "method": "GET", + "path": "/queue/{queueId}", + "summary": "Retrieve a queue item", + "tag": "Queue & Tasks" + }, + { + "method": "PUT", + "path": "/selfService/access/byUserExtRelationId/{extRelationId}", + "summary": "Modify user access", + "tag": "Self Service/External Relational ID" + }, + { + "method": "DELETE", + "path": "/selfService/credit/{creditId}", + "summary": "Cancel credit that was applied to a user", + "tag": "Self Service" + }, + { + "method": "POST", + "path": "/selfService/credit/byUserExtRelationId/{extRelationId}", + "summary": "Add credit to user", + "tag": "Self Service/External Relational ID" + }, + { + "method": "GET", + "path": "/selfService/currencies", + "summary": "Retrieve currencies", + "tag": "Self Service" + }, + { + "method": "DELETE", + "path": "/selfService/hourlyGroupProfile/{profileId}/byUserExtRelationId/{extRelationId}", + "summary": "Remove hourly group profile from a user", + "tag": "Self Service/External Relational ID" + }, + { + "method": "POST", + "path": "/selfService/hourlyGroupProfile/byUserExtRelationId/{extRelationId}", + "summary": "Add an hourly group profile to a user", + "tag": "Self Service/External Relational ID" + }, + { + "method": "PUT", + "path": "/selfService/hourlyResourcePack/byUserExtRelationId/{extRelationId}", + "summary": "Set an hourly resource pack", + "tag": "Self Service/External Relational ID" + }, + { + "method": "GET", + "path": "/selfService/hourlyStats/byUserExtRelationId/{extRelationId}", + "summary": "Retrieve hourly statistics", + "tag": "Self Service/External Relational ID" + }, + { + "method": "GET", + "path": "/selfService/report/byUserExtRelationId/{extRelationId}", + "summary": "Generate a report", + "tag": "Self Service/External Relational ID" + }, + { + "method": "DELETE", + "path": "/selfService/resourceGroupProfile/{profileId}/byUserExtRelationId/{extRelationId}", + "summary": "Remove resource group from a user", + "tag": "Self Service/External Relational ID" + }, + { + "method": "POST", + "path": "/selfService/resourceGroupProfile/byUserExtRelationId/{extRelationId}", + "summary": "Add a resource group profile to a user", + "tag": "Self Service/External Relational ID" + }, + { + "method": "DELETE", + "path": "/selfService/resourcePack/{packId}", + "summary": "Delete a user resource pack", + "tag": "Self Service" + }, + { + "method": "GET", + "path": "/selfService/resourcePack/{packId}", + "summary": "Retrieve a user resource pack", + "tag": "Self Service" + }, + { + "method": "PUT", + "path": "/selfService/resourcePack/{packId}", + "summary": "Modify user resource pack", + "tag": "Self Service" + }, + { + "method": "POST", + "path": "/selfService/resourcePack/byUserExtRelationId/{extRelationId}", + "summary": "Add a resource pack to a user", + "tag": "Self Service/External Relational ID" + }, + { + "method": "DELETE", + "path": "/selfService/resourcePackServers/{packId}", + "summary": "Delete all servers attached to a pack ID", + "tag": "Self Service" + }, + { + "method": "POST", + "path": "/selfService/resourcePackServers/{packId}/suspend", + "summary": "Suspend all servers assigned to a reosurce pack", + "tag": "Self Service" + }, + { + "method": "POST", + "path": "/selfService/resourcePackServers/{packId}/unsuspend", + "summary": "Unsuspend all servers assigned to a reosurce pack", + "tag": "Self Service" + }, + { + "method": "GET", + "path": "/selfService/usage/byUserExtRelationId/{extRelationId}", + "summary": "Retrieve a users usage", + "tag": "Self Service/External Relational ID" + }, + { + "method": "GET", + "path": "/servers", + "summary": "Retrieve servers", + "tag": "Servers" + }, + { + "method": "POST", + "path": "/servers", + "summary": "Create a server", + "tag": "Servers" + }, + { + "method": "DELETE", + "path": "/servers/{serverId}", + "summary": "Delete a server", + "tag": "Servers" + }, + { + "method": "GET", + "path": "/servers/{serverId}", + "summary": "Retrieve a server", + "tag": "Servers" + }, + { + "method": "PUT", + "path": "/servers/{serverId}/backups/plan/{planId}", + "summary": "Add, remove or modify a backup plan", + "tag": "Servers" + }, + { + "method": "POST", + "path": "/servers/{serverId}/build", + "summary": "Build a server", + "tag": "Servers" + }, + { + "method": "POST", + "path": "/servers/{serverId}/customXML", + "summary": "Set custom XML", + "tag": "Servers" + }, + { + "method": "GET", + "path": "/servers/{serverId}/firewall/{interface}", + "summary": "Retrieve firewall", + "tag": "Servers/Network/Firewall" + }, + { + "method": "POST", + "path": "/servers/{serverId}/firewall/{interface}/disable", + "summary": "Disable firewall", + "tag": "Servers/Network/Firewall" + }, + { + "method": "POST", + "path": "/servers/{serverId}/firewall/{interface}/enable", + "summary": "Enable firewall", + "tag": "Servers/Network/Firewall" + }, + { + "method": "POST", + "path": "/servers/{serverId}/firewall/{interface}/rules", + "summary": "Apply firewall rulesets", + "tag": "Servers/Network/Firewall" + }, + { + "method": "DELETE", + "path": "/servers/{serverId}/ipv4", + "summary": "Remove an array of IPv4 addresses", + "tag": "Servers/Network" + }, + { + "method": "POST", + "path": "/servers/{serverId}/ipv4", + "summary": "Add an array of IPv4 addresses", + "tag": "Servers/Network" + }, + { + "method": "POST", + "path": "/servers/{serverId}/ipv4Qty", + "summary": "Add a quantity of IPv4 addresses", + "tag": "Servers/Network" + }, + { + "method": "PUT", + "path": "/servers/{serverId}/modify/cpuCores", + "summary": "Modify CPU cores", + "tag": "Servers" + }, + { + "method": "PUT", + "path": "/servers/{serverId}/modify/cpuThrottle", + "summary": "Throttle a servers CPU", + "tag": "Servers" + }, + { + "method": "PUT", + "path": "/servers/{serverId}/modify/memory", + "summary": "Modify memory", + "tag": "Servers" + }, + { + "method": "PUT", + "path": "/servers/{serverId}/modify/name", + "summary": "Modify name", + "tag": "Servers" + }, + { + "method": "PUT", + "path": "/servers/{serverId}/modify/traffic", + "summary": "Modify primary traffic allowance", + "tag": "Servers/Network/Traffic" + }, + { + "method": "DELETE", + "path": "/servers/{serverId}/networkWhitelist", + "summary": "Remove an address from the whitelist", + "tag": "Servers/Network" + }, + { + "method": "POST", + "path": "/servers/{serverId}/networkWhitelist", + "summary": "Add an address to the whitelist", + "tag": "Servers/Network" + }, + { + "method": "PUT", + "path": "/servers/{serverId}/owner/{newOwnerId}", + "summary": "Change owner", + "tag": "Servers" + }, + { + "method": "PUT", + "path": "/servers/{serverId}/package/{packageId}", + "summary": "Change a server package", + "tag": "Servers" + }, + { + "method": "POST", + "path": "/servers/{serverId}/power/boot", + "summary": "Boot a server", + "tag": "Servers/Power" + }, + { + "method": "POST", + "path": "/servers/{serverId}/power/poweroff", + "summary": "Poweroff a server", + "tag": "Servers/Power" + }, + { + "method": "POST", + "path": "/servers/{serverId}/power/restart", + "summary": "Restart a server", + "tag": "Servers/Power" + }, + { + "method": "POST", + "path": "/servers/{serverId}/power/shutdown", + "summary": "Shutdown a server", + "tag": "Servers/Power" + }, + { + "method": "POST", + "path": "/servers/{serverId}/resetPassword", + "summary": "Reset a server password", + "tag": "Servers" + }, + { + "method": "POST", + "path": "/servers/{serverId}/suspend", + "summary": "Suspend a server", + "tag": "Servers" + }, + { + "method": "GET", + "path": "/servers/{serverId}/templates", + "summary": "Retrieve OS templates available to a server", + "tag": "Servers" + }, + { + "method": "GET", + "path": "/servers/{serverId}/traffic", + "summary": "Retrieve a servers traffic statistics", + "tag": "Servers" + }, + { + "method": "GET", + "path": "/servers/{serverId}/traffic/blocks", + "summary": "Retrieve a servers traffic blocks", + "tag": "Servers/Network/Traffic" + }, + { + "method": "POST", + "path": "/servers/{serverId}/traffic/blocks", + "summary": "Add a traffic block to a server", + "tag": "Servers/Network/Traffic" + }, + { + "method": "DELETE", + "path": "/servers/{serverId}/traffic/blocks/{blockId}", + "summary": "Remove a traffic block from a server", + "tag": "Servers/Network/Traffic" + }, + { + "method": "POST", + "path": "/servers/{serverId}/unsuspend", + "summary": "Unsuspend a server", + "tag": "Servers" + }, + { + "method": "GET", + "path": "/servers/{serverId}/vnc", + "summary": "Retrive VNC details", + "tag": "Servers" + }, + { + "method": "POST", + "path": "/servers/{serverId}/vnc", + "summary": "Enable or disable VNC", + "tag": "Servers" + }, + { + "method": "GET", + "path": "/servers/user/{userId}", + "summary": "Retrieve a users servers", + "tag": "Servers" + }, + { + "method": "POST", + "path": "/ssh_keys", + "summary": "Add an SSH key to a user account", + "tag": "SSH Keys" + }, + { + "method": "DELETE", + "path": "/ssh_keys/{keyId}", + "summary": "Delete an SSH key from a user", + "tag": "SSH Keys" + }, + { + "method": "GET", + "path": "/ssh_keys/{keyId}", + "summary": "Retrieve an SSH key", + "tag": "SSH Keys" + }, + { + "method": "GET", + "path": "/ssh_keys/user/{userId}", + "summary": "Retrieve a users SSH keys", + "tag": "SSH Keys" + }, + { + "method": "POST", + "path": "/users", + "summary": "Create a user", + "tag": "Users" + }, + { + "method": "POST", + "path": "/users/{extRelationId}/authenticationTokens", + "summary": "Generate a set of login tokens", + "tag": "Users/External Rel ID & Rel Str" + }, + { + "method": "DELETE", + "path": "/users/{extRelationId}/byExtRelation", + "summary": "Delete a user", + "tag": "Users/External Rel ID & Rel Str" + }, + { + "method": "GET", + "path": "/users/{extRelationId}/byExtRelation", + "summary": "Retrieve a user", + "tag": "Users/External Rel ID & Rel Str" + }, + { + "method": "PUT", + "path": "/users/{extRelationId}/byExtRelation", + "summary": "Modify a user", + "tag": "Users/External Rel ID & Rel Str" + }, + { + "method": "POST", + "path": "/users/{extRelationId}/byExtRelation/resetPassword", + "summary": "Change a user passowrd", + "tag": "Users/External Rel ID & Rel Str" + }, + { + "method": "POST", + "path": "/users/{extRelationId}/serverAuthenticationTokens/{serverId}", + "summary": "Generate a set of loging tokens using a server ID", + "tag": "Users/External Rel ID & Rel Str" + } +] diff --git a/openapi.yaml b/openapi.yaml new file mode 100644 index 0000000..caa19fc --- /dev/null +++ b/openapi.yaml @@ -0,0 +1,8309 @@ +openapi: 3.0.1 +info: + title: VirtFusion Global API + description: >- + You can use this API to access all Administrator API endpoints, such as the + server API to deploy and manage servers, or the system API to configure and + manage the system. + + + The API is organized around REST. All requests should be made over SSL. All + request and response bodies, including errors, are encoded in JSON. + + + Endpoint https://cp.domain.com/api/v1 + version: 1.0.0 +tags: + - name: General + - name: Hypervisors + - name: Hypervisor Groups + - name: Servers + - name: Servers/Network + - name: Servers/Network/Firewall + - name: Servers/Network/Traffic + - name: Servers/Power + - name: IP Blocks + - name: Backups + - name: DNS + - name: Media + - name: Packages + - name: Queue & Tasks + - name: SSH Keys + - name: Users + - name: Users/External Rel ID & Rel Str + - name: Self Service + - name: Self Service/External Relational ID +paths: + /connect: + get: + summary: Test connection + deprecated: false + description: '' + tags: + - General + parameters: [] + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: [] + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /compute/hypervisors/groups: + get: + summary: Retrieve hypervisor groups + deprecated: false + description: '' + tags: + - Hypervisor Groups + parameters: + - name: results + in: query + description: >- + Number of results to return. Range between 1 and 200. Defaults to + 20. + required: false + example: 20 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + current_page: 1 + data: + - id: 1 + name: Default + label: null + description: Default hypervisor group + distributionType: 5 + enabled: true + default: true + created: '2024-03-12T22:21:32+00:00' + updated: '2024-04-12T20:56:04+00:00' + - id: 2 + name: Test 1 + label: null + description: null + distributionType: 13 + enabled: true + default: false + created: '2024-10-08T13:23:28+00:00' + updated: '2024-10-08T13:23:42+00:00' + - id: 3 + name: Test 2 + label: null + description: null + distributionType: 5 + enabled: true + default: false + created: '2024-10-12T21:12:33+00:00' + updated: '2024-10-12T21:14:18+00:00' + first_page_url: >- + https://192.168.3.11/api/v1/compute/hypervisors/groups?results=20&page=1 + from: 1 + last_page: 1 + last_page_url: >- + https://192.168.3.11/api/v1/compute/hypervisors/groups?results=20&page=1 + links: + - url: null + label: '« Previous' + active: false + - url: >- + https://192.168.3.11/api/v1/compute/hypervisors/groups?results=20&page=1 + label: '1' + active: true + - url: null + label: Next » + active: false + next_page_url: null + path: https://192.168.3.11/api/v1/compute/hypervisors/groups + per_page: 20 + prev_page_url: null + to: 3 + total: 3 + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /compute/hypervisors/groups/{hypervisorGroupId}: + get: + summary: Retrieve a hypervisor group + deprecated: false + description: '' + tags: + - Hypervisor Groups + parameters: + - name: hypervisorGroupId + in: path + description: A valid hypervisor group ID as shown in VirtFusion. + required: true + example: 1 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 1 + name: Default + label: null + description: Default hypervisor group + distributionType: 5 + enabled: true + default: true + created: '2024-03-12T22:21:32+00:00' + updated: '2024-04-12T20:56:04+00:00' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /compute/hypervisors/groups/{hypervisorGroupId}/resources: + get: + summary: Retrieve a hypervisor groups resources + deprecated: false + description: '' + tags: + - Hypervisor Groups + parameters: + - name: hypervisorGroupId + in: path + description: A valid hypervisor group ID as shown in VirtFusion. + required: true + example: 1 + schema: + type: integer + - name: results + in: query + description: >- + Number of results to return. Range between 1 and 200. Defaults to + 20. + required: false + example: 20 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + current_page: 1 + data: + - hypervisor: + id: 1 + name: PHV 1 (RED) + enabled: true + prohibit: false + accept: false + commissioned: true + resources: + servers: + units: '#' + max: 0 + allocated: 1 + free: -1 + percent: null + memory: + units: MB + max: 6004 + allocated: 4096 + free: 1908 + percent: 68.2 + cpuCores: + units: '#' + max: 4 + allocated: 3 + free: 1 + percent: 75 + localStorage: + enabled: 1 + name: Local (Default mountpoint) + storageType: 0 + units: GB + max: 90 + allocated: 25 + free: 65 + percent: 27.8 + otherStorage: [] + network: + total: + ipv4: + free: 470 + - hypervisor: + id: 2 + name: PHV 2 (BLUE) + enabled: true + prohibit: false + accept: false + commissioned: true + resources: + servers: + units: '#' + max: 0 + allocated: 1 + free: -1 + percent: null + memory: + units: MB + max: 10000 + allocated: 1024 + free: 8976 + percent: 10.2 + cpuCores: + units: '#' + max: 28 + allocated: 1 + free: 27 + percent: 3.6 + localStorage: + enabled: 1 + name: Local (Default mountpoint) + storageType: 0 + units: GB + max: 150 + allocated: 10 + free: 140 + percent: 6.7 + otherStorage: [] + network: + total: + ipv4: + free: 470 + - hypervisor: + id: 3 + name: BHV 9 + enabled: true + prohibit: false + accept: false + commissioned: true + resources: + servers: + units: '#' + max: 0 + allocated: 2 + free: -2 + percent: null + memory: + units: MB + max: 27913 + allocated: 2048 + free: 25865 + percent: 7.3 + cpuCores: + units: '#' + max: 64 + allocated: 2 + free: 62 + percent: 3.1 + localStorage: + enabled: 1 + name: Local (Default mountpoint) + storageType: 0 + units: GB + max: 400 + allocated: 20 + free: 380 + percent: 5 + otherStorage: [] + network: + total: + ipv4: + free: 470 + - hypervisor: + id: 4 + name: BHV 8 + enabled: true + prohibit: false + accept: false + commissioned: true + resources: + servers: + units: '#' + max: 0 + allocated: 0 + free: 0 + percent: null + memory: + units: MB + max: 27913 + allocated: 0 + free: 27913 + percent: 0 + cpuCores: + units: '#' + max: 16 + allocated: 0 + free: 16 + percent: 0 + localStorage: + enabled: 1 + name: Local (Default mountpoint) + storageType: 0 + units: GB + max: 1000 + allocated: 0 + free: 1000 + percent: 0 + otherStorage: [] + network: + total: + ipv4: + free: 0 + - hypervisor: + id: 8 + name: BHV 3 + enabled: true + prohibit: false + accept: false + commissioned: true + resources: + servers: + units: '#' + max: 0 + allocated: 2 + free: -2 + percent: null + memory: + units: MB + max: 27913 + allocated: 2048 + free: 25865 + percent: 7.3 + cpuCores: + units: '#' + max: 120 + allocated: 3 + free: 117 + percent: 2.5 + localStorage: + enabled: 1 + name: Local (Default mountpoint) + storageType: 1 + units: GB + max: 2000 + allocated: 20 + free: 1980 + percent: 1 + otherStorage: [] + network: + total: + ipv4: + free: 470 + - hypervisor: + id: 9 + name: BHV 4 + enabled: true + prohibit: false + accept: false + commissioned: true + resources: + servers: + units: '#' + max: 0 + allocated: 0 + free: 0 + percent: null + memory: + units: MB + max: 13684 + allocated: 0 + free: 13684 + percent: 0 + cpuCores: + units: '#' + max: 4 + allocated: 0 + free: 4 + percent: 0 + localStorage: + enabled: 1 + name: Local (Default mountpoint) + storageType: 0 + units: GB + max: 1000 + allocated: 0 + free: 1000 + percent: 0 + otherStorage: [] + network: + total: + ipv4: + free: 0 + - hypervisor: + id: 10 + name: BHV 5 + enabled: true + prohibit: false + accept: false + commissioned: true + resources: + servers: + units: '#' + max: 0 + allocated: 0 + free: 0 + percent: null + memory: + units: MB + max: 13684 + allocated: 0 + free: 13684 + percent: 0 + cpuCores: + units: '#' + max: 4 + allocated: 0 + free: 4 + percent: 0 + localStorage: + enabled: 1 + name: Local (Default mountpoint) + storageType: 0 + units: GB + max: 1000 + allocated: 0 + free: 1000 + percent: 0 + otherStorage: [] + network: + total: + ipv4: + free: 0 + - hypervisor: + id: 11 + name: BHV 6 + enabled: true + prohibit: false + accept: false + commissioned: true + resources: + servers: + units: '#' + max: 0 + allocated: 1 + free: -1 + percent: null + memory: + units: MB + max: 27913 + allocated: 1024 + free: 26889 + percent: 3.7 + cpuCores: + units: '#' + max: 16 + allocated: 1 + free: 15 + percent: 6.3 + localStorage: + enabled: 1 + name: Local (Default mountpoint) + storageType: 0 + units: GB + max: 1000 + allocated: 10 + free: 990 + percent: 1 + otherStorage: [] + network: + total: + ipv4: + free: 478 + - hypervisor: + id: 12 + name: BHV 7 + enabled: true + prohibit: false + accept: false + commissioned: true + resources: + servers: + units: '#' + max: 0 + allocated: 1 + free: -1 + percent: null + memory: + units: MB + max: 27913 + allocated: 1024 + free: 26889 + percent: 3.7 + cpuCores: + units: '#' + max: 16 + allocated: 1 + free: 15 + percent: 6.3 + localStorage: + enabled: 1 + name: Local (Default mountpoint) + storageType: 0 + units: GB + max: 1000 + allocated: 10 + free: 990 + percent: 1 + otherStorage: [] + network: + total: + ipv4: + free: 478 + - hypervisor: + id: 13 + name: Ceph Hypervisor 1 + enabled: true + prohibit: false + accept: false + commissioned: true + resources: + servers: + units: '#' + max: 0 + allocated: 5 + free: -5 + percent: null + memory: + units: MB + max: 24000 + allocated: 7168 + free: 16832 + percent: 29.9 + cpuCores: + units: '#' + max: 64 + allocated: 8 + free: 56 + percent: 12.5 + localStorage: + enabled: 1 + name: Local (Default mountpoint) + storageType: 1 + units: GB + max: 100 + allocated: 50 + free: 50 + percent: 50 + otherStorage: + - id: 1 + name: Ceph RBD + enabled: 0 + path: null + units: GB + storageType: 2 + isDatastore: true + max: 10000 + allocated: 35 + free: 9965 + percent: 0.4 + - id: 4 + name: Ceph FS + enabled: 0 + path: null + units: GB + storageType: 2 + isDatastore: true + max: 558349385 + allocated: 5 + free: 558349380 + percent: 0 + network: + total: + ipv4: + free: 503 + - hypervisor: + id: 14 + name: Ceph Hypervisor 2 + enabled: true + prohibit: false + accept: false + commissioned: true + resources: + servers: + units: '#' + max: 0 + allocated: 3 + free: -3 + percent: null + memory: + units: MB + max: 24000 + allocated: 3072 + free: 20928 + percent: 12.8 + cpuCores: + units: '#' + max: 64 + allocated: 3 + free: 61 + percent: 4.7 + localStorage: + enabled: 1 + name: Local (Default mountpoint) + storageType: 1 + units: GB + max: 1000 + allocated: 10 + free: 990 + percent: 1 + otherStorage: + - id: 2 + name: Ceph RBD + enabled: 0 + path: null + units: GB + storageType: 2 + isDatastore: true + max: 10000 + allocated: 10 + free: 9990 + percent: 0.1 + - id: 3 + name: Ceph EC + enabled: 0 + path: null + units: GB + storageType: 2 + isDatastore: true + max: 13333333 + allocated: 10 + free: 13333323 + percent: 0 + network: + total: + ipv4: + free: 33 + first_page_url: >- + https://192.168.3.11/api/v1/compute/hypervisors/groups/1/resources?page=1 + from: 1 + last_page: 1 + last_page_url: >- + https://192.168.3.11/api/v1/compute/hypervisors/groups/1/resources?page=1 + links: + - url: null + label: '« Previous' + active: false + - url: >- + https://192.168.3.11/api/v1/compute/hypervisors/groups/1/resources?page=1 + label: '1' + active: true + - url: null + label: Next » + active: false + next_page_url: null + path: >- + https://192.168.3.11/api/v1/compute/hypervisors/groups/1/resources + per_page: 20 + prev_page_url: null + to: 11 + total: 11 + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/firewall/{interface}/disable: + post: + summary: Disable firewall + deprecated: false + description: '' + tags: + - Servers/Network/Firewall + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + - name: interface + in: path + description: primary or secondary. + required: true + example: primary + schema: + type: string + - name: sync + in: query + description: >- + Synchronise and apply the defined rules. true|false Defaults to + false. + required: false + example: 'true' + schema: + type: boolean + responses: + '200': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/firewall/{interface}/enable: + post: + summary: Enable firewall + deprecated: false + description: '' + tags: + - Servers/Network/Firewall + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + - name: interface + in: path + description: primary or secondary. + required: true + example: primary + schema: + type: string + - name: sync + in: query + description: >- + Synchronise and apply the defined rules. true|false Defaults to + false. + required: false + example: 'true' + schema: + type: boolean + responses: + '200': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/firewall/{interface}: + get: + summary: Retrieve firewall + deprecated: false + description: '' + tags: + - Servers/Network/Firewall + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + - name: interface + in: path + description: primary or secondary. + required: true + example: primary + schema: + type: string + - name: sync + in: query + description: >- + Synchronise and apply the defined rules. true|false Defaults to + false. + required: false + example: 'true' + schema: + type: boolean + responses: + '200': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + example: |- + { + "data": { + "enabled": true, + "rules": [] + } + } + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/firewall/{interface}/rules: + post: + summary: Apply firewall rulesets + deprecated: false + description: '' + tags: + - Servers/Network/Firewall + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + - name: interface + in: path + description: primary or secondary. + required: true + example: primary + schema: + type: string + - name: sync + in: query + description: >- + Synchronise and apply the defined rules. true|false Defaults to + false. + required: false + example: 'true' + schema: + type: boolean + requestBody: + content: + application/json: + schema: + type: object + properties: + rulesets: + type: array + items: + type: integer + description: >- + An array of ruleset IDs. All existing rules will be flushed + and the new rules applied. An empty array will flush all + rules. + required: + - rulesets + example: + rulesets: + - 1 + - 2 + - 5 + responses: + '201': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/traffic/blocks: + post: + summary: Add a traffic block to a server + deprecated: false + description: '' + tags: + - Servers/Network/Traffic + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + month: + type: integer + description: >- + The numeric month as returned by the GET request + (available). + amount: + type: integer + description: An amount of traffic in GB. + required: + - month + - amount + example: + month: 2 + amount: 100 + responses: + '201': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + get: + summary: Retrieve a servers traffic blocks + deprecated: false + description: '' + tags: + - Servers/Network/Traffic + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + month: + type: integer + description: >- + The numeric month as returned by the GET request + (available). + amount: + type: integer + description: An amount of traffic in GB. + required: + - month + - amount + example: '' + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + assigned: + - id: 2 + current: false + month: 2 + traffic: 100 + start: '2025-02-20 00:00:00' + end: '2025-03-19 23:59:59' + added: '2025-01-20T15:08:15.000000Z' + available: + total: 25 + current: + month: 1 + start: '2025-01-20 00:00:00' + end: '2025-02-19 23:59:59' + months: + '1': + month: 1 + start: '2025-01-20 00:00:00' + end: '2025-02-19 23:59:59' + '2': + month: 2 + start: '2025-02-20 00:00:00' + end: '2025-03-19 23:59:59' + '3': + month: 3 + start: '2025-03-20 00:00:00' + end: '2025-04-19 23:59:59' + '4': + month: 4 + start: '2025-04-20 00:00:00' + end: '2025-05-19 23:59:59' + '5': + month: 5 + start: '2025-05-20 00:00:00' + end: '2025-06-19 23:59:59' + '6': + month: 6 + start: '2025-06-20 00:00:00' + end: '2025-07-19 23:59:59' + '7': + month: 7 + start: '2025-07-20 00:00:00' + end: '2025-08-19 23:59:59' + '8': + month: 8 + start: '2025-08-20 00:00:00' + end: '2025-09-19 23:59:59' + '9': + month: 9 + start: '2025-09-20 00:00:00' + end: '2025-10-19 23:59:59' + '10': + month: 10 + start: '2025-10-20 00:00:00' + end: '2025-11-19 23:59:59' + '11': + month: 11 + start: '2025-11-20 00:00:00' + end: '2025-12-19 23:59:59' + '12': + month: 12 + start: '2025-12-20 00:00:00' + end: '2026-01-19 23:59:59' + '13': + month: 13 + start: '2026-01-20 00:00:00' + end: '2026-02-19 23:59:59' + '14': + month: 14 + start: '2026-02-20 00:00:00' + end: '2026-03-19 23:59:59' + '15': + month: 15 + start: '2026-03-20 00:00:00' + end: '2026-04-19 23:59:59' + '16': + month: 16 + start: '2026-04-20 00:00:00' + end: '2026-05-19 23:59:59' + '17': + month: 17 + start: '2026-05-20 00:00:00' + end: '2026-06-19 23:59:59' + '18': + month: 18 + start: '2026-06-20 00:00:00' + end: '2026-07-19 23:59:59' + '19': + month: 19 + start: '2026-07-20 00:00:00' + end: '2026-08-19 23:59:59' + '20': + month: 20 + start: '2026-08-20 00:00:00' + end: '2026-09-19 23:59:59' + '21': + month: 21 + start: '2026-09-20 00:00:00' + end: '2026-10-19 23:59:59' + '22': + month: 22 + start: '2026-10-20 00:00:00' + end: '2026-11-19 23:59:59' + '23': + month: 23 + start: '2026-11-20 00:00:00' + end: '2026-12-19 23:59:59' + '24': + month: 24 + start: '2026-12-20 00:00:00' + end: '2027-01-19 23:59:59' + '25': + month: 25 + start: '2027-01-20 00:00:00' + end: '2027-02-19 23:59:59' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/traffic/blocks/{blockId}: + delete: + summary: Remove a traffic block from a server + deprecated: false + description: '' + tags: + - Servers/Network/Traffic + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + - name: blockId + in: path + description: >- + ID of an assigned traffic block as returned by the GET request + (assigned). + required: true + example: '1' + schema: + type: string + requestBody: + content: + application/json: + schema: + type: object + properties: + month: + type: integer + description: >- + The numeric month as returned by the GET request + (available). + amount: + type: integer + description: An amount of traffic in GB. + required: + - month + - amount + example: + month: 2 + amount: 100 + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/modify/traffic: + put: + summary: Modify primary traffic allowance + deprecated: false + description: '' + tags: + - Servers/Network/Traffic + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + traffic: + type: string + description: Range of 0 - 999999999 + required: + - traffic + example: + traffic: 1000 + responses: + '201': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/networkWhitelist: + post: + summary: Add an address to the whitelist + deprecated: false + description: '' + tags: + - Servers/Network + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 9 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + interface: + type: string + description: Primary or secondary. + ip: + type: string + description: IPv4 or IPv6 address. + cidr: + type: integer + description: IPv4 or IPv6 CIDR. + required: + - interface + - ip + - cidr + example: + interface: primary + ip: 10.0.0.10 + cidr: 32 + responses: + '201': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + delete: + summary: Remove an address from the whitelist + deprecated: false + description: '' + tags: + - Servers/Network + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 9 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + interface: + type: string + description: Primary or secondary. + ip: + type: string + description: IPv4 or IPv6 address. + required: + - interface + - ip + example: + interface: primary + ip: 10.0.0.10 + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/ipv4Qty: + post: + summary: Add a quantity of IPv4 addresses + deprecated: false + description: '' + tags: + - Servers/Network + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 9 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + interface: + type: string + description: Primary or secondary. + quantity: + type: integer + description: Number of IPv4 addresses. + required: + - interface + - quantity + example: + interface: primary + quantity: 2 + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + - 192.168.4.36 + - 192.168.4.37 + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/ipv4: + post: + summary: Add an array of IPv4 addresses + deprecated: false + description: '' + tags: + - Servers/Network + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 9 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + ip: + type: array + items: + type: string + required: + - ip + example: + ip: + - 10.100.0.10 + - 10.100.0.11 + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + delete: + summary: Remove an array of IPv4 addresses + deprecated: false + description: '' + tags: + - Servers/Network + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 9 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + ip: + type: array + items: + type: string + description: Valid IPv4 addresses. + description: Valid IPv4 addresses. + required: + - ip + example: + ip: + - 10.100.0.10 + - 10.100.0.11 + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/power/boot: + post: + summary: Boot a server + deprecated: false + description: '' + tags: + - Servers/Power + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + queueId: 171 + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/power/shutdown: + post: + summary: Shutdown a server + deprecated: false + description: '' + tags: + - Servers/Power + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + queueId: 171 + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/power/restart: + post: + summary: Restart a server + deprecated: false + description: '' + tags: + - Servers/Power + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + queueId: 171 + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/power/poweroff: + post: + summary: Poweroff a server + deprecated: false + description: '' + tags: + - Servers/Power + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + queueId: 171 + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}: + get: + summary: Retrieve a server + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 1 + schema: + type: integer + - name: remoteState + in: query + description: Return the remote state of the server. + required: false + example: 'false' + schema: + type: boolean + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 69 + ownerId: 1 + hypervisorId: 6 + arch: 1 + name: Elliptical Way + selfService: 0 + selfServiceSettings: [] + hostname: null + commissionStatus: 3 + uuid: b9fd9092-7200-4a24-96d4-76aedd664274 + state: complete + migratable: true + timezone: _default + migrateLevel: 0 + deleteLevel: 0 + configLevel: 0 + backupLevel: 0 + elevated: false + elevateId: null + elevate: false + destroyable: true + rebuild: false + suspended: false + protected: false + buildFailed: false + primaryNetworkDhcp4: false + primaryNetworkDhcp6: false + built: '2025-01-15T15:00:49+00:00' + created: '2024-12-06T21:25:58+00:00' + updated: '2025-01-15T23:17:49+00:00' + traffic: + public: + countMethod: 1 + currentPeriod: + start: '2025-01-06T00:00:00.000000Z' + end: '2025-02-05T23:59:59.999999Z' + limit: 20000 + settings: + osTemplateInstall: true + osTemplateInstallId: 49 + encryptedPassword: >- + eyJpdiI6IkNtT0ZmUEQ4Q2ZuNW5yUWVXZUcvWWc9PSIsInZhbHVlIjoibHJmMTNHZXpqV3lneFUrZEZ3eThSWEZVbVk5TDlBYTJQbXpPbFRvcmd0OD0iLCJtYWMiOiI1NTNhMGVmNzBlZWViZWI3NjkyMmYzYmM3NWFjMDY3ZTlmZmE4ZDE3NDI2YzliMjY0ODM4YzQzMDViMWY3MTI1IiwidGFnIjoiIn0= + backupPlan: null + uefi: false + uefiType: 0 + cloudInit: true + cloudInitType: 1 + config: + cloud.init: + on.network: + netplan_routes_v4: true + netplan_routes_v6: true + on.network.libvirtrouted: + netplan_routes_v4: true + netplan_routes_v6: true + on.all: + user.data: + runcmd: + - >- + DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get + --option=Dpkg::Options::=--force-confold + --option=Dpkg::options::=--force-unsafe-io + --assume-yes --quiet update + - >- + DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get + --option=Dpkg::Options::=--force-confold + --option=Dpkg::options::=--force-unsafe-io + --assume-yes --quiet install qemu-guest-agent + - /usr/bin/systemctl enable qemu-guest-agent + - /usr/bin/systemctl start qemu-guest-agent + - >- + DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get + --option=Dpkg::Options::=--force-confold + --option=Dpkg::options::=--force-unsafe-io + --assume-yes --quiet dist-upgrade + on.password: + user.data: + runcmd: + - >- + /usr/bin/sed -i "s/#PermitRootLogin + prohibit-password/PermitRootLogin yes/g" + /etc/ssh/sshd_config + - >- + /usr/bin/sed -i "s/PasswordAuthentication + no/PasswordAuthentication yes/g" + /etc/ssh/sshd_config + - /usr/bin/systemctl restart sshd + on.sshkey: + user.data: [] + userConfig: [] + bootOrder: + - hd + - cdrom + tpmType: 0 + networkBoot: false + bootMenu: 1 + customISO: 1 + securityDriver: 3 + memBalloon: + model: 1 + autoDeflate: 0 + freePageReporting: 0 + hyperv: + enabled: false + passthrough: false + relaxed: 0 + vapic: 0 + spinlocks: 0 + vpindex: 0 + runtime: 0 + synic: 0 + stimer: 0 + reset: 0 + vendorId: 0 + frequencies: 0 + reenlightenment: 0 + tlbflush: 0 + ipi: 0 + evmcs: 0 + vendorIdValue: KVM VM + spinlocksValue: 8191 + clockEnabled: 0 + extended: + cpuFlags: + topoext: '1' + svm: '1' + vmx: '1' + machineType: inherit + pciPorts: 16 + resources: + memory: 1024 + storage: 11 + traffic: 20000 + cpuCores: 2 + cpu: + cores: 2 + type: inherit + typeExact: host-model + shares: 1024 + throttle: 0 + topology: + enabled: false + sockets: 1 + cores: 1 + threads: 1 + dies: 1 + customXML: + domain: + xml: '' + enabled: false + os: + xml: '' + enabled: false + devices: + xml: '' + enabled: false + features: + xml: '' + enabled: false + clock: + xml: '' + enabled: false + cpuTune: + xml: '' + enabled: false + qemuCommandline: [] + qemuAgent: + os: + screen: >- + iVBORw0KGgoAAAANSUhEUgAAAJYAAABTCAAAAABYT6E5AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfpARESACl1ShbSAAAB0klEQVRo3u3VS2/TQBAA4NlZr9eP+N04DmmKqxZFBQRC6oX/wN9G4s6l0ENFKQQoSVw7deT4xaFpRTkgkNKIivksreWd9e5oVqsFIH+N/UmUrdrrz9//tMaMxK8rSaEgAICGGnAAuGpAv+sy8f3wUYnBttibW2Wz33X9qfayOJhVXvydx8nzYLDMjcfZTrZTdH1tlHXxQbWXx1PhGD23lYVymD7LDHt7st7SKWGlxSguWRvtvq70yWjqST8YnmilCxB9GzdD8zwcnZrClKGZIna81jzMpWE97OfKWH0DArj24shac7VYlJuJNy9qgd4ZDOczYaXmhcLNeXQKgyJBP80VN40mbmp1vixYkMu5lXU/gK1Vjc+PwVwY1dNj++Ndb+u/ga0eAAbXr1UA2E0XA3Z18m4N/3mW/0UkIu4Aoio6jAP05E3EtRGYcJABbjgn5dVRrTpJ6G0t+btIme1e5IbNB2VwVn8qpB4Xvfe6G791TjabFla5Y1iG7etfz3WXMVW1jGU7kBKeMDBs4H1w1c9qteFqMdaqDTZc1AtE1mDVqnUN2HC+NW5FC20/Kctmw0ndQ7fuZ0IIIYQQQgghhBBCCCGEEEIIIYQQQgghhNwTPwDlF4AYGPA7/gAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyNS0wMS0xN1QxODowMDo0MSswMDowMA+ZFQkAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjUtMDEtMTdUMTg6MDA6NDErMDA6MDB+xK21AAAAAElFTkSuQmCC + media: + isoMounted: false + isoType: local + isoName: '' + isoFilename: '' + isoUrl: '' + isoDownload: false + backupPlan: + id: null + name: null + vnc: + ip: 192.168.4.2 + port: 5903 + enabled: false + network: + interfaces: + - id: 69 + order: 1 + enabled: true + tag: 4618706442 + name: ens3 + type: public + driver: 1 + processQueues: null + mac: 00:E7:FB:01:87:14 + ipv4ToMac: null + ipv6ToMac: null + inTrafficCount: true + outTrafficCount: false + inAverage: 0 + inPeak: 0 + inBurst: 0 + outAverage: 0 + outPeak: 0 + outBurst: 0 + ipFilter: false + vlans: [] + ipFilterType: null + portIsolated: false + ipv4_resolver_1: 1 + ipv4_resolver_2: 2 + ipv6_resolver_1: 1 + ipv6_resolver_2: 2 + networkProfile: 0 + dhcpV4: 0 + dhcpV6: 0 + firewallEnabled: false + hypervisorNetwork: 6 + isNat: false + nat: false + firewall: [] + hypervisorConnectivity: + id: 6 + type: simpleBridge + bridge: br0 + mtu: null + primary: true + default: true + ipWhitelist: [] + actions: [] + ipv4: + - id: 33 + order: 1 + enabled: true + blockId: 1 + address: 192.168.4.32 + gateway: 192.168.4.1 + netmask: 255.255.254.0 + resolver1: 8.8.8.8 + resolver2: 8.8.4.4 + rdns: null + mac: null + - id: 36 + order: 2 + enabled: true + blockId: 1 + address: 192.168.4.35 + gateway: 192.168.4.1 + netmask: 255.255.254.0 + resolver1: 8.8.8.8 + resolver2: 8.8.4.4 + rdns: null + mac: null + ipv6: + - id: 502 + block: + id: 5 + name: V6 For BHV 1,3 + order: 1 + enabled: true + addresses: [] + addressesDetailed: [] + subnet: '2001:db8:abcd:12:2::' + cidr: 80 + exhausted: false + gateway: 2001:db8:abcd:12::1 + resolver1: 2001:4860:4860::8888 + resolver2: 2001:4860:4860::8844 + routeNet: false + - id: 505 + block: + id: 5 + name: V6 For BHV 1,3 + order: 1 + enabled: true + addresses: [] + addressesDetailed: [] + subnet: '2001:db8:abcd:12:5::' + cidr: 80 + exhausted: false + gateway: 2001:db8:abcd:12::1 + resolver1: 2001:4860:4860::8888 + resolver2: 2001:4860:4860::8844 + routeNet: false + - id: 506 + block: + id: 5 + name: V6 For BHV 1,3 + order: 1 + enabled: true + addresses: [] + addressesDetailed: [] + subnet: '2001:db8:abcd:12:6::' + cidr: 80 + exhausted: false + gateway: 2001:db8:abcd:12::1 + resolver1: 2001:4860:4860::8888 + resolver2: 2001:4860:4860::8844 + routeNet: false + - id: 507 + block: + id: 5 + name: V6 For BHV 1,3 + order: 1 + enabled: true + addresses: [] + addressesDetailed: [] + subnet: '2001:db8:abcd:12:7::' + cidr: 80 + exhausted: false + gateway: 2001:db8:abcd:12::1 + resolver1: 2001:4860:4860::8888 + resolver2: 2001:4860:4860::8844 + routeNet: false + - id: 508 + block: + id: 5 + name: V6 For BHV 1,3 + order: 1 + enabled: true + addresses: [] + addressesDetailed: [] + subnet: '2001:db8:abcd:12:8::' + cidr: 80 + exhausted: false + gateway: 2001:db8:abcd:12::1 + resolver1: 2001:4860:4860::8888 + resolver2: 2001:4860:4860::8844 + routeNet: false + - id: 509 + block: + id: 5 + name: V6 For BHV 1,3 + order: 1 + enabled: true + addresses: [] + addressesDetailed: [] + subnet: '2001:db8:abcd:12:9::' + cidr: 80 + exhausted: false + gateway: 2001:db8:abcd:12::1 + resolver1: 2001:4860:4860::8888 + resolver2: 2001:4860:4860::8844 + routeNet: false + secondaryInterfaces: + - id: 4 + enabled: true + order: 1 + tag: 3933491695 + name: eth1 + type: private + driver: 1 + processQueues: null + mac: 00:F0:4A:C6:3F:08 + ipv4ToMac: null + ipv6ToMac: null + inTrafficCount: true + outTrafficCount: false + inAverage: 0 + inPeak: 0 + inBurst: 0 + outAverage: 0 + outPeak: 0 + outBurst: 0 + ipFilter: true + vlans: [] + ipFilterType: 4-6 + portIsolated: false + ipv4_resolver_1: 1 + ipv4_resolver_2: 2 + ipv6_resolver_1: 1 + ipv6_resolver_2: 2 + networkProfile: 0 + dhcpV4: 0 + dhcpV6: 0 + firewallEnabled: false + hypervisorNetwork: 6 + isNat: false + nat: false + firewall: [] + hypervisorConnectivity: + id: 6 + type: simpleBridge + bridge: br0 + mtu: null + primary: true + default: true + ipWhitelist: [] + actions: [] + ipv4: + - id: 34 + order: 1 + enabled: true + address: 192.168.4.33 + gateway: 192.168.4.1 + netmask: 255.255.254.0 + resolver1: 8.8.8.8 + resolver2: 8.8.4.4 + rdns: null + mac: null + - id: 35 + order: 2 + enabled: true + address: 192.168.4.34 + gateway: 192.168.4.1 + netmask: 255.255.254.0 + resolver1: 8.8.8.8 + resolver2: 8.8.4.4 + rdns: null + mac: null + ipv6: + - id: 503 + block: + id: 5 + name: V6 For BHV 1,3 + order: 1 + enabled: true + addresses: [] + addressesDetailed: [] + subnet: '2001:db8:abcd:12:3::' + cidr: 80 + exhausted: false + gateway: 2001:db8:abcd:12::1 + resolver1: 2001:4860:4860::8888 + resolver2: 2001:4860:4860::8844 + routeNet: false + - id: 504 + block: + id: 5 + name: V6 For BHV 1,3 + order: 1 + enabled: true + addresses: [] + addressesDetailed: [] + subnet: '2001:db8:abcd:12:4::' + cidr: 80 + exhausted: false + gateway: 2001:db8:abcd:12::1 + resolver1: 2001:4860:4860::8888 + resolver2: 2001:4860:4860::8844 + routeNet: false + storage: + - _id: 80 + id: 1 + cache: null + bus: null + capacity: 11 + drive: a + datastoreDiskId: null + filesystem: null + iops: + read: null + write: null + bytes: + read: null + write: null + type: qcow2 + profile: 1 + status: 3 + enabled: true + primary: true + created: '2024-12-06T21:25:58+00:00' + updated: '2025-01-07T22:26:16+00:00' + datastore: [] + name: b9fd9092-7200-4a24-96d4-76aedd664274_1 + filename: b9fd9092-7200-4a24-96d4-76aedd664274_1.img + hypervisorStorageId: null + local: true + locationType: mountpoint + path: /home/vf-data/disk + hypervisorAssets: [] + hypervisor: + id: 6 + ip: 192.168.4.2 + hostname: null + port: 8892 + maintenance: false + groupId: 2 + group: + name: Test + icon: null + timezone: Europe/London + forceIPv6: false + vncListenType: 1 + displayName: null + cpuSet: null + nfType: 4 + backupStorageType: 2 + defaultDiskType: inherit + defaultDiskCacheType: inherit + defaultCPU: inherit + defaultMachineType: inherit + created: '2024-03-30T09:53:38+00:00' + updated: '2024-12-06T21:25:54+00:00' + name: BHV 1 + dataDir: /home/vf-data + resources: + servers: + units: '#' + max: 0 + allocated: 5 + free: -5 + percent: null + memory: + units: MB + max: 29419 + allocated: 7168 + free: 22251 + percent: 24.4 + cpuCores: + units: '#' + max: 128 + allocated: 6 + free: 122 + percent: 4.7 + localStorage: + enabled: 1 + name: Local (Default mountpoint) + storageType: 1 + units: GB + max: 1000 + allocated: 141 + free: 859 + percent: 14.1 + otherStorage: [] + owner: + id: 1 + admin: true + extRelationId: null + name: Jon Doe + email: jon@doe.com + timezone: Europe/London + suspended: false + twoFactorAuth: false + created: '2024-03-12T22:22:09+00:00' + updated: '2025-01-15T11:01:18+00:00' + sshKeys: + - id: 1 + ownerId: 1 + type: OpenSSH + name: Test Key + public: >- + ssh-rsa + AAAAB3NzaC1yc2EAAAADAQABAAACAQC+JdL4fWELBWGAknSu0PwVpDDOlORxy9z7eVnZphZXBzYLMnux+ZogVLns6+O6NDE8JmWvP9RIg3SIga7RDOkW9UCdLzRu0jF2ALL7CK1huo1Ih0PDM9ZbFDy2Fd7a4DTvUX6923fQyW0PWRtyL11R4c9NUqzejKp5kW8vHfPQjzwb1hGIKvkSYkI0Auq4JJhlvjjnoK7Z8t5mpDrVfNTrVqevPgsW5Xwnq8R+02XywrY+Q/wnpxDs4Ujb2aA61A0x5J0xcZQpTQHoJNj77J3VmPI7Ry7Q8hPbTSLGZbN+gODr0lOaL5TdbvM3bnus5JvoqgRoszzPcTiNMZAe3v9UM8hiXise54b8rsc2M9MQ4olPu7TrROZbcw+9q4m6cV+dfVU/NRFkf27YRa4oZNKehHsMiupDyoISgSl4qSB8YXAWsX03oC/gzpB2YJIqEL1Y/SmKYEhgr0cplkvGZy6C/Q9cJHyHlMPtEBPexgcjXC9QrDZ4n2cmde3TuSRMctawcat7Nuq08C8fGHaGHr8iAeage3o/ODVOt0rhBu69PknzQeVBdlwK3+p1dH6PnMzNNBhWyNZT/NqB2eS6K8lYpOQ47byXPwYsRLvStUjpZRdikOT7D31T5g8FwOThQ+6WX+xfMD7CSLsSKCn/FhlinbVbG2IhCLH3B30Akw5bUw== + enabled: true + created: '2024-03-13T20:28:32+00:00' + sharedUsers: [] + tasks: + active: false + lastOn: '2025-01-15 15:00:49' + actions: + pending: [] + remoteState: false + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + delete: + summary: Delete a server + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 9 + schema: + type: integer + - name: delay + in: query + description: >- + How many minutes the system should wait before deleting the server. + (0-43800) + required: false + example: 5 + schema: + type: integer + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/backups/plan/{planId}: + put: + summary: Add, remove or modify a backup plan + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 9 + schema: + type: integer + - name: planId + in: path + description: >- + A valid backup plan ID as shown in VirtFusion. A value of 0 (zero) + will remove the plan. + required: true + example: 0 + schema: + type: integer + responses: + '201': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/build: + post: + summary: Build a server + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 9 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + operatingSystemId: + type: integer + description: A valid operating system template ID. + name: + type: string + description: Server name. + hostname: + type: string + description: Server Hostname. + sshKeys: + type: array + items: + type: integer + description: An array of SSH keys. + vnc: + type: boolean + description: Enable/disable. + ipv6: + type: boolean + description: Enable/disable. + email: + type: boolean + description: Enable/disable. + swap: + type: number + description: Values of 256, 512, 768, 1, 1.5, 2, 3, 4, 5,6 8 + required: + - operatingSystemId + example: + operatingSystemId: 1 + name: server 1 + hostname: server1.domain.com + sshKeys: + - 1 + - 2 + - 3 + - 4 + vnc: false + ipv6: false + swap: 512 + email: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 9 + ownerId: 3 + hypervisorId: 6 + arch: 1 + name: server 1 + selfService: 0 + selfServiceSettings: [] + hostname: server1.domain.com + commissionStatus: 1 + uuid: 5de5a89b-b707-41bf-a051-7af1a4e67795 + state: queued + migratable: true + timezone: _default + migrateLevel: 0 + deleteLevel: 0 + configLevel: 1 + backupLevel: 0 + elevated: false + elevateId: null + elevate: false + destroyable: true + rebuild: false + suspended: false + protected: false + buildFailed: false + primaryNetworkDhcp4: false + primaryNetworkDhcp6: false + built: '2024-11-29T19:32:17+00:00' + created: '2024-04-11T17:22:19+00:00' + updated: '2025-01-20T13:32:44+00:00' + traffic: + public: + countMethod: 1 + currentPeriod: + start: '2025-01-11T00:00:00.000000Z' + end: '2025-02-10T23:59:59.999999Z' + limit: 200 + settings: + osTemplateInstall: true + osTemplateInstallId: 1 + encryptedPassword: >- + eyJpdiI6IjVsdWVBMzNNWnVXZzlYMjhlTUMzSXc9PSIsInZhbHVlIjoiT2E3SDNmVTVCOW1GK1RCd0h6YjZwZnIva1ZHbU9rQU1VL1hsQSthcUVRYz0iLCJtYWMiOiIzMzdmNjkxOTcwMjkxYmM2ZmNlMjgyMzdkMTQzMDY2OWY1ZTBlYjExYzA1MjdjMzZmMTU1ZTVlMGFiMWY2ZmJlIiwidGFnIjoiIn0= + backupPlan: null + uefi: false + uefiType: 0 + cloudInit: true + cloudInitType: 1 + config: + cloud.init: + on.all: + user.data: + runcmd: + - >- + DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get + --option=Dpkg::Options::=--force-confold + --option=Dpkg::options::=--force-unsafe-io + --assume-yes --quiet update + - >- + DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get + --option=Dpkg::Options::=--force-confold + --option=Dpkg::options::=--force-unsafe-io + --assume-yes --quiet dist-upgrade + on.password: + user.data: [] + on.sshkey: + user.data: [] + on.allpre: + user.data: [] + on.allpost: + user.data: [] + on.network: [] + on.network.libvirtrouted: [] + userConfig: [] + bootOrder: + - hd + - cdrom + tpmType: 0 + networkBoot: false + bootMenu: 1 + customISO: 1 + securityDriver: 3 + memBalloon: + model: 1 + autoDeflate: 0 + freePageReporting: 0 + hyperv: + enabled: false + passthrough: false + relaxed: 0 + vapic: 0 + spinlocks: 0 + vpindex: 0 + runtime: 0 + synic: 0 + stimer: 0 + reset: 0 + vendorId: 0 + frequencies: 0 + reenlightenment: 0 + tlbflush: 0 + ipi: 0 + evmcs: 0 + vendorIdValue: KVM VM + spinlocksValue: 8191 + clockEnabled: 0 + extended: + cpuFlags: + topoext: '1' + svm: '1' + vmx: '1' + machineType: inherit + pciPorts: 16 + resources: + memory: 2048 + storage: 10 + traffic: 200 + cpuCores: 1 + decryptedPassword: uv1dmfUUaENhNpbrGUwD + cpu: + cores: 1 + type: inherit + typeExact: host-model + shares: 1024 + throttle: 0 + topology: + enabled: false + sockets: 1 + cores: 1 + threads: 1 + dies: 1 + customXML: + domain: + xml: '' + enabled: false + os: + xml: '' + enabled: false + devices: + xml: '' + enabled: false + features: + xml: '' + enabled: false + clock: + xml: '' + enabled: false + cpuTune: + xml: '' + enabled: false + qemuCommandline: [] + qemuAgent: + os: + screen: >- + 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 + media: + isoMounted: false + isoType: local + isoName: '' + isoFilename: '' + isoUrl: '' + isoDownload: false + backupPlan: + id: null + name: null + vnc: + ip: 192.168.4.2 + port: 5901 + enabled: false + network: + interfaces: + - id: 9 + order: 1 + enabled: true + tag: 4238114467 + name: eth0 + type: public + driver: 1 + processQueues: null + mac: 00:C3:BA:23:37:B3 + ipv4ToMac: null + ipv6ToMac: null + inTrafficCount: true + outTrafficCount: false + inAverage: 0 + inPeak: 0 + inBurst: 0 + outAverage: 0 + outPeak: 0 + outBurst: 0 + ipFilter: true + vlans: [] + ipFilterType: '4' + portIsolated: false + ipv4_resolver_1: 1 + ipv4_resolver_2: 2 + ipv6_resolver_1: 1 + ipv6_resolver_2: 2 + networkProfile: 0 + dhcpV4: 0 + dhcpV6: 0 + firewallEnabled: false + hypervisorNetwork: 6 + isNat: false + nat: false + firewall: [] + hypervisorConnectivity: + id: 6 + type: simpleBridge + bridge: br0 + mtu: null + primary: true + default: true + ipWhitelist: + - id: 1 + type: 4 + ip: 100.100.100.100 + mask: 32 + - id: 2 + type: 4 + ip: 10.0.0.10 + mask: 32 + actions: [] + ipv4: + - id: 22 + order: 1 + enabled: true + blockId: 1 + address: 192.168.4.21 + gateway: 192.168.4.1 + netmask: 255.255.254.0 + resolver1: 8.8.8.8 + resolver2: 8.8.4.4 + rdns: null + mac: null + - id: 37 + order: 2 + enabled: true + blockId: 1 + address: 192.168.4.36 + gateway: 192.168.4.1 + netmask: 255.255.254.0 + resolver1: 8.8.8.8 + resolver2: 8.8.4.4 + rdns: null + mac: null + - id: 38 + order: 3 + enabled: true + blockId: 1 + address: 192.168.4.37 + gateway: 192.168.4.1 + netmask: 255.255.254.0 + resolver1: 8.8.8.8 + resolver2: 8.8.4.4 + rdns: null + mac: null + ipv6: [] + secondaryInterfaces: [] + storage: + - _id: 11 + id: 1 + cache: null + bus: null + capacity: 10 + drive: a + datastoreDiskId: null + filesystem: null + iops: + read: null + write: null + bytes: + read: null + write: null + type: qcow2 + profile: 0 + status: 3 + enabled: true + primary: true + created: '2024-04-11T17:22:19+00:00' + updated: '2024-04-11T17:22:19+00:00' + datastore: [] + name: 5de5a89b-b707-41bf-a051-7af1a4e67795_1 + filename: 5de5a89b-b707-41bf-a051-7af1a4e67795_1.img + hypervisorStorageId: null + local: true + locationType: mountpoint + path: /home/vf-data/disk + hypervisorAssets: [] + hypervisor: + id: 6 + ip: 192.168.4.2 + hostname: null + port: 8892 + maintenance: false + groupId: 2 + group: + name: Test + icon: null + timezone: Europe/London + forceIPv6: false + vncListenType: 1 + displayName: null + cpuSet: null + nfType: 4 + backupStorageType: 2 + defaultDiskType: inherit + defaultDiskCacheType: inherit + defaultCPU: inherit + defaultMachineType: inherit + created: '2024-03-30T09:53:38+00:00' + updated: '2024-12-06T21:25:54+00:00' + name: BHV 1 + dataDir: /home/vf-data + resources: + servers: + units: '#' + max: 0 + allocated: 5 + free: -5 + percent: null + memory: + units: MB + max: 29419 + allocated: 7168 + free: 22251 + percent: 24.4 + cpuCores: + units: '#' + max: 128 + allocated: 6 + free: 122 + percent: 4.7 + localStorage: + enabled: 1 + name: Local (Default mountpoint) + storageType: 1 + units: GB + max: 1000 + allocated: 141 + free: 859 + percent: 14.1 + otherStorage: [] + owner: + id: 3 + admin: false + extRelationId: 1 + name: jon Doe + email: jon@doe.com + timezone: Europe/London + suspended: false + twoFactorAuth: false + created: '2025-01-20T12:48:20+00:00' + updated: '2025-01-20T13:00:38+00:00' + sshKeys: [] + sharedUsers: [] + tasks: + active: true + lastOn: '2024-11-29 19:32:17' + actions: + pending: [] + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/package/{packageId}: + put: + summary: Change a server package + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 9 + schema: + type: integer + - name: packageId + in: path + description: A valid package ID as shown in VirtFusion. + required: true + example: 1 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + backupPlan: + type: boolean + cpu: + type: boolean + memory: + type: boolean + primaryDiskReadIOPS: + type: boolean + primaryDiskReadThroughput: + type: boolean + primaryDiskSize: + type: boolean + primaryDiskWriteIOPS: + type: boolean + primaryDiskWriteThroughput: + type: boolean + primaryNetworkInboundSpeed: + type: boolean + primaryNetworkOutboundSpeed: + type: boolean + primaryNetworkTraffic: + type: boolean + example: + backupPlan: true + cpu: true + memory: true + primaryDiskReadIOPS: false + primaryDiskReadThroughput: false + primaryDiskSize: true + primaryDiskWriteIOPS: true + primaryDiskWriteThroughput: true + primaryNetworkInboundSpeed: true + primaryNetworkOutboundSpeed: true + primaryNetworkTraffic: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + info: + - CPU cores not updated. It matches the current value + - >- + primary disk not updated. It either matches or is lower than + the current value + - traffic not updated. It matches the current value + - >- + primary network speed inbound not updated. It matches the + current value + - >- + primary network speed outbound not updated. It matches the + current value + - write IOPS not updated. It matches the current value + - write bytes/sec not updated. It matches the current value + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers: + post: + summary: Create a server + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: dryRun + in: query + description: >- + Test to see if a server can be created without actual creation. + true|false Defaults to false. + required: false + example: 'false' + schema: + type: boolean + requestBody: + content: + application/json: + schema: + type: object + properties: + packageId: + type: integer + description: A valid package ID. + userId: + type: integer + description: A valid user ID. + hypervisorId: + type: integer + description: A valid hypervisor group ID. + ipv4: + type: integer + description: Number of IPv4 addresses. + storage: + type: integer + description: Number of GB primary storage. + traffic: + type: integer + description: Number of GB traffic (0=unlimited). + memory: + type: integer + description: Number of MB memory. + cpuCores: + type: integer + description: Number of CPU cores. + networkSpeedInbound: + type: integer + description: Inbound network speed (kB/s). + networkSpeedOutbound: + type: integer + description: Outbound network speed (kB/s). + storageProfile: + type: integer + description: Storage profile ID. + networkProfile: + type: integer + description: Network profile ID. + firewallRulesets: + type: array + items: + type: integer + description: >- + Array of firewall rulesets. This will override package + settings. A value of -1 will force no rulesets to be + applied. + hypervisorAssetGroups: + type: array + items: + type: integer + description: >- + Array of hypervisor asset groups. This will override package + settings. A value of -1 will force no groups to be applied. + additionalStorage1Enable: + type: boolean + description: Enable/disable additional storage 1. + additionalStorage2Enable: + type: boolean + description: Enable/disable additional storage 2. + additionalStorage1Profile: + type: integer + description: Additional storage 1 profile ID. + additionalStorage2Profile: + type: integer + description: Additional storage 2 profile ID. + additionalStorage1Capacity: + type: integer + description: Number of GB additional storage 1 capacity. + additionalStorage2Capacity: + type: integer + description: Number of GB additional storage 2 capacity. + required: + - packageId + - userId + - hypervisorId + example: + packageId: 1 + userId: 1 + hypervisorId: 1 + ipv4: 1 + storage: 20 + traffic: 20 + memory: 512 + cpuCores: 5 + networkSpeedInbound: 200 + networkSpeedOutbound: 400 + storageProfile: 1 + networkProfile: 1 + firewallRulesets: + - 1 + - 2 + hypervisorAssetGroups: + - 3 + - 4 + additionalStorage1Enable: true + additionalStorage2Enable: false + additionalStorage1Profile: 1 + additionalStorage2Profile: 2 + additionalStorage1Capacity: 10 + additionalStorage2Capacity: 20 + responses: + '201': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 70 + ownerId: 1 + hypervisorId: 14 + arch: 1 + name: '' + selfService: 0 + selfServiceSettings: [] + hostname: null + commissionStatus: 0 + uuid: ab68e20a-211f-4b90-99f1-8ee9068c81de + state: allocated + migratable: true + timezone: _default + migrateLevel: 0 + deleteLevel: 0 + configLevel: 0 + backupLevel: 0 + elevated: false + elevateId: null + elevate: false + destroyable: true + rebuild: false + suspended: false + protected: false + buildFailed: false + primaryNetworkDhcp4: false + primaryNetworkDhcp6: false + built: null + created: '2025-01-20T14:00:47+00:00' + updated: '2025-01-20T14:00:47+00:00' + traffic: + public: + countMethod: 1 + currentPeriod: + start: '2025-01-20T00:00:00.000000Z' + end: '2025-02-19T23:59:59.999999Z' + limit: 20 + settings: + osTemplateInstall: true + osTemplateInstallId: 0 + encryptedPassword: >- + eyJpdiI6IkF5L05USXR3OGRNMm80NVFpMXhaVnc9PSIsInZhbHVlIjoiZ0JtclcxSFhoeEdEOGJPa1J6cTVteTllOTh5YU1xM3ViUGphSS9qUTFPMD0iLCJtYWMiOiI3MWFmYzhkY2Y4ZTkxNmNjZWFhZDgzMjZlMjIwZGFhYTg2YTU2OThmYzdjN2MwYzZjNzZhNDBmZTE2MDY4MTc5IiwidGFnIjoiIn0= + backupPlan: null + uefi: false + uefiType: 0 + cloudInit: true + cloudInitType: 1 + config: [] + userConfig: [] + bootOrder: + - hd + - cdrom + tpmType: 0 + networkBoot: false + bootMenu: 1 + customISO: 1 + securityDriver: 3 + memBalloon: + model: 1 + autoDeflate: 0 + freePageReporting: 0 + hyperv: + enabled: false + passthrough: false + relaxed: 0 + vapic: 0 + spinlocks: 0 + vpindex: 0 + runtime: 0 + synic: 0 + stimer: 0 + reset: 0 + vendorId: 0 + frequencies: 0 + reenlightenment: 0 + tlbflush: 0 + ipi: 0 + evmcs: 0 + vendorIdValue: WIN KVM + spinlocksValue: 8191 + clockEnabled: 0 + extended: + cpuFlags: + topoext: '1' + svm: '1' + vmx: '1' + machineType: inherit + pciPorts: 16 + resources: + memory: 512 + storage: 20 + traffic: 20 + cpuCores: 5 + cpu: + cores: 5 + type: inherit + typeExact: host-model + shares: 1024 + throttle: 0 + topology: + enabled: false + sockets: 1 + cores: 5 + threads: 1 + dies: 1 + customXML: + domain: + xml: '' + enabled: false + os: + xml: '' + enabled: false + devices: + xml: '' + enabled: false + features: + xml: '' + enabled: false + clock: + xml: '' + enabled: false + cpuTune: + xml: '' + enabled: false + qemuCommandline: [] + qemuAgent: + os: + screen: >- + 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UPvQ+TVr1rhVq1alv6JrF52j48RM7IpCL/bqGo961KOyy8i3sikf5E9dV0HHyIfrr79+tq1zeklX17AyoIuojKisWPDznS9Do6Ojmc0rV650Dz/8sJ3GsiIC1KArAutfdvfdd3d//etf3b///W+3bNky96tf/cr985//dMuXL3fvfve70z86/3itz5gxw33pS19yt912W/r79a9/7S688EJ33333uauvvtq94Q1vyJ/i3vrWt6bxOubee+918+fPbztmt912y+J/8pOftMUtWLDAXXfddel5F1xwgfvFL36R3gxk88knn5zeVNpOSDYmYuNf/vKXNA3Zd/jhh+cv2bb9tre9LbNXeer022abbdrO9TduuOGGtc6VMElglL/vfOc7btNNN/VPSVkUpSeBe+ihh9zNN9/svvrVr7Zx6cXe5z//+W02vf3tb29L3zYuuuii7DiVhw984APZ9llnnZUe1ku6T3/607Pzlb8XvOAFlpR77GMfm/KwfP/rX//Kbvg6SDZa3N/+9rfsPFaqI4BAV8c2vfIHP/hBd+6557oddtjBDQ+345bAnXrqqcl3P9q/zLTddts5/QEccMABac3UN1E1QYmRzvn+97/vVKspCqppffazn12r5mjHPuEJT7BVd8ghh7ilS5e6LbfcMttnK6rBHXHEEc7EwPZP1EY9PaiWrp/VBO2adSxVY1y4cKG79NJL3ZOe9KSuTJDdc+bMcW9+85vTm+bGG2/c1Xn+Qddff7275557sl2ve93rsnVb0ZOMb9M//vEPi0qXvg/bIjps3H333U61Xws777yzrbo99tgjW9eK8unH++u6wRKqJ9CuGNWn16gUnv3sZ7vFixdnwqxHcNWk1XTgh9e+9rXuuc99brpLgisxnDt3bnaIanuqPetc/3F07733Tmvg2YG5Ff1BLVq0KLd37c33vOc9mZDrkflPf/qT+8Mf/tD2+L/nnns6PQko9NPGta0p3qPmiEsuuaTw9/Of/9zdcsstxSfm9oq9xPiaa65pa1KYNWuWKxJJna4as9LWTfOKK65oa0pR09NLX/rSXCouZTeevaq5W3jOc56T1mBtW0vdOCyo7HzjG9+wzdJlN5zkXwu77LKLra4l0IpQTd+Cf+xvf/tb282yQgIIdIVwP/GJT2TCJ5F9+ctfnjY7PO5xj3Ovec1r2lL+6Ec/mm6/6U1v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 + media: + isoMounted: false + isoType: local + isoName: '' + isoFilename: '' + isoUrl: '' + isoDownload: false + backupPlan: + id: null + name: null + vnc: + ip: 192.168.30.6 + port: 5904 + enabled: false + network: + interfaces: + - id: 70 + order: 1 + enabled: true + tag: 6927490480 + name: eth0 + type: public + driver: null + processQueues: null + mac: 00:BA:76:AB:DF:4E + ipv4ToMac: null + ipv6ToMac: null + inTrafficCount: true + outTrafficCount: false + inAverage: 200 + inPeak: 0 + inBurst: 0 + outAverage: 400 + outPeak: 0 + outBurst: 0 + ipFilter: true + vlans: [] + ipFilterType: '4' + portIsolated: false + ipv4_resolver_1: 1 + ipv4_resolver_2: 2 + ipv6_resolver_1: 1 + ipv6_resolver_2: 2 + networkProfile: 0 + dhcpV4: 0 + dhcpV6: 0 + firewallEnabled: false + hypervisorNetwork: 14 + isNat: false + nat: false + firewall: [] + hypervisorConnectivity: + id: 14 + type: simpleBridge + bridge: br0 + mtu: null + primary: true + default: true + ipWhitelist: [] + actions: [] + ipv4: + - id: 520 + order: 1 + enabled: true + blockId: 3 + address: 192.168.30.207 + gateway: 192.168.30.1 + netmask: 255.255.255.0 + resolver1: 8.8.8.8 + resolver2: 8.8.4.4 + rdns: null + mac: null + ipv6: [] + secondaryInterfaces: [] + storage: + - _id: 81 + id: 1 + cache: null + bus: null + capacity: 20 + drive: a + datastoreDiskId: null + filesystem: null + iops: + read: null + write: null + bytes: + read: null + write: null + type: qcow2 + profile: 1 + status: 3 + enabled: true + primary: true + created: '2025-01-20T14:00:47+00:00' + updated: '2025-01-20T14:00:47+00:00' + datastore: [] + name: ab68e20a-211f-4b90-99f1-8ee9068c81de_1 + filename: ab68e20a-211f-4b90-99f1-8ee9068c81de_1.img + hypervisorStorageId: null + local: true + locationType: mountpoint + path: /home/vf-data/disk + - _id: 82 + id: 2 + cache: null + bus: null + capacity: 10 + drive: b + datastoreDiskId: null + filesystem: null + iops: + read: null + write: null + bytes: + read: null + write: null + type: qcow2 + profile: 0 + status: 1 + enabled: false + primary: false + created: '2025-01-20T14:00:47+00:00' + updated: '2025-01-20T14:00:47+00:00' + datastore: [] + name: ab68e20a-211f-4b90-99f1-8ee9068c81de_2 + filename: ab68e20a-211f-4b90-99f1-8ee9068c81de_2.img + hypervisorStorageId: null + local: true + locationType: mountpoint + path: /home/vf-data/disk + hypervisorAssets: [] + hypervisor: + id: 14 + ip: 192.168.30.6 + hostname: null + port: 8892 + maintenance: false + groupId: 1 + group: + name: Default + icon: null + timezone: Europe/London + forceIPv6: false + vncListenType: 1 + displayName: null + cpuSet: null + nfType: 4 + backupStorageType: 2 + defaultDiskType: inherit + defaultDiskCacheType: inherit + defaultCPU: inherit + defaultMachineType: inherit + created: '2024-05-14T11:19:04+00:00' + updated: '2024-06-28T21:22:01+00:00' + name: Ceph Hypervisor 2 + dataDir: /home/vf-data + resources: + servers: + units: '#' + max: 0 + allocated: 4 + free: -4 + percent: null + memory: + units: MB + max: 24000 + allocated: 3584 + free: 20416 + percent: 14.9 + cpuCores: + units: '#' + max: 64 + allocated: 8 + free: 56 + percent: 12.5 + localStorage: + enabled: 1 + name: Local (Default mountpoint) + storageType: 1 + units: GB + max: 1000 + allocated: 40 + free: 960 + percent: 4 + otherStorage: + - id: 2 + name: Ceph RBD + enabled: 0 + path: null + units: GB + storageType: 2 + isDatastore: true + max: 10000 + allocated: 10 + free: 9990 + percent: 0.1 + - id: 3 + name: Ceph EC + enabled: 0 + path: null + units: GB + storageType: 2 + isDatastore: true + max: 13333333 + allocated: 10 + free: 13333323 + percent: 0 + owner: + id: 1 + admin: true + extRelationId: null + name: Jon Doe + email: jon@doe.com + timezone: Europe/London + suspended: false + twoFactorAuth: false + created: '2024-03-12T22:22:09+00:00' + updated: '2025-01-15T11:01:18+00:00' + sshKeys: [] + sharedUsers: [] + tasks: + active: false + lastOn: null + actions: + pending: + - id: 19 + action: Create HDD (sdb) + requires: + - boot + - restart + - shutdown + - poweroff + collected: false + complete: false + failed: false + payload: + disk: + id: 82 + disk_storage_id: null + created: '2025-01-20T14:00:47+00:00' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + '422': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + errors: + - Invalid or disabled firewall ruleset + headers: {} + security: + - bearer: [] + get: + summary: Retrieve servers + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: type + in: query + description: simple or full. Defaults to simple. + required: false + example: simple + schema: + type: string + - name: results + in: query + description: >- + Number of results to return. Range between 1 and 200. Defaults to + 20. + required: false + example: 20 + schema: + type: integer + - name: hypervisorId + in: query + description: >- + Filter by hypervisor ID. Specify multiple with + hypervisorId[]=1&hypervisorId[]=2 etc... + required: false + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + current_page: 1 + data: + - id: 5 + uuid: 1fb4b391-b360-40e7-8fe1-5b024c7508ac + name: Avaricious Trade + commissioned: 3 + owner: 1 + hypervisorId: 7 + suspended: false + protected: false + updated: '2024-04-02T10:15:10+00:00' + created: '2024-03-30T14:41:27+00:00' + - id: 8 + uuid: 82c37680-bf8f-4712-854f-31428933703f + name: PDNS + commissioned: 3 + owner: 1 + hypervisorId: 3 + suspended: false + protected: false + updated: '2024-04-13T22:02:04+00:00' + created: '2024-04-09T11:33:43+00:00' + - id: 9 + uuid: 5de5a89b-b707-41bf-a051-7af1a4e67795 + name: server 1 + commissioned: 2 + owner: 3 + hypervisorId: 6 + suspended: false + protected: false + updated: '2025-01-20T14:13:50+00:00' + created: '2024-04-11T17:22:19+00:00' + - id: 10 + uuid: 71178184-7554-406f-80b8-0c1d7ffcfd49 + name: Respectful Exit + commissioned: 3 + owner: 1 + hypervisorId: 6 + suspended: false + protected: false + updated: '2024-05-13T08:16:00+00:00' + created: '2024-04-23T11:50:58+00:00' + - id: 11 + uuid: ffed8ddb-c758-41ff-8380-abb1377dfb38 + name: Ubuntu Test + commissioned: 0 + owner: 1 + hypervisorId: 7 + suspended: false + protected: false + updated: '2024-05-02T18:33:20+00:00' + created: '2024-04-25T20:18:57+00:00' + - id: 19 + uuid: c77ce40f-0226-43ca-b000-c9b7fe143dc7 + name: Metallic National + commissioned: 3 + owner: 1 + hypervisorId: 2 + suspended: false + protected: false + updated: '2024-05-02T18:34:27+00:00' + created: '2024-05-02T10:36:37+00:00' + - id: 20 + uuid: 785aaddd-b08b-448b-9486-baf29cd3c0f8 + name: Rubbery Daughter + commissioned: 3 + owner: 1 + hypervisorId: 7 + suspended: false + protected: false + updated: '2024-10-07T21:32:34+00:00' + created: '2024-05-03T10:05:41+00:00' + - id: 22 + uuid: 5a7e3d49-0fdf-4cfa-bb14-864f3ca0e79a + name: Frightening Clock + commissioned: 3 + owner: 1 + hypervisorId: 7 + suspended: false + protected: false + updated: '2024-06-08T08:30:15+00:00' + created: '2024-05-03T10:35:36+00:00' + - id: 23 + uuid: b1f6efb6-22a1-4d0a-b043-17d0ccfce4b2 + name: Backup Test + commissioned: 3 + owner: 1 + hypervisorId: 6 + suspended: false + protected: false + updated: '2024-05-14T15:29:37+00:00' + created: '2024-05-04T07:30:10+00:00' + - id: 26 + uuid: 5c681c72-6828-4fa3-8011-ced2502384e6 + name: Ceph Test 1 + commissioned: 3 + owner: 1 + hypervisorId: 13 + suspended: false + protected: false + updated: '2024-05-14T11:42:08+00:00' + created: '2024-05-14T10:57:56+00:00' + - id: 27 + uuid: 8cb75e06-caae-47f5-9bf2-3ea1d341d10e + name: OVS BHV 6 + commissioned: 3 + owner: 1 + hypervisorId: 11 + suspended: false + protected: false + updated: '2024-05-17T13:25:10+00:00' + created: '2024-05-16T16:56:12+00:00' + - id: 28 + uuid: 3a63170a-2350-422d-8cfb-449ed6940414 + name: OVS BHV 7 + commissioned: 3 + owner: 1 + hypervisorId: 12 + suspended: false + protected: false + updated: '2024-05-17T13:25:04+00:00' + created: '2024-05-16T18:13:44+00:00' + - id: 29 + uuid: f24aebac-016c-4139-afcf-5dbfeda54fc8 + name: OVS BHV 1 + commissioned: 3 + owner: 1 + hypervisorId: 6 + suspended: false + protected: false + updated: '2024-05-17T13:25:00+00:00' + created: '2024-05-17T11:25:13+00:00' + - id: 30 + uuid: 67486d4d-d974-45c3-a680-980bc84635d8 + name: Test 10 + commissioned: 3 + owner: 1 + hypervisorId: 1 + suspended: false + protected: false + updated: '2024-06-07T16:41:45+00:00' + created: '2024-06-07T12:03:00+00:00' + - id: 36 + uuid: a3df9e3c-893e-4f42-ad90-cf34df155589 + name: Frail Text + commissioned: 3 + owner: 1 + hypervisorId: 13 + suspended: false + protected: false + updated: '2024-06-28T21:25:57+00:00' + created: '2024-06-28T13:39:55+00:00' + - id: 37 + uuid: a3b2e9f8-9b5c-44a3-bcb6-bbadf9bd83e2 + name: Stark Brown + commissioned: 3 + owner: 1 + hypervisorId: 13 + suspended: false + protected: false + updated: '2024-08-23T20:15:25+00:00' + created: '2024-06-28T21:36:23+00:00' + - id: 38 + uuid: 8c6f63d1-ec53-4e1a-a52f-d50f03b05c70 + name: '' + commissioned: 0 + owner: 1 + hypervisorId: 14 + suspended: false + protected: false + updated: '2024-08-23T20:17:42+00:00' + created: '2024-08-23T20:17:42+00:00' + - id: 39 + uuid: 539bff72-f6cd-4260-96f1-b7523fd890c5 + name: Thorny Impression + commissioned: 3 + owner: 1 + hypervisorId: 14 + suspended: false + protected: false + updated: '2024-08-23T20:20:32+00:00' + created: '2024-08-23T20:18:39+00:00' + - id: 40 + uuid: ce445459-c716-4f88-a7c6-a0ffd29eb9b2 + name: Present Charge + commissioned: 2 + owner: 1 + hypervisorId: 14 + suspended: false + protected: false + updated: '2024-08-23T20:57:22+00:00' + created: '2024-08-23T20:56:04+00:00' + - id: 41 + uuid: 6fce272f-c6ea-45bd-bf24-d4d357d9a788 + name: CP Test + commissioned: 3 + owner: 1 + hypervisorId: 13 + suspended: false + protected: false + updated: '2024-08-27T11:10:48+00:00' + created: '2024-08-27T11:09:54+00:00' + first_page_url: https://192.168.3.11/api/v1/servers?page=1 + from: 1 + last_page: 2 + last_page_url: https://192.168.3.11/api/v1/servers?page=2 + links: + - url: null + label: '« Previous' + active: false + - url: https://192.168.3.11/api/v1/servers?page=1 + label: '1' + active: true + - url: https://192.168.3.11/api/v1/servers?page=2 + label: '2' + active: false + - url: https://192.168.3.11/api/v1/servers?page=2 + label: Next » + active: false + next_page_url: https://192.168.3.11/api/v1/servers?page=2 + path: https://192.168.3.11/api/v1/servers + per_page: 20 + prev_page_url: null + to: 20 + total: 27 + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/modify/name: + put: + summary: Modify name + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: The new name of the server. + required: + - name + example: + name: Server 1 + responses: + '201': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/resetPassword: + post: + summary: Reset a server password + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + user: + type: string + description: Either root or Administrator. + sendMail: + type: boolean + description: >- + Optional (default true) Email the password to the user. + (true|false). + required: + - user + example: + user: root + sendMail: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + queueId: 176 + expectedPassword: l1LMzm2JGhWYdjjn8JkC + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/user/{userId}: + get: + summary: Retrieve a users servers + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: userId + in: path + description: A valid user ID as shown in VirtFusion. + required: true + example: 3 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + - id: 9 + ownerId: 3 + hypervisorId: 6 + name: server 1 + hostname: server1.domain.com + commissionStatus: 2 + uuid: 5de5a89b-b707-41bf-a051-7af1a4e67795 + state: failed + rebuild: false + suspended: false + protected: false + buildFailed: false + backup_level: 0 + backup_plan: null + os: + screen: >- + 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 + server_info: + show: false + icon: null + name: null + label: null + vnc: + expose_details: true + ip: 192.168.4.2 + hostname: null + port: 5901 + enabled: 0 + resources: + memory: 2048 + storage: 10 + traffic: 200 + cpuCores: 1 + cpu_model: null + network: + interfaces: + - order: 1 + enabled: true + tag: 4238114467 + name: eth0 + mac: 00:C3:BA:23:37:B3 + inAverage: 0 + inPeak: 0 + inBurst: 0 + outAverage: 0 + outPeak: 0 + outBurst: 0 + isNat: false + ipv4: + - order: 1 + enabled: true + address: 192.168.4.21 + gateway: 192.168.4.1 + netmask: 255.255.254.0 + resolver1: 8.8.8.8 + resolver2: 8.8.4.4 + - order: 2 + enabled: true + address: 192.168.4.36 + gateway: 192.168.4.1 + netmask: 255.255.254.0 + resolver1: 8.8.8.8 + resolver2: 8.8.4.4 + - order: 3 + enabled: true + address: 192.168.4.37 + gateway: 192.168.4.1 + netmask: 255.255.254.0 + resolver1: 8.8.8.8 + resolver2: 8.8.4.4 + ipv6: [] + config: + uefi: false + bootOrder: + - hd + - cdrom + media: + isoMounted: false + isoName: '' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/templates: + get: + summary: Retrieve OS templates available to a server + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + - name: Debian + description: >- + Debian GNU/Linux, is a Linux distribution composed of free + and open-source software, developed by the + community-supported Debian Project. + icon: debian_logo.png + templates: + - id: 8 + name: Debian + version: 11 (Bullseye) + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Debian. + icon: debian_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 46 + name: Debian + version: 12 (Bookworm) + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Debian. + icon: debian_logo.png + eol: false + eol_date: '2024-04-23 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 56 + name: Debian + version: 12 (Bookworm) + variant: Test + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Debian. + icon: debian_logo.png + eol: false + eol_date: '2024-04-23 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: true + type: linux + id: 1 + - name: CentOS + description: >- + The CentOS Linux distribution is a stable, predictable, + manageable and reproducible platform derived from the + sources of Red Hat Enterprise Linux (RHEL). + icon: centos_logo.png + templates: + - id: 1 + name: CentOS + version: '7' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Yum, the main + command-line package manager for CentOS. + icon: centos_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 2 + name: CentOS Stream + version: '9' + variant: Minimal + arch: 1 + description: >- + Base installation with limited packages. New packages + are easily installed using DNF (yum), the main + command-line package manager for CentOS. + icon: centos_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + id: 2 + - name: Rocky Linux + description: >- + Rocky Linux is a community enterprise operating system + designed to be 100% bug-for-bug compatible with America's + top enterprise Linux distribution now that its downstream + partner has shifted direction. It is under intensive + development by the community. Rocky Linux is led by + Gregory Kurtzer, founder of the CentOS project. + icon: rocky_linux_logo.png + templates: + - id: 7 + name: Rocky Linux + version: '8' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for Rocky Linux. + icon: rocky_linux_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 13 + name: Rocky Linux + version: '9' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for Rocky Linux. + icon: rocky_linux_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + id: 3 + - name: AlmaLinux + description: >- + AlmaLinux OS is an open-source, community-driven project + that intends provide and alternative to the CentOS Stable + release. AlmaLinux is an OS that is 1:1 binary compatible + with RHEL® 8 and a global collaborative of the developer + community, industry, academia and research which build + upon this technology to empower humanity. + icon: almalinux_logo.png + templates: + - id: 6 + name: AlmaLinux + version: '8' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for AlmaLinux. + icon: almalinux_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 12 + name: ARM -> AlmaLinux + version: '9' + variant: Latest + arch: 1 + description: >- + Latest version with base packages. New packages are + easily installed using DNF (yum), the main + command-line package manager for AlmaLinux. + icon: almalinux_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + id: 4 + - name: Ubuntu + description: >- + The most popular server Linux in the cloud and data + centre, you can rely on Ubuntu Server and its five years + of guaranteed free upgrades. + icon: ubuntu_logo.png + templates: + - id: 3 + name: Ubuntu Server + version: 20.04 LTS (Focal Fossa) + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Ubuntu. + icon: ubuntu_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 4 + name: Ubuntu Server + version: 18.04 LTS (Bionic Beaver) + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Ubuntu. + icon: ubuntu_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 9 + name: Ubuntu Server + version: 22.04 LTS (Jammy Jellyfish) + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Ubuntu. + icon: ubuntu_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 49 + name: Ubuntu Server + version: 24.04 LTS (Noble Numbat) + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Ubuntu. + icon: ubuntu_logo.png + eol: false + eol_date: '2024-04-25 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + id: 5 + - name: Fedora + description: >- + Fedora Server is a powerful, flexible operating system + that includes the best and latest datacenter technologies. + It puts you in control of all your infrastructure and + services. + icon: fedora_logo.png + templates: + - id: 11 + name: Fedora + version: '37' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for Fedora. + icon: fedora_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 14 + name: Fedora + version: '38' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for Fedora. + icon: fedora_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 15 + name: Fedora + version: '39' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for Fedora. + icon: fedora_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 59 + name: Fedora + version: '41' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for Fedora. + icon: fedora_logo.png + eol: false + eol_date: '2024-12-18 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + id: 6 + - name: FreeBSD + description: >- + FreeBSD is an operating system used to power modern + servers, desktops, and embedded platforms. A large + community has continually developed it for more than + thirty years. Its advanced networking, security, and + storage features have made FreeBSD the platform of choice + for many of the busiest web sites and most pervasive + embedded networking and storage devices. + icon: freebsd_logo.png + templates: + - id: 52 + name: FreeBSD + version: '13.3' + variant: Minimal + arch: 1 + description: Minimal installation with limited packages. + icon: freebsd_logo.png + eol: false + eol_date: '2024-05-15 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: unix + - id: 53 + name: FreeBSD + version: '14.0' + variant: Minimal + arch: 1 + description: Minimal installation with limited packages. + icon: freebsd_logo.png + eol: false + eol_date: '2024-05-15 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: unix + - id: 55 + name: FreeBSD + version: '14.2' + variant: Minimal + arch: 1 + description: Minimal installation with limited packages. + icon: freebsd_logo.png + eol: false + eol_date: '2024-10-20 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: true + type: unix + - id: 58 + name: FreeBSD + version: '13.2' + variant: Minimal + arch: 1 + description: Minimal installation with limited packages. + icon: freebsd_logo.png + eol: false + eol_date: '2024-12-10 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: unix + id: 7 + - name: Other + description: '' + icon: linux_logo.png + templates: + - id: 5 + name: openSUSE + version: Leap 15 + variant: Minimal + arch: 1 + description: >- + openSUSE is a project that serves to promote the use + of free and open-source software.

Minimal + installation with limited packages. New packages are + easily installed using Zypper, the main command-line + package manager for openSUSE. + icon: opensuse_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + id: 0 + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/suspend: + post: + summary: Suspend a server + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/modify/cpuThrottle: + put: + summary: Throttle a servers CPU + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + - name: sync + in: query + description: >- + Synchronise and apply the defined percentage. true|false Defaults to + false. + required: false + example: 'true' + schema: + type: boolean + requestBody: + content: + application/json: + schema: + type: object + properties: + percent: + type: integer + description: The percentage the CPU should be throttled (0-99). + required: + - percent + example: + percent: 50 + responses: + '201': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/traffic: + get: + summary: Retrieve a servers traffic statistics + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + monthly: + - month: 2 + start: '2025-01-06 00:00:00' + end: '2025-02-05 23:59:59' + rx: 1847110337 + tx: 1270421 + total: 1848380758 + limit: 20000 + blocks: + - id: 2 + traffic: 100 + - month: 1 + start: '2024-12-06 00:00:00' + end: '2025-01-05 23:59:59' + rx: 5650592916 + tx: 42336801 + total: 5692929717 + limit: 20000 + blocks: [] + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/unsuspend: + post: + summary: Unsuspend a server + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/vnc: + post: + summary: Enable or disable VNC + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + action: + type: string + enum: + - enable + - disable + required: + - action + example: + action: enable + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + vnc: + ip: 192.168.4.2 + hostname: null + port: 5903 + password: ZNYonJeU + wss: + token: 69316231-d34a-4d36-b754-ffd3253df96d + url: /vnc/?token=69316231-d34a-4d36-b754-ffd3253df96d + enabled: false + queueId: null + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + get: + summary: Retrive VNC details + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + vnc: + ip: 192.168.4.2 + hostname: null + port: 5903 + password: ZNYonJeU + wss: + token: 69316231-d34a-4d36-b754-ffd3253df96d + url: /vnc/?token=69316231-d34a-4d36-b754-ffd3253df96d + enabled: false + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/owner/{newOwnerId}: + put: + summary: Change owner + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 9 + schema: + type: integer + - name: newOwnerId + in: path + description: A vailid user ID as shown in VirtFusion. + required: true + schema: + type: integer + responses: + '201': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/modify/memory: + put: + summary: Modify memory + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + memory: + type: integer + description: The new memory value in MB. + minimum: 256 + example: 1024 + required: + - memory + example: + memory: 1024 + responses: + '201': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/modify/cpuCores: + put: + summary: Modify CPU cores + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + cores: + type: integer + description: The new core value. + minimum: 1 + maximum: 600 + example: 4 + required: + - cores + example: + cores: 4 + responses: + '201': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /servers/{serverId}/customXML: + post: + summary: Set custom XML + deprecated: false + description: '' + tags: + - Servers + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 69 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + domain: + type: string + os: + type: string + devices: + type: string + features: + type: string + clock: + type: string + cpuTune: + type: string + domainEnabled: + type: boolean + osEnabled: + type: boolean + devicesEnabled: + type: boolean + featuresEnabled: + type: boolean + clockEnabled: + type: boolean + cpuTuneEnabled: + type: boolean + example: + domain: + os: + devices: + features: + clock: + cpuTune: + domainEnabled: true + osEnabled: true + devicesEnabled: true + featuresEnabled: true + clockEnabled: true + cpuTuneEnabled: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: '' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /compute/hypervisors: + get: + summary: Retrieve hypervisors + deprecated: false + description: '' + tags: + - Hypervisors + parameters: + - name: results + in: query + description: >- + Number of results to return. Range between 1 and 200. Defaults to + 20. + required: false + example: 20 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + current_page: 1 + data: + - id: 1 + commissioned: 3 + ip: 192.168.4.10 + ipAlt: null + hostname: null + port: 8892 + sshPort: 22 + name: PHV 1 (RED) + maintenance: false + enabled: true + nfType: 4 + group: + id: 1 + name: Default + description: Default hypervisor group + default: true + enabled: true + distributionType: 5 + created: '2024-03-12T22:21:32+00:00' + updated: '2024-04-12T20:56:04+00:00' + encryptedToken: >- + eyJpdiI6Ik1Ua29ZSGp0QThxWVZhellzL2VTU3c9PSIsInZhbHVlIjoiNzc1eGdMMzFPUFpFZVpIbytzMDc1NzRsUHRJVnFTWFpKWS9WamJIaVJVMVZkSFZjZVM1YVB3bnlQeGt4eEhVamhrWGF4SnNqQVFES010Y3owUmJneTR4a05oRkp1R08xVXI1eHcvQ3NsbW5qU0dpUWhZbnFUMWYrTHM5L2NoZmhUQm9nRnV4b2Y0dENGLy9vanVDMnkwTG1mNXBYM1JVcE5TNWRCSGkvZS9qVEFsSWx5WXdXOU1wajIwam1DV1d4aUNXMUNGMThFNXI5THM4VWFmYnRFNkx3VHFaV3o3M0VVaEZXSHo0TVdKc0xSemJYVExUWEVlZHM0ZVNoUkk0ZEI2QnAySlVESVU2R0JDcWJMeG9YRUhIM0Vad2w2VHNGcFQ3R1BkbU1TbzU3V2JzbEJFNlUvSW90eGxNZkdqRjVmMGx6TTRIWEttYVA0Ti9JQkEwQURrWTRPL2k4VFJsNjhFTHh3UW1wSGMzUkxibEtDeDdlK2tOekQxVkh0bzhsWXY1RkxxaWRkSFBEQlNvM1l2akxqNitickp1TzR0ekhTbmdVSG5VUE5tMGh1WFJuejhscFpSS2dLcE1ZaS9NUlRKdnNUS0wzYWlDYjB1MVJhcmk4OEJoZURNQ3JROE5WcTZTdzV0Si9UeDhwMTFLK3lZV0NDdzB5b2NBZFhsM0hYMDJPMHlXS1g1MmxhNWdrOTRTSDJHbWNvODNuOUswMHJpYTVBL0YwRW9BVndsMllIdW95ZjBhZXdLUTRSR0xBelBVekViTCtKaG8wSGxPR1NOWmNSaXpxQ1hBUVdsdE9HMUhtc2YrRU14WkhOaUVVeWhXRlB2amtRRXkxZjY0cm85ekxVYWE1QU5zdlJDK2N6YmZrNHNOWk4xSTZXbUhxYklLTmgraTZFWHM9IiwibWFjIjoiOTY2ZmJkNzJkNzZmNmZmYTQzM2U4NDQzMDdhYTAzOWZhNTM0M2I1MDQyYWUwYzQ1ZGIyZTRlOGEwM2M0MTRhYiIsInRhZyI6IiJ9 + maxServers: 0 + maxCpu: 4 + maxMemory: 6004 + networks: + - id: 1 + type: simpleBridge + bridge: br0 + primary: true + default: true + created: '2024-03-12T22:37:15+00:00' + updated: '2024-03-12T22:37:40+00:00' + storage: [] + created: '2024-03-12T22:37:15+00:00' + updated: '2024-05-10T11:27:52+00:00' + - id: 2 + commissioned: 3 + ip: 192.168.4.9 + ipAlt: null + hostname: null + port: 8892 + sshPort: 22 + name: PHV 2 (BLUE) + maintenance: false + enabled: true + nfType: 4 + group: + id: 1 + name: Default + description: Default hypervisor group + default: true + enabled: true + distributionType: 5 + created: '2024-03-12T22:21:32+00:00' + updated: '2024-04-12T20:56:04+00:00' + encryptedToken: >- + eyJpdiI6IjgyR2FZZmJwalVDYUxBR2hMdXdTNmc9PSIsInZhbHVlIjoiSFA1Tm44VzdZMUdZSXNZSTNUdmF5dGo5WjIrZEJlU04xQlVIOEZnUzc3dFZRY0ltc3pLdTZ2SFdkUXlQWUF5UEVaZUE3dXVKQXV6ajZvUTZiY0lBQmlnVllvRDZMSDBYK0ZMV0d5dzRTZlYwaDFRcllsNEdGaTljQnpnbEg5Umt6SmdBZ3ZQL0RneTZEUHhKanFGZU9hSWtvR29lYVlLdzk5NTJNZE1hbExSaWtuMkE4cTVaSGxSbWlJZ3pHejhFWnFxbEltNUcrSXVIdE4rQW9ET2R0M0MrK0RHOXNhOFFuVEw1R2k4eEpDNmZiNWJPVS9NL2xrVk40eG93NzQxaTRFN0pBR0FEL2JTclIvd2xWM3JkbnltZGhrc0xkUzV0SGtKNVoyU2JFY2M0S3dyVXEzS256b1ZHOVRvSWlmNm9OT0d6TktEWUduaVBHT2VHaFpqakU2SjFhU2lqTUZPeVdRN3dWSjhnakVQYkFiVmpCK05ja3BVU3FxakNjUUEzRHl3WUZweFJuQ0FBVkR2eTcxLzVVR2ZPNHU2bDJGRTJ6bVkrZ201akZXT0JIeHByK2VQVmMwUEJ5aDM0TWI0RmViakprM0phVXRVMFUvU1Y5M0FCRTBORm1aNWtGUklRbW51Y010NWIzUE02Vno3SkU2MVk4WTUwRy9QUndTZEgwWmRiLzhiV0w1c0ZsNkRMZkNycUlabWQrQ0F5cnYxamgxcWZjNjk3NXlMZHNMdnZqZkhKdG1sY2VLVmFPUTI3OTJ4UVdGLzF4SE83Y0N5dEhNNUhSQWhoZ29uUXMvR3dGeHVUMlRjc3dYZkVrYVVUMWZVQjhZOVRBT1RXYk84bFpqbGZ5RGQ2Rncvb2lQbVh0djBnSHUwSWJKbnAzQmQrL1VIK1JOK2N4NE09IiwibWFjIjoiNGUyNTIxYTIyNDBjZmRiYmEzNmQ5NTc4MGZmMTU4ZjgwN2Q2OTQzYzFhYTgxODVmYzkyMmU3YWNiOGRmNWZjZSIsInRhZyI6IiJ9 + maxServers: 0 + maxCpu: 28 + maxMemory: 10000 + networks: + - id: 2 + type: simpleBridge + bridge: br0 + primary: true + default: true + created: '2024-03-16T19:31:43+00:00' + updated: '2024-03-16T19:32:46+00:00' + storage: [] + created: '2024-03-16T19:31:43+00:00' + updated: '2024-04-26T16:41:51+00:00' + - id: 3 + commissioned: 3 + ip: 192.168.4.12 + ipAlt: null + hostname: null + port: 8892 + sshPort: 22 + name: BHV 9 + maintenance: false + enabled: true + nfType: 4 + group: + id: 1 + name: Default + description: Default hypervisor group + default: true + enabled: true + distributionType: 5 + created: '2024-03-12T22:21:32+00:00' + updated: '2024-04-12T20:56:04+00:00' + encryptedToken: >- + eyJpdiI6IlZ6MFN4dnlvQm9DTXNsaDM3YWg1aUE9PSIsInZhbHVlIjoiamlrbEhzRlY5d0RRaUxMYkZIUEV2UHh5eVJVMHEwOGQ2QkVIS0tydW82Um15bGFPVHJQbmUzMDMvbGxyZkEyR0MwT1JUR3ZNaDUvZWE5UFYwOExOU2xKNDhUa2g5VnNqQ2NoamptNnp3dmY0VVhzSXEwTEsxWDRwMDdtMyttdmp2UVRHOFJsb3Q3VkpKWEw0N3JmelAxNWZGWTVRQ1lCWHFpM3N5anFnaDNlcVFWazV6ci9Fem9xQTYrYXpNeUw0a3Jobm85aFRweCsxQVNoOWJrVktveWczYm5CL3VyNVhqWHlFbEpRYUtINzhwMCtEN3N4aEEwdTQ0YzdSbVhqQk9BTDVkN3F0aDFHWngwQU1iNDJKT1BRT25LYjZacklPM1llL2hQRWJab1l5QVdTQUtiVkZXcXROZC9xOWxLdzROTUprRkUxWkNjY2l3TnIzYXk5YiswNkhIQTlKejI3YXhPc2xXRklETmtYRkNNWlIzT1RHZkZTVGMvY1lra2JaemNQcW8vUEFLbEROS3dJQkorSVNUeTJESzZtV2tUV0Q0Nk5QVWRvUnJUbWhkVFlwZmphNXZXanFUTi9SbnVacTJXUzhYZW8zby95RG9jVWJDT25UMHU3dVZSN1UrS3RxRFhlM3diYkhxL1g3OXZIdmwzUzhCZmpjN3ZpMkhlRlNSMmNPMzduektWRGpYOFQ2UktIQjdnaEVoZy95MmZYK0c1dTZOemZ0VXpxbHpneVlndkp0anNuN2Y4bXlScXhoWFQrai9yL2wrWWhLNGlGWGVhc09iSEQrRzYrOThCY1czUTdnd0pOTFdSZ05uNUU5QUZPVmtHOENBRDljOFN1UjBteUdacmZYZWdtM2RodXg4dEx5cExGZVZ0TTNrQTVVTFlTZU0vZ28xLzA9IiwibWFjIjoiNzdmZTc5MDI2NjY3OTc2NzhmMjJiMWY3ZGNjNGI4YmNkZTNiNzE0MmNlODdkYzNlNzIxNDU5NmVlMjJmODNhYSIsInRhZyI6IiJ9 + maxServers: 0 + maxCpu: 64 + maxMemory: 27913 + networks: + - id: 3 + type: simpleBridge + bridge: br0 + primary: true + default: true + created: '2024-03-29T20:10:22+00:00' + updated: '2024-04-09T11:32:14+00:00' + storage: [] + created: '2024-03-29T20:10:22+00:00' + updated: '2024-04-16T10:44:21+00:00' + - id: 4 + commissioned: 3 + ip: 192.168.4.11 + ipAlt: null + hostname: null + port: 8892 + sshPort: 22 + name: BHV 8 + maintenance: false + enabled: true + nfType: 4 + group: + id: 1 + name: Default + description: Default hypervisor group + default: true + enabled: true + distributionType: 5 + created: '2024-03-12T22:21:32+00:00' + updated: '2024-04-12T20:56:04+00:00' + encryptedToken: >- + eyJpdiI6ImpjS3JqNWhlOHl6Rm5RUk80WkRVQkE9PSIsInZhbHVlIjoiUXNSVEhXMWhJM2hrZ2gza3hBVGtQUjJMenp4bThNL2d0ajYyUGNTQ2s2TjMxZEZ3ZVJVYzQxbUhjSXZMd2greWY5MEl3ajFLK3RrL1BpSzdZbCtzRkpObisvNktxanpZZzBORFpHRUdnN2t3cmN2YXJBbnhQbWRkQ0FUVkozQTVwanlYZHJxVlF2Y3VBOWVBb21FdytUT3Z0cUdrMTU0V3YxM0w1VUh2NER4VkpESngvK0kwNmp6eHFVSDloSWxEd2t4aHV4UnM2c1kyRFRjTDJ5TXJ0dFZPRjZNL3YxKzFpODlzRHFtak5PRm1pVDFBdHJwNGhqNEZiV05Fd1c2OWlkeWxuTWdUT1Z1STE4MjFIMnBoaDA1WWhmSitFMFdnZGdqZ0lZSlBzODd5Y3hDVzNCYWFwSHlHV1hDU0lXZDJKS3RsYWN4VU9EUmJqeTM3Vy9RSXFKTGxkRnZlMnhjWm5mekRrd0VZVjEzeVFhMDFGazJKbXNOVTdCUzdTcW9PbzFsdkEzZE9nK0k4dW5ndXB6OTJDbUFWZk5hU1JveExMSGFzMnNsQzFUSzRDSHVaQkkvR2JseFFwT1BDTWcvZjlqaU5IdDJPMnRTRjJrVmxuYjJzeDRBT2NTNjl5V2hVc3c3UC9ucG11UFozZjErOFovcXVZQlJHSkt0RWJrcVFvY2NyMDdDZ0JFOE5SVklJa0loSVIvSStwVWh6b0twV1NLT0tOaXF3N1EvQkJUTmhYRit3eFR2VGxMc3NMSDVSUWtyMlZCck43ZkFYVXMrVEpoVXhHSVJlN2pMOENnL1R4SzVIeHkrWkVWKzlhRmN5RWdmK1IzaGpJZ0VtbUMvTzJvRnI2TW93RmpyNXlITXB3TGpteGZ6NmZpUEh4NlNVMytsd0hsS0kxRDA9IiwibWFjIjoiNWFkMTQ4YmQ1MGJjMTE3N2QzN2JhOGJkOTM1NzBlNjIzYjFkM2UyMjkxYjI3OWJmMTkxMmVmYjcxNzEwZTNkNCIsInRhZyI6IiJ9 + maxServers: 0 + maxCpu: 16 + maxMemory: 27913 + networks: + - id: 4 + type: macVTap + bridge: eth0 + primary: true + default: true + created: '2024-03-29T20:10:42+00:00' + updated: '2024-03-29T20:10:42+00:00' + storage: [] + created: '2024-03-29T20:10:42+00:00' + updated: '2024-04-16T10:44:21+00:00' + - id: 6 + commissioned: 3 + ip: 192.168.4.2 + ipAlt: 123.123.123.121 + hostname: null + port: 8892 + sshPort: 22 + name: BHV 1 + maintenance: false + enabled: true + nfType: 4 + group: + id: 2 + name: Test + description: null + default: false + enabled: true + distributionType: 13 + created: '2024-10-08T13:23:28+00:00' + updated: '2024-10-08T13:23:42+00:00' + encryptedToken: >- + eyJpdiI6ImQ0M1hybkc5bDJaQ2IrakJFUGZ0MEE9PSIsInZhbHVlIjoiNW1ETjRDeGMxZDlDOWx3M1ZmRkFBZG84dGRwbEZVRVlwc0JJT202UXhHekJ0cS9wNWpIUjJiUzJhaG0ySCs4aHpLakF2RnNJTjVPZ09hN0ttcEg4bkJrdjRqd2pxNHlPcTBaNnBDZ2VwTllUNzNnRTdBUlVHM245VDVhWkxhYkZ5MnRmQXZJMjZkQWxkV3BmbVdZNWt0clR2UlNGTEZES0kzaksyd2xmZFJITlRRMU0yUkp2WkNRU0lYUlRkWWF3NWxxY1cyUFo1WFByczZIak4wMnl4VmdSbUs0b0RjVWNacmZmcDM1VTgyWlo1OGYydnBXVGNOdHZIRXA3YnNIZFIzTXJiUmorcVExc216R3VyeTQ0U2JTeDZZbzlBcHN2MFNyczlNZGZPM1M5K2FuUVQrNVc1UHVuTEZUSC8yMi9FWHVOYUphblNPVnZsQ2RhVGdrSE5zczlyTEF6QTM5cTBrci8yRy9DM204NWJxOUZBZjdhTmRFZnc1UWcwVUM1L3dveGpvb2tleEd2eVF0amlSN2VRYzdlS3kzQUtMRVk2WkFma25aVjN5OWRURDZFN3JnWU5UVDRkTjRWc0Rib1JIdXAwSlZQTVViWHJTNlIrbFBvb1M0MHVOOEU4VGlBdGZ4dVI0V1BwS3dnempYSC94bmRXdHdET2FEUGZHVXptNzFQQjM3UnRwbWtSQW1wY2xscGxTTmRvZzJpQ0pEWXdoYWRGTk03aytWbGJyVUh3ZGliVWN1NGVEM1lRNjFVV3oxYkNOL0NJUFUycVlQQ212S0NsYitIK2p5QkxwdUk2bk4vL0l6QktraCtqR2lnYkYzNU1IazBPVG42RGlCVkVBV2JKcGI4My9lNDl4N29vQUNJempjaGtlQU1tVWlHZDBDUFkwVU1lTmM9IiwibWFjIjoiMTk1MWEwNGQyYzdiOGM5ZTQ4NDBjZWI2N2JjYTJjYmFhNThhOGUyMDE1MTdiZDdmN2E2YjE4MjZmZjk1OTcxYiIsInRhZyI6IiJ9 + maxServers: 0 + maxCpu: 128 + maxMemory: 29419 + networks: + - id: 6 + type: simpleBridge + bridge: br0 + primary: true + default: true + created: '2024-03-30T09:53:38+00:00' + updated: '2025-01-15T13:31:56+00:00' + - id: 17 + type: lvBridgeOVS + bridge: bhv1 + primary: false + default: false + created: '2024-05-17T11:25:57+00:00' + updated: '2024-05-17T11:25:57+00:00' + storage: [] + created: '2024-03-30T09:53:38+00:00' + updated: '2024-12-06T21:25:54+00:00' + first_page_url: >- + https://192.168.3.11/api/v1/compute/hypervisors?results=5&page=1 + from: 1 + last_page: 3 + last_page_url: >- + https://192.168.3.11/api/v1/compute/hypervisors?results=5&page=3 + links: + - url: null + label: '« Previous' + active: false + - url: >- + https://192.168.3.11/api/v1/compute/hypervisors?results=5&page=1 + label: '1' + active: true + - url: >- + https://192.168.3.11/api/v1/compute/hypervisors?results=5&page=2 + label: '2' + active: false + - url: >- + https://192.168.3.11/api/v1/compute/hypervisors?results=5&page=3 + label: '3' + active: false + - url: >- + https://192.168.3.11/api/v1/compute/hypervisors?results=5&page=2 + label: Next » + active: false + next_page_url: >- + https://192.168.3.11/api/v1/compute/hypervisors?results=5&page=2 + path: https://192.168.3.11/api/v1/compute/hypervisors + per_page: 5 + prev_page_url: null + to: 5 + total: 14 + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /compute/hypervisors/{hypervisorId}: + get: + summary: Retrive a Hypervisor + deprecated: false + description: '' + tags: + - Hypervisors + parameters: + - name: hypervisorId + in: path + description: A valid hypervisor ID as shown in VirtFusion. + required: true + example: 1 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 1 + commissioned: 3 + ip: 192.168.4.10 + ipAlt: null + hostname: null + port: 8892 + sshPort: 22 + name: PHV 1 (RED) + maintenance: false + enabled: true + nfType: 4 + group: + id: 1 + name: Default + description: Default hypervisor group + default: true + enabled: true + distributionType: 5 + created: '2024-03-12T22:21:32+00:00' + updated: '2024-04-12T20:56:04+00:00' + encryptedToken: >- + eyJpdiI6Ik1Ua29ZSGp0QThxWVZhellzL2VTU3c9PSIsInZhbHVlIjoiNzc1eGdMMzFPUFpFZVpIbytzMDc1NzRsUHRJVnFTWFpKWS9WamJIaVJVMVZkSFZjZVM1YVB3bnlQeGt4eEhVamhrWGF4SnNqQVFES010Y3owUmJneTR4a05oRkp1R08xVXI1eHcvQ3NsbW5qU0dpUWhZbnFUMWYrTHM5L2NoZmhUQm9nRnV4b2Y0dENGLy9vanVDMnkwTG1mNXBYM1JVcE5TNWRCSGkvZS9qVEFsSWx5WXdXOU1wajIwam1DV1d4aUNXMUNGMThFNXI5THM4VWFmYnRFNkx3VHFaV3o3M0VVaEZXSHo0TVdKc0xSemJYVExUWEVlZHM0ZVNoUkk0ZEI2QnAySlVESVU2R0JDcWJMeG9YRUhIM0Vad2w2VHNGcFQ3R1BkbU1TbzU3V2JzbEJFNlUvSW90eGxNZkdqRjVmMGx6TTRIWEttYVA0Ti9JQkEwQURrWTRPL2k4VFJsNjhFTHh3UW1wSGMzUkxibEtDeDdlK2tOekQxVkh0bzhsWXY1RkxxaWRkSFBEQlNvM1l2akxqNitickp1TzR0ekhTbmdVSG5VUE5tMGh1WFJuejhscFpSS2dLcE1ZaS9NUlRKdnNUS0wzYWlDYjB1MVJhcmk4OEJoZURNQ3JROE5WcTZTdzV0Si9UeDhwMTFLK3lZV0NDdzB5b2NBZFhsM0hYMDJPMHlXS1g1MmxhNWdrOTRTSDJHbWNvODNuOUswMHJpYTVBL0YwRW9BVndsMllIdW95ZjBhZXdLUTRSR0xBelBVekViTCtKaG8wSGxPR1NOWmNSaXpxQ1hBUVdsdE9HMUhtc2YrRU14WkhOaUVVeWhXRlB2amtRRXkxZjY0cm85ekxVYWE1QU5zdlJDK2N6YmZrNHNOWk4xSTZXbUhxYklLTmgraTZFWHM9IiwibWFjIjoiOTY2ZmJkNzJkNzZmNmZmYTQzM2U4NDQzMDdhYTAzOWZhNTM0M2I1MDQyYWUwYzQ1ZGIyZTRlOGEwM2M0MTRhYiIsInRhZyI6IiJ9 + maxServers: 0 + maxCpu: 4 + maxMemory: 6004 + created: '2024-03-12T22:37:15+00:00' + updated: '2024-05-10T11:27:52+00:00' + networks: + - id: 1 + type: simpleBridge + bridge: br0 + primary: true + default: true + created: '2024-03-12T22:37:15+00:00' + updated: '2024-03-12T22:37:40+00:00' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /connectivity/ipblocks/{blockId}/ipv4: + post: + summary: Add an IPv4 range to an IP block + deprecated: false + description: '' + tags: + - IP Blocks + parameters: + - name: blockId + in: path + description: A valid IPv4 block ID as shown in VirtFusion. + required: true + example: 1 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + type: + type: string + description: Must be set to range. + start: + type: string + description: Start of IPv4 range. + end: + type: string + description: End of IPv4 range. + required: + - type + - start + - end + example: + type: range + start: 192.168.1.2 + end: 192.168.1.10 + responses: + '204': + description: '' + content: + text/css: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /connectivity/ipblocks: + get: + summary: Retrieve IP blocks + deprecated: false + description: '' + tags: + - IP Blocks + parameters: + - name: results + in: query + description: >- + Number of results to return. Range between 1 and 200. Defaults to + 20. + required: false + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + current_page: 1 + data: + - id: 1 + type: 4 + name: 192.168.4.0/23 + ipv4: + gateway: 192.168.4.1 + netmask: 255.255.254.0 + resolvers: + primary: 8.8.8.8 + secondary: 8.8.4.4 + total: 521 + usedTotal: 21 + freeTotal: 500 + ipv6: + gateway: null + resolvers: + primary: null + secondary: null + subnet: null + from: 48 + to: 64 + restricted: [] + total: 0 + generatedTotal: 0 + usedTotal: 0 + freeTotal: 0 + freeGenerated: 0 + blacklistedTotal: 0 + rdnsType: 0 + rdnsZoneId: null + networkProfile: 0 + routeBlock: null + dhcp: 1 + enabled: true + created: '2024-03-12T22:40:23+00:00' + updated: '2024-12-06T21:53:15+00:00' + - id: 2 + type: 6 + name: PDNS TEST + ipv4: + gateway: null + netmask: null + resolvers: + primary: null + secondary: null + total: 0 + usedTotal: 0 + freeTotal: 0 + ipv6: + gateway: 2a03:3a61:a1::1 + resolvers: + primary: 2001:4860:4860::8888 + secondary: 2001:4860:4860::8844 + subnet: '2a03:3a61:a1::' + from: 48 + to: 64 + restricted: [] + total: 65535 + generatedTotal: 300 + usedTotal: 0 + freeTotal: 65535 + freeGenerated: 300 + blacklistedTotal: 0 + rdnsType: 2 + rdnsZoneId: 1 + networkProfile: 0 + routeBlock: null + dhcp: 1 + enabled: true + created: '2024-04-26T11:41:41+00:00' + updated: '2024-12-31T10:23:33+00:00' + - id: 3 + type: 4 + name: 192.168.30.200-240 + ipv4: + gateway: 192.168.30.1 + netmask: 255.255.255.0 + resolvers: + primary: 8.8.8.8 + secondary: 8.8.4.4 + total: 41 + usedTotal: 8 + freeTotal: 33 + ipv6: + gateway: null + resolvers: + primary: null + secondary: null + subnet: null + from: 48 + to: 64 + restricted: [] + total: 0 + generatedTotal: 0 + usedTotal: 0 + freeTotal: 0 + freeGenerated: 0 + blacklistedTotal: 0 + rdnsType: 0 + rdnsZoneId: null + networkProfile: 0 + routeBlock: null + dhcp: 1 + enabled: true + created: '2024-05-14T10:43:52+00:00' + updated: '2024-05-14T10:44:25+00:00' + - id: 4 + type: 4 + name: 10.1.1.0/24 + ipv4: + gateway: null + netmask: 255.255.255.255 + resolvers: + primary: 8.8.8.8 + secondary: 8.8.4.4 + total: 0 + usedTotal: 0 + freeTotal: 0 + ipv6: + gateway: null + resolvers: + primary: null + secondary: null + subnet: null + from: 48 + to: 64 + restricted: [] + total: 0 + generatedTotal: 0 + usedTotal: 0 + freeTotal: 0 + freeGenerated: 0 + blacklistedTotal: 0 + rdnsType: 0 + rdnsZoneId: null + networkProfile: 0 + routeBlock: null + dhcp: 1 + enabled: true + created: '2024-05-16T18:11:03+00:00' + updated: '2024-05-17T13:22:04+00:00' + - id: 5 + type: 6 + name: V6 For BHV 1,3 + ipv4: + gateway: null + netmask: null + resolvers: + primary: null + secondary: null + total: 0 + usedTotal: 0 + freeTotal: 0 + ipv6: + gateway: 2001:db8:abcd:12::1 + resolvers: + primary: 2001:4860:4860::8888 + secondary: 2001:4860:4860::8844 + subnet: '2001:db8:abcd:12::' + from: 64 + to: 80 + restricted: [] + total: 65535 + generatedTotal: 1100 + usedTotal: 9 + freeTotal: 65526 + freeGenerated: 1091 + blacklistedTotal: 0 + rdnsType: 0 + rdnsZoneId: null + networkProfile: 0 + routeBlock: null + dhcp: 1 + enabled: true + created: '2024-09-19T17:23:05+00:00' + updated: '2024-12-06T21:23:55+00:00' + first_page_url: https://192.168.3.11/api/v1/connectivity/ipblocks?page=1 + from: 1 + last_page: 1 + last_page_url: https://192.168.3.11/api/v1/connectivity/ipblocks?page=1 + links: + - url: null + label: '« Previous' + active: false + - url: https://192.168.3.11/api/v1/connectivity/ipblocks?page=1 + label: '1' + active: true + - url: null + label: Next » + active: false + next_page_url: null + path: https://192.168.3.11/api/v1/connectivity/ipblocks + per_page: 20 + prev_page_url: null + to: 5 + total: 5 + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /connectivity/ipblocks/{blockId}: + get: + summary: Retrieve an IP block + deprecated: false + description: '' + tags: + - IP Blocks + parameters: + - name: blockId + in: path + description: A valid IP block ID as shown in VirtFusion. + required: true + example: 1 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 1 + type: 4 + name: 192.168.4.0/23 + ipv4: + gateway: 192.168.4.1 + netmask: 255.255.254.0 + resolvers: + primary: 8.8.8.8 + secondary: 8.8.4.4 + total: 521 + usedTotal: 21 + freeTotal: 500 + ipv6: + gateway: null + resolvers: + primary: null + secondary: null + subnet: null + from: 48 + to: 64 + restricted: [] + total: 0 + generatedTotal: 0 + usedTotal: 0 + freeTotal: 0 + freeGenerated: 0 + blacklistedTotal: 0 + rdnsType: 0 + rdnsZoneId: null + networkProfile: 0 + routeBlock: null + dhcp: 1 + enabled: true + created: '2024-03-12T22:40:23+00:00' + updated: '2024-12-06T21:53:15+00:00' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /backups/server/{serverId}: + get: + summary: Retrieve a server backups + deprecated: false + description: '' + tags: + - Backups + parameters: + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 1 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + - id: 42 + serverId: 202 + storage: + id: 5 + name: Backup Server 1 + enabled: true + deleting: false + restoring: false + progress: false + complete: true + deleteAfter: null + created: '2022-03-03T20:25:01+00:00' + updated: '2022-03-03T20:26:01+00:00' + - id: 49 + serverId: 202 + storage: + id: 5 + name: Backup Server 1 + enabled: true + deleting: false + restoring: false + progress: false + complete: true + deleteAfter: null + created: '2022-03-04T20:25:01+00:00' + updated: '2022-03-04T20:26:01+00:00' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /dns/services/{serviceId}: + get: + summary: Retrieve a DNS service + deprecated: false + description: '' + tags: + - DNS + parameters: + - name: serviceId + in: path + description: A valid DNS service ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 4 + type: 1 + name: ClouDNS + username: '456754' + url: https://api.cloudns.net + ip: null + port: 443 + password: >- + eyJpdiI6IjVUOU11S09KNmFtNnlqLzRzR0FYd1E9PSIsInZhbHVlIjoiS01SNjdhbEt1TzFVMHM0Nk1lY2Z0bnl5cUJJUDlxeUF0VXdtTTUwWW41QT0iLCJtYWMiOiI4NTBlNzFhNzJmNTkwMTA1ODQ0MjU4OTUzNjM0MzAxN2QwYzY5OTdiMTgzNDg3ZGFjMmU5NjE0Y2E3YTE1NWVjIiwidGFnIjoiIn0= + config: {} + subAccount: false + capabilities: 1 + enabled: true + created: '2022-02-11T11:55:49+00:00' + updated: '2022-02-14T22:45:43+00:00' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /media/iso/{isoId}: + get: + summary: Retrieve an ISO + deprecated: false + description: '' + tags: + - Media + parameters: + - name: isoId + in: path + description: A valid ISO ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 1 + name: Deb Arch + description: null + arch: 2 + url: >- + https://cdimage.debian.org/debian-cd/current/arm64/iso-cd/debian-12.5.0-arm64-netinst.iso + filename: deb-arc + enabled: true + config: '[]' + global: true + download: true + users: [] + created: '2024-03-13T09:34:54+00:00' + updated: '2024-04-01T20:34:05+00:00' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /media/templates/fromServerPackageSpec/{serverPackageId}: + get: + summary: Retrieve operating system templates that are available for a package + deprecated: false + description: '' + tags: + - Media + parameters: + - name: serverPackageId + in: path + description: A valid server package ID as shown in VirtFusion. + required: true + example: 1 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + - name: Debian + description: >- + Debian GNU/Linux, is a Linux distribution composed of free + and open-source software, developed by the + community-supported Debian Project. + icon: debian_logo.png + templates: + - id: 8 + name: Debian + version: 11 (Bullseye) + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Debian. + icon: debian_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 44 + name: Debian + version: 12 (Bookworm) + variant: null + arch: 2 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Debian. + icon: debian_logo.png + eol: false + eol_date: '2024-04-02 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 46 + name: Debian + version: 12 (Bookworm) + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Debian. + icon: debian_logo.png + eol: false + eol_date: '2024-04-23 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 56 + name: Debian + version: 12 (Bookworm) + variant: Test + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Debian. + icon: debian_logo.png + eol: false + eol_date: '2024-04-23 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: true + type: linux + id: 1 + - name: CentOS + description: >- + The CentOS Linux distribution is a stable, predictable, + manageable and reproducible platform derived from the + sources of Red Hat Enterprise Linux (RHEL). + icon: centos_logo.png + templates: + - id: 1 + name: CentOS + version: '7' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Yum, the main + command-line package manager for CentOS. + icon: centos_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 2 + name: CentOS Stream + version: '9' + variant: Minimal + arch: 1 + description: >- + Base installation with limited packages. New packages + are easily installed using DNF (yum), the main + command-line package manager for CentOS. + icon: centos_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + id: 2 + - name: Rocky Linux + description: >- + Rocky Linux is a community enterprise operating system + designed to be 100% bug-for-bug compatible with America's + top enterprise Linux distribution now that its downstream + partner has shifted direction. It is under intensive + development by the community. Rocky Linux is led by + Gregory Kurtzer, founder of the CentOS project. + icon: rocky_linux_logo.png + templates: + - id: 7 + name: Rocky Linux + version: '8' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for Rocky Linux. + icon: rocky_linux_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 13 + name: Rocky Linux + version: '9' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for Rocky Linux. + icon: rocky_linux_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 40 + name: Rocky Linux + version: '9' + variant: '' + arch: 2 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for Rocky Linux. + icon: rocky_linux_logo.png + eol: false + eol_date: '2024-03-28 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + id: 3 + - name: AlmaLinux + description: >- + AlmaLinux OS is an open-source, community-driven project + that intends provide and alternative to the CentOS Stable + release. AlmaLinux is an OS that is 1:1 binary compatible + with RHEL® 8 and a global collaborative of the developer + community, industry, academia and research which build + upon this technology to empower humanity. + icon: almalinux_logo.png + templates: + - id: 6 + name: AlmaLinux + version: '8' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for AlmaLinux. + icon: almalinux_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 12 + name: ARM -> AlmaLinux + version: '9' + variant: Latest + arch: 1 + description: >- + Latest version with base packages. New packages are + easily installed using DNF (yum), the main + command-line package manager for AlmaLinux. + icon: almalinux_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 39 + name: AlmaLinux + version: '9' + variant: null + arch: 2 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for AlmaLinux. + icon: almalinux_logo.png + eol: false + eol_date: '2024-03-28 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + id: 4 + - name: Ubuntu + description: >- + The most popular server Linux in the cloud and data + centre, you can rely on Ubuntu Server and its five years + of guaranteed free upgrades. + icon: ubuntu_logo.png + templates: + - id: 3 + name: Ubuntu Server + version: 20.04 LTS (Focal Fossa) + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Ubuntu. + icon: ubuntu_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 4 + name: Ubuntu Server + version: 18.04 LTS (Bionic Beaver) + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Ubuntu. + icon: ubuntu_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 9 + name: Ubuntu Server + version: 22.04 LTS (Jammy Jellyfish) + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Ubuntu. + icon: ubuntu_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 41 + name: Ubuntu + version: 22.04 LTS (Jammy Jellyfish) + variant: '' + arch: 2 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Ubuntu. + icon: ubuntu_logo.png + eol: false + eol_date: '2024-03-28 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 49 + name: Ubuntu Server + version: 24.04 LTS (Noble Numbat) + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Ubuntu. + icon: ubuntu_logo.png + eol: false + eol_date: '2024-04-25 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 51 + name: Ubuntu + version: 24.04 LTS (Noble Numbat) + variant: null + arch: 2 + description: >- + Minimal installation with limited packages. New + packages are easily installed using Advanced Package + Tool (APT), the main command-line package manager for + Ubuntu. + icon: ubuntu_logo.png + eol: false + eol_date: '2024-05-02 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + id: 5 + - name: Fedora + description: >- + Fedora Server is a powerful, flexible operating system + that includes the best and latest datacenter technologies. + It puts you in control of all your infrastructure and + services. + icon: fedora_logo.png + templates: + - id: 11 + name: Fedora + version: '37' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for Fedora. + icon: fedora_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 14 + name: Fedora + version: '38' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for Fedora. + icon: fedora_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 15 + name: Fedora + version: '39' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for Fedora. + icon: fedora_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 59 + name: Fedora + version: '41' + variant: Minimal + arch: 1 + description: >- + Minimal installation with limited packages. New + packages are easily installed using DNF (yum), the + main command-line package manager for Fedora. + icon: fedora_logo.png + eol: false + eol_date: '2024-12-18 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + id: 6 + - name: FreeBSD + description: >- + FreeBSD is an operating system used to power modern + servers, desktops, and embedded platforms. A large + community has continually developed it for more than + thirty years. Its advanced networking, security, and + storage features have made FreeBSD the platform of choice + for many of the busiest web sites and most pervasive + embedded networking and storage devices. + icon: freebsd_logo.png + templates: + - id: 52 + name: FreeBSD + version: '13.3' + variant: Minimal + arch: 1 + description: Minimal installation with limited packages. + icon: freebsd_logo.png + eol: false + eol_date: '2024-05-15 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: unix + - id: 53 + name: FreeBSD + version: '14.0' + variant: Minimal + arch: 1 + description: Minimal installation with limited packages. + icon: freebsd_logo.png + eol: false + eol_date: '2024-05-15 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: unix + - id: 55 + name: FreeBSD + version: '14.2' + variant: Minimal + arch: 1 + description: Minimal installation with limited packages. + icon: freebsd_logo.png + eol: false + eol_date: '2024-10-20 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: true + type: unix + - id: 58 + name: FreeBSD + version: '13.2' + variant: Minimal + arch: 1 + description: Minimal installation with limited packages. + icon: freebsd_logo.png + eol: false + eol_date: '2024-12-10 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: unix + id: 7 + - name: Other + description: '' + icon: linux_logo.png + templates: + - id: 5 + name: openSUSE + version: Leap 15 + variant: Minimal + arch: 1 + description: >- + openSUSE is a project that serves to promote the use + of free and open-source software.

Minimal + installation with limited packages. New packages are + easily installed using Zypper, the main command-line + package manager for openSUSE. + icon: opensuse_logo.png + eol: false + eol_date: '2024-03-12 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + - id: 36 + name: openSUSE + version: Leap 15.6 + variant: '' + arch: 2 + description: >- + openSUSE is a project that serves to promote the use + of free and open-source software.

Minimal + installation with limited packages. New packages are + easily installed using Zypper, the main command-line + package manager for openSUSE. + icon: opensuse_logo.png + eol: false + eol_date: '2024-03-14 00:00:00' + eol_warning: false + deploy_type: 1 + vnc: false + type: linux + id: 0 + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /packages: + get: + summary: Retrieve packages + deprecated: false + description: '' + tags: + - Packages + parameters: [] + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + - id: 1 + name: Test + description: null + enabled: true + memory: 1024 + primaryStorage: 10 + traffic: 200 + cpuCores: 1 + primaryNetworkSpeedIn: 0 + primaryNetworkSpeedOut: 0 + primaryDiskType: inherit + backupPlanId: 0 + primaryStorageReadBytesSec: null + primaryStorageWriteBytesSec: null + primaryStorageReadIopsSec: null + primaryStorageWriteIopsSec: null + primaryStorageProfile: 1 + primaryNetworkProfile: 0 + created: '2024-03-12T22:41:31.000000Z' + - id: 2 + name: Test Only + description: null + enabled: true + memory: 1024 + primaryStorage: 10 + traffic: 200 + cpuCores: 1 + primaryNetworkSpeedIn: 0 + primaryNetworkSpeedOut: 0 + primaryDiskType: inherit + backupPlanId: 0 + primaryStorageReadBytesSec: null + primaryStorageWriteBytesSec: null + primaryStorageReadIopsSec: null + primaryStorageWriteIopsSec: null + primaryStorageProfile: 0 + primaryNetworkProfile: 0 + created: '2024-06-28T12:36:16.000000Z' + - id: 3 + name: BASIC + description: null + enabled: true + memory: 1024 + primaryStorage: 10 + traffic: 20000 + cpuCores: 2 + primaryNetworkSpeedIn: 0 + primaryNetworkSpeedOut: 0 + primaryDiskType: inherit + backupPlanId: 0 + primaryStorageReadBytesSec: null + primaryStorageWriteBytesSec: null + primaryStorageReadIopsSec: null + primaryStorageWriteIopsSec: null + primaryStorageProfile: 1 + primaryNetworkProfile: 0 + created: '2024-10-12T15:54:54.000000Z' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /packages/{packageId}: + get: + summary: Retrieve a packge + deprecated: false + description: '' + tags: + - Packages + parameters: + - name: packageId + in: path + description: A valid package ID as shown in VirtFusion. + required: true + example: 1 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 1 + name: Test + description: null + enabled: true + memory: 1024 + primaryStorage: 10 + traffic: 200 + cpuCores: 1 + primaryNetworkSpeedIn: 0 + primaryNetworkSpeedOut: 0 + primaryDiskType: inherit + backupPlanId: 0 + primaryStorageReadBytesSec: null + primaryStorageWriteBytesSec: null + primaryStorageReadIopsSec: null + primaryStorageWriteIopsSec: null + primaryStorageProfile: 1 + primaryNetworkProfile: 0 + created: '2024-03-12T22:41:31.000000Z' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /queue/{queueId}: + get: + summary: Retrieve a queue item + deprecated: false + description: '' + tags: + - Queue & Tasks + parameters: + - name: queueId + in: path + description: A valid queue ID as shown in VirtFusion. + required: true + example: 158 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 158 + jobId: '852' + job: App\Jobs\Server\KVM\Build + hypervisorId: 6 + serverId: 69 + action: build_server + queue: default + started: '2025-01-15T15:00:26+00:00' + updated: '2025-01-15T15:00:49+00:00' + finished: '2025-01-15T15:00:49+00:00' + failed: false + progress: 100 + errors: + exception: + stringable: false + errors: [] + type: null + trace: null + message: null + primaryActions: + - type: server.get.status + dataType: object + data: + success: true + version: '{{VERSION}}' + setOpts: + failOnVersionCheck: true + failOnDisasterRecovery: true + createDirStructure: true + writeXMLConfiguration: false + failOnCustomXML: false + failOnPriorityXML: false + failOnElevateXML: true + actions: + createDirStructure: + requested: true + output: server directory structure set. No action required + msg: null + success: true + statusTree: + disasterRecoveryActive: false + customXML: false + priorityXML: false + elevateXML: false + created: '2025-01-15T15:00:26+00:00' + updated: '2025-01-15T15:00:26+00:00' + - type: server.config.dhcp + dataType: object + data: + system: + success: true + commandline: [] + data: [] + created: '2025-01-15T15:00:26+00:00' + updated: '2025-01-15T15:00:26+00:00' + - type: server.os.template.exists + dataType: object + data: + success: false + remote: + info: + url: >- + https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img + content_type: application/octet-stream + http_code: 200 + header_size: 255 + request_size: 176 + filetime: -1 + ssl_verify_result: 20 + redirect_count: 0 + total_time: 0.070349 + namelookup_time: 0.016706 + connect_time: 0.03088 + pretransfer_time: 0.054076 + size_upload: 0 + size_download: 0 + speed_download: 0 + speed_upload: 0 + download_content_length: 609856512 + upload_content_length: 0 + starttransfer_time: 0.070305 + redirect_time: 0 + redirect_url: '' + primary_ip: 185.125.190.37 + certinfo: [] + primary_port: 443 + local_ip: 192.168.4.2 + local_port: 34728 + http_version: 2 + protocol: 2 + ssl_verifyresult: 0 + scheme: HTTPS + appconnect_time_us: 54015 + connect_time_us: 30880 + namelookup_time_us: 16706 + pretransfer_time_us: 54076 + redirect_time_us: 0 + starttransfer_time_us: 70305 + total_time_us: 70349 + effective_method: HEAD + exitCode: 0 + error: null + completed: true + created: '2025-01-15T15:00:27+00:00' + updated: '2025-01-15T15:00:27+00:00' + - type: server.os.template.download + dataType: object + data: + system: + success: true + errors: [] + commandline: [] + data: + - sourceUrl: >- + https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img + sourceDecompress: '' + destinationPath: >- + /home/vf-data/os/template/ubuntu-noble-server-cloudimg-amd64-2024-04-25.qcow2 + exitCode: null + pid: null + finished: false + error: null + errorOutput: null + success: 0 + updated: 1736953227 + created: '2025-01-15T15:00:27+00:00' + updated: '2025-01-15T15:00:27+00:00' + - type: server.os.template.download.check + dataType: object + data: + success: true + filesize: 609856512 + remote: |- + { + "sourceUrl": "https:\/\/cloud-images.ubuntu.com\/noble\/current\/noble-server-cloudimg-amd64.img", + "sourceDecompress": "", + "destinationPath": "\/home\/vf-data\/os\/template\/ubuntu-noble-server-cloudimg-amd64-2024-04-25.qcow2", + "exitCode": 0, + "pid": 387093, + "finished": true, + "error": null, + "errorOutput": null, + "success": true, + "updated": 1736953235, + "decompressOutput": null + } + created: '2025-01-15T15:00:35+00:00' + updated: '2025-01-15T15:00:35+00:00' + - type: server.create.ci + dataType: object + data: + network: + version: 2 + ethernets: + ens3: + match: + macaddress: 00:e7:fb:01:87:14 + addresses: + - 192.168.4.32/23 + - 192.168.4.35/23 + gateway4: 192.168.4.1 + nameservers: + addresses: + - 8.8.8.8 + - 8.8.4.4 + routes: + - to: 192.168.4.1 + via: 0.0.0.0 + scope: link + ens4: + match: + macaddress: 00:f0:4a:c6:3f:08 + addresses: + - 192.168.4.33/23 + - 192.168.4.34/23 + gateway4: 192.168.4.1 + nameservers: + addresses: + - 8.8.8.8 + - 8.8.4.4 + user: + timezone: Europe/London + ssh_pwauth: false + users: + - name: root + ssh-authorized-keys: + - >- + ssh-rsa + AAAAB3NzaC1yc2EAAAADAQABAAACAQC+JdL4fWELBWGAknSu0PwVpDDOlORxy9z7eVnZphZXBzYLMnux+ZogVLns6+O6NDE8JmWvP9RIg3SIga7RDOkW9UCdLzRu0jF2ALL7CK1huo1Ih0PDM9ZbFDy2Fd7a4DTvUX6923fQyW0PWRtyL11R4c9NUqzejKp5kW8vHfPQjzwb1hGIKvkSYkI0Auq4JJhlvjjnoK7Z8t5mpDrVfNTrVqevPgsW5Xwnq8R+02XywrY+Q/wnpxDs3Wjb2aA61A0x5J0xcZQpTQHoJNj77J3VmPI7Ry7Q8hPbTSLGZbN+gODr0lOaL5TdbvM3bnus5JvoqgRoszzPcTiNMZAe3v9UM8hiXise54b8rsc2M9MQ4olPu7TrROZbcw+9q4m6cV+dfVU/NRFkf27YRa4oZNKehHsMiupDyoISgSl4qSB8YXAWsX03oC/gzpB2YJIqEL1Y/SmKYEhgr0cplkvGZy6C/Q9cJHyHlMPtEBPexgcjXC9QrVK4n2cmde3TuSRMctawcat7Nuq08C8fGHaGHr8iAeage3o/ODVOt0rhBu69PknzQeVBdlwK3+p1dH6PnMzNNBhWyNZT/NqB2eS6K8lYpOQ47byXPwYsRLvStUjpZRdikOT7D31T5g8FwOThQ+6WX+xfMD7CSLsSKCn/FhlinbVbG2IhCLH3B30Akw5bUw== + hashed_passwd: '' + lock_passwd: true + runcmd: + - >- + DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get + --option=Dpkg::Options::=--force-confold + --option=Dpkg::options::=--force-unsafe-io + --assume-yes --quiet update + - >- + DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get + --option=Dpkg::Options::=--force-confold + --option=Dpkg::options::=--force-unsafe-io + --assume-yes --quiet install qemu-guest-agent + - /usr/bin/systemctl enable qemu-guest-agent + - /usr/bin/systemctl start qemu-guest-agent + - >- + DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get + --option=Dpkg::Options::=--force-confold + --option=Dpkg::options::=--force-unsafe-io + --assume-yes --quiet dist-upgrade + meta: + instance-id: b9fd9092-7200-4a24-96d4-76aedd664274 + local-hostname: elliptical-way + created: '2025-01-15T15:00:35+00:00' + updated: '2025-01-15T15:00:35+00:00' + - type: server.disk.create.os + dataType: object + data: + system: + success: true + commandline: + - result: + success: true + exitOnZero: false + command: >- + 'virsh' 'destroy' + 'b9fd9092-7200-4a24-96d4-76aedd664274' + exit_code: 1 + pid: 387131 + started: 1736953235.403454 + env: + PATH: >- + /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + timeout: 180 + output: '' + error: >- + error: failed to get domain + 'b9fd9092-7200-4a24-96d4-76aedd664274' + - command: >- + qemu-img info + '/home/vf-data/os/template/ubuntu-noble-server-cloudimg-amd64-2024-04-25.qcow2' + | grep -v grep | grep -w "file format:" | awk '{ + print $3 }' + exit_code: 0 + output: qcow2 + error: '' + - command: >- + 'cloud-localds' + '/home/vf-data/server/b9fd9092-7200-4a24-96d4-76aedd664274/cloud-drive.img' + '--network-config=/home/vf-data/server/b9fd9092-7200-4a24-96d4-76aedd664274/network-config-v2.yaml' + '/home/vf-data/server/b9fd9092-7200-4a24-96d4-76aedd664274/user-data.yaml' + '/home/vf-data/server/b9fd9092-7200-4a24-96d4-76aedd664274/meta-data.yaml' + exit_code: 0 + output: '' + error: '' + data: + success: true + forkData: + status: true + errors: [] + commandline: [] + output: + - sourcePath: >- + /home/vf-data/os/template/ubuntu-noble-server-cloudimg-amd64-2024-04-25.qcow2 + destinationPath: >- + /home/vf-data/disk/b9fd9092-7200-4a24-96d4-76aedd664274_1.img + convertProcess: + - qemu-img + - convert + - '-f' + - qcow2 + - '-O' + - qcow2 + - >- + /home/vf-data/os/template/ubuntu-noble-server-cloudimg-amd64-2024-04-25.qcow2 + - >- + /home/vf-data/disk/b9fd9092-7200-4a24-96d4-76aedd664274_1.img + resizeProcess: + - qemu-img + - resize + - '-f' + - qcow2 + - >- + /home/vf-data/disk/b9fd9092-7200-4a24-96d4-76aedd664274_1.img + - 11G + resizeProcessPid: null + convertProcessPid: null + finished: false + convertProcessOutput: null + resizeProcessOutput: null + convertProcessExitCode: null + resizeProcessExitCode: null + convertProcessError: null + resizeProcessError: null + error: null + success: false + updated: 1736953238 + abort: false + error: null + errorException: null + created: '2025-01-15T15:00:38+00:00' + updated: '2025-01-15T15:00:38+00:00' + - type: server.os.install.check + dataType: object + data: + success: true + sourceFilesize: 609856512 + destinationFilesize: 1832517808 + remote: + sourcePath: >- + /home/vf-data/os/template/ubuntu-noble-server-cloudimg-amd64-2024-04-25.qcow2 + destinationPath: >- + /home/vf-data/disk/b9fd9092-7200-4a24-96d4-76aedd664274_1.img + convertProcess: + - qemu-img + - convert + - '-f' + - qcow2 + - '-O' + - qcow2 + - >- + /home/vf-data/os/template/ubuntu-noble-server-cloudimg-amd64-2024-04-25.qcow2 + - >- + /home/vf-data/disk/b9fd9092-7200-4a24-96d4-76aedd664274_1.img + resizeProcess: + - qemu-img + - resize + - '-f' + - qcow2 + - >- + /home/vf-data/disk/b9fd9092-7200-4a24-96d4-76aedd664274_1.img + - 11G + resizeProcessPid: 387270 + convertProcessPid: 387168 + finished: true + convertProcessOutput: '' + resizeProcessOutput: | + Image resized. + convertProcessExitCode: 0 + resizeProcessExitCode: 0 + convertProcessError: null + resizeProcessError: null + error: null + success: true + updated: 1736953244 + created: '2025-01-15T15:00:44+00:00' + updated: '2025-01-15T15:00:44+00:00' + - type: server.vnc.disable + dataType: object + data: + system: + success: true + commandline: + - command: '''/usr/sbin/ufw'' ''deny'' ''5903''' + exit_code: 0 + output: |- + Skipping adding existing rule + Skipping adding existing rule (v6) + error: '' + data: [] + created: '2025-01-15T15:00:45+00:00' + updated: '2025-01-15T15:00:45+00:00' + - type: server.boot + dataType: object + data: + system: + success: true + errors: [] + commandline: + - success: true + exitOnZero: false + command: >- + 'virsh' '-q' 'domstate' + 'b9fd9092-7200-4a24-96d4-76aedd664274' + exit_code: 1 + pid: 387301 + started: 1736953245.165795 + env: + PATH: >- + /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + timeout: 60 + output: '' + error: >- + error: failed to get domain + 'b9fd9092-7200-4a24-96d4-76aedd664274' + - success: true + exitOnZero: true + command: >- + 'virsh' 'create' + '/home/vf-data/server/b9fd9092-7200-4a24-96d4-76aedd664274/server.xml' + exit_code: 0 + pid: 387303 + started: 1736953245.190122 + env: + PATH: >- + /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + timeout: 360 + output: >- + Domain 'b9fd9092-7200-4a24-96d4-76aedd664274' + created from + /home/vf-data/server/b9fd9092-7200-4a24-96d4-76aedd664274/server.xml + error: '' + - result: + success: true + exitOnZero: true + command: >- + 'virsh' 'attach-disk' + 'b9fd9092-7200-4a24-96d4-76aedd664274' + '/home/vf-data/server/b9fd9092-7200-4a24-96d4-76aedd664274/cloud-drive.img' + 'sdx' '--mode' 'readonly' + exit_code: 0 + pid: 387457 + started: 1736953246.67152 + env: + PATH: >- + /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + timeout: 60 + output: Disk attached successfully + error: '' + data: + - - filter_list: null + filter_apply: null + filter_apply_success: true + filter_apply_error: false + filter_apply_error_trace: false + filter_apply_cli: null + filter_apply_code: null + tmp_filter_1: >- + /home/vf-data/server/b9fd9092-7200-4a24-96d4-76aedd664274/networkFilter-3933491695.xml + tmp_filter_2: >- + /home/vf-data/server/b9fd9092-7200-4a24-96d4-76aedd664274/networkFilter-3933491695.xml-tmp + sha1: bd9ce80d8372e025e5de8757ec63c042986a48fa + sha1_last: bd9ce80d8372e025e5de8757ec63c042986a48fa + native: + primary: [] + secondary: + sha1: 801b6632cbb50f2c8c6dd15037ba9c9d4e03cf50 + sha1_last: 801b6632cbb50f2c8c6dd15037ba9c9d4e03cf50 + created: '2025-01-15T15:00:46+00:00' + updated: '2025-01-15T15:00:46+00:00' + - type: server.config.statistics + dataType: object + data: [] + created: '2025-01-15T15:00:48+00:00' + updated: '2025-01-15T15:00:48+00:00' + subActions: [] + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /ssh_keys: + post: + summary: Add an SSH key to a user account + deprecated: false + description: '' + tags: + - SSH Keys + parameters: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + userId: + type: integer + name: + type: string + publicKey: + type: string + required: + - userId + - name + - publicKey + example: + userId: 1 + name: Key 1 + publicKey: >- + ssh-rsa + AAAAB3NzaC1yc2EAAAADAQABAAABAQDF6O4Evybdywpi6PImTE5aJ75+5OpJKyd2QR2LSl0bVxhZjQOqN/4msCp/UjUpFDSeC1SQXeKQb4o7OZ7bUC8k2JbNxnArsYSGi/XhqczKOX/uYOMA/V8gb1e+uishQSzjYrneC0PufFYwNGStjYf0QXCsgQcYLsHbjV2g9j0FhVYxj5endy7Z1K1RMP7IzF5lh3KgtbqKhdJ8XK1fqXCcPHxEuAzjq7G2W+I9xOs8GqftxYGS4XAiOe7YLKfWM00dUdYMJ81R8lZFj5UzP0MOT9qxPNBNiB0MEQX8hc0+2nQdaQYkg8mbCJQxhT9Cr0rXyYdbaNnYWIJql3SVgigJ + responses: + '201': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 2 + name: Key 1 + type: OpenSSH + createdAt: '2025-01-20T12:16:23.000000Z' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /ssh_keys/{keyId}: + delete: + summary: Delete an SSH key from a user + deprecated: false + description: '' + tags: + - SSH Keys + parameters: + - name: keyId + in: path + description: A valid SSH key ID as shown in VirtFusion. + required: true + example: 2 + schema: + type: integer + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + get: + summary: Retrieve an SSH key + deprecated: false + description: '' + tags: + - SSH Keys + parameters: + - name: keyId + in: path + description: A valid SSH key ID as shown in VirtFusion. + required: true + example: 1 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 1 + name: MY SSH Key + publicKey: >- + ssh-rsa + AAAAB3NzaC1yc2EAAAADAQABAAACAQC+JdL4fWELBWGAknSu0PwVpDDOlORxy9z7eVnZphZXBzYLMnux+ZogVLns6+O6NDE8JmWvP9RIg3SIga7RDOkW9UCdLzRu0jF2ALL7CK1huo1Ih0PDM9ZbFDy2Fd7a4DTvUX6923fQyW0PWRtyL11R4c9NUqzejKp5kW8vHfPQjzwb1hGIKvkSYkI0Auq4JJhlvjjnoK7Z8t5mpDrVfNTrVqevPgsW5Xwnq8R+02XywrY+Q/wnpxDs4Ujb2aA61A0x5J0xcZQpTQHoJNj77J3VmPI7Ry7Q8hPbTSLGZbN+gODr0lOaL5TdbvM3bnus5JvoqgRoszzPcTiAQZAe3v9UM8hiXise54b8rsc2M9MQ4olPu7TrROZbcw+9q4m6cV+dfVU/NRFkf27YRa4oZNKehHsMiupDyoISgSl4qSB8YXAWsX03oC/gzpB2YJIqEL1Y/SmKYEhgr0cplkvGZy6C/Q9cJHyHlMPtEBPexgcjXC9QrVK4n2cmde3TuSRMctawcat7Nuq08C8fGHaGHr8iAeage3o/ODVOt0rhBu69PknzQeVBdlwK3+p1dH6PnMzNNBhWyNZT/NqB2eS6K8lYpOQ47byXPwYsRLvStUjpZRdikOT7D31T5g8FwOThQ+6WX+xfMD7CSLsSKCn/FhlinbVbG2IhCLH3B30Akw5bUw== + type: OpenSSH + enabled: true + created: '2024-03-13T20:28:32+00:00' + updated: '2024-03-13T20:28:32+00:00' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /ssh_keys/user/{userId}: + get: + summary: Retrieve a users SSH keys + deprecated: false + description: '' + tags: + - SSH Keys + parameters: + - name: userId + in: path + description: A valid user ID as shown in VirtFusion. + required: true + example: 1 + schema: + type: integer + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + - id: 1 + name: My SSH Key + publicKey: >- + ssh-rsa + AAAAB3NzaC1yc2EAAAADAQABAAACAQC+JdL4fWELBWGAknSu0PwVpDDOlORxy9z7eVnZphZXBzYLMnux+ZogVLns6+O6NDE8JmWvP9RIg3SIga7RDOkW9UCdLzRu0jF2ALL7CK1huo1Ih0PDM9ZbFDy2Fd7a4DTvUX6923fQyW0PWRtyL11R4c9NUqzejKp5kW8vHfPQjzwb1hGIKvkSYkI0Auq4JJhlvjjnoK7Z8t5mpDrVfNTrVqevPgsW5Xwnq8R+02XywrY+Q/wnpxDs4Ujb2aA61A0x5J0xcRTpTQHoJNj77J3VmPI7Ry7Q8hPbTSLGZbN+gODr0lOaL5TdbvM3bnus5JvoqgRoszzPcTiNMZAe3v9UM8hiXise54b8rsc2M9MQ4olPu7TrROZbcw+9q4m6cV+dfVU/NRFkf27YRa4oZNKehHsMiupDyoISgSl4qSB8YXAWsX03oC/gzpB2YJIqEL1Y/SmKYEhgr0cplkvGZy6C/Q9cJHyHlMPtEBPexgcjXC9QrVK4n2cmde3TuSRMctawcat7Nuq08C8fGHaGHr8iAeage3o/ODVOt0rhBu69PknzQeVBdlwK3+p1dH6PnMzNNBhWyNZT/NqB2eS6K8lYpOQ47byXPwYsRLvStUjpZRdikOT7D31T5g8FwOThQ+6WX+xfMD7CSLsSKCn/FhlinbVbG2IhCLH3B30Akw5bUw== + type: OpenSSH + enabled: true + created: '2024-03-13T20:28:32+00:00' + updated: '2024-03-13T20:28:32+00:00' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /users/{extRelationId}/byExtRelation: + delete: + summary: Delete a user + deprecated: false + description: '' + tags: + - Users/External Rel ID & Rel Str + parameters: + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: relStr + in: query + description: '' + required: false + example: 'true' + schema: + type: boolean + default: false + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + put: + summary: Modify a user + deprecated: false + description: '' + tags: + - Users/External Rel ID & Rel Str + parameters: + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: relStr + in: query + description: '' + required: false + schema: + type: boolean + default: false + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: Full name of the user. + email: + type: string + description: Email address of the user. + selfService: + type: integer + description: >- + default disabled) 0 = disabled, 1 = hourly, 2 = resource + packs, 3 = hourly & resource packs. + selfServiceHourlyCredit: + type: boolean + description: >- + Enable/disable credit balance billing for hourly self + service. (true|false). + selfServiceHourlyGroupProfiles: + type: array + items: + type: integer + description: >- + (default none) array of self service hourly group profile + ids. + selfServiceResourceGroupProfiles: + type: array + items: + type: integer + description: >- + (default none) array of self service resource group profile + ids. + selfServiceHourlyResourcePack: + type: integer + description: (default none) ID of an hourly self service resource pack. + enabled: + type: boolean + description: >- + (default false) Email the access credentials to the user. + (true|false). + example: + name: jon Doe + email: jon@doe.com + selfService: 3 + selfServiceHourlyCredit: true + selfServiceHourlyGroupProfiles: + - 1 + - 2 + - 3 + selfServiceResourceGroupProfiles: + - 4 + - 5 + - 6 + selfServiceHourlyResourcePack: 1 + enabled: true + responses: + '201': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + name: jon Doe + email: jon@doe.com + selfService: 3 + enabled: true + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + get: + summary: Retrieve a user + deprecated: false + description: '' + tags: + - Users/External Rel ID & Rel Str + parameters: + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: relStr + in: query + description: '' + required: false + schema: + type: boolean + default: false + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 3 + admin: false + extRelationId: 1 + selfService: 3 + selfServiceHourlyGroupProfiles: [] + selfServiceResourceGroupProfiles: [] + selfServiceHourlyResourcePack: null + name: jon Doe + email: jon@doe.com + timezone: Europe/London + suspended: false + twoFactorAuth: false + created: '2025-01-20T12:48:20.000000Z' + updated: '2025-01-20T13:00:38.000000Z' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /users/{extRelationId}/authenticationTokens: + post: + summary: Generate a set of login tokens + deprecated: false + description: '' + tags: + - Users/External Rel ID & Rel Str + parameters: + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: relStr + in: query + description: '' + required: false + schema: + type: boolean + default: false + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + authentication: + tokens: + '1': >- + zYpEXpWEeXR4LfogW3xIomIJS5YW8woOjo18h9st6Sh23ReeTEeQNI1RSQWXYv1AImtQzFm0CLrn6Ve8VtIP3MfDnoRWHxQ334UU + '2': >- + RGzuQDFt0KsWgPozaTZDpuXy3aSsbj6VHWbz4JrhGoj0ZOvaGHUcXM6WGeGuNgfTUPLcy0SYMNJWmI1idC8uR88ZSs00XRnEtbG9 + endpoint: /token_authenticate + endpoint_complete: >- + /token_authenticate/?1=zYpEXpWEeXR4LfogW3xIomIJS5YW8woOjo18h9st6Sh23ReeTEeQNI1RSQWXYv1AImtQzFm0CLrn6Ve8VtIP3MfDnoRWHxQ334UU&2=RGzuQDFt0KsWgPozaTZDpuXy3aSsbj6VHWbz4JrhGoj0ZOvaGHUcXM6WGeGuNgfTUPLcy0SYMNJWmI1idC8uR88ZSs00XRnEtbG9 + expiry: + ttl: 60 + expires: '2025-01-20T12:49:52.170943Z' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /users/{extRelationId}/serverAuthenticationTokens/{serverId}: + post: + summary: Generate a set of loging tokens using a server ID + deprecated: false + description: '' + tags: + - Users/External Rel ID & Rel Str + parameters: + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: serverId + in: path + description: A valid server ID as shown in VirtFusion. + required: true + example: 9 + schema: + type: integer + - name: relStr + in: query + description: '' + required: false + schema: + type: boolean + default: false + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + authentication: + tokens: + '1': >- + oIGBk2qEYTXKMGbaDVbpRFqwQC57Rzl5zWKhwQkgDbRBeXSTH865Bvv0Fm8oY6b0xYpH22xbLAKarOAy28PnToxRu5InfmkIHmo0 + '2': >- + WwiZ9XwqKM5jNGgCsCsUD4B6DDxAKeolJu3dBN7lsK1uGDVvElvfH77sDyukRIzTbbEI6fggKBXuSYRaYc5FqMab4L6PB0QcOxr9 + endpoint: /token_authenticate + endpoint_complete: >- + /token_authenticate/?1=oIGBk2qEYTXKMGbaDVbpRFqwQC57Rzl5zWKhwQkgDbRBeXSTH865Bvv0Fm8oY6b0xYpH22xbLAKarOAy28PnToxRu5InfmkIHmo0&2=WwiZ9XwqKM5jNGgCsCsUD4B6DDxAKeolJu3dBN7lsK1uGDVvElvfH77sDyukRIzTbbEI6fggKBXuSYRaYc5FqMab4L6PB0QcOxr9 + expiry: + ttl: 60 + expires: '2025-01-20T12:52:59.761522Z' + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /users/{extRelationId}/byExtRelation/resetPassword: + post: + summary: Change a user passowrd + deprecated: false + description: '' + tags: + - Users/External Rel ID & Rel Str + parameters: + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: relStr + in: query + description: '' + required: false + schema: + type: boolean + default: false + responses: + '201': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + email: jon@doe.com + password: zD2VqFKO554tdfWKOmGhw + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /users: + post: + summary: Create a user + deprecated: false + description: '' + tags: + - Users + parameters: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: Full name of the user. + email: + type: string + description: Email address of the user. + extRelationId: + type: integer + description: Relation ID. + relStr: + type: string + description: Relational string. + selfService: + type: integer + description: >- + (default disabled) 0 = disabled, 1 = hourly, 2 = resource + packs, 3 = hourly & resource packs. + selfServiceHourlyCredit: + type: boolean + description: ' Enable/disable credit balance billing for hourly self service. (true|false).' + selfServiceHourlyGroupProfiles: + type: array + items: + type: integer + description: >- + (default none) array of self service hourly group profile + ids. + selfServiceResourceGroupProfiles: + type: array + items: + type: integer + description: ' (default none) array of self service resource group profile ids.' + selfServiceHourlyResourcePack: + type: integer + description: ' (default none) ID of an hourly self service resource pack.' + sendMail: + type: boolean + description: >- + (default false) Email the access credentials to the user. + (true|false). + required: + - name + - email + example: + name: Jon Doe + email: jon@doe.com + extRelationId: 1 + selfService: 3 + selfServiceHourlyCredit: true + selfServiceHourlyGroupProfiles: + - 1 + - 2 + - 3 + selfServiceResourceGroupProfiles: + - 4 + - 5 + - 6 + selfServiceHourlyResourcePack: 1 + sendMail: false + responses: + '201': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 2 + admin: false + extRelationId: 1 + selfService: 3 + selfServiceHourlyGroupProfiles: [] + selfServiceResourceGroupProfiles: [] + selfServiceHourlyResourcePack: null + name: Jon Doe + email: jon@doe.com + timezone: Europe/London + suspended: false + twoFactorAuth: false + created: '2025-01-20T12:41:28.000000Z' + updated: '2025-01-20T12:41:28.000000Z' + password: 0hPZSAmj8Tgq1noGoenxpxlC9xf1tc + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/credit/byUserExtRelationId/{extRelationId}: + post: + summary: Add credit to user + deprecated: false + description: '' + tags: + - Self Service/External Relational ID + parameters: + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + schema: + type: string + - name: relStr + in: query + description: '' + required: false + example: 'true' + schema: + type: boolean + requestBody: + content: + application/json: + schema: + type: object + properties: + tokens: + type: number + description: A numeric token value. + reference_1: + type: integer + description: ' An optional reference number. Max 64-bit integer.' + reference_2: + type: string + description: An optional reference in string format. Max 1000 character. + required: + - tokens + example: + tokens: 100 + reference_1: 400 + reference_2: This is a string reference with a 1000 character limit. + responses: + '201': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 2 + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/hourlyGroupProfile/byUserExtRelationId/{extRelationId}: + post: + summary: Add an hourly group profile to a user + deprecated: false + description: '' + tags: + - Self Service/External Relational ID + parameters: + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: relStr + in: query + description: '' + required: false + example: 'true' + schema: + type: boolean + requestBody: + content: + application/json: + schema: + type: object + properties: + profileId: + type: integer + description: ID of an hourly group profile. + required: + - profileId + example: + profileId: 1 + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/resourceGroupProfile/byUserExtRelationId/{extRelationId}: + post: + summary: Add a resource group profile to a user + deprecated: false + description: '' + tags: + - Self Service/External Relational ID + parameters: + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: relStr + in: query + description: '' + required: false + example: 'true' + schema: + type: boolean + requestBody: + content: + application/json: + schema: + type: object + properties: + profileId: + type: integer + description: ID a resource group profile. + required: + - profileId + example: + profileId: 1 + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/resourcePack/byUserExtRelationId/{extRelationId}: + post: + summary: Add a resource pack to a user + deprecated: false + description: '' + tags: + - Self Service/External Relational ID + parameters: + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: relStr + in: query + description: '' + required: false + example: 'true' + schema: + type: boolean + requestBody: + content: + application/json: + schema: + type: object + properties: + packId: + type: integer + description: ID of a resource pack. + enabled: + type: boolean + description: Enable the pack. true|false defaults too true. + required: + - packId + - enabled + example: + packId: 1 + enabled: true + responses: + '201': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + id: 17 + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/hourlyStats/byUserExtRelationId/{extRelationId}: + get: + summary: Retrieve hourly statistics + deprecated: false + description: '' + tags: + - Self Service/External Relational ID + parameters: + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: period[] + in: query + description: 'Example: period[]=YYYY-MM-DD&period[]=YYYY-MM-D' + required: false + example: YYYY-MM-DD + schema: + type: string + - name: range + in: query + description: range=m + required: false + example: m + schema: + type: string + - name: relStr + in: query + description: '' + required: false + example: 'true' + schema: + type: boolean + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + periodId: 0 + period: January 2025 + previousPeriod: December 2024 + nextPeriod: February 2025 + monthlyTotal: + hours: 0 + value: '0.00' + tokens: false + servers: 0 + credit: + value: 0 + currency: + code: '' + prefix: '' + suffix: '' + value: 0 + currentValue: 0 + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/access/byUserExtRelationId/{extRelationId}: + put: + summary: Modify user access + deprecated: false + description: '' + tags: + - Self Service/External Relational ID + parameters: + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: relStr + in: query + description: '' + required: false + example: 'true' + schema: + type: boolean + requestBody: + content: + application/json: + schema: + type: object + properties: + syncToProfiles: + type: boolean + description: >- + true|false Default false. If true, the self service access + level will be set based on profiles. + required: + - syncToProfiles + example: + syncToProfiles: true + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/hourlyGroupProfile/{profileId}/byUserExtRelationId/{extRelationId}: + delete: + summary: Remove hourly group profile from a user + deprecated: false + description: '' + tags: + - Self Service/External Relational ID + parameters: + - name: profileId + in: path + description: ID of a hourly group profile. + required: true + example: 1 + schema: + type: integer + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: relStr + in: query + description: '' + required: false + example: 'true' + schema: + type: boolean + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/resourceGroupProfile/{profileId}/byUserExtRelationId/{extRelationId}: + delete: + summary: Remove resource group from a user + deprecated: false + description: '' + tags: + - Self Service/External Relational ID + parameters: + - name: profileId + in: path + description: ID of a hourly group profile. + required: true + example: 1 + schema: + type: integer + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: relStr + in: query + description: '' + required: false + example: 'true' + schema: + type: boolean + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/report/byUserExtRelationId/{extRelationId}: + get: + summary: Generate a report + deprecated: false + description: '' + tags: + - Self Service/External Relational ID + parameters: + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: period + in: query + description: >- + A single period in the range of 0-24 (0 being the currently defined + month in the self service settings | optional and will default to + the current month if not defined). + required: false + example: '0' + schema: + type: string + - name: currency + in: query + description: >- + A three letter currency code that is defined in the self service + settings. (optional and will default to the user defined currency if + not defined). + required: false + example: USD + schema: + type: string + - name: relStr + in: query + description: '' + required: false + example: 'true' + schema: + type: boolean + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + usage: + servers: [] + serversTotal: + hours: false + value: false + tokens: false + hourConversionRate: false + monthlyTotal: + hours: false + value: false + tokens: false + addonsTotal: + hours: 0 + value: 0 + tokens: false + taxStatus: 3 + success: false + history: '0' + breakdown: true + term: January 2025 + previousTerm: December 2024 + nextTerm: February 2025 + period: + ymd: '2025-01-01' + start: '2025-01-01T00:00:00+00:00' + end: '2025-01-31T00:00:00+00:00' + showHourlyRate: false + showMonthlyRate: false + currency: + prefix: '' + suffix: '' + code: '' + currentValue: 0 + value: 0 + default: + prefix: '' + suffix: '' + code: '' + limits: + success: true + packs: [] + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/hourlyResourcePack/byUserExtRelationId/{extRelationId}: + put: + summary: Set an hourly resource pack + deprecated: false + description: '' + tags: + - Self Service/External Relational ID + parameters: + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: relStr + in: query + description: '' + required: false + example: 'true' + schema: + type: boolean + requestBody: + content: + application/json: + schema: + type: object + properties: + packId: + type: integer + description: ID of an hourly resource pack. + required: + - packId + example: + packId: 1 + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/usage/byUserExtRelationId/{extRelationId}: + get: + summary: Retrieve a users usage + deprecated: false + description: '' + tags: + - Self Service/External Relational ID + parameters: + - name: extRelationId + in: path + description: A valid external relational ID as shown in VirtFusion. + required: true + example: '1' + schema: + type: string + - name: period[] + in: query + description: Array of periods or a single period. (YYYY-MM-DD). + required: false + example: '2025-01-01' + schema: + type: string + - name: range + in: query + description: >- + Length of period. Defaults to 1 month. Possible values d = day, w = + week, 2w = 2 weeks, 3w = 3 weeks, m = month. + required: false + example: m + schema: + type: string + - name: relStr + in: query + description: '' + required: false + example: 'true' + schema: + type: boolean + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + user: + id: 3 + relationalId: 1 + currency: null + timezone: Europe/London + name: jon Doe + email: jon@doe.com + usageServers: + hours: 0 + token: 0 + tokenReal: 0 + usageServersBillable: + hours: 0 + token: 0 + tokenReal: 0 + usageAddons: + hours: 0 + token: 0 + tokenReal: 0 + usageAddonsBillable: + hours: 0 + token: 0 + tokenReal: 0 + periods: + - period: '2025-01-01' + range: month + start: '2025-01-01T00:00:00+00:00' + end: '2025-01-31T23:59:59+00:00' + timezone: UTC + currentPeriod: true + hoursInMonthPeriod: 744 + monthToHourRate: 730 + monthToHourRateType: 1 + days: 31 + hours: 744 + minutes: 44640 + seconds: 2678400 + usageServers: + hours: 0 + token: 0 + tokenReal: 0 + usageServersBillable: + hours: 0 + token: 0 + tokenReal: 0 + usageAddons: + hours: 0 + token: 0 + tokenReal: 0 + usageAddonsBillable: + hours: 0 + token: 0 + tokenReal: 0 + servers: [] + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/credit/{creditId}: + delete: + summary: Cancel credit that was applied to a user + deprecated: false + description: '' + tags: + - Self Service + parameters: + - name: creditId + in: path + description: A valid credit ID. + required: true + example: 1 + schema: + type: integer + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/resourcePackServers/{packId}: + delete: + summary: Delete all servers attached to a pack ID + deprecated: false + description: '' + tags: + - Self Service + parameters: + - name: packId + in: path + description: ID of a resource pack. + required: true + example: 1 + schema: + type: integer + - name: delay + in: query + description: The delay in minutes. Defaults to 30 (0 - 43800). + required: false + example: 30 + schema: + type: integer + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/resourcePack/{packId}: + delete: + summary: Delete a user resource pack + deprecated: false + description: '' + tags: + - Self Service + parameters: + - name: packId + in: path + description: ID of a resource pack. + required: true + example: 1 + schema: + type: integer + - name: disable + in: query + description: >- + Disable the pack if it can't be deleted. true|false Defaults to + false. + required: false + example: 'true' + schema: + type: boolean + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + get: + summary: Retrieve a user resource pack + deprecated: false + description: '' + tags: + - Self Service + parameters: + - name: packId + in: path + description: ID of a resource pack. + required: true + example: 1 + schema: + type: integer + - name: withServers + in: query + description: include a list of assigned servers. true|false Defaults to false. + required: false + example: 'true' + schema: + type: boolean + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + type: pack + id: 18 + pid: 9 + label: null + name: Pack 2 · 2 / 4096 / 250 + limits: + total_servers: 2 + total_memory: 4096 + total_storage: 200 + total_cpu: 24 + total_traffic: 1000000 + max_memory: 4096 + max_storage: 10 + max_cpu: 8 + max_traffic: 500000 + used: + servers: 0 + memory: 0 + storage: 0 + cpu: 0 + traffic: 0 + usage: + servers: + t: 2 + u: 0 + f: 2 + p: 0 + l: true + memory: + t: 4096 + u: 0 + f: 4096 + p: 0 + l: true + storage: + t: 200 + u: 0 + f: 200 + p: 0 + l: true + cpu: + t: 24 + u: 0 + p: 0 + f: 24 + l: true + traffic: + t: 1000000 + u: 0 + f: 1000000 + p: 0 + l: true + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + put: + summary: Modify user resource pack + deprecated: false + description: '' + tags: + - Self Service + parameters: + - name: packId + in: path + description: ID of a resource pack. + required: true + example: 1 + schema: + type: integer + requestBody: + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + required: + - enabled + example: + enabled: true + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/currencies: + get: + summary: Retrieve currencies + deprecated: false + description: '' + tags: + - Self Service + parameters: [] + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: {} + example: + data: + - id: 11 + code: USD + value: '0.0100000000' + prefix: $ + suffix: null + default: true + enabled: true + - id: 12 + code: GBP + value: '0.0200000000' + prefix: £ + suffix: null + default: false + enabled: true + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/resourcePackServers/{packId}/suspend: + post: + summary: Suspend all servers assigned to a reosurce pack + deprecated: false + description: '' + tags: + - Self Service + parameters: + - name: packId + in: path + description: ID of a resource pack. + required: true + example: 1 + schema: + type: integer + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] + /selfService/resourcePackServers/{packId}/unsuspend: + post: + summary: Unsuspend all servers assigned to a reosurce pack + deprecated: false + description: '' + tags: + - Self Service + parameters: + - name: packId + in: path + description: ID of a resource pack. + required: true + example: 1 + schema: + type: integer + responses: + '204': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + headers: {} + '401': + $ref: '#/components/responses/401' + description: '' + security: + - bearer: [] +components: + schemas: {} + responses: + '401': + description: '' + content: + application/octet-stream: + schema: + type: object + properties: {} + examples: + '401': + summary: '401' + value: 401 Unauthorized + securitySchemes: + bearer: + type: http + scheme: bearer +servers: + - url: https://cp.domain.com/api/v1 + description: Example URL +security: + - bearer: [] diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..862f7e2 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1744 @@ +{ + "name": "@ezscale/virtfusion-mcp", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@ezscale/virtfusion-mcp", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.12.1", + "zod": "^3.25.0" + }, + "bin": { + "virtfusion-mcp": "dist/index.js" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "tsx": "^4.19.0", + "typescript": "^5.7.0", + "yaml": "^2.7.0" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.11.tgz", + "integrity": "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.27.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.27.1.tgz", + "integrity": "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@types/node": { + "version": "22.19.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", + "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "peer": true, + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.1.tgz", + "integrity": "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==", + "license": "MIT", + "dependencies": { + "ip-address": "10.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.12.8", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.8.tgz", + "integrity": "sha512-VJCEvtrezO1IAR+kqEYnxUOoStaQPGrCmX3j4wDTNOcD1uRPFpGlwQUIW8niPuvHXaTUxeOUl5MMDGrl+tmO9A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.1.tgz", + "integrity": "sha512-jUaKr1yrbfaImV7R2TN/b3IcZzsw38/chqMpo2XJ7i2F8AfM/lA4G1goC3JVEwg0H7UldTmSt3P68nt31W7/mw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25 || ^4" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..e6fee8f --- /dev/null +++ b/package.json @@ -0,0 +1,43 @@ +{ + "name": "@ezscale/virtfusion-mcp", + "version": "1.0.0", + "description": "Model Context Protocol (MCP) server for the VirtFusion virtualization control panel API", + "type": "module", + "main": "dist/index.js", + "bin": { + "virtfusion-mcp": "dist/index.js" + }, + "scripts": { + "build": "tsc", + "start": "node dist/index.js", + "dev": "tsx src/index.ts", + "extract-endpoints": "tsx scripts/extract-endpoints.ts", + "check-endpoint-drift": "tsx scripts/check-endpoint-drift.ts" + }, + "keywords": [ + "mcp", + "virtfusion", + "virtualization", + "hosting", + "model-context-protocol" + ], + "author": "EZSCALE Hosting, LLC", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://git.ezscale.cloud/EZSCALE/virtfusion-mcp.git" + }, + "engines": { + "node": ">=22.0.0" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.12.1", + "zod": "^3.25.0" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "tsx": "^4.19.0", + "typescript": "^5.7.0", + "yaml": "^2.7.0" + } +} diff --git a/scripts/check-endpoint-drift.ts b/scripts/check-endpoint-drift.ts new file mode 100644 index 0000000..a75c1d6 --- /dev/null +++ b/scripts/check-endpoint-drift.ts @@ -0,0 +1,79 @@ +import { readFileSync } from 'node:fs'; +import { parse } from 'yaml'; + +interface Endpoint { + method: string; + path: string; + summary: string; + tag: string; +} + +function extractEndpoints(specPath: string): Endpoint[] { + const spec = parse(readFileSync(specPath, 'utf-8')); + const endpoints: Endpoint[] = []; + + for (const [path, methods] of Object.entries(spec.paths as Record>)) { + for (const [method, details] of Object.entries(methods)) { + if (['get', 'post', 'put', 'delete', 'patch'].includes(method)) { + const op = details as { summary?: string; tags?: string[] }; + endpoints.push({ + method: method.toUpperCase(), + path, + summary: op.summary ?? '', + tag: op.tags?.[0] ?? 'Untagged', + }); + } + } + } + + return endpoints.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method)); +} + +function endpointKey(e: Endpoint): string { + return `${e.method} ${e.path}`; +} + +const specPath = new URL('../openapi.yaml', import.meta.url).pathname; +const manifestPath = new URL('../endpoint-manifest.json', import.meta.url).pathname; + +const current = extractEndpoints(specPath); + +let manifest: Endpoint[]; +try { + manifest = JSON.parse(readFileSync(manifestPath, 'utf-8')); +} catch { + console.error('Error: endpoint-manifest.json not found. Run `npm run extract-endpoints > endpoint-manifest.json` first.'); + process.exit(1); +} + +const manifestKeys = new Set(manifest.map(endpointKey)); +const currentKeys = new Set(current.map(endpointKey)); + +const added = current.filter((e) => !manifestKeys.has(endpointKey(e))); +const removed = manifest.filter((e) => !currentKeys.has(endpointKey(e))); + +if (added.length === 0 && removed.length === 0) { + console.log(`No endpoint drift detected. ${current.length} endpoints match the manifest.`); + process.exit(0); +} + +console.log('Endpoint drift detected!\n'); + +if (added.length > 0) { + console.log(`New endpoints (${added.length}):`); + for (const e of added) { + console.log(` + ${e.method} ${e.path} — ${e.summary} [${e.tag}]`); + } + console.log(); +} + +if (removed.length > 0) { + console.log(`Removed endpoints (${removed.length}):`); + for (const e of removed) { + console.log(` - ${e.method} ${e.path} — ${e.summary} [${e.tag}]`); + } + console.log(); +} + +console.log('Update endpoint-manifest.json: npm run extract-endpoints > endpoint-manifest.json'); +process.exit(1); diff --git a/scripts/extract-endpoints.ts b/scripts/extract-endpoints.ts new file mode 100644 index 0000000..3f62a4a --- /dev/null +++ b/scripts/extract-endpoints.ts @@ -0,0 +1,32 @@ +import { readFileSync } from 'node:fs'; +import { parse } from 'yaml'; + +interface Endpoint { + method: string; + path: string; + summary: string; + tag: string; +} + +const specPath = new URL('../openapi.yaml', import.meta.url).pathname; +const spec = parse(readFileSync(specPath, 'utf-8')); + +const endpoints: Endpoint[] = []; + +for (const [path, methods] of Object.entries(spec.paths as Record>)) { + for (const [method, details] of Object.entries(methods)) { + if (['get', 'post', 'put', 'delete', 'patch'].includes(method)) { + const op = details as { summary?: string; tags?: string[] }; + endpoints.push({ + method: method.toUpperCase(), + path, + summary: op.summary ?? '', + tag: op.tags?.[0] ?? 'Untagged', + }); + } + } +} + +endpoints.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method)); + +console.log(JSON.stringify(endpoints, null, 2)); diff --git a/src/client.ts b/src/client.ts new file mode 100644 index 0000000..8f6e677 --- /dev/null +++ b/src/client.ts @@ -0,0 +1,76 @@ +import { VirtFusionApiError } from './errors.js'; +import { QueryParams } from './types.js'; + +export class VirtFusionClient { + private readonly baseUrl: string; + private readonly token: string; + + constructor(baseUrl: string, token: string) { + this.baseUrl = baseUrl.replace(/\/+$/, ''); + this.token = token; + } + + private buildUrl(path: string, query?: QueryParams): string { + const url = new URL(`${this.baseUrl}${path}`); + if (query) { + for (const [key, value] of Object.entries(query)) { + if (value !== undefined) { + url.searchParams.set(key, String(value)); + } + } + } + return url.toString(); + } + + private async request(method: string, path: string, body?: unknown, query?: QueryParams): Promise { + const url = this.buildUrl(path, query); + const headers: Record = { + Authorization: `Bearer ${this.token}`, + Accept: 'application/json', + }; + + if (body !== undefined) { + headers['Content-Type'] = 'application/json'; + } + + const response = await fetch(url, { + method, + headers, + body: body !== undefined ? JSON.stringify(body) : undefined, + }); + + if (response.status === 204) { + return { success: true }; + } + + let responseBody: unknown; + const text = await response.text(); + try { + responseBody = JSON.parse(text); + } catch { + responseBody = text; + } + + if (!response.ok) { + throw new VirtFusionApiError(response.status, response.statusText, responseBody); + } + + return responseBody; + } + + async get(path: string, query?: QueryParams): Promise { + return this.request('GET', path, undefined, query); + } + + async post(path: string, body?: unknown, query?: QueryParams): Promise { + return this.request('POST', path, body, query); + } + + async put(path: string, body?: unknown, query?: QueryParams): Promise { + return this.request('PUT', path, body, query); + } + + async delete(path: string, body?: unknown, query?: QueryParams): Promise { + return this.request('DELETE', path, body, query); + } +} diff --git a/src/errors.ts b/src/errors.ts new file mode 100644 index 0000000..1058af8 --- /dev/null +++ b/src/errors.ts @@ -0,0 +1,41 @@ +import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; + +export class VirtFusionApiError extends Error { + constructor( + public readonly statusCode: number, + public readonly statusText: string, + public readonly errorBody: unknown, + ) { + super(`VirtFusion API error ${statusCode}: ${statusText}`); + this.name = 'VirtFusionApiError'; + } +} + +export function formatErrorResponse(error: unknown): CallToolResult { + if (error instanceof VirtFusionApiError) { + return { + isError: true, + content: [ + { + type: 'text', + text: JSON.stringify( + { + error: true, + statusCode: error.statusCode, + statusText: error.statusText, + details: error.errorBody, + }, + null, + 2, + ), + }, + ], + }; + } + + const message = error instanceof Error ? error.message : String(error); + return { + isError: true, + content: [{ type: 'text', text: JSON.stringify({ error: true, message }, null, 2) }], + }; +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..a566d85 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,40 @@ +#!/usr/bin/env node + +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import { VirtFusionClient } from './client.js'; +import { registerAllTools } from './tools/index.js'; + +const VIRTFUSION_API_URL = process.env.VIRTFUSION_API_URL; +const VIRTFUSION_API_TOKEN = process.env.VIRTFUSION_API_TOKEN; + +if (!VIRTFUSION_API_URL) { + console.error('Error: VIRTFUSION_API_URL environment variable is required.'); + console.error('Set it to your VirtFusion panel URL, e.g. https://cp.example.com/api/v1'); + process.exit(1); +} + +if (!VIRTFUSION_API_TOKEN) { + console.error('Error: VIRTFUSION_API_TOKEN environment variable is required.'); + console.error('Generate an API token in your VirtFusion admin panel.'); + process.exit(1); +} + +const client = new VirtFusionClient(VIRTFUSION_API_URL, VIRTFUSION_API_TOKEN); + +const server = new McpServer({ + name: 'virtfusion-mcp', + version: '1.0.0', +}); + +registerAllTools(server, client); + +async function main() { + const transport = new StdioServerTransport(); + await server.connect(transport); +} + +main().catch((error) => { + console.error('Fatal error:', error); + process.exit(1); +}); diff --git a/src/tools/backups.ts b/src/tools/backups.ts new file mode 100644 index 0000000..06d0314 --- /dev/null +++ b/src/tools/backups.ts @@ -0,0 +1,22 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerBackupTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'backups_list_by_server', + 'List all backups for a specific server', + { + serverId: z.number().describe('The server ID'), + }, + async ({ serverId }) => { + try { + const result = await client.get(`/backups/server/${serverId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/dns.ts b/src/tools/dns.ts new file mode 100644 index 0000000..a1f98af --- /dev/null +++ b/src/tools/dns.ts @@ -0,0 +1,22 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerDnsTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'dns_get_service', + 'Retrieve a DNS service', + { + serviceId: z.number().describe('The DNS service ID'), + }, + async ({ serviceId }) => { + try { + const result = await client.get(`/dns/services/${serviceId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/general.ts b/src/tools/general.ts new file mode 100644 index 0000000..d95d720 --- /dev/null +++ b/src/tools/general.ts @@ -0,0 +1,20 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerGeneralTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'general_test_connection', + 'Test the API connection to VirtFusion', + {}, + async () => { + try { + const result = await client.get('/connect'); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/hypervisor-groups.ts b/src/tools/hypervisor-groups.ts new file mode 100644 index 0000000..0514af4 --- /dev/null +++ b/src/tools/hypervisor-groups.ts @@ -0,0 +1,54 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerHypervisorGroupTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'hypervisor_groups_list', + 'List all hypervisor groups', + { + results: z.number().optional().describe('Number of results to return'), + }, + async ({ results }) => { + try { + const result = await client.get('/compute/hypervisors/groups', { results }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'hypervisor_groups_get', + 'Retrieve details of a specific hypervisor group', + { + hypervisorGroupId: z.number().describe('The hypervisor group ID'), + }, + async ({ hypervisorGroupId }) => { + try { + const result = await client.get(`/compute/hypervisors/groups/${hypervisorGroupId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'hypervisor_groups_get_resources', + 'Retrieve resources for a hypervisor group', + { + hypervisorGroupId: z.number().describe('The hypervisor group ID'), + }, + async ({ hypervisorGroupId }) => { + try { + const result = await client.get(`/compute/hypervisors/groups/${hypervisorGroupId}/resources`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/hypervisors.ts b/src/tools/hypervisors.ts new file mode 100644 index 0000000..8bfbd14 --- /dev/null +++ b/src/tools/hypervisors.ts @@ -0,0 +1,38 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerHypervisorTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'hypervisors_list', + 'List all hypervisors', + { + results: z.number().optional().describe('Number of results to return'), + }, + async ({ results }) => { + try { + const result = await client.get('/compute/hypervisors', { results }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'hypervisors_get', + 'Retrieve details of a specific hypervisor', + { + hypervisorId: z.number().describe('The hypervisor ID'), + }, + async ({ hypervisorId }) => { + try { + const result = await client.get(`/compute/hypervisors/${hypervisorId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/index.ts b/src/tools/index.ts new file mode 100644 index 0000000..56f6037 --- /dev/null +++ b/src/tools/index.ts @@ -0,0 +1,39 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { VirtFusionClient } from '../client.js'; +import { registerGeneralTools } from './general.js'; +import { registerHypervisorTools } from './hypervisors.js'; +import { registerHypervisorGroupTools } from './hypervisor-groups.js'; +import { registerServerTools } from './servers.js'; +import { registerServerPowerTools } from './servers-power.js'; +import { registerServerNetworkTools } from './servers-network.js'; +import { registerServerFirewallTools } from './servers-firewall.js'; +import { registerServerTrafficTools } from './servers-traffic.js'; +import { registerIpBlockTools } from './ip-blocks.js'; +import { registerBackupTools } from './backups.js'; +import { registerDnsTools } from './dns.js'; +import { registerMediaTools } from './media.js'; +import { registerPackageTools } from './packages.js'; +import { registerQueueTools } from './queue.js'; +import { registerSshKeyTools } from './ssh-keys.js'; +import { registerUserTools } from './users.js'; +import { registerSelfServiceTools } from './self-service.js'; + +export function registerAllTools(server: McpServer, client: VirtFusionClient): void { + registerGeneralTools(server, client); + registerHypervisorTools(server, client); + registerHypervisorGroupTools(server, client); + registerServerTools(server, client); + registerServerPowerTools(server, client); + registerServerNetworkTools(server, client); + registerServerFirewallTools(server, client); + registerServerTrafficTools(server, client); + registerIpBlockTools(server, client); + registerBackupTools(server, client); + registerDnsTools(server, client); + registerMediaTools(server, client); + registerPackageTools(server, client); + registerQueueTools(server, client); + registerSshKeyTools(server, client); + registerUserTools(server, client); + registerSelfServiceTools(server, client); +} diff --git a/src/tools/ip-blocks.ts b/src/tools/ip-blocks.ts new file mode 100644 index 0000000..a7bfdd8 --- /dev/null +++ b/src/tools/ip-blocks.ts @@ -0,0 +1,57 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerIpBlockTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'ip_blocks_list', + 'List all IP blocks', + { + results: z.number().optional().describe('Number of results to return'), + }, + async ({ results }) => { + try { + const result = await client.get('/connectivity/ipblocks', { results }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'ip_blocks_get', + 'Retrieve details of a specific IP block', + { + blockId: z.number().describe('The IP block ID'), + }, + async ({ blockId }) => { + try { + const result = await client.get(`/connectivity/ipblocks/${blockId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'ip_blocks_add_ipv4_range', + 'Add an IPv4 range to an IP block', + { + blockId: z.number().describe('The IP block ID'), + type: z.string().describe('Type of IP range (always "range")'), + start: z.string().describe('Start IPv4 address'), + end: z.string().describe('End IPv4 address'), + }, + async ({ blockId, type, start, end }) => { + try { + const result = await client.post(`/connectivity/ipblocks/${blockId}/ipv4`, { type, start, end }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/media.ts b/src/tools/media.ts new file mode 100644 index 0000000..21a195d --- /dev/null +++ b/src/tools/media.ts @@ -0,0 +1,38 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerMediaTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'media_get_iso', + 'Retrieve details of a specific ISO image', + { + isoId: z.number().describe('The ISO image ID'), + }, + async ({ isoId }) => { + try { + const result = await client.get(`/media/iso/${isoId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'media_get_templates_by_package', + 'List OS templates available for a specific package', + { + serverPackageId: z.number().describe('The server package ID'), + }, + async ({ serverPackageId }) => { + try { + const result = await client.get(`/media/templates/fromServerPackageSpec/${serverPackageId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/packages.ts b/src/tools/packages.ts new file mode 100644 index 0000000..53826f7 --- /dev/null +++ b/src/tools/packages.ts @@ -0,0 +1,36 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerPackageTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'packages_list', + 'List all available packages', + {}, + async () => { + try { + const result = await client.get('/packages'); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'packages_get', + 'Retrieve details of a specific package', + { + packageId: z.number().describe('The package ID'), + }, + async ({ packageId }) => { + try { + const result = await client.get(`/packages/${packageId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/queue.ts b/src/tools/queue.ts new file mode 100644 index 0000000..dad6b86 --- /dev/null +++ b/src/tools/queue.ts @@ -0,0 +1,22 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerQueueTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'queue_get', + 'Retrieve the status of a queue item', + { + queueId: z.number().describe('The queue item ID'), + }, + async ({ queueId }) => { + try { + const result = await client.get(`/queue/${queueId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/self-service.ts b/src/tools/self-service.ts new file mode 100644 index 0000000..4fda022 --- /dev/null +++ b/src/tools/self-service.ts @@ -0,0 +1,348 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerSelfServiceTools(server: McpServer, client: VirtFusionClient): void { + // --- Self Service / External Relational ID endpoints --- + + server.tool( + 'self_service_add_credit', + 'Add credit to a user', + { + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + tokens: z.number().describe('Number of credit tokens to add'), + reference_1: z.number().optional().describe('Optional numeric reference'), + reference_2: z.string().optional().describe('Optional string reference'), + }, + async ({ extRelationId, relStr, tokens, reference_1, reference_2 }) => { + try { + const body: Record = { tokens }; + if (reference_1 !== undefined) body.reference_1 = reference_1; + if (reference_2 !== undefined) body.reference_2 = reference_2; + + const result = await client.post(`/selfService/credit/byUserExtRelationId/${extRelationId}`, body, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_cancel_credit', + 'Cancel credit that was applied to a user', + { + creditId: z.number().describe('The credit ID to cancel'), + }, + async ({ creditId }) => { + try { + const result = await client.delete(`/selfService/credit/${creditId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_get_currencies', + 'Retrieve available currencies', + {}, + async () => { + try { + const result = await client.get('/selfService/currencies'); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_add_hourly_group_profile', + 'Add an hourly group profile to a user', + { + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + profileId: z.number().describe('The hourly group profile ID'), + }, + async ({ extRelationId, relStr, profileId }) => { + try { + const result = await client.post(`/selfService/hourlyGroupProfile/byUserExtRelationId/${extRelationId}`, { profileId }, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_remove_hourly_group_profile', + 'Remove an hourly group profile from a user', + { + profileId: z.number().describe('The hourly group profile ID'), + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + }, + async ({ profileId, extRelationId, relStr }) => { + try { + const result = await client.delete(`/selfService/hourlyGroupProfile/${profileId}/byUserExtRelationId/${extRelationId}`, undefined, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_add_resource_group_profile', + 'Add a resource group profile to a user', + { + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + profileId: z.number().describe('The resource group profile ID'), + }, + async ({ extRelationId, relStr, profileId }) => { + try { + const result = await client.post(`/selfService/resourceGroupProfile/byUserExtRelationId/${extRelationId}`, { profileId }, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_remove_resource_group_profile', + 'Remove a resource group profile from a user', + { + profileId: z.number().describe('The resource group profile ID'), + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + }, + async ({ profileId, extRelationId, relStr }) => { + try { + const result = await client.delete(`/selfService/resourceGroupProfile/${profileId}/byUserExtRelationId/${extRelationId}`, undefined, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_add_resource_pack', + 'Add a resource pack to a user', + { + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + packId: z.number().describe('The resource pack ID'), + enabled: z.boolean().optional().describe('Whether the resource pack is enabled'), + }, + async ({ extRelationId, relStr, packId, enabled }) => { + try { + const body: Record = { packId }; + if (enabled !== undefined) body.enabled = enabled; + + const result = await client.post(`/selfService/resourcePack/byUserExtRelationId/${extRelationId}`, body, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + // --- Self Service endpoints (non-relational) --- + + server.tool( + 'self_service_get_resource_pack', + 'Retrieve a user resource pack', + { + packId: z.number().describe('The resource pack ID'), + }, + async ({ packId }) => { + try { + const result = await client.get(`/selfService/resourcePack/${packId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_modify_resource_pack', + 'Modify a user resource pack', + { + packId: z.number().describe('The resource pack ID'), + }, + async ({ packId }) => { + try { + const result = await client.put(`/selfService/resourcePack/${packId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_delete_resource_pack', + 'Delete a user resource pack', + { + packId: z.number().describe('The resource pack ID'), + }, + async ({ packId }) => { + try { + const result = await client.delete(`/selfService/resourcePack/${packId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_get_hourly_stats', + 'Retrieve hourly statistics for a user', + { + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + period: z.string().optional().describe('Comma-separated dates in YYYY-MM-DD format'), + range: z.string().optional().describe('Date range filter'), + }, + async ({ extRelationId, relStr, period, range }) => { + try { + const result = await client.get(`/selfService/hourlyStats/byUserExtRelationId/${extRelationId}`, { relStr, period, range }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_set_hourly_resource_pack', + 'Set an hourly resource pack for a user', + { + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + packId: z.number().describe('The resource pack ID'), + }, + async ({ extRelationId, relStr, packId }) => { + try { + const result = await client.put(`/selfService/hourlyResourcePack/byUserExtRelationId/${extRelationId}`, { packId }, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_modify_access', + "Modify a user's self-service access settings", + { + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + selfService: z.number().optional().describe('Self-service level (0-3)'), + selfServiceHourlyCredit: z.boolean().optional().describe('Enable hourly credit for self-service'), + }, + async ({ extRelationId, relStr, selfService, selfServiceHourlyCredit }) => { + try { + const body: Record = {}; + if (selfService !== undefined) body.selfService = selfService; + if (selfServiceHourlyCredit !== undefined) body.selfServiceHourlyCredit = selfServiceHourlyCredit; + + const result = await client.put(`/selfService/access/byUserExtRelationId/${extRelationId}`, Object.keys(body).length > 0 ? body : undefined, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_get_report', + 'Generate a report for a user', + { + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + }, + async ({ extRelationId, relStr }) => { + try { + const result = await client.get(`/selfService/report/byUserExtRelationId/${extRelationId}`, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_get_usage', + 'Retrieve usage data for a user', + { + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + }, + async ({ extRelationId, relStr }) => { + try { + const result = await client.get(`/selfService/usage/byUserExtRelationId/${extRelationId}`, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_delete_pack_servers', + 'Delete all servers attached to a resource pack', + { + packId: z.number().describe('The resource pack ID'), + }, + async ({ packId }) => { + try { + const result = await client.delete(`/selfService/resourcePackServers/${packId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_suspend_pack_servers', + 'Suspend all servers assigned to a resource pack', + { + packId: z.number().describe('The resource pack ID'), + }, + async ({ packId }) => { + try { + const result = await client.post(`/selfService/resourcePackServers/${packId}/suspend`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'self_service_unsuspend_pack_servers', + 'Unsuspend all servers assigned to a resource pack', + { + packId: z.number().describe('The resource pack ID'), + }, + async ({ packId }) => { + try { + const result = await client.post(`/selfService/resourcePackServers/${packId}/unsuspend`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/servers-firewall.ts b/src/tools/servers-firewall.ts new file mode 100644 index 0000000..a49c638 --- /dev/null +++ b/src/tools/servers-firewall.ts @@ -0,0 +1,79 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerServerFirewallTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'servers_firewall_get', + 'Retrieve firewall configuration for a server interface', + { + serverId: z.number().describe('The server ID'), + interface: z.enum(['primary', 'secondary']).describe('Network interface'), + sync: z.boolean().optional().describe('Sync firewall state from hypervisor'), + }, + async ({ serverId, interface: iface, sync }) => { + try { + const result = await client.get(`/servers/${serverId}/firewall/${iface}`, { sync }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_firewall_enable', + 'Enable the firewall on a server interface', + { + serverId: z.number().describe('The server ID'), + interface: z.enum(['primary', 'secondary']).describe('Network interface'), + sync: z.boolean().optional().describe('Sync firewall state from hypervisor'), + }, + async ({ serverId, interface: iface, sync }) => { + try { + const result = await client.post(`/servers/${serverId}/firewall/${iface}/enable`, undefined, { sync }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_firewall_disable', + 'Disable the firewall on a server interface', + { + serverId: z.number().describe('The server ID'), + interface: z.enum(['primary', 'secondary']).describe('Network interface'), + sync: z.boolean().optional().describe('Sync firewall state from hypervisor'), + }, + async ({ serverId, interface: iface, sync }) => { + try { + const result = await client.post(`/servers/${serverId}/firewall/${iface}/disable`, undefined, { sync }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_firewall_set_rules', + 'Apply firewall rulesets to a server interface', + { + serverId: z.number().describe('The server ID'), + interface: z.enum(['primary', 'secondary']).describe('Network interface'), + sync: z.boolean().optional().describe('Sync firewall state from hypervisor'), + rulesets: z.array(z.number()).describe('Firewall ruleset IDs (empty array to flush all rules)'), + }, + async ({ serverId, interface: iface, sync, rulesets }) => { + try { + const result = await client.post(`/servers/${serverId}/firewall/${iface}/rules`, { rulesets }, { sync }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/servers-network.ts b/src/tools/servers-network.ts new file mode 100644 index 0000000..1f43f44 --- /dev/null +++ b/src/tools/servers-network.ts @@ -0,0 +1,105 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerServerNetworkTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'servers_network_whitelist_add', + "Add an IP address to a server's network whitelist", + { + serverId: z.number().describe('The server ID'), + interface: z.enum(['primary', 'secondary']).describe('Network interface'), + ip: z.string().describe('IP address to whitelist'), + cidr: z.number().describe('CIDR notation prefix length'), + }, + async ({ serverId, interface: iface, ip, cidr }) => { + try { + const result = await client.post(`/servers/${serverId}/networkWhitelist`, { + interface: iface, + ip, + cidr, + }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_network_whitelist_remove', + "Remove an IP from a server's network whitelist", + { + serverId: z.number().describe('The server ID'), + interface: z.string().describe('Network interface'), + ip: z.string().describe('IP address to remove'), + }, + async ({ serverId, interface: iface, ip }) => { + try { + const result = await client.delete(`/servers/${serverId}/networkWhitelist`, { + interface: iface, + ip, + }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_network_ipv4_add', + 'Add specific IPv4 addresses to a server', + { + serverId: z.number().describe('The server ID'), + ip: z.array(z.string()).describe('Array of IPv4 addresses to add'), + }, + async ({ serverId, ip }) => { + try { + const result = await client.post(`/servers/${serverId}/ipv4`, { ip }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_network_ipv4_remove', + 'Remove IPv4 addresses from a server', + { + serverId: z.number().describe('The server ID'), + ip: z.array(z.string()).describe('Array of IPv4 addresses to remove'), + }, + async ({ serverId, ip }) => { + try { + const result = await client.delete(`/servers/${serverId}/ipv4`, { ip }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_network_ipv4_add_qty', + 'Add a quantity of IPv4 addresses to a server', + { + serverId: z.number().describe('The server ID'), + interface: z.string().describe('Network interface'), + quantity: z.number().describe('Number of IPv4 addresses to add'), + }, + async ({ serverId, interface: iface, quantity }) => { + try { + const result = await client.post(`/servers/${serverId}/ipv4Qty`, { + interface: iface, + quantity, + }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/servers-power.ts b/src/tools/servers-power.ts new file mode 100644 index 0000000..c650356 --- /dev/null +++ b/src/tools/servers-power.ts @@ -0,0 +1,70 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerServerPowerTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'servers_power_boot', + 'Boot a server', + { + serverId: z.number().describe('The server ID'), + }, + async ({ serverId }) => { + try { + const result = await client.post(`/servers/${serverId}/power/boot`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_power_shutdown', + 'Gracefully shutdown a server', + { + serverId: z.number().describe('The server ID'), + }, + async ({ serverId }) => { + try { + const result = await client.post(`/servers/${serverId}/power/shutdown`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_power_restart', + 'Restart a server', + { + serverId: z.number().describe('The server ID'), + }, + async ({ serverId }) => { + try { + const result = await client.post(`/servers/${serverId}/power/restart`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_power_poweroff', + 'Force power off a server', + { + serverId: z.number().describe('The server ID'), + }, + async ({ serverId }) => { + try { + const result = await client.post(`/servers/${serverId}/power/poweroff`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/servers-traffic.ts b/src/tools/servers-traffic.ts new file mode 100644 index 0000000..892ae74 --- /dev/null +++ b/src/tools/servers-traffic.ts @@ -0,0 +1,74 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerServerTrafficTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'servers_traffic_list_blocks', + 'List traffic blocks for a server', + { + serverId: z.number().describe('The server ID'), + }, + async ({ serverId }) => { + try { + const result = await client.get(`/servers/${serverId}/traffic/blocks`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_traffic_add_block', + 'Add a traffic block to a server', + { + serverId: z.number().describe('The server ID'), + month: z.number().describe('Month number for the traffic block'), + amount: z.number().describe('Traffic amount in GB'), + }, + async ({ serverId, month, amount }) => { + try { + const result = await client.post(`/servers/${serverId}/traffic/blocks`, { month, amount }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_traffic_remove_block', + 'Remove a traffic block from a server', + { + serverId: z.number().describe('The server ID'), + blockId: z.number().describe('The traffic block ID'), + }, + async ({ serverId, blockId }) => { + try { + const result = await client.delete(`/servers/${serverId}/traffic/blocks/${blockId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_traffic_modify', + "Modify a server's primary traffic allowance", + { + serverId: z.number().describe('The server ID'), + traffic: z.string().describe('Traffic allowance (0-999999999)'), + }, + async ({ serverId, traffic }) => { + try { + const result = await client.put(`/servers/${serverId}/modify/traffic`, { traffic }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/servers.ts b/src/tools/servers.ts new file mode 100644 index 0000000..f5de295 --- /dev/null +++ b/src/tools/servers.ts @@ -0,0 +1,444 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerServerTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'servers_list', + 'List all servers with optional filtering', + { + type: z.string().optional().describe('Filter by server type'), + results: z.number().optional().describe('Number of results to return'), + hypervisorId: z.number().optional().describe('Filter by hypervisor ID'), + }, + async ({ type, results, hypervisorId }) => { + try { + const result = await client.get('/servers', { type, results, hypervisorId }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_create', + 'Create a new server', + { + dryRun: z.boolean().optional().describe('Perform a dry run without creating the server'), + packageId: z.number().describe('Package ID for the server'), + userId: z.number().describe('User ID who will own the server'), + hypervisorId: z.number().describe('Hypervisor ID to deploy on'), + ipv4: z.number().optional().describe('Number of IPv4 addresses'), + storage: z.number().optional().describe('Storage in GB'), + traffic: z.number().optional().describe('Traffic allowance'), + memory: z.number().optional().describe('Memory in MB'), + cpuCores: z.number().optional().describe('Number of CPU cores'), + networkSpeedInbound: z.number().optional().describe('Inbound network speed'), + networkSpeedOutbound: z.number().optional().describe('Outbound network speed'), + storageProfile: z.number().optional().describe('Storage profile ID'), + networkProfile: z.number().optional().describe('Network profile ID'), + firewallRulesets: z.array(z.number()).optional().describe('Firewall ruleset IDs'), + hypervisorAssetGroups: z.array(z.number()).optional().describe('Hypervisor asset group IDs'), + additionalStorage1Enable: z.boolean().optional().describe('Enable additional storage 1'), + additionalStorage2Enable: z.boolean().optional().describe('Enable additional storage 2'), + additionalStorage1Profile: z.number().optional().describe('Storage profile for additional storage 1'), + additionalStorage2Profile: z.number().optional().describe('Storage profile for additional storage 2'), + additionalStorage1Capacity: z.number().optional().describe('Capacity for additional storage 1'), + additionalStorage2Capacity: z.number().optional().describe('Capacity for additional storage 2'), + }, + async ({ dryRun, packageId, userId, hypervisorId, ipv4, storage, traffic, memory, cpuCores, networkSpeedInbound, networkSpeedOutbound, storageProfile, networkProfile, firewallRulesets, hypervisorAssetGroups, additionalStorage1Enable, additionalStorage2Enable, additionalStorage1Profile, additionalStorage2Profile, additionalStorage1Capacity, additionalStorage2Capacity }) => { + try { + const body: Record = { + packageId, + userId, + hypervisorId, + }; + if (ipv4 !== undefined) body.ipv4 = ipv4; + if (storage !== undefined) body.storage = storage; + if (traffic !== undefined) body.traffic = traffic; + if (memory !== undefined) body.memory = memory; + if (cpuCores !== undefined) body.cpuCores = cpuCores; + if (networkSpeedInbound !== undefined) body.networkSpeedInbound = networkSpeedInbound; + if (networkSpeedOutbound !== undefined) body.networkSpeedOutbound = networkSpeedOutbound; + if (storageProfile !== undefined) body.storageProfile = storageProfile; + if (networkProfile !== undefined) body.networkProfile = networkProfile; + if (firewallRulesets !== undefined) body.firewallRulesets = firewallRulesets; + if (hypervisorAssetGroups !== undefined) body.hypervisorAssetGroups = hypervisorAssetGroups; + if (additionalStorage1Enable !== undefined) body.additionalStorage1Enable = additionalStorage1Enable; + if (additionalStorage2Enable !== undefined) body.additionalStorage2Enable = additionalStorage2Enable; + if (additionalStorage1Profile !== undefined) body.additionalStorage1Profile = additionalStorage1Profile; + if (additionalStorage2Profile !== undefined) body.additionalStorage2Profile = additionalStorage2Profile; + if (additionalStorage1Capacity !== undefined) body.additionalStorage1Capacity = additionalStorage1Capacity; + if (additionalStorage2Capacity !== undefined) body.additionalStorage2Capacity = additionalStorage2Capacity; + + const result = await client.post('/servers', body, { dryRun }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_get', + 'Retrieve details of a specific server', + { + serverId: z.number().describe('The server ID'), + remoteState: z.boolean().optional().describe('Include remote state information'), + }, + async ({ serverId, remoteState }) => { + try { + const result = await client.get(`/servers/${serverId}`, { remoteState }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_delete', + 'Delete a server', + { + serverId: z.number().describe('The server ID'), + delay: z.number().optional().describe('Delay in seconds before deletion'), + }, + async ({ serverId, delay }) => { + try { + const result = await client.delete(`/servers/${serverId}`, undefined, { delay }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_build', + 'Build/rebuild a server with an OS template', + { + serverId: z.number().describe('The server ID'), + operatingSystemId: z.number().describe('Operating system template ID'), + name: z.string().optional().describe('Server name'), + hostname: z.string().optional().describe('Server hostname'), + sshKeys: z.array(z.number()).optional().describe('SSH key IDs to add'), + vnc: z.boolean().optional().describe('Enable VNC'), + ipv6: z.boolean().optional().describe('Enable IPv6'), + email: z.boolean().optional().describe('Send email notification'), + swap: z.number().optional().describe('Swap size in MB'), + }, + async ({ serverId, operatingSystemId, name, hostname, sshKeys, vnc, ipv6, email, swap }) => { + try { + const body: Record = { operatingSystemId }; + if (name !== undefined) body.name = name; + if (hostname !== undefined) body.hostname = hostname; + if (sshKeys !== undefined) body.sshKeys = sshKeys; + if (vnc !== undefined) body.vnc = vnc; + if (ipv6 !== undefined) body.ipv6 = ipv6; + if (email !== undefined) body.email = email; + if (swap !== undefined) body.swap = swap; + + const result = await client.post(`/servers/${serverId}/build`, body); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_list_templates', + 'List OS templates available for a server', + { + serverId: z.number().describe('The server ID'), + }, + async ({ serverId }) => { + try { + const result = await client.get(`/servers/${serverId}/templates`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_modify_name', + "Change a server's display name", + { + serverId: z.number().describe('The server ID'), + name: z.string().describe('New display name for the server'), + }, + async ({ serverId, name }) => { + try { + const result = await client.put(`/servers/${serverId}/modify/name`, { name }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_modify_cpu', + 'Modify the number of CPU cores for a server', + { + serverId: z.number().describe('The server ID'), + cpuCores: z.number().describe('Number of CPU cores'), + }, + async ({ serverId, cpuCores }) => { + try { + const result = await client.put(`/servers/${serverId}/modify/cpuCores`, { cpuCores }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_modify_memory', + 'Modify the memory allocation for a server in MB', + { + serverId: z.number().describe('The server ID'), + memory: z.number().describe('Memory in MB'), + }, + async ({ serverId, memory }) => { + try { + const result = await client.put(`/servers/${serverId}/modify/memory`, { memory }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_throttle_cpu', + "Throttle a server's CPU", + { + serverId: z.number().describe('The server ID'), + cpuThrottle: z.number().describe('CPU throttle value'), + }, + async ({ serverId, cpuThrottle }) => { + try { + const result = await client.put(`/servers/${serverId}/modify/cpuThrottle`, { cpuThrottle }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_get_traffic', + 'Retrieve traffic statistics for a server', + { + serverId: z.number().describe('The server ID'), + }, + async ({ serverId }) => { + try { + const result = await client.get(`/servers/${serverId}/traffic`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_suspend', + 'Suspend a server', + { + serverId: z.number().describe('The server ID'), + }, + async ({ serverId }) => { + try { + const result = await client.post(`/servers/${serverId}/suspend`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_unsuspend', + 'Unsuspend a server', + { + serverId: z.number().describe('The server ID'), + }, + async ({ serverId }) => { + try { + const result = await client.post(`/servers/${serverId}/unsuspend`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_reset_password', + "Reset a server's root/admin password", + { + serverId: z.number().describe('The server ID'), + user: z.enum(['root', 'Administrator']).describe('User to reset password for'), + sendMail: z.boolean().optional().describe('Send password via email'), + }, + async ({ serverId, user, sendMail }) => { + try { + const body: Record = { user }; + if (sendMail !== undefined) body.sendMail = sendMail; + + const result = await client.post(`/servers/${serverId}/resetPassword`, body); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_enable_vnc', + 'Enable or disable VNC for a server', + { + serverId: z.number().describe('The server ID'), + vnc: z.boolean().describe('Enable or disable VNC'), + }, + async ({ serverId, vnc }) => { + try { + const result = await client.post(`/servers/${serverId}/vnc`, { vnc }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_get_vnc', + 'Retrieve VNC connection details for a server', + { + serverId: z.number().describe('The server ID'), + }, + async ({ serverId }) => { + try { + const result = await client.get(`/servers/${serverId}/vnc`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_set_custom_xml', + 'Set custom XML configuration for a server', + { + serverId: z.number().describe('The server ID'), + xml: z.string().describe('Custom XML configuration'), + }, + async ({ serverId, xml }) => { + try { + const result = await client.post(`/servers/${serverId}/customXML`, { xml }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_change_owner', + 'Change the owner of a server', + { + serverId: z.number().describe('The server ID'), + newOwnerId: z.number().describe('New owner user ID'), + }, + async ({ serverId, newOwnerId }) => { + try { + const result = await client.put(`/servers/${serverId}/owner/${newOwnerId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_change_package', + "Change a server's package with optional resource sync flags", + { + serverId: z.number().describe('The server ID'), + packageId: z.number().describe('New package ID'), + backupPlan: z.boolean().optional().describe('Sync backup plan'), + cpu: z.boolean().optional().describe('Sync CPU'), + memory: z.boolean().optional().describe('Sync memory'), + primaryDiskReadIOPS: z.boolean().optional().describe('Sync primary disk read IOPS'), + primaryDiskReadThroughput: z.boolean().optional().describe('Sync primary disk read throughput'), + primaryDiskSize: z.boolean().optional().describe('Sync primary disk size'), + primaryDiskWriteIOPS: z.boolean().optional().describe('Sync primary disk write IOPS'), + primaryDiskWriteThroughput: z.boolean().optional().describe('Sync primary disk write throughput'), + primaryNetworkInboundSpeed: z.boolean().optional().describe('Sync primary network inbound speed'), + primaryNetworkOutboundSpeed: z.boolean().optional().describe('Sync primary network outbound speed'), + primaryNetworkTraffic: z.boolean().optional().describe('Sync primary network traffic'), + }, + async ({ serverId, packageId, backupPlan, cpu, memory, primaryDiskReadIOPS, primaryDiskReadThroughput, primaryDiskSize, primaryDiskWriteIOPS, primaryDiskWriteThroughput, primaryNetworkInboundSpeed, primaryNetworkOutboundSpeed, primaryNetworkTraffic }) => { + try { + const body: Record = {}; + if (backupPlan !== undefined) body.backupPlan = backupPlan; + if (cpu !== undefined) body.cpu = cpu; + if (memory !== undefined) body.memory = memory; + if (primaryDiskReadIOPS !== undefined) body.primaryDiskReadIOPS = primaryDiskReadIOPS; + if (primaryDiskReadThroughput !== undefined) body.primaryDiskReadThroughput = primaryDiskReadThroughput; + if (primaryDiskSize !== undefined) body.primaryDiskSize = primaryDiskSize; + if (primaryDiskWriteIOPS !== undefined) body.primaryDiskWriteIOPS = primaryDiskWriteIOPS; + if (primaryDiskWriteThroughput !== undefined) body.primaryDiskWriteThroughput = primaryDiskWriteThroughput; + if (primaryNetworkInboundSpeed !== undefined) body.primaryNetworkInboundSpeed = primaryNetworkInboundSpeed; + if (primaryNetworkOutboundSpeed !== undefined) body.primaryNetworkOutboundSpeed = primaryNetworkOutboundSpeed; + if (primaryNetworkTraffic !== undefined) body.primaryNetworkTraffic = primaryNetworkTraffic; + + const result = await client.put(`/servers/${serverId}/package/${packageId}`, Object.keys(body).length > 0 ? body : undefined); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_set_backup_plan', + "Add, remove or modify a server's backup plan", + { + serverId: z.number().describe('The server ID'), + planId: z.number().describe('Backup plan ID (0 to remove)'), + }, + async ({ serverId, planId }) => { + try { + const result = await client.put(`/servers/${serverId}/backups/plan/${planId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'servers_list_by_user', + 'List all servers owned by a specific user', + { + userId: z.number().describe('The user ID'), + }, + async ({ userId }) => { + try { + const result = await client.get(`/servers/user/${userId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/ssh-keys.ts b/src/tools/ssh-keys.ts new file mode 100644 index 0000000..e1c72ba --- /dev/null +++ b/src/tools/ssh-keys.ts @@ -0,0 +1,72 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerSshKeyTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'ssh_keys_create', + 'Add an SSH key to a user account', + { + userId: z.number().describe('The user ID'), + name: z.string().describe('Name for the SSH key'), + publicKey: z.string().describe('The public key content'), + }, + async ({ userId, name, publicKey }) => { + try { + const result = await client.post('/ssh_keys', { userId, name, publicKey }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'ssh_keys_get', + 'Retrieve details of a specific SSH key', + { + keyId: z.number().describe('The SSH key ID'), + }, + async ({ keyId }) => { + try { + const result = await client.get(`/ssh_keys/${keyId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'ssh_keys_delete', + 'Delete an SSH key', + { + keyId: z.number().describe('The SSH key ID'), + }, + async ({ keyId }) => { + try { + const result = await client.delete(`/ssh_keys/${keyId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'ssh_keys_list_by_user', + 'List all SSH keys for a specific user', + { + userId: z.number().describe('The user ID'), + }, + async ({ userId }) => { + try { + const result = await client.get(`/ssh_keys/user/${userId}`); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/tools/users.ts b/src/tools/users.ts new file mode 100644 index 0000000..30fe75c --- /dev/null +++ b/src/tools/users.ts @@ -0,0 +1,162 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; +import { VirtFusionClient } from '../client.js'; +import { formatErrorResponse } from '../errors.js'; + +export function registerUserTools(server: McpServer, client: VirtFusionClient): void { + server.tool( + 'users_create', + 'Create a new user', + { + name: z.string().describe('User display name'), + email: z.string().describe('User email address'), + extRelationId: z.number().optional().describe('External relation ID'), + relStr: z.string().optional().describe('Relation string'), + selfService: z.number().optional().describe('Self-service level (0-3)'), + selfServiceHourlyCredit: z.boolean().optional().describe('Enable hourly credit for self-service'), + selfServiceHourlyGroupProfiles: z.array(z.number()).optional().describe('Hourly group profile IDs'), + selfServiceResourceGroupProfiles: z.array(z.number()).optional().describe('Resource group profile IDs'), + selfServiceHourlyResourcePack: z.number().optional().describe('Hourly resource pack ID'), + sendMail: z.boolean().optional().describe('Send welcome email'), + }, + async ({ name, email, extRelationId, relStr, selfService, selfServiceHourlyCredit, selfServiceHourlyGroupProfiles, selfServiceResourceGroupProfiles, selfServiceHourlyResourcePack, sendMail }) => { + try { + const body: Record = { name, email }; + if (extRelationId !== undefined) body.extRelationId = extRelationId; + if (relStr !== undefined) body.relStr = relStr; + if (selfService !== undefined) body.selfService = selfService; + if (selfServiceHourlyCredit !== undefined) body.selfServiceHourlyCredit = selfServiceHourlyCredit; + if (selfServiceHourlyGroupProfiles !== undefined) body.selfServiceHourlyGroupProfiles = selfServiceHourlyGroupProfiles; + if (selfServiceResourceGroupProfiles !== undefined) body.selfServiceResourceGroupProfiles = selfServiceResourceGroupProfiles; + if (selfServiceHourlyResourcePack !== undefined) body.selfServiceHourlyResourcePack = selfServiceHourlyResourcePack; + if (sendMail !== undefined) body.sendMail = sendMail; + + const result = await client.post('/users', body); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'users_get', + 'Retrieve a user by external relation ID', + { + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + }, + async ({ extRelationId, relStr }) => { + try { + const result = await client.get(`/users/${extRelationId}/byExtRelation`, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'users_modify', + 'Modify a user', + { + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + name: z.string().optional().describe('User display name'), + email: z.string().optional().describe('User email address'), + selfService: z.number().optional().describe('Self-service level (0-3)'), + selfServiceHourlyCredit: z.boolean().optional().describe('Enable hourly credit for self-service'), + selfServiceHourlyGroupProfiles: z.array(z.number()).optional().describe('Hourly group profile IDs'), + selfServiceResourceGroupProfiles: z.array(z.number()).optional().describe('Resource group profile IDs'), + selfServiceHourlyResourcePack: z.number().optional().describe('Hourly resource pack ID'), + enabled: z.boolean().optional().describe('Enable or disable the user'), + }, + async ({ extRelationId, relStr, name, email, selfService, selfServiceHourlyCredit, selfServiceHourlyGroupProfiles, selfServiceResourceGroupProfiles, selfServiceHourlyResourcePack, enabled }) => { + try { + const body: Record = {}; + if (name !== undefined) body.name = name; + if (email !== undefined) body.email = email; + if (selfService !== undefined) body.selfService = selfService; + if (selfServiceHourlyCredit !== undefined) body.selfServiceHourlyCredit = selfServiceHourlyCredit; + if (selfServiceHourlyGroupProfiles !== undefined) body.selfServiceHourlyGroupProfiles = selfServiceHourlyGroupProfiles; + if (selfServiceResourceGroupProfiles !== undefined) body.selfServiceResourceGroupProfiles = selfServiceResourceGroupProfiles; + if (selfServiceHourlyResourcePack !== undefined) body.selfServiceHourlyResourcePack = selfServiceHourlyResourcePack; + if (enabled !== undefined) body.enabled = enabled; + + const result = await client.put(`/users/${extRelationId}/byExtRelation`, Object.keys(body).length > 0 ? body : undefined, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'users_delete', + 'Delete a user', + { + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + }, + async ({ extRelationId, relStr }) => { + try { + const result = await client.delete(`/users/${extRelationId}/byExtRelation`, undefined, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'users_reset_password', + "Reset a user's password", + { + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + }, + async ({ extRelationId, relStr }) => { + try { + const result = await client.post(`/users/${extRelationId}/byExtRelation/resetPassword`, undefined, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'users_generate_auth_tokens', + 'Generate login tokens for a user', + { + extRelationId: z.string().describe('External relation ID'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + }, + async ({ extRelationId, relStr }) => { + try { + const result = await client.post(`/users/${extRelationId}/authenticationTokens`, undefined, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); + + server.tool( + 'users_generate_server_auth_tokens', + 'Generate login tokens for a user scoped to a specific server', + { + extRelationId: z.string().describe('External relation ID'), + serverId: z.number().describe('The server ID to scope the token to'), + relStr: z.boolean().optional().describe('Treat extRelationId as a relation string'), + }, + async ({ extRelationId, serverId, relStr }) => { + try { + const result = await client.post(`/users/${extRelationId}/serverAuthenticationTokens/${serverId}`, undefined, { relStr }); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + return formatErrorResponse(error); + } + } + ); +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..e8cfe75 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,17 @@ +export interface PaginatedResponse { + current_page: number; + data: T[]; + first_page_url: string; + from: number | null; + last_page: number; + last_page_url: string; + links: Array<{ url: string | null; label: string; active: boolean }>; + next_page_url: string | null; + path: string; + per_page: number; + prev_page_url: string | null; + to: number | null; + total: number; +} + +export type QueryParams = Record; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..56ace0d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "Node16", + "moduleResolution": "Node16", + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "scripts"] +}