import { errors, Notification, Resource } from "@kumori/aurora-interfaces"; import { eventHelper } from "../backend-handler"; import { initializeGlobalWebSocketClient, getWebSocketStatus, makeGlobalWebSocketRequest, } from "../websocket-manager"; const createResourceBody = ( tenant: string, resource: Resource, kind: string, data: any ) => ({ tenant, resource: resource.name, kind, spec: { kind, data, labels: resource.certName || resource.certKeyName ? { certNameWUI : resource.certName, certKeyWUI: resource.certKeyName } : {} }, }); export const createDomain = async ( tenant: string, domain: Resource, security: string ) => { try { await initializeGlobalWebSocketClient(security); const status = getWebSocketStatus(); const domainBody = createResourceBody(tenant, domain, "domain", { domain: domain.value, }); const response = await makeGlobalWebSocketRequest( "resource:create_resource", domainBody, 30000, "CREATE", domain.name ); // eventHelper.resource.publish.created(domain); const resourceNotification: Notification = { type: "success", subtype: errors.resource.created.subtype, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: domain.name, tenant: tenant } }; eventHelper.notification.publish.creation(resourceNotification); return response; } catch (error) { console.error("Error creating domain:", { error, tenant, domain, webSocketStatus: getWebSocketStatus(), }); let contentMessage = "Unknown error"; let errorContent = "Unknown error"; if ( typeof error === "object" && error !== null && "error" in error && typeof (error as any).error === "object" && (error as any).error !== null ) { if ("code" in (error as any).error) { contentMessage = (error as any).error.code; } if ("content" in (error as any).error) { errorContent = (error as any).error.content; } } const resourceErrorNotification: Notification = { type: "error", subtype: errors.resource.creationError.subtype, info_content: { code: contentMessage, message: errorContent, }, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: domain.name, tenant: tenant }, userError: true, }; eventHelper.notification.publish.creation(resourceErrorNotification); throw error; } }; export const createPort = async ( tenant: string, port: Resource, security: string ) => { try { await initializeGlobalWebSocketClient(security); const status = getWebSocketStatus(); const portBody = createResourceBody(tenant, port, "port", { port: Number(port.value), }); const response = await makeGlobalWebSocketRequest( "resource:create_resource", portBody, 30000, "CREATE", port.name ); eventHelper.resource.publish.created(port); const resourceNotification: Notification = { type: "success", subtype: errors.resource.created.subtype, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: port.name, tenant: tenant } }; eventHelper.notification.publish.creation(resourceNotification); return response; } catch (error) { console.error("Error creating port:", { error, tenant, port, webSocketStatus: getWebSocketStatus(), }); let contentMessage = "Unknown error"; let errorContent = "Unknown error"; if ( typeof error === "object" && error !== null && "error" in error && typeof (error as any).error === "object" && (error as any).error !== null ) { if ("code" in (error as any).error) { contentMessage = (error as any).error.code; } if ("content" in (error as any).error) { errorContent = (error as any).error.content; } } const resourceErrorNotification: Notification = { type: "error", subtype: errors.resource.creationError.subtype, info_content: { code: contentMessage, message: errorContent, }, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: port.name, tenant: tenant }, userError: true, }; eventHelper.notification.publish.creation(resourceErrorNotification); throw error; } }; export const createCA = async ( tenant: string, ca: Resource, security: string ) => { try { await initializeGlobalWebSocketClient(security); const status = getWebSocketStatus(); const caBody = createResourceBody(tenant, ca, "ca", { ca: ca.value, }); const response = await makeGlobalWebSocketRequest( "resource:create_resource", caBody, 30000, "CREATE", ca.name ); eventHelper.resource.publish.created(ca); const resourceNotification: Notification = { type: "success", subtype: errors.resource.created.subtype, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: ca.name, tenant: tenant } }; eventHelper.notification.publish.creation(resourceNotification); return response; } catch (error) { console.error("Error creating CA:", { error, tenant, ca, webSocketStatus: getWebSocketStatus(), }); let contentMessage = "Unknown error"; let errorContent = "Unknown error"; if ( typeof error === "object" && error !== null && "error" in error && typeof (error as any).error === "object" && (error as any).error !== null ) { if ("code" in (error as any).error) { contentMessage = (error as any).error.code; } if ("content" in (error as any).error) { errorContent = (error as any).error.content; } } const resourceErrorNotification: Notification = { type: "error", subtype: errors.resource.creationError.subtype, info_content: { code: contentMessage, message: errorContent, }, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: ca.name, tenant: tenant }, userError: true, }; eventHelper.notification.publish.creation(resourceErrorNotification); throw error; } }; export const createCertificate = async ( tenant: string, certificate: Resource, security: string ) => { try { await initializeGlobalWebSocketClient(security); const status = getWebSocketStatus(); if (!certificate.key || !certificate.domain) { throw new Error("Certificate requires both key and domain properties"); } const certificateBody = createResourceBody( tenant, certificate, "certificate", { certificate: { cert: certificate.value, key: certificate.key, domain: certificate.domain, }, } ); const response = await makeGlobalWebSocketRequest( "resource:create_resource", certificateBody, 30000, "CREATE", certificate.name ); eventHelper.resource.publish.created(certificate); const resourceNotification: Notification = { type: "success", subtype: errors.resource.created.subtype, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: certificate.name, tenant: tenant } }; eventHelper.notification.publish.creation(resourceNotification); return response; } catch (error) { console.error("Error creating certificate:", { error, tenant, certificate, webSocketStatus: getWebSocketStatus(), }); let contentMessage = "Unknown error"; let errorContent = "Unknown error"; if ( typeof error === "object" && error !== null && "error" in error && typeof (error as any).error === "object" && (error as any).error !== null ) { if ("code" in (error as any).error) { contentMessage = (error as any).error.code; } if ("content" in (error as any).error) { errorContent = (error as any).error.content; } } const resourceErrorNotification: Notification = { type: "error", subtype: errors.resource.creationError.subtype, info_content: { code: contentMessage, message: errorContent, }, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: certificate.name, tenant: tenant }, userError: true, }; eventHelper.notification.publish.creation(resourceErrorNotification); throw error; } }; export const createSecret = async ( tenant: string, secret: Resource, security: string ) => { try { await initializeGlobalWebSocketClient(security); const status = getWebSocketStatus(); const secretBody = createResourceBody(tenant, secret, "secret", { secret: secret.value, }); const response = await makeGlobalWebSocketRequest( "resource:create_resource", secretBody, 30000, "CREATE", secret.name ); eventHelper.resource.publish.created(secret); const resourceNotification: Notification = { type: "success", subtype: errors.resource.created.subtype, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: secret.name, tenant: tenant } }; eventHelper.notification.publish.creation(resourceNotification); return response; } catch (error) { console.error("Error creating secret:", { error, tenant, secret, webSocketStatus: getWebSocketStatus(), }); let contentMessage = "Unknown error"; let errorContent = "Unknown error"; if ( typeof error === "object" && error !== null && "error" in error && typeof (error as any).error === "object" && (error as any).error !== null ) { if ("code" in (error as any).error) { contentMessage = (error as any).error.code; } if ("content" in (error as any).error) { errorContent = (error as any).error.content; } } const resourceErrorNotification: Notification = { type: "error", subtype: errors.resource.creationError.subtype, info_content: { code: contentMessage, message: errorContent, }, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: secret.name, tenant: tenant }, userError: true, }; eventHelper.notification.publish.creation(resourceErrorNotification); throw error; } }; export const createVolume = async ( tenant: string, volume: Resource, security: string ) => { try { await initializeGlobalWebSocketClient(security); const status = getWebSocketStatus(); let volumeKind: "persistent" | "nonreplicated" | "shared" = "persistent"; if (volume.kind === "nonReplicated") { volumeKind = "nonreplicated"; } else if (volume.kind === "volatile") { volumeKind = "persistent"; } const volumeData: any = { volume: { kind: volumeKind, size: Number(volume.value), }, }; if (volume.maxItems !== undefined) { volumeData.volume.maxitems = volume.maxItems; } // if (volumeKind === "persistent") { // volumeData.volume.options = {}; // } const volumeBody = createResourceBody(tenant, volume, "volume", volumeData); const response = await makeGlobalWebSocketRequest( "resource:create_resource", volumeBody, 30000, "CREATE", volume.name ); eventHelper.resource.publish.created(volume); const resourceNotification: Notification = { type: "success", subtype: errors.resource.created.subtype, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: volume.name, tenant: tenant } }; eventHelper.notification.publish.creation(resourceNotification); return response; } catch (error) { console.error("Error creating volume:", { error, tenant, volume, webSocketStatus: getWebSocketStatus(), }); let contentMessage = "Unknown error"; let errorContent = "Unknown error"; if ( typeof error === "object" && error !== null && "error" in error && typeof (error as any).error === "object" && (error as any).error !== null ) { if ("code" in (error as any).error) { contentMessage = (error as any).error.code; } if ("content" in (error as any).error) { errorContent = (error as any).error.content; } } const resourceErrorNotification: Notification = { type: "error", subtype: errors.resource.creationError.subtype, info_content: { code: contentMessage, message: errorContent, }, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: volume.name, tenant: tenant }, userError: true, }; eventHelper.notification.publish.creation(resourceErrorNotification); throw error; } }; export const createResource = async ( tenant: string, resource: Resource, security: string ) => { switch (resource.type) { case "domain": return createDomain(tenant, resource, security); case "port": return createPort(tenant, resource, security); case "ca": return createCA(tenant, resource, security); case "certificate": return createCertificate(tenant, resource, security); case "secret": return createSecret(tenant, resource, security); case "volume": return createVolume(tenant, resource, security); default: throw new Error(`Unsupported resource type: ${resource.type}`); } }; const deleteResourceBody = ( tenant: string, resource: Resource, kind: string ) => ({ tenant, resource: resource.name, kind, }); const deleteResourceBase = async ( tenant: string, resource: Resource, kind: string, security: string ) => { try { await initializeGlobalWebSocketClient(security); const body = deleteResourceBody(tenant, resource, kind); const response = await makeGlobalWebSocketRequest( "resource:delete_resource", body, 30000, "DELETE", resource.name, "resource" ); //eventHelper.resource.publish.deleted(resource); const resourceNotification: Notification = { type: "success", subtype: errors.resource.deleting.subtype, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: resource.name, type: resource.type, tenant: tenant } }; eventHelper.notification.publish.creation(resourceNotification); return response; } catch (error) { console.error(`Error deleting ${kind}:`, { error, tenant, resource }); let contentMessage = "Unknown error"; let errorContent = "Unknown error"; if ( typeof error === "object" && error !== null && "error" in error && typeof (error as any).error === "object" && (error as any).error !== null ) { if ("code" in (error as any).error) { contentMessage = (error as any).error.code; } if ("content" in (error as any).error) { errorContent = (error as any).error.content; } } const resourceErrorNotification: Notification = { type: "error", subtype: errors.resource.deletionError.subtype, info_content: { code: contentMessage, message: errorContent, }, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: resource.name, type: resource.type, tenant: tenant }, userError: true, }; eventHelper.notification.publish.creation(resourceErrorNotification); throw error; } }; export const deleteResource = async ( tenant: string, resource: Resource, security: string ) => { const supportedKinds = [ "domain", "port", "ca", "certificate", "secret", "volume", ]; if (!supportedKinds.includes(resource.type)) { throw new Error(`Unsupported resource type: ${resource.type}`); } return deleteResourceBase(tenant, resource, resource.type, security); }; const updateResourceBase = async ( tenant: string, resource: Resource, kind: string, data: any, security: string ) => { try { await initializeGlobalWebSocketClient(security); const body = createResourceBody(tenant, resource, kind, data); const response = await makeGlobalWebSocketRequest( "resource:update_resource", body, 30000, "UPDATE", resource.name ); // eventHelper.resource.publish.updated(resource); const resourceNotification: Notification = { type: "success", subtype: errors.resource.updated.subtype, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: resource.name, type: resource.type, tenant: tenant } }; eventHelper.notification.publish.creation(resourceNotification); return response; } catch (error) { console.error(`Error updating ${kind}:`, { error, tenant, resource }); let contentMessage = "Unknown error"; let errorContent = "Unknown error"; if ( typeof error === "object" && error !== null && "error" in error && typeof (error as any).error === "object" && (error as any).error !== null ) { if ("code" in (error as any).error) { contentMessage = (error as any).error.code; } if ("content" in (error as any).error) { errorContent = (error as any).error.content; } } const resourceErrorNotification: Notification = { type: "error", subtype: errors.resource.updateError.subtype, info_content: { code: contentMessage, message: errorContent, }, date: Date.now().toString(), status: "unread", callToAction: false, data: { resource: resource.name, type: resource.type, tenant: tenant }, userError: true, }; eventHelper.notification.publish.creation(resourceErrorNotification); throw error; } }; export const updateResource = async ( tenant: string, resource: Resource, security: string ) => { switch (resource.type) { case "domain": return updateResourceBase( tenant, resource, "domain", { domain: resource.value }, security ); case "port": return updateResourceBase( tenant, resource, "port", { port: Number(resource.value) }, security ); case "ca": return updateResourceBase( tenant, resource, "ca", { ca: resource.value }, security ); case "certificate": if (!resource.key || !resource.domain) { throw new Error("Certificate requires both key and domain"); } return updateResourceBase( tenant, resource, "certificate", { certificate: { cert: resource.value, key: resource.key, domain: resource.domain, }, }, security ); case "secret": return updateResourceBase( tenant, resource, "secret", { secret: resource.value }, security ); case "volume": let volumeKind: "persistent" | "nonreplicated" | "shared" = "persistent"; if (resource.kind === "nonReplicated") { volumeKind = "nonreplicated"; } else if (resource.kind === "volatile") { volumeKind = "shared"; } const volumeData: any = { volume: { kind: volumeKind, size: Number(resource.value), }, }; if (resource.maxItems !== undefined) { volumeData.volume.maxitems = resource.maxItems; } if (volumeKind === "shared") { volumeData.volume.options = {}; } return updateResourceBase( tenant, resource, "volume", volumeData, security ); default: throw new Error(`Unsupported resource type: ${resource.type}`); } };