mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-20 20:06:39 +00:00
Merge branch 'dev' into audit-logs
This commit is contained in:
@@ -127,7 +127,7 @@ export const targets = pgTable("targets", {
|
||||
pathMatchType: text("pathMatchType"), // exact, prefix, regex
|
||||
rewritePath: text("rewritePath"), // if set, rewrites the path to this value before sending to the target
|
||||
rewritePathType: text("rewritePathType"), // exact, prefix, regex, stripPrefix
|
||||
priority: integer("priority").default(100)
|
||||
priority: integer("priority").notNull().default(100)
|
||||
});
|
||||
|
||||
export const targetHealthCheck = pgTable("targetHealthCheck", {
|
||||
|
||||
@@ -138,7 +138,7 @@ export const targets = sqliteTable("targets", {
|
||||
pathMatchType: text("pathMatchType"), // exact, prefix, regex
|
||||
rewritePath: text("rewritePath"), // if set, rewrites the path to this value before sending to the target
|
||||
rewritePathType: text("rewritePathType"), // exact, prefix, regex, stripPrefix
|
||||
priority: integer("priority").default(100)
|
||||
priority: integer("priority").notNull().default(100)
|
||||
});
|
||||
|
||||
export const targetHealthCheck = sqliteTable("targetHealthCheck", {
|
||||
|
||||
@@ -88,7 +88,14 @@ export async function getTraefikConfig(
|
||||
and(
|
||||
eq(targets.enabled, true),
|
||||
eq(resources.enabled, true),
|
||||
eq(sites.exitNodeId, exitNodeId),
|
||||
or(
|
||||
eq(sites.exitNodeId, exitNodeId),
|
||||
and(
|
||||
isNull(sites.exitNodeId),
|
||||
sql`(${siteTypes.includes("local") ? 1 : 0} = 1)`, // only allow local sites if "local" is in siteTypes
|
||||
eq(sites.type, "local")
|
||||
)
|
||||
),
|
||||
or(
|
||||
ne(targetHealthCheck.hcHealth, "unhealthy"), // Exclude unhealthy targets
|
||||
isNull(targetHealthCheck.hcHealth) // Include targets with no health check record
|
||||
|
||||
@@ -40,6 +40,7 @@ import {
|
||||
CertificateResult,
|
||||
getValidCertificatesForDomains
|
||||
} from "#private/lib/certificates";
|
||||
import { build } from "@server/build";
|
||||
|
||||
const redirectHttpsMiddlewareName = "redirect-to-https";
|
||||
const redirectToRootMiddlewareName = "redirect-to-root";
|
||||
@@ -120,7 +121,14 @@ export async function getTraefikConfig(
|
||||
and(
|
||||
eq(targets.enabled, true),
|
||||
eq(resources.enabled, true),
|
||||
eq(sites.exitNodeId, exitNodeId),
|
||||
or(
|
||||
eq(sites.exitNodeId, exitNodeId),
|
||||
and(
|
||||
isNull(sites.exitNodeId),
|
||||
sql`(${siteTypes.includes("local") ? 1 : 0} = 1)`, // only allow local sites if "local" is in siteTypes
|
||||
eq(sites.type, "local")
|
||||
)
|
||||
),
|
||||
or(
|
||||
ne(targetHealthCheck.hcHealth, "unhealthy"), // Exclude unhealthy targets
|
||||
isNull(targetHealthCheck.hcHealth) // Include targets with no health check record
|
||||
|
||||
@@ -267,50 +267,10 @@ export async function createSite(
|
||||
})
|
||||
.returning();
|
||||
} else if (type == "local") {
|
||||
let exitNodeIdToCreate = exitNodeId;
|
||||
if (!exitNodeIdToCreate) {
|
||||
if (build == "saas") {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
"Exit node ID of a remote node is required for local sites"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// select the exit node for local sites
|
||||
// TODO: THIS SHOULD BE CHOSEN IN THE FRONTEND OR SOMETHING BECAUSE
|
||||
// YOU CAN HAVE MORE THAN ONE NODE IN THE SYSTEM AND YOU SHOULD SELECT
|
||||
// WHICH GERBIL NODE TO PUT THE SITE ON BUT FOR NOW THIS WILL DO
|
||||
const [localExitNode] = await trx
|
||||
.select()
|
||||
.from(exitNodes)
|
||||
.where(eq(exitNodes.type, "gerbil"))
|
||||
.limit(1);
|
||||
|
||||
if (!localExitNode) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
"No gerbil exit node found for organization. Please create a gerbil exit node first."
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
exitNodeIdToCreate = localExitNode.exitNodeId;
|
||||
} else {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
"Site type not recognized"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
[newSite] = await trx
|
||||
.insert(sites)
|
||||
.values({
|
||||
exitNodeId: exitNodeIdToCreate,
|
||||
exitNodeId: exitNodeId || null,
|
||||
orgId,
|
||||
name,
|
||||
niceId,
|
||||
@@ -321,6 +281,13 @@ export async function createSite(
|
||||
subnet: "0.0.0.0/32"
|
||||
})
|
||||
.returning();
|
||||
} else {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
"Site type not recognized"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const adminRole = await trx
|
||||
|
||||
@@ -1,41 +1,57 @@
|
||||
import { ActionsEnum } from "@server/auth/actions";
|
||||
import { db } from "@server/db";
|
||||
import { db, orgs } from "@server/db";
|
||||
import { actions, roles, roleActions } from "@server/db";
|
||||
import { eq, inArray } from "drizzle-orm";
|
||||
import logger from "@server/logger";
|
||||
|
||||
export async function ensureActions() {
|
||||
const actionIds = Object.values(ActionsEnum);
|
||||
const existingActions = await db.select().from(actions).execute();
|
||||
const existingActionIds = existingActions.map((action) => action.actionId);
|
||||
|
||||
const actionsToAdd = actionIds.filter(
|
||||
(id) => !existingActionIds.includes(id)
|
||||
);
|
||||
const actionsToRemove = existingActionIds.filter(
|
||||
(id) => !actionIds.includes(id as ActionsEnum)
|
||||
);
|
||||
|
||||
const defaultRoles = await db
|
||||
.select()
|
||||
.from(roles)
|
||||
.where(eq(roles.isAdmin, true))
|
||||
.execute();
|
||||
|
||||
await db.transaction(async (trx) => {
|
||||
const actionIds = Object.values(ActionsEnum);
|
||||
const existingActions = await trx.select().from(actions).execute();
|
||||
const existingActionIds = existingActions.map(
|
||||
(action) => action.actionId
|
||||
);
|
||||
|
||||
const actionsToAdd = actionIds.filter(
|
||||
(id) => !existingActionIds.includes(id)
|
||||
);
|
||||
const actionsToRemove = existingActionIds.filter(
|
||||
(id) => !actionIds.includes(id as ActionsEnum)
|
||||
);
|
||||
|
||||
const defaultRoles = await trx
|
||||
.select()
|
||||
.from(roles)
|
||||
.where(eq(roles.isAdmin, true))
|
||||
.execute();
|
||||
|
||||
const allOrgs = await trx
|
||||
.select({ orgId: orgs.orgId })
|
||||
.from(orgs)
|
||||
.execute();
|
||||
const allOrgIds = new Set(allOrgs.map((o) => o.orgId));
|
||||
const validRoles = defaultRoles.filter(
|
||||
(r) => r.orgId && r.roleId && allOrgIds.has(r.orgId)
|
||||
);
|
||||
|
||||
const skipped = defaultRoles.length - validRoles.length;
|
||||
if (skipped > 0) {
|
||||
logger.warn(`Skipped ${skipped} orphaned admin roles missing orgs`);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (validRoles.length != 0) {
|
||||
await trx
|
||||
.insert(roleActions)
|
||||
.values(
|
||||
defaultRoles.map((role) => ({
|
||||
roleId: role.roleId!,
|
||||
validRoles.map((role) => ({
|
||||
roleId: role.roleId,
|
||||
actionId,
|
||||
orgId: role.orgId!
|
||||
orgId: role.orgId
|
||||
}))
|
||||
)
|
||||
.execute();
|
||||
@@ -45,14 +61,14 @@ export async function ensureActions() {
|
||||
// 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();
|
||||
await trx
|
||||
.delete(actions)
|
||||
.where(inArray(actions.actionId, actionsToRemove))
|
||||
.execute();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import m4 from "./scriptsPg/1.9.0";
|
||||
import m5 from "./scriptsPg/1.10.0";
|
||||
import m6 from "./scriptsPg/1.10.2";
|
||||
import m7 from "./scriptsPg/1.11.0";
|
||||
import m8 from "./scriptsPg/1.11.1";
|
||||
|
||||
// THIS CANNOT IMPORT ANYTHING FROM THE SERVER
|
||||
// EXCEPT FOR THE DATABASE AND THE SCHEMA
|
||||
@@ -25,6 +26,7 @@ const migrations = [
|
||||
{ version: "1.10.0", run: m5 },
|
||||
{ version: "1.10.2", run: m6 },
|
||||
{ version: "1.11.0", run: m7 },
|
||||
{ version: "1.11.1", run: m8 }
|
||||
// Add new migrations here as they are created
|
||||
] as {
|
||||
version: string;
|
||||
|
||||
@@ -30,6 +30,7 @@ import m25 from "./scriptsSqlite/1.10.0";
|
||||
import m26 from "./scriptsSqlite/1.10.1";
|
||||
import m27 from "./scriptsSqlite/1.10.2";
|
||||
import m28 from "./scriptsSqlite/1.11.0";
|
||||
import m29 from "./scriptsSqlite/1.11.1";
|
||||
|
||||
// THIS CANNOT IMPORT ANYTHING FROM THE SERVER
|
||||
// EXCEPT FOR THE DATABASE AND THE SCHEMA
|
||||
@@ -59,6 +60,7 @@ const migrations = [
|
||||
{ version: "1.10.1", run: m26 },
|
||||
{ version: "1.10.2", run: m27 },
|
||||
{ version: "1.11.0", run: m28 },
|
||||
{ version: "1.11.1", run: m29 }
|
||||
// Add new migrations here as they are created
|
||||
] as const;
|
||||
|
||||
|
||||
@@ -7,30 +7,9 @@ export default async function migration() {
|
||||
console.log(`Running setup script ${version}...`);
|
||||
|
||||
try {
|
||||
// Get the first exit node with type 'gerbil'
|
||||
const exitNodesQuery = await db.execute(
|
||||
sql`SELECT "exitNodeId" FROM "exitNodes" WHERE "type" = 'gerbil' LIMIT 1`
|
||||
);
|
||||
const exitNodes = exitNodesQuery.rows as {
|
||||
exitNodeId: number;
|
||||
}[];
|
||||
await db.execute(sql`BEGIN`);
|
||||
|
||||
const exitNodeId = exitNodes.length > 0 ? exitNodes[0].exitNodeId : null;
|
||||
|
||||
// Get all sites with type 'local'
|
||||
const sitesQuery = await db.execute(
|
||||
sql`SELECT "siteId" FROM "sites" WHERE "type" = 'local'`
|
||||
);
|
||||
const sites = sitesQuery.rows as {
|
||||
siteId: number;
|
||||
}[];
|
||||
|
||||
// Update sites to use the exit node
|
||||
for (const site of sites) {
|
||||
await db.execute(sql`
|
||||
UPDATE "sites" SET "exitNodeId" = ${exitNodeId} WHERE "siteId" = ${site.siteId}
|
||||
`);
|
||||
}
|
||||
await db.execute(sql`UPDATE "exitNodes" SET "online" = true`); // Mark exit nodes as online
|
||||
|
||||
await db.execute(sql`COMMIT`);
|
||||
console.log(`Updated sites with exit node`);
|
||||
|
||||
@@ -11,27 +11,8 @@ export default async function migration() {
|
||||
const db = new Database(location);
|
||||
|
||||
db.transaction(() => {
|
||||
const exitNodes = db.prepare(`SELECT * FROM exitNodes WHERE type = 'gerbil' LIMIT 1`).all() as {
|
||||
exitNodeId: number;
|
||||
name: string;
|
||||
}[];
|
||||
|
||||
const exitNodeId = exitNodes.length > 0 ? exitNodes[0].exitNodeId : null;
|
||||
|
||||
// get all of the targets
|
||||
const sites = db.prepare(`SELECT * FROM sites WHERE type = 'local'`).all() as {
|
||||
siteId: number;
|
||||
exitNodeId: number | null;
|
||||
}[];
|
||||
|
||||
const defineExitNodeOnSite = db.prepare(
|
||||
`UPDATE sites SET exitNodeId = ? WHERE siteId = ?`
|
||||
);
|
||||
|
||||
for (const site of sites) {
|
||||
defineExitNodeOnSite.run(exitNodeId, site.siteId);
|
||||
}
|
||||
db.prepare(`UPDATE exitNodes SET online = 1`).run(); // mark exit nodes as online
|
||||
})();
|
||||
|
||||
console.log(`${version} migration complete`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user