Merge branch 'resource-policies' into dev

This commit is contained in:
Owen
2026-05-28 15:30:16 -07:00
81 changed files with 13114 additions and 1618 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,60 @@
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 { ResourcePolicyProvider } from "@app/providers/ResourcePolicyProvider";
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 policyResponse: GetResourcePolicyResponse | null = null;
try {
const res = await internal.get<
AxiosResponse<GetResourcePolicyResponse>
>(
`/org/${params.orgId}/resource-policy/${params.niceId}`,
await authCookieHeader()
);
policyResponse = res.data.data;
} catch {
redirect(`/${params.orgId}/settings/policies/resource`);
}
if (!policyResponse) {
redirect(`/${params.orgId}/settings/policies/resource`);
}
return (
<>
<div className="flex justify-between">
<SettingsSectionTitle
title={t("resourcePolicySetting", {
policyName: policyResponse.name
})}
description={t("resourcePolicySettingDescription")}
/>
<Button asChild variant="outline">
<Link href={`/${params.orgId}/settings/policies/resource`}>
{t("resourcePoliciesSeeAll")}
</Link>
</Button>
</div>
<ResourcePolicyProvider policy={policyResponse}>
<EditPolicyForm />
</ResourcePolicyProvider>
</>
);
}

View File

@@ -0,0 +1,35 @@
import { CreatePolicyForm } from "@app/components/resource-policy/CreatePolicyForm";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { Button } from "@app/components/ui/button";
import { getTranslations } from "next-intl/server";
import Link from "next/link";
export interface CreateResourcePolicyPageProps {
params: Promise<{ orgId: string }>;
}
export default async function CreateResourcePolicyPage(
props: CreateResourcePolicyPageProps
) {
const params = await props.params;
const t = await getTranslations();
return (
<>
<div className="flex justify-between">
<SettingsSectionTitle
title={t("resourcePoliciesCreate")}
description={t("resourcePoliciesCreateDescription")}
/>
<Button asChild variant="outline">
<Link href={`/${params.orgId}/settings/policies/resource`}>
{t("resourcePoliciesSeeAll")}
</Link>
</Button>
</div>
<CreatePolicyForm />
</>
);
}

View File

@@ -0,0 +1,68 @@
import { ResourcePoliciesTable } from "@app/components/ResourcePoliciesTable";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { internal } from "@app/lib/api";
import { authCookieHeader } from "@app/lib/api/cookies";
import { getCachedOrg } from "@app/lib/api/getCachedOrg";
import type { GetOrgResponse } from "@server/routers/org";
import type { ListResourcePoliciesResponse } from "@server/routers/resource/types";
import type { AxiosResponse } from "axios";
import { getTranslations } from "next-intl/server";
import { redirect } from "next/navigation";
export interface ResourcePoliciesPageProps {
params: Promise<{ orgId: string }>;
searchParams: Promise<Record<string, string>>;
}
export default async function ResourcePoliciesPage(
props: ResourcePoliciesPageProps
) {
const params = await props.params;
const t = await getTranslations();
const searchParams = new URLSearchParams(await props.searchParams);
let org: GetOrgResponse | null = null;
try {
const res = await getCachedOrg(params.orgId);
org = res.data.data;
} catch {
redirect(`/${params.orgId}/settings/resources`);
}
let policies: ListResourcePoliciesResponse["policies"] = [];
let pagination: ListResourcePoliciesResponse["pagination"] = {
total: 0,
page: 1,
pageSize: 20
};
try {
const res = await internal.get<
AxiosResponse<ListResourcePoliciesResponse>
>(
`/org/${params.orgId}/resource-policies?${searchParams.toString()}`,
await authCookieHeader()
);
const responseData = res.data.data;
policies = responseData.policies;
pagination = responseData.pagination;
} catch (e) {}
return (
<>
<SettingsSectionTitle
title={t("resourcePoliciesTitle")}
description={t("resourcePoliciesDescription")}
/>
<ResourcePoliciesTable
policies={policies}
orgId={params.orgId}
rowCount={pagination.total}
pagination={{
pageIndex: pagination.page - 1,
pageSize: pagination.pageSize
}}
/>
</>
);
}

View File

@@ -13,6 +13,7 @@ import { Layout } from "@app/components/Layout";
import { getTranslations } from "next-intl/server";
import { pullEnv } from "@app/lib/pullEnv";
import { orgNavSections } from "@app/app/navigation";
import { getCachedOrgUser } from "@app/lib/api/getCachedOrgUser";
export const dynamic = "force-dynamic";
@@ -48,13 +49,7 @@ export default async function SettingsLayout(props: SettingsLayoutProps) {
const t = await getTranslations();
try {
const getOrgUser = cache(() =>
internal.get<AxiosResponse<GetOrgUserResponse>>(
`/org/${params.orgId}/user/${user.userId}`,
cookie
)
);
const orgUser = await getOrgUser();
const orgUser = await getCachedOrgUser(params.orgId, user.userId);
if (!orgUser.data.data.isAdmin && !orgUser.data.data.isOwner) {
throw new Error(t("userErrorNotAdminOrOwner"));

View File

@@ -169,8 +169,6 @@ export default function GeneralPage() {
router.replace(`?${params.toString()}`, { scroll: false });
};
<<<<<<< HEAD
=======
const queryDateTime = async (
startDate: DateTimeValue,
endDate: DateTimeValue,
@@ -276,7 +274,6 @@ export default function GeneralPage() {
}
};
>>>>>>> main
const exportData = async () => {
try {
const params: any = {

View File

@@ -143,9 +143,7 @@ export default function ConnectionLogsPage() {
enabled: isPaidUser(tierMatrix.connectionLogs) && build !== "oss"
});
const rows = isLoading
? generateSampleConnectionLogs()
: (data?.log ?? []);
const rows = isLoading ? generateSampleConnectionLogs() : (data?.log ?? []);
const totalCount = data?.pagination?.total ?? 0;
const filterAttributes = data?.filterAttributes ?? {
protocols: [],
@@ -205,8 +203,6 @@ export default function ConnectionLogsPage() {
router.replace(`?${params.toString()}`, { scroll: false });
};
<<<<<<< HEAD
=======
const queryDateTime = async (
startDate: DateTimeValue,
endDate: DateTimeValue,
@@ -311,7 +307,6 @@ export default function ConnectionLogsPage() {
}
};
>>>>>>> main
const exportData = async () => {
try {
const params: any = {
@@ -729,10 +724,11 @@ function generateSampleConnectionLogs(): QueryConnectionAuditLogResponse["log"]
userId: i % 2 === 0 ? `user-${i}` : null,
sourceAddr: `192.168.1.${i + 1}:${40000 + i}`,
destAddr: destAddrs[Math.floor(Math.random() * destAddrs.length)],
protocol:
protocols[Math.floor(Math.random() * protocols.length)],
protocol: protocols[Math.floor(Math.random() * protocols.length)],
startedAt,
endedAt: active ? null : startedAt + Math.floor(Math.random() * 3600),
endedAt: active
? null
: startedAt + Math.floor(Math.random() * 3600),
bytesTx: active ? null : Math.floor(Math.random() * 1024 * 1024),
bytesRx: active ? null : Math.floor(Math.random() * 1024 * 1024),
resourceName: `Resource ${(i % 3) + 1}`,

View File

@@ -96,10 +96,10 @@ export default async function ResourceLayout(props: ResourceLayoutProps) {
title: t("authentication"),
href: `/{orgId}/settings/resources/proxy/{niceId}/authentication`
});
navItems.push({
title: t("rules"),
href: `/{orgId}/settings/resources/proxy/{niceId}/rules`
});
// navItems.push({
// title: t("rules"),
// href: `/{orgId}/settings/resources/proxy/{niceId}/rules`
// });
}
return (

View File

@@ -114,7 +114,13 @@ import { useTranslations } from "next-intl";
import Link from "next/link";
import { useParams, useRouter } from "next/navigation";
import { toASCII } from "punycode";
import { useEffect, useMemo, useState, useCallback } from "react";
import {
useMemo,
useState,
useCallback,
useTransition,
useEffect
} from "react";
import { Controller, useForm } from "react-hook-form";
import { z } from "zod";
import { cn } from "@app/lib/cn";
@@ -226,7 +232,7 @@ export default function Page() {
>([]);
const [loadingExitNodes, setLoadingExitNodes] = useState(build === "saas");
const [createLoading, setCreateLoading] = useState(false);
const [createLoading, startTransition] = useTransition();
const [showSnippets, setShowSnippets] = useState(false);
const [niceId, setNiceId] = useState<string>("");
@@ -386,8 +392,6 @@ export default function Page() {
};
async function onSubmit() {
setCreateLoading(true);
const baseData = baseForm.getValues();
try {
@@ -628,8 +632,6 @@ export default function Page() {
)
});
}
setCreateLoading(false);
}
// SSH strategy options

View File

@@ -11,6 +11,7 @@ import {
CreditCard,
Fingerprint,
Globe,
GlobeIcon,
GlobeLock,
KeyRound,
Laptop,
@@ -22,6 +23,7 @@ import {
ScanEye,
Server,
Settings,
ShieldIcon,
SquareMousePointer,
TagIcon,
TicketCheck,
@@ -135,6 +137,24 @@ export const orgNavSections = (
}
]
},
...(build !== "oss"
? [
{
title: "sidebarPolicies",
icon: <ShieldIcon className="size-4 flex-none" />,
items: [
{
title: "sidebarResourcePolicies",
href: "/{orgId}/settings/policies/resource",
icon: (
<GlobeIcon className="size-4 flex-none" />
)
}
]
}
]
: []),
// PaidFeaturesAlert
...((build === "oss" && !env?.flags.disableEnterpriseFeatures) ||
build === "saas" ||