Merge branch 'dev' into feat/update-popup

This commit is contained in:
Milo Schwartz
2025-11-14 09:12:11 -08:00
committed by GitHub
71 changed files with 2562 additions and 935 deletions

View File

@@ -60,6 +60,7 @@ type BasicUserData = {
username: string;
email: string | null;
name: string | null;
role: string | null;
};
export type VerifyUserResponse = {
@@ -883,7 +884,8 @@ async function isUserAllowedToAccessResource(
return {
username: user.username,
email: user.email,
name: user.name
name: user.name,
role: user.role
};
}
@@ -896,7 +898,8 @@ async function isUserAllowedToAccessResource(
return {
username: user.username,
email: user.email,
name: user.name
name: user.name,
role: user.role
};
}

View File

@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { Client, db, exitNodes, sites } from "@server/db";
import { Client, db, exitNodes, olms, sites } from "@server/db";
import { clients, clientSites } from "@server/db";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
@@ -18,6 +18,7 @@ import {
deletePeer as olmDeletePeer
} from "../olm/peers";
import { sendToExitNode } from "#dynamic/lib/exitNodes";
import { hashPassword } from "@server/auth/password";
const updateClientParamsSchema = z
.object({
@@ -30,7 +31,7 @@ const updateClientSchema = z
name: z.string().min(1).max(255).optional(),
siteIds: z
.array(z.number().int().positive())
.optional()
.optional(),
})
.strict();
@@ -89,6 +90,7 @@ export async function updateClient(
const { clientId } = parsedParams.data;
// Fetch the client to make sure it exists and the user has access to it
const [client] = await db
.select()

View File

@@ -178,6 +178,7 @@ authenticated.post(
client.updateClient,
);
// authenticated.get(
// "/site/:siteId/roles",
// verifySiteAccess,
@@ -191,6 +192,7 @@ authenticated.post(
logActionAudit(ActionsEnum.updateSite),
site.updateSite,
);
authenticated.delete(
"/site/:siteId",
verifySiteAccess,

View File

@@ -365,9 +365,9 @@ export async function validateOidcCallback(
if (!existingUserOrgs.length) {
// delete the user
await db
.delete(users)
.where(eq(users.userId, existingUser.userId));
// await db
// .delete(users)
// .where(eq(users.userId, existingUser.userId));
return next(
createHttpError(
HttpCode.UNAUTHORIZED,

View File

@@ -6,6 +6,11 @@ export type CreateRemoteExitNodeResponse = {
secret: string;
};
export type UpdateRemoteExitNodeResponse = {
remoteExitNodeId: string;
secret: string;
}
export type PickRemoteExitNodeDefaultsResponse = {
remoteExitNodeId: string;
secret: string;

View File

@@ -37,6 +37,7 @@ const updateResourceParamsSchema = z
const updateHttpResourceBodySchema = z
.object({
name: z.string().min(1).max(255).optional(),
niceId: z.string().min(1).max(255).optional(),
subdomain: subdomainSchema.nullable().optional(),
ssl: z.boolean().optional(),
sso: z.boolean().optional(),
@@ -97,6 +98,7 @@ export type UpdateResourceResponse = Resource;
const updateRawResourceBodySchema = z
.object({
name: z.string().min(1).max(255).optional(),
niceId: z.string().min(1).max(255).optional(),
proxyPort: z.number().int().min(1).max(65535).optional(),
stickySession: z.boolean().optional(),
enabled: z.boolean().optional(),
@@ -236,6 +238,30 @@ async function updateHttpResource(
const updateData = parsedBody.data;
if (updateData.niceId) {
const [existingResource] = await db
.select()
.from(resources)
.where(
and(
eq(resources.niceId, updateData.niceId),
eq(resources.orgId, resource.orgId)
)
);
if (
existingResource &&
existingResource.resourceId !== resource.resourceId
) {
return next(
createHttpError(
HttpCode.CONFLICT,
`A resource with niceId "${updateData.niceId}" already exists`
)
);
}
}
if (updateData.domainId) {
const domainId = updateData.domainId;
@@ -362,6 +388,30 @@ async function updateRawResource(
const updateData = parsedBody.data;
if (updateData.niceId) {
const [existingResource] = await db
.select()
.from(resources)
.where(
and(
eq(resources.niceId, updateData.niceId),
eq(resources.orgId, resource.orgId)
)
);
if (
existingResource &&
existingResource.resourceId !== resource.resourceId
) {
return next(
createHttpError(
HttpCode.CONFLICT,
`A resource with niceId "${updateData.niceId}" already exists`
)
);
}
}
const updatedResource = await db
.update(resources)
.set(updateData)

View File

@@ -5,4 +5,4 @@ export * from "./updateSite";
export * from "./listSites";
export * from "./listSiteRoles";
export * from "./pickSiteDefaults";
export * from "./socketIntegration";
export * from "./socketIntegration";

View File

@@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { sites } from "@server/db";
import { eq } from "drizzle-orm";
import { eq, and } from "drizzle-orm";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
@@ -20,6 +20,7 @@ const updateSiteParamsSchema = z
const updateSiteBodySchema = z
.object({
name: z.string().min(1).max(255).optional(),
niceId: z.string().min(1).max(255).optional(),
dockerSocketEnabled: z.boolean().optional(),
remoteSubnets: z
.string()
@@ -89,6 +90,29 @@ export async function updateSite(
const { siteId } = parsedParams.data;
const updateData = parsedBody.data;
// if niceId is provided, check if it's already in use by another site
if (updateData.niceId) {
const existingSite = await db
.select()
.from(sites)
.where(
and(
eq(sites.niceId, updateData.niceId),
eq(sites.orgId, sites.orgId)
)
)
.limit(1);
if (existingSite.length > 0 && existingSite[0].siteId !== siteId) {
return next(
createHttpError(
HttpCode.CONFLICT,
`A site with niceId "${updateData.niceId}" already exists`
)
);
}
}
// if remoteSubnets is provided, ensure it's a valid comma-separated list of cidrs
if (updateData.remoteSubnets) {
const subnets = updateData.remoteSubnets.split(",").map((s) => s.trim());

View File

@@ -163,12 +163,8 @@ export async function createTarget(
);
if (existingTarget) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
`Target with IP ${targetData.ip}, port ${targetData.port}, method ${targetData.method} already exists for resource ID ${resourceId}`
)
);
// log a warning
logger.warn(`Target with IP ${targetData.ip}, port ${targetData.port}, method ${targetData.method} already exists for resource ID ${resourceId}`);
}
let newTarget: Target[] = [];

View File

@@ -48,12 +48,10 @@ export async function deleteTarget(
const { targetId } = parsedParams.data;
const [deletedTarget] = await db.transaction(async (tx) => {
return await tx
.delete(targets)
.where(eq(targets.targetId, targetId))
.returning();
});
const [deletedTarget] = await db
.delete(targets)
.where(eq(targets.targetId, targetId))
.returning();
if (!deletedTarget) {
return next(

View File

@@ -170,12 +170,8 @@ export async function updateTarget(
);
if (foundTarget) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
`Target with IP ${targetData.ip}, port ${targetData.port}, and method ${targetData.method} already exists on the same site.`
)
);
// log a warning
logger.warn(`Target with IP ${targetData.ip}, port ${targetData.port}, method ${targetData.method} already exists for resource ID ${target.resourceId}`);
}
const { internalPort, targetIps } = await pickPort(site.siteId!, db);