mirror of
https://github.com/fosrl/pangolin.git
synced 2026-05-22 00:35:22 +00:00
Merge branch 'dev' into alerting-rules
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db, orgs, userOrgs, users } from "@server/db";
|
import { db, orgs, userOrgs, users } from "@server/db";
|
||||||
import { eq, and, inArray } from "drizzle-orm";
|
import { eq, and, inArray, not } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
@@ -17,11 +17,10 @@ import { verifyTotpCode } from "@server/auth/totp";
|
|||||||
import { calculateUserClientsForOrgs } from "@server/lib/calculateUserClientsForOrgs";
|
import { calculateUserClientsForOrgs } from "@server/lib/calculateUserClientsForOrgs";
|
||||||
import { build } from "@server/build";
|
import { build } from "@server/build";
|
||||||
import { getOrgTierData } from "#dynamic/lib/billing";
|
import { getOrgTierData } from "#dynamic/lib/billing";
|
||||||
import {
|
import { deleteOrgById, sendTerminationMessages } from "@server/lib/deleteOrg";
|
||||||
deleteOrgById,
|
|
||||||
sendTerminationMessages
|
|
||||||
} from "@server/lib/deleteOrg";
|
|
||||||
import { UserType } from "@server/types/UserTypes";
|
import { UserType } from "@server/types/UserTypes";
|
||||||
|
import { usageService } from "@server/lib/billing/usageService";
|
||||||
|
import { FeatureId } from "@server/lib/billing";
|
||||||
|
|
||||||
const deleteMyAccountBody = z.strictObject({
|
const deleteMyAccountBody = z.strictObject({
|
||||||
password: z.string().optional(),
|
password: z.string().optional(),
|
||||||
@@ -98,9 +97,9 @@ export async function deleteMyAccount(
|
|||||||
and(eq(userOrgs.userId, userId), eq(userOrgs.isOwner, true))
|
and(eq(userOrgs.userId, userId), eq(userOrgs.isOwner, true))
|
||||||
);
|
);
|
||||||
|
|
||||||
const orgIds = ownedOrgsRows.map((r) => r.orgId);
|
const ownedOrgIds = ownedOrgsRows.map((r) => r.orgId);
|
||||||
|
|
||||||
if (build === "saas" && orgIds.length > 0) {
|
if (build === "saas" && ownedOrgIds.length > 0) {
|
||||||
const primaryOrgId = ownedOrgsRows.find(
|
const primaryOrgId = ownedOrgsRows.find(
|
||||||
(r) => r.isBillingOrg && r.isOwner
|
(r) => r.isBillingOrg && r.isOwner
|
||||||
)?.orgId;
|
)?.orgId;
|
||||||
@@ -119,14 +118,14 @@ export async function deleteMyAccount(
|
|||||||
|
|
||||||
if (!password) {
|
if (!password) {
|
||||||
const orgsWithNames =
|
const orgsWithNames =
|
||||||
orgIds.length > 0
|
ownedOrgIds.length > 0
|
||||||
? await db
|
? await db
|
||||||
.select({
|
.select({
|
||||||
orgId: orgs.orgId,
|
orgId: orgs.orgId,
|
||||||
name: orgs.name
|
name: orgs.name
|
||||||
})
|
})
|
||||||
.from(orgs)
|
.from(orgs)
|
||||||
.where(inArray(orgs.orgId, orgIds))
|
.where(inArray(orgs.orgId, ownedOrgIds))
|
||||||
: [];
|
: [];
|
||||||
return response<DeleteMyAccountPreviewResponse>(res, {
|
return response<DeleteMyAccountPreviewResponse>(res, {
|
||||||
data: {
|
data: {
|
||||||
@@ -206,9 +205,23 @@ export async function deleteMyAccount(
|
|||||||
olmsToTerminate: allOlmsToTerminate
|
olmsToTerminate: allOlmsToTerminate
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const otherOrgsTheUserWasIn = await db
|
||||||
|
.select()
|
||||||
|
.from(userOrgs)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(userOrgs.userId, userId),
|
||||||
|
not(inArray(userOrgs.orgId, ownedOrgIds))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
await db.transaction(async (trx) => {
|
await db.transaction(async (trx) => {
|
||||||
await trx.delete(users).where(eq(users.userId, userId));
|
await trx.delete(users).where(eq(users.userId, userId));
|
||||||
await calculateUserClientsForOrgs(userId, trx);
|
await calculateUserClientsForOrgs(userId, trx);
|
||||||
|
// loop through the other orgs and decrement the count
|
||||||
|
for (const userOrg of otherOrgsTheUserWasIn) {
|
||||||
|
await usageService.add(userOrg.orgId, FeatureId.USERS, -1, trx);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -233,10 +246,7 @@ export async function deleteMyAccount(
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
|
||||||
HttpCode.INTERNAL_SERVER_ERROR,
|
|
||||||
"An error occurred"
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,8 @@ import dynamic from "next/dynamic";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import { useEffect, useState } from "react";
|
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 { is } from "drizzle-orm";
|
|
||||||
|
|
||||||
const ProductUpdates = dynamic(() => import("./ProductUpdates"), {
|
const ProductUpdates = dynamic(() => import("./ProductUpdates"), {
|
||||||
ssr: false
|
ssr: false
|
||||||
@@ -291,7 +289,6 @@ export function LayoutSidebar({
|
|||||||
: build === "enterprise"
|
: build === "enterprise"
|
||||||
? t("enterpriseEdition")
|
? t("enterpriseEdition")
|
||||||
: "Pangolin Cloud"}
|
: "Pangolin Cloud"}
|
||||||
<FaGithub size={12} />
|
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{build === "enterprise" &&
|
{build === "enterprise" &&
|
||||||
|
|||||||
@@ -103,16 +103,15 @@ export default function ProductUpdates({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
|
{filteredUpdates.length > 1 && (
|
||||||
<small
|
<small
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-xs text-muted-foreground flex items-center gap-1 mt-2 empty:mt-0",
|
"text-xs text-muted-foreground flex items-center gap-1 mt-2",
|
||||||
showMoreUpdatesText
|
showMoreUpdatesText
|
||||||
? "animate-in fade-in duration-300"
|
? "animate-in fade-in duration-300"
|
||||||
: "opacity-0"
|
: "opacity-0"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{filteredUpdates.length > 1 && (
|
|
||||||
<>
|
|
||||||
<BellIcon className="flex-none size-3" />
|
<BellIcon className="flex-none size-3" />
|
||||||
<span>
|
<span>
|
||||||
{showNewVersionPopup
|
{showNewVersionPopup
|
||||||
@@ -123,9 +122,8 @@ export default function ProductUpdates({
|
|||||||
noOfUpdates: filteredUpdates.length
|
noOfUpdates: filteredUpdates.length
|
||||||
})}
|
})}
|
||||||
</span>
|
</span>
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</small>
|
</small>
|
||||||
|
)}
|
||||||
<ProductUpdatesListPopup
|
<ProductUpdatesListPopup
|
||||||
updates={filteredUpdates}
|
updates={filteredUpdates}
|
||||||
show={filteredUpdates.length > 0}
|
show={filteredUpdates.length > 0}
|
||||||
@@ -378,7 +376,7 @@ function NewVersionAvailable({
|
|||||||
<span>
|
<span>
|
||||||
{t("pangolinUpdateAvailableReleaseNotes")}
|
{t("pangolinUpdateAvailableReleaseNotes")}
|
||||||
</span>
|
</span>
|
||||||
<ArrowRight className="flex-none size-3 transition-transform duration-300 ease-in-out group-hover:translate-x-1" />
|
<ArrowRight className="flex-none size-3" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user