mirror of
https://github.com/fosrl/pangolin.git
synced 2026-10-21 12:09:04 +02:00
Make rekey not private and in integration api
This commit is contained in:
@@ -1504,6 +1504,7 @@
|
||||
"actionDeleteSite": "Delete Site",
|
||||
"actionGetSite": "Get Site",
|
||||
"actionListSites": "List Sites",
|
||||
"actionReGenerateSecret": "Regenerate Credentials",
|
||||
"actionCreateAiProvider": "Create AI Provider",
|
||||
"actionDeleteAiProvider": "Delete AI Provider",
|
||||
"actionGetAiProvider": "Get AI Provider",
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
/**
|
||||
* Builds the timestamp segment of a database backup file name.
|
||||
*
|
||||
* `Date#getMonth` is zero-indexed, so building this inline produced names like
|
||||
* `db_2026-8-12_...` for a backup taken on 12 September 2026. Every field is
|
||||
* also zero-padded, which keeps the names unambiguous and makes them sort
|
||||
* lexicographically in the order they were taken.
|
||||
*
|
||||
* @param date The moment the backup is being taken. Defaults to now.
|
||||
* @returns A timestamp of the form `YYYY-MM-DD_HH-MM-SS`.
|
||||
*/
|
||||
export function formatBackupTimestamp(date: Date = new Date()): string {
|
||||
const pad = (value: number): string => String(value).padStart(2, "0");
|
||||
|
||||
|
||||
+2
-1
@@ -34,7 +34,8 @@ export enum OpenAPITags {
|
||||
AiBudget = "AI Budget",
|
||||
VirtualApiKey = "Virtual API Key",
|
||||
Redirect = "Redirect",
|
||||
Label = "Label"
|
||||
Label = "Label",
|
||||
RemoteExitNode = "Remote Exit Node"
|
||||
}
|
||||
|
||||
// Order here controls the order tags are displayed in Swagger UI
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
exportAiSessionLogs
|
||||
} from "@server/routers/auditLogs";
|
||||
import * as misc from "#private/routers/misc";
|
||||
import * as reKey from "#private/routers/re-key";
|
||||
import * as approval from "#private/routers/approvals";
|
||||
import * as user from "#private/routers/user";
|
||||
import * as client from "#private/routers/client";
|
||||
@@ -617,36 +616,6 @@ authenticated.get(
|
||||
exportAiSessionLogs
|
||||
);
|
||||
|
||||
authenticated.post(
|
||||
"/re-key/:clientId/regenerate-client-secret",
|
||||
verifyClientAccess, // this is first to set the org id
|
||||
verifyValidLicense,
|
||||
verifyValidSubscription(tierMatrix.rotateCredentials),
|
||||
verifyLimits,
|
||||
verifyUserHasAction(ActionsEnum.reGenerateSecret),
|
||||
reKey.reGenerateClientSecret
|
||||
);
|
||||
|
||||
authenticated.post(
|
||||
"/re-key/:siteId/regenerate-site-secret",
|
||||
verifySiteAccess, // this is first to set the org id
|
||||
verifyValidLicense,
|
||||
verifyValidSubscription(tierMatrix.rotateCredentials),
|
||||
verifyLimits,
|
||||
verifyUserHasAction(ActionsEnum.reGenerateSecret),
|
||||
reKey.reGenerateSiteSecret
|
||||
);
|
||||
|
||||
authenticated.put(
|
||||
"/re-key/:orgId/regenerate-remote-exit-node-secret",
|
||||
verifyValidLicense,
|
||||
verifyValidSubscription(tierMatrix.rotateCredentials),
|
||||
verifyOrgAccess,
|
||||
verifyLimits,
|
||||
verifyUserHasAction(ActionsEnum.reGenerateSecret),
|
||||
reKey.reGenerateExitNodeSecret
|
||||
);
|
||||
|
||||
authenticated.post(
|
||||
"/user/:userId/add-role/:roleId",
|
||||
verifyRoleAccess,
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* This file is part of a proprietary work.
|
||||
*
|
||||
* Copyright (c) 2025-2026 Fossorial, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is licensed under the Fossorial Commercial License.
|
||||
* You may not use this file except in compliance with the License.
|
||||
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
*
|
||||
* This file is not licensed under the AGPLv3.
|
||||
*/
|
||||
|
||||
export * from "./reGenerateClientSecret";
|
||||
export * from "./reGenerateSiteSecret";
|
||||
export * from "./reGenerateExitNodeSecret";
|
||||
@@ -1,3 +1,16 @@
|
||||
/*
|
||||
* This file is part of a proprietary work.
|
||||
*
|
||||
* Copyright (c) 2025-2026 Fossorial, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is licensed under the Fossorial Commercial License.
|
||||
* You may not use this file except in compliance with the License.
|
||||
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
*
|
||||
* This file is not licensed under the AGPLv3.
|
||||
*/
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db, resourceMtlsCertificates, resources } from "@server/db";
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/*
|
||||
* This file is part of a proprietary work.
|
||||
*
|
||||
* Copyright (c) 2025-2026 Fossorial, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is licensed under the Fossorial Commercial License.
|
||||
* You may not use this file except in compliance with the License.
|
||||
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
*
|
||||
* This file is not licensed under the AGPLv3.
|
||||
*/
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db, resourceMtlsCertificates, resources } from "@server/db";
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/*
|
||||
* This file is part of a proprietary work.
|
||||
*
|
||||
* Copyright (c) 2025-2026 Fossorial, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is licensed under the Fossorial Commercial License.
|
||||
* You may not use this file except in compliance with the License.
|
||||
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
*
|
||||
* This file is not licensed under the AGPLv3.
|
||||
*/
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db, resourceMtlsCertificates, resources } from "@server/db";
|
||||
|
||||
@@ -22,6 +22,7 @@ import * as newt from "./newt";
|
||||
import * as olm from "./olm";
|
||||
import * as ssh from "./ssh";
|
||||
import * as serverInfo from "./serverInfo";
|
||||
import * as reKey from "./re-key";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
import {
|
||||
verifyAccessTokenAccess,
|
||||
@@ -1972,6 +1973,30 @@ authenticated.post(
|
||||
certificates.restartCertificate
|
||||
);
|
||||
|
||||
authenticated.post(
|
||||
"/re-key/:clientId/regenerate-client-secret",
|
||||
verifyClientAccess, // this is first to set the org id
|
||||
verifyLimits,
|
||||
verifyUserHasAction(ActionsEnum.reGenerateSecret),
|
||||
reKey.reGenerateClientSecret
|
||||
);
|
||||
|
||||
authenticated.post(
|
||||
"/re-key/:siteId/regenerate-site-secret",
|
||||
verifySiteAccess, // this is first to set the org id
|
||||
verifyLimits,
|
||||
verifyUserHasAction(ActionsEnum.reGenerateSecret),
|
||||
reKey.reGenerateSiteSecret
|
||||
);
|
||||
|
||||
authenticated.put(
|
||||
"/re-key/:orgId/regenerate-remote-exit-node-secret",
|
||||
verifyOrgAccess,
|
||||
verifyLimits,
|
||||
verifyUserHasAction(ActionsEnum.reGenerateSecret),
|
||||
reKey.reGenerateExitNodeSecret
|
||||
);
|
||||
|
||||
// Auth routes
|
||||
export const authRouter = Router();
|
||||
unauthenticated.use("/auth", authRouter);
|
||||
|
||||
@@ -18,6 +18,7 @@ import * as aiBudget from "./aiBudget";
|
||||
import * as virtualApiKey from "./virtualApiKey";
|
||||
import * as redirect from "./redirect";
|
||||
import * as labels from "./labels";
|
||||
import * as reKey from "./re-key";
|
||||
import {
|
||||
verifyApiKey,
|
||||
verifyApiKeyOrgAccess,
|
||||
@@ -149,6 +150,24 @@ authenticated.post(
|
||||
site.updateSite
|
||||
);
|
||||
|
||||
authenticated.post(
|
||||
"/re-key/:siteId/regenerate-site-secret",
|
||||
verifyApiKeySiteAccess,
|
||||
verifyLimits,
|
||||
verifyApiKeyHasAction(ActionsEnum.reGenerateSecret),
|
||||
logActionAudit(ActionsEnum.reGenerateSecret),
|
||||
reKey.reGenerateSiteSecret
|
||||
);
|
||||
|
||||
authenticated.put(
|
||||
"/re-key/:orgId/regenerate-remote-exit-node-secret",
|
||||
verifyApiKeyOrgAccess,
|
||||
verifyLimits,
|
||||
verifyApiKeyHasAction(ActionsEnum.reGenerateSecret),
|
||||
logActionAudit(ActionsEnum.reGenerateSecret),
|
||||
reKey.reGenerateExitNodeSecret
|
||||
);
|
||||
|
||||
authenticated.post(
|
||||
"/org/:orgId/reset-bandwidth",
|
||||
verifyApiKeyOrgAccess,
|
||||
@@ -1497,6 +1516,15 @@ authenticated.post(
|
||||
client.updateClient
|
||||
);
|
||||
|
||||
authenticated.post(
|
||||
"/re-key/:clientId/regenerate-client-secret",
|
||||
verifyApiKeyClientAccess,
|
||||
verifyLimits,
|
||||
verifyApiKeyHasAction(ActionsEnum.reGenerateSecret),
|
||||
logActionAudit(ActionsEnum.reGenerateSecret),
|
||||
reKey.reGenerateClientSecret
|
||||
);
|
||||
|
||||
authenticated.put(
|
||||
"/org/:orgId/blueprint",
|
||||
verifyApiKeyOrgAccess,
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./reGenerateClientSecret";
|
||||
export * from "./reGenerateSiteSecret";
|
||||
export * from "./reGenerateExitNodeSecret";
|
||||
+35
-14
@@ -1,16 +1,3 @@
|
||||
/*
|
||||
* This file is part of a proprietary work.
|
||||
*
|
||||
* Copyright (c) 2025-2026 Fossorial, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is licensed under the Fossorial Commercial License.
|
||||
* You may not use this file except in compliance with the License.
|
||||
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
*
|
||||
* This file is not licensed under the AGPLv3.
|
||||
*/
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db, Olm, olms } from "@server/db";
|
||||
@@ -23,7 +10,7 @@ import { eq, and } from "drizzle-orm";
|
||||
import { fromError } from "zod-validation-error";
|
||||
import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { hashPassword } from "@server/auth/password";
|
||||
import { disconnectClient, sendToClient } from "#private/routers/ws";
|
||||
import { disconnectClient, sendToClient } from "#dynamic/routers/ws";
|
||||
import { OlmErrorCodes, sendOlmError } from "@server/routers/olm/error";
|
||||
import { sendTerminateClient } from "@server/routers/client/terminate";
|
||||
|
||||
@@ -39,6 +26,40 @@ const reGenerateSecretBodySchema = z.strictObject({
|
||||
|
||||
export type ReGenerateSecretBody = z.infer<typeof reGenerateSecretBodySchema>;
|
||||
|
||||
registry.registerPath({
|
||||
method: "post",
|
||||
path: "/re-key/{clientId}/regenerate-client-secret",
|
||||
description:
|
||||
"Regenerate the secret for a machine client's olm. Optionally disconnects the olm so it must reconnect with the new secret.",
|
||||
tags: [OpenAPITags.Client],
|
||||
request: {
|
||||
params: reGenerateSecretParamsSchema,
|
||||
body: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: reGenerateSecretBodySchema
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: "Successful response",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
data: z.object({ olmId: z.string() }),
|
||||
success: z.boolean(),
|
||||
error: z.boolean(),
|
||||
message: z.string(),
|
||||
status: z.number()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export async function reGenerateClientSecret(
|
||||
req: Request,
|
||||
res: Response,
|
||||
+50
-16
@@ -1,16 +1,3 @@
|
||||
/*
|
||||
* This file is part of a proprietary work.
|
||||
*
|
||||
* Copyright (c) 2025-2026 Fossorial, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is licensed under the Fossorial Commercial License.
|
||||
* You may not use this file except in compliance with the License.
|
||||
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
*
|
||||
* This file is not licensed under the AGPLv3.
|
||||
*/
|
||||
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import {
|
||||
db,
|
||||
@@ -30,7 +17,7 @@ import { hashPassword } from "@server/auth/password";
|
||||
import logger from "@server/logger";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { disconnectClient, sendToClient } from "#private/routers/ws";
|
||||
import { disconnectClient, sendToClient } from "#dynamic/routers/ws";
|
||||
|
||||
export const paramsSchema = z.object({
|
||||
orgId: z.string()
|
||||
@@ -42,6 +29,40 @@ const bodySchema = z.strictObject({
|
||||
disconnect: z.boolean().optional().default(true)
|
||||
});
|
||||
|
||||
registry.registerPath({
|
||||
method: "put",
|
||||
path: "/re-key/{orgId}/regenerate-remote-exit-node-secret",
|
||||
description:
|
||||
"Regenerate the secret for a remote exit node. Optionally disconnects the node so it must reconnect with the new secret.",
|
||||
tags: [OpenAPITags.RemoteExitNode],
|
||||
request: {
|
||||
params: paramsSchema,
|
||||
body: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: bodySchema
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: "Successful response",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
data: z.null(),
|
||||
success: z.boolean(),
|
||||
error: z.boolean(),
|
||||
message: z.string(),
|
||||
status: z.number()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export async function reGenerateExitNodeSecret(
|
||||
req: Request,
|
||||
res: Response,
|
||||
@@ -68,12 +89,25 @@ export async function reGenerateExitNodeSecret(
|
||||
);
|
||||
}
|
||||
|
||||
const { orgId } = parsedParams.data;
|
||||
const { remoteExitNodeId, secret, disconnect } = parsedBody.data;
|
||||
|
||||
// Only allow re-keying a node that belongs to this org (a node can be
|
||||
// shared across several orgs; any of them may re-key it)
|
||||
const [existingRemoteExitNode] = await db
|
||||
.select()
|
||||
.select({ remoteExitNodeId: remoteExitNodes.remoteExitNodeId })
|
||||
.from(remoteExitNodes)
|
||||
.where(eq(remoteExitNodes.remoteExitNodeId, remoteExitNodeId));
|
||||
.innerJoin(
|
||||
exitNodeOrgs,
|
||||
eq(exitNodeOrgs.exitNodeId, remoteExitNodes.exitNodeId)
|
||||
)
|
||||
.where(
|
||||
and(
|
||||
eq(remoteExitNodes.remoteExitNodeId, remoteExitNodeId),
|
||||
eq(exitNodeOrgs.orgId, orgId)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (!existingRemoteExitNode) {
|
||||
return next(
|
||||
+35
-14
@@ -1,16 +1,3 @@
|
||||
/*
|
||||
* This file is part of a proprietary work.
|
||||
*
|
||||
* Copyright (c) 2025-2026 Fossorial, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is licensed under the Fossorial Commercial License.
|
||||
* You may not use this file except in compliance with the License.
|
||||
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
*
|
||||
* This file is not licensed under the AGPLv3.
|
||||
*/
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db, Newt, newts, sites } from "@server/db";
|
||||
@@ -24,7 +11,7 @@ import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { hashPassword } from "@server/auth/password";
|
||||
import { addPeer, deletePeer } from "@server/routers/gerbil/peers";
|
||||
import { getAllowedIps } from "@server/routers/target/helpers";
|
||||
import { disconnectClient, sendToClient } from "#private/routers/ws";
|
||||
import { disconnectClient, sendToClient } from "#dynamic/routers/ws";
|
||||
|
||||
const updateSiteParamsSchema = z.strictObject({
|
||||
siteId: z.coerce.number().int().positive()
|
||||
@@ -37,6 +24,40 @@ const updateSiteBodySchema = z.strictObject({
|
||||
disconnect: z.boolean().optional().default(true)
|
||||
});
|
||||
|
||||
registry.registerPath({
|
||||
method: "post",
|
||||
path: "/re-key/{siteId}/regenerate-site-secret",
|
||||
description:
|
||||
"Regenerate the credentials for a site. For newt sites a new secret is required; for wireguard sites a new public key is required.",
|
||||
tags: [OpenAPITags.Site],
|
||||
request: {
|
||||
params: updateSiteParamsSchema,
|
||||
body: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: updateSiteBodySchema
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: "Successful response",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
data: z.object({ newtId: z.string().optional() }),
|
||||
success: z.boolean(),
|
||||
error: z.boolean(),
|
||||
message: z.string(),
|
||||
status: z.number()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export async function reGenerateSiteSecret(
|
||||
req: Request,
|
||||
res: Response,
|
||||
-15
@@ -32,9 +32,6 @@ import {
|
||||
import CopyToClipboard from "@app/components/CopyToClipboard";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
|
||||
import { InfoIcon } from "lucide-react";
|
||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||
|
||||
export default function CredentialsPage() {
|
||||
const { env } = useEnvContext();
|
||||
@@ -44,8 +41,6 @@ export default function CredentialsPage() {
|
||||
const t = useTranslations();
|
||||
const { remoteExitNode } = useRemoteExitNodeContext();
|
||||
|
||||
const { isPaidUser } = usePaidStatus();
|
||||
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [credentials, setCredentials] =
|
||||
useState<PickRemoteExitNodeDefaultsResponse | null>(null);
|
||||
@@ -129,10 +124,6 @@ export default function CredentialsPage() {
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix.rotateCredentials}
|
||||
/>
|
||||
|
||||
<InfoSections cols={3}>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
@@ -196,9 +187,6 @@ export default function CredentialsPage() {
|
||||
setShouldDisconnect(false);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={
|
||||
!isPaidUser(tierMatrix.rotateCredentials)
|
||||
}
|
||||
>
|
||||
{t("regenerateCredentialsButton")}
|
||||
</Button>
|
||||
@@ -207,9 +195,6 @@ export default function CredentialsPage() {
|
||||
setShouldDisconnect(true);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={
|
||||
!isPaidUser(tierMatrix.rotateCredentials)
|
||||
}
|
||||
>
|
||||
{t("remoteExitNodeRegenerateAndDisconnect")}
|
||||
</Button>
|
||||
|
||||
@@ -28,10 +28,7 @@ import {
|
||||
import CopyToClipboard from "@app/components/CopyToClipboard";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
|
||||
import { InfoIcon } from "lucide-react";
|
||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||
import { OlmInstallCommands } from "@app/components/olm-install-commands";
|
||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||
|
||||
export default function CredentialsPage() {
|
||||
const { env } = useEnvContext();
|
||||
@@ -53,8 +50,6 @@ export default function CredentialsPage() {
|
||||
const [showCredentialsAlert, setShowCredentialsAlert] = useState(false);
|
||||
const [shouldDisconnect, setShouldDisconnect] = useState(true);
|
||||
|
||||
const { isPaidUser } = usePaidStatus();
|
||||
|
||||
const handleConfirmRegenerate = async () => {
|
||||
try {
|
||||
const res = await api.get(`/org/${orgId}/pick-client-defaults`);
|
||||
@@ -119,10 +114,6 @@ export default function CredentialsPage() {
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix.rotateCredentials}
|
||||
/>
|
||||
|
||||
<InfoSections cols={3}>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
@@ -182,9 +173,6 @@ export default function CredentialsPage() {
|
||||
setShouldDisconnect(false);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={
|
||||
!isPaidUser(tierMatrix.rotateCredentials)
|
||||
}
|
||||
>
|
||||
{t("regenerateCredentialsButton")}
|
||||
</Button>
|
||||
@@ -193,9 +181,6 @@ export default function CredentialsPage() {
|
||||
setShouldDisconnect(true);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={
|
||||
!isPaidUser(tierMatrix.rotateCredentials)
|
||||
}
|
||||
>
|
||||
{t("clientRegenerateAndDisconnect")}
|
||||
</Button>
|
||||
|
||||
@@ -35,10 +35,7 @@ import {
|
||||
generateObfuscatedWireGuardConfig
|
||||
} from "@app/lib/wireguard";
|
||||
import { QRCodeCanvas } from "qrcode.react";
|
||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||
import { NewtSiteInstallCommands } from "@app/components/newt-install-commands";
|
||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { productUpdatesQueries } from "@app/lib/queries";
|
||||
@@ -67,8 +64,6 @@ export default function CredentialsPage() {
|
||||
const [loadingDefaults, setLoadingDefaults] = useState(false);
|
||||
const [shouldDisconnect, setShouldDisconnect] = useState(true);
|
||||
|
||||
const { isPaidUser } = usePaidStatus();
|
||||
|
||||
const { data: latestVersions } = useQuery(
|
||||
productUpdatesQueries.latestVersion(true)
|
||||
);
|
||||
@@ -206,10 +201,6 @@ export default function CredentialsPage() {
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix.rotateCredentials}
|
||||
/>
|
||||
|
||||
<SettingsSectionBody>
|
||||
<InfoSections cols={3}>
|
||||
<InfoSection>
|
||||
@@ -280,11 +271,6 @@ export default function CredentialsPage() {
|
||||
setShouldDisconnect(false);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={
|
||||
!isPaidUser(
|
||||
tierMatrix.rotateCredentials
|
||||
)
|
||||
}
|
||||
>
|
||||
{t("regenerateCredentialsButton")}
|
||||
</Button>
|
||||
@@ -293,11 +279,6 @@ export default function CredentialsPage() {
|
||||
setShouldDisconnect(true);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={
|
||||
!isPaidUser(
|
||||
tierMatrix.rotateCredentials
|
||||
)
|
||||
}
|
||||
>
|
||||
{t("siteRegenerateAndDisconnect")}
|
||||
</Button>
|
||||
@@ -324,10 +305,6 @@ export default function CredentialsPage() {
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix.rotateCredentials}
|
||||
/>
|
||||
|
||||
<SettingsSectionBody>
|
||||
{!loadingDefaults && (
|
||||
<>
|
||||
@@ -393,14 +370,7 @@ export default function CredentialsPage() {
|
||||
</SettingsSectionBody>
|
||||
{!env.flags.disableEnterpriseFeatures && (
|
||||
<SettingsSectionFooter>
|
||||
<Button
|
||||
onClick={() => setModalOpen(true)}
|
||||
disabled={
|
||||
!isPaidUser(
|
||||
tierMatrix.rotateCredentials
|
||||
)
|
||||
}
|
||||
>
|
||||
<Button onClick={() => setModalOpen(true)}>
|
||||
{t("siteRegenerateAndDisconnect")}
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
|
||||
@@ -36,7 +36,8 @@ function getActionsCategories(root: boolean) {
|
||||
[t("createOrgUser")]: "createOrgUser",
|
||||
[t("actionApplyBlueprint")]: "applyBlueprint",
|
||||
[t("actionListBlueprints")]: "listBlueprints",
|
||||
[t("actionGetBlueprint")]: "getBlueprint"
|
||||
[t("actionGetBlueprint")]: "getBlueprint",
|
||||
[t("actionReGenerateSecret")]: "reGenerateSecret"
|
||||
},
|
||||
|
||||
Domain: {
|
||||
|
||||
Reference in New Issue
Block a user