mirror of
https://github.com/fosrl/pangolin.git
synced 2026-09-04 18:29:21 +02:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e7fdbf9e85 | |||
| 778a840ed7 | |||
| 9d19195089 | |||
| 54bbe82504 |
Generated
+284
-669
File diff suppressed because it is too large
Load Diff
+8
-8
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@asteasolutions/zod-to-openapi": "9.1.0",
|
||||
"@aws-sdk/client-s3": "3.1123.0",
|
||||
"@aws-sdk/client-s3": "3.1121.0",
|
||||
"@devolutions/iron-remote-desktop": "https://static.pangolin.net/packages/devolutions-iron-remote-desktop-0.0.0.tgz",
|
||||
"@devolutions/iron-remote-desktop-rdp": "https://static.pangolin.net/packages/devolutions-iron-remote-desktop-rdp-0.0.1.tgz",
|
||||
"@headlessui/react": "2.2.10",
|
||||
@@ -69,7 +69,7 @@
|
||||
"@simplewebauthn/server": "13.3.3",
|
||||
"@tailwindcss/forms": "0.5.11",
|
||||
"@tanstack/react-query": "5.102.8",
|
||||
"@tanstack/react-table": "9.2.4",
|
||||
"@tanstack/react-table": "8.21.3",
|
||||
"@xterm/addon-fit": "^0.11.0",
|
||||
"@xterm/addon-web-links": "^0.12.0",
|
||||
"@xterm/xterm": "^6.0.0",
|
||||
@@ -99,7 +99,7 @@
|
||||
"lucide-react": "1.38.0",
|
||||
"maxmind": "5.0.7",
|
||||
"moment": "2.30.1",
|
||||
"next": "16.3.4",
|
||||
"next": "16.3.3",
|
||||
"next-intl": "4.14.1",
|
||||
"next-themes": "0.4.6",
|
||||
"nextjs-toploader": "3.9.17",
|
||||
@@ -110,7 +110,7 @@
|
||||
"posthog-node": "5.51.4",
|
||||
"qrcode.react": "4.2.0",
|
||||
"react": "19.2.8",
|
||||
"react-day-picker": "10.0.1",
|
||||
"react-day-picker": "9.14.0",
|
||||
"react-dom": "19.2.8",
|
||||
"react-easy-sort": "1.8.0",
|
||||
"react-hook-form": "7.87.0",
|
||||
@@ -169,15 +169,15 @@
|
||||
"esbuild": "0.28.2",
|
||||
"esbuild-node-externals": "2.0.0",
|
||||
"eslint": "10.9.1",
|
||||
"eslint-config-next": "16.3.4",
|
||||
"eslint-config-next": "16.3.3",
|
||||
"postcss": "8.5.26",
|
||||
"prettier": "3.9.6",
|
||||
"react-email": "6.9.3",
|
||||
"tailwindcss": "4.3.3",
|
||||
"tsc-alias": "1.9.3",
|
||||
"tsc-alias": "1.9.2",
|
||||
"tsx": "4.23.13",
|
||||
"typescript": "7.0.2",
|
||||
"typescript-eslint": "8.69.0"
|
||||
"typescript": "6.0.3",
|
||||
"typescript-eslint": "8.68.0"
|
||||
},
|
||||
"overrides": {
|
||||
"esbuild": "0.28.2",
|
||||
|
||||
@@ -112,6 +112,11 @@ export class Config {
|
||||
? "true"
|
||||
: "false";
|
||||
|
||||
process.env.FLAGS_DISABLE_VIRTUAL_API_KEYS_UI = parsedConfig.flags
|
||||
?.disable_virtual_api_keys_ui
|
||||
? "true"
|
||||
: "false";
|
||||
|
||||
this.rawConfig = parsedConfig;
|
||||
}
|
||||
|
||||
|
||||
@@ -442,6 +442,7 @@ export const configSchema = z
|
||||
disable_config_managed_domains: z.boolean().optional(),
|
||||
disable_product_help_banners: z.boolean().optional(),
|
||||
disable_enterprise_features: z.boolean().optional(),
|
||||
disable_virtual_api_keys_ui: z.boolean().optional(),
|
||||
enable_acme_cert_sync: z.boolean().optional().default(true),
|
||||
disable_private_http_placeholder: z
|
||||
.boolean()
|
||||
|
||||
@@ -104,7 +104,8 @@ const processMessage = async (
|
||||
|
||||
const handler = messageHandlers[message.type];
|
||||
if (!handler) {
|
||||
throw new Error(`Unsupported message type: ${message.type}`);
|
||||
logger.debug(`No handler found for message type: ${message.type}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await handler({
|
||||
|
||||
@@ -353,7 +353,10 @@ const setupConnection = async (
|
||||
|
||||
const handler = messageHandlers[message.type];
|
||||
if (!handler) {
|
||||
throw new Error(`Unsupported message type: ${message.type}`);
|
||||
logger.debug(
|
||||
`No handler found for message type: ${message.type}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await handler({
|
||||
|
||||
@@ -76,6 +76,11 @@ export default async function KeysPage(props: KeysPageProps) {
|
||||
redirect("/");
|
||||
}
|
||||
|
||||
const env = pullEnv();
|
||||
if (env.flags.disableVirtualApiKeysUi) {
|
||||
redirect(`/${orgId}`);
|
||||
}
|
||||
|
||||
let keysData: ListMyVirtualApiKeysResponse | null = null;
|
||||
try {
|
||||
const res = await internal.get<
|
||||
@@ -90,7 +95,6 @@ export default async function KeysPage(props: KeysPageProps) {
|
||||
redirect(`/${orgId}`);
|
||||
}
|
||||
|
||||
const env = pullEnv();
|
||||
const primaryOrg = orgs.find((o) => o.orgId === orgId)?.isPrimaryOrg;
|
||||
const isAdminOrOwner = Boolean(overview?.isAdmin || overview?.isOwner);
|
||||
|
||||
@@ -106,6 +110,7 @@ export default async function KeysPage(props: KeysPageProps) {
|
||||
showSidebar={false}
|
||||
launcherMode
|
||||
showViewAsAdmin={isAdminOrOwner}
|
||||
env={env}
|
||||
>
|
||||
<UserVirtualApiKeys orgId={orgId} initialData={keysData} />
|
||||
</Layout>
|
||||
|
||||
@@ -83,6 +83,7 @@ export default async function OrgPage(props: OrgPageProps) {
|
||||
showSidebar={false}
|
||||
launcherMode
|
||||
showViewAsAdmin={isAdminOrOwner}
|
||||
env={env}
|
||||
>
|
||||
{overview && launcherData ? (
|
||||
<ResourceLauncher
|
||||
|
||||
@@ -117,6 +117,7 @@ export default function SecurityPage() {
|
||||
}
|
||||
|
||||
function LogRetentionSectionForm({ org }: SectionFormProps) {
|
||||
const { updateOrg } = useOrgContext();
|
||||
const form = useForm({
|
||||
resolver: zodResolver(
|
||||
SecurityFormSchema.pick({
|
||||
@@ -173,6 +174,11 @@ function LogRetentionSectionForm({ org }: SectionFormProps) {
|
||||
// Update organization
|
||||
await api.post(`/org/${org.orgId}`, reqData);
|
||||
|
||||
// Update the org context immediately so the dropdowns reflect
|
||||
// the saved values without waiting on a re-fetch that could
|
||||
// race a lagging read replica
|
||||
updateOrg(reqData);
|
||||
|
||||
toast({
|
||||
title: t("orgUpdated"),
|
||||
description: t("orgUpdatedDescription")
|
||||
@@ -199,7 +205,10 @@ function LogRetentionSectionForm({ org }: SectionFormProps) {
|
||||
<SettingsSectionForm>
|
||||
<Form {...form}>
|
||||
<form
|
||||
action={formAction}
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
formAction();
|
||||
}}
|
||||
className="grid gap-4"
|
||||
id="org-log-retention-settings-form"
|
||||
>
|
||||
@@ -827,6 +836,7 @@ function LogRetentionSectionForm({ org }: SectionFormProps) {
|
||||
|
||||
function SecuritySettingsSectionForm({ org }: SectionFormProps) {
|
||||
const router = useRouter();
|
||||
const { updateOrg } = useOrgContext();
|
||||
const form = useForm({
|
||||
resolver: zodResolver(
|
||||
SecurityFormSchema.pick({
|
||||
@@ -899,6 +909,11 @@ function SecuritySettingsSectionForm({ org }: SectionFormProps) {
|
||||
// Update organization
|
||||
await api.post(`/org/${org.orgId}`, reqData);
|
||||
|
||||
// Update the org context immediately so the dropdowns reflect
|
||||
// the saved values without waiting on a re-fetch that could
|
||||
// race a lagging read replica
|
||||
updateOrg(reqData);
|
||||
|
||||
toast({
|
||||
title: t("orgUpdated"),
|
||||
description: t("orgUpdatedDescription")
|
||||
@@ -942,7 +957,10 @@ function SecuritySettingsSectionForm({ org }: SectionFormProps) {
|
||||
<SettingsSectionForm>
|
||||
<Form {...form}>
|
||||
<form
|
||||
action={formAction}
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
formAction();
|
||||
}}
|
||||
ref={formRef}
|
||||
id="security-settings-section-form"
|
||||
className="space-y-4"
|
||||
|
||||
@@ -30,9 +30,15 @@ import { normalizePostAuthPath } from "@server/lib/normalizePostAuthPath";
|
||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Resource Access"
|
||||
};
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const env = pullEnv();
|
||||
const title =
|
||||
env.branding.resourceAuthPage?.titleText ||
|
||||
env.branding.appName ||
|
||||
"Resource Access";
|
||||
|
||||
return { title };
|
||||
}
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
|
||||
+10
-6
@@ -53,17 +53,21 @@ export type OrgNavSectionsOptions = {
|
||||
};
|
||||
|
||||
// Merged from 'user-management-and-resources' branch
|
||||
export const orgLangingNavItems: SidebarNavItem[] = [
|
||||
export const orgLangingNavItems = (env?: Env): SidebarNavItem[] => [
|
||||
{
|
||||
title: "sidebarAccount",
|
||||
href: "/{orgId}",
|
||||
icon: <LayoutGrid className="size-4 flex-none" />
|
||||
},
|
||||
{
|
||||
title: "sidebarMyApiKeys",
|
||||
href: "/{orgId}/keys",
|
||||
icon: <KeyRound className="size-4 flex-none" />
|
||||
}
|
||||
...(!env?.flags.disableVirtualApiKeysUi
|
||||
? [
|
||||
{
|
||||
title: "sidebarMyApiKeys",
|
||||
href: "/{orgId}/keys",
|
||||
icon: <KeyRound className="size-4 flex-none" />
|
||||
}
|
||||
]
|
||||
: [])
|
||||
];
|
||||
|
||||
export const orgNavSections = (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import { cn } from "@app/lib/cn";
|
||||
import { ListUserOrgsResponse } from "@server/routers/org";
|
||||
import { Env } from "@app/lib/types/env";
|
||||
import {
|
||||
orgLangingNavItems,
|
||||
type CommandBarNavSection,
|
||||
@@ -25,6 +26,7 @@ interface LayoutProps {
|
||||
defaultSidebarCollapsed?: boolean;
|
||||
launcherMode?: boolean;
|
||||
showViewAsAdmin?: boolean;
|
||||
env?: Env;
|
||||
}
|
||||
|
||||
export async function Layout({
|
||||
@@ -38,7 +40,8 @@ export async function Layout({
|
||||
showTopBar = true,
|
||||
defaultSidebarCollapsed = false,
|
||||
launcherMode = false,
|
||||
showViewAsAdmin = false
|
||||
showViewAsAdmin = false,
|
||||
env
|
||||
}: LayoutProps) {
|
||||
const allCookies = await cookies();
|
||||
const sidebarStateCookie = allCookies.get("pangolin-sidebar-state")?.value;
|
||||
@@ -49,7 +52,7 @@ export async function Layout({
|
||||
(sidebarStateCookie !== "expanded" && defaultSidebarCollapsed);
|
||||
|
||||
const launcherNavItems: SidebarNavItem[] = launcherMode
|
||||
? orgLangingNavItems
|
||||
? orgLangingNavItems(env)
|
||||
: [];
|
||||
|
||||
return (
|
||||
|
||||
@@ -70,6 +70,10 @@ export function pullEnv(): Env {
|
||||
: false,
|
||||
disableEnterpriseFeatures:
|
||||
process.env.DISABLE_ENTERPRISE_FEATURES === "true"
|
||||
? true
|
||||
: false,
|
||||
disableVirtualApiKeysUi:
|
||||
process.env.FLAGS_DISABLE_VIRTUAL_API_KEYS_UI === "true"
|
||||
? true
|
||||
: false
|
||||
},
|
||||
|
||||
@@ -36,6 +36,7 @@ export type Env = {
|
||||
usePangolinDns: boolean;
|
||||
disableProductHelpBanners: boolean;
|
||||
disableEnterpriseFeatures: boolean;
|
||||
disableVirtualApiKeysUi: boolean;
|
||||
};
|
||||
branding: {
|
||||
appName?: string;
|
||||
|
||||
Reference in New Issue
Block a user