Merge pull request #1988 from Fredkiss3/refactor/show-product-updates-conditionnally

refactor: Only show the product updates to an org admin or owner
This commit is contained in:
Milo Schwartz
2025-12-06 09:39:48 -08:00
committed by GitHub
4 changed files with 49 additions and 44 deletions

View File

@@ -1,8 +1,7 @@
import { db } from "@server/db";
import { clients, olms, newts, sites } from "@server/db";
import { eq } from "drizzle-orm";
import { sendToClient } from "#dynamic/routers/ws"; import { sendToClient } from "#dynamic/routers/ws";
import { db, olms } from "@server/db";
import logger from "@server/logger"; import logger from "@server/logger";
import { eq } from "drizzle-orm";
import { Alias } from "yaml"; import { Alias } from "yaml";
export async function addPeer( export async function addPeer(
@@ -102,7 +101,7 @@ export async function updatePeer(
.where(eq(olms.clientId, clientId)) .where(eq(olms.clientId, clientId))
.limit(1); .limit(1);
if (!olm) { if (!olm) {
return return;
} }
olmId = olm.olmId; olmId = olm.olmId;
} }
@@ -162,5 +161,7 @@ export async function initPeerAddHandshake(
logger.warn(`Error sending message:`, error); logger.warn(`Error sending message:`, error);
}); });
logger.info(`Initiated peer add handshake for site ${peer.siteId} to olm ${olmId}`); logger.info(
`Initiated peer add handshake for site ${peer.siteId} to olm ${olmId}`
);
} }

View File

@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db } from "@server/db"; import { db, roles } from "@server/db";
import { Org, orgs, userOrgs } from "@server/db"; import { Org, orgs, userOrgs } from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
@@ -40,7 +40,7 @@ const listOrgsSchema = z.object({
// responses: {} // responses: {}
// }); // });
type ResponseOrg = Org & { isOwner?: boolean }; type ResponseOrg = Org & { isOwner?: boolean; isAdmin?: boolean };
export type ListUserOrgsResponse = { export type ListUserOrgsResponse = {
orgs: ResponseOrg[]; orgs: ResponseOrg[];
@@ -112,6 +112,7 @@ export async function listUserOrgs(
userOrgs, userOrgs,
and(eq(userOrgs.orgId, orgs.orgId), eq(userOrgs.userId, userId)) and(eq(userOrgs.orgId, orgs.orgId), eq(userOrgs.userId, userId))
) )
.leftJoin(roles, eq(userOrgs.orgId, roles.orgId))
.limit(limit) .limit(limit)
.offset(offset); .offset(offset);
@@ -128,6 +129,9 @@ export async function listUserOrgs(
if (val.userOrgs && val.userOrgs.isOwner) { if (val.userOrgs && val.userOrgs.isOwner) {
res.isOwner = val.userOrgs.isOwner; res.isOwner = val.userOrgs.isOwner;
} }
if (val.roles && val.roles.isAdmin) {
res.isAdmin = val.roles.isAdmin;
}
return res; return res;
}); });

View File

@@ -1,24 +1,26 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { import {
clientSiteResources, clientSiteResources,
db, db,
newts, newts,
roles, roles,
roleSiteResources, roleSiteResources,
SiteResource,
siteResources,
sites,
userSiteResources userSiteResources
} from "@server/db"; } from "@server/db";
import { siteResources, sites, SiteResource } from "@server/db"; import { getUniqueSiteResourceName } from "@server/db/names";
import { getNextAvailableAliasAddress } from "@server/lib/ip";
import { rebuildClientAssociationsFromSiteResource } from "@server/lib/rebuildClientAssociations";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { eq, and, is } from "drizzle-orm";
import { fromError } from "zod-validation-error";
import logger from "@server/logger"; import logger from "@server/logger";
import { OpenAPITags, registry } from "@server/openApi"; import { OpenAPITags, registry } from "@server/openApi";
import { getUniqueSiteResourceName } from "@server/db/names"; import HttpCode from "@server/types/HttpCode";
import { rebuildClientAssociationsFromSiteResource } from "@server/lib/rebuildClientAssociations"; import { and, eq } from "drizzle-orm";
import { getNextAvailableAliasAddress } from "@server/lib/ip"; import { NextFunction, Request, Response } from "express";
import createHttpError from "http-errors";
import { z } from "zod";
import { fromError } from "zod-validation-error";
const createSiteResourceParamsSchema = z.strictObject({ const createSiteResourceParamsSchema = z.strictObject({
siteId: z.string().transform(Number).pipe(z.int().positive()), siteId: z.string().transform(Number).pipe(z.int().positive()),

View File

@@ -1,38 +1,30 @@
"use client"; "use client";
import React, { useEffect, useState } from "react";
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 {
ExternalLink,
Server,
BookOpenText,
Zap,
CreditCard,
FileText,
TicketCheck
} from "lucide-react";
import { FaDiscord, FaGithub } from "react-icons/fa";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useUserContext } from "@app/hooks/useUserContext";
import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { useTranslations } from "next-intl";
import type { SidebarNavSection } from "@app/app/navigation"; import type { SidebarNavSection } from "@app/app/navigation";
import { OrgSelector } from "@app/components/OrgSelector";
import { SidebarNav } from "@app/components/SidebarNav";
import SupporterStatus from "@app/components/SupporterStatus";
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,
TooltipProvider, TooltipProvider,
TooltipTrigger TooltipTrigger
} from "@app/components/ui/tooltip"; } from "@app/components/ui/tooltip";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext";
import { useUserContext } from "@app/hooks/useUserContext";
import { cn } from "@app/lib/cn";
import { build } from "@server/build"; import { build } from "@server/build";
import { ListUserOrgsResponse } from "@server/routers/org";
import { ExternalLink, Server } from "lucide-react";
import { useTranslations } from "next-intl";
import dynamic from "next/dynamic";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import { FaGithub } from "react-icons/fa";
import SidebarLicenseButton from "./SidebarLicenseButton"; import SidebarLicenseButton from "./SidebarLicenseButton";
import { SidebarSupportButton } from "./SidebarSupportButton"; import { SidebarSupportButton } from "./SidebarSupportButton";
import dynamic from "next/dynamic";
const ProductUpdates = dynamic(() => import("./ProductUpdates"), { const ProductUpdates = dynamic(() => import("./ProductUpdates"), {
ssr: false ssr: false
@@ -48,7 +40,7 @@ interface LayoutSidebarProps {
export function LayoutSidebar({ export function LayoutSidebar({
orgId, orgId,
orgs, orgs = [],
navItems, navItems,
defaultSidebarCollapsed, defaultSidebarCollapsed,
hasCookiePreference hasCookiePreference
@@ -105,6 +97,10 @@ export function LayoutSidebar({
} }
} }
const currentOrg = orgs.find((org) => org.orgId === orgId);
const canShowProductUpdates =
user.serverAdmin || Boolean(currentOrg?.isOwner || currentOrg?.isAdmin);
return ( return (
<div <div
className={cn( className={cn(
@@ -159,9 +155,11 @@ export function LayoutSidebar({
</div> </div>
<div className="p-4 flex flex-col shrink-0"> <div className="p-4 flex flex-col shrink-0">
<div className="mb-3"> {canShowProductUpdates && (
<ProductUpdates isCollapsed={isSidebarCollapsed} /> <div className="mb-3">
</div> <ProductUpdates isCollapsed={isSidebarCollapsed} />
</div>
)}
{build === "enterprise" && ( {build === "enterprise" && (
<div className="mb-3"> <div className="mb-3">