import axios, { AxiosInstance, AxiosError } from 'axios'; const crossplaneAPI = process.env.NEXT_PUBLIC_CROSSPLANE_API || 'http://localhost:8080'; // Error types export class CrossplaneError extends Error { constructor( message: string, public statusCode?: number, public code?: string ) { super(message); this.name = 'CrossplaneError'; Object.setPrototypeOf(this, CrossplaneError.prototype); } } function handleAxiosError(error: unknown): never { if (axios.isAxiosError(error)) { const axiosError = error as AxiosError<{ message?: string }>; const statusCode = axiosError.response?.status; const message = (axiosError.response?.data as { message?: string })?.message || axiosError.message || 'An error occurred'; throw new CrossplaneError(message, statusCode, axiosError.code); } if (error instanceof Error) { throw new CrossplaneError(error.message); } throw new CrossplaneError('An unknown error occurred'); } export interface CrossplaneClient { getVMs(): Promise; getVM(name: string): Promise; createVM(spec: VMSpec): Promise; updateVM(name: string, spec: Partial): Promise; deleteVM(name: string): Promise; } export interface VM { metadata: { name: string; namespace: string; creationTimestamp: string; }; spec: VMSpec; status: { vmId?: number; state?: string; ipAddress?: string; conditions?: Array<{ type: string; status: string; reason?: string; message?: string; }>; }; } export interface VMSpec { forProvider: { node: string; name: string; cpu: number; memory: string; disk: string; storage: string; network: string; image: string; site: string; userData?: string; sshKeys?: string[]; }; providerConfigRef: { name: string; }; } class CrossplaneClientImpl implements CrossplaneClient { private client: AxiosInstance; constructor(accessToken?: string) { this.client = axios.create({ baseURL: crossplaneAPI, headers: accessToken ? { Authorization: `Bearer ${accessToken}`, } : {}, }); } async getVMs(): Promise { try { const apiGroup = process.env.NEXT_PUBLIC_CROSSPLANE_API_GROUP || 'proxmox.sankofa.nexus' const response = await this.client.get(`/apis/${apiGroup}/v1alpha1/proxmoxvms`); return response.data.items || []; } catch (error) { handleAxiosError(error); } } async getVM(name: string): Promise { try { const apiGroup = process.env.NEXT_PUBLIC_CROSSPLANE_API_GROUP || 'proxmox.sankofa.nexus' const response = await this.client.get( `/apis/${apiGroup}/v1alpha1/proxmoxvms/${name}` ); return response.data; } catch (error) { handleAxiosError(error); } } async createVM(spec: VMSpec): Promise { try { const apiGroup = process.env.NEXT_PUBLIC_CROSSPLANE_API_GROUP || 'proxmox.sankofa.nexus' const response = await this.client.post(`/apis/${apiGroup}/v1alpha1/proxmoxvms`, { apiVersion: `${apiGroup}/v1alpha1`, kind: 'ProxmoxVM', metadata: { name: spec.forProvider.name, namespace: 'default', }, spec, }); return response.data; } catch (error) { handleAxiosError(error); } } async updateVM(name: string, spec: Partial): Promise { try { const apiGroup = process.env.NEXT_PUBLIC_CROSSPLANE_API_GROUP || 'proxmox.sankofa.nexus' const response = await this.client.patch( `/apis/${apiGroup}/v1alpha1/proxmoxvms/${name}`, { spec, }, { headers: { 'Content-Type': 'application/merge-patch+json', }, } ); return response.data; } catch (error) { handleAxiosError(error); } } async deleteVM(name: string): Promise { try { const apiGroup = process.env.NEXT_PUBLIC_CROSSPLANE_API_GROUP || 'proxmox.sankofa.nexus' await this.client.delete(`/apis/${apiGroup}/v1alpha1/proxmoxvms/${name}`); } catch (error) { handleAxiosError(error); } } } export function createCrossplaneClient(accessToken?: string): CrossplaneClient { return new CrossplaneClientImpl(accessToken); }