mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-27 07:16:40 +00:00
✨ filter by auth state
This commit is contained in:
@@ -17,7 +17,18 @@ import {
|
|||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import { sql, eq, or, inArray, and, count, ilike } from "drizzle-orm";
|
import {
|
||||||
|
sql,
|
||||||
|
eq,
|
||||||
|
or,
|
||||||
|
inArray,
|
||||||
|
and,
|
||||||
|
count,
|
||||||
|
ilike,
|
||||||
|
asc,
|
||||||
|
not,
|
||||||
|
isNull
|
||||||
|
} from "drizzle-orm";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { fromZodError } from "zod-validation-error";
|
import { fromZodError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
@@ -48,7 +59,7 @@ const listResourcesSchema = z.object({
|
|||||||
.optional()
|
.optional()
|
||||||
.catch(undefined),
|
.catch(undefined),
|
||||||
authState: z
|
authState: z
|
||||||
.enum(["protected", "not_protected"])
|
.enum(["protected", "not_protected", "none"])
|
||||||
.optional()
|
.optional()
|
||||||
.catch(undefined)
|
.catch(undefined)
|
||||||
});
|
});
|
||||||
@@ -277,9 +288,63 @@ export async function listResources(
|
|||||||
conditions = and(conditions, eq(resources.enabled, enabled));
|
conditions = and(conditions, eq(resources.enabled, enabled));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof authState !== "undefined") {
|
||||||
|
switch (authState) {
|
||||||
|
case "none":
|
||||||
|
conditions = and(conditions, eq(resources.http, false));
|
||||||
|
break;
|
||||||
|
case "protected":
|
||||||
|
conditions = and(
|
||||||
|
conditions,
|
||||||
|
or(
|
||||||
|
eq(resources.sso, true),
|
||||||
|
eq(resources.emailWhitelistEnabled, true),
|
||||||
|
not(isNull(resourceHeaderAuth.headerAuthId)),
|
||||||
|
not(isNull(resourcePincode.pincodeId)),
|
||||||
|
not(isNull(resourcePassword.passwordId))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "not_protected":
|
||||||
|
conditions = and(
|
||||||
|
conditions,
|
||||||
|
not(eq(resources.sso, true)),
|
||||||
|
not(eq(resources.emailWhitelistEnabled, true)),
|
||||||
|
isNull(resourceHeaderAuth.headerAuthId),
|
||||||
|
isNull(resourcePincode.pincodeId),
|
||||||
|
isNull(resourcePassword.passwordId)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const countQuery: any = db
|
const countQuery: any = db
|
||||||
.select({ count: count() })
|
.select({ count: count() })
|
||||||
.from(resources)
|
.from(resources)
|
||||||
|
.leftJoin(
|
||||||
|
resourcePassword,
|
||||||
|
eq(resourcePassword.resourceId, resources.resourceId)
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
resourcePincode,
|
||||||
|
eq(resourcePincode.resourceId, resources.resourceId)
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
resourceHeaderAuth,
|
||||||
|
eq(resourceHeaderAuth.resourceId, resources.resourceId)
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
resourceHeaderAuthExtendedCompatibility,
|
||||||
|
eq(
|
||||||
|
resourceHeaderAuthExtendedCompatibility.resourceId,
|
||||||
|
resources.resourceId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.leftJoin(targets, eq(targets.resourceId, resources.resourceId))
|
||||||
|
.leftJoin(
|
||||||
|
targetHealthCheck,
|
||||||
|
eq(targetHealthCheck.targetId, targets.targetId)
|
||||||
|
)
|
||||||
.where(conditions);
|
.where(conditions);
|
||||||
|
|
||||||
const baseQuery = queryResourcesBase();
|
const baseQuery = queryResourcesBase();
|
||||||
@@ -287,7 +352,8 @@ export async function listResources(
|
|||||||
const rows: JoinedRow[] = await baseQuery
|
const rows: JoinedRow[] = await baseQuery
|
||||||
.where(conditions)
|
.where(conditions)
|
||||||
.limit(pageSize)
|
.limit(pageSize)
|
||||||
.offset(pageSize * (page - 1));
|
.offset(pageSize * (page - 1))
|
||||||
|
.orderBy(asc(resources.resourceId));
|
||||||
|
|
||||||
// avoids TS issues with reduce/never[]
|
// avoids TS issues with reduce/never[]
|
||||||
const map = new Map<number, ResourceWithTargets>();
|
const map = new Map<number, ResourceWithTargets>();
|
||||||
|
|||||||
@@ -185,23 +185,24 @@ export default function ProxyResourcesTable({
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function toggleResourceEnabled(val: boolean, resourceId: number) {
|
async function toggleResourceEnabled(val: boolean, resourceId: number) {
|
||||||
await api
|
try {
|
||||||
.post<AxiosResponse<UpdateResourceResponse>>(
|
await api.post<AxiosResponse<UpdateResourceResponse>>(
|
||||||
`resource/${resourceId}`,
|
`resource/${resourceId}`,
|
||||||
{
|
{
|
||||||
enabled: val
|
enabled: val
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
.catch((e) => {
|
router.refresh();
|
||||||
toast({
|
} catch (e) {
|
||||||
variant: "destructive",
|
toast({
|
||||||
title: t("resourcesErrorUpdate"),
|
variant: "destructive",
|
||||||
description: formatAxiosError(
|
title: t("resourcesErrorUpdate"),
|
||||||
e,
|
description: formatAxiosError(
|
||||||
t("resourcesErrorUpdateDescription")
|
e,
|
||||||
)
|
t("resourcesErrorUpdateDescription")
|
||||||
});
|
)
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function TargetStatusCell({ targets }: { targets?: TargetHealth[] }) {
|
function TargetStatusCell({ targets }: { targets?: TargetHealth[] }) {
|
||||||
@@ -313,38 +314,14 @@ export default function ProxyResourcesTable({
|
|||||||
accessorKey: "name",
|
accessorKey: "name",
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
friendlyName: t("name"),
|
friendlyName: t("name"),
|
||||||
header: ({ column }) => {
|
header: () => <span className="p-3">{t("name")}</span>
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() =>
|
|
||||||
column.toggleSorting(column.getIsSorted() === "asc")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{t("name")}
|
|
||||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "niceId",
|
id: "niceId",
|
||||||
accessorKey: "nice",
|
accessorKey: "nice",
|
||||||
friendlyName: t("identifier"),
|
friendlyName: t("identifier"),
|
||||||
enableHiding: true,
|
enableHiding: true,
|
||||||
header: ({ column }) => {
|
header: () => <span className="p-3">{t("identifier")}</span>,
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() =>
|
|
||||||
column.toggleSorting(column.getIsSorted() === "asc")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{t("identifier")}
|
|
||||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
return <span>{row.original.nice || "-"}</span>;
|
return <span>{row.original.nice || "-"}</span>;
|
||||||
}
|
}
|
||||||
@@ -370,19 +347,7 @@ export default function ProxyResourcesTable({
|
|||||||
id: "status",
|
id: "status",
|
||||||
accessorKey: "status",
|
accessorKey: "status",
|
||||||
friendlyName: t("status"),
|
friendlyName: t("status"),
|
||||||
header: ({ column }) => {
|
header: () => <span className="p-3">{t("status")}</span>,
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() =>
|
|
||||||
column.toggleSorting(column.getIsSorted() === "asc")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{t("status")}
|
|
||||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const resourceRow = row.original;
|
const resourceRow = row.original;
|
||||||
return <TargetStatusCell targets={resourceRow.targets} />;
|
return <TargetStatusCell targets={resourceRow.targets} />;
|
||||||
@@ -430,19 +395,23 @@ export default function ProxyResourcesTable({
|
|||||||
{
|
{
|
||||||
accessorKey: "authState",
|
accessorKey: "authState",
|
||||||
friendlyName: t("authentication"),
|
friendlyName: t("authentication"),
|
||||||
header: ({ column }) => {
|
header: () => (
|
||||||
return (
|
<ColumnFilterButton
|
||||||
<Button
|
options={[
|
||||||
variant="ghost"
|
{ value: "protected", label: t("protected") },
|
||||||
onClick={() =>
|
{ value: "not_protected", label: t("notProtected") },
|
||||||
column.toggleSorting(column.getIsSorted() === "asc")
|
{ value: "none", label: t("none") }
|
||||||
}
|
]}
|
||||||
>
|
selectedValue={searchParams.get("authState") ?? undefined}
|
||||||
{t("authentication")}
|
onValueChange={(value) =>
|
||||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
handleFilterChange("authState", value)
|
||||||
</Button>
|
}
|
||||||
);
|
searchPlaceholder={t("searchPlaceholder")}
|
||||||
},
|
emptyMessage={t("emptySearchOptions")}
|
||||||
|
label={t("authentication")}
|
||||||
|
className="p-3"
|
||||||
|
/>
|
||||||
|
),
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const resourceRow = row.original;
|
const resourceRow = row.original;
|
||||||
return (
|
return (
|
||||||
@@ -487,16 +456,16 @@ export default function ProxyResourcesTable({
|
|||||||
),
|
),
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<Switch
|
<Switch
|
||||||
defaultChecked={
|
checked={
|
||||||
row.original.http
|
row.original.http
|
||||||
? !!row.original.domainId && row.original.enabled
|
? !!row.original.domainId && row.original.enabled
|
||||||
: row.original.enabled
|
: row.original.enabled
|
||||||
}
|
}
|
||||||
disabled={
|
disabled={row.original.http && !row.original.domainId}
|
||||||
row.original.http ? !row.original.domainId : false
|
|
||||||
}
|
|
||||||
onCheckedChange={(val) =>
|
onCheckedChange={(val) =>
|
||||||
toggleResourceEnabled(val, row.original.id)
|
startTransition(() =>
|
||||||
|
toggleResourceEnabled(val, row.original.id)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user