add site targets, client resources, and auto login

This commit is contained in:
miloschwartz
2025-08-14 18:24:21 -07:00
parent 67ba225003
commit 5c04b1e14a
80 changed files with 5651 additions and 2385 deletions

View File

@@ -26,6 +26,7 @@ const createTargetParamsSchema = z
const createTargetSchema = z
.object({
siteId: z.number().int().positive(),
ip: z.string().refine(isTargetValid),
method: z.string().optional().nullable(),
port: z.number().int().min(1).max(65535),
@@ -98,17 +99,41 @@ export async function createTarget(
);
}
const siteId = targetData.siteId;
const [site] = await db
.select()
.from(sites)
.where(eq(sites.siteId, resource.siteId!))
.where(eq(sites.siteId, siteId))
.limit(1);
if (!site) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Site with ID ${resource.siteId} not found`
`Site with ID ${siteId} not found`
)
);
}
const existingTargets = await db
.select()
.from(targets)
.where(eq(targets.resourceId, resourceId));
const existingTarget = existingTargets.find(
(target) =>
target.ip === targetData.ip &&
target.port === targetData.port &&
target.method === targetData.method &&
target.siteId === targetData.siteId
);
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}`
)
);
}
@@ -173,7 +198,12 @@ export async function createTarget(
.where(eq(newts.siteId, site.siteId))
.limit(1);
addTargets(newt.newtId, newTarget, resource.protocol, resource.proxyPort);
await addTargets(
newt.newtId,
newTarget,
resource.protocol,
resource.proxyPort
);
}
}
}

View File

@@ -76,38 +76,38 @@ export async function deleteTarget(
);
}
const [site] = await db
.select()
.from(sites)
.where(eq(sites.siteId, resource.siteId!))
.limit(1);
if (!site) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Site with ID ${resource.siteId} not found`
)
);
}
if (site.pubKey) {
if (site.type == "wireguard") {
await addPeer(site.exitNodeId!, {
publicKey: site.pubKey,
allowedIps: await getAllowedIps(site.siteId)
});
} else if (site.type == "newt") {
// get the newt on the site by querying the newt table for siteId
const [newt] = await db
.select()
.from(newts)
.where(eq(newts.siteId, site.siteId))
.limit(1);
removeTargets(newt.newtId, [deletedTarget], resource.protocol, resource.proxyPort);
}
}
// const [site] = await db
// .select()
// .from(sites)
// .where(eq(sites.siteId, resource.siteId!))
// .limit(1);
//
// if (!site) {
// return next(
// createHttpError(
// HttpCode.NOT_FOUND,
// `Site with ID ${resource.siteId} not found`
// )
// );
// }
//
// if (site.pubKey) {
// if (site.type == "wireguard") {
// await addPeer(site.exitNodeId!, {
// publicKey: site.pubKey,
// allowedIps: await getAllowedIps(site.siteId)
// });
// } else if (site.type == "newt") {
// // get the newt on the site by querying the newt table for siteId
// const [newt] = await db
// .select()
// .from(newts)
// .where(eq(newts.siteId, site.siteId))
// .limit(1);
//
// removeTargets(newt.newtId, [deletedTarget], resource.protocol, resource.proxyPort);
// }
// }
return response(res, {
data: null,

View File

@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { db, Target } from "@server/db";
import { targets } from "@server/db";
import { eq } from "drizzle-orm";
import response from "@server/lib/response";
@@ -16,6 +16,8 @@ const getTargetSchema = z
})
.strict();
type GetTargetResponse = Target;
registry.registerPath({
method: "get",
path: "/target/{targetId}",
@@ -60,7 +62,7 @@ export async function getTarget(
);
}
return response(res, {
return response<GetTargetResponse>(res, {
data: target[0],
success: true,
error: false,

View File

@@ -8,29 +8,21 @@ export async function pickPort(siteId: number): Promise<{
internalPort: number;
targetIps: string[];
}> {
const resourcesRes = await db
.select()
.from(resources)
.where(eq(resources.siteId, siteId));
// TODO: is this all inefficient?
// Fetch targets for all resources of this site
const targetIps: string[] = [];
const targetInternalPorts: number[] = [];
await Promise.all(
resourcesRes.map(async (resource) => {
const targetsRes = await db
.select()
.from(targets)
.where(eq(targets.resourceId, resource.resourceId));
targetsRes.forEach((target) => {
targetIps.push(`${target.ip}/32`);
if (target.internalPort) {
targetInternalPorts.push(target.internalPort);
}
});
})
);
const targetsRes = await db
.select()
.from(targets)
.where(eq(targets.siteId, siteId));
targetsRes.forEach((target) => {
targetIps.push(`${target.ip}/32`);
if (target.internalPort) {
targetInternalPorts.push(target.internalPort);
}
});
let internalPort!: number;
// pick a port random port from 40000 to 65535 that is not in use
@@ -43,28 +35,20 @@ export async function pickPort(siteId: number): Promise<{
break;
}
}
currentBannedPorts.push(internalPort);
return { internalPort, targetIps };
}
export async function getAllowedIps(siteId: number) {
// TODO: is this all inefficient?
const resourcesRes = await db
.select()
.from(resources)
.where(eq(resources.siteId, siteId));
// Fetch targets for all resources of this site
const targetIps = await Promise.all(
resourcesRes.map(async (resource) => {
const targetsRes = await db
.select()
.from(targets)
.where(eq(targets.resourceId, resource.resourceId));
return targetsRes.map((target) => `${target.ip}/32`);
})
);
const targetsRes = await db
.select()
.from(targets)
.where(eq(targets.siteId, siteId));
const targetIps = targetsRes.map((target) => `${target.ip}/32`);
return targetIps.flat();
}

View File

@@ -2,4 +2,4 @@ export * from "./getTarget";
export * from "./createTarget";
export * from "./deleteTarget";
export * from "./updateTarget";
export * from "./listTargets";
export * from "./listTargets";

View File

@@ -1,4 +1,4 @@
import { db } from "@server/db";
import { db, sites } from "@server/db";
import { targets } from "@server/db";
import HttpCode from "@server/types/HttpCode";
import response from "@server/lib/response";
@@ -42,11 +42,12 @@ function queryTargets(resourceId: number) {
method: targets.method,
port: targets.port,
enabled: targets.enabled,
resourceId: targets.resourceId
// resourceName: resources.name,
resourceId: targets.resourceId,
siteId: targets.siteId,
siteType: sites.type
})
.from(targets)
// .leftJoin(resources, eq(targets.resourceId, resources.resourceId))
.leftJoin(sites, eq(sites.siteId, targets.siteId))
.where(eq(targets.resourceId, resourceId));
return baseQuery;

View File

@@ -22,6 +22,7 @@ const updateTargetParamsSchema = z
const updateTargetBodySchema = z
.object({
siteId: z.number().int().positive(),
ip: z.string().refine(isTargetValid),
method: z.string().min(1).max(10).optional().nullable(),
port: z.number().int().min(1).max(65535).optional(),
@@ -77,6 +78,7 @@ export async function updateTarget(
}
const { targetId } = parsedParams.data;
const { siteId } = parsedBody.data;
const [target] = await db
.select()
@@ -111,14 +113,42 @@ export async function updateTarget(
const [site] = await db
.select()
.from(sites)
.where(eq(sites.siteId, resource.siteId!))
.where(eq(sites.siteId, siteId))
.limit(1);
if (!site) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Site with ID ${resource.siteId} not found`
`Site with ID ${siteId} not found`
)
);
}
const targetData = {
...target,
...parsedBody.data
};
const existingTargets = await db
.select()
.from(targets)
.where(eq(targets.resourceId, target.resourceId));
const foundTarget = existingTargets.find(
(target) =>
target.targetId !== targetId && // Exclude the current target being updated
target.ip === targetData.ip &&
target.port === targetData.port &&
target.method === targetData.method &&
target.siteId === targetData.siteId
);
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.`
)
);
}
@@ -157,7 +187,12 @@ export async function updateTarget(
.where(eq(newts.siteId, site.siteId))
.limit(1);
addTargets(newt.newtId, [updatedTarget], resource.protocol, resource.proxyPort);
await addTargets(
newt.newtId,
[updatedTarget],
resource.protocol,
resource.proxyPort
);
}
}
return response(res, {