This commit is contained in:
Fred KISSIE
2026-02-26 19:20:15 +01:00
parent 4d803a40c9
commit c5231d37f6
19 changed files with 4177 additions and 116 deletions

View File

@@ -0,0 +1,23 @@
import { getCachedOrg } from "@app/lib/api/getCachedOrg";
import OrgProvider from "@app/providers/OrgProvider";
import type { GetOrgResponse } from "@server/routers/org";
import { redirect } from "next/navigation";
export interface PolicyLayoutPageProps {
params: Promise<{ orgId: string }>;
children: React.ReactNode;
}
export default async function PolicyLayoutPage(props: PolicyLayoutPageProps) {
const params = await props.params;
let org: GetOrgResponse | null = null;
try {
const res = await getCachedOrg(params.orgId);
org = res.data.data;
} catch {
redirect(`/${params.orgId}/settings`);
}
return <OrgProvider org={org}>{props.children}</OrgProvider>;
}

View File

@@ -0,0 +1,58 @@
import { EditPolicyForm } from "@app/components/resource-policy/EditPolicyForm";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { Button } from "@app/components/ui/button";
import { internal } from "@app/lib/api";
import { authCookieHeader } from "@app/lib/api/cookies";
import type { ResourcePolicy } from "@server/db";
import type { GetResourcePolicyResponse } from "@server/routers/policy";
import type { AxiosResponse } from "axios";
import { getTranslations } from "next-intl/server";
import Link from "next/link";
import { redirect } from "next/navigation";
export interface EditPolicyPageProps {
params: Promise<{ niceId: string; orgId: string }>;
}
export default async function EditPolicyPage(props: EditPolicyPageProps) {
const params = await props.params;
const t = await getTranslations();
let policy: ResourcePolicy | null = null;
try {
const res = await internal.get<
AxiosResponse<GetResourcePolicyResponse>
>(
`/org/${params.orgId}/resource-policy/${params.niceId}`,
await authCookieHeader()
);
policy = res.data.data.policy;
} catch {
redirect(`/${params.orgId}/settings/policies/resource`);
}
if (!policy) {
redirect(`/${params.orgId}/settings/policies/resource`);
}
return (
<>
<div className="flex justify-between">
<SettingsSectionTitle
title={t("resourcePolicySetting", {
policyName: policy.name
})}
description={t("resourcePolicySettingDescription")}
/>
<Button asChild variant="outline">
<Link href={`/${params.orgId}/settings/policies/resource`}>
{t("resourcePoliciesSeeAll")}
</Link>
</Button>
</div>
<EditPolicyForm policy={policy} />
</>
);
}

View File

@@ -1,12 +1,8 @@
import { CreatePolicyForm } from "@app/components/resource-policy/CreatePolicyForm";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { Button } from "@app/components/ui/button";
import { getCachedOrg } from "@app/lib/api/getCachedOrg";
import OrgProvider from "@app/providers/OrgProvider";
import type { GetOrgResponse } from "@server/routers/org";
import { getTranslations } from "next-intl/server";
import Link from "next/link";
import { redirect } from "next/navigation";
export interface CreateResourcePolicyPageProps {
params: Promise<{ orgId: string }>;
@@ -18,13 +14,6 @@ export default async function CreateResourcePolicyPage(
const params = await props.params;
const t = await getTranslations();
let org: GetOrgResponse | null = null;
try {
const res = await getCachedOrg(params.orgId);
org = res.data.data;
} catch {
redirect(`/${params.orgId}/settings/resources`);
}
return (
<>
<div className="flex justify-between">
@@ -34,15 +23,13 @@ export default async function CreateResourcePolicyPage(
/>
<Button asChild variant="outline">
<Link href={`/${params.orgId}/settings/resources/policies`}>
<Link href={`/${params.orgId}/settings/policies/resource`}>
{t("resourcePoliciesSeeAll")}
</Link>
</Button>
</div>
<OrgProvider org={org}>
<CreatePolicyForm />
</OrgProvider>
<CreatePolicyForm />
</>
);
}

View File

@@ -55,17 +55,15 @@ export default async function ResourcePoliciesPage(
description={t("resourcePoliciesDescription")}
/>
<OrgProvider org={org}>
<ResourcePoliciesTable
policies={policies}
orgId={params.orgId}
rowCount={pagination.total}
pagination={{
pageIndex: pagination.page - 1,
pageSize: pagination.pageSize
}}
/>
</OrgProvider>
<ResourcePoliciesTable
policies={policies}
orgId={params.orgId}
rowCount={pagination.total}
pagination={{
pageIndex: pagination.page - 1,
pageSize: pagination.pageSize
}}
/>
</>
);
}

View File

@@ -132,7 +132,7 @@ export const orgNavSections = (env?: Env): SidebarNavSection[] => [
items: [
{
title: "sidebarResourcePolicies",
href: "/{orgId}/settings/policies/resources",
href: "/{orgId}/settings/policies/resource",
icon: (
<GlobeIcon className="size-4 flex-none" />
)

View File

@@ -103,7 +103,7 @@ export function ResourcePoliciesTable({
<DropdownMenuContent align="end">
<Link
className="block w-full"
href={`/${policyRow.orgId}/settings/resources/proxy/${policyRow.niceId}`}
href={`/${policyRow.orgId}/settings/policies/resource/${policyRow.niceId}`}
>
<DropdownMenuItem>
{t("viewSettings")}
@@ -122,7 +122,7 @@ export function ResourcePoliciesTable({
</DropdownMenuContent>
</DropdownMenu>
<Link
href={`/${policyRow.orgId}/settings/resources/proxy/${policyRow.niceId}`}
href={`/${policyRow.orgId}/settings/policies/resource/${policyRow.niceId}`}
>
<Button variant={"outline"}>
{t("edit")}
@@ -165,7 +165,7 @@ export function ResourcePoliciesTable({
onAdd={() =>
startNavigation(() =>
router.push(
`/${orgId}/settings/policies/resources/create`
`/${orgId}/settings/policies/resource/create`
)
)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -121,6 +121,62 @@ type LocalRule = {
updated?: boolean;
};
// ─── PolicyNameSection ──────────────────────────────────────────────────
type PolicyNameSectionProps = {
form: UseFormReturn<PolicyFormValues, any, any>;
isEditing?: boolean;
};
export function PolicyNameSection({ form }: PolicyNameSectionProps) {
const t = useTranslations();
return (
<SettingsSection>
<SettingsSectionHeader>
<SettingsSectionTitle>
{t("resourcePolicyName")}
</SettingsSectionTitle>
<SettingsSectionDescription>
{t("resourcePolicyNameDescription")}
</SettingsSectionDescription>
</SettingsSectionHeader>
<SettingsSectionBody>
<SettingsSectionForm>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>{t("name")}</FormLabel>
<FormControl>
<Input
{...field}
placeholder={t(
"resourcePolicyNamePlaceholder"
)}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</SettingsSectionForm>
</SettingsSectionBody>
<div className="flex py-6 justify-end">
<Button
type="submit"
// loading={isSubmitting}
// disabled={isSubmitting}
>
{t("resourcePoliciesCreate")}
</Button>
</div>
</SettingsSection>
);
}
// ─── PolicyUsersRolesSection ──────────────────────────────────────────────────
type PolicyUsersRolesSectionProps = {
form: UseFormReturn<PolicyFormValues, any, any>;
allRoles: { id: string; text: string }[];
@@ -128,8 +184,6 @@ type PolicyUsersRolesSectionProps = {
allIdps: { id: number; text: string }[];
};
// ─── PolicyUsersRolesSection ──────────────────────────────────────────────────
export function PolicyUsersRolesSection({
form,
allRoles,

View File

@@ -45,3 +45,21 @@ export const createPolicySchema = z.object({
});
export type PolicyFormValues = z.infer<typeof createPolicySchema>;
export const addRuleSchema = z.object({
action: z.enum(["ACCEPT", "DROP", "PASS"]),
match: z.string(),
value: z.string(),
priority: z.coerce.number<number>().int().optional()
});
export type LocalRule = {
ruleId: number;
action: "ACCEPT" | "DROP" | "PASS";
match: string;
value: string;
priority: number;
enabled: boolean;
new?: boolean;
updated?: boolean;
};