support postgresql as database option

This commit is contained in:
miloschwartz 2025-06-04 12:02:07 -04:00
parent 62a0104e70
commit 2cca561e51
No known key found for this signature in database
218 changed files with 1417 additions and 713 deletions

View file

@ -8,7 +8,7 @@ import {
resourceSessions,
sessions,
userInvites
} from "@server/db/schemas";
} from "@server/db";
import logger from "@server/logger";
import { lt } from "drizzle-orm";

View file

@ -1,5 +1,5 @@
import { db } from "@server/db";
import { domains, exitNodes, orgDomains, orgs, resources } from "../db/schemas/schema";
import { domains, exitNodes, orgDomains, orgs, resources } from "@server/db";
import config from "@server/lib/config";
import { eq, ne } from "drizzle-orm";
import logger from "@server/logger";

View file

@ -1,6 +1,6 @@
import { ActionsEnum } from "@server/auth/actions";
import { db } from "@server/db";
import { actions, roles, roleActions } from "../db/schemas/schema";
import { actions, roles, roleActions } from "@server/db";
import { eq, inArray } from "drizzle-orm";
import logger from "@server/logger";
@ -22,85 +22,37 @@ export async function ensureActions() {
.where(eq(roles.isAdmin, true))
.execute();
await db.transaction(async (trx) => {
await db.transaction(async (trx) => {
// Add new actions
for (const actionId of actionsToAdd) {
logger.debug(`Adding action: ${actionId}`);
await trx.insert(actions).values({ actionId }).execute();
// Add new actions to the Default role
if (defaultRoles.length != 0) {
await trx
.insert(roleActions)
.values(
defaultRoles.map((role) => ({
roleId: role.roleId!,
actionId,
orgId: role.orgId!
}))
)
.execute();
}
}
// Add new actions
for (const actionId of actionsToAdd) {
logger.debug(`Adding action: ${actionId}`);
await trx.insert(actions).values({ actionId }).execute();
// Add new actions to the Default role
if (defaultRoles.length != 0) {
// Remove deprecated actions
if (actionsToRemove.length > 0) {
logger.debug(`Removing actions: ${actionsToRemove.join(", ")}`);
await trx
.insert(roleActions)
.values(
defaultRoles.map((role) => ({
roleId: role.roleId!,
actionId,
orgId: role.orgId!
}))
)
.delete(actions)
.where(inArray(actions.actionId, actionsToRemove))
.execute();
await trx
.delete(roleActions)
.where(inArray(roleActions.actionId, actionsToRemove))
.execute();
}
}
// Remove deprecated actions
if (actionsToRemove.length > 0) {
logger.debug(`Removing actions: ${actionsToRemove.join(", ")}`);
await trx
.delete(actions)
.where(inArray(actions.actionId, actionsToRemove))
.execute();
await trx
.delete(roleActions)
.where(inArray(roleActions.actionId, actionsToRemove))
.execute();
}
});
}
export async function createAdminRole(orgId: string) {
let roleId: any;
await db.transaction(async (trx) => {
const [insertedRole] = await trx
.insert(roles)
.values({
orgId,
isAdmin: true,
name: "Admin",
description: "Admin role with the most permissions"
})
.returning({ roleId: roles.roleId })
.execute();
if (!insertedRole || !insertedRole.roleId) {
throw new Error("Failed to create Admin role");
}
roleId = insertedRole.roleId;
const actionIds = await trx.select().from(actions).execute();
if (actionIds.length === 0) {
logger.info("No actions to assign to the Admin role");
return;
}
await trx
.insert(roleActions)
.values(
actionIds.map((action) => ({
roleId,
actionId: action.actionId,
orgId
}))
)
.execute();
});
if (!roleId) {
throw new Error("Failed to create Admin role");
}
return roleId;
}

View file

@ -0,0 +1,133 @@
import { migrate } from "drizzle-orm/node-postgres/migrator";
import { db } from "../db/pg";
import semver from "semver";
import { versionMigrations } from "../db/pg";
import { __DIRNAME, APP_PATH, APP_VERSION } from "@server/lib/consts";
import path from "path";
// THIS CANNOT IMPORT ANYTHING FROM THE SERVER
// EXCEPT FOR THE DATABASE AND THE SCHEMA
// Define the migration list with versions and their corresponding functions
const migrations = [
// Add new migrations here as they are created
] as {
version: string;
run: () => Promise<void>;
}[];
await run();
async function run() {
// run the migrations
await runMigrations();
}
export async function runMigrations() {
try {
const appVersion = APP_VERSION;
// determine if the migrations table exists
const exists = await db
.select()
.from(versionMigrations)
.limit(1)
.execute()
.then((res) => res.length > 0)
.catch(() => false);
if (exists) {
console.log("Migrations table exists, running scripts...");
await executeScripts();
} else {
console.log("Migrations table does not exist, creating it...");
console.log("Running migrations...");
try {
await migrate(db, {
migrationsFolder: path.join(__DIRNAME, "init") // put here during the docker build
});
console.log("Migrations completed successfully.");
} catch (error) {
console.error("Error running migrations:", error);
}
await db
.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)
);
}
}
async function executeScripts() {
try {
// Get the last executed version from the database
const lastExecuted = await db.select().from(versionMigrations);
// Filter and sort migrations
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 migrationsToRun) {
console.log(`Running migration ${migration.version}`);
try {
await migration.run();
// Update version in database
await db
.insert(versionMigrations)
.values({
version: migration.version,
executedAt: Date.now()
})
.execute();
console.log(
`Successfully completed migration ${migration.version}`
);
} catch (e) {
if (
e instanceof Error &&
typeof (e as any).code === "string" &&
(e as any).code === "23505"
) {
console.error("Migration has already run! Skipping...");
continue; // or return, depending on context
}
console.error(
`Failed to run migration ${migration.version}:`,
e
);
throw e;
}
}
console.log("All migrations completed successfully");
} catch (error) {
console.error("Migration process failed:", error);
throw error;
}
}

View file

@ -1,25 +1,25 @@
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
import db, { exists } from "@server/db";
import { db, exists } from "../db/sqlite";
import path from "path";
import semver from "semver";
import { versionMigrations } from "@server/db/schemas";
import { versionMigrations } from "../db/sqlite";
import { __DIRNAME, APP_PATH, APP_VERSION } from "@server/lib/consts";
import { SqliteError } from "better-sqlite3";
import fs from "fs";
import m1 from "./scripts/1.0.0-beta1";
import m2 from "./scripts/1.0.0-beta2";
import m3 from "./scripts/1.0.0-beta3";
import m4 from "./scripts/1.0.0-beta5";
import m5 from "./scripts/1.0.0-beta6";
import m6 from "./scripts/1.0.0-beta9";
import m7 from "./scripts/1.0.0-beta10";
import m8 from "./scripts/1.0.0-beta12";
import m13 from "./scripts/1.0.0-beta13";
import m15 from "./scripts/1.0.0-beta15";
import m16 from "./scripts/1.0.0";
import m17 from "./scripts/1.1.0";
import m18 from "./scripts/1.2.0";
import m19 from "./scripts/1.3.0";
import m1 from "./scriptsSqlite/1.0.0-beta1";
import m2 from "./scriptsSqlite/1.0.0-beta2";
import m3 from "./scriptsSqlite/1.0.0-beta3";
import m4 from "./scriptsSqlite/1.0.0-beta5";
import m5 from "./scriptsSqlite/1.0.0-beta6";
import m6 from "./scriptsSqlite/1.0.0-beta9";
import m7 from "./scriptsSqlite/1.0.0-beta10";
import m8 from "./scriptsSqlite/1.0.0-beta12";
import m13 from "./scriptsSqlite/1.0.0-beta13";
import m15 from "./scriptsSqlite/1.0.0-beta15";
import m16 from "./scriptsSqlite/1.0.0";
import m17 from "./scriptsSqlite/1.1.0";
import m18 from "./scriptsSqlite/1.2.0";
import m19 from "./scriptsSqlite/1.3.0";
import { setHostMeta } from "./setHostMeta";
// THIS CANNOT IMPORT ANYTHING FROM THE SERVER

View file

@ -1,4 +1,4 @@
import db from "@server/db";
import { db } from "../../db/sqlite";
import { configFilePath1, configFilePath2 } from "@server/lib/consts";
import { sql } from "drizzle-orm";
import fs from "fs";

View file

@ -1,4 +1,4 @@
import db from "@server/db";
import { db } from "../../db/sqlite";
import { sql } from "drizzle-orm";
const version = "1.0.0-beta.13";

View file

@ -1,9 +1,9 @@
import db from "@server/db";
import { db } from "../../db/sqlite";
import { configFilePath1, configFilePath2 } from "@server/lib/consts";
import fs from "fs";
import yaml from "js-yaml";
import { sql } from "drizzle-orm";
import { domains, orgDomains, resources } from "@server/db/schemas";
import { domains, orgDomains, resources } from "@server/db";
const version = "1.0.0-beta.15";

View file

@ -1,4 +1,4 @@
import db from "@server/db";
import { db } from "../../db/sqlite";
import {
emailVerificationCodes,
passwordResetTokens,
@ -8,7 +8,7 @@ import {
targets,
userInvites,
users
} from "@server/db/schemas";
} from "../../db/sqlite";
import { APP_PATH, configFilePath1, configFilePath2 } from "@server/lib/consts";
import { eq, sql } from "drizzle-orm";
import fs from "fs";

View file

@ -1,4 +1,4 @@
import db from "@server/db";
import { db } from "../../db/sqlite";
import { sql } from "drizzle-orm";
const version = "1.1.0";

View file

@ -1,4 +1,4 @@
import db from "@server/db";
import { db } from "../../db/sqlite";
import { APP_PATH, configFilePath1, configFilePath2 } from "@server/lib/consts";
import { sql } from "drizzle-orm";
import fs from "fs";

View file

@ -1,5 +1,5 @@
import db from "@server/db";
import { hostMeta } from "@server/db/schemas";
import { db } from "@server/db";
import { hostMeta } from "@server/db";
import { v4 as uuidv4 } from "uuid";
export async function setHostMeta() {

View file

@ -1,8 +1,8 @@
import { generateId, invalidateAllSessions } from "@server/auth/sessions/app";
import { hashPassword, verifyPassword } from "@server/auth/password";
import config from "@server/lib/config";
import db from "@server/db";
import { users } from "@server/db/schemas";
import { db } from "@server/db";
import { users } from "@server/db";
import logger from "@server/logger";
import { eq } from "drizzle-orm";
import moment from "moment";