mirror of
https://github.com/fosrl/pangolin.git
synced 2026-09-15 23:30:17 +02:00
🚧 wip
This commit is contained in:
@@ -242,8 +242,19 @@ export const redirects = pgTable("redirects", {
|
||||
}),
|
||||
niceId: text("niceId").notNull(),
|
||||
name: varchar("name").notNull(),
|
||||
sourcePath: varchar("sourcePath").notNull(),
|
||||
destinationUrl: varchar("destinationUrl"),
|
||||
subdomain: varchar("subdomain"),
|
||||
destinationDomain: varchar("destinationDomain").notNull(),
|
||||
pathMatchType: varchar("pathMatchType")
|
||||
.$type<"exact" | "prefix" | "regex">()
|
||||
.notNull()
|
||||
.default("regex"), // exact, prefix, regex
|
||||
matchPath: varchar("matchPath").notNull().default("*"),
|
||||
rewritePath: varchar("rewritePath"), // if set, rewrites the path to this value,
|
||||
// else, the original path will be kept
|
||||
rewritePathType: varchar("rewritePathType").$type<
|
||||
"exact" | "prefix" | "regex" | "stripPrefix"
|
||||
>(), // exact, prefix, regex, stripPrefix
|
||||
|
||||
permanent: boolean("permanent").notNull().default(false),
|
||||
enabled: boolean("enabled").notNull().default(true)
|
||||
});
|
||||
|
||||
@@ -258,8 +258,18 @@ export const redirects = sqliteTable("redirects", {
|
||||
}),
|
||||
niceId: text("niceId").notNull(),
|
||||
name: text("name").notNull(),
|
||||
sourcePath: text("sourcePath").notNull(),
|
||||
destinationUrl: text("destinationUrl"),
|
||||
destinationDomain: text("destinationDomain").notNull(),
|
||||
pathMatchType: text("pathMatchType")
|
||||
.$type<"exact" | "prefix" | "regex">()
|
||||
.notNull()
|
||||
.default("regex"), // exact, prefix, regex
|
||||
matchPath: text("matchPath").notNull().default("*"),
|
||||
rewritePath: text("rewritePath"), // if set, rewrites the path to this value,
|
||||
// else, the original path will be kept
|
||||
rewritePathType: text("rewritePathType").$type<
|
||||
"exact" | "prefix" | "regex" | "stripPrefix"
|
||||
>(), // exact, prefix, regex, stripPrefix
|
||||
|
||||
permanent: integer("permanent", { mode: "boolean" })
|
||||
.notNull()
|
||||
.default(false),
|
||||
|
||||
@@ -9,7 +9,12 @@ import logger from "@server/logger";
|
||||
import { fromError } from "zod-validation-error";
|
||||
import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { redirectSourcePathSchema } from "@server/routers/redirect/validation";
|
||||
import {
|
||||
redirectMatchPathSchema,
|
||||
redirectPathMatchTypeSchema,
|
||||
redirectRewritePathSchema,
|
||||
redirectRewritePathTypeSchema
|
||||
} from "@server/routers/redirect/validation";
|
||||
import { getUniqueRedirectName } from "@server/db/names";
|
||||
|
||||
export type CreateRedirectResponse = {
|
||||
@@ -24,11 +29,26 @@ const bodySchema = z.strictObject({
|
||||
name: z.string().nonempty(),
|
||||
resourceId: z.number().int().positive().optional().nullable(),
|
||||
domainId: z.string().nonempty().optional().nullable(),
|
||||
sourcePath: redirectSourcePathSchema,
|
||||
destinationUrl: z.url().optional().nullable(),
|
||||
destinationDomain: z.string().nonempty(),
|
||||
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"]
|
||||
}
|
||||
);
|
||||
|
||||
registry.registerPath({
|
||||
method: "put",
|
||||
@@ -83,8 +103,11 @@ export async function createRedirect(
|
||||
name,
|
||||
resourceId,
|
||||
domainId,
|
||||
sourcePath,
|
||||
destinationUrl,
|
||||
destinationDomain,
|
||||
pathMatchType,
|
||||
matchPath,
|
||||
rewritePath,
|
||||
rewritePathType,
|
||||
permanent,
|
||||
enabled
|
||||
} = parsedBody.data;
|
||||
@@ -147,8 +170,11 @@ export async function createRedirect(
|
||||
niceId,
|
||||
resourceId: resourceId ?? null,
|
||||
domainId: domainId ?? null,
|
||||
sourcePath,
|
||||
destinationUrl: destinationUrl ?? null,
|
||||
destinationDomain,
|
||||
pathMatchType: pathMatchType ?? "regex",
|
||||
matchPath,
|
||||
rewritePath: rewritePath ?? null,
|
||||
rewritePathType: rewritePathType ?? null,
|
||||
permanent: permanent ?? false,
|
||||
enabled: enabled ?? true
|
||||
})
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { redirects, db } from "@server/db";
|
||||
import type { Redirect } from "@server/db";
|
||||
import { domains, redirects, resources, db } from "@server/db";
|
||||
import response from "@server/lib/response";
|
||||
import stoi from "@server/lib/stoi";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
@@ -12,7 +11,49 @@ import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
|
||||
export type GetRedirectResponse = {
|
||||
redirect: Redirect;
|
||||
redirect: {
|
||||
redirectId: number;
|
||||
orgId: string;
|
||||
niceId: string;
|
||||
name: string;
|
||||
destinationDomain: string;
|
||||
pathMatchType: "exact" | "prefix" | "regex";
|
||||
matchPath: string;
|
||||
rewritePath: string | null;
|
||||
rewritePathType: "exact" | "prefix" | "regex" | "stripPrefix" | null;
|
||||
permanent: boolean;
|
||||
enabled: boolean;
|
||||
resourceId: number | null;
|
||||
resourceName: string | null;
|
||||
resourceNiceId: string | null;
|
||||
resourceFullDomain: string | null;
|
||||
resourceSsl: boolean | null;
|
||||
resourceWildcard: boolean | null;
|
||||
domainId: string | null;
|
||||
baseDomain: string | null;
|
||||
};
|
||||
};
|
||||
|
||||
const redirectColumns = {
|
||||
redirectId: redirects.redirectId,
|
||||
orgId: redirects.orgId,
|
||||
niceId: redirects.niceId,
|
||||
name: redirects.name,
|
||||
destinationDomain: redirects.destinationDomain,
|
||||
pathMatchType: redirects.pathMatchType,
|
||||
matchPath: redirects.matchPath,
|
||||
rewritePath: redirects.rewritePath,
|
||||
rewritePathType: redirects.rewritePathType,
|
||||
permanent: redirects.permanent,
|
||||
enabled: redirects.enabled,
|
||||
resourceId: redirects.resourceId,
|
||||
resourceName: resources.name,
|
||||
resourceNiceId: resources.niceId,
|
||||
resourceFullDomain: resources.fullDomain,
|
||||
resourceSsl: resources.ssl,
|
||||
resourceWildcard: resources.wildcard,
|
||||
domainId: redirects.domainId,
|
||||
baseDomain: domains.baseDomain
|
||||
};
|
||||
|
||||
const paramsSchema = z.strictObject({
|
||||
@@ -29,8 +70,10 @@ const paramsSchema = z.strictObject({
|
||||
async function query(orgId: string, redirectId?: number, niceId?: string) {
|
||||
if (redirectId) {
|
||||
const [res] = await db
|
||||
.select()
|
||||
.select(redirectColumns)
|
||||
.from(redirects)
|
||||
.leftJoin(resources, eq(resources.resourceId, redirects.resourceId))
|
||||
.leftJoin(domains, eq(domains.domainId, redirects.domainId))
|
||||
.where(
|
||||
and(
|
||||
eq(redirects.redirectId, redirectId),
|
||||
@@ -41,8 +84,10 @@ async function query(orgId: string, redirectId?: number, niceId?: string) {
|
||||
return res;
|
||||
} else if (niceId) {
|
||||
const [res] = await db
|
||||
.select()
|
||||
.select(redirectColumns)
|
||||
.from(redirects)
|
||||
.leftJoin(resources, eq(resources.resourceId, redirects.resourceId))
|
||||
.leftJoin(domains, eq(domains.domainId, redirects.domainId))
|
||||
.where(
|
||||
and(eq(redirects.niceId, niceId), eq(redirects.orgId, orgId))
|
||||
)
|
||||
|
||||
@@ -16,8 +16,11 @@ export type ListRedirectsResponse = PaginatedResponse<{
|
||||
orgId: string;
|
||||
niceId: string;
|
||||
name: string;
|
||||
sourcePath: string;
|
||||
destinationUrl: string | null;
|
||||
destinationDomain: string;
|
||||
pathMatchType: "exact" | "prefix" | "regex";
|
||||
matchPath: string;
|
||||
rewritePath: string | null;
|
||||
rewritePathType: "exact" | "prefix" | "regex" | "stripPrefix" | null;
|
||||
permanent: boolean;
|
||||
enabled: boolean;
|
||||
resourceId: number | null;
|
||||
@@ -122,8 +125,8 @@ export async function listRedirects(
|
||||
conditions.push(
|
||||
or(
|
||||
like(sql`LOWER(${redirects.name})`, term),
|
||||
like(sql`LOWER(${redirects.sourcePath})`, term),
|
||||
like(sql`LOWER(${redirects.destinationUrl})`, term)
|
||||
like(sql`LOWER(${redirects.matchPath})`, term),
|
||||
like(sql`LOWER(${redirects.destinationDomain})`, term)
|
||||
)!
|
||||
);
|
||||
}
|
||||
@@ -134,8 +137,11 @@ export async function listRedirects(
|
||||
orgId: redirects.orgId,
|
||||
niceId: redirects.niceId,
|
||||
name: redirects.name,
|
||||
sourcePath: redirects.sourcePath,
|
||||
destinationUrl: redirects.destinationUrl,
|
||||
destinationDomain: redirects.destinationDomain,
|
||||
pathMatchType: redirects.pathMatchType,
|
||||
matchPath: redirects.matchPath,
|
||||
rewritePath: redirects.rewritePath,
|
||||
rewritePathType: redirects.rewritePathType,
|
||||
permanent: redirects.permanent,
|
||||
enabled: redirects.enabled,
|
||||
resourceId: redirects.resourceId,
|
||||
|
||||
@@ -11,7 +11,10 @@ import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { and, eq, ne } from "drizzle-orm";
|
||||
import {
|
||||
redirectNiceIdSchema,
|
||||
redirectSourcePathSchema
|
||||
redirectMatchPathSchema,
|
||||
redirectPathMatchTypeSchema,
|
||||
redirectRewritePathSchema,
|
||||
redirectRewritePathTypeSchema
|
||||
} from "@server/routers/redirect/validation";
|
||||
|
||||
export type UpdateRedirectResponse = {
|
||||
@@ -28,8 +31,11 @@ const bodySchema = z.strictObject({
|
||||
niceId: redirectNiceIdSchema.optional(),
|
||||
resourceId: z.number().int().positive().optional().nullable(),
|
||||
domainId: z.string().nonempty().optional().nullable(),
|
||||
sourcePath: redirectSourcePathSchema.optional(),
|
||||
destinationUrl: z.url().optional().nullable(),
|
||||
destinationDomain: z.string().nonempty().optional(),
|
||||
pathMatchType: redirectPathMatchTypeSchema.optional(),
|
||||
matchPath: redirectMatchPathSchema.optional(),
|
||||
rewritePath: redirectRewritePathSchema.optional().nullable(),
|
||||
rewritePathType: redirectRewritePathTypeSchema.optional().nullable(),
|
||||
permanent: z.boolean().optional(),
|
||||
enabled: z.boolean().optional()
|
||||
});
|
||||
@@ -190,11 +196,20 @@ export async function updateRedirect(
|
||||
if (body.domainId !== undefined) {
|
||||
updateData.domainId = body.domainId;
|
||||
}
|
||||
if (body.sourcePath !== undefined) {
|
||||
updateData.sourcePath = body.sourcePath;
|
||||
if (body.destinationDomain !== undefined) {
|
||||
updateData.destinationDomain = body.destinationDomain;
|
||||
}
|
||||
if (body.destinationUrl !== undefined) {
|
||||
updateData.destinationUrl = body.destinationUrl;
|
||||
if (body.pathMatchType !== undefined) {
|
||||
updateData.pathMatchType = body.pathMatchType;
|
||||
}
|
||||
if (body.matchPath !== undefined) {
|
||||
updateData.matchPath = body.matchPath;
|
||||
}
|
||||
if (body.rewritePath !== undefined) {
|
||||
updateData.rewritePath = body.rewritePath;
|
||||
}
|
||||
if (body.rewritePathType !== undefined) {
|
||||
updateData.rewritePathType = body.rewritePathType;
|
||||
}
|
||||
if (body.permanent !== undefined) {
|
||||
updateData.permanent = body.permanent;
|
||||
|
||||
@@ -9,8 +9,15 @@ export const redirectNiceIdSchema = z
|
||||
"niceId can only contain letters, numbers, and dashes"
|
||||
);
|
||||
|
||||
export const redirectSourcePathSchema = z
|
||||
.string()
|
||||
.nonempty()
|
||||
.regex(/^\//, "sourcePath must start with a /")
|
||||
.default("/*");
|
||||
export const redirectPathMatchTypeSchema = z.enum(["exact", "prefix", "regex"]);
|
||||
|
||||
export const redirectRewritePathTypeSchema = z.enum([
|
||||
"exact",
|
||||
"prefix",
|
||||
"regex",
|
||||
"stripPrefix"
|
||||
]);
|
||||
|
||||
export const redirectMatchPathSchema = z.string().nonempty().default("*");
|
||||
|
||||
export const redirectRewritePathSchema = z.string().nonempty();
|
||||
|
||||
Reference in New Issue
Block a user