mirror of
https://github.com/fosrl/pangolin.git
synced 2025-05-13 13:50:40 +01:00
added traefik config provider endpoint
This commit is contained in:
parent
a67463a518
commit
07bf2059c6
10 changed files with 134 additions and 472 deletions
|
@ -4,8 +4,8 @@ import path from "path";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
dialect: "sqlite",
|
dialect: "sqlite",
|
||||||
schema: "server/db/schema.ts",
|
schema: path.join(__dirname, "server", "db", "schema.ts"),
|
||||||
out: "server/migrations",
|
out: path.join(__dirname, "server", "migrations"),
|
||||||
verbose: true,
|
verbose: true,
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
url: path.join(environment.CONFIG_PATH, "db", "db.sqlite"),
|
url: path.join(environment.CONFIG_PATH, "db", "db.sqlite"),
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
const runMigrations = async () => {
|
const runMigrations = async () => {
|
||||||
console.log("Running migrations...");
|
console.log("Running migrations...");
|
||||||
try {
|
try {
|
||||||
migrate(db, { migrationsFolder: "./server/migrations" });
|
migrate(db, {
|
||||||
|
migrationsFolder: path.join(__dirname, "server/migrations"),
|
||||||
|
});
|
||||||
console.log("Migrations completed successfully.");
|
console.log("Migrations completed successfully.");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error running migrations:", error);
|
console.error("Error running migrations:", error);
|
||||||
|
|
|
@ -41,8 +41,8 @@ export const targets = sqliteTable("targets", {
|
||||||
targetId: integer("targetId").primaryKey({ autoIncrement: true }),
|
targetId: integer("targetId").primaryKey({ autoIncrement: true }),
|
||||||
resourceId: text("resourceId").references(() => resources.resourceId, { onDelete: "cascade" }),
|
resourceId: text("resourceId").references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
ip: text("ip").notNull(),
|
ip: text("ip").notNull(),
|
||||||
method: text("method"),
|
method: text("method").notNull(),
|
||||||
port: integer("port"),
|
port: integer("port").notNull(),
|
||||||
protocol: text("protocol"),
|
protocol: text("protocol"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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>;
|
||||||
|
|
0
server/migrations/.gitkeep
Normal file
0
server/migrations/.gitkeep
Normal file
|
@ -1,59 +0,0 @@
|
||||||
CREATE TABLE `exitNodes` (
|
|
||||||
`exitNodeId` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
||||||
`name` text NOT NULL,
|
|
||||||
`address` text NOT NULL,
|
|
||||||
`privateKey` text,
|
|
||||||
`listenPort` integer
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE `orgs` (
|
|
||||||
`orgId` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
||||||
`name` text NOT NULL,
|
|
||||||
`domain` text NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE `resources` (
|
|
||||||
`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 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 cascade
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE `sites` (
|
|
||||||
`siteId` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
||||||
`orgId` integer,
|
|
||||||
`exitNode` integer,
|
|
||||||
`name` text NOT NULL,
|
|
||||||
`subdomain` text,
|
|
||||||
`pubKey` text,
|
|
||||||
`subnet` text,
|
|
||||||
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` text,
|
|
||||||
`ip` text NOT NULL,
|
|
||||||
`method` text,
|
|
||||||
`port` integer,
|
|
||||||
`protocol` text,
|
|
||||||
FOREIGN KEY (`resourceId`) REFERENCES `resources`(`resourceId`) ON UPDATE no action ON DELETE cascade
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE `users` (
|
|
||||||
`userId` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
||||||
`orgId` integer,
|
|
||||||
`name` text NOT NULL,
|
|
||||||
`email` text NOT NULL,
|
|
||||||
`groups` text,
|
|
||||||
FOREIGN KEY (`orgId`) REFERENCES `orgs`(`orgId`) ON UPDATE no action ON DELETE cascade
|
|
||||||
);
|
|
|
@ -1,394 +0,0 @@
|
||||||
{
|
|
||||||
"version": "6",
|
|
||||||
"dialect": "sqlite",
|
|
||||||
"id": "379ca2f9-068a-4289-8a23-001e7dc269b1",
|
|
||||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
|
||||||
"tables": {
|
|
||||||
"exitNodes": {
|
|
||||||
"name": "exitNodes",
|
|
||||||
"columns": {
|
|
||||||
"exitNodeId": {
|
|
||||||
"name": "exitNodeId",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"address": {
|
|
||||||
"name": "address",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"privateKey": {
|
|
||||||
"name": "privateKey",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"listenPort": {
|
|
||||||
"name": "listenPort",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"orgs": {
|
|
||||||
"name": "orgs",
|
|
||||||
"columns": {
|
|
||||||
"orgId": {
|
|
||||||
"name": "orgId",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"domain": {
|
|
||||||
"name": "domain",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"resources": {
|
|
||||||
"name": "resources",
|
|
||||||
"columns": {
|
|
||||||
"resourceId": {
|
|
||||||
"name": "resourceId",
|
|
||||||
"type": "text(2048)",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"siteId": {
|
|
||||||
"name": "siteId",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"subdomain": {
|
|
||||||
"name": "subdomain",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"resources_siteId_sites_siteId_fk": {
|
|
||||||
"name": "resources_siteId_sites_siteId_fk",
|
|
||||||
"tableFrom": "resources",
|
|
||||||
"tableTo": "sites",
|
|
||||||
"columnsFrom": [
|
|
||||||
"siteId"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"siteId"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"routes": {
|
|
||||||
"name": "routes",
|
|
||||||
"columns": {
|
|
||||||
"routeId": {
|
|
||||||
"name": "routeId",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": true
|
|
||||||
},
|
|
||||||
"exitNodeId": {
|
|
||||||
"name": "exitNodeId",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"subnet": {
|
|
||||||
"name": "subnet",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"routes_exitNodeId_exitNodes_exitNodeId_fk": {
|
|
||||||
"name": "routes_exitNodeId_exitNodes_exitNodeId_fk",
|
|
||||||
"tableFrom": "routes",
|
|
||||||
"tableTo": "exitNodes",
|
|
||||||
"columnsFrom": [
|
|
||||||
"exitNodeId"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"exitNodeId"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"sites": {
|
|
||||||
"name": "sites",
|
|
||||||
"columns": {
|
|
||||||
"siteId": {
|
|
||||||
"name": "siteId",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": true
|
|
||||||
},
|
|
||||||
"orgId": {
|
|
||||||
"name": "orgId",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"exitNode": {
|
|
||||||
"name": "exitNode",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"subdomain": {
|
|
||||||
"name": "subdomain",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"pubKey": {
|
|
||||||
"name": "pubKey",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"subnet": {
|
|
||||||
"name": "subnet",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"sites_orgId_orgs_orgId_fk": {
|
|
||||||
"name": "sites_orgId_orgs_orgId_fk",
|
|
||||||
"tableFrom": "sites",
|
|
||||||
"tableTo": "orgs",
|
|
||||||
"columnsFrom": [
|
|
||||||
"orgId"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"orgId"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"sites_exitNode_exitNodes_exitNodeId_fk": {
|
|
||||||
"name": "sites_exitNode_exitNodes_exitNodeId_fk",
|
|
||||||
"tableFrom": "sites",
|
|
||||||
"tableTo": "exitNodes",
|
|
||||||
"columnsFrom": [
|
|
||||||
"exitNode"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"exitNodeId"
|
|
||||||
],
|
|
||||||
"onDelete": "set null",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"targets": {
|
|
||||||
"name": "targets",
|
|
||||||
"columns": {
|
|
||||||
"targetId": {
|
|
||||||
"name": "targetId",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": true
|
|
||||||
},
|
|
||||||
"resourceId": {
|
|
||||||
"name": "resourceId",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"ip": {
|
|
||||||
"name": "ip",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"method": {
|
|
||||||
"name": "method",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"port": {
|
|
||||||
"name": "port",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"protocol": {
|
|
||||||
"name": "protocol",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"targets_resourceId_resources_resourceId_fk": {
|
|
||||||
"name": "targets_resourceId_resources_resourceId_fk",
|
|
||||||
"tableFrom": "targets",
|
|
||||||
"tableTo": "resources",
|
|
||||||
"columnsFrom": [
|
|
||||||
"resourceId"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"resourceId"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"users": {
|
|
||||||
"name": "users",
|
|
||||||
"columns": {
|
|
||||||
"userId": {
|
|
||||||
"name": "userId",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": true
|
|
||||||
},
|
|
||||||
"orgId": {
|
|
||||||
"name": "orgId",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"name": "email",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"autoincrement": false
|
|
||||||
},
|
|
||||||
"groups": {
|
|
||||||
"name": "groups",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"autoincrement": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"users_orgId_orgs_orgId_fk": {
|
|
||||||
"name": "users_orgId_orgs_orgId_fk",
|
|
||||||
"tableFrom": "users",
|
|
||||||
"tableTo": "orgs",
|
|
||||||
"columnsFrom": [
|
|
||||||
"orgId"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"orgId"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enums": {},
|
|
||||||
"_meta": {
|
|
||||||
"schemas": {},
|
|
||||||
"tables": {},
|
|
||||||
"columns": {}
|
|
||||||
},
|
|
||||||
"internal": {
|
|
||||||
"indexes": {}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
"version": "7",
|
|
||||||
"dialect": "sqlite",
|
|
||||||
"entries": [
|
|
||||||
{
|
|
||||||
"idx": 0,
|
|
||||||
"version": "6",
|
|
||||||
"when": 1727557783608,
|
|
||||||
"tag": "0000_wealthy_captain_midlands",
|
|
||||||
"breakpoints": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
import badger from "./badger/badger";
|
import badger from "./badger/badger";
|
||||||
import gerbil from "./gerbil/gerbil";
|
import gerbil from "./gerbil/gerbil";
|
||||||
|
import { traefikConfigProvider } from "@server/traefik-config-provider";
|
||||||
|
|
||||||
const unauth = Router();
|
const unauth = Router();
|
||||||
|
|
||||||
|
@ -11,4 +12,6 @@ unauth.get("/", (_, res) => {
|
||||||
unauth.use("/badger", badger);
|
unauth.use("/badger", badger);
|
||||||
unauth.use("/gerbil", gerbil);
|
unauth.use("/gerbil", gerbil);
|
||||||
|
|
||||||
|
unauth.get("/traefik-config-provider", traefikConfigProvider);
|
||||||
|
|
||||||
export default unauth;
|
export default unauth;
|
||||||
|
|
52
server/traefik-config-provider/configSchema.ts
Normal file
52
server/traefik-config-provider/configSchema.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
export type DynamicTraefikConfig = {
|
||||||
|
http: Http;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Http = {
|
||||||
|
routers: Routers;
|
||||||
|
services: Services;
|
||||||
|
middlewares: Middlewares;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Routers = {
|
||||||
|
[key: string]: Router;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Router = {
|
||||||
|
entryPoints: string[];
|
||||||
|
middlewares: string[];
|
||||||
|
service: string;
|
||||||
|
rule: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Services = {
|
||||||
|
[key: string]: Service;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Service = {
|
||||||
|
loadBalancer: LoadBalancer;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LoadBalancer = {
|
||||||
|
servers: Server[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Server = {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Middlewares = {
|
||||||
|
[key: string]: MiddlewarePlugin;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MiddlewarePlugin = {
|
||||||
|
plugin: Plugin;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Plugin = {
|
||||||
|
[key: string]: MiddlewarePluginConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MiddlewarePluginConfig = {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
70
server/traefik-config-provider/index.ts
Normal file
70
server/traefik-config-provider/index.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import { Request, Response } from "express";
|
||||||
|
import db from "@server/db";
|
||||||
|
import * as schema from "@server/db/schema";
|
||||||
|
import { DynamicTraefikConfig } from "./configSchema";
|
||||||
|
import { like } from "drizzle-orm";
|
||||||
|
import logger from "@server/logger";
|
||||||
|
|
||||||
|
export async function traefikConfigProvider(_: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
const targets = await getAllTargets();
|
||||||
|
const traefikConfig = buildTraefikConfig(targets);
|
||||||
|
res.status(200).send(traefikConfig);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(`Failed to build traefik config: ${e}`);
|
||||||
|
res.status(500).send({ message: "Failed to build traefik config" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildTraefikConfig(
|
||||||
|
targets: schema.Target[],
|
||||||
|
): DynamicTraefikConfig {
|
||||||
|
const middlewareName = "gerbil";
|
||||||
|
|
||||||
|
const http: DynamicTraefikConfig["http"] = {
|
||||||
|
routers: {},
|
||||||
|
services: {},
|
||||||
|
middlewares: {
|
||||||
|
[middlewareName]: {
|
||||||
|
plugin: {
|
||||||
|
[middlewareName]: {
|
||||||
|
// These are temporary values
|
||||||
|
APIEndpoint:
|
||||||
|
"http://host.docker.internal:3001/api/v1/gerbil",
|
||||||
|
ValidToken: "abc123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const target of targets) {
|
||||||
|
const routerName = `router-${target.targetId}`;
|
||||||
|
const serviceName = `service-${target.targetId}`;
|
||||||
|
|
||||||
|
http.routers[routerName] = {
|
||||||
|
entryPoints: [target.method],
|
||||||
|
middlewares: [middlewareName],
|
||||||
|
service: serviceName,
|
||||||
|
rule: `Host(\`${target.resourceId}\`)`, // assuming resourceId is a valid full hostname
|
||||||
|
};
|
||||||
|
|
||||||
|
http.services[serviceName] = {
|
||||||
|
loadBalancer: {
|
||||||
|
servers: [
|
||||||
|
{ url: `${target.method}://${target.ip}:${target.port}` },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { http } as DynamicTraefikConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllTargets(): Promise<schema.Target[]> {
|
||||||
|
const all = await db
|
||||||
|
.select()
|
||||||
|
.from(schema.targets)
|
||||||
|
.where(like(schema.targets.resourceId, "%.%")); // any resourceId with a dot is a valid hostname; otherwise it's a UUID placeholder
|
||||||
|
return all;
|
||||||
|
}
|
Loading…
Reference in a new issue