Merge branch 'main' into feature/mongodb-secret-rotation

This commit is contained in:
Victor Santos
2025-12-03 17:23:06 -03:00
877 changed files with 20380 additions and 10351 deletions
+7 -7
View File
@@ -8,15 +8,15 @@
http-equiv="Content-Security-Policy"
content="
default-src 'self';
connect-src 'self' https://*.posthog.com http://127.0.0.1:* https://cdn.jsdelivr.net/npm/@lottiefiles/[email protected]/dist/dotlottie-player.wasm;
script-src 'self' https://*.posthog.com https://js.stripe.com https://api.stripe.com https://widget.intercom.io https://js.intercomcdn.com https://hcaptcha.com https://*.hcaptcha.com 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net/npm/@lottiefiles/[email protected]/dist/dotlottie-player.wasm;
style-src 'self' 'unsafe-inline' https://hcaptcha.com https://*.hcaptcha.com;
connect-src 'self' https://d1zwf0dwl0k2ky.cloudfront.net https://*.posthog.com http://127.0.0.1:* https://cdn.jsdelivr.net/npm/@lottiefiles/[email protected]/dist/dotlottie-player.wasm;
script-src 'self' https://d1zwf0dwl0k2ky.cloudfront.net https://*.posthog.com https://js.stripe.com https://api.stripe.com https://widget.intercom.io https://js.intercomcdn.com https://hcaptcha.com https://*.hcaptcha.com 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net/npm/@lottiefiles/[email protected]/dist/dotlottie-player.wasm;
style-src 'self' https://d1zwf0dwl0k2ky.cloudfront.net 'unsafe-inline' https://hcaptcha.com https://*.hcaptcha.com;
child-src https://api.stripe.com;
frame-src https://js.stripe.com/ https://api.stripe.com https://www.youtube.com/ https://hcaptcha.com https://*.hcaptcha.com;
connect-src 'self' wss://nexus-websocket-a.intercom.io https://api-iam.intercom.io https://api.heroku.com/ https://id.heroku.com/oauth/authorize https://id.heroku.com/oauth/token https://checkout.stripe.com https://app.posthog.com https://api.stripe.com https://api.pwnedpasswords.com http://127.0.0.1:* https://hcaptcha.com https://*.hcaptcha.com;
img-src 'self' https://static.intercomassets.com https://js.intercomcdn.com https://downloads.intercomcdn.com https://*.stripe.com https://i.ytimg.com/ data:;
media-src https://js.intercomcdn.com;
font-src 'self' https://fonts.intercomcdn.com/ https://fonts.gstatic.com;
connect-src 'self' https://d1zwf0dwl0k2ky.cloudfront.net wss://nexus-websocket-a.intercom.io https://api-iam.intercom.io https://api.heroku.com/ https://id.heroku.com/oauth/authorize https://id.heroku.com/oauth/token https://checkout.stripe.com https://app.posthog.com https://api.stripe.com https://api.pwnedpasswords.com http://127.0.0.1:* https://hcaptcha.com https://*.hcaptcha.com;
img-src 'self' https://d1zwf0dwl0k2ky.cloudfront.net https://static.intercomassets.com https://js.intercomcdn.com https://downloads.intercomcdn.com https://*.stripe.com https://i.ytimg.com/ data:;
media-src https://d1zwf0dwl0k2ky.cloudfront.net https://js.intercomcdn.com;
font-src 'self' https://d1zwf0dwl0k2ky.cloudfront.net https://fonts.intercomcdn.com/ https://fonts.gstatic.com;
"
/>
<title>Infisical</title>
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 120 60" width="120" height="60" style="enable-background:new 0 0 120 60;" xml:space="preserve">
<style type="text/css">
.st0{fill:#808285;}
.st1{fill:url(#SVGID_1_);}
.st2{fill:#005B99;}
.st3{enable-background:new ;}
.st4{fill:#77787B;}
</style>
<g>
<path class="st0" d="M34.1,42.6h9.4v-3.2C41.5,41.2,38.2,42.3,34.1,42.6L34.1,42.6z"/>
<path class="st0" d="M17.3,40.1v2.5h11.9c-2.4-0.2-5-0.6-7.6-1.2C20.1,41,18.7,40.6,17.3,40.1L17.3,40.1z"/>
<radialGradient id="SVGID_1_" cx="-183.5882" cy="811.1324" r="47.498" gradientTransform="matrix(1 0 0 1 241.0864 -776.3726)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#0095DA"/>
<stop offset="0.21" style="stop-color:#0095DA"/>
<stop offset="0.33" style="stop-color:#00ACE4"/>
<stop offset="0.9045" style="stop-color:#005093"/>
<stop offset="0.9335" style="stop-color:#005396"/>
<stop offset="0.954" style="stop-color:#005C9F"/>
<stop offset="0.9718" style="stop-color:#006BAE"/>
<stop offset="0.988" style="stop-color:#0080C4"/>
<stop offset="1" style="stop-color:#0095DA"/>
</radialGradient>
<path class="st1" d="M43.5,18.5H29.2C23,17.1,15.4,17.1,10,18.2c2.4-0.3,5.1-0.3,7.8,0.1c0.7,0.1,1.3,0.2,2,0.3l0,0
c2.9,0.5,5.7,1.2,8.1,2.2l0,0c0.3,0.1,0.5,0.2,0.8,0.3h0c0.1,0,0.2,0.1,0.3,0.1h0c0.1,0,0.2,0.1,0.3,0.1h0c0.1,0,0.2,0.1,0.2,0.1
l0,0c0.1,0,0.1,0.1,0.2,0.1l0,0c0.1,0,0.1,0.1,0.2,0.1l0,0c0.1,0,0.2,0.1,0.2,0.1l0,0l0,0h0c0.1,0,0.1,0.1,0.2,0.1l0,0
c0.1,0,0.1,0.1,0.2,0.1l0,0c0.1,0,0.1,0.1,0.2,0.1l0,0c0.1,0,0.1,0.1,0.2,0.1l0,0c0.1,0,0.1,0.1,0.2,0.1l0,0c0.1,0,0.1,0.1,0.2,0.1
l0.1,0c0.1,0,0.1,0.1,0.2,0.1l0.1,0c0.1,0,0.1,0.1,0.2,0.1l0.1,0c0.1,0,0.1,0.1,0.2,0.1l0.1,0c0.1,0,0.1,0.1,0.1,0.1l0.1,0.1
c0.1,0,0.1,0.1,0.1,0.1l0.1,0.1c0.1,0,0.1,0.1,0.1,0.1l0.1,0.1c0,0,0.1,0.1,0.1,0.1c3.2,2.2,5.1,4.9,5.1,7.6c0,3.1-2.6,5.7-6.8,7.2
c2.9-1.5,4.6-3.6,4.6-6.1c0-3.1-2.7-6.3-7-8.7c-0.1,0-0.1-0.1-0.2-0.1c-0.1,0-0.1-0.1-0.2-0.1c-0.1,0-0.1-0.1-0.2-0.1
c-0.1,0-0.1-0.1-0.2-0.1c-0.1,0-0.1-0.1-0.2-0.1c-0.1,0-0.1-0.1-0.2-0.1c-0.1,0-0.1-0.1-0.2-0.1l-0.2-0.1c-0.1,0-0.2-0.1-0.2-0.1
l-0.1-0.1c-0.1,0-0.1-0.1-0.2-0.1c-0.1,0-0.1-0.1-0.2-0.1c-0.1,0-0.1-0.1-0.2-0.1c-0.1,0-0.1-0.1-0.2-0.1c-0.1,0-0.1-0.1-0.2-0.1
L26,22c-0.1,0-0.2-0.1-0.3-0.1h0l-0.2-0.1l0,0c-0.1,0-0.2-0.1-0.3-0.1l-0.1,0c-0.1-0.1-0.2-0.1-0.4-0.1h0c-0.1,0-0.2-0.1-0.3-0.1h0
c-0.1-0.1-0.3-0.1-0.5-0.2l-0.1,0c-0.1-0.1-0.3-0.1-0.5-0.1h0c-0.3-0.1-0.5-0.2-0.8-0.3l0,0c-0.3-0.1-0.6-0.2-0.8-0.3l0,0
c-0.2-0.1-0.3-0.1-0.5-0.1l0,0c-0.7-0.2-1.5-0.4-2.3-0.5l0,0c-0.6-0.1-1.2-0.2-1.8-0.3v19.2c0.2,0.1,0.4,0.1,0.6,0.1
C30.3,42,41,39.4,41.8,33.2c0.4-3.3-2-6.9-6.1-10c3.3,1.9,6,4.1,7.8,6.4c1,1.4,1.7,2.7,2,4.1c-0.1-1.9-0.9-4.8-2-6.9L43.5,18.5
L43.5,18.5z"/>
<g>
<path class="st2" d="M67.5,26.9c0,1-0.2,2-0.7,2.9c-0.4,0.9-1,1.7-1.8,2.3c-0.8,0.7-1.7,1.2-2.8,1.6c-1.1,0.4-2.2,0.6-3.5,0.6H49
v-9h4.5v5.3h5.2c0.6,0,1.2-0.1,1.7-0.3c0.5-0.2,1-0.4,1.4-0.7c0.4-0.3,0.7-0.7,0.9-1.1c0.2-0.4,0.3-0.9,0.3-1.4
c0-0.5-0.1-1-0.3-1.4c-0.2-0.4-0.5-0.8-0.9-1.1c-0.4-0.3-0.8-0.6-1.4-0.7c-0.5-0.2-1.1-0.3-1.7-0.3H49l2.9-3.8h6.8
c1.3,0,2.4,0.2,3.5,0.5c1.1,0.3,2,0.8,2.8,1.5s1.4,1.4,1.8,2.3C67.2,24.9,67.5,25.8,67.5,26.9z"/>
<g>
<path class="st2" d="M82.8,21.4v6.8l-8.9-8c-0.4-0.3-0.7-0.5-1-0.6c-0.3-0.1-0.6-0.1-0.8-0.1c-0.3,0-0.6,0.1-0.9,0.1
c-0.3,0.1-0.6,0.3-0.8,0.5c-0.2,0.2-0.4,0.5-0.5,0.8c-0.1,0.3-0.2,0.8-0.2,1.2v12h4.1v-8.5l8.9,8c0.3,0.3,0.7,0.5,0.9,0.6
c0.3,0.1,0.6,0.1,0.8,0.1c0.3,0,0.6-0.1,0.9-0.1c0.3-0.1,0.6-0.3,0.8-0.5c0.2-0.2,0.4-0.5,0.5-0.8c0.1-0.3,0.2-0.8,0.2-1.2V19.9
L82.8,21.4z"/>
</g>
<path class="st2" d="M103,25.5c1.8,0,3.1,0.3,4,1c0.9,0.7,1.4,1.6,1.4,3c0,0.7-0.1,1.4-0.3,2c-0.2,0.6-0.6,1.1-1.1,1.5
c-0.5,0.4-1.2,0.7-1.9,0.9c-0.8,0.2-1.7,0.3-2.8,0.3H88.7l2.9-3.7h11c0.5,0,0.9-0.1,1.2-0.3c0.3-0.2,0.4-0.4,0.4-0.8
s-0.1-0.7-0.4-0.8c-0.3-0.2-0.6-0.2-1.2-0.2h-7.9c-0.9,0-1.8-0.1-2.4-0.3c-0.7-0.2-1.2-0.5-1.7-0.8c-0.5-0.4-0.8-0.8-1-1.3
c-0.2-0.5-0.3-1.1-0.3-1.7c0-0.7,0.1-1.3,0.4-1.9c0.2-0.6,0.6-1,1.1-1.4c0.5-0.4,1.1-0.7,1.9-0.9c0.8-0.2,1.7-0.3,2.8-0.3h12.6
l-2.9,3.8H95.1c-0.5,0-0.9,0.1-1.2,0.2c-0.3,0.1-0.4,0.4-0.4,0.8c0,0.4,0.1,0.6,0.4,0.8c0.3,0.1,0.6,0.2,1.2,0.2L103,25.5
L103,25.5z"/>
</g>
<g class="st3">
<path class="st4" d="M57.5,36.6v5h-1.4v-2.7v-0.7l0.1-0.4v-0.4h-0.1l-0.2,0.3l-0.2,0.3l-0.4,0.7l-1.7,2.8h-1.3l-1.7-2.8l-0.4-0.7
l-0.2-0.3l-0.2-0.3h-0.1l0,0.4v0.4l0.1,0.7v2.7h-1.5v-5h2.4l1.4,2.3l0.4,0.7l0.2,0.3l0.2,0.3H53l0.2-0.3l0.2-0.3l0.4-0.7l1.4-2.3
L57.5,36.6L57.5,36.6z"/>
<path class="st4" d="M63.6,40.6h-3.3l-0.5,0.9h-1.6l2.6-5H63l2.6,5H64L63.6,40.6z M63.2,39.9l-1.3-2.6l-1.3,2.6H63.2z"/>
<path class="st4" d="M66.2,41.6v-5H70c1,0,1.8,0.2,2.2,0.5s0.7,0.8,0.7,1.6c0,0.7,0,1.2-0.1,1.5c0,0.3-0.2,0.6-0.5,0.9
c-0.3,0.3-1,0.5-2.3,0.5H66.2L66.2,41.6z M67.7,40.7h2.1c0.7,0,1.2-0.1,1.4-0.3s0.3-0.7,0.3-1.4c0-0.7-0.1-1.2-0.3-1.4
s-0.6-0.3-1.3-0.3h-2.2L67.7,40.7L67.7,40.7z"/>
<path class="st4" d="M75.3,37.4v1.3h3.6v0.7h-3.6v1.4h3.8v0.8h-5.3v-5h5.3v0.8L75.3,37.4L75.3,37.4z"/>
<path class="st4" d="M84.4,37.4v1.3H88v0.7h-3.6v1.4h3.8v0.8H83v-5h5.3v0.8L84.4,37.4L84.4,37.4z"/>
<path class="st4" d="M94,40.6h-3.3l-0.5,0.9h-1.6l2.6-5h2.2l2.6,5h-1.5L94,40.6z M93.7,39.9l-1.3-2.6L91,39.9H93.7z"/>
<path class="st4" d="M102.4,38.1H101v-0.1c0-0.3-0.1-0.4-0.3-0.5c-0.2-0.1-0.6-0.1-1.2-0.1c-0.7,0-1.2,0-1.4,0.1
c-0.2,0.1-0.3,0.3-0.3,0.5c0,0.3,0.1,0.5,0.3,0.6s0.8,0.1,1.7,0.1c1.1,0,1.9,0.1,2.2,0.3c0.3,0.2,0.5,0.5,0.5,1.1
c0,0.7-0.2,1.1-0.6,1.3s-1.2,0.3-2.5,0.3c-1.3,0-2.2-0.1-2.5-0.3c-0.4-0.2-0.6-0.6-0.6-1.2V40h1.4v0.1c0,0.3,0.1,0.5,0.3,0.6
c0.2,0.1,0.7,0.1,1.5,0.1c0.7,0,1.1,0,1.2-0.1s0.3-0.3,0.3-0.6c0-0.3-0.1-0.4-0.2-0.5c-0.1-0.1-0.4-0.1-0.9-0.1l-0.8,0
c-1.2,0-2-0.1-2.3-0.3c-0.4-0.2-0.5-0.6-0.5-1.1c0-0.6,0.2-1,0.6-1.2c0.4-0.2,1.2-0.3,2.5-0.3c1.1,0,1.9,0.1,2.3,0.3
c0.4,0.2,0.6,0.5,0.6,1L102.4,38.1L102.4,38.1z"/>
<path class="st4" d="M110,36.6l-2.9,3.1v1.9h-1.5v-1.9l-2.8-3.1h1.7l1.2,1.3l0.3,0.4l0.2,0.2l0.2,0.2l0.2-0.2l0.2-0.2l0.3-0.4
l1.2-1.3H110z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.2 KiB

+1 -1
View File
@@ -289,7 +289,7 @@
}
},
"project": {
"title": "Settings",
"title": "Project Settings",
"description": "These settings only apply to the currently selected Project.",
"danger-zone": "Danger Zone",
"delete-project": "Delete Project",
@@ -20,9 +20,14 @@ export default function TeamInviteStep(): JSX.Element {
const { mutateAsync } = useAddUsersToOrg();
const { handlePopUpToggle, popUp, handlePopUpOpen } = usePopUp(["setUpEmail"] as const);
const orgId = String(localStorage.getItem("orgData.id"));
// Redirect user to the getting started page
const redirectToHome = async () => {
navigate({ to: "/organization/projects" as const });
navigate({
to: orgId ? ("/organizations/$orgId/projects" as const) : "/",
params: { orgId }
});
};
const inviteUsers = async ({ emails: inviteEmails }: { emails: string }) => {
@@ -32,7 +37,7 @@ export default function TeamInviteStep(): JSX.Element {
.map(async (email) => {
mutateAsync({
inviteeEmails: [email],
organizationId: String(localStorage.getItem("orgData.id")),
organizationId: orgId,
organizationRoleSlug: "member"
});
});
@@ -70,7 +70,8 @@ export default function NavHeader({
{currentOrg?.name?.charAt(0)}
</div>
<Link
to="/organization/projects"
to="/organizations/$orgId/projects"
params={{ orgId: currentOrg.id }}
className="truncate pl-0.5 text-sm font-medium text-primary/80 hover:text-primary"
>
{currentOrg?.name}
@@ -90,8 +91,8 @@ export default function NavHeader({
<FontAwesomeIcon icon={faAngleRight} className="mr-3 ml-3 text-sm text-gray-400" />
{pageName === "Secrets" ? (
<Link
to="/projects/secret-management/$projectId/overview"
params={{ projectId: currentProject.id }}
to="/organizations/$orgId/projects/secret-management/$projectId/overview"
params={{ orgId: currentOrg?.id || "", projectId: currentProject.id }}
className="text-sm font-medium text-primary/80 hover:text-primary"
>
{pageName}
@@ -126,8 +127,8 @@ export default function NavHeader({
<div className="flex items-center space-x-3">
<FontAwesomeIcon icon={faAngleRight} className="mr-1.5 ml-3 text-xs text-gray-400" />
<Link
to="/projects/secret-management/$projectId/secrets/$envSlug"
params={{ projectId: currentProject.id, envSlug: routerEnvSlug }}
to="/organizations/$orgId/projects/secret-management/$projectId/secrets/$envSlug"
params={{ orgId: currentOrg.id, projectId: currentProject.id, envSlug: routerEnvSlug }}
className="text-sm font-medium text-primary/80 hover:text-primary"
>
{userAvailableEnvs?.find(({ slug }) => slug === currentEnv)?.name}
@@ -188,8 +189,9 @@ export default function NavHeader({
</div>
) : (
<Link
to="/projects/secret-management/$projectId/secrets/$envSlug"
to="/organizations/$orgId/projects/secret-management/$projectId/secrets/$envSlug"
params={{
orgId: currentOrg?.id || "",
projectId: currentProject.id,
envSlug: routerEnvSlug || ""
}}
@@ -3,6 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link } from "@tanstack/react-router";
import { twMerge } from "tailwind-merge";
import { useOrganization } from "@app/context";
import { useTimedReset } from "@app/hooks";
import { createNotification } from "../notifications";
@@ -23,6 +24,7 @@ export const SecretDashboardPathBreadcrumb = ({
projectId,
disableCopy
}: Props) => {
const { currentOrg } = useOrganization();
const [, isCopying, setIsCopying] = useTimedReset({
initialState: false
});
@@ -69,8 +71,9 @@ export const SecretDashboardPathBreadcrumb = ({
</div>
) : (
<Link
to="/projects/secret-management/$projectId/secrets/$envSlug"
to="/organizations/$orgId/projects/secret-management/$projectId/secrets/$envSlug"
params={{
orgId: currentOrg.id,
projectId,
envSlug: environmentSlug
}}
@@ -57,7 +57,8 @@ export const CreateOrgModal: FC<CreateOrgModalProps> = ({ isOpen, onClose }) =>
});
navigate({
to: "/organization/projects"
to: "/organizations/$orgId/projects",
params: { orgId: organization.id }
});
localStorage.setItem("orgData.id", organization.id);
@@ -87,17 +87,24 @@ const shouldShowConditionalAccess = (
folderPath: string,
conditionalFields: string[]
): boolean => {
return actionRuleMap.some((rule) => {
// Find all rules that apply to this environment/path
const applicableRules = actionRuleMap.filter((rule) => {
const ruleConditions = rule[action]?.conditions;
if (!ruleConditions) return false;
// Check if any of the conditional fields are present
const hasConditionalField = conditionalFields.some((field) => ruleConditions[field]);
if (!hasConditionalField) return false;
// Check if base conditions (environment and secretPath) apply
return doBaseConditionsApply(ruleConditions, environment, folderPath);
});
// If no rules apply, don't show conditional
if (applicableRules.length === 0) return false;
// Check if ALL applicable rules have conditional fields and if at least one rule applies without conditional fields, show full access
const allRulesHaveConditionalFields = applicableRules.every((rule) => {
const ruleConditions = rule[action]?.conditions;
if (!ruleConditions) return false;
return conditionalFields.some((field) => ruleConditions[field]);
});
return allRulesHaveConditionalFields;
};
const determineAccessLevel = (
@@ -157,7 +157,7 @@ const NewProjectForm = ({ onOpenChange }: NewProjectFormProps) => {
onOpenChange(false);
navigate({
to: getProjectHomePage(project.type, project.environments),
params: { projectId: project.id }
params: { projectId: project.id, orgId: currentOrg.id }
});
};
const onSubmit = handleSubmit((data) => {
@@ -3,14 +3,7 @@ import { ReactNode } from "@tanstack/react-router";
import { LucideIcon } from "lucide-react";
import { twMerge } from "tailwind-merge";
import {
Badge,
InstanceIcon,
OrgIcon,
ProjectIcon,
SubOrgIcon,
TBadgeProps
} from "@app/components/v3";
import { InstanceIcon, OrgIcon, ProjectIcon, SubOrgIcon } from "@app/components/v3";
import { ProjectType } from "@app/hooks/api/projects/types";
type Props = {
@@ -21,41 +14,40 @@ type Props = {
scope: "org" | "namespace" | "instance" | ProjectType | null;
};
const SCOPE_NAME: Record<NonNullable<Props["scope"]>, { label: string; icon: LucideIcon }> = {
org: { label: "Organization", icon: OrgIcon },
[ProjectType.SecretManager]: { label: "Project", icon: ProjectIcon },
[ProjectType.CertificateManager]: { label: "Project", icon: ProjectIcon },
[ProjectType.SSH]: { label: "Project", icon: ProjectIcon },
[ProjectType.KMS]: { label: "Project", icon: ProjectIcon },
[ProjectType.PAM]: { label: "Project", icon: ProjectIcon },
[ProjectType.SecretScanning]: { label: "Project", icon: ProjectIcon },
namespace: { label: "Sub-Organization", icon: SubOrgIcon },
instance: { label: "Server", icon: InstanceIcon }
};
const SCOPE_VARIANT: Record<NonNullable<Props["scope"]>, TBadgeProps["variant"]> = {
org: "org",
[ProjectType.SecretManager]: "project",
[ProjectType.CertificateManager]: "project",
[ProjectType.SSH]: "project",
[ProjectType.KMS]: "project",
[ProjectType.PAM]: "project",
[ProjectType.SecretScanning]: "project",
namespace: "sub-org",
instance: "neutral"
const SCOPE_BADGE: Record<NonNullable<Props["scope"]>, { icon: LucideIcon; className: string }> = {
org: { className: "text-org", icon: OrgIcon },
[ProjectType.SecretManager]: { className: "text-project", icon: ProjectIcon },
[ProjectType.CertificateManager]: { className: "text-project", icon: ProjectIcon },
[ProjectType.SSH]: { className: "text-project", icon: ProjectIcon },
[ProjectType.KMS]: { className: "text-project", icon: ProjectIcon },
[ProjectType.PAM]: { className: "text-project", icon: ProjectIcon },
[ProjectType.SecretScanning]: { className: "text-project", icon: ProjectIcon },
namespace: { className: "text-sub-org", icon: SubOrgIcon },
instance: { className: "text-neutral", icon: InstanceIcon }
};
export const PageHeader = ({ title, description, children, className, scope }: Props) => (
<div className={twMerge("mb-10 w-full", className)}>
<div className="flex w-full justify-between">
<div className="mr-4 flex w-full items-center">
<h1 className="text-3xl font-medium text-white capitalize">{title}</h1>
{scope && (
<Badge variant={SCOPE_VARIANT[scope]} className="mt-1 ml-2.5">
{createElement(SCOPE_NAME[scope].icon)}
{SCOPE_NAME[scope].label}
</Badge>
)}
<h1
className={twMerge(
"text-3xl font-medium text-white capitalize underline decoration-2 underline-offset-4",
scope === "org" && "decoration-org/90",
scope === "instance" && "decoration-neutral/90",
scope === "namespace" && "decoration-sub-org/90",
Object.values(ProjectType).includes((scope as ProjectType) ?? "") &&
"decoration-project/90",
!scope && "no-underline"
)}
>
{scope &&
createElement(SCOPE_BADGE[scope].icon, {
size: 26,
className: twMerge(SCOPE_BADGE[scope].className, "mr-3 mb-1 inline-block")
})}
{title}
</h1>
</div>
<div className="flex items-center gap-2">{children}</div>
</div>
+2 -2
View File
@@ -47,8 +47,8 @@ export const Tab = ({
}) => (
<TabsPrimitive.Trigger
className={twMerge(
"flex h-10 cursor-pointer items-center justify-center border-transparent",
"px-3 text-sm font-medium whitespace-nowrap text-mineshaft-400 transition-all select-none",
"flex h-11 cursor-pointer items-center justify-center border-transparent",
"px-3 text-sm font-medium whitespace-nowrap text-mineshaft-300/75 transition-all select-none",
"data-[orientation=vertical]:xl:h-5 data-[orientation=vertical]:xl:border-b-0 data-[orientation=vertical]:xl:border-l",
"border-b hover:text-mineshaft-200",
"data-[state=active]:border-mineshaft-400 data-[state=active]:text-white",
@@ -1,4 +1,4 @@
import { BookOpenIcon } from "lucide-react";
import { ExternalLinkIcon } from "lucide-react";
import { Badge } from "@app/components/v3";
@@ -8,10 +8,10 @@ type TDocumentationLinkBadgeProps = {
export function DocumentationLinkBadge({ href }: TDocumentationLinkBadgeProps) {
return (
<Badge variant="neutral" asChild>
<Badge variant="info" asChild>
<a href={href} target="_blank" rel="noopener noreferrer">
<BookOpenIcon />
Documentation
<ExternalLinkIcon />
</a>
</Badge>
);
+162 -162
View File
@@ -25,343 +25,343 @@ export const ROUTE_PATHS = Object.freeze({
Organization: {
Settings: {
OauthCallbackPage: setRoute(
"/organization/settings/oauth/callback",
"/_authenticate/_inject-org-details/_org-layout/organization/settings/oauth/callback"
"/organizations/$orgId/settings/oauth/callback",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/settings/oauth/callback"
)
},
SecretSharing: setRoute(
"/organization/secret-sharing",
"/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/"
"/organizations/$orgId/secret-sharing",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/secret-sharing/"
),
SettingsPage: setRoute(
"/organization/settings",
"/_authenticate/_inject-org-details/_org-layout/organization/settings/"
"/organizations/$orgId/settings",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/settings/"
),
GroupDetailsByIDPage: setRoute(
"/organization/groups/$groupId",
"/_authenticate/_inject-org-details/_org-layout/organization/groups/$groupId"
"/organizations/$orgId/groups/$groupId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/groups/$groupId"
),
IdentityDetailsByIDPage: setRoute(
"/organization/identities/$identityId",
"/_authenticate/_inject-org-details/_org-layout/organization/identities/$identityId"
"/organizations/$orgId/identities/$identityId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/identities/$identityId"
),
UserDetailsByIDPage: setRoute(
"/organization/members/$membershipId",
"/_authenticate/_inject-org-details/_org-layout/organization/members/$membershipId"
"/organizations/$orgId/members/$membershipId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/members/$membershipId"
),
AccessControlPage: setRoute(
"/organization/access-management",
"/_authenticate/_inject-org-details/_org-layout/organization/access-management"
"/organizations/$orgId/access-management",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/access-management"
),
RoleByIDPage: setRoute(
"/organization/roles/$roleId",
"/_authenticate/_inject-org-details/_org-layout/organization/roles/$roleId"
"/organizations/$orgId/roles/$roleId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/roles/$roleId"
),
AppConnections: {
OauthCallbackPage: setRoute(
"/organization/app-connections/$appConnection/oauth/callback",
"/_authenticate/_inject-org-details/_org-layout/organization/app-connections/$appConnection/oauth/callback"
"/organizations/$orgId/app-connections/$appConnection/oauth/callback",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/app-connections/$appConnection/oauth/callback"
)
},
NetworkingPage: setRoute(
"/organization/networking",
"/_authenticate/_inject-org-details/_org-layout/organization/networking"
"/organizations/$orgId/networking",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/networking"
)
},
SecretManager: {
ApprovalPage: setRoute(
"/projects/secret-management/$projectId/approval",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/approval"
"/organizations/$orgId/projects/secret-management/$projectId/approval",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/approval"
),
SecretDashboardPage: setRoute(
"/projects/secret-management/$projectId/secrets/$envSlug",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/secrets/$envSlug"
"/organizations/$orgId/projects/secret-management/$projectId/secrets/$envSlug",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/secrets/$envSlug"
),
RollbackPreviewPage: setRoute(
"/projects/secret-management/$projectId/commits/$environment/$folderId/$commitId/restore",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/restore"
"/organizations/$orgId/projects/secret-management/$projectId/commits/$environment/$folderId/$commitId/restore",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/restore"
),
CommitDetailsPage: setRoute(
"/projects/secret-management/$projectId/commits/$environment/$folderId/$commitId",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId"
"/organizations/$orgId/projects/secret-management/$projectId/commits/$environment/$folderId/$commitId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId"
),
CommitsPage: setRoute(
"/projects/secret-management/$projectId/commits/$environment/$folderId",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/commits/$environment/$folderId"
"/organizations/$orgId/projects/secret-management/$projectId/commits/$environment/$folderId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/commits/$environment/$folderId"
),
OverviewPage: setRoute(
"/projects/secret-management/$projectId/overview",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/overview"
"/organizations/$orgId/projects/secret-management/$projectId/overview",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/overview"
),
IntegrationsListPage: setRoute(
"/projects/secret-management/$projectId/integrations",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/"
"/organizations/$orgId/projects/secret-management/$projectId/integrations",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/"
),
IntegrationDetailsByIDPage: setRoute(
"/projects/secret-management/$projectId/integrations/$integrationId",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/$integrationId"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/$integrationId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/$integrationId"
),
SecretSyncDetailsByIDPage: setRoute(
"/projects/secret-management/$projectId/integrations/secret-syncs/$destination/$syncId",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/secret-syncs/$destination/$syncId"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/secret-syncs/$destination/$syncId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/secret-syncs/$destination/$syncId"
),
Integratons: {
SelectIntegrationAuth: setRoute(
"/projects/secret-management/$projectId/integrations/select-integration-auth",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/select-integration-auth"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/select-integration-auth",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/select-integration-auth"
),
HerokuOauthCallbackPage: setRoute(
"/projects/secret-management/$projectId/integrations/heroku/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/heroku/oauth2/callback"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/heroku/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/heroku/oauth2/callback"
),
HerokuConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/heroku/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/heroku/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/heroku/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/heroku/create"
),
AwsParameterStoreConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/aws-parameter-store/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/aws-parameter-store/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/aws-parameter-store/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/aws-parameter-store/create"
),
AwsSecretManagerConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/aws-secret-manager/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/aws-secret-manager/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/aws-secret-manager/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/aws-secret-manager/create"
),
AzureAppConfigurationsOauthCallbackPage: setRoute(
"/projects/secret-management/$projectId/integrations/azure-app-configuration/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/azure-app-configuration/oauth2/callback"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/azure-app-configuration/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/azure-app-configuration/oauth2/callback"
),
AzureAppConfigurationsConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/azure-app-configuration/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/azure-app-configuration/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/azure-app-configuration/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/azure-app-configuration/create"
),
AzureDevopsConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/azure-devops/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/azure-devops/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/azure-devops/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/azure-devops/create"
),
AzureKeyVaultAuthorizePage: setRoute(
"/projects/secret-management/$projectId/integrations/azure-key-vault/authorize",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/azure-key-vault/authorize"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/azure-key-vault/authorize",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/azure-key-vault/authorize"
),
AzureKeyVaultOauthCallbackPage: setRoute(
"/projects/secret-management/$projectId/integrations/azure-key-vault/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/azure-key-vault/oauth2/callback"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/azure-key-vault/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/azure-key-vault/oauth2/callback"
),
AzureKeyVaultConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/azure-key-vault/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/azure-key-vault/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/azure-key-vault/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/azure-key-vault/create"
),
BitbucketOauthCallbackPage: setRoute(
"/projects/secret-management/$projectId/integrations/bitbucket/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/bitbucket/oauth2/callback"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/bitbucket/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/bitbucket/oauth2/callback"
),
BitbucketConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/bitbucket/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/bitbucket/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/bitbucket/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/bitbucket/create"
),
ChecklyConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/checkly/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/checkly/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/checkly/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/checkly/create"
),
CircleConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/circleci/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/circleci/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/circleci/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/circleci/create"
),
CloudflarePagesConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/cloudflare-pages/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/cloudflare-pages/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/cloudflare-pages/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/cloudflare-pages/create"
),
DigitalOceanAppPlatformConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/digital-ocean-app-platform/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/digital-ocean-app-platform/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/digital-ocean-app-platform/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/digital-ocean-app-platform/create"
),
CloudflareWorkersConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/cloudflare-workers/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/cloudflare-workers/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/cloudflare-workers/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/cloudflare-workers/create"
),
CodefreshConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/codefresh/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/codefresh/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/codefresh/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/codefresh/create"
),
GcpSecretManagerConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/gcp-secret-manager/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/gcp-secret-manager/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/gcp-secret-manager/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/gcp-secret-manager/create"
),
GcpSecretManagerOauthCallbackPage: setRoute(
"/projects/secret-management/$projectId/integrations/gcp-secret-manager/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/gcp-secret-manager/oauth2/callback"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/gcp-secret-manager/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/gcp-secret-manager/oauth2/callback"
),
GithubConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/github/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/github/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/github/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/github/create"
),
GithubOauthCallbackPage: setRoute(
"/projects/secret-management/$projectId/integrations/github/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/github/oauth2/callback"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/github/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/github/oauth2/callback"
),
GitlabConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/gitlab/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/gitlab/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/gitlab/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/gitlab/create"
),
GitlabOauthCallbackPage: setRoute(
"/projects/secret-management/$projectId/integrations/gitlab/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/gitlab/oauth2/callback"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/gitlab/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/gitlab/oauth2/callback"
),
VercelOauthCallbackPage: setRoute(
"/projects/secret-management/$projectId/integrations/vercel/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/vercel/oauth2/callback"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/vercel/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/vercel/oauth2/callback"
),
VercelConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/vercel/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/vercel/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/vercel/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/vercel/create"
),
FlyioConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/flyio/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/flyio/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/flyio/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/flyio/create"
),
HashicorpVaultConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/hashicorp-vault/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/hashicorp-vault/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/hashicorp-vault/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/hashicorp-vault/create"
),
HasuraCloudConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/hasura-cloud/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/hasura-cloud/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/hasura-cloud/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/hasura-cloud/create"
),
LaravelForgeConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/laravel-forge/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/laravel-forge/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/laravel-forge/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/laravel-forge/create"
),
NorthflankConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/northflank/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/northflank/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/northflank/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/northflank/create"
),
RailwayConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/railway/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/railway/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/railway/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/railway/create"
),
RenderConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/render/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/render/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/render/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/render/create"
),
RundeckConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/rundeck/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/rundeck/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/rundeck/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/rundeck/create"
),
WindmillConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/windmill/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/windmill/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/windmill/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/windmill/create"
),
TravisCIConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/travisci/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/travisci/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/travisci/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/travisci/create"
),
TerraformCloudConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/terraform-cloud/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/terraform-cloud/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/terraform-cloud/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/terraform-cloud/create"
),
TeamcityConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/teamcity/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/teamcity/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/teamcity/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/teamcity/create"
),
SupabaseConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/supabase/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/supabase/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/supabase/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/supabase/create"
),
OctopusDeployCloudConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/octopus-deploy/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/octopus-deploy/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/octopus-deploy/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/octopus-deploy/create"
),
DatabricksConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/databricks/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/databricks/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/databricks/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/databricks/create"
),
QoveryConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/qovery/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/qovery/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/qovery/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/qovery/create"
),
Cloud66ConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/cloud-66/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/cloud-66/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/cloud-66/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/cloud-66/create"
),
NetlifyConfigurePage: setRoute(
"/projects/secret-management/$projectId/integrations/netlify/create",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/netlify/create"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/netlify/create",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/netlify/create"
),
NetlifyOuathCallbackPage: setRoute(
"/projects/secret-management/$projectId/integrations/netlify/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/netlify/oauth2/callback"
"/organizations/$orgId/projects/secret-management/$projectId/integrations/netlify/oauth2/callback",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/netlify/oauth2/callback"
)
}
},
CertManager: {
CertAuthDetailsByIDPage: setRoute(
"/projects/cert-management/$projectId/ca/$caName",
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/ca/$caName"
"/organizations/$orgId/projects/cert-management/$projectId/ca/$caId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caId"
),
SubscribersPage: setRoute(
"/projects/cert-management/$projectId/subscribers",
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/subscribers"
"/organizations/$orgId/projects/cert-management/$projectId/subscribers",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/subscribers"
),
CertificateAuthoritiesPage: setRoute(
"/projects/cert-management/$projectId/certificate-authorities",
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/certificate-authorities"
"/organizations/$orgId/projects/cert-management/$projectId/certificate-authorities",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/certificate-authorities"
),
AlertingPage: setRoute(
"/projects/cert-management/$projectId/alerting",
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/alerting"
"/organizations/$orgId/projects/cert-management/$projectId/alerting",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/alerting"
),
PkiCollectionDetailsByIDPage: setRoute(
"/projects/cert-management/$projectId/pki-collections/$collectionId",
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/pki-collections/$collectionId"
"/organizations/$orgId/projects/cert-management/$projectId/pki-collections/$collectionId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/pki-collections/$collectionId"
),
PkiSubscriberDetailsByIDPage: setRoute(
"/projects/cert-management/$projectId/subscribers/$subscriberName",
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/subscribers/$subscriberName"
"/organizations/$orgId/projects/cert-management/$projectId/subscribers/$subscriberName",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/subscribers/$subscriberName"
),
IntegrationsListPage: setRoute(
"/projects/cert-management/$projectId/integrations",
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/integrations/"
"/organizations/$orgId/projects/cert-management/$projectId/integrations",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/integrations/"
),
PkiSyncDetailsByIDPage: setRoute(
"/projects/cert-management/$projectId/integrations/$syncId",
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/integrations/$syncId"
"/organizations/$orgId/projects/cert-management/$projectId/integrations/$syncId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/integrations/$syncId"
)
},
Ssh: {
SshCaByIDPage: setRoute(
"/projects/ssh/$projectId/ca/$caId",
"/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/ca/$caId"
"/organizations/$orgId/projects/ssh/$projectId/ca/$caId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/ssh/$projectId/_ssh-layout/ca/$caId"
),
SshHostGroupDetailsByIDPage: setRoute(
"/projects/ssh/$projectId/ssh-host-groups/$sshHostGroupId",
"/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/ssh-host-groups/$sshHostGroupId"
"/organizations/$orgId/projects/ssh/$projectId/ssh-host-groups/$sshHostGroupId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/ssh/$projectId/_ssh-layout/ssh-host-groups/$sshHostGroupId"
)
},
SecretScanning: {
DataSourceByIdPage: setRoute(
"/projects/secret-scanning/$projectId/data-sources/$type/$dataSourceId",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/data-sources/$type/$dataSourceId"
"/organizations/$orgId/projects/secret-scanning/$projectId/data-sources/$type/$dataSourceId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-scanning/$projectId/_secret-scanning-layout/data-sources/$type/$dataSourceId"
),
FindingsPage: setRoute(
"/projects/secret-scanning/$projectId/findings",
"/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/findings"
"/organizations/$orgId/projects/secret-scanning/$projectId/findings",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-scanning/$projectId/_secret-scanning-layout/findings"
)
},
Pam: {
AccountsPage: setRoute(
"/projects/pam/$projectId/accounts",
"/_authenticate/_inject-org-details/_org-layout/projects/pam/$projectId/_pam-layout/accounts"
"/organizations/$orgId/projects/pam/$projectId/accounts",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/accounts"
),
ResourcesPage: setRoute(
"/projects/pam/$projectId/resources",
"/_authenticate/_inject-org-details/_org-layout/projects/pam/$projectId/_pam-layout/resources"
"/organizations/$orgId/projects/pam/$projectId/resources",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/resources"
),
SessionsPage: setRoute(
"/projects/pam/$projectId/sessions",
"/_authenticate/_inject-org-details/_org-layout/projects/pam/$projectId/_pam-layout/sessions/"
"/organizations/$orgId/projects/pam/$projectId/sessions",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/"
),
PamSessionByIDPage: setRoute(
"/projects/pam/$projectId/sessions/$sessionId",
"/_authenticate/_inject-org-details/_org-layout/projects/pam/$projectId/_pam-layout/sessions/$sessionId"
"/organizations/$orgId/projects/pam/$projectId/sessions/$sessionId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/$sessionId"
)
},
Public: {
+4
View File
@@ -50,6 +50,7 @@ import { BitbucketConnectionMethod } from "@app/hooks/api/appConnections/types/b
import { ChecklyConnectionMethod } from "@app/hooks/api/appConnections/types/checkly-connection";
import { ChefConnectionMethod } from "@app/hooks/api/appConnections/types/chef-connection";
import { DigitalOceanConnectionMethod } from "@app/hooks/api/appConnections/types/digital-ocean";
import { DNSMadeEasyConnectionMethod } from "@app/hooks/api/appConnections/types/dns-made-easy-connection";
import { HerokuConnectionMethod } from "@app/hooks/api/appConnections/types/heroku-connection";
import { LaravelForgeConnectionMethod } from "@app/hooks/api/appConnections/types/laravel-forge-connection";
import { NetlifyConnectionMethod } from "@app/hooks/api/appConnections/types/netlify-connection";
@@ -112,6 +113,7 @@ export const APP_CONNECTION_MAP: Record<
[AppConnection.Flyio]: { name: "Fly.io", image: "Flyio.svg" },
[AppConnection.GitLab]: { name: "GitLab", image: "GitLab.png" },
[AppConnection.Cloudflare]: { name: "Cloudflare", image: "Cloudflare.png" },
[AppConnection.DNSMadeEasy]: { name: "DNS Made Easy", image: "DNSMadeEasy.svg", size: 120 },
[AppConnection.Zabbix]: { name: "Zabbix", image: "Zabbix.png" },
[AppConnection.Railway]: { name: "Railway", image: "Railway.png" },
[AppConnection.Bitbucket]: { name: "Bitbucket", image: "Bitbucket.png" },
@@ -217,6 +219,8 @@ export const getAppConnectionMethodDetails = (method: TAppConnection["method"])
return { name: "Client Secret", icon: faKey };
case AzureClientSecretsConnectionMethod.Certificate:
return { name: "Certificate", icon: faCertificate };
case DNSMadeEasyConnectionMethod.APIKeySecret:
return { name: "API Key & Secret", icon: faKey };
default:
throw new Error(`Unhandled App Connection Method: ${method}`);
}
+8 -13
View File
@@ -14,11 +14,6 @@ const secretsToBeAdded = [
secretValue: "OVERRIDE_THIS",
secretComment: "Override secrets with personal value"
},
{
secretKey: "DB_PASSWORD",
secretValue: "OVERRIDE_THIS",
secretComment: "Another secret override"
},
{
secretKey: "DB_PASSWORD",
secretValue: "example_password"
@@ -64,11 +59,11 @@ export const initProjectHelper = async ({ projectName }: { projectName: string }
export const getProjectBaseURL = (type: ProjectType) => {
switch (type) {
case ProjectType.SecretManager:
return "/projects/secret-management/$projectId";
return "/organizations/$orgId/projects/secret-management/$projectId";
case ProjectType.CertificateManager:
return "/projects/cert-management/$projectId";
return "/organizations/$orgId/projects/cert-management/$projectId";
default:
return `/projects/${type}/$projectId` as const;
return `/organizations/$orgId/projects/${type}/$projectId` as const;
}
};
@@ -77,15 +72,15 @@ export const getProjectBaseURL = (type: ProjectType) => {
export const getProjectHomePage = (type: ProjectType, environments: ProjectEnv[]) => {
switch (type) {
case ProjectType.SecretManager:
return "/projects/secret-management/$projectId/overview" as const;
return "/organizations/$orgId/projects/secret-management/$projectId/overview" as const;
case ProjectType.CertificateManager:
return "/projects/cert-management/$projectId/policies" as const;
return "/organizations/$orgId/projects/cert-management/$projectId/policies" as const;
case ProjectType.SecretScanning:
return `/projects/${type}/$projectId/data-sources` as const;
return `/organizations/$orgId/projects/${type}/$projectId/data-sources` as const;
case ProjectType.PAM:
return `/projects/${type}/$projectId/accounts` as const;
return `/organizations/$orgId/projects/${type}/$projectId/accounts` as const;
default:
return `/projects/${type}/$projectId/overview` as const;
return `/organizations/$orgId/projects/${type}/$projectId/overview` as const;
}
};
@@ -0,0 +1,2 @@
export * from "./queries";
export * from "./types";
@@ -0,0 +1,37 @@
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { appConnectionKeys } from "../queries";
import { TDNSMadeEasyZone } from "./types";
const dnsMadeEasyConnectionKeys = {
all: [...appConnectionKeys.all, "dns-made-easy"] as const,
listZones: (connectionId: string) =>
[...dnsMadeEasyConnectionKeys.all, "zones", connectionId] as const
};
export const useDNSMadeEasyConnectionListZones = (
connectionId: string,
options?: Omit<
UseQueryOptions<
TDNSMadeEasyZone[],
unknown,
TDNSMadeEasyZone[],
ReturnType<typeof dnsMadeEasyConnectionKeys.listZones>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: dnsMadeEasyConnectionKeys.listZones(connectionId),
queryFn: async () => {
const { data } = await apiRequest.get<TDNSMadeEasyZone[]>(
`/api/v1/app-connections/dns-made-easy/${connectionId}/dns-made-easy-zones`
);
return data;
},
...options
});
};
@@ -0,0 +1,4 @@
export type TDNSMadeEasyZone = {
id: string;
name: string;
};
@@ -29,6 +29,7 @@ export enum AppConnection {
Flyio = "flyio",
GitLab = "gitlab",
Cloudflare = "cloudflare",
DNSMadeEasy = "dns-made-easy",
Bitbucket = "bitbucket",
Zabbix = "zabbix",
Railway = "railway",
@@ -188,6 +188,10 @@ export type TMongoDBConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.MongoDB;
};
export type TDNSMadeEasyConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.DNSMadeEasy;
};
export type TAppConnectionOption =
| TAwsConnectionOption
| TGitHubConnectionOption
@@ -231,7 +235,8 @@ export type TAppConnectionOption =
| TLaravelForgeConnectionOption
| TRedisConnectionOption
| TMongoDBConnectionOption
| TChefConnectionOption;
| TChefConnectionOption
| TDNSMadeEasyConnectionOption;
export type TAppConnectionOptionMap = {
[AppConnection.AWS]: TAwsConnectionOption;
@@ -263,6 +268,7 @@ export type TAppConnectionOptionMap = {
[AppConnection.Flyio]: TFlyioConnectionOption;
[AppConnection.GitLab]: TGitlabConnectionOption;
[AppConnection.Cloudflare]: TCloudflareConnectionOption;
[AppConnection.DNSMadeEasy]: TDNSMadeEasyConnectionOption;
[AppConnection.Bitbucket]: TBitbucketConnectionOption;
[AppConnection.Zabbix]: TZabbixConnectionOption;
[AppConnection.Railway]: TRailwayConnectionOption;
@@ -0,0 +1,14 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
export enum DNSMadeEasyConnectionMethod {
APIKeySecret = "api-key-secret"
}
export type TDNSMadeEasyConnection = TRootAppConnection & { app: AppConnection.DNSMadeEasy } & {
method: DNSMadeEasyConnectionMethod.APIKeySecret;
credentials: {
apiKey: string;
secretKey: string;
};
};
@@ -15,6 +15,7 @@ import { TChefConnection } from "./chef-connection";
import { TCloudflareConnection } from "./cloudflare-connection";
import { TDatabricksConnection } from "./databricks-connection";
import { TDigitalOceanConnection } from "./digital-ocean";
import { TDNSMadeEasyConnection } from "./dns-made-easy-connection";
import { TFlyioConnection } from "./flyio-connection";
import { TGcpConnection } from "./gcp-connection";
import { TGitHubConnection } from "./github-connection";
@@ -58,6 +59,7 @@ export * from "./checkly-connection";
export * from "./chef-connection";
export * from "./cloudflare-connection";
export * from "./databricks-connection";
export * from "./dns-made-easy-connection";
export * from "./flyio-connection";
export * from "./gcp-connection";
export * from "./github-connection";
@@ -130,7 +132,8 @@ export type TAppConnection =
| TOktaConnection
| TRedisConnection
| TMongoDBConnection
| TChefConnection;
| TChefConnection
| TDNSMadeEasyConnection;
export type TAvailableAppConnection = Pick<TAppConnection, "name" | "id" | "projectId">;
+4 -2
View File
@@ -16,12 +16,14 @@ export const caStatusToNameMap: { [K in CaStatus]: string } = {
export const ACME_DNS_PROVIDER_NAME_MAP: Record<AcmeDnsProvider, string> = {
[AcmeDnsProvider.ROUTE53]: "Route53",
[AcmeDnsProvider.Cloudflare]: "Cloudflare"
[AcmeDnsProvider.Cloudflare]: "Cloudflare",
[AcmeDnsProvider.DNSMadeEasy]: "DNS Made Easy"
};
export const ACME_DNS_PROVIDER_APP_CONNECTION_MAP: Record<AcmeDnsProvider, AppConnection> = {
[AcmeDnsProvider.ROUTE53]: AppConnection.AWS,
[AcmeDnsProvider.Cloudflare]: AppConnection.Cloudflare
[AcmeDnsProvider.Cloudflare]: AppConnection.Cloudflare,
[AcmeDnsProvider.DNSMadeEasy]: AppConnection.DNSMadeEasy
};
export const CA_TYPE_CAPABILITIES_MAP: Record<CaType, CaCapability[]> = {
+2 -1
View File
@@ -21,7 +21,8 @@ export enum CaRenewalType {
export enum AcmeDnsProvider {
ROUTE53 = "route53",
Cloudflare = "cloudflare"
Cloudflare = "cloudflare",
DNSMadeEasy = "dns-made-easy"
}
export enum CaCapability {
+1 -1
View File
@@ -13,12 +13,12 @@ export {
export {
useGetAzureAdcsTemplates,
useGetCa,
useGetCaById,
useGetCaCert,
useGetCaCerts,
useGetCaCertTemplates,
useGetCaCrls,
useGetCaCsr,
useGetInternalCaById,
useListCasByProjectId,
useListCasByTypeAndProjectId,
useListExternalCasByProjectId
+14 -19
View File
@@ -27,21 +27,20 @@ import {
export const useUpdateCa = () => {
const queryClient = useQueryClient();
return useMutation<TUnifiedCertificateAuthority, object, TUpdateCertificateAuthorityDTO>({
mutationFn: async ({ caName, ...body }) => {
mutationFn: async ({ id, ...body }) => {
const { data } = await apiRequest.patch<TUnifiedCertificateAuthority>(
`/api/v1/pki/ca/${body.type}/${caName}`,
`/api/v1/cert-manager/ca/${body.type}/${id}`,
body
);
return data;
},
onSuccess: ({ projectId, type }, { caName }) => {
caKeys.getCaByNameAndProjectId(caName, projectId);
onSuccess: ({ projectId, type }, { id }) => {
queryClient.invalidateQueries({
queryKey: caKeys.listCasByTypeAndProjectId(type, projectId)
});
queryClient.invalidateQueries({
queryKey: caKeys.getCaByNameAndProjectId(caName, projectId)
queryKey: caKeys.getCaById(id)
});
// Invalidate external CAs list
queryClient.invalidateQueries({
@@ -56,7 +55,7 @@ export const useCreateCa = () => {
return useMutation<TUnifiedCertificateAuthority, object, TCreateCertificateAuthorityDTO>({
mutationFn: async (body) => {
const { data } = await apiRequest.post<TUnifiedCertificateAuthority>(
`/api/v1/pki/ca/${body.type}`,
`/api/v1/cert-manager/ca/${body.type}`,
body
);
return data;
@@ -76,14 +75,9 @@ export const useCreateCa = () => {
export const useDeleteCa = () => {
const queryClient = useQueryClient();
return useMutation<TUnifiedCertificateAuthority, object, TDeleteCertificateAuthorityDTO>({
mutationFn: async ({ caName, type, projectId }) => {
mutationFn: async ({ id, type }) => {
const { data } = await apiRequest.delete<TUnifiedCertificateAuthority>(
`/api/v1/pki/ca/${type}/${caName}`,
{
data: {
projectId
}
}
`/api/v1/cert-manager/ca/${type}/${id}`
);
return data;
},
@@ -104,7 +98,7 @@ export const useSignIntermediate = () => {
return useMutation<TSignIntermediateResponse, object, TSignIntermediateDTO>({
mutationFn: async (body) => {
const { data } = await apiRequest.post<TSignIntermediateResponse>(
`/api/v1/pki/ca/${body.caId}/sign-intermediate`,
`/api/v1/cert-manager/ca/internal/${body.caId}/sign-intermediate`,
body
);
return data;
@@ -117,13 +111,14 @@ export const useImportCaCertificate = (projectId: string) => {
return useMutation<TImportCaCertificateResponse, object, TImportCaCertificateDTO>({
mutationFn: async ({ caId, ...body }) => {
const { data } = await apiRequest.post<TImportCaCertificateResponse>(
`/api/v1/pki/ca/${caId}/import-certificate`,
`/api/v1/cert-manager/ca/internal/${caId}/import-certificate`,
body
);
return data;
},
onSuccess: (_, { caId }) => {
queryClient.invalidateQueries({ queryKey: projectKeys.getProjectCas({ projectId }) });
queryClient.invalidateQueries({ queryKey: caKeys.getCaById(caId) });
queryClient.invalidateQueries({ queryKey: caKeys.getCaCerts(caId) });
queryClient.invalidateQueries({ queryKey: caKeys.getCaCert(caId) });
queryClient.invalidateQueries({
@@ -133,7 +128,7 @@ export const useImportCaCertificate = (projectId: string) => {
});
};
// consider rename to issue certificate
// TODO: DEPRECATE
export const useCreateCertificate = () => {
const queryClient = useQueryClient();
return useMutation<TCreateCertificateResponse, object, TCreateCertificateDTO>({
@@ -157,7 +152,7 @@ export const useCreateCertificateV3 = (options?: { projectId?: string }) => {
return useMutation<TCreateCertificateV3Response, object, TCreateCertificateV3DTO>({
mutationFn: async (body) => {
const { data } = await apiRequest.post<TCreateCertificateV3Response>(
"/api/v3/pki/certificates/issue-certificate",
"/api/v1/cert-manager/certificates/issue-certificate",
body
);
return data;
@@ -185,7 +180,7 @@ export const useOrderCertificateWithProfile = () => {
return useMutation<TOrderCertificateResponse, object, TOrderCertificateDTO>({
mutationFn: async (body) => {
const { data } = await apiRequest.post<TOrderCertificateResponse>(
"/api/v3/pki/certificates/order-certificate",
"/api/v1/cert-manager/certificates/order-certificate",
body
);
return data;
@@ -203,7 +198,7 @@ export const useRenewCa = () => {
return useMutation<TRenewCaResponse, object, TRenewCaDTO>({
mutationFn: async (body) => {
const { data } = await apiRequest.post<TRenewCaResponse>(
`/api/v1/pki/ca/${body.caId}/renew`,
`/api/v1/cert-manager/ca/internal/${body.caId}/renew`,
body
);
return data;
+28 -29
View File
@@ -4,7 +4,11 @@ import { apiRequest } from "@app/config/request";
import { TCertificateTemplate } from "../certificateTemplates/types";
import { CaType } from "./enums";
import { TAzureAdCsTemplate, TCertificateAuthority, TUnifiedCertificateAuthority } from "./types";
import {
TAzureAdCsTemplate,
TInternalCertificateAuthority,
TUnifiedCertificateAuthority
} from "./types";
export const caKeys = {
getCaById: (caId: string) => [{ caId }, "ca"],
@@ -25,24 +29,16 @@ export const caKeys = {
]
};
export const useGetCa = ({
caName,
projectId,
type
}: {
caName: string;
projectId: string;
type: CaType;
}) => {
export const useGetCa = ({ caId, type }: { caId: string; type: CaType }) => {
return useQuery({
queryKey: caKeys.getCaByNameAndProjectId(caName, projectId),
queryKey: caKeys.getCaById(caId),
queryFn: async () => {
const { data } = await apiRequest.get<TUnifiedCertificateAuthority>(
`/api/v1/pki/ca/${type}/${caName}?projectId=${projectId}`
`/api/v1/cert-manager/ca/${type}/${caId}`
);
return data;
},
enabled: Boolean(caName && projectId && type)
enabled: Boolean(caId && type)
});
};
@@ -51,7 +47,7 @@ export const useListCasByTypeAndProjectId = (type: CaType, projectId: string) =>
queryKey: caKeys.listCasByTypeAndProjectId(type, projectId),
queryFn: async () => {
const { data } = await apiRequest.get<TUnifiedCertificateAuthority[]>(
`/api/v1/pki/ca/${type}?projectId=${projectId}`
`/api/v1/cert-manager/ca/${type}?projectId=${projectId}`
);
return data;
@@ -65,7 +61,7 @@ export const useListCasByProjectId = (projectId: string) => {
queryFn: async () => {
const { data } = await apiRequest.get<{
certificateAuthorities: TUnifiedCertificateAuthority[];
}>(`/api/v2/pki/ca?projectId=${projectId}`);
}>(`/api/v1/cert-manager/ca?projectId=${projectId}`);
return data.certificateAuthorities;
}
@@ -78,10 +74,10 @@ export const useListExternalCasByProjectId = (projectId: string) => {
queryFn: async () => {
const [acmeResponse, azureAdCsResponse] = await Promise.allSettled([
apiRequest.get<TUnifiedCertificateAuthority[]>(
`/api/v1/pki/ca/${CaType.ACME}?projectId=${projectId}`
`/api/v1/cert-manager/ca/${CaType.ACME}?projectId=${projectId}`
),
apiRequest.get<TUnifiedCertificateAuthority[]>(
`/api/v1/pki/ca/${CaType.AZURE_AD_CS}?projectId=${projectId}`
`/api/v1/cert-manager/ca/${CaType.AZURE_AD_CS}?projectId=${projectId}`
)
]);
@@ -100,14 +96,14 @@ export const useListExternalCasByProjectId = (projectId: string) => {
});
};
export const useGetCaById = (caId: string) => {
export const useGetInternalCaById = (caId: string) => {
return useQuery({
queryKey: caKeys.getCaById(caId),
queryFn: async () => {
const {
data: { ca }
} = await apiRequest.get<{ ca: TCertificateAuthority }>(`/api/v1/pki/ca/${caId}`);
return ca;
const { data } = await apiRequest.get<TInternalCertificateAuthority>(
`/api/v1/cert-manager/ca/internal/${caId}`
);
return data;
},
enabled: Boolean(caId)
});
@@ -124,7 +120,7 @@ export const useGetCaCerts = (caId: string) => {
serialNumber: string;
version: number;
}[]
>(`/api/v1/pki/ca/${caId}/ca-certificates`); // TODO: consider updating endpoint structure
>(`/api/v1/cert-manager/ca/internal/${caId}/ca-certificates`);
return data;
},
enabled: Boolean(caId)
@@ -139,7 +135,7 @@ export const useGetCaCert = (caId: string) => {
certificate: string;
certificateChain: string;
serialNumber: string;
}>(`/api/v1/pki/ca/${caId}/certificate`); // TODO: consider updating endpoint structure
}>(`/api/v1/cert-manager/ca/internal/${caId}/certificate`);
return data;
},
enabled: Boolean(caId)
@@ -154,7 +150,7 @@ export const useGetCaCsr = (caId: string) => {
data: { csr }
} = await apiRequest.get<{
csr: string;
}>(`/api/v1/pki/ca/${caId}/csr`);
}>(`/api/v1/cert-manager/ca/internal/${caId}/csr`);
return csr;
},
enabled: Boolean(caId)
@@ -170,13 +166,14 @@ export const useGetCaCrls = (caId: string) => {
id: string;
crl: string;
}[]
>(`/api/v1/pki/ca/${caId}/crls`);
>(`/api/v1/cert-manager/ca/internal/${caId}/crls`);
return data;
},
enabled: Boolean(caId)
});
};
// TODO: DEPRECATE
export const useGetCaCertTemplates = (caId: string) => {
return useQuery({
queryKey: caKeys.getCaCertTemplates(caId),
@@ -192,19 +189,21 @@ export const useGetCaCertTemplates = (caId: string) => {
export const useGetAzureAdcsTemplates = ({
caId,
projectId
projectId,
isAzureAdcsCa
}: {
caId: string;
projectId: string;
isAzureAdcsCa: boolean;
}) => {
return useQuery({
queryKey: caKeys.getAzureAdcsTemplates(caId, projectId),
queryFn: async () => {
const { data } = await apiRequest.get<{
templates: TAzureAdCsTemplate[];
}>(`/api/v1/pki/ca/azure-ad-cs/${caId}/templates?projectId=${projectId}`);
}>(`/api/v1/cert-manager/ca/azure-ad-cs/${caId}/templates?projectId=${projectId}`);
return data;
},
enabled: Boolean(caId && projectId)
enabled: Boolean(caId && projectId && isAzureAdcsCa)
});
};
+7 -5
View File
@@ -66,17 +66,19 @@ export type TUnifiedCertificateAuthority =
| TAzureAdCsCertificateAuthority
| TInternalCertificateAuthority;
export type TCreateCertificateAuthorityDTO = Omit<TUnifiedCertificateAuthority, "id">;
export type TCreateCertificateAuthorityDTO = Omit<
TUnifiedCertificateAuthority,
"id" | "enableDirectIssuance"
>;
export type TUpdateCertificateAuthorityDTO = Partial<TUnifiedCertificateAuthority> & {
caName: string;
projectId: string;
id: string;
type: CaType;
};
export type TDeleteCertificateAuthorityDTO = {
caName: string;
type: CaType;
id: string;
projectId: string;
type: CaType;
};
export type TCertificateAuthority = {
@@ -10,4 +10,4 @@ export {
useGetProfileCertificates,
useListCertificateProfiles
} from "./queries";
export type * from "./types";
export * from "./types";
@@ -17,7 +17,7 @@ export const useCreateCertificateProfile = () => {
mutationFn: async (data) => {
const { data: response } = await apiRequest.post<{
certificateProfile: TCertificateProfile;
}>("/api/v1/pki/certificate-profiles", data);
}>("/api/v1/cert-manager/certificate-profiles", data);
return response.certificateProfile;
},
onSuccess: (_, { projectId }) => {
@@ -35,7 +35,7 @@ export const useUpdateCertificateProfile = () => {
mutationFn: async ({ profileId, ...data }) => {
const { data: response } = await apiRequest.patch<{
certificateProfile: TCertificateProfile;
}>(`/api/v1/pki/certificate-profiles/${profileId}`, data);
}>(`/api/v1/cert-manager/certificate-profiles/${profileId}`, data);
return response.certificateProfile;
},
onSuccess: (profile, { profileId }) => {
@@ -56,7 +56,7 @@ export const useDeleteCertificateProfile = () => {
mutationFn: async ({ profileId }) => {
const { data: response } = await apiRequest.delete<{
certificateProfile: TCertificateProfile;
}>(`/api/v1/pki/certificate-profiles/${profileId}`);
}>(`/api/v1/cert-manager/certificate-profiles/${profileId}`);
return response.certificateProfile;
},
onSuccess: (profile, { profileId }) => {
@@ -71,7 +71,7 @@ export const useListCertificateProfiles = ({
const { data } = await apiRequest.get<{
certificateProfiles: TCertificateProfile[];
totalCount: number;
}>("/api/v1/pki/certificate-profiles", {
}>("/api/v1/cert-manager/certificate-profiles", {
params: {
projectId,
limit,
@@ -93,7 +93,7 @@ export const useGetCertificateProfileById = ({ profileId }: TGetCertificateProfi
queryFn: async () => {
const { data } = await apiRequest.get<{
certificateProfile: TCertificateProfileWithDetails;
}>(`/api/v1/pki/certificate-profiles/${profileId}`);
}>(`/api/v1/cert-manager/certificate-profiles/${profileId}`);
return data.certificateProfile;
},
enabled: Boolean(profileId)
@@ -109,7 +109,7 @@ export const useGetCertificateProfileBySlug = ({
queryFn: async () => {
const { data } = await apiRequest.get<{
certificateProfile: TCertificateProfile;
}>(`/api/v1/pki/certificate-profiles/slug/${slug}`, {
}>(`/api/v1/cert-manager/certificate-profiles/slug/${slug}`, {
params: { projectId }
});
return data.certificateProfile;
@@ -125,7 +125,7 @@ export const useRevealAcmeEabSecret = ({ profileId }: TRevealAcmeEabSecretDTO) =
const { data } = await apiRequest.get<{
eabKid: string;
eabSecret: string;
}>(`/api/v1/pki/certificate-profiles/${profileId}/acme/eab-secret/reveal`);
}>(`/api/v1/cert-manager/certificate-profiles/${profileId}/acme/eab-secret/reveal`);
return data;
},
enabled: Boolean(profileId)
@@ -144,7 +144,7 @@ export const useGetProfileCertificates = ({
queryFn: async () => {
const { data } = await apiRequest.get<{
certificates: TProfileCertificate[];
}>(`/api/v1/pki/certificate-profiles/${profileId}/certificates`, {
}>(`/api/v1/cert-manager/certificate-profiles/${profileId}/certificates`, {
params: {
offset,
limit,
@@ -1,23 +1,46 @@
export enum EnrollmentType {
API = "api",
EST = "est",
ACME = "acme"
}
export enum IssuerType {
CA = "ca",
SELF_SIGNED = "self-signed"
}
export type TCertificateProfile = {
id: string;
projectId: string;
caId: string;
caId: string | null;
certificateTemplateId: string;
slug: string;
description?: string;
enrollmentType: "api" | "est" | "acme";
enrollmentType: EnrollmentType;
issuerType: IssuerType;
estConfigId?: string;
apiConfigId?: string;
createdAt: string;
updatedAt: string;
externalConfigs?: Record<string, unknown> | null;
certificateAuthority?: {
id: string;
projectId?: string;
status: string;
name: string;
isExternal?: boolean;
externalType?: string | null;
};
};
export type TCertificateProfileWithDetails = TCertificateProfile & {
certificateAuthority?: {
id: string;
projectId: string;
projectId?: string;
status: string;
name: string;
isExternal?: boolean;
externalType?: string | null;
};
certificateTemplate?: {
id: string;
@@ -44,11 +67,12 @@ export type TCertificateProfileWithDetails = TCertificateProfile & {
export type TCreateCertificateProfileDTO = {
projectId: string;
caId: string;
caId?: string;
certificateTemplateId: string;
slug: string;
description?: string;
enrollmentType: "api" | "est" | "acme";
enrollmentType: EnrollmentType;
issuerType: IssuerType;
estConfig?: {
disableBootstrapCaValidation?: boolean;
passphrase: string;
@@ -59,12 +83,15 @@ export type TCreateCertificateProfileDTO = {
renewBeforeDays?: number;
};
acmeConfig?: unknown;
externalConfigs?: Record<string, unknown> | null;
};
export type TUpdateCertificateProfileDTO = {
profileId: string;
slug?: string;
description?: string;
enrollmentType?: EnrollmentType;
issuerType?: IssuerType;
estConfig?: {
disableBootstrapCaValidation?: boolean;
passphrase?: string;
@@ -75,6 +102,7 @@ export type TUpdateCertificateProfileDTO = {
renewBeforeDays?: number;
};
acmeConfig?: unknown;
externalConfigs?: Record<string, unknown> | null;
};
export type TDeleteCertificateProfileDTO = {
@@ -87,7 +115,9 @@ export type TListCertificateProfilesDTO = {
offset?: number;
search?: string;
includeConfigs?: boolean;
enrollmentType?: "api" | "est" | "acme";
enrollmentType?: EnrollmentType;
issuerType?: IssuerType;
caId?: string;
};
export type TGetCertificateProfileByIdDTO = {
@@ -21,6 +21,7 @@ import {
TUpdateEstConfigDTO
} from "./types";
// TODO: DEPRECATE
export const useCreateCertTemplate = () => {
const queryClient = useQueryClient();
return useMutation<TCertificateTemplate, object, TCreateCertificateTemplateDTO>({
@@ -40,6 +41,7 @@ export const useCreateCertTemplate = () => {
});
};
// TODO: DEPRECATE
export const useUpdateCertTemplate = () => {
const queryClient = useQueryClient();
return useMutation<TCertificateTemplate, object, TUpdateCertificateTemplateDTO>({
@@ -61,6 +63,7 @@ export const useUpdateCertTemplate = () => {
});
};
// TODO: DEPRECATE
export const useDeleteCertTemplate = () => {
const queryClient = useQueryClient();
return useMutation<TCertificateTemplate, object, TDeleteCertificateTemplateDTO>({
@@ -147,6 +150,7 @@ export const useDeleteCertTemplateV2 = () => {
});
};
// TODO: DEPRECATE
export const useCreateEstConfig = () => {
const queryClient = useQueryClient();
return useMutation<object, object, TCreateEstConfigDTO>({
@@ -165,6 +169,7 @@ export const useCreateEstConfig = () => {
});
};
// TODO: DEPRECATE
export const useUpdateEstConfig = () => {
const queryClient = useQueryClient();
return useMutation<object, object, TUpdateEstConfigDTO>({
@@ -193,7 +198,7 @@ export const useCreateCertificateTemplateV2WithPolicies = () => {
mutationFn: async (data) => {
const { data: response } = await apiRequest.post<{
certificateTemplate: TCertificateTemplateV2WithPolicies;
}>("/api/v2/certificate-templates", data);
}>("/api/v1/cert-manager/certificate-templates", data);
return response.certificateTemplate;
},
onSuccess: (_, { projectId }) => {
@@ -214,7 +219,7 @@ export const useUpdateCertificateTemplateV2WithPolicies = () => {
mutationFn: async ({ templateId, ...data }) => {
const { data: response } = await apiRequest.patch<{
certificateTemplate: TCertificateTemplateV2WithPolicies;
}>(`/api/v2/certificate-templates/${templateId}`, data);
}>(`/api/v1/cert-manager/certificate-templates/${templateId}`, data);
return response.certificateTemplate;
},
onSuccess: (template, { templateId }) => {
@@ -238,7 +243,7 @@ export const useDeleteCertificateTemplateV2WithPolicies = () => {
mutationFn: async ({ templateId }) => {
const { data: response } = await apiRequest.delete<{
certificateTemplate: TCertificateTemplateV2WithPolicies;
}>(`/api/v2/certificate-templates/${templateId}`);
}>(`/api/v1/cert-manager/certificate-templates/${templateId}`);
return response.certificateTemplate;
},
onSuccess: (template, { templateId }) => {
@@ -31,6 +31,7 @@ export const certTemplateKeys = {
getTemplateV2ById: (id: string) => ["cert-template-v2", id]
};
// TODO: DEPRECATE
export const useGetCertTemplate = (id: string) => {
return useQuery({
queryKey: certTemplateKeys.getCertTemplateById(id),
@@ -44,6 +45,7 @@ export const useGetCertTemplate = (id: string) => {
});
};
// TODO: DEPRECATE
export const useListCertificateTemplates = ({
limit = 100,
offset = 0,
@@ -67,6 +69,7 @@ export const useListCertificateTemplates = ({
});
};
// TODO: DEPRECATE
export const useGetEstConfig = (certificateTemplateId: string) => {
return useQuery({
queryKey: certTemplateKeys.getEstConfig(certificateTemplateId),
@@ -92,7 +95,7 @@ export const useListCertificateTemplatesV2 = ({
const { data } = await apiRequest.get<{
certificateTemplates: TCertificateTemplateV2WithPolicies[];
totalCount: number;
}>("/api/v2/certificate-templates", {
}>("/api/v1/cert-manager/certificate-templates", {
params: {
projectId,
limit,
@@ -113,7 +116,7 @@ export const useGetCertificateTemplateV2ById = ({
queryFn: async () => {
const { data } = await apiRequest.get<{
certificateTemplate: TCertificateTemplateV2WithPolicies;
}>(`/api/v2/certificate-templates/${templateId}`);
}>(`/api/v1/cert-manager/certificate-templates/${templateId}`);
return data.certificateTemplate;
},
enabled: Boolean(templateId)
@@ -13,17 +13,19 @@ import {
TRenewCertificateDTO,
TRenewCertificateResponse,
TRevokeCertDTO,
TUnifiedCertificateIssuanceDTO,
TUnifiedCertificateIssuanceResponse,
TUpdateRenewalConfigDTO
} from "./types";
export const useDeleteCert = () => {
const queryClient = useQueryClient();
return useMutation<TCertificate, object, TDeleteCertDTO>({
mutationFn: async ({ serialNumber }) => {
mutationFn: async ({ id }) => {
const {
data: { certificate }
} = await apiRequest.delete<{ certificate: TCertificate }>(
`/api/v1/pki/certificates/${serialNumber}`
`/api/v1/cert-manager/certificates/${id}`
);
return certificate;
},
@@ -47,11 +49,11 @@ export const useDeleteCert = () => {
export const useRevokeCert = () => {
const queryClient = useQueryClient();
return useMutation<TCertificate, object, TRevokeCertDTO>({
mutationFn: async ({ serialNumber, revocationReason }) => {
mutationFn: async ({ id, revocationReason }) => {
const {
data: { certificate }
} = await apiRequest.post<{ certificate: TCertificate }>(
`/api/v1/pki/certificates/${serialNumber}/revoke`,
`/api/v1/cert-manager/certificates/${id}/revoke`,
{
revocationReason
}
@@ -80,7 +82,7 @@ export const useImportCertificate = () => {
return useMutation<TImportCertificateResponse, object, TImportCertificateDTO>({
mutationFn: async (body) => {
const { data } = await apiRequest.post<TImportCertificateResponse>(
"/api/v1/pki/certificates/import-certificate",
"/api/v1/cert-manager/certificates/import-certificate",
body
);
return data;
@@ -98,7 +100,7 @@ export const useRenewCertificate = () => {
return useMutation<TRenewCertificateResponse, object, TRenewCertificateDTO>({
mutationFn: async ({ certificateId }) => {
const { data } = await apiRequest.post<TRenewCertificateResponse>(
`/api/v3/pki/certificates/${certificateId}/renew`,
`/api/v1/cert-manager/certificates/${certificateId}/renew`,
{}
);
return data;
@@ -131,7 +133,7 @@ export const useUpdateRenewalConfig = () => {
>({
mutationFn: async ({ certificateId, renewBeforeDays, enableAutoRenewal }) => {
const { data } = await apiRequest.patch<{ message: string; renewBeforeDays?: number }>(
`/api/v3/pki/certificates/${certificateId}/config`,
`/api/v1/cert-manager/certificates/${certificateId}/config`,
{ renewBeforeDays, enableAutoRenewal }
);
return data;
@@ -149,10 +151,10 @@ export const useUpdateRenewalConfig = () => {
export const useDownloadCertPkcs12 = () => {
return useMutation<void, object, TDownloadPkcs12DTO>({
mutationFn: async ({ serialNumber, projectSlug, password, alias }) => {
mutationFn: async ({ certificateId, projectSlug, password, alias }) => {
try {
const response = await apiRequest.post(
`/api/v1/pki/certificates/${serialNumber}/pkcs12`,
`/api/v1/cert-manager/certificates/${certificateId}/pkcs12`,
{
password,
alias
@@ -168,7 +170,7 @@ export const useDownloadCertPkcs12 = () => {
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = `certificate-${serialNumber}.p12`;
link.download = `certificate-${certificateId}.p12`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
@@ -185,3 +187,31 @@ export const useDownloadCertPkcs12 = () => {
}
});
};
export const useUnifiedCertificateIssuance = () => {
const queryClient = useQueryClient();
return useMutation<TUnifiedCertificateIssuanceResponse, object, TUnifiedCertificateIssuanceDTO>({
mutationFn: async (body) => {
const { projectSlug, ...requestData } = body;
const { data } = await apiRequest.post<TUnifiedCertificateIssuanceResponse>(
"/api/v1/cert-manager/certificates",
requestData
);
return data;
},
onSuccess: (_, { projectSlug }) => {
queryClient.invalidateQueries({
queryKey: ["certificate-profiles", "list"]
});
queryClient.invalidateQueries({
queryKey: pkiSubscriberKeys.allPkiSubscriberCertificates()
});
queryClient.invalidateQueries({
queryKey: projectKeys.allProjectCertificates()
});
queryClient.invalidateQueries({
queryKey: projectKeys.forProjectCertificates(projectSlug)
});
}
});
};
@@ -7,7 +7,11 @@ import { TCertificate } from "./types";
export const certKeys = {
getCertById: (serialNumber: string) => [{ serialNumber }, "cert"],
getCertBody: (serialNumber: string) => [{ serialNumber }, "certBody"],
getCertBundle: (serialNumber: string) => [{ serialNumber }, "certBundle"]
getCertBundle: (serialNumber: string) => [{ serialNumber }, "certBundle"],
getCertificateRequest: (requestId: string, projectSlug: string) => [
{ requestId, projectSlug },
"certificateRequest"
]
};
export const useGetCert = (serialNumber: string) => {
+60 -3
View File
@@ -24,13 +24,13 @@ export type TCertificate = {
};
export type TDeleteCertDTO = {
id: string;
projectId: string;
serialNumber: string;
};
export type TRevokeCertDTO = {
projectId: string;
serialNumber: string;
id: string;
revocationReason: string;
};
@@ -64,6 +64,7 @@ export type TRenewCertificateResponse = {
serialNumber: string;
certificateId: string;
projectId: string;
certificateRequestId?: string;
};
export type TUpdateRenewalConfigDTO = {
@@ -74,8 +75,64 @@ export type TUpdateRenewalConfigDTO = {
};
export type TDownloadPkcs12DTO = {
serialNumber: string;
certificateId: string;
projectSlug: string;
password: string;
alias: string;
};
export type TUnifiedCertificateIssuanceDTO = {
projectSlug: string;
profileId: string;
projectId: string;
csr?: string;
attributes?: {
commonName?: string;
keyUsages?: string[];
extendedKeyUsages?: string[];
altNames?: Array<{
type: string;
value: string;
}>;
signatureAlgorithm: string;
keyAlgorithm: string;
subjectAlternativeNames?: Array<{
type: string;
value: string;
}>;
ttl: string;
notBefore?: string;
notAfter?: string;
};
removeRootsFromChain?: boolean;
};
export type TUnifiedCertificateResponse = {
certificate: {
certificate: string;
issuingCaCertificate: string;
certificateChain: string;
privateKey?: string;
serialNumber: string;
certificateId: string;
};
certificateRequestId: string;
};
export type TCertificateRequestResponse = {
certificateRequestId: string;
status: "pending" | "issued" | "failed";
projectId: string;
};
export type TUnifiedCertificateIssuanceResponse =
| TUnifiedCertificateResponse
| TCertificateRequestResponse;
export type TCertificateRequestDetails = {
status: "pending" | "issued" | "failed";
certificate: TCertificate | null;
errorMessage: string | null;
createdAt: string;
updatedAt: string;
};
@@ -6,6 +6,7 @@ import { projectKeys } from "../projects";
import { pkiAlertKeys } from "./queries";
import { TCreatePkiAlertDTO, TDeletePkiAlertDTO, TPkiAlert, TUpdatePkiAlertDTO } from "./types";
// TODO: DEPRECATE
export const useCreatePkiAlert = () => {
const queryClient = useQueryClient();
return useMutation<TPkiAlert, object, TCreatePkiAlertDTO>({
@@ -19,6 +20,7 @@ export const useCreatePkiAlert = () => {
});
};
// TODO: DEPRECATE
export const useUpdatePkiAlert = () => {
const queryClient = useQueryClient();
return useMutation<TPkiAlert, object, TUpdatePkiAlertDTO>({
@@ -36,6 +38,7 @@ export const useUpdatePkiAlert = () => {
});
};
// TODO: DEPRECATE
export const useDeletePkiAlert = () => {
const queryClient = useQueryClient();
return useMutation<TPkiAlert, object, TDeletePkiAlertDTO>({
@@ -8,6 +8,7 @@ export const pkiAlertKeys = {
getPkiAlertById: (alertId: string) => [{ alertId }, "alert"]
};
// TODO: DEPRECATE
export const useGetPkiAlertById = (alertId: string) => {
return useQuery({
queryKey: pkiAlertKeys.getPkiAlertById(alertId),
@@ -11,7 +11,7 @@ export const useCreatePkiAlertV2 = () => {
return useMutation<TPkiAlertV2, unknown, TCreatePkiAlertV2>({
mutationFn: async (data) => {
const { data: response } = await apiRequest.post<{ alert: TPkiAlertV2 }>(
"/api/v2/pki/alerts",
"/api/v1/cert-manager/alerts",
data
);
return response.alert;
@@ -30,7 +30,7 @@ export const useUpdatePkiAlertV2 = () => {
return useMutation<TPkiAlertV2, unknown, TUpdatePkiAlertV2>({
mutationFn: async ({ alertId, ...data }) => {
const { data: response } = await apiRequest.patch<{ alert: TPkiAlertV2 }>(
`/api/v2/pki/alerts/${alertId}`,
`/api/v1/cert-manager/alerts/${alertId}`,
data
);
return response.alert;
@@ -52,7 +52,7 @@ export const useDeletePkiAlertV2 = () => {
return useMutation<TPkiAlertV2, unknown, TDeletePkiAlertV2>({
mutationFn: async ({ alertId }) => {
const { data } = await apiRequest.delete<{ alert: TPkiAlertV2 }>(
`/api/v2/pki/alerts/${alertId}`
`/api/v1/cert-manager/alerts/${alertId}`
);
return data.alert;
},
@@ -24,14 +24,16 @@ export const pkiAlertsV2Keys = {
};
const fetchPkiAlertsV2 = async (params: TGetPkiAlertsV2): Promise<TGetPkiAlertsV2Response> => {
const { data } = await apiRequest.get<TGetPkiAlertsV2Response>("/api/v2/pki/alerts", {
const { data } = await apiRequest.get<TGetPkiAlertsV2Response>("/api/v1/cert-manager/alerts", {
params
});
return data;
};
const fetchPkiAlertV2ById = async ({ alertId }: TGetPkiAlertV2ById): Promise<TPkiAlertV2> => {
const { data } = await apiRequest.get<{ alert: TPkiAlertV2 }>(`/api/v2/pki/alerts/${alertId}`);
const { data } = await apiRequest.get<{ alert: TPkiAlertV2 }>(
`/api/v1/cert-manager/alerts/${alertId}`
);
return data.alert;
};
@@ -40,7 +42,7 @@ const fetchPkiAlertV2MatchingCertificates = async (
): Promise<TGetPkiAlertV2MatchingCertificatesResponse> => {
const { alertId, ...queryParams } = params;
const { data } = await apiRequest.get<TGetPkiAlertV2MatchingCertificatesResponse>(
`/api/v2/pki/alerts/${alertId}/certificates`,
`/api/v1/cert-manager/alerts/${alertId}/certificates`,
{ params: queryParams }
);
return data;
@@ -50,7 +52,7 @@ const fetchPkiAlertV2CurrentMatchingCertificates = async (
params: TGetPkiAlertV2CurrentMatchingCertificates
): Promise<TGetPkiAlertV2CurrentMatchingCertificatesResponse> => {
const { data } = await apiRequest.post<TGetPkiAlertV2CurrentMatchingCertificatesResponse>(
"/api/v2/pki/alerts/preview/certificates",
"/api/v1/cert-manager/alerts/preview/certificates",
params
);
return data;
+30 -14
View File
@@ -17,7 +17,10 @@ export const useCreatePkiSync = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ destination, ...params }: TCreatePkiSyncDTO) => {
const { data } = await apiRequest.post<TPkiSync>(`/api/v1/pki/syncs/${destination}`, params);
const { data } = await apiRequest.post<TPkiSync>(
`/api/v1/cert-manager/syncs/${destination}`,
params
);
return data;
},
@@ -31,7 +34,7 @@ export const useUpdatePkiSync = () => {
return useMutation({
mutationFn: async ({ syncId, projectId, destination, ...params }: TUpdatePkiSyncDTO) => {
const { data } = await apiRequest.patch<TPkiSync>(
`/api/v1/pki/syncs/${destination}/${syncId}`,
`/api/v1/cert-manager/syncs/${destination}/${syncId}`,
params,
{ params: { projectId } }
);
@@ -49,9 +52,12 @@ export const useDeletePkiSync = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ syncId, projectId, destination }: TDeletePkiSyncDTO) => {
const { data } = await apiRequest.delete(`/api/v1/pki/syncs/${destination}/${syncId}`, {
params: { projectId }
});
const { data } = await apiRequest.delete(
`/api/v1/cert-manager/syncs/${destination}/${syncId}`,
{
params: { projectId }
}
);
return data;
},
@@ -66,7 +72,9 @@ export const useTriggerPkiSyncSyncCertificates = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ syncId, destination }: TTriggerPkiSyncSyncCertificatesDTO) => {
const { data } = await apiRequest.post(`/api/v1/pki/syncs/${destination}/${syncId}/sync`);
const { data } = await apiRequest.post(
`/api/v1/cert-manager/syncs/${destination}/${syncId}/sync`
);
return data;
},
@@ -111,7 +119,9 @@ export const useTriggerPkiSyncImportCertificates = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ syncId, destination }: TTriggerPkiSyncImportCertificatesDTO) => {
const { data } = await apiRequest.post(`/api/v1/pki/syncs/${destination}/${syncId}/import`);
const { data } = await apiRequest.post(
`/api/v1/cert-manager/syncs/${destination}/${syncId}/import`
);
return data;
},
@@ -157,7 +167,7 @@ export const useTriggerPkiSyncRemoveCertificates = () => {
return useMutation({
mutationFn: async ({ syncId, destination }: TTriggerPkiSyncRemoveCertificatesDTO) => {
const { data } = await apiRequest.post(
`/api/v1/pki/syncs/${destination}/${syncId}/remove-certificates`
`/api/v1/cert-manager/syncs/${destination}/${syncId}/remove-certificates`
);
return data;
@@ -209,9 +219,12 @@ export const useAddCertificatesToPkiSync = () => {
pkiSyncId: string;
certificateIds: string[];
}) => {
const { data } = await apiRequest.post(`/api/v1/pki/syncs/${pkiSyncId}/certificates`, {
certificateIds
});
const { data } = await apiRequest.post(
`/api/v1/cert-manager/syncs/${pkiSyncId}/certificates`,
{
certificateIds
}
);
return data;
},
@@ -231,9 +244,12 @@ export const useRemoveCertificatesFromPkiSync = () => {
pkiSyncId: string;
certificateIds: string[];
}) => {
const { data } = await apiRequest.delete(`/api/v1/pki/syncs/${pkiSyncId}/certificates`, {
data: { certificateIds }
});
const { data } = await apiRequest.delete(
`/api/v1/cert-manager/syncs/${pkiSyncId}/certificates`,
{
data: { certificateIds }
}
);
return data;
},
+6 -4
View File
@@ -37,7 +37,9 @@ export const usePkiSyncOptions = (
return useQuery({
queryKey: pkiSyncKeys.options(),
queryFn: async () => {
const { data } = await apiRequest.get<TListPkiSyncOptions>("/api/v1/pki/syncs/options");
const { data } = await apiRequest.get<TListPkiSyncOptions>(
"/api/v1/cert-manager/syncs/options"
);
return data.pkiSyncOptions;
},
@@ -58,7 +60,7 @@ export const fetchPkiSyncsByProjectId = async (projectId: string, certificateId?
params.certificateId = certificateId;
}
const { data } = await apiRequest.get<TListPkiSyncs>("/api/v1/pki/syncs", {
const { data } = await apiRequest.get<TListPkiSyncs>("/api/v1/cert-manager/syncs", {
params
});
@@ -110,7 +112,7 @@ export const useGetPkiSync = (
return useQuery({
queryKey: pkiSyncKeys.byId(syncId, projectId),
queryFn: async () => {
const { data } = await apiRequest.get<TPkiSync>(`/api/v1/pki/syncs/${syncId}`, {
const { data } = await apiRequest.get<TPkiSync>(`/api/v1/cert-manager/syncs/${syncId}`, {
params: { projectId }
});
@@ -138,7 +140,7 @@ export const useListPkiSyncCertificates = (
return useQuery({
queryKey: pkiSyncKeys.certificates(syncId, { offset, limit }),
queryFn: async () => {
const { data } = await apiRequest.get(`/api/v1/pki/syncs/${syncId}/certificates`, {
const { data } = await apiRequest.get(`/api/v1/cert-manager/syncs/${syncId}/certificates`, {
params: { offset, limit }
});
return {
+1 -1
View File
@@ -338,7 +338,7 @@ export const clearSession = (keepQueryClient?: boolean) => {
sessionStorage.removeItem(SessionStorageKeys.CLI_TERMINAL_TOKEN);
if (!keepQueryClient) {
qc.clear(); // Clear React Query cache
qc.invalidateQueries();
}
};
+10 -14
View File
@@ -1,9 +1,5 @@
@import "tailwindcss";
@import "@fontsource/inter/400.css" layer(base);
@import "@fontsource/inter/500.css" layer(base);
@import "@fontsource/inter/700.css" layer(base);
@source not "../public";
/*
@@ -29,11 +25,11 @@
}
:root {
font-family: var(--font-inter);
--foreground: oklch(0.985 0 0);
--background: oklch(0.145 0 0);
font-family: var(--font-inter);
--foreground: oklch(0.985 0 0);
--background: oklch(0.145 0 0);
--toastify-color-dark: var(--color-mineshaft-700);
--toastify-color-dark: var(--color-mineshaft-700);
}
@theme {
@@ -45,16 +41,16 @@
--color-background: #19191c;
--color-foreground: white;
--color-success: #2ecc71;
--color-info: #34c2db;
--color-info: #63b0bd;
--color-warning: #f1c40f;
--color-danger: #e74c3c;
--color-org: #30B3FF;
--color-org: #30b3ff;
--color-sub-org: #96ff59;
--color-project: #e0ed34;
--color-neutral: #adaeb0;
/*legacy color schema */
--color-org-v1: #30B3FF;
--color-org-v1: #30b3ff;
--color-namespace-v1: #96ff59;
/* Primary */
@@ -418,15 +414,15 @@
}
.Toastify__toast {
@apply rounded-md;
@apply rounded-md;
}
.Toastify__toast-body {
@apply items-start;
@apply items-start;
}
.Toastify__toast-icon {
@apply w-4 pt-1;
@apply w-4 pt-1;
}
.tags-conic-bg {
@@ -14,6 +14,7 @@ import { Link, useMatchRoute } from "@tanstack/react-router";
import { motion } from "framer-motion";
import { Tab, TabList, Tabs, Tooltip } from "@app/components/v2";
import { useOrganization } from "@app/context";
const generalTabs = [
{
@@ -60,6 +61,7 @@ const generalTabs = [
export const AdminNavBar = () => {
const matchRoute = useMatchRoute();
const { currentOrg } = useOrganization();
return (
<div className="border-b border-mineshaft-600 bg-mineshaft-900">
@@ -74,7 +76,7 @@ export const AdminNavBar = () => {
<Tabs value="selected">
<TabList className="border-b-0">
<Tooltip position="bottom" content="Back to organization">
<Link to="/organization/projects">
<Link to="/organizations/$orgId/projects" params={{ orgId: currentOrg.id }}>
<Tab value="back">
<FontAwesomeIcon icon={faArrowLeft} />
</Tab>
+14 -8
View File
@@ -2,19 +2,20 @@ import { Link, Outlet, useLocation } from "@tanstack/react-router";
import { motion } from "framer-motion";
import { Tab, TabList, Tabs } from "@app/components/v2";
import { useProject, useProjectPermission } from "@app/context";
import { useOrganization, useProject, useProjectPermission } from "@app/context";
import { AssumePrivilegeModeBanner } from "../ProjectLayout/components/AssumePrivilegeModeBanner";
export const KmsLayout = () => {
const { currentProject } = useProject();
const { currentOrg } = useOrganization();
const { assumedPrivilegeDetails } = useProjectPermission();
const location = useLocation();
return (
<div className="dark flex h-full w-full flex-col overflow-x-hidden">
<div className="border-b border-mineshaft-600 bg-mineshaft-900">
<div className="dark flex h-full w-full flex-col overflow-x-hidden bg-mineshaft-900">
<div className="border-y border-t-project/10 border-b-project/5 bg-gradient-to-b from-project/[0.075] to-project/[0.025] px-4 pt-0.5">
<motion.div
key="menu-project-items"
initial={{ x: -150 }}
@@ -27,24 +28,27 @@ export const KmsLayout = () => {
<Tabs value="selected">
<TabList className="border-b-0">
<Link
to="/projects/kms/$projectId/overview"
to="/organizations/$orgId/projects/kms/$projectId/overview"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Overview</Tab>}
</Link>
<Link
to="/projects/kms/$projectId/kmip"
to="/organizations/$orgId/projects/kms/$projectId/kmip"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>KMIP</Tab>}
</Link>
<Link
to="/projects/kms/$projectId/access-management"
to="/organizations/$orgId/projects/kms/$projectId/access-management"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -62,16 +66,18 @@ export const KmsLayout = () => {
)}
</Link>
<Link
to="/projects/kms/$projectId/audit-logs"
to="/organizations/$orgId/projects/kms/$projectId/audit-logs"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Audit Logs</Tab>}
</Link>
<Link
to="/projects/kms/$projectId/settings"
to="/organizations/$orgId/projects/kms/$projectId/settings"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -22,8 +22,8 @@ import {
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { Link, useLocation, useNavigate, useRouter, useRouterState } from "@tanstack/react-router";
import { UserPlusIcon } from "lucide-react";
import { Link, useLocation, useNavigate, useRouter } from "@tanstack/react-router";
import { ChevronRight, UserPlusIcon } from "lucide-react";
import { twMerge } from "tailwind-merge";
import { Mfa } from "@app/components/auth/Mfa";
@@ -31,7 +31,6 @@ import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions";
import SecurityClient from "@app/components/utilities/SecurityClient";
import {
BreadcrumbContainer,
Button,
DropdownMenu,
DropdownMenuContent,
@@ -43,7 +42,6 @@ import {
IconButton,
Modal,
ModalContent,
TBreadcrumbFormat,
Tooltip
} from "@app/components/v2";
import { Badge, InstanceIcon, OrgIcon, SubOrgIcon } from "@app/components/v3";
@@ -69,6 +67,7 @@ import { MfaMethod } from "@app/hooks/api/auth/types";
import { getAuthToken } from "@app/hooks/api/reactQuery";
import { Organization, SubscriptionPlan } from "@app/hooks/api/types";
import { AuthMethod } from "@app/hooks/api/users/types";
import { ProjectSelect } from "@app/layouts/ProjectLayout/components/ProjectSelect";
import { navigateUserToOrg } from "@app/pages/auth/LoginPage/Login.utils";
import { ServerAdminsPanel } from "../ServerAdminsPanel/ServerAdminsPanel";
@@ -183,9 +182,6 @@ export const Navbar = () => {
}
}, [subscription, isBillingPage, isModalIntrusive]);
const matches = useRouterState({ select: (s) => s.matches.at(-1)?.context });
const breadcrumbs = matches && "breadcrumbs" in matches ? matches.breadcrumbs : undefined;
const handleOrgChange = async (orgId: string) => {
queryClient.removeQueries({ queryKey: authKeys.getAuthToken });
queryClient.removeQueries({ queryKey: projectKeys.getAllUserProjects() });
@@ -249,7 +245,9 @@ export const Navbar = () => {
const isServerAdminPanel = location.pathname.startsWith("/admin");
const isOrgScope = location.pathname.startsWith("/organization"); // TODO: scott/akhil is this adequate?
const isProjectScope =
location.pathname.startsWith(`/organizations/${currentOrg.id}/projects`) &&
location.pathname !== `/organizations/${currentOrg.id}/projects`;
const handleOrgNav = async (org: Organization) => {
if (currentOrg?.id === org.id) return;
@@ -279,64 +277,59 @@ export const Navbar = () => {
};
return (
<div className="z-10 flex min-h-12 items-center bg-mineshaft-900 px-4 pt-1">
<div className="mr-auto flex items-center overflow-hidden">
<div className="shrink-0">
<Link to="/organization/projects">
<div className="z-10 flex min-h-12 items-center bg-mineshaft-900 px-4">
<div className="mr-auto flex h-full min-w-34 items-center">
<div className="mt-0.5 shrink-0">
<Link to="/organizations/$orgId/projects" params={{ orgId: currentOrg.id }}>
<img alt="infisical logo" src="/images/logotransparent.png" className="h-4" />
</Link>
</div>
<p className="pr-3 pl-1 text-lg text-mineshaft-400/70">/</p>
<ChevronRight size={18} className="mx-3 mt-[3px] text-mineshaft-400/70" />
{isServerAdminPanel ? (
<>
<Link
to="/admin"
className="group flex cursor-pointer items-center gap-2 text-sm text-white transition-all duration-100 hover:text-primary"
>
<InstanceIcon className="size-3.5 text-xs text-bunker-300" />
<div className="whitespace-nowrap">Server Console</div>
</Link>
<p className="pr-3 pl-3 text-lg text-mineshaft-400/70">/</p>
{breadcrumbs ? (
// scott: remove /admin as we show server console above
<BreadcrumbContainer breadcrumbs={breadcrumbs.slice(1) as TBreadcrumbFormat[]} />
) : null}
</>
<Link
to="/admin"
className="group flex cursor-pointer items-center gap-2 text-sm text-white transition-all duration-100 hover:text-primary"
>
<InstanceIcon className="size-3.5 text-xs text-bunker-300" />
<div className="whitespace-nowrap">Server Console</div>
</Link>
) : (
<>
<div className="flex min-w-12 items-center overflow-hidden">
<div
className={twMerge(
"relative flex min-w-16 items-center self-end rounded-t-md border-x border-t pt-1.5 pr-2 pb-2.5 pl-3",
!isProjectScope && !isSubOrganization
? "border-org/15 bg-gradient-to-b from-org/10 to-org/[0.075]"
: "border-transparent"
)}
>
{/* scott: the below is used to hide the top border from the org nav bar */}
{!isProjectScope && !isSubOrganization && (
<div className="absolute -bottom-px left-0 h-px w-full bg-mineshaft-900">
<div className="h-full bg-org/[0.075]" />
</div>
)}
<DropdownMenu modal={false} open={isOrgSelectOpen} onOpenChange={setIsOrgSelectOpen}>
<div className="group flex cursor-pointer items-center gap-2 overflow-hidden text-sm text-white transition-all duration-100 hover:text-primary">
<Badge
asChild
variant="org"
isTruncatable
// TODO(scott): either add badge size/style variant or create designated component for namespace/org nav bar
className={twMerge(
"gap-x-1.5 text-sm",
(!isOrgScope || isSubOrganization) &&
"bg-transparent text-mineshaft-200 hover:!bg-transparent hover:underline [&>svg]:!text-org"
)}
<div className="group mr-1 flex min-w-0 cursor-pointer items-center gap-2 overflow-hidden text-sm text-white transition-all duration-100">
<button
className="flex cursor-pointer items-center gap-x-2 truncate whitespace-nowrap"
type="button"
onClick={async () => {
navigate({
to: "/organizations/$orgId/projects",
params: { orgId: currentOrg.id }
});
if (isSubOrganization) {
await router.invalidate({ sync: true }).catch(() => null);
}
}}
>
<button
type="button"
onClick={async () => {
navigate({
to: "/organization/projects",
search: (search) => ({ ...search, subOrganization: undefined })
});
if (isSubOrganization) {
await router.invalidate({ sync: true }).catch(() => null);
}
}}
>
<OrgIcon className="size-[12px]" />
<span>{currentOrg?.name}</span>
</button>
</Badge>
<div className="mr-1 hidden rounded-sm border border-mineshaft-500 px-1 text-xs text-bunker-300 no-underline! md:inline-block">
{getPlan(subscription)}
</div>
<OrgIcon className={twMerge("size-[14px] shrink-0 text-org")} />
<span className="truncate">{currentOrg?.name}</span>
<Badge variant="org" className="hidden lg:inline-flex">
Organization
</Badge>
</button>
{subscription.cardDeclined && (
<Tooltip
content={`Your payment could not be processed${subscription.cardDeclinedReason ? `: ${subscription.cardDeclinedReason}` : ""}. Please update your payment method to continue enjoying premium features.`}
@@ -407,8 +400,8 @@ export const Navbar = () => {
<DropdownMenuItem
onClick={async () => {
navigate({
to: "/organization/projects",
search: (prev) => ({ ...prev, subOrganization: subOrg.name })
to: "/organizations/$orgId/projects",
params: { orgId: subOrg.id }
});
await router.invalidate({ sync: true }).catch(() => null);
}}
@@ -477,11 +470,11 @@ export const Navbar = () => {
// TODO(scott): either add badge size/style variant or create designated component for namespace/org nav bar
className={twMerge(
"gap-x-1.5 text-sm",
!isOrgScope &&
isProjectScope &&
"min-w-6 bg-transparent text-mineshaft-200 hover:!bg-transparent hover:underline [&>svg]:!text-sub-org"
)}
>
<Link to="/organization/projects">
<Link to="/organizations/$orgId/projects" params={{ orgId: currentOrg.id }}>
<SubOrgIcon className="size-[12px]" />
<span>{currentOrg.subOrganization.name}</span>
</Link>
@@ -511,8 +504,8 @@ export const Navbar = () => {
<DropdownMenuItem
onClick={async () => {
navigate({
to: "/organization/projects",
search: (prev) => ({ ...prev, subOrganization: subOrg.name })
to: "/organizations/$orgId/projects",
params: { orgId: subOrg.id }
});
await router.invalidate({ sync: true }).catch(() => null);
}}
@@ -541,21 +534,17 @@ export const Navbar = () => {
</DropdownMenu>
</>
)}
{!isOrgScope && (
{isProjectScope && (
<>
<p className="pr-3 pl-1 text-lg text-mineshaft-400/70">/</p>
{breadcrumbs ? (
<BreadcrumbContainer
className="min-w-[15rem] flex-1"
breadcrumbs={[breadcrumbs[0]] as TBreadcrumbFormat[]}
/>
) : null}
<ChevronRight size={18} className="mx-3 mt-[3px] text-mineshaft-400/70" />
<ProjectSelect />
</>
)}
</>
)}
</div>
{subscription && subscription.slug === "starter" && !subscription.has_used_trial && (
{subscription && subscription.slug === "starter" && !subscription.has_used_trial ? (
<Tooltip content="Start Free Pro Trial">
<Button
variant="plain"
@@ -576,6 +565,10 @@ export const Navbar = () => {
Free Pro Trial
</Button>
</Tooltip>
) : (
<div className="mt-0.5 mr-3 hidden rounded-sm border border-mineshaft-400 px-1 text-xs text-mineshaft-100 no-underline! opacity-50 md:inline-block">
{getPlan(subscription)}
</div>
)}
{/* eslint-disable-next-line no-nested-ternary */}
{!location.pathname.startsWith("/admin") ? (
@@ -593,14 +586,15 @@ export const Navbar = () => {
isAllowed ? (
<Link
className="mr-2 flex h-[34px] items-center rounded-md border border-mineshaft-500 px-2.5 py-1.5 text-sm whitespace-nowrap text-mineshaft-200 hover:bg-mineshaft-600"
to="/organization/access-management"
to="/organizations/$orgId/access-management"
params={{ orgId: currentOrg.id }}
search={{
selectedTab: "members",
action: "invite-members"
}}
>
<UserPlusIcon className="inline-block size-3.5" />
<span className="ml-2 hidden md:inline-block">Invite Members</span>
<span className="ml-2 hidden md:inline-block">Invite Users</span>
</Link>
) : null
}
@@ -696,14 +690,15 @@ export const Navbar = () => {
{(isAllowed) =>
isAllowed ? (
<Link
to="/organization/access-management"
to="/organizations/$orgId/access-management"
params={{ orgId: currentOrg.id }}
search={{
selectedTab: "members",
action: "invite-members"
}}
>
<DropdownMenuItem icon={<FontAwesomeIcon icon={faUserPlus} />}>
Invite Members
Invite Users
</DropdownMenuItem>
</Link>
) : null
@@ -778,7 +773,11 @@ export const Navbar = () => {
</div>
<div className="mt-4">
<div className="flex space-x-3">
<Link to="/organization/billing" className="inline-flex">
<Link
to="/organizations/$orgId/billing"
params={{ orgId: currentOrg.id }}
className="inline-flex"
>
<Button
colorSchema="primary"
variant="solid"
@@ -47,8 +47,8 @@ export const NewSubOrganizationForm = ({ onClose }: ContentProps) => {
onClose();
navigate({
to: "/organization/projects",
search: (prev) => ({ ...prev, subOrganization: organization.name })
to: "/organizations/$orgId/projects",
params: { orgId: organization.id }
});
await router.invalidate({ sync: true }).catch(() => null);
};
@@ -11,7 +11,7 @@ type Props = {
};
export const OrgNavBar = ({ isHidden }: Props) => {
const { isRootOrganization } = useOrganization();
const { isRootOrganization, currentOrg } = useOrganization();
const { popUp, handlePopUpToggle } = usePopUp(["createOrg"] as const);
const { pathname } = useLocation();
@@ -19,9 +19,9 @@ export const OrgNavBar = ({ isHidden }: Props) => {
const variant = isRootOrganization ? "org" : "namespace";
return (
<>
<div className="bg-mineshaft-900">
{!isHidden && (
<div className="dark flex w-full flex-col overflow-x-hidden border-b border-mineshaft-600 bg-mineshaft-900 px-4">
<div className="dark flex w-full flex-col overflow-x-hidden border-y border-t-org/15 border-b-org/5 bg-gradient-to-b from-org/[0.075] to-org/[0.025] px-4 pt-0.5">
<motion.div
key="menu-org-items"
initial={{ x: -150 }}
@@ -32,43 +32,47 @@ export const OrgNavBar = ({ isHidden }: Props) => {
<nav className="w-full">
<Tabs value="selected">
<TabList className="border-b-0">
<Link to="/organization/projects">
<Link to="/organizations/$orgId/projects" params={{ orgId: currentOrg.id }}>
{({ isActive }) => (
<Tab variant={variant} value={isActive ? "selected" : ""}>
Overview
</Tab>
)}
</Link>
<Link to="/organization/app-connections">
<Link
to="/organizations/$orgId/app-connections"
params={{ orgId: currentOrg.id }}
>
{({ isActive }) => (
<Tab variant={variant} value={isActive ? "selected" : ""}>
App Connections
</Tab>
)}
</Link>
<Link to="/organization/networking">
<Link to="/organizations/$orgId/networking" params={{ orgId: currentOrg.id }}>
{({ isActive }) => (
<Tab variant={variant} value={isActive ? "selected" : ""}>
Networking
</Tab>
)}
</Link>
<Link to="/organization/secret-sharing">
<Link to="/organizations/$orgId/secret-sharing" params={{ orgId: currentOrg.id }}>
{({ isActive }) => (
<Tab value={isActive ? "selected" : ""} variant={variant}>
Secret Sharing
</Tab>
)}
</Link>
<Link to="/organization/access-management">
<Link
to="/organizations/$orgId/access-management"
params={{ orgId: currentOrg.id }}
>
{({ isActive }) => (
<Tab
variant={variant}
value={
isActive ||
pathname.match(
/organization\/members|organization\/identities|organization\/groups|organization\/roles/
)
pathname.match(/organizations\/[^/]+\/(members|identities|groups|roles)/)
? "selected"
: ""
}
@@ -77,7 +81,7 @@ export const OrgNavBar = ({ isHidden }: Props) => {
</Tab>
)}
</Link>
<Link to="/organization/audit-logs">
<Link to="/organizations/$orgId/audit-logs" params={{ orgId: currentOrg.id }}>
{({ isActive }) => (
<Tab variant={variant} value={isActive ? "selected" : ""}>
Audit Logs
@@ -85,7 +89,7 @@ export const OrgNavBar = ({ isHidden }: Props) => {
)}
</Link>
{isRootOrganization && (
<Link to="/organization/billing">
<Link to="/organizations/$orgId/billing" params={{ orgId: currentOrg.id }}>
{({ isActive }) => (
<Tab variant={variant} value={isActive ? "selected" : ""}>
Usage & Billing
@@ -93,7 +97,7 @@ export const OrgNavBar = ({ isHidden }: Props) => {
)}
</Link>
)}
<Link to="/organization/settings">
<Link to="/organizations/$orgId/settings" params={{ orgId: currentOrg.id }}>
{({ isActive }) => (
<Tab variant={variant} value={isActive ? "selected" : ""}>
Settings
@@ -110,6 +114,6 @@ export const OrgNavBar = ({ isHidden }: Props) => {
isOpen={popUp?.createOrg?.isOpen}
onClose={() => handlePopUpToggle("createOrg", false)}
/>
</>
</div>
);
};
+16 -9
View File
@@ -4,13 +4,14 @@ import { motion } from "framer-motion";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { Tab, TabList, Tabs } from "@app/components/v2";
import { useProject, useProjectPermission, useSubscription } from "@app/context";
import { useOrganization, useProject, useProjectPermission, useSubscription } from "@app/context";
import { usePopUp } from "@app/hooks";
import { AssumePrivilegeModeBanner } from "../ProjectLayout/components/AssumePrivilegeModeBanner";
export const PamLayout = () => {
const { currentProject } = useProject();
const { currentOrg } = useOrganization();
const { subscription } = useSubscription();
const { assumedPrivilegeDetails } = useProjectPermission();
const location = useLocation();
@@ -28,8 +29,8 @@ export const PamLayout = () => {
return (
<>
<div className="dark flex h-full w-full flex-col overflow-x-hidden">
<div className="border-b border-mineshaft-600 bg-mineshaft-900">
<div className="dark flex h-full w-full flex-col overflow-x-hidden bg-mineshaft-900">
<div className="border-y border-t-project/10 border-b-project/5 bg-gradient-to-b from-project/[0.075] to-project/[0.025] px-4 pt-0.5">
<motion.div
key="menu-project-items"
initial={{ x: -150 }}
@@ -42,32 +43,36 @@ export const PamLayout = () => {
<Tabs value="selected">
<TabList className="border-b-0">
<Link
to="/projects/pam/$projectId/accounts"
to="/organizations/$orgId/projects/pam/$projectId/accounts"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Accounts</Tab>}
</Link>
<Link
to="/projects/pam/$projectId/resources"
to="/organizations/$orgId/projects/pam/$projectId/resources"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Resources</Tab>}
</Link>
<Link
to="/projects/pam/$projectId/sessions"
to="/organizations/$orgId/projects/pam/$projectId/sessions"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Sessions</Tab>}
</Link>
<Link
to="/projects/pam/$projectId/access-management"
to="/organizations/$orgId/projects/pam/$projectId/access-management"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -85,16 +90,18 @@ export const PamLayout = () => {
)}
</Link>
<Link
to="/projects/pam/$projectId/audit-logs"
to="/organizations/$orgId/projects/pam/$projectId/audit-logs"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Audit Logs</Tab>}
</Link>
<Link
to="/projects/pam/$projectId/settings"
to="/organizations/$orgId/projects/pam/$projectId/settings"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -2,7 +2,7 @@ import { Link, Outlet, useLocation } from "@tanstack/react-router";
import { motion } from "framer-motion";
import { Tab, TabList, Tabs } from "@app/components/v2";
import { useProject, useProjectPermission, useSubscription } from "@app/context";
import { useOrganization, useProject, useProjectPermission, useSubscription } from "@app/context";
import {
useListWorkspaceCertificateTemplates,
useListWorkspacePkiSubscribers
@@ -11,6 +11,7 @@ import {
import { AssumePrivilegeModeBanner } from "../ProjectLayout/components/AssumePrivilegeModeBanner";
export const PkiManagerLayout = () => {
const { currentOrg } = useOrganization();
const { currentProject } = useProject();
const { assumedPrivilegeDetails } = useProjectPermission();
const { subscription } = useSubscription();
@@ -28,8 +29,8 @@ export const PkiManagerLayout = () => {
const location = useLocation();
return (
<div className="dark flex h-full w-full flex-col overflow-x-hidden">
<div className="border-b border-mineshaft-600 bg-mineshaft-900">
<div className="dark flex h-full w-full flex-col overflow-x-hidden bg-mineshaft-900">
<div className="border-y border-t-project/10 border-b-project/5 bg-gradient-to-b from-project/[0.075] to-project/[0.025] px-4 pt-0.5">
<motion.div
key="menu-project-items"
initial={{ x: -150 }}
@@ -42,16 +43,18 @@ export const PkiManagerLayout = () => {
<Tabs value="selected">
<TabList className="border-b-0">
<Link
to="/projects/cert-management/$projectId/policies"
to="/organizations/$orgId/projects/cert-management/$projectId/policies"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Certificates</Tab>}
</Link>
<Link
to="/projects/cert-management/$projectId/certificate-authorities"
to="/organizations/$orgId/projects/cert-management/$projectId/certificate-authorities"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -62,24 +65,27 @@ export const PkiManagerLayout = () => {
)}
</Link>
<Link
to="/projects/cert-management/$projectId/alerting"
to="/organizations/$orgId/projects/cert-management/$projectId/alerting"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Alerting</Tab>}
</Link>
<Link
to="/projects/cert-management/$projectId/integrations"
to="/organizations/$orgId/projects/cert-management/$projectId/integrations"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Integrations</Tab>}
</Link>
<Link
to="/projects/cert-management/$projectId/app-connections"
to="/organizations/$orgId/projects/cert-management/$projectId/app-connections"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -89,8 +95,9 @@ export const PkiManagerLayout = () => {
<>
{(subscription.pkiLegacyTemplates || hasExistingSubscribers) && (
<Link
to="/projects/cert-management/$projectId/subscribers"
to="/organizations/$orgId/projects/cert-management/$projectId/subscribers"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -101,8 +108,9 @@ export const PkiManagerLayout = () => {
)}
{(subscription.pkiLegacyTemplates || hasExistingTemplates) && (
<Link
to="/projects/cert-management/$projectId/certificate-templates"
to="/organizations/$orgId/projects/cert-management/$projectId/certificate-templates"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -116,8 +124,9 @@ export const PkiManagerLayout = () => {
</>
)}
<Link
to="/projects/cert-management/$projectId/access-management"
to="/organizations/$orgId/projects/cert-management/$projectId/access-management"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -135,16 +144,18 @@ export const PkiManagerLayout = () => {
)}
</Link>
<Link
to="/projects/cert-management/$projectId/audit-logs"
to="/organizations/$orgId/projects/cert-management/$projectId/audit-logs"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Audit Logs</Tab>}
</Link>
<Link
to="/projects/cert-management/$projectId/settings"
to="/organizations/$orgId/projects/cert-management/$projectId/settings"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -38,7 +38,9 @@ export const AssumePrivilegeModeBanner = () => {
{
onSuccess: () => {
const url = `${getProjectHomePage(currentProject.type, currentProject.environments)}${isSubOrganization ? `?subOrganization=${currentOrg.slug}` : ""}`;
window.location.href = url.replace("$projectId", currentProject.id);
window.location.assign(
url.replace("$orgId", currentOrg.id).replace("$projectId", currentProject.id)
);
}
}
);
@@ -8,7 +8,7 @@ import {
faStar as faSolidStar
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link, linkOptions } from "@tanstack/react-router";
import { Link, linkOptions, useParams } from "@tanstack/react-router";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { OrgPermissionCan } from "@app/components/permissions";
@@ -46,7 +46,7 @@ const PROJECT_TYPE_NAME: Record<ProjectType, string> = {
[ProjectType.SecretScanning]: "Secret Scanning"
};
export const ProjectSelect = () => {
const ProjectSelectInner = () => {
const [searchProject, setSearchProject] = useState("");
const { currentProject: currentWorkspace } = useProject();
const { currentOrg } = useOrganization();
@@ -92,23 +92,24 @@ export const ProjectSelect = () => {
}, [projects, projectFavorites, currentWorkspace]);
return (
<div className="mr-2 flex items-center gap-1 overflow-hidden">
<div className="relative mr-2 flex min-w-16 items-center gap-1 self-end rounded-t-md border-x border-t border-project/10 bg-gradient-to-b from-project/10 to-project/[0.075] pt-1.5 pr-1 pb-2.5 pl-3">
{/* scott: the below is used to hide the top border from the org nav bar */}
<div className="absolute -bottom-px left-0 h-px w-full bg-mineshaft-900">
<div className="h-full bg-project/[0.075]" />
</div>
<DropdownMenu modal={false}>
<Link
to={getProjectHomePage(currentWorkspace.type, currentWorkspace.environments)}
params={{
projectId: currentWorkspace.id
projectId: currentWorkspace.id,
orgId: currentWorkspace.orgId
}}
className="group flex cursor-pointer items-center gap-x-1.5 overflow-hidden hover:text-white"
className="group flex cursor-pointer items-center gap-x-2 overflow-hidden pt-0.5 text-sm text-white"
>
<p className="inline-block truncate text-mineshaft-200 group-hover:underline">
{currentWorkspace?.name}
</p>
<Badge variant="project">
<ProjectIcon />
<span className="hidden sm:inline-block">
{currentWorkspace.type ? PROJECT_TYPE_NAME[currentWorkspace.type] : "Project"}
</span>
<ProjectIcon className="size-[14px] shrink-0 text-project" />
<span className="truncate">{currentWorkspace?.name}</span>
<Badge variant="project" className="hidden lg:inline-flex">
{currentWorkspace.type ? PROJECT_TYPE_NAME[currentWorkspace.type] : "Project"}
</Badge>
</Link>
<DropdownMenuTrigger asChild>
@@ -117,7 +118,7 @@ export const ProjectSelect = () => {
variant="plain"
colorSchema="secondary"
ariaLabel="switch-project"
className="px-2 py-1"
className="top-px px-2 py-1"
>
<FontAwesomeIcon icon={faCaretDown} className="text-xs text-bunker-300" />
</IconButton>
@@ -154,14 +155,15 @@ export const ProjectSelect = () => {
const url = linkOptions({
to: getProjectHomePage(workspace.type, workspace.environments),
params: {
projectId: workspace.id
projectId: workspace.id,
orgId: workspace.orgId
},
search: {
subOrganization: currentOrg?.subOrganization?.name
}
});
const urlInstance = new URL(
`${window.location.origin}/${url.to.replaceAll("$projectId", workspace.id)}`
`${window.location.origin}${url.to.replaceAll("$orgId", url.params.orgId).replaceAll("$projectId", url.params.projectId)}`
);
if (currentOrg?.subOrganization) {
urlInstance.searchParams.set(
@@ -236,3 +238,14 @@ export const ProjectSelect = () => {
</div>
);
};
export const ProjectSelect = () => {
const params = useParams({ strict: false });
// Return null during navigation when projectId is not available
if (!params.projectId) {
return null;
}
return <ProjectSelectInner />;
};
@@ -3,7 +3,7 @@ import { motion } from "framer-motion";
import { Tab, TabList, Tabs } from "@app/components/v2";
import { Badge } from "@app/components/v3";
import { useProject, useProjectPermission } from "@app/context";
import { useOrganization, useProject, useProjectPermission } from "@app/context";
import {
useGetAccessRequestsCount,
useGetSecretApprovalRequestCount,
@@ -15,6 +15,7 @@ import { AssumePrivilegeModeBanner } from "../ProjectLayout/components/AssumePri
export const SecretManagerLayout = () => {
const { currentProject, projectId } = useProject();
const { assumedPrivilegeDetails } = useProjectPermission();
const { currentOrg } = useOrganization();
const location = useLocation();
const projectSlug = currentProject?.slug || "";
@@ -38,8 +39,8 @@ export const SecretManagerLayout = () => {
(secretApprovalReqCount?.open || 0) + (accessApprovalRequestCount?.pendingCount || 0);
return (
<div className="dark flex h-full w-full flex-col overflow-x-hidden">
<div className="border-b border-mineshaft-600 bg-mineshaft-900">
<div className="dark flex h-full w-full flex-col overflow-x-hidden bg-mineshaft-900">
<div className="border-y border-t-project/10 border-b-project/5 bg-gradient-to-b from-project/[0.075] to-project/[0.025] px-4 pt-0.5">
<motion.div
key="menu-project-items"
initial={{ x: -150 }}
@@ -52,8 +53,9 @@ export const SecretManagerLayout = () => {
<Tabs value="selected">
<TabList className="border-b-0">
<Link
to="/projects/secret-management/$projectId/overview"
to="/organizations/$orgId/projects/secret-management/$projectId/overview"
params={{
orgId: currentOrg.id,
projectId: currentProject.id,
...(currentProject.environments.length
? { envSlug: currentProject.environments[0]?.slug }
@@ -73,8 +75,9 @@ export const SecretManagerLayout = () => {
)}
</Link>
<Link
to="/projects/secret-management/$projectId/approval"
to="/organizations/$orgId/projects/secret-management/$projectId/approval"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -92,8 +95,9 @@ export const SecretManagerLayout = () => {
)}
</Link>
<Link
to="/projects/secret-management/$projectId/integrations"
to="/organizations/$orgId/projects/secret-management/$projectId/integrations"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -101,8 +105,9 @@ export const SecretManagerLayout = () => {
</Link>
{Boolean(secretRotations?.length) && (
<Link
to="/projects/secret-management/$projectId/secret-rotation"
to="/organizations/$orgId/projects/secret-management/$projectId/secret-rotation"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -112,16 +117,18 @@ export const SecretManagerLayout = () => {
</Link>
)}
<Link
to="/projects/secret-management/$projectId/app-connections"
to="/organizations/$orgId/projects/secret-management/$projectId/app-connections"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>App Connections</Tab>}
</Link>
<Link
to="/projects/secret-management/$projectId/access-management"
to="/organizations/$orgId/projects/secret-management/$projectId/access-management"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -139,16 +146,18 @@ export const SecretManagerLayout = () => {
)}
</Link>
<Link
to="/projects/secret-management/$projectId/audit-logs"
to="/organizations/$orgId/projects/secret-management/$projectId/audit-logs"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Audit Logs</Tab>}
</Link>
<Link
to="/projects/secret-management/$projectId/settings"
to="/organizations/$orgId/projects/secret-management/$projectId/settings"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -5,6 +5,7 @@ import { Tab, TabList, Tabs } from "@app/components/v2";
import { Badge } from "@app/components/v3";
import {
ProjectPermissionSub,
useOrganization,
useProject,
useProjectPermission,
useSubscription
@@ -15,6 +16,7 @@ import { useGetSecretScanningUnresolvedFindingCount } from "@app/hooks/api/secre
import { AssumePrivilegeModeBanner } from "../ProjectLayout/components/AssumePrivilegeModeBanner";
export const SecretScanningLayout = () => {
const { currentOrg } = useOrganization();
const { currentProject } = useProject();
const { assumedPrivilegeDetails } = useProjectPermission();
@@ -36,8 +38,8 @@ export const SecretScanningLayout = () => {
);
return (
<div className="dark flex h-full w-full flex-col overflow-x-hidden">
<div className="border-b border-mineshaft-600 bg-mineshaft-900">
<div className="dark flex h-full w-full flex-col overflow-x-hidden bg-mineshaft-900">
<div className="border-y border-t-project/10 border-b-project/5 bg-gradient-to-b from-project/[0.075] to-project/[0.025] px-4 pt-0.5">
<motion.div
key="menu-project-items"
initial={{ x: -150 }}
@@ -50,16 +52,18 @@ export const SecretScanningLayout = () => {
<Tabs value="selected">
<TabList className="border-b-0">
<Link
to="/projects/secret-scanning/$projectId/data-sources"
to="/organizations/$orgId/projects/secret-scanning/$projectId/data-sources"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Data Sources</Tab>}
</Link>
<Link
to="/projects/secret-scanning/$projectId/findings"
to="/organizations/$orgId/projects/secret-scanning/$projectId/findings"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -75,16 +79,18 @@ export const SecretScanningLayout = () => {
)}
</Link>
<Link
to="/projects/secret-scanning/$projectId/app-connections"
to="/organizations/$orgId/projects/secret-scanning/$projectId/app-connections"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>App Connections</Tab>}
</Link>
<Link
to="/projects/secret-scanning/$projectId/access-management"
to="/organizations/$orgId/projects/secret-scanning/$projectId/access-management"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -102,16 +108,18 @@ export const SecretScanningLayout = () => {
)}
</Link>
<Link
to="/projects/secret-scanning/$projectId/audit-logs"
to="/organizations/$orgId/projects/secret-scanning/$projectId/audit-logs"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Audit Logs</Tab>}
</Link>
<Link
to="/projects/secret-scanning/$projectId/settings"
to="/organizations/$orgId/projects/secret-scanning/$projectId/settings"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
+14 -7
View File
@@ -6,6 +6,7 @@ import { Tab, TabList, Tabs } from "@app/components/v2";
import {
ProjectPermissionActions,
ProjectPermissionSub,
useOrganization,
useProject,
useProjectPermission
} from "@app/context";
@@ -13,13 +14,14 @@ import {
import { AssumePrivilegeModeBanner } from "../ProjectLayout/components/AssumePrivilegeModeBanner";
export const SshLayout = () => {
const { currentOrg } = useOrganization();
const { currentProject } = useProject();
const { assumedPrivilegeDetails } = useProjectPermission();
const location = useLocation();
return (
<div className="dark flex h-full w-full flex-col overflow-x-hidden">
<div className="border-b border-mineshaft-600 bg-mineshaft-900">
<div className="dark flex h-full w-full flex-col overflow-x-hidden bg-mineshaft-900">
<div className="border-y border-t-project/10 border-b-project/5 bg-gradient-to-b from-project/[0.075] to-project/[0.025] px-4 pt-0.5">
<motion.div
key="menu-project-items"
initial={{ x: -150 }}
@@ -32,8 +34,9 @@ export const SshLayout = () => {
<Tabs value="selected">
<TabList className="border-b-0">
<Link
to="/projects/ssh/$projectId/overview"
to="/organizations/$orgId/projects/ssh/$projectId/overview"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -54,8 +57,9 @@ export const SshLayout = () => {
{(isAllowed) =>
isAllowed && (
<Link
to="/projects/ssh/$projectId/cas"
to="/organizations/$orgId/projects/ssh/$projectId/cas"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -71,8 +75,9 @@ export const SshLayout = () => {
}
</ProjectPermissionCan>
<Link
to="/projects/ssh/$projectId/access-management"
to="/organizations/$orgId/projects/ssh/$projectId/access-management"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
@@ -90,16 +95,18 @@ export const SshLayout = () => {
)}
</Link>
<Link
to="/projects/ssh/$projectId/audit-logs"
to="/organizations/$orgId/projects/ssh/$projectId/audit-logs"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Audit Logs</Tab>}
</Link>
<Link
to="/projects/ssh/$projectId/settings"
to="/organizations/$orgId/projects/ssh/$projectId/settings"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
+2
View File
@@ -12,6 +12,8 @@ import { NotFoundPage } from "./pages/public/NotFoundPage/NotFoundPage";
// Import the generated route tree
import { routeTree } from "./routeTree.gen";
import "@fontsource/inter/400.css";
import "@fontsource/inter/500.css";
import "@xyflow/react/dist/style.css";
import "nprogress/nprogress.css";
import "react-toastify/dist/ReactToastify.css";
@@ -17,7 +17,7 @@ export const AccessManagementPage = () => {
<div className="mx-auto mb-6 w-full max-w-8xl">
<PageHeader
scope="instance"
title="Access Control"
title="Server Admin Access Control"
description="Manage server admins within your Infisical instance."
/>
<ServerAdminsTable />
@@ -29,7 +29,7 @@ export const ResourceOverviewPage = () => {
Users
</Tab>
<Tab variant="instance" value="tab-identities">
Identities
Machine Identities
</Tab>
</TabList>
<TabPanel value="tab-organizations">
@@ -90,7 +90,7 @@ const IdentityPanelTable = ({
value={searchIdentityFilter}
onChange={(e) => setSearchIdentityFilter(e.target.value)}
leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
placeholder="Search identities by name..."
placeholder="Search machine identities by name..."
className="flex-1"
/>
</div>
@@ -350,8 +350,8 @@ const ViewMembersModalContent = ({
className="my-auto bg-mineshaft-700"
title={
members.length
? "No organization members match search..."
: "No organization members found"
? "No organization users match search..."
: "No organization users found"
}
icon={faUsers}
/>
+2 -1
View File
@@ -7,7 +7,8 @@ export const Route = createFileRoute("/_authenticate/_inject-org-details/admin/_
beforeLoad: ({ context }) => {
if (!context.user.superAdmin) {
throw redirect({
to: "/organization/projects"
to: "/organizations/$orgId/projects",
params: { orgId: context.organizationId }
});
}
}
@@ -12,7 +12,10 @@ export const navigateUserToOrg = async (navigate: NavigateFn, organizationId?: s
if (organizationId) {
localStorage.setItem("orgData.id", organizationId);
navigate({ to: "/organization/projects" });
navigate({
to: "/organizations/$orgId/projects",
params: { orgId: organizationId }
});
return;
}
@@ -20,11 +23,14 @@ export const navigateUserToOrg = async (navigate: NavigateFn, organizationId?: s
// user is part of at least 1 non-auth enforced org
const userOrg = nonAuthEnforcedOrgs[0] && nonAuthEnforcedOrgs[0].id;
localStorage.setItem("orgData.id", userOrg);
navigate({ to: "/organization/projects" });
navigate({
to: "/organizations/$orgId/projects",
params: { orgId: userOrg }
});
} else {
// user is not part of any non-auth enforced orgs
localStorage.removeItem("orgData.id");
navigate({ to: "/organization/none" });
navigate({ to: "/organizations/none" });
}
};
@@ -222,7 +222,7 @@ export const SelectOrganizationSection = () => {
// Case: User has no organizations.
// This can happen if the user was previously a member, but the organization was deleted or the user was removed.
if (organizations.data.length === 0) {
navigate({ to: "/organization/none" });
navigate({ to: "/organizations/none" });
} else if (organizations.data.length === 1) {
if (callbackPort) {
handleCliRedirect();
@@ -122,7 +122,8 @@ export const SignupInvitePage = () => {
localStorage.setItem("orgData.id", orgId);
navigate({
to: "/organization/projects"
to: "/organizations/$orgId/projects",
params: { orgId }
});
};
@@ -72,7 +72,7 @@ export const SignUpPage = () => {
if (!serverDetails?.emailConfigured && step === 4) {
navigate({
to: "/organization/projects"
to: "/"
});
}
})();
@@ -129,7 +129,8 @@ export const UserInfoSSOStep = ({
localStorage.setItem("orgData.id", orgId);
navigate({
to: "/organization/projects"
to: "/organizations/$orgId/projects",
params: { orgId }
});
} catch (error) {
setIsLoading(false);
@@ -6,7 +6,7 @@ import { z } from "zod";
import { createNotification } from "@app/components/notifications";
import { Button, FormControl, Input, Modal, ModalContent } from "@app/components/v2";
import { useProject } from "@app/context";
import { useOrganization, useProject } from "@app/context";
import {
useCreatePkiCollection,
useGetPkiCollectionById,
@@ -28,6 +28,7 @@ type Props = {
export const PkiCollectionModal = ({ popUp, handlePopUpToggle }: Props) => {
const navigate = useNavigate();
const { currentOrg } = useOrganization();
const { currentProject } = useProject();
const projectId = currentProject?.id || "";
@@ -81,8 +82,9 @@ export const PkiCollectionModal = ({ popUp, handlePopUpToggle }: Props) => {
});
navigate({
to: "/projects/cert-management/$projectId/pki-collections/$collectionId",
to: "/organizations/$orgId/projects/cert-management/$projectId/pki-collections/$collectionId",
params: {
orgId: currentOrg.id,
projectId,
collectionId
}
@@ -19,7 +19,12 @@ import {
THead,
Tr
} from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useProject } from "@app/context";
import {
ProjectPermissionActions,
ProjectPermissionSub,
useOrganization,
useProject
} from "@app/context";
import { useListWorkspacePkiCollections } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -31,6 +36,7 @@ type Props = {
};
export const PkiCollectionTable = ({ handlePopUpOpen }: Props) => {
const { currentOrg } = useOrganization();
const navigate = useNavigate();
const { currentProject } = useProject();
const projectId = currentProject?.id || "";
@@ -60,8 +66,9 @@ export const PkiCollectionTable = ({ handlePopUpOpen }: Props) => {
key={`pki-collection-${pkiCollection.id}`}
onClick={() =>
navigate({
to: "/projects/cert-management/$projectId/pki-collections/$collectionId",
to: "/organizations/$orgId/projects/cert-management/$projectId/pki-collections/$collectionId",
params: {
orgId: currentOrg.id,
projectId,
collectionId: pkiCollection.id
}
@@ -3,7 +3,7 @@ import { createFileRoute } from "@tanstack/react-router";
import { AlertingPage } from "./AlertingPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/alerting"
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/alerting"
)({
component: AlertingPage,
beforeLoad: ({ context }) => {
@@ -17,7 +17,12 @@ import {
Tooltip
} from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import { ProjectPermissionActions, ProjectPermissionSub, useProject } from "@app/context";
import {
ProjectPermissionActions,
ProjectPermissionSub,
useOrganization,
useProject
} from "@app/context";
import { CaType, useDeleteCa, useGetCa } from "@app/hooks/api";
import { TInternalCertificateAuthority } from "@app/hooks/api/ca/types";
import { ProjectType } from "@app/hooks/api/projects/types";
@@ -34,14 +39,14 @@ import {
const Page = () => {
const { currentProject } = useProject();
const { currentOrg } = useOrganization();
const navigate = useNavigate();
const params = useParams({
from: ROUTE_PATHS.CertManager.CertAuthDetailsByIDPage.id
});
const { caName } = params as { caName: string };
const { caId } = params as { caId: string };
const { data } = useGetCa({
caName,
projectId: currentProject?.id || "",
caId,
type: CaType.INTERNAL
}) as { data: TInternalCertificateAuthority };
@@ -60,7 +65,7 @@ const Page = () => {
if (!currentProject?.slug) return;
await deleteCa({
caName,
id: data.id,
projectId: currentProject.id,
type: CaType.INTERNAL
});
@@ -72,8 +77,9 @@ const Page = () => {
handlePopUpClose("deleteCa");
navigate({
to: "/projects/cert-management/$projectId/certificate-authorities",
to: "/organizations/$orgId/projects/cert-management/$projectId/certificate-authorities",
params: {
orgId: currentOrg.id,
projectId
}
});
@@ -84,8 +90,9 @@ const Page = () => {
{data && (
<div className="mx-auto mb-6 w-full max-w-8xl">
<Link
to="/projects/cert-management/$projectId/certificate-authorities"
to="/organizations/$orgId/projects/cert-management/$projectId/certificate-authorities"
params={{
orgId: currentOrg.id,
projectId
}}
className="mb-4 flex items-center gap-x-2 text-sm text-mineshaft-400"
@@ -130,7 +137,7 @@ const Page = () => {
</PageHeader>
<div className="flex">
<div className="mr-4 w-96">
<CaDetailsSection caName={data.name} handlePopUpOpen={handlePopUpOpen} />
<CaDetailsSection caId={data.id} handlePopUpOpen={handlePopUpOpen} />
</div>
<div className="w-full">
<CaCertificatesSection caId={data.id} />
@@ -51,7 +51,7 @@ export const CaCrlsTable = ({ caId }: Props) => {
<Tr key={`ca-crl-${id}`}>
<Td>
<div className="flex items-center">
{`${window.origin}/api/v1/pki/crl/${id}`}
{`${window.origin}/api/v1/cert-manager/crl/${id}`}
</div>
</Td>
{/* <Td>{format(new Date(caCrlObj.thisUpdate), "yyyy-MM-dd")}</Td> */}
@@ -4,7 +4,7 @@ import { format } from "date-fns";
import { ProjectPermissionCan } from "@app/components/permissions";
import { Button, IconButton, Tooltip } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useProject } from "@app/context";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { useTimedReset } from "@app/hooks";
import { CaStatus, CaType, InternalCaType, useGetCa } from "@app/hooks/api";
import { caStatusToNameMap, caTypeToNameMap } from "@app/hooks/api/ca/constants";
@@ -13,15 +13,14 @@ import { certKeyAlgorithmToNameMap } from "@app/hooks/api/certificates/constants
import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = {
caName: string;
caId: string;
handlePopUpOpen: (
popUpName: keyof UsePopUpState<["ca", "renewCa", "installCaCert"]>,
data?: object
) => void;
};
export const CaDetailsSection = ({ caName, handlePopUpOpen }: Props) => {
const { currentProject } = useProject();
export const CaDetailsSection = ({ caId, handlePopUpOpen }: Props) => {
const [copyTextId, isCopyingId, setCopyTextId] = useTimedReset<string>({
initialState: "Copy ID to clipboard"
});
@@ -30,8 +29,7 @@ export const CaDetailsSection = ({ caName, handlePopUpOpen }: Props) => {
});
const { data } = useGetCa({
caName,
projectId: currentProject.id,
caId,
type: CaType.INTERNAL
});
@@ -53,7 +51,7 @@ export const CaDetailsSection = ({ caName, handlePopUpOpen }: Props) => {
onClick={(e) => {
e.stopPropagation();
handlePopUpOpen("ca", {
name: ca.name
caId: ca.id
});
}}
>
@@ -154,10 +152,6 @@ export const CaDetailsSection = ({ caName, handlePopUpOpen }: Props) => {
: "-"}
</p>
</div>
<div className="mb-4">
<p className="text-sm font-medium text-mineshaft-300">Enable Direct Issuance</p>
<p className="text-sm text-mineshaft-300">{ca.enableDirectIssuance ? "True" : "False"}</p>
</div>
{ca.status === CaStatus.ACTIVE && (
<ProjectPermissionCan
I={ProjectPermissionActions.Edit}
@@ -3,7 +3,7 @@ import { createFileRoute, linkOptions } from "@tanstack/react-router";
import { CertAuthDetailsByIDPage } from "./CertAuthDetailsByIDPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/ca/$caName"
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caId"
)({
component: CertAuthDetailsByIDPage,
beforeLoad: ({ context, params }) => {
@@ -13,14 +13,15 @@ export const Route = createFileRoute(
{
label: "Certificate Authorities",
link: linkOptions({
to: "/projects/cert-management/$projectId/certificate-authorities",
to: "/organizations/$orgId/projects/cert-management/$projectId/certificate-authorities",
params: {
orgId: params.orgId,
projectId: params.projectId
}
})
},
{
label: params.caName
label: params.caId
}
]
};
@@ -9,8 +9,8 @@ import { Button, FormControl, Input, Select, SelectItem } from "@app/components/
import { useProject } from "@app/context";
import {
CaStatus,
useGetCaById,
useGetCaCsr,
useGetInternalCaById,
useImportCaCertificate,
useListWorkspaceCas,
useSignIntermediate
@@ -51,7 +51,7 @@ export const InternalCaInstallForm = ({ caId, handlePopUpToggle }: Props) => {
projectId: currentProject.id,
status: CaStatus.ACTIVE
});
const { data: ca } = useGetCaById(caId);
const { data: ca } = useGetInternalCaById(caId);
const { data: csr } = useGetCaCsr(caId);
const { mutateAsync: signIntermediate } = useSignIntermediate();
@@ -83,18 +83,21 @@ export const InternalCaInstallForm = ({ caId, handlePopUpToggle }: Props) => {
const parentCaId = watch("parentCaId");
const { data: parentCa } = useGetCaById(parentCaId);
const { data: parentCa } = useGetInternalCaById(parentCaId);
useEffect(() => {
if (parentCa?.maxPathLength) {
if (parentCa?.configuration.maxPathLength) {
setValue(
"maxPathLength",
(parentCa.maxPathLength === -1 ? 3 : parentCa.maxPathLength - 1).toString()
(parentCa.configuration.maxPathLength === -1
? 3
: parentCa.configuration.maxPathLength - 1
).toString()
);
}
if (parentCa?.notAfter) {
const parentCaNotAfter = new Date(parentCa.notAfter);
if (parentCa?.configuration.notAfter) {
const parentCaNotAfter = new Date(parentCa.configuration.notAfter);
const middleDate = getMiddleDate(new Date(), parentCaNotAfter);
setValue("notAfter", format(middleDate, "yyyy-MM-dd"));
}
@@ -197,7 +200,7 @@ export const InternalCaInstallForm = ({ caId, handlePopUpToggle }: Props) => {
onValueChange={onChange}
className="w-full"
>
{generatePathLengthOpts(parentCa?.maxPathLength || 0).map((value) => (
{generatePathLengthOpts(parentCa?.configuration.maxPathLength || 0).map((value) => (
<SelectItem value={String(value)} key={`ca-path-length-${value}`}>
{`${value}`}
</SelectItem>
@@ -46,7 +46,6 @@ const schema = z
name: slugSchema({
field: "Name"
}),
enableDirectIssuance: z.boolean(),
status: z.nativeEnum(CaStatus),
configuration: z
.object({
@@ -80,8 +79,7 @@ const caTypes = [
export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
const { currentProject } = useProject();
const { data: ca } = useGetCa({
caName: (popUp?.ca?.data as { name: string })?.name || "",
projectId: currentProject?.id || "",
caId: (popUp?.ca?.data as { caId: string })?.caId || "",
type: CaType.INTERNAL
});
@@ -100,7 +98,6 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
type: CaType.INTERNAL,
name: "",
status: CaStatus.ACTIVE,
enableDirectIssuance: true,
configuration: {
type: InternalCaType.ROOT,
organization: "",
@@ -124,7 +121,6 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
type: ca.type,
name: ca.name,
status: ca.status,
enableDirectIssuance: ca.enableDirectIssuance,
configuration: {
type: ca.configuration.type,
organization: ca.configuration.organization,
@@ -151,7 +147,6 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
type: CaType.INTERNAL,
name: "",
status: CaStatus.ACTIVE,
enableDirectIssuance: false,
configuration: {
type: InternalCaType.ROOT,
organization: "",
@@ -168,24 +163,16 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
}
}, [ca]);
const onFormSubmit = async ({
type,
name,
enableDirectIssuance,
status,
configuration
}: FormData) => {
const onFormSubmit = async ({ type, name, status, configuration }: FormData) => {
if (!currentProject?.slug) return;
if (ca) {
// update
await updateMutateAsync({
caName: ca.name,
projectId: currentProject.id,
id: ca.id,
name,
type: CaType.INTERNAL,
status,
enableDirectIssuance
status
});
} else {
// create
@@ -194,7 +181,6 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
name,
type,
status,
enableDirectIssuance,
configuration: {
...configuration,
maxPathLength: Number(configuration.maxPathLength)
@@ -26,10 +26,10 @@ export const CaSection = () => {
"caStatus" // enable / disable
] as const);
const onRemoveCaSubmit = async (caName: string) => {
const onRemoveCaSubmit = async (id: string) => {
if (!currentProject?.slug) return;
await deleteCa({ caName, projectId: currentProject.id, type: CaType.INTERNAL });
await deleteCa({ id, projectId: currentProject.id, type: CaType.INTERNAL });
createNotification({
text: "Successfully deleted CA",
@@ -39,10 +39,10 @@ export const CaSection = () => {
handlePopUpClose("deleteCa");
};
const onUpdateCaStatus = async ({ caName, status }: { caName: string; status: CaStatus }) => {
const onUpdateCaStatus = async ({ caId, status }: { caId: string; status: CaStatus }) => {
if (!currentProject?.slug) return;
await updateCa({ caName, projectId: currentProject.id, type: CaType.INTERNAL, status });
await updateCa({ id: caId, type: CaType.INTERNAL, status });
createNotification({
text: `Successfully ${status === CaStatus.ACTIVE ? "enabled" : "disabled"} CA`,
@@ -85,9 +85,7 @@ export const CaSection = () => {
subTitle="This action will delete other CAs and certificates below it in your CA hierarchy."
onChange={(isOpen) => handlePopUpToggle("deleteCa", isOpen)}
deleteKey="confirm"
onDeleteApproved={() =>
onRemoveCaSubmit((popUp?.deleteCa?.data as { caName: string })?.caName)
}
onDeleteApproved={() => onRemoveCaSubmit((popUp?.deleteCa?.data as { caId: string })?.caId)}
/>
<DeleteActionModal
isOpen={popUp.caStatus.isOpen}
@@ -104,7 +102,7 @@ export const CaSection = () => {
onChange={(isOpen) => handlePopUpToggle("caStatus", isOpen)}
deleteKey="confirm"
onDeleteApproved={() =>
onUpdateCaStatus(popUp?.caStatus?.data as { caName: string; status: CaStatus })
onUpdateCaStatus(popUp?.caStatus?.data as { caId: string; status: CaStatus })
}
/>
</div>
@@ -22,7 +22,12 @@ import {
Tr
} from "@app/components/v2";
import { Badge } from "@app/components/v3";
import { ProjectPermissionActions, ProjectPermissionSub, useProject } from "@app/context";
import {
ProjectPermissionActions,
ProjectPermissionSub,
useOrganization,
useProject
} from "@app/context";
import { CaStatus, CaType, useListCasByTypeAndProjectId } from "@app/hooks/api";
import {
caStatusToNameMap,
@@ -46,6 +51,7 @@ type Props = {
export const CaTable = ({ handlePopUpOpen }: Props) => {
const navigate = useNavigate();
const { currentOrg } = useOrganization();
const { currentProject } = useProject();
const { data, isPending } = useListCasByTypeAndProjectId(CaType.INTERNAL, currentProject.id);
const cas = data as TInternalCertificateAuthority[];
@@ -75,10 +81,11 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
key={`ca-${ca.id}`}
onClick={() =>
navigate({
to: "/projects/cert-management/$projectId/ca/$caName",
to: "/organizations/$orgId/projects/cert-management/$projectId/ca/$caId",
params: {
orgId: currentOrg.id,
projectId: currentProject.id,
caName: ca.name
caId: ca.id
}
})
}
@@ -173,7 +180,7 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
onClick={(e) => {
e.stopPropagation();
handlePopUpOpen("caStatus", {
caName: ca.name,
caId: ca.id,
status:
ca.status === CaStatus.ACTIVE
? CaStatus.DISABLED
@@ -200,7 +207,7 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
onClick={(e) => {
e.stopPropagation();
handlePopUpOpen("deleteCa", {
caName: ca.name
caId: ca.id
});
}}
disabled={!isAllowed}
@@ -14,8 +14,7 @@ import {
Modal,
ModalContent,
Select,
SelectItem,
Switch
SelectItem
} from "@app/components/v2";
import { useProject } from "@app/context";
import { APP_CONNECTION_MAP } from "@app/helpers/appConnections";
@@ -27,6 +26,10 @@ import {
TCloudflareZone,
useCloudflareConnectionListZones
} from "@app/hooks/api/appConnections/cloudflare";
import {
TDNSMadeEasyZone,
useDNSMadeEasyConnectionListZones
} from "@app/hooks/api/appConnections/dns-made-easy";
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import {
AcmeDnsProvider,
@@ -59,7 +62,6 @@ const baseSchema = z.object({
name: slugSchema({
field: "Name"
}),
enableDirectIssuance: z.boolean(),
status: z.nativeEnum(CaStatus)
});
@@ -131,8 +133,7 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
const { currentProject } = useProject();
const { data: ca, isLoading: isCaLoading } = useGetCa({
caName: (popUp?.ca?.data as { name: string })?.name || "",
projectId: currentProject?.id || "",
caId: (popUp?.ca?.data as { caId: string })?.caId || "",
type: (popUp?.ca?.data as { type: CaType })?.type || ""
});
@@ -168,7 +169,6 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
type: CaType.AZURE_AD_CS,
name: "",
status: CaStatus.ACTIVE,
enableDirectIssuance: false,
configuration: {
azureAdcsConnection: {
id: "",
@@ -181,7 +181,6 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
type: CaType.ACME,
name: "",
status: CaStatus.ACTIVE,
enableDirectIssuance: true,
configuration: {
dnsAppConnection: {
id: "",
@@ -211,6 +210,11 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
enabled: caType === CaType.ACME
});
const { data: availableDNSMadeEasyConnections, isPending: isDNSMadeEasyPending } =
useListAvailableAppConnections(AppConnection.DNSMadeEasy, currentProject.id, {
enabled: caType === CaType.ACME
});
const { data: availableAzureConnections, isPending: isAzurePending } =
useListAvailableAppConnections(AppConnection.AzureADCS, currentProject.id, {
enabled: caType === CaType.AZURE_AD_CS
@@ -220,16 +224,24 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
if (caType === CaType.AZURE_AD_CS) {
return availableAzureConnections || [];
}
return [...(availableRoute53Connections || []), ...(availableCloudflareConnections || [])];
return [
...(availableRoute53Connections || []),
...(availableCloudflareConnections || []),
...(availableDNSMadeEasyConnections || [])
];
}, [
caType,
availableRoute53Connections,
availableCloudflareConnections,
availableDNSMadeEasyConnections,
availableAzureConnections
]);
const isPending =
isRoute53Pending || isCloudflarePending || (isAzurePending && caType === CaType.AZURE_AD_CS);
isRoute53Pending ||
isCloudflarePending ||
isDNSMadeEasyPending ||
(isAzurePending && caType === CaType.AZURE_AD_CS);
const dnsAppConnection =
caType === CaType.ACME && configuration && "dnsAppConnection" in configuration
@@ -241,6 +253,11 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
enabled: dnsProvider === AcmeDnsProvider.Cloudflare && !!dnsAppConnection.id
});
const { data: dnsMadeEasyZones = [], isPending: isDNSMadeEasyZonesPending } =
useDNSMadeEasyConnectionListZones(dnsAppConnection.id, {
enabled: dnsProvider === AcmeDnsProvider.DNSMadeEasy && !!dnsAppConnection.id
});
// Populate form with CA data when editing
useEffect(() => {
if (ca && !isCaLoading) {
@@ -253,7 +270,6 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
type: ca.type,
name: ca.name,
status: ca.status,
enableDirectIssuance: ca.enableDirectIssuance,
configuration: {
dnsAppConnection: {
id: ca.configuration.dnsAppConnectionId,
@@ -278,7 +294,6 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
type: ca.type,
name: ca.name,
status: ca.status,
enableDirectIssuance: false,
configuration: {
azureAdcsConnection: {
id: ca.configuration.azureAdcsConnectionId,
@@ -293,7 +308,6 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
const onFormSubmit = async ({
type,
name,
enableDirectIssuance,
status,
configuration: formConfiguration
}: FormData) => {
@@ -320,12 +334,11 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
if (ca) {
await updateMutateAsync({
caName: ca.name,
id: ca.id,
projectId: currentProject.id,
name,
type,
status,
enableDirectIssuance: type === CaType.AZURE_AD_CS ? false : enableDirectIssuance,
configuration: configPayload
});
} else {
@@ -334,7 +347,6 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
name,
type,
status,
enableDirectIssuance: type === CaType.AZURE_AD_CS ? false : enableDirectIssuance,
configuration: configPayload
});
}
@@ -500,6 +512,32 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
)}
/>
)}
{dnsProvider === AcmeDnsProvider.DNSMadeEasy && (
<Controller
name="configuration.dnsProviderConfig.hostedZoneId"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Zone"
>
<FilterableSelect
isLoading={isDNSMadeEasyZonesPending && !!dnsAppConnection.id}
isDisabled={!dnsAppConnection.id}
value={dnsMadeEasyZones.find((zone) => zone.id === value)}
onChange={(option) => {
onChange((option as SingleValue<TDNSMadeEasyZone>)?.id ?? null);
}}
options={dnsMadeEasyZones}
placeholder="Select a zone..."
getOptionLabel={(option) => option.name}
getOptionValue={(option) => option.id}
/>
</FormControl>
)}
/>
)}
<Controller
control={control}
defaultValue=""
@@ -601,25 +639,6 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
name="configuration.azureAdcsConnection"
/>
)}
{caType === CaType.ACME && (
<Controller
control={control}
name="enableDirectIssuance"
render={({ field, fieldState: { error } }) => {
return (
<FormControl isError={Boolean(error)} errorText={error?.message} className="my-8">
<Switch
id="is-active"
onCheckedChange={(value) => field.onChange(value)}
isChecked={field.value}
>
<p className="w-full">Enable Direct Issuance</p>
</Switch>
</FormControl>
);
}}
/>
)}
<div className="flex items-center">
<Button
className="mr-4"
@@ -22,10 +22,10 @@ export const ExternalCaSection = () => {
"caStatus" // enable / disable
] as const);
const onRemoveCaSubmit = async (caName: string, type: CaType) => {
const onRemoveCaSubmit = async (id: string, type: CaType) => {
if (!currentProject?.id) return;
await deleteCa({ caName, type, projectId: currentProject.id });
await deleteCa({ id, type, projectId: currentProject.id });
createNotification({
text: "Successfully deleted CA",
@@ -36,17 +36,17 @@ export const ExternalCaSection = () => {
};
const onUpdateCaStatus = async ({
name,
caId,
type,
status
}: {
name: string;
caId: string;
type: CaType;
status: CaStatus;
}) => {
if (!currentProject?.slug) return;
await updateCa({ caName: name, type, status, projectId: currentProject.id });
await updateCa({ id: caId, type, status });
createNotification({
text: `Successfully ${status === CaStatus.ACTIVE ? "enabled" : "disabled"} CA`,
@@ -88,7 +88,7 @@ export const ExternalCaSection = () => {
deleteKey="confirm"
onDeleteApproved={() =>
onRemoveCaSubmit(
(popUp?.deleteCa?.data as { name: string })?.name,
(popUp?.deleteCa?.data as { caId: string })?.caId,
(popUp?.deleteCa?.data as { type: CaType })?.type
)
}
@@ -110,7 +110,7 @@ export const ExternalCaSection = () => {
deleteKey="confirm"
onDeleteApproved={() =>
onUpdateCaStatus(
popUp?.caStatus?.data as { name: string; type: CaType; status: CaStatus }
popUp?.caStatus?.data as { caId: string; type: CaType; status: CaStatus }
)
}
/>
@@ -35,6 +35,7 @@ type Props = {
handlePopUpOpen: (
popUpName: keyof UsePopUpState<["ca", "deleteCa", "caStatus"]>,
data?: {
caId?: string;
name?: string;
type?: CaType;
status?: CaStatus;
@@ -70,6 +71,7 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
key={`ca-${ca.id}`}
onClick={() => {
handlePopUpOpen("ca", {
caId: ca.id,
name: ca.name,
type: ca.type
});
@@ -104,6 +106,7 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
onClick={(e) => {
e.stopPropagation();
handlePopUpOpen("ca", {
caId: ca.id,
name: ca.name,
type: ca.type
});
@@ -129,7 +132,7 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
onClick={(e) => {
e.stopPropagation();
handlePopUpOpen("caStatus", {
name: ca.name,
caId: ca.id,
type: ca.type,
status:
ca.status === CaStatus.ACTIVE
@@ -157,7 +160,7 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
onClick={(e) => {
e.stopPropagation();
handlePopUpOpen("deleteCa", {
name: ca.name,
caId: ca.id,
type: ca.type
});
}}
@@ -3,7 +3,7 @@ import { createFileRoute } from "@tanstack/react-router";
import { CertificateAuthoritiesPage } from "./CertificateAuthoritiesPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/certificate-authorities"
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/certificate-authorities"
)({
component: CertificateAuthoritiesPage,
beforeLoad: ({ context }) => {
@@ -21,7 +21,13 @@ type Props = {
) => void;
onFormatSelected: (
format: "pem" | "pkcs12",
serialNumber: string,
{
certificateId,
serialNumber
}: {
certificateId: string;
serialNumber: string;
},
options?: ExportOptions
) => void;
};
@@ -42,8 +48,11 @@ export const CertificateExportModal = ({ popUp, handlePopUpToggle, onFormatSelec
alias: ""
});
const serialNumber =
(popUp?.certificateExport?.data as { serialNumber: string })?.serialNumber || "";
const { certificateId, serialNumber } =
(popUp?.certificateExport?.data as {
certificateId: string;
serialNumber: string;
}) || {};
// Reset form whenever the modal opens
useEffect(() => {
@@ -64,14 +73,21 @@ export const CertificateExportModal = ({ popUp, handlePopUpToggle, onFormatSelec
};
const handleExport = () => {
if (serialNumber && isFormValid()) {
if ((certificateId || serialNumber) && isFormValid()) {
const options: ExportOptions = {};
if (selectedFormat === "pkcs12") {
options.pkcs12 = pkcs12Options;
}
onFormatSelected(selectedFormat, serialNumber, options);
onFormatSelected(
selectedFormat,
{
certificateId,
serialNumber
},
options
);
handlePopUpToggle("certificateExport", false);
}
};
@@ -151,7 +167,7 @@ export const CertificateExportModal = ({ popUp, handlePopUpToggle, onFormatSelec
colorSchema="primary"
leftIcon={<FontAwesomeIcon icon={faDownload} />}
onClick={handleExport}
disabled={!serialNumber || !isFormValid()}
disabled={!(certificateId || serialNumber) || !isFormValid()}
>
Export {selectedFormat.toUpperCase()}
</Button>
@@ -20,9 +20,9 @@ import {
} from "@app/components/v2";
import { useProject } from "@app/context";
import { useGetCert } from "@app/hooks/api";
import { useCreateCertificateV3 } from "@app/hooks/api/ca";
import { useListCertificateProfiles } from "@app/hooks/api/certificateProfiles";
import { EnrollmentType, useListCertificateProfiles } from "@app/hooks/api/certificateProfiles";
import { CertExtendedKeyUsage, CertKeyUsage } from "@app/hooks/api/certificates/enums";
import { useUnifiedCertificateIssuance } from "@app/hooks/api/certificates/mutations";
import { useGetCertificateTemplateV2ById } from "@app/hooks/api/certificateTemplates/queries";
import { UsePopUpState } from "@app/hooks/usePopUp";
import { CertSubjectAlternativeNameType } from "@app/pages/cert-manager/PoliciesPage/components/CertificateTemplatesV2Tab/shared/certificate-constants";
@@ -103,10 +103,11 @@ type Props = {
};
type TCertificateDetails = {
serialNumber: string;
certificate: string;
certificateChain: string;
privateKey: string;
serialNumber?: string;
certificate?: string;
certificateChain?: string;
privateKey?: string;
issuingCaCertificate?: string;
};
export const CertificateIssuanceModal = ({ popUp, handlePopUpToggle, profileId }: Props) => {
@@ -122,12 +123,11 @@ export const CertificateIssuanceModal = ({ popUp, handlePopUpToggle, profileId }
const { data: profilesData } = useListCertificateProfiles({
projectId: currentProject?.id || "",
enrollmentType: "api"
enrollmentType: EnrollmentType.API,
includeConfigs: true
});
const { mutateAsync: createCertificate } = useCreateCertificateV3({
projectId: currentProject?.id
});
const { mutateAsync: issueCertificate } = useUnifiedCertificateIssuance();
const formResolver = useMemo(() => {
return zodResolver(createSchema(shouldShowSubjectSection));
@@ -243,7 +243,7 @@ export const CertificateIssuanceModal = ({ popUp, handlePopUpToggle, profileId }
keyUsages,
extendedKeyUsages
}: FormData) => {
if (!currentProject?.slug) {
if (!currentProject?.slug || !currentProject?.id) {
createNotification({
text: "Project not found. Please refresh and try again.",
type: "error"
@@ -275,44 +275,70 @@ export const CertificateIssuanceModal = ({ popUp, handlePopUpToggle, profileId }
}
}
const certificateRequest: any = {
profileId: formProfileId,
projectSlug: currentProject.slug,
ttl,
signatureAlgorithm,
keyAlgorithm,
keyUsages: filterUsages(keyUsages) as CertKeyUsage[],
extendedKeyUsages: filterUsages(extendedKeyUsages) as CertExtendedKeyUsage[]
};
try {
// Prepare unified request
const request: any = {
profileId: formProfileId,
projectSlug: currentProject.slug,
projectId: currentProject.id,
attributes: {
ttl,
signatureAlgorithm: signatureAlgorithm || "",
keyAlgorithm: keyAlgorithm || "",
keyUsages: filterUsages(keyUsages) as CertKeyUsage[],
extendedKeyUsages: filterUsages(extendedKeyUsages) as CertExtendedKeyUsage[]
}
};
if (constraints.shouldShowSubjectSection && commonName) {
certificateRequest.commonName = commonName;
}
if (constraints.shouldShowSanSection && subjectAltNames && subjectAltNames.length > 0) {
const formattedSans = formatSubjectAltNames(subjectAltNames);
if (formattedSans && formattedSans.length > 0) {
certificateRequest.altNames = formattedSans;
if (constraints.shouldShowSubjectSection && commonName) {
request.attributes.commonName = commonName;
}
if (constraints.shouldShowSanSection && subjectAltNames && subjectAltNames.length > 0) {
const formattedSans = formatSubjectAltNames(subjectAltNames);
if (formattedSans && formattedSans.length > 0) {
request.attributes.altNames = formattedSans;
}
}
const response = await issueCertificate(request);
// Handle certificate issuance response
if ("certificate" in response && response.certificate) {
const certData = response.certificate;
const certificateDetailsToSet = {
serialNumber: certData.serialNumber || "",
certificate: certData.certificate || "",
certificateChain: certData.certificateChain || "",
privateKey: certData.privateKey || "",
issuingCaCertificate: certData.issuingCaCertificate || ""
};
setCertificateDetails(certificateDetailsToSet);
createNotification({
text: "Successfully created certificate",
type: "success"
});
} else {
// Certificate request - async processing
createNotification({
text: `Certificate request submitted successfully. This may take a few minutes to process. Certificate Request ID: ${response.certificateRequestId}`,
type: "success"
});
handlePopUpToggle("issueCertificate", false);
}
} catch (error) {
createNotification({
text: `Failed to request certificate: ${(error as Error)?.message || "Unknown error"}`,
type: "error"
});
}
const { serialNumber, certificate, certificateChain, privateKey } =
await createCertificate(certificateRequest);
setCertificateDetails({
serialNumber,
certificate,
certificateChain,
privateKey
});
createNotification({
text: "Successfully created certificate",
type: "success"
});
},
[
currentProject?.slug,
createCertificate,
issueCertificate,
constraints.shouldShowSubjectSection,
constraints.shouldShowSanSection
]
@@ -321,13 +347,13 @@ export const CertificateIssuanceModal = ({ popUp, handlePopUpToggle, profileId }
const getModalTitle = () => {
if (certificateDetails) return "Certificate Created Successfully";
if (cert) return "Certificate Details";
return "Issue New Certificate";
return "Request New Certificate";
};
const getModalSubTitle = () => {
if (certificateDetails) return "Certificate has been successfully created and is ready for use";
if (cert) return "View certificate information";
return "Issue a new certificate using a certificate profile";
return "Request a new certificate using a certificate profile";
};
return (
@@ -343,10 +369,10 @@ export const CertificateIssuanceModal = ({ popUp, handlePopUpToggle, profileId }
<ModalContent title={getModalTitle()} subTitle={getModalSubTitle()}>
{certificateDetails && (
<CertificateContent
serialNumber={certificateDetails.serialNumber}
certificate={certificateDetails.certificate}
certificateChain={certificateDetails.certificateChain}
privateKey={certificateDetails.privateKey}
serialNumber={certificateDetails.serialNumber!}
certificate={certificateDetails.certificate!}
certificateChain={certificateDetails.certificateChain!}
privateKey={certificateDetails.privateKey!}
/>
)}
{cert && (
@@ -498,7 +524,7 @@ export const CertificateIssuanceModal = ({ popUp, handlePopUpToggle, profileId }
isLoading={isSubmitting}
isDisabled={isSubmitting || (!actualSelectedProfile && !profileId)}
>
{cert ? "Update" : "Issue Certificate"}
{cert ? "Update" : "Request Certificate"}
</Button>
<Button
colorSchema="secondary"
@@ -20,7 +20,7 @@ import {
Tr
} from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import { useProject } from "@app/context";
import { useOrganization, useProject } from "@app/context";
import {
PkiSync,
useAddCertificatesToPkiSync,
@@ -49,6 +49,7 @@ export const CertificateManagePkiSyncsModal = ({ popUp, handlePopUpToggle }: Pro
const [currentPage, setCurrentPage] = useState(1);
const [searchTerm, setSearchTerm] = useState("");
const { currentOrg } = useOrganization();
const { currentProject } = useProject();
const navigate = useNavigate();
const { certificateId, commonName } = popUp.data || {};
@@ -92,6 +93,7 @@ export const CertificateManagePkiSyncsModal = ({ popUp, handlePopUpToggle }: Pro
navigate({
to: ROUTE_PATHS.CertManager.IntegrationsListPage.path,
params: {
orgId: currentOrg.id,
projectId: currentProject.id
},
search: {
@@ -20,12 +20,16 @@ export const CertificateRenewalModal = ({ popUp, handlePopUpToggle }: Props) =>
const onRenewConfirm = async () => {
const { certificateId } = popUp.renewCertificate.data as { certificateId: string };
await renewCertificate({
const result = await renewCertificate({
certificateId
});
const notificationText = result.certificateRequestId
? `Certificate renewal initiated successfully. Certificate Request ID: ${result.certificateRequestId}`
: "Certificate renewed successfully";
createNotification({
text: "Certificate renewed successfully",
text: notificationText,
type: "success"
});
@@ -50,11 +50,11 @@ export const CertificateRevocationModal = ({ popUp, handlePopUpToggle }: Props)
const onFormSubmit = async ({ revocationReason }: FormData) => {
if (!currentProject?.slug) return;
const { serialNumber } = popUp.revokeCertificate.data as { serialNumber: string };
const { certificateId } = popUp.revokeCertificate.data as { certificateId: string };
await revokeCertificate({
projectId: currentProject.id,
serialNumber,
id: certificateId,
revocationReason
});
@@ -26,8 +26,8 @@ import { useProject } from "@app/context";
import {
CaStatus,
useCreateCertTemplate,
useGetCaById,
useGetCertTemplate,
useGetInternalCaById,
useListWorkspaceCas,
useListWorkspacePkiCollections,
useUpdateCertTemplate
@@ -84,7 +84,7 @@ type Props = {
export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Props) => {
const { currentProject } = useProject();
const { data: ca } = useGetCaById(caId);
const { data: ca } = useGetInternalCaById(caId);
const { data: certTemplate } = useGetCertTemplate(
(popUp?.certificateTemplate?.data as { id: string })?.id || ""
@@ -39,11 +39,11 @@ export const CertificatesSection = () => {
"managePkiSyncs"
] as const);
const onRemoveCertificateSubmit = async (serialNumber: string) => {
const onRemoveCertificateSubmit = async (id: string) => {
if (!currentProject?.slug) return;
await deleteCert({
serialNumber,
id,
projectId: currentProject.id
});
@@ -57,7 +57,13 @@ export const CertificatesSection = () => {
const handleCertificateExport = async (
format: "pem" | "pkcs12",
serialNumber: string,
{
certificateId,
serialNumber
}: {
certificateId: string;
serialNumber: string;
},
options?: ExportOptions
) => {
if (format === "pem") {
@@ -75,7 +81,7 @@ export const CertificatesSection = () => {
try {
await downloadCertPkcs12({
serialNumber,
certificateId,
projectSlug: currentProject.slug,
password: options.pkcs12.password,
alias: options.pkcs12.alias
@@ -119,7 +125,7 @@ export const CertificatesSection = () => {
onClick={() => handlePopUpOpen("issueCertificate")}
isDisabled={!isAllowed}
>
Issue
Request
</Button>
</div>
)}
@@ -150,7 +156,7 @@ export const CertificatesSection = () => {
deleteKey="confirm"
onDeleteApproved={() =>
onRemoveCertificateSubmit(
(popUp?.deleteCertificate?.data as { serialNumber: string })?.serialNumber
(popUp?.deleteCertificate?.data as { certificateId: string })?.certificateId
)
}
/>
@@ -277,6 +277,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
)}
onClick={async () =>
handlePopUpOpen("certificateExport", {
certificateId: certificate.id,
serialNumber: certificate.serialNumber
})
}
@@ -416,7 +417,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
{/* Manual renewal action for profile-issued certificates that are not revoked/expired (including failed ones) */}
{(() => {
const canRenew =
certificate.profileId &&
(certificate.profileId || certificate.caId) &&
certificate.hasPrivateKey !== false &&
!certificate.renewedByCertificateId &&
!isRevoked &&
@@ -501,7 +502,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
)}
onClick={async () =>
handlePopUpOpen("revokeCertificate", {
serialNumber: certificate.serialNumber
certificateId: certificate.id
})
}
disabled={!isAllowed}
@@ -524,7 +525,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
)}
onClick={async () =>
handlePopUpOpen("deleteCertificate", {
serialNumber: certificate.serialNumber,
certificateId: certificate.id,
commonName: certificate.commonName
})
}
@@ -5,7 +5,7 @@ import { useNavigate, useSearch } from "@tanstack/react-router";
import { ProjectPermissionCan } from "@app/components/permissions";
import { PageHeader, Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import { ProjectPermissionSub, useProject } from "@app/context";
import { ProjectPermissionSub, useOrganization, useProject } from "@app/context";
import { ProjectPermissionPkiSyncActions } from "@app/context/ProjectPermissionContext/types";
import { ProjectType } from "@app/hooks/api/projects/types";
import { IntegrationsListPageTabs } from "@app/types/integrations";
@@ -14,6 +14,7 @@ import { PkiSyncsTab } from "./components";
export const IntegrationsListPage = () => {
const navigate = useNavigate();
const { currentOrg } = useOrganization();
const { currentProject } = useProject();
const { t } = useTranslation();
@@ -30,7 +31,8 @@ export const IntegrationsListPage = () => {
selectedTab: tab as IntegrationsListPageTabs
},
params: {
projectId: currentProject?.id
projectId: currentProject?.id,
orgId: currentOrg.id
}
});
};
@@ -47,7 +49,7 @@ export const IntegrationsListPage = () => {
<div className="mb-8">
<PageHeader
scope={ProjectType.CertificateManager}
title="Integrations"
title="Project Integrations"
description="Manage integrations with third-party certificate services."
/>
<Tabs orientation="vertical" value={currentTab} onValueChange={updateSelectedTab}>
@@ -39,7 +39,7 @@ import {
} from "@app/components/v2";
import { Badge } from "@app/components/v3";
import { ROUTE_PATHS } from "@app/const/routes";
import { ProjectPermissionSub } from "@app/context";
import { ProjectPermissionSub, useOrganization } from "@app/context";
import { ProjectPermissionPkiSyncActions } from "@app/context/ProjectPermissionContext/types";
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
import { useToggle } from "@app/hooks";
@@ -82,6 +82,7 @@ export const PkiSyncRow = ({
const { syncOption } = usePkiSyncOption(destination);
const { currentOrg } = useOrganization();
const [isIdCopied, setIsIdCopied] = useToggle(false);
const handleCopyId = useCallback(() => {
@@ -127,7 +128,8 @@ export const PkiSyncRow = ({
to: ROUTE_PATHS.CertManager.PkiSyncDetailsByIDPage.path,
params: {
syncId: id,
projectId
projectId,
orgId: currentOrg.id
}
});
}}
@@ -8,7 +8,7 @@ import { CreatePkiSyncModal } from "@app/components/pki-syncs";
import { Button, Spinner } from "@app/components/v2";
import { DocumentationLinkBadge } from "@app/components/v3";
import { ROUTE_PATHS } from "@app/const/routes";
import { ProjectPermissionSub, useProject } from "@app/context";
import { ProjectPermissionSub, useOrganization, useProject } from "@app/context";
import { ProjectPermissionPkiSyncActions } from "@app/context/ProjectPermissionContext/types";
import { usePopUp } from "@app/hooks";
import { useListPkiSyncs } from "@app/hooks/api/pkiSyncs";
@@ -26,18 +26,19 @@ export const PkiSyncsTab = () => {
const navigate = useNavigate();
const { currentProject } = useProject();
const { currentOrg } = useOrganization();
const memoizedSearch = useMemo(() => search, [search]);
const navigateToBase = useCallback(() => {
navigate({
to: ROUTE_PATHS.CertManager.IntegrationsListPage.path,
params: {
projectId: currentProject?.id
projectId: currentProject?.id,
orgId: currentOrg.id
},
search: memoizedSearch
});
}, [navigate, currentProject?.id, memoizedSearch]);
}, [navigate, currentProject?.id, currentOrg.id, memoizedSearch]);
useEffect(() => {
if (!addSync) return;
@@ -59,7 +60,7 @@ export const PkiSyncsTab = () => {
handlePopUpOpen("addSync", { destination: parsedData.destination, initialData });
navigate({
to: ROUTE_PATHS.CertManager.IntegrationsListPage.path,
params: { projectId: currentProject?.id },
params: { projectId: currentProject?.id, orgId: currentOrg.id },
search: { selectedTab: IntegrationsListPageTabs.PkiSyncs },
replace: true
});
@@ -79,7 +80,8 @@ export const PkiSyncsTab = () => {
connectionId,
connectionName,
navigate,
currentProject?.id
currentProject?.id,
currentOrg.id
]);
const { data: pkiSyncs = [], isPending: isPkiSyncsPending } = useListPkiSyncs(
@@ -15,7 +15,7 @@ const IntegrationsListPageQuerySchema = z.object({
});
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/integrations/"
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/integrations/"
)({
component: IntegrationsListPage,
validateSearch: zodValidator(IntegrationsListPageQuerySchema),
@@ -18,7 +18,12 @@ import {
Tooltip
} from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import { ProjectPermissionActions, ProjectPermissionSub, useProject } from "@app/context";
import {
ProjectPermissionActions,
ProjectPermissionSub,
useOrganization,
useProject
} from "@app/context";
import { useDeletePkiCollection, useGetPkiCollectionById } from "@app/hooks/api";
import { PkiItemType } from "@app/hooks/api/pkiCollections/constants";
import { ProjectType } from "@app/hooks/api/projects/types";
@@ -34,6 +39,7 @@ export const PkiCollectionPage = () => {
});
const collectionId = params.collectionId as string;
const { currentProject } = useProject();
const { currentOrg } = useOrganization();
const projectId = currentProject?.id || "";
const { data } = useGetPkiCollectionById(collectionId);
@@ -58,8 +64,9 @@ export const PkiCollectionPage = () => {
});
handlePopUpClose("deletePkiCollection");
navigate({
to: "/projects/cert-management/$projectId/policies",
to: "/organizations/$orgId/projects/cert-management/$projectId/policies",
params: {
orgId: currentOrg.id,
projectId: params.projectId
}
});
@@ -70,8 +77,9 @@ export const PkiCollectionPage = () => {
{data && (
<div className="mx-auto mb-6 w-full max-w-8xl">
<Link
to="/projects/cert-management/$projectId/policies"
to="/organizations/$orgId/projects/cert-management/$projectId/policies"
params={{
orgId: currentOrg.id,
projectId: params.projectId
}}
className="mb-4 flex items-center gap-x-2 text-sm text-mineshaft-400"
@@ -3,7 +3,7 @@ import { createFileRoute, linkOptions } from "@tanstack/react-router";
import { PkiCollectionDetailsByIDPage } from "./PkiCollectionDetailsByIDPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/pki-collections/$collectionId"
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/pki-collections/$collectionId"
)({
component: PkiCollectionDetailsByIDPage,
beforeLoad: ({ context, params }) => {
@@ -13,8 +13,9 @@ export const Route = createFileRoute(
{
label: "Certificate Collections",
link: linkOptions({
to: "/projects/cert-management/$projectId/policies",
to: "/organizations/$orgId/projects/cert-management/$projectId/policies",
params: {
orgId: params.orgId,
projectId: params.projectId
}
})
@@ -21,6 +21,7 @@ import { ROUTE_PATHS } from "@app/const/routes";
import {
ProjectPermissionPkiSubscriberActions,
ProjectPermissionSub,
useOrganization,
useProject
} from "@app/context";
import { useDeletePkiSubscriber, useGetPkiSubscriber } from "@app/hooks/api";
@@ -32,6 +33,7 @@ import { PkiSubscriberCertificatesSection, PkiSubscriberDetailsSection } from ".
const Page = () => {
const navigate = useNavigate();
const { currentOrg } = useOrganization();
const { currentProject } = useProject();
const projectId = currentProject.id;
const subscriberName = useParams({
@@ -62,8 +64,9 @@ const Page = () => {
handlePopUpClose("deletePkiSubscriber");
navigate({
to: "/projects/cert-management/$projectId/subscribers",
to: "/organizations/$orgId/projects/cert-management/$projectId/subscribers",
params: {
orgId: currentOrg.id,
projectId
}
});
@@ -74,8 +77,9 @@ const Page = () => {
{data && (
<div className="mx-auto mb-6 w-full max-w-8xl">
<Link
to="/projects/cert-management/$projectId/subscribers"
to="/organizations/$orgId/projects/cert-management/$projectId/subscribers"
params={{
orgId: currentOrg.id,
projectId
}}
className="mb-4 flex items-center gap-x-2 text-sm text-mineshaft-400"
@@ -171,7 +171,7 @@ export const PkiSubscriberCertificatesTable = ({ subscriberName, handlePopUpOpen
onClick={() =>
handlePopUpOpen &&
handlePopUpOpen("revokeCertificate", {
serialNumber: certificate.serialNumber
certificateId: certificate.id
})
}
disabled={!isAllowed}
@@ -3,7 +3,7 @@ import { createFileRoute, linkOptions } from "@tanstack/react-router";
import { PkiSubscriberDetailsByIDPage } from "./PkiSubscriberDetailsByIDPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/subscribers/$subscriberName"
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/subscribers/$subscriberName"
)({
component: PkiSubscriberDetailsByIDPage,
beforeLoad: ({ context, params }) => {
@@ -13,8 +13,9 @@ export const Route = createFileRoute(
{
label: "Subscribers",
link: linkOptions({
to: "/projects/cert-management/$projectId/subscribers",
to: "/organizations/$orgId/projects/cert-management/$projectId/subscribers",
params: {
orgId: params.orgId,
projectId: params.projectId
}
})

Some files were not shown because too many files have changed in this diff Show More