Compare commits

...

4 Commits

Author SHA1 Message Date
Milo Schwartz
722b877ea5 Merge pull request #125 from fosrl/dev
Hotfix Various Bugs
2025-01-31 15:11:48 -05:00
Owen Schwartz
a9477d7eb9 Complex filter generating config; Resolves #124 2025-01-31 15:07:28 -05:00
Milo Schwartz
bb5573a8f4 allow comma in password closes #121 2025-01-31 15:03:36 -05:00
Owen Schwartz
57cd776c34 Fix migrations ordering 2025-01-30 23:30:33 -05:00
4 changed files with 88 additions and 55 deletions

View File

@@ -4,7 +4,7 @@ export const passwordSchema = z
.string() .string()
.min(8, { message: "Password must be at least 8 characters long" }) .min(8, { message: "Password must be at least 8 characters long" })
.max(64, { message: "Password must be at most 64 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: message: `Your password must meet the following conditions:
at least one uppercase English letter, at least one uppercase English letter,
at least one lowercase English letter, at least one lowercase English letter,

View File

@@ -118,14 +118,6 @@ export async function traefikConfigProvider(
continue; continue;
} }
if (
targets.filter(
(target: Target) => target.internalPort != null
).length == 0
) {
continue;
}
// add routers and services empty objects if they don't exist // add routers and services empty objects if they don't exist
if (!config_output.http.routers) { if (!config_output.http.routers) {
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] = { config_output.http.routers![routerName] = {
entryPoints: [ entryPoints: [
@@ -164,7 +157,10 @@ export async function traefikConfigProvider(
? config.getRawConfig().traefik.https_entrypoint ? config.getRawConfig().traefik.https_entrypoint
: config.getRawConfig().traefik.http_entrypoint : config.getRawConfig().traefik.http_entrypoint
], ],
middlewares: [badgerMiddlewareName, ...additionalMiddlewares], middlewares: [
badgerMiddlewareName,
...additionalMiddlewares
],
service: serviceName, service: serviceName,
rule: `Host(\`${fullDomain}\`)`, rule: `Host(\`${fullDomain}\`)`,
...(resource.ssl ? { tls } : {}) ...(resource.ssl ? { tls } : {})
@@ -184,9 +180,31 @@ export async function traefikConfigProvider(
config_output.http.services![serviceName] = { config_output.http.services![serviceName] = {
loadBalancer: { loadBalancer: {
servers: targets servers: targets
.filter( .filter((target: Target) => {
(target: Target) => target.internalPort != null 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) => { .map((target: Target) => {
if ( if (
site.type === "local" || site.type === "local" ||
@@ -213,14 +231,6 @@ export async function traefikConfigProvider(
continue; continue;
} }
if (
targets.filter(
(target: Target) => target.internalPort != null
).length == 0
) {
continue;
}
if (!config_output[protocol]) { if (!config_output[protocol]) {
config_output[protocol] = { config_output[protocol] = {
routers: {}, routers: {},
@@ -237,9 +247,24 @@ export async function traefikConfigProvider(
config_output[protocol].services[serviceName] = { config_output[protocol].services[serviceName] = {
loadBalancer: { loadBalancer: {
servers: targets servers: targets
.filter( .filter((target: Target) => {
(target: Target) => target.internalPort != null 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) => { .map((target: Target) => {
if ( if (
site.type === "local" || site.type === "local" ||
@@ -261,9 +286,9 @@ export async function traefikConfigProvider(
} }
return res.status(HttpCode.OK).json(config_output); return res.status(HttpCode.OK).json(config_output);
} catch (e) { } 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({ return res.status(HttpCode.INTERNAL_SERVER_ERROR).json({
error: "Failed to build traefik config" error: "Failed to build Traefik config"
}); });
} }
} }

View File

@@ -3,9 +3,9 @@ import db, { exists } from "@server/db";
import path from "path"; import path from "path";
import semver from "semver"; import semver from "semver";
import { versionMigrations } from "@server/db/schema"; import { versionMigrations } from "@server/db/schema";
import { desc } from "drizzle-orm";
import { __DIRNAME } from "@server/lib/consts"; import { __DIRNAME } from "@server/lib/consts";
import { loadAppVersion } from "@server/lib/loadAppVersion"; import { loadAppVersion } from "@server/lib/loadAppVersion";
import { SqliteError } from "better-sqlite3";
import m1 from "./scripts/1.0.0-beta1"; import m1 from "./scripts/1.0.0-beta1";
import m2 from "./scripts/1.0.0-beta2"; import m2 from "./scripts/1.0.0-beta2";
import m3 from "./scripts/1.0.0-beta3"; import m3 from "./scripts/1.0.0-beta3";
@@ -53,38 +53,44 @@ export async function runMigrations() {
} }
await db await db
.insert(versionMigrations) .insert(versionMigrations)
.values({ .values({
version: appVersion, version: appVersion,
executedAt: Date.now() executedAt: Date.now()
}) })
.execute(); .execute();
} }
} catch (e) { } catch (e) {
console.error("Error running migrations:", 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() { async function executeScripts() {
try { try {
// Get the last executed version from the database // Get the last executed version from the database
const lastExecuted = await db const lastExecuted = await db.select().from(versionMigrations);
.select()
.from(versionMigrations)
.orderBy(desc(versionMigrations.version))
.limit(1);
const startVersion = lastExecuted[0]?.version ?? "0.0.0";
console.log(`Starting migrations from version ${startVersion}`);
// Filter and sort migrations // Filter and sort migrations
const pendingMigrations = migrations const pendingMigrations = lastExecuted
.filter((migration) => semver.gt(migration.version, startVersion)) .map((m) => m)
.sort((a, b) => semver.compare(a.version, b.version)); .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 // Run migrations in order
for (const migration of pendingMigrations) { for (const migration of migrationsToRun) {
console.log(`Running migration ${migration.version}`); console.log(`Running migration ${migration.version}`);
try { try {
@@ -102,12 +108,16 @@ async function executeScripts() {
console.log( console.log(
`Successfully completed migration ${migration.version}` `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( console.error(
`Failed to run migration ${migration.version}:`, `Failed to run migration ${migration.version}:`,
error e
); );
throw error; // Re-throw to stop migration process throw e; // Re-throw to stop migration process
} }
} }

View File

@@ -164,8 +164,6 @@ export default function CreateResourceForm({
}, [open]); }, [open]);
async function onSubmit(data: CreateResourceFormValues) { async function onSubmit(data: CreateResourceFormValues) {
console.log(data);
const res = await api const res = await api
.put<AxiosResponse<Resource>>( .put<AxiosResponse<Resource>>(
`/org/${orgId}/site/${data.siteId}/resource/`, `/org/${orgId}/site/${data.siteId}/resource/`,
@@ -194,16 +192,16 @@ export default function CreateResourceForm({
setResourceId(id); setResourceId(id);
if (data.http) { if (data.http) {
goToResource(); goToResource(id);
} else { } else {
setShowSnippets(true); setShowSnippets(true);
} }
} }
} }
function goToResource() { function goToResource(id?: number) {
// navigate to the resource page // navigate to the resource page
router.push(`/${orgId}/settings/resources/${resourceId}`); router.push(`/${orgId}/settings/resources/${id || resourceId}`);
} }
return ( return (