🚧 Create certificate for Redirect (in case of domain)

This commit is contained in:
Fred KISSIE
2026-09-14 23:42:20 +02:00
parent 4a41e6b650
commit fd7780528f
4 changed files with 94 additions and 39 deletions
@@ -459,6 +459,11 @@ export async function getTraefikConfig(
}
};
console.dir(
{ resourcesMap, resourcesWithTargetsAndSites },
{ depth: null }
);
// get the key and the resource
for (const [, resource] of resourcesMap.entries()) {
const targets = resource.targets as TargetWithSite[];
+48 -30
View File
@@ -1,7 +1,7 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db, domains, orgDomains, redirects, resources } from "@server/db";
import type { Redirect } from "@server/db";
import type { Domain, Redirect, Resource } from "@server/db";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
@@ -17,6 +17,7 @@ import {
redirectRewritePathTypeSchema
} from "@server/routers/redirect/validation";
import { getUniqueRedirectName } from "@server/db/names";
import { createCertificate } from "../certificates";
export type CreateRedirectResponse = {
redirect: Redirect;
@@ -26,31 +27,37 @@ const paramsSchema = z.strictObject({
orgId: z.string().nonempty()
});
const bodySchema = z.strictObject({
name: z.string().nonempty(),
resourceId: z.number().int().positive().optional().nullable(),
domainId: z.string().nonempty().optional().nullable(),
subdomain: z.string().nonempty().optional().nullable(),
destinationDomain: redirectDestinationDomainSchema,
pathMatchType: redirectPathMatchTypeSchema.optional(),
matchPath: redirectMatchPathSchema,
rewritePath: redirectRewritePathSchema.optional().nullable(),
rewritePathType: redirectRewritePathTypeSchema.optional().nullable(),
permanent: z.boolean().optional(),
enabled: z.boolean().optional()
}).refine(
(data) =>
// stripPrefix removes the matched prefix and needs no replacement
// value; every other rewrite type is meaningless without one.
!data.rewritePathType ||
data.rewritePathType === "stripPrefix" ||
Boolean(data.rewritePath),
{
message:
"rewritePath is required unless rewritePathType is stripPrefix",
path: ["rewritePath"]
}
);
const bodySchema = z
.strictObject({
name: z.string().nonempty(),
resourceId: z.number().int().positive().optional().nullable(),
domainId: z.string().nonempty().optional().nullable(),
subdomain: z.string().nonempty().optional().nullable(),
destinationDomain: redirectDestinationDomainSchema,
pathMatchType: redirectPathMatchTypeSchema.optional(),
matchPath: redirectMatchPathSchema,
rewritePath: redirectRewritePathSchema.optional().nullable(),
rewritePathType: redirectRewritePathTypeSchema.optional().nullable(),
permanent: z.boolean().optional(),
enabled: z.boolean().optional()
})
.refine(
(data) =>
// stripPrefix removes the matched prefix and needs no replacement
// value; every other rewrite type is meaningless without one.
!data.rewritePathType ||
data.rewritePathType === "stripPrefix" ||
Boolean(data.rewritePath),
{
message:
"rewritePath is required unless rewritePathType is stripPrefix",
path: ["rewritePath"]
}
)
.refine((data) => Boolean(data.resourceId) !== Boolean(data.domainId), {
message: "Exactly one of resourceId or domainId must be provided",
path: ["resourceId"]
});
registry.registerPath({
method: "put",
@@ -115,9 +122,10 @@ export async function createRedirect(
enabled
} = parsedBody.data;
let resource: Resource | null = null;
if (resourceId) {
const [resource] = await db
.select({ resourceId: resources.resourceId })
const res = await db
.select()
.from(resources)
.where(
and(
@@ -127,6 +135,7 @@ export async function createRedirect(
)
.limit(1);
resource = res.at(0) ?? null;
if (!resource) {
return next(
createHttpError(
@@ -137,9 +146,10 @@ export async function createRedirect(
}
}
let domain: Domain | null = null;
if (domainId) {
const [domain] = await db
.select({ domainId: domains.domainId })
const res = await db
.select()
.from(domains)
.innerJoin(
orgDomains,
@@ -153,6 +163,7 @@ export async function createRedirect(
)
.limit(1);
domain = res.at(0)?.domains ?? null;
if (!domain) {
return next(
createHttpError(
@@ -184,6 +195,13 @@ export async function createRedirect(
})
.returning();
if (domain) {
const fullDomain = [subdomain ?? null, domain.baseDomain]
.filter(Boolean)
.join(".");
await createCertificate(domain.domainId, fullDomain, db);
}
return response<CreateRedirectResponse>(res, {
data: {
redirect
+38 -5
View File
@@ -17,6 +17,7 @@ import {
redirectRewritePathSchema,
redirectRewritePathTypeSchema
} from "@server/routers/redirect/validation";
import { createCertificate } from "../certificates";
export type UpdateRedirectResponse = {
redirect: Redirect;
@@ -113,6 +114,22 @@ export async function updateRedirect(
);
}
const effectiveResourceId =
body.resourceId !== undefined
? body.resourceId
: existing.resourceId;
const effectiveDomainId =
body.domainId !== undefined ? body.domainId : existing.domainId;
if (Boolean(effectiveResourceId) === Boolean(effectiveDomainId)) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Exactly one of resourceId or domainId must be provided"
)
);
}
if (body.resourceId) {
const [resource] = await db
.select({ resourceId: resources.resourceId })
@@ -135,9 +152,13 @@ export async function updateRedirect(
}
}
if (body.domainId) {
const [domain] = await db
.select({ domainId: domains.domainId })
let domain: { domainId: string; baseDomain: string } | null = null;
if (effectiveDomainId) {
const [d] = await db
.select({
domainId: domains.domainId,
baseDomain: domains.baseDomain
})
.from(domains)
.innerJoin(
orgDomains,
@@ -145,17 +166,18 @@ export async function updateRedirect(
)
.where(
and(
eq(domains.domainId, body.domainId),
eq(domains.domainId, effectiveDomainId),
eq(orgDomains.orgId, existing.orgId)
)
)
.limit(1);
domain = d ?? null;
if (!domain) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Domain with ID ${body.domainId} not found`
`Domain with ID ${effectiveDomainId} not found`
)
);
}
@@ -234,6 +256,17 @@ export async function updateRedirect(
)
.returning();
if (domain) {
const effectiveSubdomain =
body.subdomain !== undefined
? body.subdomain
: existing.subdomain;
const fullDomain = [effectiveSubdomain ?? null, domain.baseDomain]
.filter(Boolean)
.join(".");
await createCertificate(domain.domainId, fullDomain, db);
}
return response<UpdateRedirectResponse>(res, {
data: {
redirect
@@ -29,8 +29,8 @@ export async function traefikConfigProvider(
const traefikConfig = await getTraefikConfig(
currentExitNodeId,
config.getRawConfig().traefik.site_types,
build == "oss", // filter out the namespace domains in open source
build != "oss", // generate the login pages on the cloud and and enterprise,
build === "oss", // filter out the namespace domains in open source
build !== "oss", // generate the login pages on the cloud and and enterprise,
config.getRawConfig().traefik.allow_raw_resources,
pangolinUIUrl,
pangolinUIUrl,
@@ -71,8 +71,7 @@ export async function traefikConfigProvider(
.resource_session_request_param,
remoteUserIdHeader:
config.getRawConfig().server.remote_headers
.user_id,
config.getRawConfig().server.remote_headers.user_id,
remoteVirtualApiKeyIdHeader:
config.getRawConfig().server.remote_headers