import { db, newts, Target } from "@server/db"; import { Config, ConfigSchema } from "./types"; import { ProxyResourcesResults, updateProxyResources } from "./proxyResources"; import { fromError } from "zod-validation-error"; import logger from "@server/logger"; import { resources, targets, sites } from "@server/db"; import { eq, and, asc, or, ne, count, isNotNull } from "drizzle-orm"; import { addTargets as addProxyTargets } from "@server/routers/newt/targets"; import { addTargets as addClientTargets } from "@server/routers/client/targets"; import { ClientResourcesResults, updateClientResources } from "./clientResources"; export async function applyBlueprint( orgId: string, configData: unknown, siteId?: number ): Promise { // Validate the input data const validationResult = ConfigSchema.safeParse(configData); if (!validationResult.success) { throw new Error(fromError(validationResult.error).toString()); } const config: Config = validationResult.data; try { let proxyResourcesResults: ProxyResourcesResults = []; let clientResourcesResults: ClientResourcesResults = []; await db.transaction(async (trx) => { proxyResourcesResults = await updateProxyResources( orgId, config, trx, siteId ); clientResourcesResults = await updateClientResources( orgId, config, trx, siteId ); }); logger.debug( `Successfully updated proxy resources for org ${orgId}: ${JSON.stringify(proxyResourcesResults)}` ); // We need to update the targets on the newts from the successfully updated information for (const result of proxyResourcesResults) { for (const target of result.targetsToUpdate) { const [site] = await db .select() .from(sites) .innerJoin(newts, eq(sites.siteId, newts.siteId)) .where( and( eq(sites.siteId, target.siteId), eq(sites.orgId, orgId), eq(sites.type, "newt"), isNotNull(sites.pubKey) ) ) .limit(1); if (site) { logger.debug( `Updating target ${target.targetId} on site ${site.sites.siteId}` ); await addProxyTargets( site.newt.newtId, [target], result.proxyResource.protocol, result.proxyResource.proxyPort ); } } } logger.debug( `Successfully updated client resources for org ${orgId}: ${JSON.stringify(clientResourcesResults)}` ); // We need to update the targets on the newts from the successfully updated information for (const result of clientResourcesResults) { const [site] = await db .select() .from(sites) .innerJoin(newts, eq(sites.siteId, newts.siteId)) .where( and( eq(sites.siteId, result.resource.siteId), eq(sites.orgId, orgId), eq(sites.type, "newt"), isNotNull(sites.pubKey) ) ) .limit(1); if (site) { logger.debug( `Updating client resource ${result.resource.siteResourceId} on site ${site.sites.siteId}` ); await addClientTargets( site.newt.newtId, result.resource.destinationIp, result.resource.destinationPort, result.resource.protocol, result.resource.proxyPort ); } } } catch (error) { logger.error(`Failed to update database from config: ${error}`); throw error; } } // await updateDatabaseFromConfig("org_i21aifypnlyxur2", { // resources: { // "resource-nice-id": { // name: "this is my resource", // protocol: "http", // "full-domain": "level1.test.example.com", // "host-header": "example.com", // "tls-server-name": "example.com", // auth: { // pincode: 123456, // password: "sadfasdfadsf", // "sso-enabled": true, // "sso-roles": ["Member"], // "sso-users": ["owen@fossorial.io"], // "whitelist-users": ["owen@fossorial.io"] // }, // targets: [ // { // site: "glossy-plains-viscacha-rat", // hostname: "localhost", // method: "http", // port: 8000, // healthcheck: { // port: 8000, // hostname: "localhost" // } // }, // { // site: "glossy-plains-viscacha-rat", // hostname: "localhost", // method: "http", // port: 8001 // } // ] // }, // "resource-nice-id2": { // name: "http server", // protocol: "tcp", // "proxy-port": 3000, // targets: [ // { // site: "glossy-plains-viscacha-rat", // hostname: "localhost", // port: 3000, // } // ] // } // } // });