- Add comprehensive database migrations (001-024) for schema evolution - Enhance API schema with expanded type definitions and resolvers - Add new middleware: audit logging, rate limiting, MFA enforcement, security, tenant auth - Implement new services: AI optimization, billing, blockchain, compliance, marketplace - Add adapter layer for cloud integrations (Cloudflare, Kubernetes, Proxmox, storage) - Update Crossplane provider with enhanced VM management capabilities - Add comprehensive test suite for API endpoints and services - Update frontend components with improved GraphQL subscriptions and real-time updates - Enhance security configurations and headers (CSP, CORS, etc.) - Update documentation and configuration files - Add new CI/CD workflows and validation scripts - Implement design system improvements and UI enhancements
169 lines
4.3 KiB
TypeScript
169 lines
4.3 KiB
TypeScript
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<VM[]>;
|
|
getVM(name: string): Promise<VM>;
|
|
createVM(spec: VMSpec): Promise<VM>;
|
|
updateVM(name: string, spec: Partial<VMSpec>): Promise<VM>;
|
|
deleteVM(name: string): Promise<void>;
|
|
}
|
|
|
|
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<VM[]> {
|
|
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<VM> {
|
|
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<VM> {
|
|
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<VMSpec>): Promise<VM> {
|
|
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<void> {
|
|
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);
|
|
}
|
|
|