diff --git a/server/db/schema.ts b/server/db/schema.ts index 3380cdbf..a583e4ad 100644 --- a/server/db/schema.ts +++ b/server/db/schema.ts @@ -1,12 +1,26 @@ import { InferSelectModel } from "drizzle-orm"; import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"; +export const domains = sqliteTable("domains", { + domainId: integer("domainId").primaryKey({ autoIncrement: true }), + baseDomain: text("domain").notNull().unique() +}); + export const orgs = sqliteTable("orgs", { orgId: text("orgId").primaryKey(), name: text("name").notNull(), domain: text("domain").notNull() }); +export const orgDomains = sqliteTable("orgDomains", { + orgId: text("orgId") + .notNull() + .references(() => orgs.orgId, { onDelete: "cascade" }), + domainId: integer("domainId") + .notNull() + .references(() => domains.domainId, { onDelete: "cascade" }) +}); + export const sites = sqliteTable("sites", { siteId: integer("siteId").primaryKey({ autoIncrement: true }), orgId: text("orgId") @@ -43,6 +57,9 @@ export const resources = sqliteTable("resources", { name: text("name").notNull(), subdomain: text("subdomain"), fullDomain: text("fullDomain"), + domainId: integer("domainId").references(() => domains.domainId, { + onDelete: "set null" + }), ssl: integer("ssl", { mode: "boolean" }).notNull().default(false), blockAccess: integer("blockAccess", { mode: "boolean" }) .notNull() @@ -55,7 +72,9 @@ export const resources = sqliteTable("resources", { .notNull() .default(false), isBaseDomain: integer("isBaseDomain", { mode: "boolean" }), - applyRules: integer("applyRules", { mode: "boolean" }).notNull().default(false) + applyRules: integer("applyRules", { mode: "boolean" }) + .notNull() + .default(false) }); export const targets = sqliteTable("targets", { diff --git a/server/lib/config.ts b/server/lib/config.ts index 7c5ad227..bf1f17dc 100644 --- a/server/lib/config.ts +++ b/server/lib/config.ts @@ -34,15 +34,27 @@ const configSchema = z.object({ .transform(getEnvOrYaml("APP_DASHBOARDURL")) .pipe(z.string().url()) .transform((url) => url.toLowerCase()), - base_domain: hostnameSchema - .optional() - .transform(getEnvOrYaml("APP_BASEDOMAIN")) - .pipe(hostnameSchema) - .transform((url) => url.toLowerCase()), log_level: z.enum(["debug", "info", "warn", "error"]), save_logs: z.boolean(), log_failed_attempts: z.boolean().optional() }), + domains: z + .array( + z.object({ + base_domain: hostnameSchema.transform((url) => + 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({ external_port: portSchema .optional() @@ -283,10 +295,6 @@ export class Config { return this.rawConfig; } - public getBaseDomain(): string { - return this.rawConfig.app.base_domain; - } - public getNoReplyEmail(): string | undefined { return ( this.rawConfig.email?.no_reply || this.rawConfig.email?.smtp_user