feat: resolved almost all ts issues

This commit is contained in:
=
2024-12-28 00:12:55 +05:30
parent ef7b81734a
commit d77c26fa38
141 changed files with 3212 additions and 469 deletions

View File

@@ -4,6 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link, useParams } from "@tanstack/react-router";
import { twMerge } from "tailwind-merge";
import { ROUTE_PATHS } from "@app/const/routes";
import { useOrganization, useWorkspace } from "@app/context";
import { useToggle } from "@app/hooks";
import { ProjectType } from "@app/hooks/api/workspace/types";
@@ -59,7 +60,7 @@ export default function NavHeader({
const [isHoveringCopyButton, setIsHoveringCopyButton] = useState(false);
const routerEnvSlug = useParams({
from: "/_authenticate/_ctx-org-details/secret-manager/$projectId/_layout-secret-manager/secrets/$envSlug/",
from: ROUTE_PATHS.SecretDashboardPage.id,
select: (el) => el.envSlug
});

View File

@@ -1 +1 @@
export { hasSecretReference,SecretReferenceTree } from "./SecretReferenceDetails";
export { hasSecretReference, SecretReferenceTree } from "./SecretReferenceDetails";

View File

@@ -6,12 +6,36 @@ const setRoute = <TFull extends FileRouteTypes["fullPaths"], TId extends FileRou
) => ({ full, id }) as const;
export const ROUTE_PATHS = Object.freeze({
Auth: {
LoginSSO: setRoute("/login/sso", "/_restrict-login-signup/login/sso")
},
Organization: {
SecretScanning: setRoute(
"/organization/secret-scanning",
"/_authenticate/_inject-org-details/organization/_layout/secret-scanning"
),
SettingsPage: setRoute(
"/organization/settings",
"/_authenticate/_inject-org-details/organization/_layout/settings"
)
},
SecretManager: {
ApprovalPage: setRoute(
"/secret-manager/$projectId/approval",
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/approval"
),
SecretDashboardPage: setRoute(
"/secret-manager/$projectId/secrets/$envSlug",
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/secrets/$envSlug"
)
},
ProviderSuccessPage: setRoute(
"/login/provider/success",
"/_restrict-login-signup/login/provider/success"
),
SignUpSsoPage: setRoute("/signup/sso", "/_restrict-login-signup/signup/sso"),
PasswordResetPage: setRoute("/password-reset", "/_restrict-login-signup/password-reset"),
ViewSharedSecretByIDPage: setRoute("/shared/secret/$secretId", "/shared/secret/$secretId"),
OrgGroupDetailsByIDPage: setRoute(
"/organization/groups/$groupId",
"/_authenticate/_inject-org-details/organization/_layout/groups/$groupId"

View File

@@ -13,7 +13,7 @@ import { OrgPermissionSet } from "./types";
export const useOrgPermission = () => {
const organizationId = useRouteContext({
from: "/_authenticate/_ctx-org-details",
from: "/_authenticate/_inject-org-details",
select: (el) => el.organizationId
});

View File

@@ -5,7 +5,7 @@ import { fetchOrganizationById, organizationKeys } from "@app/hooks/api/organiza
export const useOrganization = () => {
const organizationId = useRouteContext({
from: "/_authenticate/_ctx-org-details",
from: "/_authenticate/_inject-org-details",
select: (el) => el.organizationId
});

View File

@@ -5,7 +5,7 @@ import { fetchOrgSubscription, subscriptionQueryKeys } from "@app/hooks/api/subs
export const useSubscription = () => {
const organizationId = useRouteContext({
from: "/_authenticate/_ctx-org-details",
from: "/_authenticate/_inject-org-details",
select: (el) => el.organizationId
});

View File

@@ -0,0 +1,89 @@
import { useTranslation } from "react-i18next";
import { faArrowLeft, faInfo, faMobile, faQuestion } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link, Outlet } from "@tanstack/react-router";
import { WishForm } from "@app/components/features/WishForm";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger
} from "@app/components/v2";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { InsecureConnectionBanner } from "../OrganizationLayout/components/InsecureConnectionBanner";
import { INFISICAL_SUPPORT_OPTIONS } from "../OrganizationLayout/components/SidebarFooter/SidebarFooter";
export const AdminLayout = () => {
const { t } = useTranslation();
const infisicalPlatformVersion = import.meta.env.VITE_INFISICAL_PLATFORM_VERSION;
return (
<>
<div className="dark hidden h-screen w-full flex-col overflow-x-hidden md:flex">
{!window.isSecureContext && <InsecureConnectionBanner />}
<div className="flex flex-grow flex-col overflow-y-hidden md:flex-row">
<aside className="dark w-full border-r border-mineshaft-600 bg-gradient-to-tr from-mineshaft-700 via-mineshaft-800 to-mineshaft-900 md:w-60">
<nav className="items-between flex h-full flex-col justify-between overflow-y-auto dark:[color-scheme:dark]">
<div className="flex-grow">
<Link to={`/organization/${ProjectType.SecretManager}/overview` as const}>
<div className="my-6 flex cursor-default items-center justify-center pr-2 text-sm text-mineshaft-300 hover:text-mineshaft-100">
<FontAwesomeIcon icon={faArrowLeft} className="pr-3" />
Back to organization
</div>
</Link>
</div>
<div className="relative mt-10 flex w-full cursor-default flex-col items-center px-3 text-sm text-mineshaft-400">
{(window.location.origin.includes("https://app.infisical.com") ||
window.location.origin.includes("https://gamma.infisical.com")) && <WishForm />}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<div className="mb-2 w-full pl-5 duration-200 hover:text-mineshaft-200">
<FontAwesomeIcon icon={faQuestion} className="mr-3 px-[0.1rem]" />
Help & Support
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="p-1">
{INFISICAL_SUPPORT_OPTIONS.map(([icon, text, url]) => (
<DropdownMenuItem key={url as string}>
<a
target="_blank"
rel="noopener noreferrer"
href={String(url)}
className="flex w-full items-center rounded-md font-normal text-mineshaft-300 duration-200"
>
<div className="relative flex w-full cursor-pointer select-none items-center justify-start rounded-md">
{icon}
<div className="text-sm">{text}</div>
</div>
</a>
</DropdownMenuItem>
))}
{infisicalPlatformVersion && (
<div className="mb-2 mt-2 w-full cursor-default pl-5 text-sm duration-200 hover:text-mineshaft-200">
<FontAwesomeIcon icon={faInfo} className="mr-4 px-[0.1rem]" />
Version: {infisicalPlatformVersion}
</div>
)}
</DropdownMenuContent>
</DropdownMenu>
</div>
)
</nav>
</aside>
<main className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 dark:[color-scheme:dark]">
<Outlet />
</main>
</div>
</div>
<div className="z-[200] flex h-screen w-screen flex-col items-center justify-center bg-bunker-800 md:hidden">
<FontAwesomeIcon icon={faMobile} className="mb-8 text-7xl text-gray-300" />
<p className="max-w-sm px-6 text-center text-lg text-gray-200">
{` ${t("common.no-mobile")} `}
</p>
</div>
</>
);
};

View File

@@ -0,0 +1 @@
export { AdminLayout } from "./AdminLayout";

View File

@@ -92,7 +92,7 @@ export const OrganizationLayout = () => {
</MenuItem>
)}
</Link>
<Link to="/organization/members">
<Link to="/organization/access-management">
{({ isActive }) => (
<MenuItem isSelected={isActive} icon="system-outline-96-groups">
Access Control

View File

@@ -62,7 +62,7 @@ export const SidebarFooter = () => {
{(window.location.origin.includes("https://app.infisical.com") ||
window.location.origin.includes("https://gamma.infisical.com")) && <WishForm />}
<Link
to="/organization/members"
to="/organization/access-management"
search={{
action: "invite"
}}

View File

@@ -134,7 +134,7 @@ export const ProjectLayout = () => {
</Link>
)}
<Link
to={`/${currentWorkspace.type}/$projectId/access` as const}
to={`/${currentWorkspace.type}/$projectId/access-management` as const}
params={{
projectId: currentWorkspace.id
}}

View File

@@ -42,7 +42,7 @@ export const SidebarHeader = ({ onChangeOrg }: Prop) => {
return (
<div className="flex h-12 cursor-default items-center px-3 pt-6">
<Link to={`/organization/$${currentWorkspace.type}/overview` as const}>
<Link to={`/organization/${currentWorkspace.type}/overview` as const}>
<div className="pl-1 pr-2 text-mineshaft-400 duration-200 hover:text-mineshaft-100">
<FontAwesomeIcon icon={faArrowLeft} />
</div>

View File

@@ -0,0 +1,357 @@
import { Helmet } from "react-helmet";
import { Controller, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { faAt } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { createNotification } from "@app/components/notifications";
import {
Button,
ContentLoader,
FormControl,
Input,
Select,
SelectClear,
SelectItem,
Switch,
Tab,
TabList,
TabPanel,
Tabs
} from "@app/components/v2";
import { useServerConfig, useUser } from "@app/context";
import {
useGetOrganizations,
useGetServerRootKmsEncryptionDetails,
useUpdateServerConfig
} from "@app/hooks/api";
import { AuthPanel } from "./components/AuthPanel";
import { EncryptionPanel } from "./components/EncryptionPanel";
import { IntegrationPanel } from "./components/IntegrationPanel";
import { RateLimitPanel } from "./components/RateLimitPanel";
import { UserPanel } from "./components/UserPanel";
enum TabSections {
Settings = "settings",
Encryption = "encryption",
Auth = "auth",
RateLimit = "rate-limit",
Integrations = "integrations",
Users = "users"
}
enum SignUpModes {
Disabled = "disabled",
Anyone = "anyone"
}
const formSchema = z.object({
signUpMode: z.nativeEnum(SignUpModes),
allowedSignUpDomain: z.string().optional().nullable(),
trustSamlEmails: z.boolean(),
trustLdapEmails: z.boolean(),
trustOidcEmails: z.boolean(),
defaultAuthOrgId: z.string()
});
type TDashboardForm = z.infer<typeof formSchema>;
export const OverviewPage = () => {
const { t } = useTranslation();
const data = useServerConfig();
const { data: serverRootKmsDetails } = useGetServerRootKmsEncryptionDetails();
const { config } = data;
const {
control,
handleSubmit,
watch,
formState: { isSubmitting, isDirty }
} = useForm<TDashboardForm>({
resolver: zodResolver(formSchema),
values: {
// eslint-disable-next-line
signUpMode: config.allowSignUp ? SignUpModes.Anyone : SignUpModes.Disabled,
allowedSignUpDomain: config.allowedSignUpDomain,
trustSamlEmails: config.trustSamlEmails,
trustLdapEmails: config.trustLdapEmails,
trustOidcEmails: config.trustOidcEmails,
defaultAuthOrgId: config.defaultAuthOrgId ?? ""
}
});
const signUpMode = watch("signUpMode");
const defaultAuthOrgId = watch("defaultAuthOrgId");
const { user } = useUser();
const { mutateAsync: updateServerConfig } = useUpdateServerConfig();
const organizations = useGetOrganizations();
const isNotAllowed = !user?.superAdmin;
const onFormSubmit = async (formData: TDashboardForm) => {
try {
const { allowedSignUpDomain, trustSamlEmails, trustLdapEmails, trustOidcEmails } = formData;
await updateServerConfig({
defaultAuthOrgId: defaultAuthOrgId || null,
allowSignUp: signUpMode !== SignUpModes.Disabled,
allowedSignUpDomain: signUpMode === SignUpModes.Anyone ? allowedSignUpDomain : null,
trustSamlEmails,
trustLdapEmails,
trustOidcEmails
});
createNotification({
text: "Successfully changed sign up setting.",
type: "success"
});
} catch (e) {
console.error(e);
createNotification({
type: "error",
text: "Failed to update sign up setting."
});
}
};
return (
<>
<Helmet>
<title>{t("common.head-title", { title: t("admin.dashboard") })}</title>
<link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" />
<meta property="og:title" content={t("admin.dashboard.og-title") ?? ""} />
<meta name="og:description" content={t("admin.dashboard.og-description") ?? ""} />
</Helmet>
<div className="h-full">
<div className="container mx-auto max-w-7xl px-4 pb-12 text-white dark:[color-scheme:dark]">
<div className="mx-auto mb-6 w-full max-w-7xl pt-6">
<div className="mb-8 flex flex-col items-start justify-between text-xl">
<h1 className="text-3xl font-semibold">Server Admin Console</h1>
<p className="text-base text-bunker-300">
Manage your instance level configurations.
</p>
</div>
</div>
{isNotAllowed ? (
<ContentLoader text={isNotAllowed ? "Redirecting to org page..." : undefined} />
) : (
<div>
<Tabs defaultValue={TabSections.Settings}>
<TabList>
<div className="flex w-full flex-row border-b border-mineshaft-600">
<Tab value={TabSections.Settings}>General</Tab>
<Tab value={TabSections.Encryption}>Encryption</Tab>
<Tab value={TabSections.Auth}>Authentication</Tab>
<Tab value={TabSections.RateLimit}>Rate Limit</Tab>
<Tab value={TabSections.Integrations}>Integrations</Tab>
<Tab value={TabSections.Users}>Users</Tab>
</div>
</TabList>
<TabPanel value={TabSections.Settings}>
<form
className="mb-6 space-y-8 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
onSubmit={handleSubmit(onFormSubmit)}
>
<div className="flex flex-col justify-start">
<div className="mb-2 text-xl font-semibold text-mineshaft-100">
Allow user signups
</div>
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
Select if you want users to be able to signup freely into your Infisical
instance.
</div>
<Controller
control={control}
name="signUpMode"
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
className="max-w-sm"
errorText={error?.message}
isError={Boolean(error)}
>
<Select
className="w-full bg-mineshaft-700"
dropdownContainerClassName="bg-mineshaft-800"
defaultValue={field.value}
onValueChange={(e) => onChange(e)}
{...field}
>
<SelectItem value={SignUpModes.Disabled}>Disabled</SelectItem>
<SelectItem value={SignUpModes.Anyone}>Anyone</SelectItem>
</Select>
</FormControl>
)}
/>
</div>
{signUpMode === "anyone" && (
<div className="flex flex-col justify-start">
<div className="mb-4 text-xl font-semibold text-mineshaft-100">
Restrict signup by email domain(s)
</div>
<Controller
control={control}
defaultValue=""
name="allowedSignUpDomain"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Leave blank to allow any email domains"
className="w-72"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || ""}
placeholder="gmail.com, aws.com, redhat.com"
leftIcon={<FontAwesomeIcon icon={faAt} />}
/>
</FormControl>
)}
/>
</div>
)}
<div className="flex flex-col justify-start">
<div className="mb-2 text-xl font-semibold text-mineshaft-100">
Default organization
</div>
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
Select the default organization you want to set for SAML/LDAP/OIDC based
logins. When selected, user logins will be automatically scoped to the
selected organization.
</div>
<Controller
control={control}
name="defaultAuthOrgId"
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
className="max-w-sm"
errorText={error?.message}
isError={Boolean(error)}
>
<Select
placeholder="Allow all organizations"
className="w-full bg-mineshaft-700"
dropdownContainerClassName="bg-mineshaft-800"
defaultValue={field.value ?? " "}
onValueChange={(e) => onChange(e)}
{...field}
>
<SelectClear
selectValue={defaultAuthOrgId}
onClear={() => {
console.log("clearing");
onChange("");
}}
>
Allow all organizations
</SelectClear>
{organizations.data?.map((org) => (
<SelectItem key={org.id} value={org.id}>
{org.name}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
</div>
<div className="flex flex-col justify-start">
<div className="mb-2 text-xl font-semibold text-mineshaft-100">
Trust emails
</div>
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
Select if you want Infisical to trust external emails from SAML/LDAP/OIDC
identity providers. If set to false, then Infisical will prompt
SAML/LDAP/OIDC provisioned users to verify their email upon their first
login.
</div>
<Controller
control={control}
name="trustSamlEmails"
render={({ field, fieldState: { error } }) => {
return (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Switch
id="trust-saml-emails"
onCheckedChange={(value) => field.onChange(value)}
isChecked={field.value}
>
<p className="w-full">Trust SAML emails</p>
</Switch>
</FormControl>
);
}}
/>
<Controller
control={control}
name="trustLdapEmails"
render={({ field, fieldState: { error } }) => {
return (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Switch
id="trust-ldap-emails"
onCheckedChange={(value) => field.onChange(value)}
isChecked={field.value}
>
<p className="w-full">Trust LDAP emails</p>
</Switch>
</FormControl>
);
}}
/>
<Controller
control={control}
name="trustOidcEmails"
render={({ field, fieldState: { error } }) => {
return (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Switch
id="trust-oidc-emails"
onCheckedChange={(value) => field.onChange(value)}
isChecked={field.value}
>
<p className="w-full">Trust OIDC emails</p>
</Switch>
</FormControl>
);
}}
/>
</div>
<Button
type="submit"
isLoading={isSubmitting}
isDisabled={isSubmitting || !isDirty}
>
Save
</Button>
</form>
</TabPanel>
<TabPanel value={TabSections.Encryption}>
<EncryptionPanel rootKmsDetails={serverRootKmsDetails} />
</TabPanel>
<TabPanel value={TabSections.Auth}>
<AuthPanel />
</TabPanel>
<TabPanel value={TabSections.RateLimit}>
<RateLimitPanel />
</TabPanel>
<TabPanel value={TabSections.Integrations}>
<IntegrationPanel />
</TabPanel>
<TabPanel value={TabSections.Users}>
<UserPanel />
</TabPanel>
</Tabs>
</div>
)}
</div>
</div>
</>
);
};

View File

@@ -0,0 +1,252 @@
import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { createNotification } from "@app/components/notifications";
import { Button, FormControl, Switch } from "@app/components/v2";
import { useServerConfig } from "@app/context";
import { useUpdateServerConfig } from "@app/hooks/api";
import { LoginMethod } from "@app/hooks/api/admin/types";
const formSchema = z.object({
isEmailEnabled: z.boolean(),
isGoogleEnabled: z.boolean(),
isGithubEnabled: z.boolean(),
isGitlabEnabled: z.boolean(),
isSamlEnabled: z.boolean(),
isLdapEnabled: z.boolean(),
isOidcEnabled: z.boolean()
});
type TAuthForm = z.infer<typeof formSchema>;
export const AuthPanel = () => {
const { config } = useServerConfig();
const { enabledLoginMethods } = config;
const { mutateAsync: updateServerConfig } = useUpdateServerConfig();
const {
control,
handleSubmit,
formState: { isSubmitting, isDirty }
} = useForm<TAuthForm>({
resolver: zodResolver(formSchema),
// if not yet explicitly defined by the admin, all login methods should be enabled by default
values: enabledLoginMethods
? {
isEmailEnabled: enabledLoginMethods.includes(LoginMethod.EMAIL),
isGoogleEnabled: enabledLoginMethods.includes(LoginMethod.GOOGLE),
isGithubEnabled: enabledLoginMethods.includes(LoginMethod.GITHUB),
isGitlabEnabled: enabledLoginMethods.includes(LoginMethod.GITLAB),
isSamlEnabled: enabledLoginMethods.includes(LoginMethod.SAML),
isLdapEnabled: enabledLoginMethods.includes(LoginMethod.LDAP),
isOidcEnabled: enabledLoginMethods.includes(LoginMethod.OIDC)
}
: {
isEmailEnabled: true,
isGoogleEnabled: true,
isGithubEnabled: true,
isGitlabEnabled: true,
isSamlEnabled: true,
isLdapEnabled: true,
isOidcEnabled: true
}
});
const onAuthFormSubmit = async (formData: TAuthForm) => {
try {
const enabledMethods: LoginMethod[] = [];
if (formData.isEmailEnabled) {
enabledMethods.push(LoginMethod.EMAIL);
}
if (formData.isGoogleEnabled) {
enabledMethods.push(LoginMethod.GOOGLE);
}
if (formData.isGithubEnabled) {
enabledMethods.push(LoginMethod.GITHUB);
}
if (formData.isGitlabEnabled) {
enabledMethods.push(LoginMethod.GITLAB);
}
if (formData.isSamlEnabled) {
enabledMethods.push(LoginMethod.SAML);
}
if (formData.isLdapEnabled) {
enabledMethods.push(LoginMethod.LDAP);
}
if (formData.isOidcEnabled) {
enabledMethods.push(LoginMethod.OIDC);
}
if (!enabledMethods.length) {
createNotification({
type: "error",
text: "At least one login method should be enabled."
});
return;
}
await updateServerConfig({
enabledLoginMethods: enabledMethods
});
createNotification({
text: "Login methods have been successfully updated.",
type: "success"
});
} catch (e) {
console.error(e);
createNotification({
type: "error",
text: "Failed to update login methods."
});
}
};
return (
<form
className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
onSubmit={handleSubmit(onAuthFormSubmit)}
>
<div className="flex flex-col justify-start">
<div className="mb-2 text-xl font-semibold text-mineshaft-100">Login Methods</div>
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
Select the login methods you wish to allow for all users of this instance.
</div>
<Controller
control={control}
name="isEmailEnabled"
render={({ field, fieldState: { error } }) => {
return (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Switch
id="email-enabled"
onCheckedChange={(value) => field.onChange(value)}
isChecked={field.value}
>
<p className="w-24">Email</p>
</Switch>
</FormControl>
);
}}
/>
<Controller
control={control}
name="isGoogleEnabled"
render={({ field, fieldState: { error } }) => {
return (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Switch
id="google-enabled"
onCheckedChange={(value) => field.onChange(value)}
isChecked={field.value}
>
<p className="w-24">Google SSO</p>
</Switch>
</FormControl>
);
}}
/>
<Controller
control={control}
name="isGithubEnabled"
render={({ field, fieldState: { error } }) => {
return (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Switch
id="enable-github"
onCheckedChange={(value) => field.onChange(value)}
isChecked={field.value}
>
<p className="w-24">Github SSO</p>
</Switch>
</FormControl>
);
}}
/>
<Controller
control={control}
name="isGitlabEnabled"
render={({ field, fieldState: { error } }) => {
return (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Switch
id="enable-gitlab"
onCheckedChange={(value) => field.onChange(value)}
isChecked={field.value}
>
<p className="w-24">Gitlab SSO</p>
</Switch>
</FormControl>
);
}}
/>
<Controller
control={control}
name="isSamlEnabled"
render={({ field, fieldState: { error } }) => {
return (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Switch
id="enable-saml"
onCheckedChange={(value) => field.onChange(value)}
isChecked={field.value}
>
<p className="w-24">SAML SSO</p>
</Switch>
</FormControl>
);
}}
/>
<Controller
control={control}
name="isOidcEnabled"
render={({ field, fieldState: { error } }) => {
return (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Switch
id="enable-oidc"
onCheckedChange={(value) => field.onChange(value)}
isChecked={field.value}
>
<p className="w-24">OIDC SSO</p>
</Switch>
</FormControl>
);
}}
/>
</div>
<Controller
control={control}
name="isLdapEnabled"
render={({ field, fieldState: { error } }) => {
return (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Switch
id="enable-ldap"
onCheckedChange={(value) => field.onChange(value)}
isChecked={field.value}
>
<p className="w-24">LDAP</p>
</Switch>
</FormControl>
);
}}
/>
<Button
className="mt-2"
type="submit"
isLoading={isSubmitting}
isDisabled={isSubmitting || !isDirty}
>
Save
</Button>
</form>
);
};

View File

@@ -0,0 +1,138 @@
import { useCallback } from "react";
import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { createNotification } from "@app/components/notifications";
import { Button, FormControl, Select, SelectItem } from "@app/components/v2";
import { useSubscription } from "@app/context";
import { usePopUp } from "@app/hooks";
import { useUpdateServerEncryptionStrategy } from "@app/hooks/api";
import {
RootKeyEncryptionStrategy,
TGetServerRootKmsEncryptionDetails
} from "@app/hooks/api/admin/types";
const formSchema = z.object({
encryptionStrategy: z.nativeEnum(RootKeyEncryptionStrategy)
});
const strategies: Record<RootKeyEncryptionStrategy, string> = {
[RootKeyEncryptionStrategy.Software]: "Software-based Encryption",
[RootKeyEncryptionStrategy.HSM]: "Hardware Security Module (HSM)"
};
type TForm = z.infer<typeof formSchema>;
type Props = {
rootKmsDetails?: TGetServerRootKmsEncryptionDetails;
};
export const EncryptionPanel = ({ rootKmsDetails }: Props) => {
const { mutateAsync: updateEncryptionStrategy } = useUpdateServerEncryptionStrategy();
const { subscription } = useSubscription();
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
const {
control,
handleSubmit,
formState: { isSubmitting, isDirty }
} = useForm<TForm>({
resolver: zodResolver(formSchema),
values: {
encryptionStrategy:
rootKmsDetails?.strategies?.find((s) => s.enabled)?.strategy ??
RootKeyEncryptionStrategy.Software
}
});
const onSubmit = useCallback(async (formData: TForm) => {
if (!subscription) return;
if (!subscription.hsm) {
handlePopUpOpen("upgradePlan", {
description: "Hardware Security Module's (HSM's), are only available on Enterprise plans."
});
return;
}
try {
await updateEncryptionStrategy(formData.encryptionStrategy);
createNotification({
type: "success",
text: "Encryption strategy updated successfully"
});
} catch {
createNotification({
type: "error",
text: "Failed to update encryption strategy"
});
}
}, []);
return (
<>
<form
className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
onSubmit={handleSubmit(onSubmit)}
>
<div className="flex flex-col justify-start">
<div className="flex w-full justify-between">
<div className="mb-2 text-xl font-semibold text-mineshaft-100">
KMS Encryption Strategy
</div>
</div>
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
Select which type of encryption strategy you want to use for your KMS root key. HSM is
supported on Enterprise plans.
</div>
{!!rootKmsDetails && (
<Controller
control={control}
name="encryptionStrategy"
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
className="max-w-sm"
errorText={error?.message}
isError={Boolean(error)}
>
<Select
className="w-full bg-mineshaft-700"
dropdownContainerClassName="bg-mineshaft-800"
defaultValue={field.value}
onValueChange={(e) => onChange(e)}
{...field}
>
{rootKmsDetails.strategies?.map((strategy) => (
<SelectItem key={strategy.strategy} value={strategy.strategy}>
{strategies[strategy.strategy]}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
)}
</div>
<Button
className="mt-2"
type="submit"
isLoading={isSubmitting}
isDisabled={isSubmitting || !isDirty}
>
Save
</Button>
</form>
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text={(popUp.upgradePlan?.data as { description: string })?.description}
/>
</>
);
};

View File

@@ -0,0 +1,166 @@
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { createNotification } from "@app/components/notifications";
import { Button, FormControl, Input } from "@app/components/v2";
import { useToggle } from "@app/hooks";
import { useGetAdminSlackConfig, useUpdateServerConfig } from "@app/hooks/api";
const slackFormSchema = z.object({
clientId: z.string(),
clientSecret: z.string()
});
type TSlackForm = z.infer<typeof slackFormSchema>;
const getCustomSlackAppCreationUrl = () =>
`https://api.slack.com/apps?new_app=1&manifest_json=${encodeURIComponent(
JSON.stringify({
display_information: {
name: "Infisical",
description: "Get real-time Infisical updates in Slack",
background_color: "#c2d62b",
long_description: `This Slack application is designed specifically for use with your self-hosted Infisical instance, allowing seamless integration between your Infisical projects and your Slack workspace. With this integration, your team can stay up-to-date with the latest events, changes, and notifications directly inside Slack.
- Notifications: Receive real-time updates and alerts about critical events in your Infisical projects. Whether it's a new project being created, updates to secrets, or changes to your team's configuration, you will be promptly notified within the designated Slack channels of your choice.
- Customization: Tailor the notifications to your team's specific needs by configuring which types of events trigger alerts and in which channels they are sent.
- Collaboration: Keep your entire team in the loop with notifications that help facilitate more efficient collaboration by ensuring that everyone is aware of important developments in your Infisical projects.
By integrating Infisical with Slack, you can enhance your workflow by combining the power of secure secrets management with the communication capabilities of Slack.`
},
features: {
app_home: {
home_tab_enabled: false,
messages_tab_enabled: false,
messages_tab_read_only_enabled: true
},
bot_user: {
display_name: "Infisical",
always_online: true
}
},
oauth_config: {
redirect_urls: [`${window.origin}/api/v1/workflow-integrations/slack/oauth_redirect`],
scopes: {
bot: ["chat:write.public", "chat:write", "channels:read", "groups:read"]
}
},
settings: {
org_deploy_enabled: false,
socket_mode_enabled: false,
token_rotation_enabled: false
}
})
)}`;
export const IntegrationPanel = () => {
const {
control,
handleSubmit,
setValue,
formState: { isSubmitting, isDirty }
} = useForm<TSlackForm>({
resolver: zodResolver(slackFormSchema)
});
const { data: adminSlackConfig } = useGetAdminSlackConfig();
const { mutateAsync: updateAdminServerConfig } = useUpdateServerConfig();
const [isSlackClientIdFocused, setIsSlackClientIdFocused] = useToggle();
const [isSlackClientSecretFocused, setIsSlackClientSecretFocused] = useToggle();
useEffect(() => {
if (adminSlackConfig) {
setValue("clientId", adminSlackConfig.clientId);
setValue("clientSecret", adminSlackConfig.clientSecret);
}
}, [adminSlackConfig]);
const onSlackFormSubmit = async (data: TSlackForm) => {
await updateAdminServerConfig({
slackClientId: data.clientId,
slackClientSecret: data.clientSecret
});
createNotification({
text: "Updated admin slack configuration",
type: "success"
});
};
return (
<form
className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
onSubmit={handleSubmit(onSlackFormSubmit)}
>
<div className="flex flex-col justify-start">
<div className="mb-2 text-xl font-semibold text-mineshaft-100">Slack Integration</div>
<div className="mb-4 max-w-lg text-sm text-mineshaft-300">
Step 1: Create your Infisical Slack App
</div>
<div className="mb-6">
<Button
colorSchema="secondary"
onClick={() => window.open(getCustomSlackAppCreationUrl())}
>
Create Slack App
</Button>
</div>
<div className="mb-4 max-w-lg text-sm text-mineshaft-300">
Step 2: Configure your instance-wide settings to enable integration with Slack. Copy the
values from the App Credentials page of your custom Slack App.
</div>
<Controller
control={control}
name="clientId"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Client ID"
className="w-96"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || ""}
type={isSlackClientIdFocused ? "text" : "password"}
onFocus={() => setIsSlackClientIdFocused.on()}
onBlur={() => setIsSlackClientIdFocused.off()}
onChange={(e) => field.onChange(e.target.value)}
/>
</FormControl>
)}
/>
<Controller
control={control}
name="clientSecret"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Client Secret"
className="w-96"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || ""}
type={isSlackClientSecretFocused ? "text" : "password"}
onFocus={() => setIsSlackClientSecretFocused.on()}
onBlur={() => setIsSlackClientSecretFocused.off()}
onChange={(e) => field.onChange(e.target.value)}
/>
</FormControl>
)}
/>
</div>
<Button
className="mt-2"
type="submit"
isLoading={isSubmitting}
isDisabled={isSubmitting || !isDirty}
>
Save
</Button>
</form>
);
};

View File

@@ -0,0 +1,240 @@
import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { createNotification } from "@app/components/notifications";
import { Button, ContentLoader, FormControl, Input } from "@app/components/v2";
import { useSubscription } from "@app/context";
import { usePopUp } from "@app/hooks";
import { useGetRateLimit, useUpdateRateLimit } from "@app/hooks/api";
const formSchema = z.object({
readRateLimit: z.number(),
writeRateLimit: z.number(),
secretsRateLimit: z.number(),
authRateLimit: z.number(),
inviteUserRateLimit: z.number(),
mfaRateLimit: z.number(),
publicEndpointLimit: z.number()
});
type TRateLimitForm = z.infer<typeof formSchema>;
export const RateLimitPanel = () => {
const { data: rateLimit, isPending } = useGetRateLimit();
const { subscription } = useSubscription();
const { mutateAsync: updateRateLimit } = useUpdateRateLimit();
const { handlePopUpToggle, handlePopUpOpen, popUp } = usePopUp(["upgradePlan"] as const);
const {
control,
handleSubmit,
formState: { isSubmitting, isDirty }
} = useForm<TRateLimitForm>({
resolver: zodResolver(formSchema),
values: {
// eslint-disable-next-line
readRateLimit: rateLimit?.readRateLimit ?? 600,
writeRateLimit: rateLimit?.writeRateLimit ?? 200,
secretsRateLimit: rateLimit?.secretsRateLimit ?? 60,
authRateLimit: rateLimit?.authRateLimit ?? 60,
inviteUserRateLimit: rateLimit?.inviteUserRateLimit ?? 30,
mfaRateLimit: rateLimit?.mfaRateLimit ?? 20,
publicEndpointLimit: rateLimit?.publicEndpointLimit ?? 30
}
});
const onRateLimitFormSubmit = async (formData: TRateLimitForm) => {
try {
if (subscription && !subscription.customRateLimits) {
handlePopUpOpen("upgradePlan");
return;
}
const {
readRateLimit,
writeRateLimit,
secretsRateLimit,
authRateLimit,
inviteUserRateLimit,
mfaRateLimit,
publicEndpointLimit
} = formData;
await updateRateLimit({
readRateLimit,
writeRateLimit,
secretsRateLimit,
authRateLimit,
inviteUserRateLimit,
mfaRateLimit,
publicEndpointLimit
});
createNotification({
text: "Rate limits have been successfully updated. Please allow at least 10 minutes for the changes to take effect.",
type: "success"
});
} catch (e) {
console.error(e);
createNotification({
type: "error",
text: "Failed to update rate limiting setting."
});
}
};
return isPending ? (
<ContentLoader />
) : (
<form
className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
onSubmit={handleSubmit(onRateLimitFormSubmit)}
>
<div className="mb-8 flex flex-col justify-start">
<div className="mb-4 text-xl font-semibold text-mineshaft-100">Configure rate limits</div>
<Controller
control={control}
name="readRateLimit"
defaultValue={300}
render={({ field, fieldState: { error } }) => (
<FormControl
label="Global read requests per minute"
className="w-72"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
value={field.value}
onChange={(e) => field.onChange(Number(e.target.value))}
/>
</FormControl>
)}
/>
<Controller
control={control}
defaultValue={300}
name="writeRateLimit"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Global write requests per minute"
className="w-72"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || ""}
onChange={(e) => field.onChange(Number(e.target.value))}
/>
</FormControl>
)}
/>
<Controller
control={control}
defaultValue={300}
name="secretsRateLimit"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Secret requests per minute"
className="w-72"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || ""}
onChange={(e) => field.onChange(Number(e.target.value))}
/>
</FormControl>
)}
/>
<Controller
control={control}
defaultValue={300}
name="authRateLimit"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Auth requests per minute"
className="w-72"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || ""}
onChange={(e) => field.onChange(Number(e.target.value))}
/>
</FormControl>
)}
/>
<Controller
control={control}
defaultValue={300}
name="inviteUserRateLimit"
render={({ field, fieldState: { error } }) => (
<FormControl
label="User invitation requests per minute"
className="w-72"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || ""}
onChange={(e) => field.onChange(Number(e.target.value))}
/>
</FormControl>
)}
/>
<Controller
control={control}
defaultValue={300}
name="mfaRateLimit"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Multi factor auth requests per minute"
className="w-72"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || ""}
onChange={(e) => field.onChange(Number(e.target.value))}
/>
</FormControl>
)}
/>
<Controller
control={control}
defaultValue={300}
name="publicEndpointLimit"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Secret sharing requests per minute"
className="w-72"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || ""}
onChange={(e) => field.onChange(Number(e.target.value))}
/>
</FormControl>
)}
/>
</div>
<Button type="submit" isLoading={isSubmitting} isDisabled={isSubmitting || !isDirty}>
Save
</Button>
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text="You can configure custom rate limits if you switch to Infisical's Enterprise plan."
/>
</form>
);
};

View File

@@ -0,0 +1,183 @@
import { useState } from "react";
import { faMagnifyingGlass, faUsers, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { createNotification } from "@app/components/notifications";
import {
Badge,
Button,
DeleteActionModal,
EmptyState,
IconButton,
Input,
Table,
TableContainer,
TableSkeleton,
TBody,
Td,
Th,
THead,
Tr
} from "@app/components/v2";
import { useSubscription, useUser } from "@app/context";
import { useDebounce, usePopUp } from "@app/hooks";
import { useAdminDeleteUser, useAdminGetUsers } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp";
const UserPanelTable = ({
handlePopUpOpen
}: {
handlePopUpOpen: (
popUpName: keyof UsePopUpState<["removeUser", "upgradePlan"]>,
data?: {
username: string;
id: string;
}
) => void;
}) => {
const [searchUserFilter, setSearchUserFilter] = useState("");
const { user } = useUser();
const userId = user?.id || "";
const [debounedSearchTerm] = useDebounce(searchUserFilter, 500);
const { subscription } = useSubscription();
const { data, isPending, isFetchingNextPage, hasNextPage, fetchNextPage } = useAdminGetUsers({
limit: 20,
searchTerm: debounedSearchTerm
});
const isEmpty = !isPending && !data?.pages?.[0].length;
return (
<>
<Input
value={searchUserFilter}
onChange={(e) => setSearchUserFilter(e.target.value)}
leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
placeholder="Search users..."
/>
<div className="mt-4">
<TableContainer>
<Table>
<THead>
<Tr>
<Th className="w-5/12">Name</Th>
<Th className="w-5/12">Username</Th>
<Th className="w-5" />
</Tr>
</THead>
<TBody>
{isPending && <TableSkeleton columns={4} innerKey="users" />}
{!isPending &&
data?.pages?.map((users) =>
users.map(({ username, email, firstName, lastName, id, superAdmin }) => {
const name = firstName || lastName ? `${firstName} ${lastName}` : "-";
return (
<Tr key={`user-${id}`} className="w-full">
<Td className="w-5/12">
{name}
{superAdmin && (
<Badge variant="primary" className="ml-2">
Server Admin
</Badge>
)}
</Td>
<Td className="w-5/12">{email}</Td>
<Td>
{userId !== id && (
<div className="flex justify-end">
<IconButton
size="lg"
colorSchema="danger"
variant="plain"
ariaLabel="update"
isDisabled={userId === id}
onClick={() => {
if (!subscription?.instanceUserManagement) {
handlePopUpOpen("upgradePlan");
return;
}
handlePopUpOpen("removeUser", { username, id });
}}
>
<FontAwesomeIcon icon={faXmark} />
</IconButton>
</div>
)}
</Td>
</Tr>
);
})
)}
</TBody>
</Table>
{!isPending && isEmpty && <EmptyState title="No users found" icon={faUsers} />}
</TableContainer>
{!isEmpty && (
<Button
className="mt-4 py-3 text-sm"
isFullWidth
variant="star"
isLoading={isFetchingNextPage}
isDisabled={isFetchingNextPage || !hasNextPage}
onClick={() => fetchNextPage()}
>
{hasNextPage ? "Load More" : "End of list"}
</Button>
)}
</div>
</>
);
};
export const UserPanel = () => {
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
"removeUser",
"upgradePlan"
] as const);
const { mutateAsync: deleteUser } = useAdminDeleteUser();
const handleRemoveUser = async () => {
const { id } = popUp?.removeUser?.data as { id: string; username: string };
try {
await deleteUser(id);
createNotification({
type: "success",
text: "Successfully deleted user"
});
} catch {
createNotification({
type: "error",
text: "Error deleting user"
});
}
handlePopUpClose("removeUser");
};
return (
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-4">
<p className="text-xl font-semibold text-mineshaft-100">Users</p>
</div>
<UserPanelTable handlePopUpOpen={handlePopUpOpen} />
<DeleteActionModal
isOpen={popUp.removeUser.isOpen}
deleteKey="remove"
title={`Are you sure you want to delete User with username ${
(popUp?.removeUser?.data as { id: string; username: string })?.username || ""
}?`}
onChange={(isOpen) => handlePopUpToggle("removeUser", isOpen)}
onDeleteApproved={handleRemoveUser}
/>
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text="Deleting users via Admin UI is only available on Infisical's Pro plan and above."
/>
</div>
);
};

View File

@@ -0,0 +1,7 @@
import { createFileRoute } from "@tanstack/react-router";
import { OverviewPage } from "./OverviewPage";
export const Route = createFileRoute("/_authenticate/admin/_admin-layout/")({
component: OverviewPage
});

View File

@@ -0,0 +1,255 @@
import { useState } from "react";
import { Helmet } from "react-helmet";
import { Controller, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { zodResolver } from "@hookform/resolvers/zod";
import { useNavigate } from "@tanstack/react-router";
import { AnimatePresence, motion } from "framer-motion";
import { z } from "zod";
import { createNotification } from "@app/components/notifications";
import { generateBackupPDFAsync } from "@app/components/utilities/generateBackupPDF";
// TODO(akhilmhdh): rewrite this into module functions in lib
import { saveTokenToLocalStorage } from "@app/components/utilities/saveTokenToLocalStorage";
import SecurityClient from "@app/components/utilities/SecurityClient";
import { Button, ContentLoader, FormControl, Input } from "@app/components/v2";
import { useServerConfig } from "@app/context";
import { useCreateAdminUser, useSelectOrganization } from "@app/hooks/api";
import { generateUserBackupKey, generateUserPassKey } from "@app/lib/crypto";
import { DownloadBackupKeys } from "./components/DownloadBackupKeys";
const formSchema = z
.object({
email: z.string().email().trim(),
firstName: z.string().trim(),
lastName: z.string().trim().optional(),
password: z.string().trim().min(14).max(100),
confirmPassword: z.string().trim()
})
.refine((data) => data.password === data.confirmPassword, {
message: "Password don't match",
path: ["confirmPassword"]
});
type TFormSchema = z.infer<typeof formSchema>;
enum SignupSteps {
DetailsForm = "details-form",
BackupKey = "backup-key"
}
export const SignUpPage = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const {
control,
handleSubmit,
getValues,
formState: { isSubmitting }
} = useForm<TFormSchema>({
resolver: zodResolver(formSchema)
});
const [step, setStep] = useState(SignupSteps.DetailsForm);
const { config } = useServerConfig();
// TODO(rbr): do this is layout
// useEffect(() => {
// if (config?.initialized) {
// if (isLoggedIn()) {
// navigate.push("/admin");
// } else {
// navigate.push("/login");
// }
// }
// }, []);
const { mutateAsync: createAdminUser } = useCreateAdminUser();
const { mutateAsync: selectOrganization } = useSelectOrganization();
const handleFormSubmit = async ({ email, password, firstName, lastName }: TFormSchema) => {
// avoid multi submission
if (isSubmitting) return;
try {
const { privateKey, ...userPass } = await generateUserPassKey(email, password);
const res = await createAdminUser({
email,
password,
firstName,
lastName,
...userPass
});
SecurityClient.setToken(res.token);
saveTokenToLocalStorage({
publicKey: userPass.publicKey,
encryptedPrivateKey: userPass.encryptedPrivateKey,
iv: userPass.encryptedPrivateKeyIV,
tag: userPass.encryptedPrivateKeyTag,
privateKey
});
await selectOrganization({ organizationId: res.organization.id });
// TODO(akhilmhdh): This is such a confusing pattern and too unreliable
// Will be refactored in next iteration to make it url based rather than local storage ones
// Part of migration to nextjs 14
localStorage.setItem("orgData.id", res.organization.id);
setStep(SignupSteps.BackupKey);
} catch (err) {
console.log(err);
createNotification({
type: "error",
text: "Failed to create admin"
});
}
};
const handleBackupKeyGenerate = async () => {
try {
const { email, password, firstName, lastName } = getValues();
const generatedKey = await generateUserBackupKey(email, password);
await generateBackupPDFAsync({
generatedKey,
personalEmail: email,
personalName: `${firstName} ${lastName}`
});
navigate({ to: "/admin" });
} catch (err) {
console.log(err);
createNotification({
type: "error",
text: "Failed to generate backup"
});
}
};
if (config?.initialized && step === SignupSteps.DetailsForm)
return <ContentLoader text="Redirecting to admin page..." />;
return (
<div className="flex max-h-screen min-h-screen flex-col justify-center overflow-y-auto bg-gradient-to-tr from-mineshaft-600 via-mineshaft-800 to-bunker-700 px-6">
<Helmet>
<title>{t("common.head-title", { title: t("signup.title") })}</title>
<link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" />
<meta property="og:title" content={t("signup.og-title") ?? ""} />
<meta name="og:description" content={t("signup.og-description") ?? ""} />
</Helmet>
<div className="flex items-center justify-center">
<AnimatePresence mode="wait">
{step === SignupSteps.DetailsForm && (
<motion.div
className="text-mineshaft-200"
key="panel-1"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<div className="flex flex-col items-center space-y-2 text-center">
<img src="/images/gradientLogo.svg" height={90} width={120} alt="Infisical logo" />
<div className="pt-4 text-4xl">Welcome to Infisical</div>
<div className="pb-4 text-bunker-300">Create your first Super Admin Account</div>
</div>
<form onSubmit={handleSubmit(handleFormSubmit)}>
<div className="mt-8">
<div className="flex items-center space-x-4">
<Controller
control={control}
name="firstName"
render={({ field, fieldState: { error } }) => (
<FormControl
label="First name"
errorText={error?.message}
isError={Boolean(error)}
>
<Input isFullWidth size="md" {...field} />
</FormControl>
)}
/>
<Controller
control={control}
name="lastName"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Last name"
errorText={error?.message}
isError={Boolean(error)}
>
<Input isFullWidth size="md" {...field} />
</FormControl>
)}
/>
</div>
<Controller
control={control}
name="email"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Email"
errorText={error?.message}
isError={Boolean(error)}
>
<Input isFullWidth size="md" {...field} />
</FormControl>
)}
/>
<Controller
control={control}
name="password"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Password"
errorText={error?.message}
isError={Boolean(error)}
>
<Input isFullWidth size="md" type="password" {...field} />
</FormControl>
)}
/>
<Controller
control={control}
name="confirmPassword"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Confirm password"
errorText={error?.message}
isError={Boolean(error)}
>
<Input isFullWidth size="md" type="password" {...field} />
</FormControl>
)}
/>
</div>
<Button
type="submit"
colorSchema="primary"
variant="outline_bg"
isFullWidth
className="mt-4"
isLoading={isSubmitting}
>
Continue
</Button>
</form>
</motion.div>
)}
{step === SignupSteps.BackupKey && (
<motion.div
className="text-mineshaft-200"
key="panel-2"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<DownloadBackupKeys onGenerate={handleBackupKeyGenerate} />
</motion.div>
)}
</AnimatePresence>
</div>
</div>
);
};

View File

@@ -0,0 +1,56 @@
import { useTranslation } from "react-i18next";
import { faWarning } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Button } from "@app/components/v2";
import { useToggle } from "@app/hooks";
type Props = {
onGenerate: () => Promise<void>;
};
export const DownloadBackupKeys = ({ onGenerate }: Props): JSX.Element => {
const { t } = useTranslation();
const [isLoading, setIsLoading] = useToggle();
return (
<div className="mx-auto mb-36 flex h-full w-full flex-col items-center md:mb-16 md:px-6">
<p className="flex flex-col items-center justify-center bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-center text-xl font-medium text-transparent">
<FontAwesomeIcon
icon={faWarning}
className="mb-6 ml-2 mr-3 pt-1 text-6xl text-bunker-200"
/>
{t("signup.step4-message")}
</p>
<div className="text-md mt-8 flex w-full max-w-md flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 pb-2 text-center text-bunker-300 md:min-w-[24rem] lg:w-1/6">
<div className="m-2 mx-auto mt-4 flex w-full flex-row items-center rounded-md px-3 text-center text-bunker-300 md:mt-8 md:min-w-[23rem] lg:w-1/6">
<span className="mb-2">
{t("signup.step4-description1")} {t("signup.step4-description3")}
</span>
</div>
<div className="mx-auto mb-2 mt-2 flex w-full flex-col items-center justify-center px-3 text-center text-sm md:mb-4 md:mt-4 md:min-w-[20rem] md:max-w-md md:text-left lg:w-1/6">
<div className="text-l w-full py-1 text-lg">
<Button
onClick={async () => {
try {
setIsLoading.on();
await onGenerate();
} finally {
setIsLoading.off();
}
}}
size="sm"
isFullWidth
className="h-12"
colorSchema="primary"
variant="outline_bg"
isLoading={isLoading}
>
Download PDF
</Button>
</div>
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1 @@
export { DownloadBackupKeys } from "./DownloadBackupKeys";

View File

@@ -0,0 +1,14 @@
import { createFileRoute, redirect } from "@tanstack/react-router";
import { SignUpPage } from "./SignUpPage";
export const Route = createFileRoute("/_restrict-login-signup/admin/signup")({
component: SignUpPage,
beforeLoad: ({ context }) => {
if (context.serverConfig.initialized) {
throw redirect({
to: "/login"
});
}
}
});

View File

@@ -0,0 +1,14 @@
import { createFileRoute, redirect } from "@tanstack/react-router";
import { AdminLayout } from "@app/layouts/AdminLayout";
export const Route = createFileRoute("/_authenticate/admin/_admin-layout")({
component: AdminLayout,
beforeLoad: ({ context }) => {
if (!context.user.superAdmin) {
throw redirect({
to: "/organization/secret-manager/overview"
});
}
}
});

View File

@@ -2,6 +2,6 @@ import { createFileRoute } from "@tanstack/react-router";
import { CliRedirectPage } from "./CliRedirectPage";
export const Route = createFileRoute("/_authenticate/cli-redirect")({
export const Route = createFileRoute("/_restrict-login-signup/cli-redirect")({
component: CliRedirectPage
});

View File

@@ -1,4 +1,5 @@
import { createFileRoute } from "@tanstack/react-router";
import { EmailNotVerifiedPage } from "./EmailNotVerifiedPage";
export const Route = createFileRoute("/_restrict-login-signup/email-not-verified")({

View File

@@ -1,7 +1,7 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { LoginPage } from './LoginPage'
import { LoginPage } from "./LoginPage";
export const Route = createFileRoute('/_restrict-login-signup/login/')({
component: LoginPage,
})
export const Route = createFileRoute("/_restrict-login-signup/login/")({
component: LoginPage
});

View File

@@ -4,11 +4,13 @@ import { useTranslation } from "react-i18next";
import { Link, useSearch } from "@tanstack/react-router";
import { jwtDecode } from "jwt-decode";
import { ROUTE_PATHS } from "@app/const/routes";
import { PasswordStep } from "../LoginPage/components";
export const LoginSsoPage = () => {
const { t } = useTranslation();
const search = useSearch({ from: "/_restrict-login-signup/login/sso/" });
const search = useSearch({ from: ROUTE_PATHS.Auth.LoginSSO.id });
const token = search.token as string;
const [step, setStep] = useState(0);
const [password, setPassword] = useState("");

View File

@@ -1,9 +1,7 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { LoginProviderErrorPage } from './ProviderErrorPage'
import { LoginProviderErrorPage } from "./ProviderErrorPage";
export const Route = createFileRoute(
'/_restrict-login-signup/login/provider/error',
)({
component: LoginProviderErrorPage,
})
export const Route = createFileRoute("/_restrict-login-signup/login/provider/error")({
component: LoginProviderErrorPage
});

View File

@@ -1,16 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'
import { z } from 'zod'
import { createFileRoute } from "@tanstack/react-router";
import { zodValidator } from "@tanstack/zod-adapter";
import { z } from "zod";
import { LoginProviderSuccessPage } from './ProviderSuccessPage'
import { LoginProviderSuccessPage } from "./ProviderSuccessPage";
const LoginProviderSuccessQuerySchema = z.object({
token: z.string(),
})
token: z.string()
});
export const Route = createFileRoute(
'/_restrict-login-signup/login/provider/success',
)({
export const Route = createFileRoute("/_restrict-login-signup/login/provider/success")({
component: LoginProviderSuccessPage,
validateSearch: zodValidator(LoginProviderSuccessQuerySchema),
})
validateSearch: zodValidator(LoginProviderSuccessQuerySchema)
});

View File

@@ -1,9 +1,7 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { SelectOrganizationPage } from './SelectOrgPage'
import { SelectOrganizationPage } from "./SelectOrgPage";
export const Route = createFileRoute(
'/_restrict-login-signup/login/select-organization',
)({
component: SelectOrganizationPage,
})
export const Route = createFileRoute("/_restrict-login-signup/login/select-organization")({
component: SelectOrganizationPage
});

View File

@@ -1,14 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'
import { z } from 'zod'
import { createFileRoute } from "@tanstack/react-router";
import { zodValidator } from "@tanstack/zod-adapter";
import { z } from "zod";
import { SignupSsoPage } from './SignUpSsoPage'
import { SignupSsoPage } from "./SignUpSsoPage";
const SignupSSOPageQueryParamsSchema = z.object({
token: z.string(),
})
token: z.string()
});
export const Route = createFileRoute('/_restrict-login-signup/signup/sso')({
export const Route = createFileRoute("/_restrict-login-signup/signup/sso")({
component: SignupSsoPage,
validateSearch: zodValidator(SignupSSOPageQueryParamsSchema),
})
validateSearch: zodValidator(SignupSSOPageQueryParamsSchema)
});

View File

@@ -1,4 +1,5 @@
import { createFileRoute } from "@tanstack/react-router";
import { VerifyEmailPage } from "./VerifyEmailPage";
export const Route = createFileRoute("/_restrict-login-signup/verify-email")({

View File

@@ -63,7 +63,12 @@ const Page = () => {
});
handlePopUpClose("deleteCa");
navigate({ to: `/${ProjectType.CertificateManager}/${projectId}/certificates` as const });
navigate({
to: `/${ProjectType.CertificateManager}/$projectId/overview` as const,
params: {
projectId
}
});
} catch {
createNotification({
text: "Failed to delete CA",
@@ -82,7 +87,10 @@ const Page = () => {
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
onClick={() =>
navigate({
to: `/${ProjectType.CertificateManager}/${projectId}/certificates` as const
to: `/${ProjectType.CertificateManager}/${projectId}/overview` as const,
params: {
projectId
}
})
}
className="mb-4"

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { CertAuthDetailsByIDPage } from './CertAuthDetailsByIDPage'
import { CertAuthDetailsByIDPage } from "./CertAuthDetailsByIDPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/cert-manager/$projectId/_cert-manager-layout/ca/$caId',
"/_authenticate/_inject-org-details/cert-manager/$projectId/_cert-manager-layout/ca/$caId"
)({
component: CertAuthDetailsByIDPage,
})
component: CertAuthDetailsByIDPage
});

View File

@@ -65,7 +65,7 @@ export const ExternalCaInstallForm = ({ caId, handlePopUpToggle }: Props) => {
type: "success"
});
handlePopUpToggle("installCaCert", false);
} catch (err) {
} catch {
createNotification({
text: "Failed to install certificate for CA",
type: "error"

View File

@@ -126,7 +126,7 @@ export const InternalCaInstallForm = ({ caId, handlePopUpToggle }: Props) => {
type: "success"
});
handlePopUpToggle("installCaCert", false);
} catch (err) {
} catch {
createNotification({
text: "Failed to install certificate for CA",
type: "error"

View File

@@ -78,7 +78,11 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
key={`ca-${ca.id}`}
onClick={() =>
navigate({
to: `/${ProjectType.CertificateManager}/${currentWorkspace?.id}/ca/${ca.id}` as const
to: `/${ProjectType.CertificateManager}/$projectId/ca/$caId` as const,
params: {
projectId: currentWorkspace.id,
caId: ca.id
}
})
}
>

View File

@@ -407,7 +407,7 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
errorText={error?.message}
isError={Boolean(error)}
>
<div className="mt-2 mb-7 grid grid-cols-2 gap-2">
<div className="mb-7 mt-2 grid grid-cols-2 gap-2">
{KEY_USAGES_OPTIONS.map(({ label, value: optionValue }) => {
return (
<Checkbox
@@ -441,7 +441,7 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
errorText={error?.message}
isError={Boolean(error)}
>
<div className="mt-2 mb-7 grid grid-cols-2 gap-2">
<div className="mb-7 mt-2 grid grid-cols-2 gap-2">
{EXTENDED_KEY_USAGES_OPTIONS.map(({ label, value: optionValue }) => {
return (
<Checkbox

View File

@@ -76,14 +76,18 @@ export const PkiCollectionModal = ({ popUp, handlePopUpToggle }: Props) => {
});
} else {
// create
const { id: createdId } = await createPkiCollection({
const { id: collectionId } = await createPkiCollection({
name,
description,
projectId
});
navigate({
to: `/${ProjectType.CertificateManager}/${projectId}/pki-collections/${createdId}` as const
to: `/${ProjectType.CertificateManager}/$projectId/pki-collections/$collectionId` as const,
params: {
projectId,
collectionId
}
});
}

View File

@@ -61,7 +61,11 @@ export const PkiCollectionTable = ({ handlePopUpOpen }: Props) => {
key={`pki-collection-${pkiCollection.id}`}
onClick={() =>
navigate({
to: `/${ProjectType.CertificateManager}/${projectId}/pki-collections/${pkiCollection.id}` as const
to: `/${ProjectType.CertificateManager}/$projectId/pki-collections/$collectionId` as const,
params: {
projectId,
collectionId: pkiCollection.id
}
})
}
>

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { CertificatesPage } from './CertificatesPage'
import { CertificatesPage } from "./CertificatesPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/cert-manager/$projectId/_cert-manager-layout/overview',
"/_authenticate/_inject-org-details/cert-manager/$projectId/_cert-manager-layout/overview"
)({
component: CertificatesPage,
})
component: CertificatesPage
});

View File

@@ -58,7 +58,10 @@ export const PkiCollectionPage = () => {
});
handlePopUpClose("deletePkiCollection");
navigate({
to: `/${ProjectType.CertificateManager}/${projectId}/certificates` as const
to: `/${ProjectType.CertificateManager}/$projectId/overview` as const,
params: {
projectId
}
});
} catch {
createNotification({
@@ -78,7 +81,10 @@ export const PkiCollectionPage = () => {
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
onClick={() => {
navigate({
to: `/${ProjectType.CertificateManager}/${projectId}/certificates` as const
to: `/${ProjectType.CertificateManager}/$projectId/overview` as const,
params: {
projectId
}
});
}}
className="mb-4"

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { PkiCollectionDetailsByIDPage } from './PkiCollectionDetailsByIDPage'
import { PkiCollectionDetailsByIDPage } from "./PkiCollectionDetailsByIDPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/cert-manager/$projectId/_cert-manager-layout/pki-collections/$collectionId',
"/_authenticate/_inject-org-details/cert-manager/$projectId/_cert-manager-layout/pki-collections/$collectionId"
)({
component: PkiCollectionDetailsByIDPage,
})
component: PkiCollectionDetailsByIDPage
});

View File

@@ -1,6 +1,2 @@
export { AutoCapitalizationSection } from "./AutoCapitalizationSection";
export { BackfillSecretReferenceSecretion } from "./BackfillSecretReferenceSection";
export { DeleteProjectSection } from "./DeleteProjectSection";
export { EnvironmentSection } from "./EnvironmentSection";
export { ProjectOverviewChangeSection } from "./ProjectOverviewChangeSection";
export { SecretTagsSection } from "./SecretTagsSection";

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { SettingsPage } from './SettingsPage'
import { SettingsPage } from "./SettingsPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/cert-manager/$projectId/_cert-manager-layout/settings',
"/_authenticate/_inject-org-details/cert-manager/$projectId/_cert-manager-layout/settings"
)({
component: SettingsPage,
})
component: SettingsPage
});

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { OverviewPage } from './OverviewPage'
import { OverviewPage } from "./OverviewPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/kms/$projectId/_kms-layout/overview',
"/_authenticate/_inject-org-details/kms/$projectId/_kms-layout/overview"
)({
component: OverviewPage,
})
component: OverviewPage
});

View File

@@ -1,6 +1,2 @@
export { AutoCapitalizationSection } from "./AutoCapitalizationSection";
export { BackfillSecretReferenceSecretion } from "./BackfillSecretReferenceSection";
export { DeleteProjectSection } from "./DeleteProjectSection";
export { EnvironmentSection } from "./EnvironmentSection";
export { ProjectOverviewChangeSection } from "./ProjectOverviewChangeSection";
export { SecretTagsSection } from "./SecretTagsSection";

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { SettingsPage } from './SettingsPage'
import { SettingsPage } from "./SettingsPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/kms/$projectId/_kms-layout/settings',
"/_authenticate/_inject-org-details/kms/$projectId/_kms-layout/settings"
)({
component: SettingsPage,
})
component: SettingsPage
});

View File

@@ -1,29 +1,25 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { workspaceKeys } from '@app/hooks/api'
import {
fetchUserProjectPermissions,
roleQueryKeys,
} from '@app/hooks/api/roles/queries'
import { fetchWorkspaceById } from '@app/hooks/api/workspace/queries'
import { ProjectLayout } from '@app/layouts/ProjectLayout'
import { workspaceKeys } from "@app/hooks/api";
import { fetchUserProjectPermissions, roleQueryKeys } from "@app/hooks/api/roles/queries";
import { fetchWorkspaceById } from "@app/hooks/api/workspace/queries";
import { ProjectLayout } from "@app/layouts/ProjectLayout";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/kms/$projectId/_kms-layout',
"/_authenticate/_inject-org-details/kms/$projectId/_kms-layout"
)({
component: ProjectLayout,
beforeLoad: async ({ params, context }) => {
await context.queryClient.ensureQueryData({
queryKey: workspaceKeys.getWorkspaceById(params.projectId),
queryFn: () => fetchWorkspaceById(params.projectId),
})
queryFn: () => fetchWorkspaceById(params.projectId)
});
await context.queryClient.ensureQueryData({
queryKey: roleQueryKeys.getUserProjectPermissions({
workspaceId: params.projectId,
workspaceId: params.projectId
}),
queryFn: () =>
fetchUserProjectPermissions({ workspaceId: params.projectId }),
})
},
})
queryFn: () => fetchUserProjectPermissions({ workspaceId: params.projectId })
});
}
});

View File

@@ -1,36 +1,27 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import {
fetchOrganizationById,
organizationKeys,
} from '@app/hooks/api/organization/queries'
import {
fetchUserOrgPermissions,
roleQueryKeys,
} from '@app/hooks/api/roles/queries'
import {
fetchOrgSubscription,
subscriptionQueryKeys,
} from '@app/hooks/api/subscriptions/queries'
import { fetchOrganizationById, organizationKeys } from "@app/hooks/api/organization/queries";
import { fetchUserOrgPermissions, roleQueryKeys } from "@app/hooks/api/roles/queries";
import { fetchOrgSubscription, subscriptionQueryKeys } from "@app/hooks/api/subscriptions/queries";
// Route context to fill in organization's data like details, subscription etc
export const Route = createFileRoute('/_authenticate/_inject-org-details')({
export const Route = createFileRoute("/_authenticate/_inject-org-details")({
beforeLoad: async ({ context }) => {
const organizationId = context.organizationId!
const organizationId = context.organizationId!;
await context.queryClient.ensureQueryData({
queryKey: organizationKeys.getOrgById(organizationId),
queryFn: () => fetchOrganizationById(organizationId),
})
queryFn: () => fetchOrganizationById(organizationId)
});
await context.queryClient.ensureQueryData({
queryKey: subscriptionQueryKeys.getOrgSubsription(organizationId),
queryFn: () => fetchOrgSubscription(organizationId),
})
queryFn: () => fetchOrgSubscription(organizationId)
});
await context.queryClient.ensureQueryData({
queryKey: roleQueryKeys.getUserOrgPermissions({ orgId: organizationId }),
queryFn: () => fetchUserOrgPermissions({ orgId: organizationId }),
})
return { organizationId }
},
})
queryFn: () => fetchUserOrgPermissions({ orgId: organizationId })
});
return { organizationId };
}
});

View File

@@ -306,7 +306,7 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLinks }: Pro
className="h-10 w-full cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
onClick={() =>
navigate({
to: "/organization/memberships/$membershipId" as const,
to: "/organization/members/$membershipId" as const,
params: {
membershipId: orgMembershipId
}
@@ -401,7 +401,7 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLinks }: Pro
onClick={(e) => {
e.stopPropagation();
navigate({
to: "/organization/memberships/$membershipId" as const,
to: "/organization/members/$membershipId" as const,
params: {
membershipId: orgMembershipId
}

View File

@@ -34,8 +34,7 @@ import { isCustomOrgRole } from "@app/helpers/roles";
import { usePopUp } from "@app/hooks";
import { useDeleteOrgRole, useGetOrgRoles, useUpdateOrg } from "@app/hooks/api";
import { TOrgRole } from "@app/hooks/api/roles/types";
import { RoleModal } from "../../../roles/$roleId/-components";
import { RoleModal } from "@app/pages/organization/RoleByIDPage/components/RoleModal";
export const OrgRoleTable = () => {
const navigate = useNavigate();

View File

@@ -1,19 +1,19 @@
import { createFileRoute } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'
import { z } from 'zod'
import { createFileRoute } from "@tanstack/react-router";
import { zodValidator } from "@tanstack/zod-adapter";
import { z } from "zod";
import { OrgAccessControlTabSections } from '@app/types/org'
import { OrgAccessControlTabSections } from "@app/types/org";
import { AccessManagementPage } from './AccessManagementPage'
import { AccessManagementPage } from "./AccessManagementPage";
const AccessControlPageQuerySchema = z.object({
selectedTab: z.string().catch(OrgAccessControlTabSections.Member),
action: z.string().catch(''),
})
action: z.string().catch("")
});
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout/access-management',
"/_authenticate/_inject-org-details/organization/_layout/access-management"
)({
component: AccessManagementPage,
validateSearch: zodValidator(AccessControlPageQuerySchema),
})
validateSearch: zodValidator(AccessControlPageQuerySchema)
});

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { AdminPage } from './AdminPage'
import { AdminPage } from "./AdminPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout/admin',
"/_authenticate/_inject-org-details/organization/_layout/admin"
)({
component: AdminPage,
})
component: AdminPage
});

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { AuditLogsPage } from './AuditLogsPage'
import { AuditLogsPage } from "./AuditLogsPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout/audit-logs',
"/_authenticate/_inject-org-details/organization/_layout/audit-logs"
)({
component: AuditLogsPage,
})
component: AuditLogsPage
});

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { BillingPage } from './BillingPage'
import { BillingPage } from "./BillingPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout/billing',
"/_authenticate/_inject-org-details/organization/_layout/billing"
)({
component: BillingPage,
})
component: BillingPage
});

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { CertManagerOverviewPage } from './CertManagerOverviewPage'
import { CertManagerOverviewPage } from "./CertManagerOverviewPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout/cert-manager/overview',
"/_authenticate/_inject-org-details/organization/_layout/cert-manager/overview"
)({
component: CertManagerOverviewPage,
})
component: CertManagerOverviewPage
});

View File

@@ -62,7 +62,7 @@ const Page = () => {
type: "success"
});
navigate({
to: "/organization/members" as const,
to: "/organization/access-management" as const,
search: {
selectedTab: TabSections.Groups
}
@@ -90,7 +90,7 @@ const Page = () => {
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
onClick={() => {
navigate({
to: "/organization/members" as const,
to: "/organization/access-management" as const,
search: {
selectedTab: TabSections.Groups
}

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { GroupDetailsByIDPage } from './GroupDetailsByIDPage'
import { GroupDetailsByIDPage } from "./GroupDetailsByIDPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout/groups/$groupId',
"/_authenticate/_inject-org-details/organization/_layout/groups/$groupId"
)({
component: GroupDetailsByIDPage,
})
component: GroupDetailsByIDPage
});

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { useState } from "react";
import { Helmet } from "react-helmet";
import { useTranslation } from "react-i18next";
@@ -32,6 +31,9 @@ import { Identity } from "@app/hooks/api/identities/types";
import { usePopUp } from "@app/hooks/usePopUp";
import { OrgAccessControlTabSections } from "@app/types/org";
import { IdentityAuthMethodModal } from "../AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityAuthMethodModal";
import { IdentityModal } from "../AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityModal";
import { IdentityUniversalAuthClientSecretModal } from "../AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthClientSecretModal";
import {
IdentityAuthenticationSection,
IdentityClientSecretModal,
@@ -86,7 +88,7 @@ const Page = () => {
handlePopUpClose("deleteIdentity");
navigate({
to: "/organization/members",
to: "/organization/access-management",
search: {
selectedTab: OrgAccessControlTabSections.Identities
}
@@ -170,7 +172,7 @@ const Page = () => {
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
onClick={() => {
navigate({
to: "/organization/members",
to: "/organization/access-management",
search: {
selectedTab: OrgAccessControlTabSections.Identities
}

View File

@@ -59,7 +59,10 @@ export const IdentityProjectRow = ({
onClick={() => {
if (isAccessible) {
navigate({
to: `/${project?.type}/${project.id}/access` as const,
to: `/${project?.type}/$projectId/access-management` as const,
params: {
projectId: project.id
},
search: {
selectedTab: TabSections.Identities
}

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { IdentityDetailsByIDPage } from './IdentityDetailsByIDPage'
import { IdentityDetailsByIDPage } from "./IdentityDetailsByIDPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout/identities/$identityId',
"/_authenticate/_inject-org-details/organization/_layout/identities/$identityId"
)({
component: IdentityDetailsByIDPage,
})
component: IdentityDetailsByIDPage
});

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { KmsOverviewPage } from './KmsOverviewPage'
import { KmsOverviewPage } from "./KmsOverviewPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout/kms/overview',
"/_authenticate/_inject-org-details/organization/_layout/kms/overview"
)({
component: KmsOverviewPage,
})
component: KmsOverviewPage
});

View File

@@ -1,8 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { NoOrgPage } from './NoOrgPage'
import { createFileRoute } from "@tanstack/react-router";
import { NoOrgPage } from "./NoOrgPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout/none',
"/_authenticate/_inject-org-details/organization/_layout/none"
)({
component: NoOrgPage,
})
component: NoOrgPage
});

View File

@@ -56,7 +56,7 @@ export const Page = () => {
handlePopUpClose("deleteOrgRole");
navigate({
to: "/organization/members" as const,
to: "/organization/access-management" as const,
search: {
selectedTab: OrgAccessControlTabSections.Roles
}
@@ -85,7 +85,7 @@ export const Page = () => {
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
onClick={() => {
navigate({
to: "/organization/members" as const,
to: "/organization/access-management" as const,
search: {
selectedTab: OrgAccessControlTabSections.Roles
}

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { RoleByIDPage } from './RoleByIDPage'
import { RoleByIDPage } from "./RoleByIDPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout/roles/$roleId',
"/_authenticate/_inject-org-details/organization/_layout/roles/$roleId"
)({
component: RoleByIDPage,
})
component: RoleByIDPage
});

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { SecretManagerOverviewPage } from './SecretManagerOverviewPage'
import { SecretManagerOverviewPage } from "./SecretManagerOverviewPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout/secret-manager/overview',
"/_authenticate/_inject-org-details/organization/_layout/secret-manager/overview"
)({
component: SecretManagerOverviewPage,
})
component: SecretManagerOverviewPage
});

View File

@@ -4,6 +4,7 @@ import { useSearch } from "@tanstack/react-router";
import { OrgPermissionCan } from "@app/components/permissions";
import { Button, NoticeBanner } from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import {
OrgPermissionActions,
OrgPermissionSubjects,
@@ -22,7 +23,7 @@ import { SecretScanningLogsTable } from "./components";
export const SecretScanningPage = withPermission(
() => {
const queryParams = useSearch({
from: "/_authenticate/_ctx-org-details/organization/_layout-org/secret-scanning/"
from: ROUTE_PATHS.Organization.SecretScanning.id
});
const { config } = useServerConfig();
const { currentOrg } = useOrganization();

View File

@@ -1,17 +1,17 @@
import { createFileRoute } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'
import { z } from 'zod'
import { createFileRoute } from "@tanstack/react-router";
import { zodValidator } from "@tanstack/zod-adapter";
import { z } from "zod";
import { SecretScanningPage } from './SecretScanningPage'
import { SecretScanningPage } from "./SecretScanningPage";
const SecretScanningQueryParams = z.object({
state: z.string().catch(''),
installation_id: z.string().catch(''),
})
state: z.string().catch(""),
installation_id: z.string().catch("")
});
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout/secret-scanning',
"/_authenticate/_inject-org-details/organization/_layout/secret-scanning"
)({
component: SecretScanningPage,
validateSearch: zodValidator(SecretScanningQueryParams),
})
validateSearch: zodValidator(SecretScanningQueryParams)
});

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { SecretSharingPage } from './SecretSharingPage'
import { SecretSharingPage } from "./SecretSharingPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout/secret-sharing',
"/_authenticate/_inject-org-details/organization/_layout/secret-sharing"
)({
component: SecretSharingPage,
})
component: SecretSharingPage
});

View File

@@ -279,7 +279,7 @@ export const LDAPGroupMapModal = ({ popUp, handlePopUpOpen, handlePopUpToggle }:
size="sm"
onClick={() =>
navigate({
to: "/organization/members"
to: "/organization/access-management"
})
}
>

View File

@@ -5,7 +5,7 @@ import { Button, DeleteActionModal } from "@app/components/v2";
import { useOrganization, useOrgPermission } from "@app/context";
import { useDeleteOrgById } from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp";
import { navigateUserToOrg } from "@app/routes/_restrict-login-signup/login/-components/Login.utils";
import { navigateUserToOrg } from "@app/pages/auth/LoginPage/Login.utils";
export const OrgDeleteSection = () => {
const navigate = useNavigate();

View File

@@ -3,6 +3,7 @@ import { useSearch } from "@tanstack/react-router";
import { OrgPermissionCan } from "@app/components/permissions";
import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { AuditLogStreamsTab } from "../AuditLogStreamTab";
@@ -24,7 +25,7 @@ const tabs = [
];
export const OrgTabGroup = () => {
const search = useSearch({
from: "/_authenticate/_ctx-org-details/organization/_layout-org/settings/"
from: ROUTE_PATHS.Organization.SettingsPage.id
});
const [selectedTab, setSelectedTab] = useState(search.selectedTab || tabs[0].key);

View File

@@ -6,22 +6,22 @@ import { twMerge } from "tailwind-merge";
import { z } from "zod";
import { createNotification } from "@app/components/notifications";
import { GeneralPermissionPolicies } from "@app/components/permissions/ProjectRolePermissionsSection/components/GeneralPermissionPolicies";
import { NewPermissionRule } from "@app/components/permissions/ProjectRolePermissionsSection/components/NewPermissionRule";
import { PermissionEmptyState } from "@app/components/permissions/ProjectRolePermissionsSection/PermissionEmptyState";
import {
formRolePermission2API,
PROJECT_PERMISSION_OBJECT,
projectRoleFormSchema,
rolePermission2Form
} from "@app/components/permissions/ProjectRolePermissionsSection/ProjectRoleModifySection.utils";
import { renderConditionalComponents } from "@app/components/permissions/ProjectRolePermissionsSection/RolePermissionsSection";
import { Button, FormControl, Input, Modal, ModalContent, ModalTrigger } from "@app/components/v2";
import { ProjectPermissionSub } from "@app/context";
import { isCustomProjectRole } from "@app/helpers/roles";
import { usePopUp } from "@app/hooks";
import { TProjectTemplate, useUpdateProjectTemplate } from "@app/hooks/api/projectTemplates";
import { slugSchema } from "@app/lib/schemas";
import { GeneralPermissionPolicies } from "@app/pages/project/RoleDetailsBySlugPage/components/GeneralPermissionPolicies";
import { NewPermissionRule } from "@app/pages/project/RoleDetailsBySlugPage/components/NewPermissionRule";
import { PermissionEmptyState } from "@app/pages/project/RoleDetailsBySlugPage/components/PermissionEmptyState";
import {
formRolePermission2API,
PROJECT_PERMISSION_OBJECT,
projectRoleFormSchema,
rolePermission2Form
} from "@app/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils";
import { renderConditionalComponents } from "@app/pages/project/RoleDetailsBySlugPage/components/RolePermissionsSection";
type Props = {
projectTemplate: TProjectTemplate;

View File

@@ -1,16 +1,16 @@
import { createFileRoute } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'
import { z } from 'zod'
import { createFileRoute } from "@tanstack/react-router";
import { zodValidator } from "@tanstack/zod-adapter";
import { z } from "zod";
import { SettingsPage } from './SettingsPage'
import { SettingsPage } from "./SettingsPage";
const SettingsPageQueryParams = z.object({
selectedTab: z.string().catch(''),
})
selectedTab: z.string().catch("")
});
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout/settings',
"/_authenticate/_inject-org-details/organization/_layout/settings"
)({
component: SettingsPage,
validateSearch: zodValidator(SettingsPageQueryParams),
})
validateSearch: zodValidator(SettingsPageQueryParams)
});

View File

@@ -99,7 +99,7 @@ const Page = withPermission(
handlePopUpClose("removeMember");
navigate({
to: "/organization/members" as const,
to: "/organization/access-management" as const,
search: {
selectedTab: OrgAccessControlTabSections.Member
}
@@ -125,7 +125,7 @@ const Page = withPermission(
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
onClick={() => {
navigate({
to: "/organization/members" as const,
to: "/organization/access-management" as const,
search: {
selectedTab: OrgAccessControlTabSections.Member
}

View File

@@ -6,8 +6,7 @@ import { IconButton, Tooltip } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects, useSubscription } from "@app/context";
import { withPermission } from "@app/hoc";
import { OrgUser } from "@app/hooks/api/types";
import { LogsSection } from "../../../../audit-logs/-components";
import { LogsSection } from "@app/pages/organization/AuditLogsPage/components";
type Props = {
orgMembership: OrgUser;

View File

@@ -52,7 +52,7 @@ export const UserProjectRow = ({
onClick={() => {
if (isAccessible) {
navigate({
to: `/${project.type}/$projectId/access` as const,
to: `/${project.type}/$projectId/access-management` as const,
params: {
projectId: project.id
},

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { UserDetailsByIDPage } from './UserDetailsByIDPage'
import { UserDetailsByIDPage } from "./UserDetailsByIDPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout/members/$membershipId',
"/_authenticate/_inject-org-details/organization/_layout/members/$membershipId"
)({
component: UserDetailsByIDPage,
})
component: UserDetailsByIDPage
});

View File

@@ -1,9 +1,7 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import { OrganizationLayout } from '@app/layouts/OrganizationLayout'
import { OrganizationLayout } from "@app/layouts/OrganizationLayout";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/organization/_layout',
)({
component: OrganizationLayout,
})
export const Route = createFileRoute("/_authenticate/_inject-org-details/organization/_layout")({
component: OrganizationLayout
});

View File

@@ -28,7 +28,7 @@ const Page = withProjectPermission(
const updateSelectedTab = (tab: string) => {
navigate({
to: `/${currentWorkspace.type}/$projectId/access` as const,
to: `/${currentWorkspace.type}/$projectId/access-management` as const,
search: (prev) => ({ ...prev, selectedTab: tab }),
params: {
projectId: currentWorkspace.id

View File

@@ -15,4 +15,3 @@ export const GroupsTab = () => {
</motion.div>
);
};

View File

@@ -147,7 +147,7 @@ const Content = ({ popUp, handlePopUpToggle }: Props) => {
<div className="text-sm">
All groups in your organization have already been added to this project.
</div>
<Link to={"/organization/members" as const}>
<Link to={"/organization/access-management" as const}>
<Button variant="outline_bg">Create a new group</Button>
</Link>
</div>

View File

@@ -1 +1 @@
export { GroupsSection } from "./GroupsSection";
export { GroupsSection } from "./GroupsSection";

View File

@@ -1 +1 @@
export { GroupsSection } from "./GroupsSection";
export { GroupsSection } from "./GroupsSection";

View File

@@ -1 +1 @@
export { GroupsTab } from "./GroupsTab";
export { GroupsTab } from "./GroupsTab";

View File

@@ -182,7 +182,7 @@ const Content = ({ popUp, handlePopUpToggle }: Props) => {
<div className="text-sm">
All identities in your organization have already been added to this project.
</div>
<Link to={"/organization/members" as const}>
<Link to={"/organization/access-management" as const}>
<Button isDisabled={isRolesLoading} isLoading={isRolesLoading} variant="outline_bg">
Create a new identity
</Button>

View File

@@ -211,7 +211,7 @@ export const AddMemberModal = ({ popUp, handlePopUpToggle }: Props) => {
) : (
<div className="flex flex-col space-y-4">
<div>All the users in your organization are already invited.</div>
<Link to={"/organization/members" as const}>
<Link to={"/organization/access-management" as const}>
<Button variant="outline_bg">Add users to organization</Button>
</Link>
</div>

View File

@@ -25,7 +25,7 @@ import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@a
import { usePopUp } from "@app/hooks";
import { useDeleteProjectRole, useGetProjectRoles } from "@app/hooks/api";
import { TProjectRole } from "@app/hooks/api/roles/types";
import { RoleModal } from "@app/routes/_authenticate/_ctx-org-details/organization/_layout-org/roles/$roleId/-components";
import { RoleModal } from "@app/pages/organization/RoleByIDPage/components";
export const ProjectRoleList = () => {
const navigate = useNavigate();

View File

@@ -18,7 +18,7 @@ type DeleteModalData = { name: string; id: string };
export const ServiceTokenSection = withProjectPermission(
() => {
const { t } = useTranslation();
const deleteServiceToken = useDeleteServiceToken();
const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([

View File

@@ -1,20 +1,18 @@
import { createFileRoute } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'
import { z } from 'zod'
import { createFileRoute } from "@tanstack/react-router";
import { zodValidator } from "@tanstack/zod-adapter";
import { z } from "zod";
import { ProjectAccessControlTabs } from '@app/types/project'
import { ProjectAccessControlTabs } from "@app/types/project";
import { AccessControlPage } from './AccessControlPage'
import { AccessControlPage } from "./AccessControlPage";
const AccessControlPageQuerySchema = z.object({
selectedTab: z
.nativeEnum(ProjectAccessControlTabs)
.catch(ProjectAccessControlTabs.Member),
})
selectedTab: z.nativeEnum(ProjectAccessControlTabs).catch(ProjectAccessControlTabs.Member)
});
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/cert-manager/$projectId/_cert-manager-layout/access-management',
"/_authenticate/_inject-org-details/cert-manager/$projectId/_cert-manager-layout/access-management"
)({
component: AccessControlPage,
validateSearch: zodValidator(AccessControlPageQuerySchema),
})
validateSearch: zodValidator(AccessControlPageQuerySchema)
});

View File

@@ -1,20 +1,18 @@
import { createFileRoute } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'
import { z } from 'zod'
import { createFileRoute } from "@tanstack/react-router";
import { zodValidator } from "@tanstack/zod-adapter";
import { z } from "zod";
import { ProjectAccessControlTabs } from '@app/types/project'
import { ProjectAccessControlTabs } from "@app/types/project";
import { AccessControlPage } from './AccessControlPage'
import { AccessControlPage } from "./AccessControlPage";
const AccessControlPageQuerySchema = z.object({
selectedTab: z
.nativeEnum(ProjectAccessControlTabs)
.catch(ProjectAccessControlTabs.Member),
})
selectedTab: z.nativeEnum(ProjectAccessControlTabs).catch(ProjectAccessControlTabs.Member)
});
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/kms/$projectId/_kms-layout/access-management',
"/_authenticate/_inject-org-details/kms/$projectId/_kms-layout/access-management"
)({
component: AccessControlPage,
validateSearch: zodValidator(AccessControlPageQuerySchema),
})
validateSearch: zodValidator(AccessControlPageQuerySchema)
});

View File

@@ -1,20 +1,18 @@
import { createFileRoute } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'
import { z } from 'zod'
import { createFileRoute } from "@tanstack/react-router";
import { zodValidator } from "@tanstack/zod-adapter";
import { z } from "zod";
import { ProjectAccessControlTabs } from '@app/types/project'
import { ProjectAccessControlTabs } from "@app/types/project";
import { AccessControlPage } from './AccessControlPage'
import { AccessControlPage } from "./AccessControlPage";
const AccessControlPageQuerySchema = z.object({
selectedTab: z
.nativeEnum(ProjectAccessControlTabs)
.catch(ProjectAccessControlTabs.Member),
})
selectedTab: z.nativeEnum(ProjectAccessControlTabs).catch(ProjectAccessControlTabs.Member)
});
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/access-management',
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/access-management"
)({
component: AccessControlPage,
validateSearch: zodValidator(AccessControlPageQuerySchema),
})
validateSearch: zodValidator(AccessControlPageQuerySchema)
});

View File

@@ -53,7 +53,7 @@ const Page = () => {
});
handlePopUpClose("deleteIdentity");
navigate({
to: `/${currentWorkspace.type}/$projectId/access` as const,
to: `/${currentWorkspace.type}/$projectId/access-management` as const,
params: {
projectId: workspaceId
},
@@ -90,7 +90,7 @@ const Page = () => {
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
onClick={() => {
navigate({
to: `/${currentWorkspace.type}/$projectId/access` as const,
to: `/${currentWorkspace.type}/$projectId/access-management` as const,
params: {
projectId: workspaceId
},

View File

@@ -1,8 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { IdentityDetailsByIDPage } from './IdentityDetailsByIDPage'
import { createFileRoute } from "@tanstack/react-router";
import { IdentityDetailsByIDPage } from "./IdentityDetailsByIDPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/cert-manager/$projectId/_cert-manager-layout/identities/$identityId',
"/_authenticate/_inject-org-details/cert-manager/$projectId/_cert-manager-layout/identities/$identityId"
)({
component: IdentityDetailsByIDPage,
})
component: IdentityDetailsByIDPage
});

View File

@@ -1,8 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { IdentityDetailsByIDPage } from './IdentityDetailsByIDPage'
import { createFileRoute } from "@tanstack/react-router";
import { IdentityDetailsByIDPage } from "./IdentityDetailsByIDPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/kms/$projectId/_kms-layout/identities/$identityId',
"/_authenticate/_inject-org-details/kms/$projectId/_kms-layout/identities/$identityId"
)({
component: IdentityDetailsByIDPage,
})
component: IdentityDetailsByIDPage
});

View File

@@ -1,8 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import { IdentityDetailsByIDPage } from './IdentityDetailsByIDPage'
import { createFileRoute } from "@tanstack/react-router";
import { IdentityDetailsByIDPage } from "./IdentityDetailsByIDPage";
export const Route = createFileRoute(
'/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/identities/$identityId',
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/identities/$identityId"
)({
component: IdentityDetailsByIDPage,
})
component: IdentityDetailsByIDPage
});

View File

@@ -58,7 +58,7 @@ export const Page = () => {
type: "success"
});
navigate({
to: `/${currentWorkspace.type}/$projectId/access` as const,
to: `/${currentWorkspace.type}/$projectId/access-management` as const,
params: {
projectId: currentWorkspace.id
}
@@ -90,7 +90,7 @@ export const Page = () => {
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
onClick={() => {
navigate({
to: `/${currentWorkspace.type}/$projectId/access` as const,
to: `/${currentWorkspace.type}/$projectId/access-management` as const,
params: {
projectId: currentWorkspace.id
}

View File

@@ -15,16 +15,6 @@ import { z } from "zod";
import { TtlFormLabel } from "@app/components/features";
import { createNotification } from "@app/components/notifications";
import { GeneralPermissionPolicies } from "@app/components/permissions/ProjectRolePermissionsSection/components/GeneralPermissionPolicies";
import { PermissionEmptyState } from "@app/components/permissions/ProjectRolePermissionsSection/PermissionEmptyState";
import {
formRolePermission2API,
isConditionalSubjects,
PROJECT_PERMISSION_OBJECT,
projectRoleFormSchema,
rolePermission2Form
} from "@app/components/permissions/ProjectRolePermissionsSection/ProjectRoleModifySection.utils";
import { renderConditionalComponents } from "@app/components/permissions/ProjectRolePermissionsSection/RolePermissionsSection";
import {
Button,
DropdownMenu,
@@ -52,6 +42,16 @@ import {
useUpdateProjectUserAdditionalPrivilege
} from "@app/hooks/api";
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/hooks/api/projectUserAdditionalPrivilege/types";
import { GeneralPermissionPolicies } from "@app/pages/project/RoleDetailsBySlugPage/components/GeneralPermissionPolicies";
import { PermissionEmptyState } from "@app/pages/project/RoleDetailsBySlugPage/components/PermissionEmptyState";
import {
formRolePermission2API,
isConditionalSubjects,
PROJECT_PERMISSION_OBJECT,
projectRoleFormSchema,
rolePermission2Form
} from "@app/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils";
import { renderConditionalComponents } from "@app/pages/project/RoleDetailsBySlugPage/components/RolePermissionsSection";
type Props = {
privilegeId?: string;

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