mirror of
https://github.com/fosrl/pangolin.git
synced 2025-08-31 06:59:33 +02:00
sync config managed domains to db
This commit is contained in:
parent
abcc99a830
commit
e41c0c9d7c
3 changed files with 106 additions and 39 deletions
|
@ -2,21 +2,23 @@ import { InferSelectModel } from "drizzle-orm";
|
||||||
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
||||||
|
|
||||||
export const domains = sqliteTable("domains", {
|
export const domains = sqliteTable("domains", {
|
||||||
domainId: integer("domainId").primaryKey({ autoIncrement: true }),
|
domainId: text("domainId").primaryKey(),
|
||||||
baseDomain: text("domain").notNull().unique()
|
baseDomain: text("baseDomain").notNull().unique(),
|
||||||
|
configManaged: integer("configManaged", { mode: "boolean" })
|
||||||
|
.notNull()
|
||||||
|
.default(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const orgs = sqliteTable("orgs", {
|
export const orgs = sqliteTable("orgs", {
|
||||||
orgId: text("orgId").primaryKey(),
|
orgId: text("orgId").primaryKey(),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull()
|
||||||
domain: text("domain").notNull()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const orgDomains = sqliteTable("orgDomains", {
|
export const orgDomains = sqliteTable("orgDomains", {
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
domainId: integer("domainId")
|
domainId: text("domainId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => domains.domainId, { onDelete: "cascade" })
|
.references(() => domains.domainId, { onDelete: "cascade" })
|
||||||
});
|
});
|
||||||
|
@ -57,7 +59,7 @@ export const resources = sqliteTable("resources", {
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
subdomain: text("subdomain"),
|
subdomain: text("subdomain"),
|
||||||
fullDomain: text("fullDomain"),
|
fullDomain: text("fullDomain"),
|
||||||
domainId: integer("domainId").references(() => domains.domainId, {
|
domainId: text("domainId").references(() => domains.domainId, {
|
||||||
onDelete: "set null"
|
onDelete: "set null"
|
||||||
}),
|
}),
|
||||||
ssl: integer("ssl", { mode: "boolean" }).notNull().default(false),
|
ssl: integer("ssl", { mode: "boolean" }).notNull().default(false),
|
||||||
|
|
|
@ -38,22 +38,11 @@ const configSchema = z.object({
|
||||||
save_logs: z.boolean(),
|
save_logs: z.boolean(),
|
||||||
log_failed_attempts: z.boolean().optional()
|
log_failed_attempts: z.boolean().optional()
|
||||||
}),
|
}),
|
||||||
domains: z
|
domains: z.record(
|
||||||
.array(
|
z.string(),
|
||||||
z.object({
|
z.object({
|
||||||
base_domain: hostnameSchema.transform((url) =>
|
base_domain: hostnameSchema.transform((url) => url.toLowerCase())
|
||||||
url.toLowerCase()
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
)
|
|
||||||
.refine(
|
|
||||||
(data) => {
|
|
||||||
const baseDomains = data.map((d) => d.base_domain);
|
|
||||||
return new Set(baseDomains).size === baseDomains.length;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
message: "Base domains must be unique"
|
|
||||||
}
|
|
||||||
),
|
),
|
||||||
server: z.object({
|
server: z.object({
|
||||||
external_port: portSchema
|
external_port: portSchema
|
||||||
|
|
|
@ -1,40 +1,116 @@
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { exitNodes, orgs, resources } from "../db/schema";
|
import { domains, exitNodes, orgDomains, orgs, resources } from "../db/schema";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
import { eq, ne } from "drizzle-orm";
|
import { eq, ne } from "drizzle-orm";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
|
|
||||||
export async function copyInConfig() {
|
export async function copyInConfig() {
|
||||||
const domain = config.getBaseDomain();
|
|
||||||
const endpoint = config.getRawConfig().gerbil.base_endpoint;
|
const endpoint = config.getRawConfig().gerbil.base_endpoint;
|
||||||
const listenPort = config.getRawConfig().gerbil.start_port;
|
const listenPort = config.getRawConfig().gerbil.start_port;
|
||||||
|
|
||||||
// update the domain on all of the orgs where the domain is not equal to the new domain
|
|
||||||
// TODO: eventually each org could have a unique domain that we do not want to overwrite, so this will be unnecessary
|
|
||||||
await db.update(orgs).set({ domain }).where(ne(orgs.domain, domain));
|
|
||||||
|
|
||||||
// TODO: eventually each exit node could have a different endpoint
|
|
||||||
await db.update(exitNodes).set({ endpoint }).where(ne(exitNodes.endpoint, endpoint));
|
|
||||||
// TODO: eventually each exit node could have a different port
|
|
||||||
await db.update(exitNodes).set({ listenPort }).where(ne(exitNodes.listenPort, listenPort));
|
|
||||||
|
|
||||||
// update all resources fullDomain to use the new domain
|
|
||||||
await db.transaction(async (trx) => {
|
await db.transaction(async (trx) => {
|
||||||
const allResources = await trx.select().from(resources);
|
const rawDomains = config.getRawConfig().domains;
|
||||||
|
|
||||||
|
const configDomains = Object.entries(rawDomains).map(
|
||||||
|
([key, value]) => ({
|
||||||
|
domainId: key,
|
||||||
|
baseDomain: value.base_domain.toLowerCase()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const existingDomains = await trx
|
||||||
|
.select()
|
||||||
|
.from(domains)
|
||||||
|
.where(eq(domains.configManaged, true));
|
||||||
|
const existingDomainKeys = new Set(
|
||||||
|
existingDomains.map((d) => d.domainId)
|
||||||
|
);
|
||||||
|
|
||||||
|
const configDomainKeys = new Set(configDomains.map((d) => d.domainId));
|
||||||
|
for (const existingDomain of existingDomains) {
|
||||||
|
if (!configDomainKeys.has(existingDomain.domainId)) {
|
||||||
|
await trx
|
||||||
|
.delete(domains)
|
||||||
|
.where(eq(domains.domainId, existingDomain.domainId))
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const { domainId, baseDomain } of configDomains) {
|
||||||
|
if (existingDomainKeys.has(domainId)) {
|
||||||
|
await trx
|
||||||
|
.update(domains)
|
||||||
|
.set({ baseDomain })
|
||||||
|
.where(eq(domains.domainId, domainId))
|
||||||
|
.execute();
|
||||||
|
} else {
|
||||||
|
await trx
|
||||||
|
.insert(domains)
|
||||||
|
.values({ domainId, baseDomain, configManaged: true })
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const allResources = await trx
|
||||||
|
.select()
|
||||||
|
.from(resources)
|
||||||
|
.leftJoin(domains, eq(domains.domainId, resources.domainId));
|
||||||
|
|
||||||
|
for (const { resources: resource, domains: domain } of allResources) {
|
||||||
|
if (!resource || !domain) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!domain.configManaged) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (const resource of allResources) {
|
|
||||||
let fullDomain = "";
|
let fullDomain = "";
|
||||||
if (resource.isBaseDomain) {
|
if (resource.isBaseDomain) {
|
||||||
fullDomain = domain;
|
fullDomain = domain.baseDomain;
|
||||||
} else {
|
} else {
|
||||||
fullDomain = `${resource.subdomain}.${domain}`;
|
fullDomain = `${resource.subdomain}.${domain}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
await trx
|
await trx
|
||||||
.update(resources)
|
.update(resources)
|
||||||
.set({ fullDomain })
|
.set({ fullDomain })
|
||||||
.where(eq(resources.resourceId, resource.resourceId));
|
.where(eq(resources.resourceId, resource.resourceId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const allOrgs = await trx.select().from(orgs);
|
||||||
|
|
||||||
|
const existingOrgDomains = await trx.select().from(orgDomains);
|
||||||
|
const existingOrgDomainSet = new Set(
|
||||||
|
existingOrgDomains.map((od) => `${od.orgId}-${od.domainId}`)
|
||||||
|
);
|
||||||
|
|
||||||
|
const newOrgDomains = [];
|
||||||
|
for (const org of allOrgs) {
|
||||||
|
for (const domain of configDomains) {
|
||||||
|
const key = `${org.orgId}-${domain.domainId}`;
|
||||||
|
if (!existingOrgDomainSet.has(key)) {
|
||||||
|
newOrgDomains.push({
|
||||||
|
orgId: org.orgId,
|
||||||
|
domainId: domain.domainId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newOrgDomains.length > 0) {
|
||||||
|
await trx.insert(orgDomains).values(newOrgDomains).execute();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info(`Updated orgs with new domain (${domain})`);
|
// TODO: eventually each exit node could have a different endpoint
|
||||||
|
await db
|
||||||
|
.update(exitNodes)
|
||||||
|
.set({ endpoint })
|
||||||
|
.where(ne(exitNodes.endpoint, endpoint));
|
||||||
|
// TODO: eventually each exit node could have a different port
|
||||||
|
await db
|
||||||
|
.update(exitNodes)
|
||||||
|
.set({ listenPort })
|
||||||
|
.where(ne(exitNodes.listenPort, listenPort));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue