From 57cd776c340fd4899654059cb33a5c8c3d25557a Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Thu, 30 Jan 2025 23:30:33 -0500 Subject: [PATCH 1/3] Fix migrations ordering --- server/setup/migrations.ts | 56 ++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/server/setup/migrations.ts b/server/setup/migrations.ts index 2f1aae21..b06f176c 100644 --- a/server/setup/migrations.ts +++ b/server/setup/migrations.ts @@ -3,9 +3,9 @@ import db, { exists } from "@server/db"; import path from "path"; import semver from "semver"; import { versionMigrations } from "@server/db/schema"; -import { desc } from "drizzle-orm"; import { __DIRNAME } from "@server/lib/consts"; import { loadAppVersion } from "@server/lib/loadAppVersion"; +import { SqliteError } from "better-sqlite3"; import m1 from "./scripts/1.0.0-beta1"; import m2 from "./scripts/1.0.0-beta2"; import m3 from "./scripts/1.0.0-beta3"; @@ -53,38 +53,44 @@ export async function runMigrations() { } await db - .insert(versionMigrations) - .values({ - version: appVersion, - executedAt: Date.now() - }) - .execute(); + .insert(versionMigrations) + .values({ + version: appVersion, + executedAt: Date.now() + }) + .execute(); } } catch (e) { console.error("Error running migrations:", e); - await new Promise((resolve) => setTimeout(resolve, 1000 * 60 * 60 * 24 * 1)); + await new Promise((resolve) => + setTimeout(resolve, 1000 * 60 * 60 * 24 * 1) + ); } } async function executeScripts() { try { // Get the last executed version from the database - const lastExecuted = await db - .select() - .from(versionMigrations) - .orderBy(desc(versionMigrations.version)) - .limit(1); - - const startVersion = lastExecuted[0]?.version ?? "0.0.0"; - console.log(`Starting migrations from version ${startVersion}`); + const lastExecuted = await db.select().from(versionMigrations); // Filter and sort migrations - const pendingMigrations = migrations - .filter((migration) => semver.gt(migration.version, startVersion)) - .sort((a, b) => semver.compare(a.version, b.version)); + const pendingMigrations = lastExecuted + .map((m) => m) + .sort((a, b) => semver.compare(b.version, a.version)); + const startVersion = pendingMigrations[0]?.version ?? "0.0.0"; + console.log(`Starting migrations from version ${startVersion}`); + + const migrationsToRun = migrations.filter((migration) => + semver.gt(migration.version, startVersion) + ); + + console.log( + "Migrations to run:", + migrationsToRun.map((m) => m.version).join(", ") + ); // Run migrations in order - for (const migration of pendingMigrations) { + for (const migration of migrationsToRun) { console.log(`Running migration ${migration.version}`); try { @@ -102,12 +108,16 @@ async function executeScripts() { console.log( `Successfully completed migration ${migration.version}` ); - } catch (error) { + } catch (e) { + if (e instanceof SqliteError && e.code === "SQLITE_CONSTRAINT_UNIQUE") { + console.error("Migration has already run! Skipping..."); + continue; + } console.error( `Failed to run migration ${migration.version}:`, - error + e ); - throw error; // Re-throw to stop migration process + throw e; // Re-throw to stop migration process } } From bb5573a8f485eab92967d0920eec16aa5b9afd2a Mon Sep 17 00:00:00 2001 From: Milo Schwartz Date: Fri, 31 Jan 2025 15:03:36 -0500 Subject: [PATCH 2/3] allow comma in password closes #121 --- server/auth/passwordSchema.ts | 2 +- src/app/[orgId]/settings/resources/CreateResourceForm.tsx | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/server/auth/passwordSchema.ts b/server/auth/passwordSchema.ts index f4030dee..5554b741 100644 --- a/server/auth/passwordSchema.ts +++ b/server/auth/passwordSchema.ts @@ -4,7 +4,7 @@ export const passwordSchema = z .string() .min(8, { message: "Password must be at least 8 characters long" }) .max(64, { message: "Password must be at most 64 characters long" }) - .regex(/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).*$/, { + .regex(/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[,#?!@$%^&*-]).*$/, { message: `Your password must meet the following conditions: at least one uppercase English letter, at least one lowercase English letter, diff --git a/src/app/[orgId]/settings/resources/CreateResourceForm.tsx b/src/app/[orgId]/settings/resources/CreateResourceForm.tsx index 11f215d4..abe5608c 100644 --- a/src/app/[orgId]/settings/resources/CreateResourceForm.tsx +++ b/src/app/[orgId]/settings/resources/CreateResourceForm.tsx @@ -164,8 +164,6 @@ export default function CreateResourceForm({ }, [open]); async function onSubmit(data: CreateResourceFormValues) { - console.log(data); - const res = await api .put>( `/org/${orgId}/site/${data.siteId}/resource/`, @@ -194,16 +192,16 @@ export default function CreateResourceForm({ setResourceId(id); if (data.http) { - goToResource(); + goToResource(id); } else { setShowSnippets(true); } } } - function goToResource() { + function goToResource(id?: number) { // navigate to the resource page - router.push(`/${orgId}/settings/resources/${resourceId}`); + router.push(`/${orgId}/settings/resources/${id || resourceId}`); } return ( From a9477d7eb93e8d87e1dd0dd5c9026de06e8f90eb Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 31 Jan 2025 15:06:25 -0500 Subject: [PATCH 3/3] Complex filter generating config; Resolves #124 --- server/routers/traefik/getTraefikConfig.ts | 77 ++++++++++++++-------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/server/routers/traefik/getTraefikConfig.ts b/server/routers/traefik/getTraefikConfig.ts index 93eddde4..7c12cdb3 100644 --- a/server/routers/traefik/getTraefikConfig.ts +++ b/server/routers/traefik/getTraefikConfig.ts @@ -118,14 +118,6 @@ export async function traefikConfigProvider( continue; } - if ( - targets.filter( - (target: Target) => target.internalPort != null - ).length == 0 - ) { - continue; - } - // add routers and services empty objects if they don't exist if (!config_output.http.routers) { config_output.http.routers = {}; @@ -156,7 +148,8 @@ export async function traefikConfigProvider( : {}) }; - const additionalMiddlewares = config.getRawConfig().traefik.additional_middlewares || []; + const additionalMiddlewares = + config.getRawConfig().traefik.additional_middlewares || []; config_output.http.routers![routerName] = { entryPoints: [ @@ -164,7 +157,10 @@ export async function traefikConfigProvider( ? config.getRawConfig().traefik.https_entrypoint : config.getRawConfig().traefik.http_entrypoint ], - middlewares: [badgerMiddlewareName, ...additionalMiddlewares], + middlewares: [ + badgerMiddlewareName, + ...additionalMiddlewares + ], service: serviceName, rule: `Host(\`${fullDomain}\`)`, ...(resource.ssl ? { tls } : {}) @@ -184,9 +180,31 @@ export async function traefikConfigProvider( config_output.http.services![serviceName] = { loadBalancer: { servers: targets - .filter( - (target: Target) => target.internalPort != null - ) + .filter((target: Target) => { + if (!target.enabled) { + return false; + } + if ( + site.type === "local" || + site.type === "wireguard" + ) { + if ( + !target.ip || + !target.port || + !target.method + ) { + return false; + } + } else if (site.type === "newt") { + if ( + !target.internalPort || + !target.method + ) { + return false; + } + } + return true; + }) .map((target: Target) => { if ( site.type === "local" || @@ -213,14 +231,6 @@ export async function traefikConfigProvider( continue; } - if ( - targets.filter( - (target: Target) => target.internalPort != null - ).length == 0 - ) { - continue; - } - if (!config_output[protocol]) { config_output[protocol] = { routers: {}, @@ -237,9 +247,24 @@ export async function traefikConfigProvider( config_output[protocol].services[serviceName] = { loadBalancer: { servers: targets - .filter( - (target: Target) => target.internalPort != null - ) + .filter((target: Target) => { + if (!target.enabled) { + return false; + } + if ( + site.type === "local" || + site.type === "wireguard" + ) { + if (!target.ip || !target.port) { + return false; + } + } else if (site.type === "newt") { + if (!target.internalPort) { + return false; + } + } + return true; + }) .map((target: Target) => { if ( site.type === "local" || @@ -261,9 +286,9 @@ export async function traefikConfigProvider( } return res.status(HttpCode.OK).json(config_output); } catch (e) { - logger.error(`Failed to build traefik config: ${e}`); + logger.error(`Failed to build Traefik config: ${e}`); return res.status(HttpCode.INTERNAL_SERVER_ERROR).json({ - error: "Failed to build traefik config" + error: "Failed to build Traefik config" }); } }