Adjust schema

This commit is contained in:
Owen Schwartz 2024-09-28 17:10:03 -04:00
parent 7bb81af3bb
commit 2970088b29
No known key found for this signature in database
GPG key ID: 8271FDFFD9E0CCBD
6 changed files with 123 additions and 104 deletions

View file

@ -25,43 +25,56 @@ async function insertDummyData() {
]); ]);
// Insert dummy exit nodes // Insert dummy exit nodes
const exitNode1 = await db.insert(exitNodes).values({ const exitNode1 = db.insert(exitNodes).values({
name: 'Exit Node 1', name: 'Exit Node 1',
address: '192.168.1.1' address: '10.0.0.1/24',
privateKey: 'sKQlCNErB2n+dV8eLp5Yw/avsjK/zkrxJE0n48hjb10=',
listenPort: 51820
}).returning().get(); }).returning().get();
const exitNode2 = await db.insert(exitNodes).values({ const exitNode2 = db.insert(exitNodes).values({
name: 'Exit Node 2', name: 'Exit Node 2',
address: '192.168.1.2' address: '172.16.1.1/24',
privateKey: 'ACaw+q5vHVm8Xb0jIgIkMzlkJiriC7cURuOiNbGsGHg=',
listenPort: 51820
}).returning().get(); }).returning().get();
// Insert dummy sites // Insert dummy sites
const site1 = await db.insert(sites).values({ const site1 = db.insert(sites).values({
orgId: org1.orgId, orgId: org1.orgId,
exitNode: exitNode1.exitNodeId, exitNode: exitNode1.exitNodeId,
name: 'Main Site', name: 'Main Site',
subdomain: 'main', subdomain: 'main',
pubKey: 'abc123', pubKey: 'Kn4eD0kvcTwjO//zqH/CtNVkMNdMiUkbqFxysEym2D8=',
subnet: '10.0.0.0/24' subnet: '10.0.0.16/28'
}).returning().get(); }).returning().get();
const site2 = await db.insert(sites).values({ const site2 = db.insert(sites).values({
orgId: org2.orgId, orgId: org2.orgId,
exitNode: exitNode2.exitNodeId, exitNode: exitNode2.exitNodeId,
name: 'Dev Site', name: 'Dev Site',
subdomain: 'dev', subdomain: 'dev',
pubKey: 'def456', pubKey: 'V329Uf/vhnBwYxAuT/ZlMZuLokHy5tug/sGsLfIMK1w=',
subnet: '10.0.1.0/24' subnet: '172.16.1.16/28'
}).returning().get(); }).returning().get();
// Insert dummy resources // 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, siteId: site1.siteId,
name: 'Web Server', name: 'Web Server',
subdomain: 'web' subdomain: 'web'
}).returning().get(); }).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, siteId: site2.siteId,
name: 'Database', name: 'Database',
subdomain: 'db' subdomain: 'db'
@ -69,14 +82,15 @@ async function insertDummyData() {
// Insert dummy routes // Insert dummy routes
await db.insert(routes).values([ await db.insert(routes).values([
{ exitNodeId: exitNode1.exitNodeId, subnet: '192.168.0.0/24' }, { exitNodeId: exitNode1.exitNodeId, subnet: '10.0.0.0/24' },
{ exitNodeId: exitNode2.exitNodeId, subnet: '172.16.0.0/24' } { exitNodeId: exitNode2.exitNodeId, subnet: '172.16.1.1/24' }
]); ]);
// Insert dummy targets // Insert dummy targets
await db.insert(targets).values([ await db.insert(targets).values([
{ resourceId: resource1.resourceId, ip: '10.0.0.10', method: 'GET', port: 80, protocol: 'http' }, { resourceId: resource1.resourceId, ip: '10.0.0.16', method: 'https', port: 443, protocol: 'TCP' },
{ resourceId: resource2.resourceId, ip: '10.0.1.20', method: 'TCP', port: 5432, protocol: 'postgresql' } { 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'); console.log('Dummy data inserted successfully');

View file

@ -11,7 +11,7 @@ export const orgs = sqliteTable("orgs", {
// Users table // Users table
export const users = sqliteTable("users", { export const users = sqliteTable("users", {
userId: integer("userId").primaryKey({ autoIncrement: true }), userId: integer("userId").primaryKey({ autoIncrement: true }),
orgId: integer("orgId").references(() => orgs.orgId), orgId: integer("orgId").references(() => orgs.orgId, { onDelete: "cascade" }),
name: text("name").notNull(), name: text("name").notNull(),
email: text("email").notNull(), email: text("email").notNull(),
groups: text("groups"), groups: text("groups"),
@ -20,8 +20,8 @@ export const users = sqliteTable("users", {
// Sites table // Sites table
export const sites = sqliteTable("sites", { export const sites = sqliteTable("sites", {
siteId: integer("siteId").primaryKey({ autoIncrement: true }), siteId: integer("siteId").primaryKey({ autoIncrement: true }),
orgId: integer("orgId").references(() => orgs.orgId), orgId: integer("orgId").references(() => orgs.orgId, { onDelete: "cascade" }),
exitNode: integer("exitNode").references(() => exitNodes.exitNodeId), exitNode: integer("exitNode").references(() => exitNodes.exitNodeId, { onDelete: "set null" }),
name: text("name").notNull(), name: text("name").notNull(),
subdomain: text("subdomain"), subdomain: text("subdomain"),
pubKey: text("pubKey"), pubKey: text("pubKey"),
@ -30,12 +30,22 @@ export const sites = sqliteTable("sites", {
// Resources table // Resources table
export const resources = sqliteTable("resources", { export const resources = sqliteTable("resources", {
resourceId: integer("resourceId").primaryKey({ autoIncrement: true }), resourceId: text("resourceId", { length: 2048 }).primaryKey(),
siteId: integer("siteId").references(() => sites.siteId), siteId: integer("siteId").references(() => sites.siteId, { onDelete: "cascade" }),
name: text("name").notNull(), name: text("name").notNull(),
subdomain: text("subdomain"), 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 // Exit Nodes table
export const exitNodes = sqliteTable("exitNodes", { export const exitNodes = sqliteTable("exitNodes", {
exitNodeId: integer("exitNodeId").primaryKey({ autoIncrement: true }), exitNodeId: integer("exitNodeId").primaryKey({ autoIncrement: true }),
@ -48,20 +58,10 @@ export const exitNodes = sqliteTable("exitNodes", {
// Routes table // Routes table
export const routes = sqliteTable("routes", { export const routes = sqliteTable("routes", {
routeId: integer("routeId").primaryKey({ autoIncrement: true }), routeId: integer("routeId").primaryKey({ autoIncrement: true }),
exitNodeId: integer("exitNodeId").references(() => exitNodes.exitNodeId), exitNodeId: integer("exitNodeId").references(() => exitNodes.exitNodeId, { onDelete: "cascade" }),
subnet: text("subnet").notNull(), 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 // Define the model types for type inference
export type Org = InferSelectModel<typeof orgs>; export type Org = InferSelectModel<typeof orgs>;
export type User = InferSelectModel<typeof users>; export type User = InferSelectModel<typeof users>;
@ -69,4 +69,4 @@ export type Site = InferSelectModel<typeof sites>;
export type Resource = InferSelectModel<typeof resources>; export type Resource = InferSelectModel<typeof resources>;
export type ExitNode = InferSelectModel<typeof exitNodes>; export type ExitNode = InferSelectModel<typeof exitNodes>;
export type Route = InferSelectModel<typeof routes>; export type Route = InferSelectModel<typeof routes>;
export type Target = InferSelectModel<typeof targets>; export type Target = InferSelectModel<typeof targets>;

View file

@ -13,18 +13,18 @@ CREATE TABLE `orgs` (
); );
--> statement-breakpoint --> statement-breakpoint
CREATE TABLE `resources` ( CREATE TABLE `resources` (
`resourceId` integer PRIMARY KEY AUTOINCREMENT NOT NULL, `resourceId` text(2048) PRIMARY KEY NOT NULL,
`siteId` integer, `siteId` integer,
`name` text NOT NULL, `name` text NOT NULL,
`subdomain` text, `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 --> statement-breakpoint
CREATE TABLE `routes` ( CREATE TABLE `routes` (
`routeId` integer PRIMARY KEY AUTOINCREMENT NOT NULL, `routeId` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`exitNodeId` integer, `exitNodeId` integer,
`subnet` text NOT NULL, `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 --> statement-breakpoint
CREATE TABLE `sites` ( CREATE TABLE `sites` (
@ -35,18 +35,18 @@ CREATE TABLE `sites` (
`subdomain` text, `subdomain` text,
`pubKey` text, `pubKey` text,
`subnet` text, `subnet` 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,
FOREIGN KEY (`exitNode`) REFERENCES `exitNodes`(`exitNodeId`) ON UPDATE no action ON DELETE no action FOREIGN KEY (`exitNode`) REFERENCES `exitNodes`(`exitNodeId`) ON UPDATE no action ON DELETE set null
); );
--> statement-breakpoint --> statement-breakpoint
CREATE TABLE `targets` ( CREATE TABLE `targets` (
`targetId` integer PRIMARY KEY AUTOINCREMENT NOT NULL, `targetId` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`resourceId` integer, `resourceId` text,
`ip` text NOT NULL, `ip` text NOT NULL,
`method` text, `method` text,
`port` integer, `port` integer,
`protocol` text, `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 --> statement-breakpoint
CREATE TABLE `users` ( CREATE TABLE `users` (
@ -55,5 +55,5 @@ CREATE TABLE `users` (
`name` text NOT NULL, `name` text NOT NULL,
`email` text NOT NULL, `email` text NOT NULL,
`groups` text, `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
); );

View file

@ -1,7 +1,7 @@
{ {
"version": "6", "version": "6",
"dialect": "sqlite", "dialect": "sqlite",
"id": "369f669c-f220-4706-9a5c-8a66ab5653b2", "id": "379ca2f9-068a-4289-8a23-001e7dc269b1",
"prevId": "00000000-0000-0000-0000-000000000000", "prevId": "00000000-0000-0000-0000-000000000000",
"tables": { "tables": {
"exitNodes": { "exitNodes": {
@ -83,10 +83,10 @@
"columns": { "columns": {
"resourceId": { "resourceId": {
"name": "resourceId", "name": "resourceId",
"type": "integer", "type": "text(2048)",
"primaryKey": true, "primaryKey": true,
"notNull": true, "notNull": true,
"autoincrement": true "autoincrement": false
}, },
"siteId": { "siteId": {
"name": "siteId", "name": "siteId",
@ -122,7 +122,7 @@
"columnsTo": [ "columnsTo": [
"siteId" "siteId"
], ],
"onDelete": "no action", "onDelete": "cascade",
"onUpdate": "no action" "onUpdate": "no action"
} }
}, },
@ -166,7 +166,7 @@
"columnsTo": [ "columnsTo": [
"exitNodeId" "exitNodeId"
], ],
"onDelete": "no action", "onDelete": "cascade",
"onUpdate": "no action" "onUpdate": "no action"
} }
}, },
@ -238,7 +238,7 @@
"columnsTo": [ "columnsTo": [
"orgId" "orgId"
], ],
"onDelete": "no action", "onDelete": "cascade",
"onUpdate": "no action" "onUpdate": "no action"
}, },
"sites_exitNode_exitNodes_exitNodeId_fk": { "sites_exitNode_exitNodes_exitNodeId_fk": {
@ -251,7 +251,7 @@
"columnsTo": [ "columnsTo": [
"exitNodeId" "exitNodeId"
], ],
"onDelete": "no action", "onDelete": "set null",
"onUpdate": "no action" "onUpdate": "no action"
} }
}, },
@ -270,7 +270,7 @@
}, },
"resourceId": { "resourceId": {
"name": "resourceId", "name": "resourceId",
"type": "integer", "type": "text",
"primaryKey": false, "primaryKey": false,
"notNull": false, "notNull": false,
"autoincrement": false "autoincrement": false
@ -316,7 +316,7 @@
"columnsTo": [ "columnsTo": [
"resourceId" "resourceId"
], ],
"onDelete": "no action", "onDelete": "cascade",
"onUpdate": "no action" "onUpdate": "no action"
} }
}, },
@ -374,7 +374,7 @@
"columnsTo": [ "columnsTo": [
"orgId" "orgId"
], ],
"onDelete": "no action", "onDelete": "cascade",
"onUpdate": "no action" "onUpdate": "no action"
} }
}, },

View file

@ -5,8 +5,8 @@
{ {
"idx": 0, "idx": 0,
"version": "6", "version": "6",
"when": 1727551266674, "when": 1727557783608,
"tag": "0000_unique_killraven", "tag": "0000_wealthy_captain_midlands",
"breakpoints": true "breakpoints": true
} }
] ]

View file

@ -1,66 +1,71 @@
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import { DrizzleError, eq } from 'drizzle-orm'; import { DrizzleError, eq } from 'drizzle-orm';
import { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'; 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'; import db from '../../db';
export const getConfig = async (req: Request, res: Response, next: NextFunction): Promise<void> => { export const getConfig = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
try { try {
const exitNodeId = parseInt(req.query.exitNodeId as string); if (!req.query.exitNodeId) {
throw new Error('Missing exitNodeId query parameter');
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
}
}
}
}
} }
}); const exitNodeId = parseInt(req.query.exitNodeId as string);
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)
)
}))
};
res.json(config); // Fetch exit node
} catch (error) { const exitNode = await db.query.exitNodes.findFirst({
console.error('Error querying database:', error); where: eq(exitNodes.exitNodeId, exitNodeId),
if (error instanceof DrizzleError) { });
res.status(500).json({ error: 'Database query error', message: error.message });
} else { if (!exitNode) {
next(error); 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 { function calculateSubnet(index: number): string {
const baseIp = 10 << 24; const baseIp = 10 << 24;
const subnetSize = 16; const subnetSize = 16;
return `${(baseIp | (index * subnetSize)).toString()}/28`; return `${(baseIp | (index * subnetSize)).toString()}/28`;
} }