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

View File

@@ -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"
});
});

View File

@@ -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 || ""
}}

View File

@@ -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
}}

View File

@@ -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);

View File

@@ -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 = (

View File

@@ -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) => {

View File

@@ -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>

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",

View File

@@ -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>
);