diff --git a/scripts/hydrate.ts b/scripts/hydrate.ts index 8058ebd..a4a0a17 100644 --- a/scripts/hydrate.ts +++ b/scripts/hydrate.ts @@ -25,43 +25,56 @@ async function insertDummyData() { ]); // Insert dummy exit nodes - const exitNode1 = await db.insert(exitNodes).values({ + const exitNode1 = db.insert(exitNodes).values({ name: 'Exit Node 1', - address: '192.168.1.1' + address: '10.0.0.1/24', + privateKey: 'sKQlCNErB2n+dV8eLp5Yw/avsjK/zkrxJE0n48hjb10=', + listenPort: 51820 }).returning().get(); - const exitNode2 = await db.insert(exitNodes).values({ + const exitNode2 = db.insert(exitNodes).values({ name: 'Exit Node 2', - address: '192.168.1.2' + address: '172.16.1.1/24', + privateKey: 'ACaw+q5vHVm8Xb0jIgIkMzlkJiriC7cURuOiNbGsGHg=', + listenPort: 51820 }).returning().get(); // Insert dummy sites - const site1 = await db.insert(sites).values({ + const site1 = db.insert(sites).values({ orgId: org1.orgId, exitNode: exitNode1.exitNodeId, name: 'Main Site', subdomain: 'main', - pubKey: 'abc123', - subnet: '10.0.0.0/24' + pubKey: 'Kn4eD0kvcTwjO//zqH/CtNVkMNdMiUkbqFxysEym2D8=', + subnet: '10.0.0.16/28' }).returning().get(); - const site2 = await db.insert(sites).values({ + const site2 = db.insert(sites).values({ orgId: org2.orgId, exitNode: exitNode2.exitNodeId, name: 'Dev Site', subdomain: 'dev', - pubKey: 'def456', - subnet: '10.0.1.0/24' + pubKey: 'V329Uf/vhnBwYxAuT/ZlMZuLokHy5tug/sGsLfIMK1w=', + subnet: '172.16.1.16/28' }).returning().get(); // Insert dummy resources - const resource1 = await db.insert(resources).values({ + const resource1 = db.insert(resources).values({ + resourceId: `web.${site1.subdomain}.${org1.domain}`, siteId: site1.siteId, name: 'Web Server', subdomain: 'web' }).returning().get(); - const resource2 = await db.insert(resources).values({ + const resource2 = db.insert(resources).values({ + resourceId: `web2.${site1.subdomain}.${org1.domain}`, + siteId: site1.siteId, + name: 'Web Server 2', + subdomain: 'web2' + }).returning().get(); + + const resource3 = db.insert(resources).values({ + resourceId: `db.${site2.subdomain}.${org2.domain}`, siteId: site2.siteId, name: 'Database', subdomain: 'db' @@ -69,14 +82,15 @@ async function insertDummyData() { // Insert dummy routes await db.insert(routes).values([ - { exitNodeId: exitNode1.exitNodeId, subnet: '192.168.0.0/24' }, - { exitNodeId: exitNode2.exitNodeId, subnet: '172.16.0.0/24' } + { exitNodeId: exitNode1.exitNodeId, subnet: '10.0.0.0/24' }, + { exitNodeId: exitNode2.exitNodeId, subnet: '172.16.1.1/24' } ]); // Insert dummy targets await db.insert(targets).values([ - { resourceId: resource1.resourceId, ip: '10.0.0.10', method: 'GET', port: 80, protocol: 'http' }, - { resourceId: resource2.resourceId, ip: '10.0.1.20', method: 'TCP', port: 5432, protocol: 'postgresql' } + { resourceId: resource1.resourceId, ip: '10.0.0.16', method: 'https', port: 443, protocol: 'TCP' }, + { resourceId: resource2.resourceId, ip: '10.0.0.17', method: 'http', port: 80, protocol: 'TCP' }, + { resourceId: resource3.resourceId, ip: '172.16.1.16', method: 'http', port: 80, protocol: 'TCP' } ]); console.log('Dummy data inserted successfully'); diff --git a/server/db/schema.ts b/server/db/schema.ts index 081fad7..0c29459 100644 --- a/server/db/schema.ts +++ b/server/db/schema.ts @@ -11,7 +11,7 @@ export const orgs = sqliteTable("orgs", { // Users table export const users = sqliteTable("users", { userId: integer("userId").primaryKey({ autoIncrement: true }), - orgId: integer("orgId").references(() => orgs.orgId), + orgId: integer("orgId").references(() => orgs.orgId, { onDelete: "cascade" }), name: text("name").notNull(), email: text("email").notNull(), groups: text("groups"), @@ -20,8 +20,8 @@ export const users = sqliteTable("users", { // Sites table export const sites = sqliteTable("sites", { siteId: integer("siteId").primaryKey({ autoIncrement: true }), - orgId: integer("orgId").references(() => orgs.orgId), - exitNode: integer("exitNode").references(() => exitNodes.exitNodeId), + orgId: integer("orgId").references(() => orgs.orgId, { onDelete: "cascade" }), + exitNode: integer("exitNode").references(() => exitNodes.exitNodeId, { onDelete: "set null" }), name: text("name").notNull(), subdomain: text("subdomain"), pubKey: text("pubKey"), @@ -30,12 +30,22 @@ export const sites = sqliteTable("sites", { // Resources table export const resources = sqliteTable("resources", { - resourceId: integer("resourceId").primaryKey({ autoIncrement: true }), - siteId: integer("siteId").references(() => sites.siteId), + resourceId: text("resourceId", { length: 2048 }).primaryKey(), + siteId: integer("siteId").references(() => sites.siteId, { onDelete: "cascade" }), name: text("name").notNull(), subdomain: text("subdomain"), }); +// Targets table +export const targets = sqliteTable("targets", { + targetId: integer("targetId").primaryKey({ autoIncrement: true }), + resourceId: text("resourceId").references(() => resources.resourceId, { onDelete: "cascade" }), + ip: text("ip").notNull(), + method: text("method"), + port: integer("port"), + protocol: text("protocol"), +}); + // Exit Nodes table export const exitNodes = sqliteTable("exitNodes", { exitNodeId: integer("exitNodeId").primaryKey({ autoIncrement: true }), @@ -48,20 +58,10 @@ export const exitNodes = sqliteTable("exitNodes", { // Routes table export const routes = sqliteTable("routes", { routeId: integer("routeId").primaryKey({ autoIncrement: true }), - exitNodeId: integer("exitNodeId").references(() => exitNodes.exitNodeId), + exitNodeId: integer("exitNodeId").references(() => exitNodes.exitNodeId, { onDelete: "cascade" }), subnet: text("subnet").notNull(), }); -// Targets table -export const targets = sqliteTable("targets", { - targetId: integer("targetId").primaryKey({ autoIncrement: true }), - resourceId: integer("resourceId").references(() => resources.resourceId), - ip: text("ip").notNull(), - method: text("method"), - port: integer("port"), - protocol: text("protocol"), -}); - // Define the model types for type inference export type Org = InferSelectModel; export type User = InferSelectModel; @@ -69,4 +69,4 @@ export type Site = InferSelectModel; export type Resource = InferSelectModel; export type ExitNode = InferSelectModel; export type Route = InferSelectModel; -export type Target = InferSelectModel; +export type Target = InferSelectModel; \ No newline at end of file diff --git a/server/migrations/0000_unique_killraven.sql b/server/migrations/0000_wealthy_captain_midlands.sql similarity index 84% rename from server/migrations/0000_unique_killraven.sql rename to server/migrations/0000_wealthy_captain_midlands.sql index 229c589..0643a3d 100644 --- a/server/migrations/0000_unique_killraven.sql +++ b/server/migrations/0000_wealthy_captain_midlands.sql @@ -13,18 +13,18 @@ CREATE TABLE `orgs` ( ); --> statement-breakpoint CREATE TABLE `resources` ( - `resourceId` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `resourceId` text(2048) PRIMARY KEY NOT NULL, `siteId` integer, `name` text NOT NULL, `subdomain` text, - FOREIGN KEY (`siteId`) REFERENCES `sites`(`siteId`) ON UPDATE no action ON DELETE no action + FOREIGN KEY (`siteId`) REFERENCES `sites`(`siteId`) ON UPDATE no action ON DELETE cascade ); --> statement-breakpoint CREATE TABLE `routes` ( `routeId` integer PRIMARY KEY AUTOINCREMENT NOT NULL, `exitNodeId` integer, `subnet` text NOT NULL, - FOREIGN KEY (`exitNodeId`) REFERENCES `exitNodes`(`exitNodeId`) ON UPDATE no action ON DELETE no action + FOREIGN KEY (`exitNodeId`) REFERENCES `exitNodes`(`exitNodeId`) ON UPDATE no action ON DELETE cascade ); --> statement-breakpoint CREATE TABLE `sites` ( @@ -35,18 +35,18 @@ CREATE TABLE `sites` ( `subdomain` text, `pubKey` text, `subnet` text, - FOREIGN KEY (`orgId`) REFERENCES `orgs`(`orgId`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`exitNode`) REFERENCES `exitNodes`(`exitNodeId`) ON UPDATE no action ON DELETE no action + FOREIGN KEY (`orgId`) REFERENCES `orgs`(`orgId`) ON UPDATE no action ON DELETE cascade, + FOREIGN KEY (`exitNode`) REFERENCES `exitNodes`(`exitNodeId`) ON UPDATE no action ON DELETE set null ); --> statement-breakpoint CREATE TABLE `targets` ( `targetId` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `resourceId` integer, + `resourceId` text, `ip` text NOT NULL, `method` text, `port` integer, `protocol` text, - FOREIGN KEY (`resourceId`) REFERENCES `resources`(`resourceId`) ON UPDATE no action ON DELETE no action + FOREIGN KEY (`resourceId`) REFERENCES `resources`(`resourceId`) ON UPDATE no action ON DELETE cascade ); --> statement-breakpoint CREATE TABLE `users` ( @@ -55,5 +55,5 @@ CREATE TABLE `users` ( `name` text NOT NULL, `email` text NOT NULL, `groups` text, - FOREIGN KEY (`orgId`) REFERENCES `orgs`(`orgId`) ON UPDATE no action ON DELETE no action + FOREIGN KEY (`orgId`) REFERENCES `orgs`(`orgId`) ON UPDATE no action ON DELETE cascade ); diff --git a/server/migrations/meta/0000_snapshot.json b/server/migrations/meta/0000_snapshot.json index 75117eb..99da50f 100644 --- a/server/migrations/meta/0000_snapshot.json +++ b/server/migrations/meta/0000_snapshot.json @@ -1,7 +1,7 @@ { "version": "6", "dialect": "sqlite", - "id": "369f669c-f220-4706-9a5c-8a66ab5653b2", + "id": "379ca2f9-068a-4289-8a23-001e7dc269b1", "prevId": "00000000-0000-0000-0000-000000000000", "tables": { "exitNodes": { @@ -83,10 +83,10 @@ "columns": { "resourceId": { "name": "resourceId", - "type": "integer", + "type": "text(2048)", "primaryKey": true, "notNull": true, - "autoincrement": true + "autoincrement": false }, "siteId": { "name": "siteId", @@ -122,7 +122,7 @@ "columnsTo": [ "siteId" ], - "onDelete": "no action", + "onDelete": "cascade", "onUpdate": "no action" } }, @@ -166,7 +166,7 @@ "columnsTo": [ "exitNodeId" ], - "onDelete": "no action", + "onDelete": "cascade", "onUpdate": "no action" } }, @@ -238,7 +238,7 @@ "columnsTo": [ "orgId" ], - "onDelete": "no action", + "onDelete": "cascade", "onUpdate": "no action" }, "sites_exitNode_exitNodes_exitNodeId_fk": { @@ -251,7 +251,7 @@ "columnsTo": [ "exitNodeId" ], - "onDelete": "no action", + "onDelete": "set null", "onUpdate": "no action" } }, @@ -270,7 +270,7 @@ }, "resourceId": { "name": "resourceId", - "type": "integer", + "type": "text", "primaryKey": false, "notNull": false, "autoincrement": false @@ -316,7 +316,7 @@ "columnsTo": [ "resourceId" ], - "onDelete": "no action", + "onDelete": "cascade", "onUpdate": "no action" } }, @@ -374,7 +374,7 @@ "columnsTo": [ "orgId" ], - "onDelete": "no action", + "onDelete": "cascade", "onUpdate": "no action" } }, diff --git a/server/migrations/meta/_journal.json b/server/migrations/meta/_journal.json index 55506fe..4eb8fe0 100644 --- a/server/migrations/meta/_journal.json +++ b/server/migrations/meta/_journal.json @@ -5,8 +5,8 @@ { "idx": 0, "version": "6", - "when": 1727551266674, - "tag": "0000_unique_killraven", + "when": 1727557783608, + "tag": "0000_wealthy_captain_midlands", "breakpoints": true } ] diff --git a/server/routers/badger/getConfig.ts b/server/routers/badger/getConfig.ts index a7b47d5..3a352af 100644 --- a/server/routers/badger/getConfig.ts +++ b/server/routers/badger/getConfig.ts @@ -1,66 +1,71 @@ import { Request, Response, NextFunction } from 'express'; import { DrizzleError, eq } from 'drizzle-orm'; import { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'; -import { sites, Site } from '../../db/schema'; +import { sites, resources, targets, exitNodes } from '../../db/schema'; import db from '../../db'; export const getConfig = async (req: Request, res: Response, next: NextFunction): Promise => { - try { - const exitNodeId = parseInt(req.query.exitNodeId as string); - - if (!db) { - throw new Error('Database is not attached to the request'); - } - - - const exitNode = await db.query.exitNodes.findFirst({ - where: { - exitNodeId: eq(exitNodeId) - }, - with: { - routes: true, - sites: { - with: { - resources: { - with: { - targets: true - } - } - } - } + try { + if (!req.query.exitNodeId) { + throw new Error('Missing exitNodeId query parameter'); } - }); - - if (!exitNode) { - throw new Error('Exit node not found'); - } - - const config = { - privateKey, - listenPort, - ipAddress: exitNode.address, - peers: exitNode.sites.map((site, index) => ({ - publicKey: site.pubKey, - allowedIps: site.resources.flatMap(resource => - resource.targets.map(target => target.ip) - ) - })) - }; + const exitNodeId = parseInt(req.query.exitNodeId as string); - res.json(config); - } catch (error) { - console.error('Error querying database:', error); - if (error instanceof DrizzleError) { - res.status(500).json({ error: 'Database query error', message: error.message }); - } else { - next(error); + // Fetch exit node + const exitNode = await db.query.exitNodes.findFirst({ + where: eq(exitNodes.exitNodeId, exitNodeId), + }); + + if (!exitNode) { + throw new Error('Exit node not found'); + } + + // Fetch sites for this exit node + const sitesRes = await db.query.sites.findMany({ + where: eq(sites.exitNode, exitNodeId), + }); + + const peers = await Promise.all(sitesRes.map(async (site) => { + // Fetch resources for this site + const resourcesRes = await db.query.resources.findMany({ + where: eq(resources.siteId, site.siteId), + }); + + // Fetch targets for all resources of this site + const targetIps = await Promise.all(resourcesRes.map(async (resource) => { + const targetsRes = await db.query.targets.findMany({ + where: eq(targets.resourceId, resource.resourceId), + }); + return targetsRes.map(target => `${target.ip}/32`); + })); + + return { + publicKey: site.pubKey, + allowedIps: targetIps.flat(), + }; + })); + + const config = { + privateKey: exitNode.privateKey, + listenPort: exitNode.listenPort, + ipAddress: exitNode.address, + peers, + }; + + + res.json(config); + } catch (error) { + console.error('Error querying database:', error); + if (error instanceof DrizzleError) { + res.status(500).json({ error: 'Database query error', message: error.message }); + } else { + next(error); + } } - } }; function calculateSubnet(index: number): string { const baseIp = 10 << 24; const subnetSize = 16; return `${(baseIp | (index * subnetSize)).toString()}/28`; - } - \ No newline at end of file +}