Compare commits

..

1 Commits

Author SHA1 Message Date
miloschwartz
848d4d91e6 fix sidebar 2026-02-23 13:40:08 -08:00
7 changed files with 489 additions and 540 deletions

881
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -33,7 +33,7 @@
},
"dependencies": {
"@asteasolutions/zod-to-openapi": "8.4.0",
"@aws-sdk/client-s3": "3.996.0",
"@aws-sdk/client-s3": "3.989.0",
"@faker-js/faker": "10.3.0",
"@headlessui/react": "2.2.9",
"@hookform/resolvers": "5.2.2",
@@ -89,7 +89,7 @@
"jmespath": "0.16.0",
"js-yaml": "4.1.1",
"jsonwebtoken": "9.0.3",
"lucide-react": "0.575.0",
"lucide-react": "0.563.0",
"maxmind": "5.0.5",
"moment": "2.30.1",
"next": "15.5.12",
@@ -100,7 +100,7 @@
"nodemailer": "8.0.1",
"oslo": "1.2.1",
"pg": "8.18.0",
"posthog-node": "5.25.0",
"posthog-node": "5.24.15",
"qrcode.react": "4.2.0",
"react": "19.2.4",
"react-day-picker": "9.13.2",
@@ -115,7 +115,7 @@
"sshpk": "^1.18.0",
"stripe": "20.3.1",
"swagger-ui-express": "5.0.1",
"tailwind-merge": "3.5.0",
"tailwind-merge": "3.4.0",
"topojson-client": "3.1.0",
"tw-animate-css": "1.4.0",
"use-debounce": "^10.1.0",

View File

@@ -11,7 +11,7 @@ import {
userSiteResources
} from "@server/db";
import { sites } from "@server/db";
import { eq, and, ne, inArray, or } from "drizzle-orm";
import { eq, and, ne, inArray } from "drizzle-orm";
import { Config } from "./types";
import logger from "@server/logger";
import { getNextAvailableAliasAddress } from "../ip";
@@ -142,10 +142,7 @@ export async function updateClientResources(
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
.where(
and(
or(
inArray(users.username, resourceData.users),
inArray(users.email, resourceData.users)
),
inArray(users.username, resourceData.users),
eq(userOrgs.orgId, orgId)
)
);
@@ -279,10 +276,7 @@ export async function updateClientResources(
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
.where(
and(
or(
inArray(users.username, resourceData.users),
inArray(users.email, resourceData.users)
),
inArray(users.username, resourceData.users),
eq(userOrgs.orgId, orgId)
)
);

View File

@@ -212,10 +212,7 @@ export async function updateProxyResources(
} else {
// Update existing resource
const isLicensed = await isLicensedOrSubscribed(
orgId,
tierMatrix.maintencePage
);
const isLicensed = await isLicensedOrSubscribed(orgId, tierMatrix.maintencePage);
if (!isLicensed) {
resourceData.maintenance = undefined;
}
@@ -593,10 +590,7 @@ export async function updateProxyResources(
existingRule.action !== getRuleAction(rule.action) ||
existingRule.match !== rule.match.toUpperCase() ||
existingRule.value !==
getRuleValue(
rule.match.toUpperCase(),
rule.value
) ||
getRuleValue(rule.match.toUpperCase(), rule.value) ||
existingRule.priority !== intendedPriority
) {
validateRule(rule);
@@ -654,10 +648,7 @@ export async function updateProxyResources(
);
}
const isLicensed = await isLicensedOrSubscribed(
orgId,
tierMatrix.maintencePage
);
const isLicensed = await isLicensedOrSubscribed(orgId, tierMatrix.maintencePage);
if (!isLicensed) {
resourceData.maintenance = undefined;
}
@@ -944,12 +935,7 @@ async function syncUserResources(
.select()
.from(users)
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
.where(
and(
or(eq(users.username, username), eq(users.email, username)),
eq(userOrgs.orgId, orgId)
)
)
.where(and(eq(users.username, username), eq(userOrgs.orgId, orgId)))
.limit(1);
if (!user) {

View File

@@ -69,7 +69,7 @@ export const AuthSchema = z.object({
.refine((roles) => !roles.includes("Admin"), {
error: "Admin role cannot be included in sso-roles"
}),
"sso-users": z.array(z.string()).optional().default([]),
"sso-users": z.array(z.email()).optional().default([]),
"whitelist-users": z.array(z.email()).optional().default([]),
"auto-login-idp": z.int().positive().optional()
});
@@ -335,7 +335,7 @@ export const ClientResourceSchema = z
.refine((roles) => !roles.includes("Admin"), {
error: "Admin role cannot be included in roles"
}),
users: z.array(z.string()).optional().default([]),
users: z.array(z.email()).optional().default([]),
machines: z.array(z.string()).optional().default([])
})
.refine(

View File

@@ -5,13 +5,11 @@ import { SidebarNav } from "@app/components/SidebarNav";
import { OrgSelector } from "@app/components/OrgSelector";
import { cn } from "@app/lib/cn";
import { ListUserOrgsResponse } from "@server/routers/org";
import SupporterStatus from "@app/components/SupporterStatus";
import { Button } from "@app/components/ui/button";
import { ExternalLink, Menu, Server } from "lucide-react";
import { ArrowRight, Menu, Server } from "lucide-react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useUserContext } from "@app/hooks/useUserContext";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { useTranslations } from "next-intl";
import ProfileIcon from "@app/components/ProfileIcon";
import ThemeSwitcher from "@app/components/ThemeSwitcher";
@@ -44,7 +42,6 @@ export function LayoutMobileMenu({
const pathname = usePathname();
const isAdminPage = pathname?.startsWith("/admin");
const { user } = useUserContext();
const { env } = useEnvContext();
const t = useTranslations();
return (
@@ -83,7 +80,7 @@ export function LayoutMobileMenu({
<div className="px-3 pt-3">
{!isAdminPage &&
user.serverAdmin && (
<div className="py-2">
<div className="mb-1">
<Link
href="/admin"
className={cn(
@@ -98,11 +95,12 @@ export function LayoutMobileMenu({
<span className="flex-shrink-0 mr-2">
<Server className="h-4 w-4" />
</span>
<span>
<span className="flex-1">
{t(
"serverAdmin"
)}
</span>
<ArrowRight className="h-4 w-4 shrink-0 ml-auto opacity-70" />
</Link>
</div>
)}
@@ -115,22 +113,6 @@ export function LayoutMobileMenu({
</div>
<div className="sticky bottom-0 left-0 right-0 h-8 pointer-events-none bg-gradient-to-t from-card to-transparent" />
</div>
<div className="px-3 pt-3 pb-3 space-y-4 border-t shrink-0">
<SupporterStatus />
{env?.app?.version && (
<div className="text-xs text-muted-foreground text-center">
<Link
href={`https://github.com/fosrl/pangolin/releases/tag/${env.app.version}`}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-1"
>
v{env.app.version}
<ExternalLink size={12} />
</Link>
</div>
)}
</div>
</SheetContent>
</Sheet>
</div>

View File

@@ -146,6 +146,46 @@ export function LayoutSidebar({
/>
<div className="flex-1 overflow-y-auto relative">
<div className="px-2 pt-3">
{!isAdminPage && user.serverAdmin && (
<div
className={cn(
"shrink-0",
isSidebarCollapsed ? "mb-4" : "mb-1"
)}
>
<Link
href="/admin"
className={cn(
"flex items-center transition-colors text-muted-foreground hover:text-foreground text-sm w-full hover:bg-secondary/80 dark:hover:bg-secondary/50 rounded-md",
isSidebarCollapsed
? "px-2 py-2 justify-center"
: "px-3 py-1.5"
)}
title={
isSidebarCollapsed
? t("serverAdmin")
: undefined
}
>
<span
className={cn(
"shrink-0",
!isSidebarCollapsed && "mr-2"
)}
>
<Server className="h-4 w-4" />
</span>
{!isSidebarCollapsed && (
<>
<span className="flex-1">
{t("serverAdmin")}
</span>
<ArrowRight className="h-4 w-4 shrink-0 ml-auto opacity-70" />
</>
)}
</Link>
</div>
)}
<SidebarNav
sections={navItems}
isCollapsed={isSidebarCollapsed}
@@ -156,40 +196,6 @@ export function LayoutSidebar({
<div className="sticky bottom-0 left-0 right-0 h-8 pointer-events-none bg-gradient-to-t from-card to-transparent" />
</div>
{!isAdminPage && user.serverAdmin && (
<div className="shrink-0 px-2 pb-2">
<Link
href="/admin"
className={cn(
"flex items-center transition-colors text-muted-foreground hover:text-foreground text-sm w-full hover:bg-secondary/80 dark:hover:bg-secondary/50 rounded-md",
isSidebarCollapsed
? "px-2 py-2 justify-center"
: "px-3 py-1.5"
)}
title={
isSidebarCollapsed ? t("serverAdmin") : undefined
}
>
<span
className={cn(
"shrink-0",
!isSidebarCollapsed && "mr-2"
)}
>
<Server className="h-4 w-4" />
</span>
{!isSidebarCollapsed && (
<>
<span className="flex-1">
{t("serverAdmin")}
</span>
<ArrowRight className="h-4 w-4 shrink-0 ml-auto opacity-70" />
</>
)}
</Link>
</div>
)}
{isSidebarCollapsed && (
<div className="shrink-0 flex justify-center py-2">
<TooltipProvider>
@@ -218,7 +224,7 @@ export function LayoutSidebar({
<div className="w-full border-t border-border mb-3" />
<div className="p-4 pt-0 mt-0 flex flex-col shrink-0">
<div className="p-4 pt-1 flex flex-col shrink-0">
{canShowProductUpdates && (
<div className="mb-3 empty:mb-0">
<ProductUpdates isCollapsed={isSidebarCollapsed} />