fix(proxmox): remove dummy data, fix add-remote, fix updater
- Replace hardcoded dummy data in VMs, Containers, Storage, Backup, and
Firewall pages with live API calls; show empty-state UI when no
clusters are configured
- Add list_proxmox_containers backend command (LXC via cluster/resources)
and register it in the Tauri handler and frontend proxmoxClient.ts
- Fix add_proxmox_cluster to store credentials without requiring a live
Proxmox connection; persist username in DB (migration 034); update
list/get queries to read username column from new schema
- Replace alert() in RemotesPage with toast.error() + rethrow so errors
surface correctly in Tauri WebView
- Replace tauri-plugin-updater with direct Gitea HTTP API call for
update checks; use tauri-plugin-opener for browser launch; Updater UI
now shows current/latest version and release notes
- Add gogs.tftsr.com to CSP connect-src
- Fix all 74 pre-existing ESLint no-explicit-any warnings in
proxmoxClient.ts; remove stale eslint-disable directive in ACLPage.tsx
- All checks pass: cargo fmt, clippy -D warnings, 411 Rust tests,
tsc --noEmit, eslint --max-warnings 0, 386 frontend tests
2026-06-13 22:33:23 +00:00
|
|
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
2026-06-11 14:38:36 +00:00
|
|
|
// Proxmox client module
|
|
|
|
|
// Provides TypeScript client wrapper for Proxmox API
|
|
|
|
|
|
|
|
|
|
import { invoke } from "@tauri-apps/api/core";
|
|
|
|
|
import { ClusterInfo, ClusterType } from "./domain";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add a Proxmox cluster
|
|
|
|
|
* @param id - Unique cluster identifier
|
|
|
|
|
* @param name - Display name for the cluster
|
|
|
|
|
* @param clusterType - Type of cluster (ve or pbs)
|
|
|
|
|
* @param connection - Connection details (url and port)
|
|
|
|
|
* @param username - Root username for authentication
|
|
|
|
|
* @param password - Root password for authentication
|
|
|
|
|
*/
|
|
|
|
|
export async function addProxmoxCluster(
|
|
|
|
|
id: string,
|
|
|
|
|
name: string,
|
|
|
|
|
clusterType: ClusterType,
|
|
|
|
|
connection: { url: string; port: number },
|
|
|
|
|
username: string,
|
|
|
|
|
password: string
|
|
|
|
|
): Promise<ClusterInfo> {
|
|
|
|
|
return await invoke<ClusterInfo>("add_proxmox_cluster", {
|
|
|
|
|
id,
|
|
|
|
|
name,
|
2026-06-19 22:37:25 +00:00
|
|
|
clusterType,
|
2026-06-11 14:38:36 +00:00
|
|
|
connection,
|
|
|
|
|
username,
|
|
|
|
|
password,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Remove a Proxmox cluster
|
|
|
|
|
* @param id - Cluster identifier to remove
|
|
|
|
|
*/
|
|
|
|
|
export async function removeProxmoxCluster(id: string): Promise<void> {
|
|
|
|
|
await invoke("remove_proxmox_cluster", { id });
|
|
|
|
|
}
|
|
|
|
|
|
fix: address PR review findings — race condition, real ping, atomic edit, listener cleanup
Race condition in get_proxmox_client_for_cluster: two concurrent callers
for an uncached cluster could both authenticate and insert, with the second
overwriting the first. Re-check under write lock before inserting so the
later caller returns the already-stored client instead of overwriting it.
handleConnectRemote used getProxmoxCluster (a DB-only lookup) to set status
'connected', which passed even when the Proxmox API was unreachable. Replace
with pingProxmoxCluster, a new command that authenticates and calls
GET /api2/json/version, providing a real end-to-end connectivity test.
handleEditRemote used remove-then-add, leaving a gap where the record was
absent and silently lost if addProxmoxCluster failed. Replace with
updateProxmoxCluster, a new command that issues a single SQL UPDATE (plus
in-memory pool eviction) so the record is never transiently missing.
ActionsMenu useEffect added the mousedown listener only when open=true but
the dependency array contained open, causing ambiguity about cleanup timing.
Attach the listener unconditionally on mount (empty dep array) so there is
always exactly one add and one remove with no conditional branches.
New Rust tests cover update_proxmox_cluster not-found logic and ping error
message format (420 Rust + 386 frontend, zero failures).
2026-06-20 03:26:33 +00:00
|
|
|
/**
|
|
|
|
|
* Update an existing Proxmox cluster's metadata and credentials atomically.
|
|
|
|
|
* Uses a single SQL UPDATE so there is no window where the record is missing.
|
|
|
|
|
*/
|
|
|
|
|
export async function updateProxmoxCluster(
|
|
|
|
|
id: string,
|
|
|
|
|
name: string,
|
|
|
|
|
clusterType: ClusterType,
|
|
|
|
|
connection: { url: string; port: number },
|
|
|
|
|
username: string,
|
|
|
|
|
password: string
|
|
|
|
|
): Promise<ClusterInfo> {
|
|
|
|
|
return await invoke<ClusterInfo>("update_proxmox_cluster", {
|
|
|
|
|
id,
|
|
|
|
|
name,
|
|
|
|
|
clusterType,
|
|
|
|
|
connection,
|
|
|
|
|
username,
|
|
|
|
|
password,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Ping a Proxmox cluster — authenticates and calls the version endpoint to verify
|
|
|
|
|
* the API is reachable and credentials are valid.
|
|
|
|
|
*/
|
|
|
|
|
export async function pingProxmoxCluster(clusterId: string): Promise<any> {
|
|
|
|
|
return await invoke<any>("ping_proxmox_cluster", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-11 14:38:36 +00:00
|
|
|
/**
|
|
|
|
|
* List all Proxmox clusters
|
|
|
|
|
*/
|
|
|
|
|
export async function listProxmoxClusters(): Promise<ClusterInfo[]> {
|
|
|
|
|
return await invoke<ClusterInfo[]>("list_proxmox_clusters");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get a specific Proxmox cluster
|
|
|
|
|
* @param id - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function getProxmoxCluster(id: string): Promise<ClusterInfo | null> {
|
|
|
|
|
return await invoke<ClusterInfo | null>("get_proxmox_cluster", { id });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List all Proxmox VMs
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listProxmoxVms(clusterId: string): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_proxmox_vms", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
fix(proxmox): remove dummy data, fix add-remote, fix updater
- Replace hardcoded dummy data in VMs, Containers, Storage, Backup, and
Firewall pages with live API calls; show empty-state UI when no
clusters are configured
- Add list_proxmox_containers backend command (LXC via cluster/resources)
and register it in the Tauri handler and frontend proxmoxClient.ts
- Fix add_proxmox_cluster to store credentials without requiring a live
Proxmox connection; persist username in DB (migration 034); update
list/get queries to read username column from new schema
- Replace alert() in RemotesPage with toast.error() + rethrow so errors
surface correctly in Tauri WebView
- Replace tauri-plugin-updater with direct Gitea HTTP API call for
update checks; use tauri-plugin-opener for browser launch; Updater UI
now shows current/latest version and release notes
- Add gogs.tftsr.com to CSP connect-src
- Fix all 74 pre-existing ESLint no-explicit-any warnings in
proxmoxClient.ts; remove stale eslint-disable directive in ACLPage.tsx
- All checks pass: cargo fmt, clippy -D warnings, 411 Rust tests,
tsc --noEmit, eslint --max-warnings 0, 386 frontend tests
2026-06-13 22:33:23 +00:00
|
|
|
/**
|
|
|
|
|
* List all Proxmox LXC containers
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listProxmoxContainers(clusterId: string): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_proxmox_containers", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-11 14:38:36 +00:00
|
|
|
/**
|
|
|
|
|
* Get Proxmox VM details
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
* @param vmId - VM identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function getProxmoxVm(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string,
|
|
|
|
|
vmId: number
|
|
|
|
|
): Promise<any> {
|
|
|
|
|
return await invoke<any>("get_proxmox_vm", { clusterId, nodeId, vmId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Start a Proxmox VM
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
* @param vmId - VM identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function startProxmoxVm(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string,
|
|
|
|
|
vmId: number
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("start_proxmox_vm", { clusterId, nodeId, vmId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Stop a Proxmox VM
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
* @param vmId - VM identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function stopProxmoxVm(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string,
|
|
|
|
|
vmId: number
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("stop_proxmox_vm", { clusterId, nodeId, vmId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Reboot a Proxmox VM
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
* @param vmId - VM identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function rebootProxmoxVm(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string,
|
|
|
|
|
vmId: number
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("reboot_proxmox_vm", { clusterId, nodeId, vmId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Shutdown a Proxmox VM
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
* @param vmId - VM identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function shutdownProxmoxVm(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string,
|
|
|
|
|
vmId: number
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("shutdown_proxmox_vm", { clusterId, nodeId, vmId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List Proxmox Backup Jobs
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listProxmoxBackupJobs(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string
|
|
|
|
|
): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_proxmox_backup_jobs", { clusterId, nodeId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List Proxmox Datastores
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listProxmoxDatastores(
|
|
|
|
|
clusterId: string
|
|
|
|
|
): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_proxmox_datastores", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Trigger Proxmox Backup Job
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
* @param jobId - Backup job identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function triggerProxmoxBackupJob(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string,
|
|
|
|
|
jobId: number
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("trigger_proxmox_backup_job", { clusterId, nodeId, jobId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List Ceph Pools
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listCephPools(clusterId: string): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_ceph_pools", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List Ceph OSDs
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listCephOsd(clusterId: string): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_ceph_osd", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get Ceph Health
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function getCephHealth(clusterId: string): Promise<any> {
|
|
|
|
|
return await invoke<any>("get_ceph_health", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── User Management (LDAP/AD/OpenID) ─────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List authentication realms
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listAuthRealms(clusterId: string): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_auth_realms", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add LDAP authentication realm
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param realm - Realm configuration
|
|
|
|
|
*/
|
|
|
|
|
export async function addLdapRealm(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
realm: any
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("add_ldap_realm", { clusterId, realm });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add Active Directory authentication realm
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param realm - Realm configuration
|
|
|
|
|
*/
|
|
|
|
|
export async function addAdRealm(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
realm: any
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("add_ad_realm", { clusterId, realm });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add OpenID Connect authentication realm
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param realm - Realm configuration
|
|
|
|
|
*/
|
|
|
|
|
export async function addOpenidRealm(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
realm: any
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("add_openid_realm", { clusterId, realm });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── ACME/Let's Encrypt ───────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List ACME accounts
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listAcmeAccounts(clusterId: string): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_acme_accounts", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Register ACME account
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param account - Account configuration
|
|
|
|
|
*/
|
|
|
|
|
export async function registerAcmeAccount(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
account: any
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("register_acme_account", { clusterId, account });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get ACME challenges
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function getAcmeChallenges(clusterId: string): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("get_acme_challenges", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── APT Repository Management ────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List APT updates
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listAptUpdates(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string
|
|
|
|
|
): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_apt_updates", { clusterId, nodeId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Update APT repositories
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function updateAptRepos(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("update_apt_repos", { clusterId, nodeId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List APT repositories
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listAptRepositories(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string
|
|
|
|
|
): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_apt_repositories", { clusterId, nodeId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Remote Shell ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get shell ticket for remote terminal access
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function getShellTicket(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string
|
|
|
|
|
): Promise<any> {
|
|
|
|
|
return await invoke<any>("get_shell_ticket", { clusterId, nodeId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Dashboard Views ──────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List dashboard views
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listViews(clusterId: string): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_views", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add a dashboard view
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param view - View configuration
|
|
|
|
|
*/
|
|
|
|
|
export async function addView(clusterId: string, view: any): Promise<void> {
|
|
|
|
|
await invoke("add_view", { clusterId, view });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Update a dashboard view
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param viewId - View identifier
|
|
|
|
|
* @param view - View configuration
|
|
|
|
|
*/
|
|
|
|
|
export async function updateView(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
viewId: string,
|
|
|
|
|
view: any
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("update_view", { clusterId, viewId, view });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Delete a dashboard view
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param viewId - View identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function deleteView(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
viewId: string
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("delete_view", { clusterId, viewId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Certificate Management ───────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List certificates
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listCertificates(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string
|
|
|
|
|
): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_certificates", { clusterId, nodeId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Upload a certificate
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
* @param cert - Certificate data
|
|
|
|
|
*/
|
|
|
|
|
export async function uploadCertificate(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string,
|
|
|
|
|
cert: any
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("upload_certificate", { clusterId, nodeId, cert });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get certificate details
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
* @param certId - Certificate identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function getCertificate(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string,
|
|
|
|
|
certId: string
|
|
|
|
|
): Promise<any> {
|
|
|
|
|
return await invoke<any>("get_certificate", { clusterId, nodeId, certId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Firewall Management ──────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List firewall rules
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listFirewallRules(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string
|
|
|
|
|
): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_firewall_rules", { clusterId, nodeId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add a firewall rule
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
* @param rule - Rule configuration
|
|
|
|
|
*/
|
|
|
|
|
export async function addFirewallRule(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string,
|
|
|
|
|
rule: any
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("add_firewall_rule", { clusterId, nodeId, rule });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Delete a firewall rule
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
* @param ruleId - Rule identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function deleteFirewallRule(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string,
|
|
|
|
|
ruleId: number
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("delete_firewall_rule", { clusterId, nodeId, ruleId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── SDN Management ───────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List SDN controllers
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listSdnControllers(clusterId: string): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_sdn_controllers", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List SDN virtual networks
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listSdnVnets(clusterId: string): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_sdn_vnets", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List SDN zones
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listSdnZones(clusterId: string): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_sdn_zones", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Ceph Cluster Management ──────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List Ceph clusters
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listCephClusters(clusterId: string): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_ceph_clusters", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get Ceph cluster status
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function getCephClusterStatus(clusterId: string): Promise<any> {
|
|
|
|
|
return await invoke<any>("get_ceph_cluster_status", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Remote Migration ─────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Migrate a VM
|
|
|
|
|
* @param clusterId - Source cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
* @param vmId - VM identifier
|
|
|
|
|
* @param targetClusterId - Target cluster identifier
|
|
|
|
|
* @param online - Whether to migrate online
|
|
|
|
|
*/
|
|
|
|
|
export async function migrateVm(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string,
|
|
|
|
|
vmId: number,
|
|
|
|
|
targetClusterId: string,
|
|
|
|
|
online: boolean
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("migrate_vm", {
|
|
|
|
|
clusterId,
|
|
|
|
|
nodeId,
|
|
|
|
|
vmId,
|
|
|
|
|
targetClusterId,
|
|
|
|
|
online,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List migration status
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listMigrationStatus(clusterId: string): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_migration_status", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── System Updates ───────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List updates
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listUpdates(clusterId: string): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_updates", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Refresh updates
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function refreshUpdates(clusterId: string): Promise<void> {
|
|
|
|
|
await invoke("refresh_updates", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Install updates
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param updates - Updates to install
|
|
|
|
|
*/
|
|
|
|
|
export async function installUpdates(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
updates: any[]
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("install_updates", { clusterId, updates });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Task Management ──────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List tasks
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listTasks(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string
|
|
|
|
|
): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_tasks", { clusterId, nodeId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get task status
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
* @param taskId - Task identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function getTaskStatus(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string,
|
|
|
|
|
taskId: string
|
|
|
|
|
): Promise<any> {
|
|
|
|
|
return await invoke<any>("get_task_status", { clusterId, nodeId, taskId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Stop a task
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
* @param taskId - Task identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function stopTask(
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string,
|
|
|
|
|
taskId: string
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await invoke("stop_task", { clusterId, nodeId, taskId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Metric Collection ────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get metrics summary
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function getMetricsSummary(clusterId: string): Promise<any> {
|
|
|
|
|
return await invoke<any>("get_metrics_summary", { clusterId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List metric collections
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export async function listMetricCollections(
|
|
|
|
|
clusterId: string
|
|
|
|
|
): Promise<any[]> {
|
|
|
|
|
return await invoke<any[]>("list_metric_collections", { clusterId });
|
|
|
|
|
}
|
2026-06-13 02:48:56 +00:00
|
|
|
|
|
|
|
|
// ─── HA (High Availability) ───────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export interface HaGroup {
|
|
|
|
|
id: string;
|
|
|
|
|
nodes: string;
|
|
|
|
|
comment?: string;
|
|
|
|
|
restricted?: boolean;
|
|
|
|
|
noQuorumPolicy?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface HaResource {
|
|
|
|
|
sid: string;
|
|
|
|
|
group?: string;
|
|
|
|
|
state: string;
|
|
|
|
|
maxRestart?: number;
|
|
|
|
|
maxRelocate?: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List HA groups
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export const listHaGroups = async (clusterId: string): Promise<HaGroup[]> =>
|
|
|
|
|
invoke<HaGroup[]>("list_ha_groups", { clusterId });
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create an HA group
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param config - HA group configuration
|
|
|
|
|
*/
|
|
|
|
|
export const createHaGroup = async (
|
|
|
|
|
clusterId: string,
|
|
|
|
|
config: Partial<HaGroup>
|
|
|
|
|
): Promise<void> => invoke<void>("create_ha_group", { clusterId, config });
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Update an HA group
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param id - HA group identifier
|
|
|
|
|
* @param config - HA group configuration
|
|
|
|
|
*/
|
|
|
|
|
export const updateHaGroup = async (
|
|
|
|
|
clusterId: string,
|
|
|
|
|
id: string,
|
|
|
|
|
config: Partial<HaGroup>
|
|
|
|
|
): Promise<void> => invoke<void>("update_ha_group", { clusterId, id, config });
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Delete an HA group
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param id - HA group identifier
|
|
|
|
|
*/
|
|
|
|
|
export const deleteHaGroup = async (
|
|
|
|
|
clusterId: string,
|
|
|
|
|
id: string
|
|
|
|
|
): Promise<void> => invoke<void>("delete_ha_group", { clusterId, id });
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List HA resources
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export const listHaResources = async (
|
|
|
|
|
clusterId: string
|
|
|
|
|
): Promise<HaResource[]> =>
|
|
|
|
|
invoke<HaResource[]>("list_ha_resources", { clusterId });
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Enable an HA resource
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param id - HA resource identifier
|
|
|
|
|
*/
|
|
|
|
|
export const enableHaResource = async (
|
|
|
|
|
clusterId: string,
|
|
|
|
|
id: string
|
|
|
|
|
): Promise<void> => invoke<void>("enable_ha_resource", { clusterId, id });
|
|
|
|
|
|
|
|
|
|
// ─── ACL / User Management ────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export interface AclEntry {
|
|
|
|
|
path: string;
|
|
|
|
|
type: "user" | "group" | "token";
|
|
|
|
|
ugid: string;
|
|
|
|
|
roleid: string;
|
|
|
|
|
propagate?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ProxmoxUser {
|
|
|
|
|
userid: string;
|
|
|
|
|
comment?: string;
|
|
|
|
|
email?: string;
|
|
|
|
|
enabled: boolean;
|
|
|
|
|
expire?: number;
|
|
|
|
|
firstname?: string;
|
|
|
|
|
lastname?: string;
|
|
|
|
|
groups?: string[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface AuthRealm {
|
|
|
|
|
realm: string;
|
|
|
|
|
type: string;
|
|
|
|
|
comment?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List ACL entries
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export const listAcls = async (clusterId: string): Promise<AclEntry[]> =>
|
|
|
|
|
invoke<AclEntry[]>("list_acls", { clusterId });
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List users
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export const listUsers = async (clusterId: string): Promise<ProxmoxUser[]> =>
|
|
|
|
|
invoke<ProxmoxUser[]>("list_users", { clusterId });
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List authentication realms (typed)
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export const listRealms = async (clusterId: string): Promise<AuthRealm[]> =>
|
|
|
|
|
invoke<AuthRealm[]>("list_realms", { clusterId });
|
|
|
|
|
|
|
|
|
|
// ─── Cluster Notes ────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get cluster notes
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export const getClusterNotes = async (clusterId: string): Promise<string> =>
|
|
|
|
|
invoke<string>("get_cluster_notes", { clusterId });
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Update cluster notes
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param notes - Notes content
|
|
|
|
|
*/
|
|
|
|
|
export const updateClusterNotes = async (
|
|
|
|
|
clusterId: string,
|
|
|
|
|
notes: string
|
|
|
|
|
): Promise<void> => invoke<void>("update_cluster_notes", { clusterId, notes });
|
|
|
|
|
|
|
|
|
|
// ─── Resource Search ──────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export interface SearchResult {
|
|
|
|
|
id: string;
|
|
|
|
|
type: "vm" | "container" | "node" | "storage" | "pool";
|
|
|
|
|
name: string;
|
|
|
|
|
node?: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Search Proxmox resources
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param query - Search query string
|
|
|
|
|
*/
|
|
|
|
|
export const searchResources = async (
|
|
|
|
|
clusterId: string,
|
|
|
|
|
query: string
|
|
|
|
|
): Promise<SearchResult[]> =>
|
|
|
|
|
invoke<SearchResult[]>("search_proxmox_resources", { clusterId, query });
|
|
|
|
|
|
|
|
|
|
// ─── Node Status ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export interface NodeStatus {
|
|
|
|
|
uptime: number;
|
|
|
|
|
memory: { used: number; total: number };
|
|
|
|
|
cpu: number;
|
|
|
|
|
swap: { used: number; total: number };
|
|
|
|
|
disk: { used: number; total: number };
|
|
|
|
|
loadAvg: number[];
|
|
|
|
|
version: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get node status
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
*/
|
|
|
|
|
export const getNodeStatus = async (
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string
|
|
|
|
|
): Promise<NodeStatus> =>
|
|
|
|
|
invoke<NodeStatus>("get_node_status", { clusterId, nodeId });
|
|
|
|
|
|
|
|
|
|
// ─── APT (typed) ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export interface AptPackage {
|
|
|
|
|
package: string;
|
|
|
|
|
version: string;
|
|
|
|
|
newVersion?: string;
|
|
|
|
|
priority: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface AptRepository {
|
|
|
|
|
types: string[];
|
|
|
|
|
uris: string[];
|
|
|
|
|
suites: string[];
|
|
|
|
|
components: string[];
|
|
|
|
|
enabled: boolean;
|
|
|
|
|
comment?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Syslog ───────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export interface SyslogEntry {
|
|
|
|
|
n: number;
|
|
|
|
|
t: string;
|
|
|
|
|
msg: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get node syslog
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
* @param limit - Maximum number of entries (default 500)
|
|
|
|
|
*/
|
|
|
|
|
export const getSyslog = async (
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string,
|
|
|
|
|
limit?: number
|
|
|
|
|
): Promise<SyslogEntry[]> =>
|
|
|
|
|
invoke<SyslogEntry[]>("get_syslog", {
|
|
|
|
|
clusterId,
|
|
|
|
|
nodeId,
|
|
|
|
|
limit: limit ?? 500,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ─── Network Interfaces ───────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export interface NetworkInterface {
|
|
|
|
|
iface: string;
|
|
|
|
|
type: string;
|
|
|
|
|
address?: string;
|
|
|
|
|
netmask?: string;
|
|
|
|
|
gateway?: string;
|
|
|
|
|
active: boolean;
|
|
|
|
|
autostart: boolean;
|
|
|
|
|
comments?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List network interfaces on a node
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param nodeId - Node identifier
|
|
|
|
|
*/
|
|
|
|
|
export const listNetworkInterfaces = async (
|
|
|
|
|
clusterId: string,
|
|
|
|
|
nodeId: string
|
|
|
|
|
): Promise<NetworkInterface[]> =>
|
|
|
|
|
invoke<NetworkInterface[]>("list_network_interfaces", { clusterId, nodeId });
|
|
|
|
|
|
|
|
|
|
// ─── Cluster Views (typed) ────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export interface ClusterView {
|
2026-06-13 02:58:31 +00:00
|
|
|
view_id: string;
|
2026-06-13 02:48:56 +00:00
|
|
|
name: string;
|
2026-06-13 02:58:31 +00:00
|
|
|
description?: string;
|
|
|
|
|
layout?: string;
|
|
|
|
|
enabled?: boolean;
|
2026-06-13 02:48:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List cluster views
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export const listClusterViews = async (
|
|
|
|
|
clusterId: string
|
|
|
|
|
): Promise<ClusterView[]> =>
|
|
|
|
|
invoke<ClusterView[]>("list_cluster_views", { clusterId });
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a cluster view
|
|
|
|
|
* @param clusterId - Cluster identifier
|
2026-06-13 02:58:31 +00:00
|
|
|
* @param viewId - View identifier
|
|
|
|
|
* @param name - View display name
|
2026-06-13 02:48:56 +00:00
|
|
|
*/
|
|
|
|
|
export const createClusterView = async (
|
|
|
|
|
clusterId: string,
|
2026-06-13 02:58:31 +00:00
|
|
|
viewId: string,
|
|
|
|
|
name: string
|
2026-06-13 02:48:56 +00:00
|
|
|
): Promise<void> =>
|
2026-06-13 02:58:31 +00:00
|
|
|
invoke<void>("create_cluster_view", { clusterId, viewId, name });
|
2026-06-13 02:48:56 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Delete a cluster view
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param viewId - View identifier
|
|
|
|
|
*/
|
|
|
|
|
export const deleteClusterView = async (
|
|
|
|
|
clusterId: string,
|
|
|
|
|
viewId: string
|
|
|
|
|
): Promise<void> => invoke<void>("delete_cluster_view", { clusterId, viewId });
|
|
|
|
|
|
|
|
|
|
// ─── Subscription ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export interface SubscriptionStatus {
|
|
|
|
|
status: "active" | "expired" | "none";
|
|
|
|
|
productname?: string;
|
|
|
|
|
regdate?: string;
|
|
|
|
|
nextduedate?: string;
|
|
|
|
|
key?: string;
|
|
|
|
|
serverid?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get subscription status
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
*/
|
|
|
|
|
export const getSubscriptionStatus = async (
|
|
|
|
|
clusterId: string
|
|
|
|
|
): Promise<SubscriptionStatus> =>
|
|
|
|
|
invoke<SubscriptionStatus>("get_subscription_status", { clusterId });
|
|
|
|
|
|
|
|
|
|
// ─── Cluster Task Log ─────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export interface ClusterTask {
|
|
|
|
|
upid: string;
|
|
|
|
|
node: string;
|
|
|
|
|
pid: number;
|
|
|
|
|
starttime: number;
|
|
|
|
|
type: string;
|
|
|
|
|
user: string;
|
|
|
|
|
status?: string;
|
|
|
|
|
exitstatus?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List cluster-level tasks
|
|
|
|
|
* @param clusterId - Cluster identifier
|
|
|
|
|
* @param limit - Maximum number of tasks to return (default 50)
|
|
|
|
|
*/
|
|
|
|
|
export const listClusterTasks = async (
|
|
|
|
|
clusterId: string,
|
|
|
|
|
limit?: number
|
|
|
|
|
): Promise<ClusterTask[]> =>
|
|
|
|
|
invoke<ClusterTask[]>("list_cluster_tasks", {
|
|
|
|
|
clusterId,
|
|
|
|
|
limit: limit ?? 50,
|
|
|
|
|
});
|