Merge branch 'dev' into multi-domain

This commit is contained in:
miloschwartz
2025-02-26 21:26:20 -05:00
30 changed files with 1508 additions and 230 deletions

View File

@@ -149,8 +149,6 @@ export async function resetPassword(
const passwordHash = await hashPassword(newPassword);
await invalidateAllSessions(resetRequest[0].userId);
await db.transaction(async (trx) => {
await trx
.update(users)
@@ -162,11 +160,21 @@ export async function resetPassword(
.where(eq(passwordResetTokens.email, email));
});
await sendEmail(ConfirmPasswordReset({ email }), {
from: config.getNoReplyEmail(),
to: email,
subject: "Password Reset Confirmation"
});
try {
await invalidateAllSessions(resetRequest[0].userId);
} catch (e) {
logger.error("Failed to invalidate user sessions", e);
}
try {
await sendEmail(ConfirmPasswordReset({ email }), {
from: config.getNoReplyEmail(),
to: email,
subject: "Password Reset Confirmation"
});
} catch (e) {
logger.error("Failed to send password reset confirmation email", e);
}
return response<ResetPasswordResponse>(res, {
data: null,

View File

@@ -90,7 +90,15 @@ export async function verifyResourceSession(
const clientIp = requestIp?.split(":")[0];
const resourceCacheKey = `resource:${host}`;
let cleanHost = host;
// if the host ends with :443 or :80 remove it
if (cleanHost.endsWith(":443")) {
cleanHost = cleanHost.slice(0, -4);
} else if (cleanHost.endsWith(":80")) {
cleanHost = cleanHost.slice(0, -3);
}
const resourceCacheKey = `resource:${cleanHost}`;
let resourceData:
| {
resource: Resource | null;
@@ -111,11 +119,11 @@ export async function verifyResourceSession(
resourcePassword,
eq(resourcePassword.resourceId, resources.resourceId)
)
.where(eq(resources.fullDomain, host))
.where(eq(resources.fullDomain, cleanHost))
.limit(1);
if (!result) {
logger.debug("Resource not found", host);
logger.debug("Resource not found", cleanHost);
return notAllowed(res);
}
@@ -131,7 +139,7 @@ export async function verifyResourceSession(
const { resource, pincode, password } = resourceData;
if (!resource) {
logger.debug("Resource not found", host);
logger.debug("Resource not found", cleanHost);
return notAllowed(res);
}

View File

@@ -12,34 +12,7 @@ import { fromError } from "zod-validation-error";
import { addTargets } from "../newt/targets";
import { eq } from "drizzle-orm";
import { pickPort } from "./helpers";
// Regular expressions for validation
const DOMAIN_REGEX =
/^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
const IPV4_REGEX =
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
const IPV6_REGEX = /^(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}$/i;
// Schema for domain names and IP addresses
const domainSchema = z
.string()
.min(1, "Domain cannot be empty")
.max(255, "Domain name too long")
.refine(
(value) => {
// Check if it's a valid IP address (v4 or v6)
if (IPV4_REGEX.test(value) || IPV6_REGEX.test(value)) {
return true;
}
// Check if it's a valid domain name
return DOMAIN_REGEX.test(value);
},
{
message: "Invalid domain name or IP address format",
path: ["domain"]
}
);
import { isTargetValid } from "@server/lib/validators";
const createTargetParamsSchema = z
.object({
@@ -52,7 +25,7 @@ const createTargetParamsSchema = z
const createTargetSchema = z
.object({
ip: domainSchema,
ip: z.string().refine(isTargetValid),
method: z.string().optional().nullable(),
port: z.number().int().min(1).max(65535),
enabled: z.boolean().default(true)

View File

@@ -11,34 +11,7 @@ import { fromError } from "zod-validation-error";
import { addPeer } from "../gerbil/peers";
import { addTargets } from "../newt/targets";
import { pickPort } from "./helpers";
// Regular expressions for validation
const DOMAIN_REGEX =
/^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
const IPV4_REGEX =
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
const IPV6_REGEX = /^(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}$/i;
// Schema for domain names and IP addresses
const domainSchema = z
.string()
.min(1, "Domain cannot be empty")
.max(255, "Domain name too long")
.refine(
(value) => {
// Check if it's a valid IP address (v4 or v6)
if (IPV4_REGEX.test(value) || IPV6_REGEX.test(value)) {
return true;
}
// Check if it's a valid domain name
return DOMAIN_REGEX.test(value);
},
{
message: "Invalid domain name or IP address format",
path: ["domain"]
}
);
import { isTargetValid } from "@server/lib/validators";
const updateTargetParamsSchema = z
.object({
@@ -48,7 +21,7 @@ const updateTargetParamsSchema = z
const updateTargetBodySchema = z
.object({
ip: domainSchema.optional(),
ip: z.string().refine(isTargetValid),
method: z.string().min(1).max(10).optional().nullable(),
port: z.number().int().min(1).max(65535).optional(),
enabled: z.boolean().optional()