Merge branch 'dev' of github.com:fosrl/pangolin into dev

This commit is contained in:
Owen
2025-09-08 17:50:57 -07:00
20 changed files with 264 additions and 157 deletions

View File

@@ -22,6 +22,7 @@ import config from "@server/lib/config";
import { OpenAPITags, registry } from "@server/openApi";
import { build } from "@server/build";
import { getUniqueResourceName } from "@server/db/names";
import { validateAndConstructDomain } from "@server/lib/domainUtils";
const createResourceParamsSchema = z
.object({
@@ -194,76 +195,21 @@ async function createHttpResource(
}
const { name, domainId } = parsedBody.data;
let subdomain = parsedBody.data.subdomain;
const subdomain = parsedBody.data.subdomain;
const [domainRes] = await db
.select()
.from(domains)
.where(eq(domains.domainId, domainId))
.leftJoin(
orgDomains,
and(eq(orgDomains.orgId, orgId), eq(orgDomains.domainId, domainId))
);
if (!domainRes || !domainRes.domains) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Domain with ID ${domainId} not found`
)
);
}
if (domainRes.orgDomains && domainRes.orgDomains.orgId !== orgId) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
`Organization does not have access to domain with ID ${domainId}`
)
);
}
if (!domainRes.domains.verified) {
// Validate domain and construct full domain
const domainResult = await validateAndConstructDomain(domainId, orgId, subdomain);
if (!domainResult.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
`Domain with ID ${domainRes.domains.domainId} is not verified`
domainResult.error
)
);
}
let fullDomain = "";
if (domainRes.domains.type == "ns") {
if (subdomain) {
fullDomain = `${subdomain}.${domainRes.domains.baseDomain}`;
} else {
fullDomain = domainRes.domains.baseDomain;
}
} else if (domainRes.domains.type == "cname") {
fullDomain = domainRes.domains.baseDomain;
} else if (domainRes.domains.type == "wildcard") {
if (subdomain) {
// the subdomain cant have a dot in it
const parsedSubdomain = subdomainSchema.safeParse(subdomain);
if (!parsedSubdomain.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedSubdomain.error).toString()
)
);
}
fullDomain = `${subdomain}.${domainRes.domains.baseDomain}`;
} else {
fullDomain = domainRes.domains.baseDomain;
}
}
if (fullDomain === domainRes.domains.baseDomain) {
subdomain = null;
}
fullDomain = fullDomain.toLowerCase();
const { fullDomain, subdomain: finalSubdomain } = domainResult;
logger.debug(`Full domain: ${fullDomain}`);
@@ -295,7 +241,7 @@ async function createHttpResource(
domainId,
orgId,
name,
subdomain,
subdomain: finalSubdomain,
http: true,
protocol: "tcp",
ssl: true

View File

@@ -20,6 +20,7 @@ import { tlsNameSchema } from "@server/lib/schemas";
import { subdomainSchema } from "@server/lib/schemas";
import { registry } from "@server/openApi";
import { OpenAPITags } from "@server/openApi";
import { validateAndConstructDomain } from "@server/lib/domainUtils";
const updateResourceParamsSchema = z
.object({
@@ -230,78 +231,19 @@ async function updateHttpResource(
if (updateData.domainId) {
const domainId = updateData.domainId;
const [domainRes] = await db
.select()
.from(domains)
.where(eq(domains.domainId, domainId))
.leftJoin(
orgDomains,
and(
eq(orgDomains.orgId, resource.orgId),
eq(orgDomains.domainId, domainId)
)
);
if (!domainRes || !domainRes.domains) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Domain with ID ${updateData.domainId} not found`
)
);
}
if (
domainRes.orgDomains &&
domainRes.orgDomains.orgId !== resource.orgId
) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
`You do not have permission to use domain with ID ${updateData.domainId}`
)
);
}
if (!domainRes.domains.verified) {
// Validate domain and construct full domain
const domainResult = await validateAndConstructDomain(domainId, resource.orgId, updateData.subdomain);
if (!domainResult.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
`Domain with ID ${updateData.domainId} is not verified`
domainResult.error
)
);
}
let fullDomain = "";
if (domainRes.domains.type == "ns") {
if (updateData.subdomain) {
fullDomain = `${updateData.subdomain}.${domainRes.domains.baseDomain}`;
} else {
fullDomain = domainRes.domains.baseDomain;
}
} else if (domainRes.domains.type == "cname") {
fullDomain = domainRes.domains.baseDomain;
} else if (domainRes.domains.type == "wildcard") {
if (updateData.subdomain !== undefined) {
// the subdomain cant have a dot in it
const parsedSubdomain = subdomainSchema.safeParse(
updateData.subdomain
);
if (!parsedSubdomain.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedSubdomain.error).toString()
)
);
}
fullDomain = `${updateData.subdomain}.${domainRes.domains.baseDomain}`;
} else {
fullDomain = domainRes.domains.baseDomain;
}
}
fullDomain = fullDomain.toLowerCase();
const { fullDomain, subdomain: finalSubdomain } = domainResult;
logger.debug(`Full domain: ${fullDomain}`);
@@ -332,9 +274,8 @@ async function updateHttpResource(
.where(eq(resources.resourceId, resource.resourceId));
}
if (fullDomain === domainRes.domains.baseDomain) {
updateData.subdomain = null;
}
// Update the subdomain in the update data
updateData.subdomain = finalSubdomain;
}
const updatedResource = await db

View File

@@ -28,7 +28,7 @@ const updateSiteResourceSchema = z
protocol: z.enum(["tcp", "udp"]).optional(),
proxyPort: z.number().int().positive().optional(),
destinationPort: z.number().int().positive().optional(),
destinationIp: z.string().ip().optional(),
destinationIp: z.string().optional(),
enabled: z.boolean().optional()
})
.strict();