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
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');

View file

@ -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<typeof orgs>;
export type User = InferSelectModel<typeof users>;
@ -69,4 +69,4 @@ export type Site = InferSelectModel<typeof sites>;
export type Resource = InferSelectModel<typeof resources>;
export type ExitNode = InferSelectModel<typeof exitNodes>;
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
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
);

View file

@ -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"
}
},

View file

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

View file

@ -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<void> => {
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`;
}
}