mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 03:29:35 +00:00
Fix merge conflicts
This commit is contained in:
+11
-1
@@ -1,5 +1,6 @@
|
||||
import { useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { appWithTranslation } from "next-i18next";
|
||||
import { config } from "@fortawesome/fontawesome-svg-core";
|
||||
|
||||
import Layout from "~/components/basic/Layout";
|
||||
@@ -16,6 +17,15 @@ config.autoAddCss = false;
|
||||
const App = ({ Component, pageProps, ...appProps }) => {
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
const storedLang = localStorage.getItem("lang");
|
||||
if (router.locale ?? "en" !== storedLang ?? "en") {
|
||||
router.push(router.asPath, router.asPath, {
|
||||
locale: storedLang ?? "en",
|
||||
});
|
||||
}
|
||||
}, [router.locale, router.pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
// Init for auto capturing
|
||||
const telemetry = new Telemetry().getInstance();
|
||||
@@ -52,7 +62,7 @@ const App = ({ Component, pageProps, ...appProps }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
export default appWithTranslation(App);
|
||||
|
||||
{
|
||||
/* <Script
|
||||
|
||||
@@ -10,13 +10,7 @@ const checkAuth = async () => {
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}).then((res) => {
|
||||
if (res && res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Not authorized');
|
||||
}
|
||||
});
|
||||
}).then((res) => res);
|
||||
};
|
||||
|
||||
export default checkAuth;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Fragment, useCallback, useEffect, useState } from 'react';
|
||||
import Head from 'next/head';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useTranslation } from "next-i18next";
|
||||
import {
|
||||
faArrowDownAZ,
|
||||
faArrowDownZA,
|
||||
@@ -26,6 +27,7 @@ import SideBar from '~/components/dashboard/SideBar';
|
||||
import NavHeader from '~/components/navigation/NavHeader';
|
||||
import getSecretsForProject from '~/components/utilities/secrets/getSecretsForProject';
|
||||
import pushKeys from '~/components/utilities/secrets/pushKeys';
|
||||
import { getTranslatedServerSideProps } from '~/components/utilities/withTranslateProps';
|
||||
import guidGenerator from '~/utilities/randomId';
|
||||
|
||||
import { envMapping } from '../../public/data/frequentConstants';
|
||||
@@ -91,6 +93,7 @@ export default function Dashboard() {
|
||||
const [sidebarSecretId, toggleSidebar] = useState("None");
|
||||
const [sharedToHide, setSharedToHide] = useState<string[]>([]);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { createNotification } = useNotificationContext();
|
||||
|
||||
// #TODO: fix save message for changing reroutes
|
||||
@@ -249,6 +252,15 @@ export default function Dashboard() {
|
||||
*/
|
||||
const deleteOverride = (id: string) => {
|
||||
setButtonReady(true);
|
||||
|
||||
// find which shared secret corresponds to the overriden version
|
||||
const sharedVersionOfOverride = data!.filter(secret => secret.type == "shared" && secret.key == data!.filter(row => row.id == id)[0]?.key)[0]?.id;
|
||||
|
||||
// change the sidebar to this shared secret; and unhide it
|
||||
toggleSidebar(sharedVersionOfOverride)
|
||||
setSharedToHide(sharedToHide!.filter(tempId => tempId != sharedVersionOfOverride))
|
||||
|
||||
// resort secrets
|
||||
const tempData = data!.filter((row: SecretDataProps) => !(row.id == id && row.type == 'personal'))
|
||||
sortValuesHandler(tempData, sortMethod == "alhpabetical" ? "-alphabetical" : "alphabetical")
|
||||
};
|
||||
@@ -407,19 +419,16 @@ export default function Dashboard() {
|
||||
return data ? (
|
||||
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
|
||||
<Head>
|
||||
<title>Secrets</title>
|
||||
<title>{t("common:head-title", { title: t("dashboard:title") })}</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
<meta property="og:image" content="/images/message.png" />
|
||||
<meta property="og:title" content="Manage your .env files in seconds" />
|
||||
<meta
|
||||
name="og:description"
|
||||
content="Infisical a simple end-to-end encrypted platform that enables teams to sync and manage their .env files."
|
||||
/>
|
||||
<meta property="og:title" content={String(t("dashboard:og-title"))} />
|
||||
<meta name="og:description" content={String(t("dashboard:og-description"))} />
|
||||
</Head>
|
||||
<div className="flex flex-row">
|
||||
{sidebarSecretId != "None" && <SideBar
|
||||
toggleSidebar={toggleSidebar}
|
||||
data={data.filter((row: SecretDataProps) => row.id == sidebarSecretId)}
|
||||
data={data.filter((row: SecretDataProps) => row.key == data.filter(row => row.id == sidebarSecretId)[0]?.key)}
|
||||
modifyKey={listenChangeKey}
|
||||
modifyValue={listenChangeValue}
|
||||
modifyComment={listenChangeComment}
|
||||
@@ -431,21 +440,21 @@ export default function Dashboard() {
|
||||
setSharedToHide={setSharedToHide}
|
||||
/>}
|
||||
<div className="w-full max-h-96 pb-2">
|
||||
<NavHeader pageName="Secrets" isProjectRelated={true} />
|
||||
<NavHeader pageName={t("dashboard:title")} isProjectRelated={true} />
|
||||
{checkDocsPopUpVisible && (
|
||||
<BottonRightPopup
|
||||
buttonText="Check Docs"
|
||||
buttonText={t("dashboard:check-docs.button")}
|
||||
buttonLink="https://infisical.com/docs/getting-started/introduction"
|
||||
titleText="Good job!"
|
||||
titleText={t("dashboard:check-docs.title")}
|
||||
emoji="🎉"
|
||||
textLine1="Congrats on adding more secrets."
|
||||
textLine2="Here is how to connect them to your codebase."
|
||||
textLine1={t("dashboard:check-docs.line1")}
|
||||
textLine2={t("dashboard:check-docs.line2")}
|
||||
setCheckDocsPopUpVisible={setCheckDocsPopUpVisible}
|
||||
/>
|
||||
)}
|
||||
<div className="flex flex-row justify-between items-center mx-6 mt-6 mb-3 text-xl max-w-5xl">
|
||||
<div className="flex flex-row justify-start items-center text-3xl">
|
||||
<p className="font-semibold mr-4 mt-1">Secrets</p>
|
||||
<p className="font-semibold mr-4 mt-1">{t("dashboard:title")}</p>
|
||||
{data?.length == 0 && (
|
||||
<ListBox
|
||||
selected={env}
|
||||
@@ -456,7 +465,9 @@ export default function Dashboard() {
|
||||
</div>
|
||||
<div className="flex flex-row">
|
||||
<div className="flex justify-end items-center bg-white/[0.07] text-base mt-2 mr-2 rounded-md text-gray-400">
|
||||
<p className="mr-2 font-bold pl-4">Project ID:</p>
|
||||
<p className="mr-2 font-bold pl-4">{`${t(
|
||||
"common:project-id"
|
||||
)}:`}</p>
|
||||
<input
|
||||
type="text"
|
||||
value={workspaceId}
|
||||
@@ -476,20 +487,20 @@ export default function Dashboard() {
|
||||
)}
|
||||
</button>
|
||||
<span className="absolute hidden group-hover:flex group-hover:animate-popup duration-300 w-28 -left-8 -top-20 translate-y-full pl-3 py-2 bg-white/10 rounded-md text-center text-gray-400 text-sm">
|
||||
Click to Copy
|
||||
{t("common:click-to-copy")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{(data?.length !== 0 || buttonReady) && (
|
||||
<div className={`flex justify-start max-w-sm mt-2`}>
|
||||
<Button
|
||||
text="Save Changes"
|
||||
text={String(t("common:save-changes"))}
|
||||
onButtonPressed={savePush}
|
||||
color="primary"
|
||||
size="md"
|
||||
active={buttonReady}
|
||||
iconDisabled={faCheck}
|
||||
textDisabled="Saved"
|
||||
textDisabled={String(t("common:saved"))}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -514,7 +525,7 @@ export default function Dashboard() {
|
||||
className="pl-2 text-gray-400 rounded-r-md bg-white/5 w-full h-full outline-none"
|
||||
value={searchKeys}
|
||||
onChange={(e) => setSearchKeys(e.target.value)}
|
||||
placeholder={'Search keys...'}
|
||||
placeholder={String(t("dashboard:search-keys"))}
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-2 min-w-max flex flex-row items-start justify-start">
|
||||
@@ -547,7 +558,7 @@ export default function Dashboard() {
|
||||
</div>
|
||||
<div className="relative ml-2 min-w-max flex flex-row items-start justify-end">
|
||||
<Button
|
||||
text="Add Key"
|
||||
text={String(t("dashboard:add-key"))}
|
||||
onButtonPressed={addRow}
|
||||
color="mineshaft"
|
||||
icon={faPlus}
|
||||
@@ -647,3 +658,5 @@ export default function Dashboard() {
|
||||
}
|
||||
|
||||
Dashboard.requireAuth = true;
|
||||
|
||||
export const getServerSideProps = getTranslatedServerSideProps(["dashboard"]);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { IconProp } from '@fortawesome/fontawesome-svg-core';
|
||||
import { faSlack } from '@fortawesome/free-brands-svg-icons';
|
||||
import {
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
import onboardingCheck from '~/components/utilities/checks/OnboardingCheck';
|
||||
import { getTranslatedServerSideProps } from '~/components/utilities/withTranslateProps';
|
||||
|
||||
import registerUserAction from '../api/userActions/registerUserAction';
|
||||
|
||||
@@ -139,6 +141,8 @@ export default function Home() {
|
||||
const [hasUserPushedSecrets, setHasUserPushedSecrets] = useState(false);
|
||||
const [usersInOrg, setUsersInOrg] = useState(false);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
onboardingCheck({
|
||||
setHasUserClickedIntro,
|
||||
@@ -226,3 +230,5 @@ export default function Home() {
|
||||
}
|
||||
|
||||
Home.requireAuth = true;
|
||||
|
||||
export const getServerSideProps = getTranslatedServerSideProps(["home"]);
|
||||
|
||||
@@ -2,14 +2,21 @@ import { useEffect, useState } from "react";
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import {
|
||||
faArrowRight,
|
||||
faCheck,
|
||||
faRotate,
|
||||
faX,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import ActivateBotDialog from "~/components/basic/dialog/ActivateBotDialog";
|
||||
import IntegrationAccessTokenDialog from "~/components/basic/dialog/IntegrationAccessTokenDialog";
|
||||
import CloudIntegrationSection from "~/components/integrations/CloudIntegrationSection";
|
||||
import FrameworkIntegrationSection from "~/components/integrations/FrameworkIntegrationSection";
|
||||
import Integration from "~/components/integrations/Integration";
|
||||
import IntegrationSection from "~/components/integrations/IntegrationSection";
|
||||
import NavHeader from "~/components/navigation/NavHeader";
|
||||
import { getTranslatedServerSideProps } from "~/utilities/withTranslateProps";
|
||||
|
||||
import frameworkIntegrationOptions from "../../public/json/frameworkIntegrations.json";
|
||||
import getBot from "../api/bot/getBot";
|
||||
@@ -35,6 +42,8 @@ export default function Integrations() {
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(async () => {
|
||||
try {
|
||||
// get cloud integration options
|
||||
@@ -181,18 +190,17 @@ export default function Integrations() {
|
||||
return (
|
||||
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
|
||||
<Head>
|
||||
<title>Dashboard</title>
|
||||
<title>
|
||||
{t("common:head-title", { title: t("integrations:title") })}
|
||||
</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
<meta property="og:image" content="/images/message.png" />
|
||||
<meta property="og:title" content="Manage your .env files in seconds" />
|
||||
<meta
|
||||
name="og:description"
|
||||
content="Infisical a simple end-to-end encrypted platform that enables teams to sync and manage their .env files."
|
||||
/>
|
||||
<meta name="og:description" content={t("integrations:description")} />
|
||||
</Head>
|
||||
<div className="w-full max-h-96 pb-2 h-screen max-h-[calc(100vh-10px)] overflow-y-scroll no-scrollbar no-scrollbar::-webkit-scrollbar">
|
||||
<NavHeader
|
||||
pageName="Project Integrations"
|
||||
pageName={t("integrations:title")}
|
||||
isProjectRelated={true}
|
||||
/>
|
||||
<ActivateBotDialog
|
||||
@@ -229,3 +237,7 @@ export default function Integrations() {
|
||||
}
|
||||
|
||||
Integrations.requireAuth = true;
|
||||
|
||||
export const getServerSideProps = getTranslatedServerSideProps([
|
||||
"integrations",
|
||||
]);
|
||||
|
||||
+55
-33
@@ -1,24 +1,34 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import Head from 'next/head';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { faWarning } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { useEffect, useState } from "react";
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { faWarning } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import Button from '~/components/basic/buttons/Button';
|
||||
import Error from '~/components/basic/Error';
|
||||
import InputField from '~/components/basic/InputField';
|
||||
import attemptLogin from '~/utilities/attemptLogin';
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import Error from "~/components/basic/Error";
|
||||
import InputField from "~/components/basic/InputField";
|
||||
import ListBox from "~/components/basic/Listbox";
|
||||
import { getTranslatedStaticProps } from "~/components/utilities/withTranslateProps";
|
||||
import attemptLogin from "~/utilities/attemptLogin";
|
||||
|
||||
import getWorkspaces from './api/workspace/getWorkspaces';
|
||||
import getWorkspaces from "./api/workspace/getWorkspaces";
|
||||
|
||||
export default function Login() {
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [errorLogin, setErrorLogin] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
const lang = router.locale ?? "en";
|
||||
|
||||
const setLanguage = async (to: string) => {
|
||||
router.push("/login", "/login", { locale: to });
|
||||
localStorage.setItem("lang", to);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const redirectToDashboard = async () => {
|
||||
@@ -26,9 +36,9 @@ export default function Login() {
|
||||
try {
|
||||
const userWorkspaces = await getWorkspaces();
|
||||
userWorkspace = userWorkspaces[0]._id;
|
||||
router.push('/dashboard/' + userWorkspace);
|
||||
router.push("/dashboard/" + userWorkspace);
|
||||
} catch (error) {
|
||||
console.log('Error - Not logged in yet');
|
||||
console.log("Error - Not logged in yet");
|
||||
}
|
||||
};
|
||||
redirectToDashboard();
|
||||
@@ -49,7 +59,7 @@ export default function Login() {
|
||||
setErrorLogin,
|
||||
router,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
).then(() => {
|
||||
setTimeout(function () {
|
||||
setIsLoading(false);
|
||||
@@ -60,14 +70,11 @@ export default function Login() {
|
||||
return (
|
||||
<div className="bg-bunker-800 h-screen flex flex-col justify-start px-6">
|
||||
<Head>
|
||||
<title>Login</title>
|
||||
<title>{t("common:head-title", { title: t("login:title") })}</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
<meta property="og:image" content="/images/message.png" />
|
||||
<meta property="og:title" content="Log In to Infisical" />
|
||||
<meta
|
||||
name="og:description"
|
||||
content="Infisical a simple end-to-end encrypted platform that enables teams to sync and manage their .env files."
|
||||
/>
|
||||
<meta property="og:title" content={t("login:og-title") ?? ""} />
|
||||
<meta name="og:description" content={t("login:og-description") ?? ""} />
|
||||
</Head>
|
||||
<Link href="/">
|
||||
<div className="flex justify-center mb-8 mt-20 cursor-pointer">
|
||||
@@ -80,15 +87,16 @@ export default function Login() {
|
||||
</div>
|
||||
</Link>
|
||||
<form
|
||||
onChange={() => setErrorLogin(false)} onSubmit={(e) => e.preventDefault()}
|
||||
onChange={() => setErrorLogin(false)}
|
||||
onSubmit={(e) => e.preventDefault()}
|
||||
>
|
||||
<div className="bg-bunker w-full max-w-md mx-auto h-7/12 py-4 pt-8 px-6 rounded-xl drop-shadow-xl">
|
||||
<p className="text-3xl w-max mx-auto flex justify-center font-semibold text-bunker-100 mb-6">
|
||||
Log in to your account
|
||||
{t("login:login")}
|
||||
</p>
|
||||
<div className="flex items-center justify-center w-full md:p-2 rounded-lg mt-4 md:mt-0 max-h-24 md:max-h-28">
|
||||
<InputField
|
||||
label="Email"
|
||||
label={t("common:email")}
|
||||
onChangeHandler={setEmail}
|
||||
type="email"
|
||||
value={email}
|
||||
@@ -99,7 +107,7 @@ export default function Login() {
|
||||
</div>
|
||||
<div className="relative flex items-center justify-center w-full md:p-2 rounded-lg md:mt-2 mt-6 max-h-24 md:max-h-28">
|
||||
<InputField
|
||||
label="Password"
|
||||
label={t("common:password")}
|
||||
onChangeHandler={setPassword}
|
||||
type="password"
|
||||
value={password}
|
||||
@@ -112,12 +120,14 @@ export default function Login() {
|
||||
<Link href="/verify-email">Forgot password?</Link>
|
||||
</div>
|
||||
</div>
|
||||
{!isLoading && errorLogin && <Error text="Your email and/or password are wrong." />}
|
||||
{!isLoading && errorLogin && (
|
||||
<Error text="Your email and/or password are wrong." />
|
||||
)}
|
||||
<div className="flex flex-col items-center justify-center w-full md:p-2 max-h-20 max-w-md mt-4 mx-auto text-sm">
|
||||
<div className="text-l mt-6 m-8 px-8 py-3 text-lg">
|
||||
<Button
|
||||
type="submit"
|
||||
text="Log In"
|
||||
text={t("login:login") ?? ""}
|
||||
onButtonPressed={loginCheck}
|
||||
loading={isLoading}
|
||||
size="lg"
|
||||
@@ -131,21 +141,33 @@ export default function Login() {
|
||||
{false && (
|
||||
<div className="w-full p-2 flex flex-row items-center bg-white/10 text-gray-300 rounded-md max-w-md mx-auto mt-4">
|
||||
<FontAwesomeIcon icon={faWarning} className="ml-2 mr-6 text-6xl" />
|
||||
We are experiencing minor technical difficulties. We are working on
|
||||
solving it right now. Please come back in a few minutes.
|
||||
{t("common:maintenance-alert")}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-row items-center justify-center md:pb-4 mt-4">
|
||||
<p className="text-sm flex justify-center text-gray-400 w-max">
|
||||
Need an Infisical account?
|
||||
{t("login:need-account")}
|
||||
</p>
|
||||
<Link href="/signup">
|
||||
<button className="text-primary-700 hover:text-primary duration-200 font-normal text-sm underline-offset-4 ml-1.5">
|
||||
Sign up here.
|
||||
{t("login:create-account")}
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</form>
|
||||
<div className="absolute right-4 top-0 mt-4 flex items-center justify-center">
|
||||
<div className="w-48 mx-auto">
|
||||
<ListBox
|
||||
selected={lang}
|
||||
onChange={setLanguage}
|
||||
data={["en", "ko"]}
|
||||
isFull
|
||||
text={`${t("common:language")}: `}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps = getTranslatedStaticProps(["auth", "login"]);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
import { faFolderOpen } from "@fortawesome/free-regular-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { getTranslatedServerSideProps } from "~/utilities/withTranslateProps";
|
||||
|
||||
export default function NoProjects() {
|
||||
return (
|
||||
@@ -31,3 +31,5 @@ export default function NoProjects() {
|
||||
}
|
||||
|
||||
NoProjects.requireAuth = true;
|
||||
|
||||
export const getServerSideProps = getTranslatedServerSideProps(["noprojects"]);
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
import { useState } from 'react';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/router';
|
||||
import { faCheck, faX } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
import { faCheck, faX } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import Button from '~/components/basic/buttons/Button';
|
||||
import InputField from '~/components/basic/InputField';
|
||||
import passwordCheck from '~/components/utilities/checks/PasswordCheck';
|
||||
import Aes256Gcm from '~/components/utilities/cryptography/aes-256-gcm';
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import InputField from "~/components/basic/InputField";
|
||||
import passwordCheck from "~/components/utilities/checks/PasswordCheck";
|
||||
import Aes256Gcm from "~/components/utilities/cryptography/aes-256-gcm";
|
||||
import { getTranslatedStaticProps } from "~/components/utilities/withTranslateProps";
|
||||
|
||||
import EmailVerifyOnPasswordReset from './api/auth/EmailVerifyOnPasswordReset';
|
||||
import getBackupEncryptedPrivateKey from './api/auth/getBackupEncryptedPrivateKey';
|
||||
import resetPasswordOnAccountRecovery from './api/auth/resetPasswordOnAccountRecovery';
|
||||
import EmailVerifyOnPasswordReset from "./api/auth/EmailVerifyOnPasswordReset";
|
||||
import getBackupEncryptedPrivateKey from "./api/auth/getBackupEncryptedPrivateKey";
|
||||
import resetPasswordOnAccountRecovery from "./api/auth/resetPasswordOnAccountRecovery";
|
||||
|
||||
const queryString = require('query-string');
|
||||
const nacl = require('tweetnacl');
|
||||
const jsrp = require('jsrp');
|
||||
nacl.util = require('tweetnacl-util');
|
||||
const queryString = require("query-string");
|
||||
const nacl = require("tweetnacl");
|
||||
const jsrp = require("jsrp");
|
||||
nacl.util = require("tweetnacl-util");
|
||||
const client = new jsrp.client();
|
||||
|
||||
export default function PasswordReset() {
|
||||
const router = useRouter();
|
||||
const parsedUrl = queryString.parse(router.asPath.split('?')[1]);
|
||||
const parsedUrl = queryString.parse(router.asPath.split("?")[1]);
|
||||
const token = parsedUrl.token;
|
||||
const email = parsedUrl.to?.replace(' ', '+').trim();
|
||||
const [verificationToken, setVerificationToken] = useState('');
|
||||
const email = parsedUrl.to?.replace(" ", "+").trim();
|
||||
const [verificationToken, setVerificationToken] = useState("");
|
||||
const [step, setStep] = useState(1);
|
||||
const [backupKey, setBackupKey] = useState('');
|
||||
const [privateKey, setPrivateKey] = useState('');
|
||||
const [newPassword, setNewPassword] = useState('');
|
||||
const [backupKey, setBackupKey] = useState("");
|
||||
const [privateKey, setPrivateKey] = useState("");
|
||||
const [newPassword, setNewPassword] = useState("");
|
||||
const [backupKeyError, setBackupKeyError] = useState(false);
|
||||
const [passwordErrorLength, setPasswordErrorLength] = useState(false);
|
||||
const [passwordErrorNumber, setPasswordErrorNumber] = useState(false);
|
||||
@@ -43,7 +44,7 @@ export default function PasswordReset() {
|
||||
ciphertext: result.encryptedPrivateKey,
|
||||
iv: result.iv,
|
||||
tag: result.tag,
|
||||
secret: backupKey
|
||||
secret: backupKey,
|
||||
})
|
||||
);
|
||||
setStep(3);
|
||||
@@ -60,7 +61,7 @@ export default function PasswordReset() {
|
||||
setPasswordErrorLength,
|
||||
setPasswordErrorNumber,
|
||||
setPasswordErrorLowerCase,
|
||||
currentErrorCheck: errorCheck
|
||||
currentErrorCheck: errorCheck,
|
||||
});
|
||||
|
||||
if (!errorCheck) {
|
||||
@@ -72,14 +73,14 @@ export default function PasswordReset() {
|
||||
.padStart(
|
||||
32 +
|
||||
(newPassword.slice(0, 32).length - new Blob([newPassword]).size),
|
||||
'0'
|
||||
)
|
||||
"0"
|
||||
),
|
||||
}) as { ciphertext: string; iv: string; tag: string };
|
||||
|
||||
client.init(
|
||||
{
|
||||
username: email,
|
||||
password: newPassword
|
||||
password: newPassword,
|
||||
},
|
||||
async () => {
|
||||
client.createVerifier(
|
||||
@@ -90,12 +91,12 @@ export default function PasswordReset() {
|
||||
iv,
|
||||
tag,
|
||||
salt: result.salt,
|
||||
verifier: result.verifier
|
||||
verifier: result.verifier,
|
||||
});
|
||||
|
||||
// if everything works, go the main dashboard page.
|
||||
if (response?.status === 200) {
|
||||
router.push('/login');
|
||||
router.push("/login");
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -122,14 +123,14 @@ export default function PasswordReset() {
|
||||
onButtonPressed={async () => {
|
||||
const response = await EmailVerifyOnPasswordReset({
|
||||
email,
|
||||
code: token
|
||||
code: token,
|
||||
});
|
||||
if (response.status == 200) {
|
||||
setVerificationToken((await response.json()).token);
|
||||
setStep(2);
|
||||
} else {
|
||||
console.log('ERROR', response);
|
||||
router.push('/email-not-verified');
|
||||
console.log("ERROR", response);
|
||||
router.push("/email-not-verified");
|
||||
}
|
||||
}}
|
||||
size="lg"
|
||||
@@ -195,7 +196,7 @@ export default function PasswordReset() {
|
||||
setPasswordErrorLength,
|
||||
setPasswordErrorNumber,
|
||||
setPasswordErrorLowerCase,
|
||||
currentErrorCheck: false
|
||||
currentErrorCheck: false,
|
||||
});
|
||||
}}
|
||||
type="password"
|
||||
@@ -224,7 +225,7 @@ export default function PasswordReset() {
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
passwordErrorLength ? 'text-gray-400' : 'text-gray-600'
|
||||
passwordErrorLength ? "text-gray-400" : "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
14 characters
|
||||
@@ -241,7 +242,7 @@ export default function PasswordReset() {
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
passwordErrorLowerCase ? 'text-gray-400' : 'text-gray-600'
|
||||
passwordErrorLowerCase ? "text-gray-400" : "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
1 lowercase character
|
||||
@@ -258,7 +259,7 @@ export default function PasswordReset() {
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
passwordErrorNumber ? 'text-gray-400' : 'text-gray-600'
|
||||
passwordErrorNumber ? "text-gray-400" : "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
1 number
|
||||
@@ -288,3 +289,5 @@ export default function PasswordReset() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = getTranslatedStaticProps([]);
|
||||
|
||||
@@ -1,21 +1,31 @@
|
||||
import React from "react";
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
|
||||
export default function Activity() {
|
||||
export default function RequestNewInvite() {
|
||||
return (
|
||||
<div className="bg-bunker-800 md:h-screen flex flex-col justify-between">
|
||||
<div className="bg-bunker-700 md:h-screen flex flex-col justify-between">
|
||||
<Head>
|
||||
<title>Request a New Invite</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Head>
|
||||
<div className="flex flex-col items-center justify-center text-gray-200 h-screen w-screen">
|
||||
<p className="text-6xl">Oops.</p>
|
||||
<p className="mt-2 mb-1 text-xl">Your invite has expired. </p>
|
||||
<p className="text-xl">Ask the administrator for a new one.</p>
|
||||
<p className="text-md mt-8 text-gray-600 max-w-sm text-center">
|
||||
<div className="flex flex-col items-center justify-center text-bunker-200 h-screen w-screen mt-24">
|
||||
<p className="text-4xl text-bunker-100">Oops, your invite has expired.</p>
|
||||
<p className="text-lg my-6">Ask the administrator for a new one.</p>
|
||||
<p className="text-md text-bunker-400 max-w-sm text-center">
|
||||
Note: If it still {"doesn't work"}, please reach out to us at
|
||||
support@infisical.com
|
||||
</p>
|
||||
<div
|
||||
className=""
|
||||
>
|
||||
<Image
|
||||
src="/images/invitation-expired.svg"
|
||||
height={500}
|
||||
width={800}
|
||||
alt="google logo"
|
||||
></Image>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import Head from "next/head";
|
||||
import { useTranslation } from "next-i18next";
|
||||
|
||||
import Plan from "~/components/billing/Plan";
|
||||
import NavHeader from "~/components/navigation/NavHeader";
|
||||
import { STRIPE_PRODUCT_PRO, STRIPE_PRODUCT_STARTER } from "~/utilities/config";
|
||||
import { getTranslatedServerSideProps } from "~/utilities/withTranslateProps";
|
||||
|
||||
import getOrganizationSubscriptions from "../../api/organization/GetOrgSubscription";
|
||||
import getOrganizationUsers from "../../api/organization/GetOrgUsers";
|
||||
@@ -12,36 +14,38 @@ export default function SettingsBilling() {
|
||||
let [currentPlan, setCurrentPlan] = useState("");
|
||||
let [numUsers, setNumUsers] = useState("");
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const plans = [
|
||||
{
|
||||
key: 1,
|
||||
name: "Starter",
|
||||
price: "Free",
|
||||
priceExplanation: "up to 5 team members",
|
||||
text: "Manage any project with 5 members for free!",
|
||||
subtext: "$5 per member/month afterwards.",
|
||||
buttonTextMain: "Downgrade",
|
||||
buttonTextSecondary: "Learn More",
|
||||
name: t("billing:starter.name"),
|
||||
price: t("billing:free"),
|
||||
priceExplanation: t("billing:starter.price-explanation"),
|
||||
text: t("billing:starter.text"),
|
||||
subtext: t("billing:starter.subtext"),
|
||||
buttonTextMain: t("billing:downgrade"),
|
||||
buttonTextSecondary: t("billing:learn-more"),
|
||||
current: currentPlan == STRIPE_PRODUCT_STARTER,
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
name: "Professional",
|
||||
name: t("billing:professional.name"),
|
||||
price: "$9",
|
||||
priceExplanation: "/member/month",
|
||||
subtext: "Includes unlimited projects & members.",
|
||||
text: "Keep up with key management as you grow.",
|
||||
buttonTextMain: "Upgrade",
|
||||
buttonTextSecondary: "Learn More",
|
||||
priceExplanation: t("billing:professional.price-explanation"),
|
||||
subtext: t("billing:professional.subtext"),
|
||||
text: t("billing:professional.text"),
|
||||
buttonTextMain: t("billing:upgrade"),
|
||||
buttonTextSecondary: t("billing:learn-more"),
|
||||
current: currentPlan == STRIPE_PRODUCT_PRO,
|
||||
},
|
||||
{
|
||||
key: 3,
|
||||
name: "Enterprise",
|
||||
price: "Custom Pricing",
|
||||
text: "More control for advanced organizations.",
|
||||
buttonTextMain: "Schedule a Demo",
|
||||
buttonTextSecondary: "Learn More",
|
||||
name: t("billing:enterprise.name"),
|
||||
price: t("billing:custom-pricing"),
|
||||
text: t("billing:enterprise.text"),
|
||||
buttonTextMain: t("billing:schedule-demo"),
|
||||
buttonTextSecondary: t("billing:learn-more"),
|
||||
current: false,
|
||||
},
|
||||
];
|
||||
@@ -61,30 +65,32 @@ export default function SettingsBilling() {
|
||||
return (
|
||||
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
|
||||
<Head>
|
||||
<title>Settings - Billing</title>
|
||||
<title>{t("common:head-title", { title: t("billing:title") })}</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Head>
|
||||
<div className="flex flex-row">
|
||||
<div className="w-full max-h-screen pb-2 overflow-y-auto">
|
||||
<NavHeader pageName="Usage & Billing" />
|
||||
<NavHeader pageName={t("billing:title")} />
|
||||
<div className="flex flex-row justify-between items-center ml-6 my-8 text-xl max-w-5xl">
|
||||
<div className="flex flex-col justify-start items-start text-3xl">
|
||||
<p className="font-semibold mr-4 text-gray-200">
|
||||
Usage & Billing
|
||||
{t("billing:title")}
|
||||
</p>
|
||||
<p className="font-normal mr-4 text-gray-400 text-base">
|
||||
View and manage your {"organization's"} subscription here.
|
||||
{t("billing:description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col ml-6 text-mineshaft-50">
|
||||
<p className="text-xl font-semibold">Subscription</p>
|
||||
<p className="text-xl font-semibold">{t("billing:subscription")}</p>
|
||||
<div className="flex flex-row mt-4 overflow-x-auto">
|
||||
{plans.map((plan) => (
|
||||
<Plan key={plan.name} plan={plan} />
|
||||
))}
|
||||
</div>
|
||||
<p className="text-xl font-bold mt-12">Current Usage</p>
|
||||
<p className="text-xl font-bold mt-12">
|
||||
{t("billing:current-usage")}
|
||||
</p>
|
||||
<div className="flex flex-row">
|
||||
<div className="mr-4 mt-8 text-gray-300 w-60 pt-6 pb-10 rounded-md bg-white/5 flex justify-center items-center flex flex-col">
|
||||
<p className="text-6xl font-bold">{numUsers}</p>
|
||||
@@ -107,3 +113,8 @@ export default function SettingsBilling() {
|
||||
}
|
||||
|
||||
SettingsBilling.requireAuth = true;
|
||||
|
||||
export const getServerSideProps = getTranslatedServerSideProps([
|
||||
"settings",
|
||||
"billing",
|
||||
]);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from "react";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import {
|
||||
faMagnifyingGlass,
|
||||
faPlus,
|
||||
@@ -9,13 +10,14 @@ import {
|
||||
import { faCheck } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
import Button from '~/components/basic/buttons/Button';
|
||||
import AddIncidentContactDialog from '~/components/basic/dialog/AddIncidentContactDialog';
|
||||
import AddUserDialog from '~/components/basic/dialog/AddUserDialog';
|
||||
import InputField from '~/components/basic/InputField';
|
||||
import UserTable from '~/components/basic/table/UserTable';
|
||||
import NavHeader from '~/components/navigation/NavHeader';
|
||||
import guidGenerator from '~/utilities/randomId';
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import AddIncidentContactDialog from "~/components/basic/dialog/AddIncidentContactDialog";
|
||||
import AddUserDialog from "~/components/basic/dialog/AddUserDialog";
|
||||
import InputField from "~/components/basic/InputField";
|
||||
import UserTable from "~/components/basic/table/UserTable";
|
||||
import NavHeader from "~/components/navigation/NavHeader";
|
||||
import guidGenerator from "~/utilities/randomId";
|
||||
import { getTranslatedServerSideProps } from "~/utilities/withTranslateProps";
|
||||
|
||||
import addUserToOrg from '../../api/organization/addUserToOrg';
|
||||
import deleteIncidentContact from '../../api/organization/deleteIncidentContact';
|
||||
@@ -49,6 +51,8 @@ export default function SettingsOrg() {
|
||||
const [email, setEmail] = useState('');
|
||||
const [currentPlan, setCurrentPlan] = useState('');
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(async () => {
|
||||
let org = await getOrganization({
|
||||
orgId: localStorage.getItem('orgData.id')
|
||||
@@ -156,12 +160,14 @@ export default function SettingsOrg() {
|
||||
return (
|
||||
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
|
||||
<Head>
|
||||
<title>Settings</title>
|
||||
<title>
|
||||
{t("common:head-title", { title: t("settings-org:title") })}
|
||||
</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Head>
|
||||
<div className="flex flex-row">
|
||||
<div className="w-full max-h-screen pb-2 overflow-y-auto">
|
||||
<NavHeader pageName="Organization Settings" />
|
||||
<NavHeader pageName={t("settings-org:title")} />
|
||||
<AddIncidentContactDialog
|
||||
isOpen={isAddIncidentContactOpen}
|
||||
closeModal={closeAddIncidentContactModal}
|
||||
@@ -172,10 +178,10 @@ export default function SettingsOrg() {
|
||||
<div className="flex flex-row justify-between items-center ml-6 my-8 text-xl max-w-5xl">
|
||||
<div className="flex flex-col justify-start items-start text-3xl">
|
||||
<p className="font-semibold mr-4 text-gray-200">
|
||||
Organization Settings
|
||||
{t("settings-org:title")}
|
||||
</p>
|
||||
<p className="font-normal mr-4 text-gray-400 text-base">
|
||||
View and manage your organization here.
|
||||
{t("settings-org:description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -185,7 +191,7 @@ export default function SettingsOrg() {
|
||||
<div className="bg-white/5 rounded-md px-6 py-4 flex flex-col items-start flex flex-col items-start w-full mb-6">
|
||||
<div className="max-h-28 w-full max-w-md mr-auto">
|
||||
<p className="font-semibold mr-4 text-gray-200 text-xl mb-2">
|
||||
Display Name
|
||||
{t("common:display-name")}
|
||||
</p>
|
||||
<InputField
|
||||
// label="Organization Name"
|
||||
@@ -199,13 +205,13 @@ export default function SettingsOrg() {
|
||||
<div className="flex justify-start w-full">
|
||||
<div className={`flex justify-start max-w-sm mt-4 mb-2`}>
|
||||
<Button
|
||||
text="Save Changes"
|
||||
text={t("common:save-changes")}
|
||||
onButtonPressed={() => submitChanges(orgName)}
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
active={buttonReady}
|
||||
iconDisabled={faCheck}
|
||||
textDisabled="Saved"
|
||||
textDisabled={t("common:saved")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -214,11 +220,10 @@ export default function SettingsOrg() {
|
||||
</div>
|
||||
<div className="bg-white/5 rounded-md px-6 pt-6 pb-2 flex flex-col items-start flex flex-col items-start w-full mb-6">
|
||||
<p className="font-semibold mr-4 text-white text-xl">
|
||||
Organization Members
|
||||
{t("section-members:org-members")}
|
||||
</p>
|
||||
<p className="mr-4 text-gray-400 mt-2 mb-2">
|
||||
Manage members of your organization. These users could
|
||||
afterwards be formed into projects.
|
||||
{t("section-members:org-members-description")}
|
||||
</p>
|
||||
<AddUserDialog
|
||||
isOpen={isAddUserOpen}
|
||||
@@ -241,12 +246,12 @@ export default function SettingsOrg() {
|
||||
className="pl-2 text-gray-400 rounded-r-md bg-white/5 w-full h-full outline-none"
|
||||
value={searchUsers}
|
||||
onChange={(e) => setSearchUsers(e.target.value)}
|
||||
placeholder={'Search members...'}
|
||||
placeholder={t("section-members:search-members")}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2 ml-2 min-w-max flex flex-row items-start justify-start">
|
||||
<Button
|
||||
text="Add Member"
|
||||
text={t("section-members:add-member")}
|
||||
onButtonPressed={openAddUserModal}
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
@@ -276,16 +281,15 @@ export default function SettingsOrg() {
|
||||
<div className="flex flex-row max-w-5xl justify-between items-center w-full">
|
||||
<div className="flex flex-col justify-between w-full max-w-3xl">
|
||||
<p className="text-xl font-semibold mb-3 min-w-max">
|
||||
Incident Contacts
|
||||
{t("section-incident:incident-contacts")}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mb-2 min-w-max">
|
||||
These contacts will be notified in the unlikely event of a
|
||||
severe incident.
|
||||
{t("section-incident:incident-contacts-description")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-4 mb-2 min-w-max flex flex-row items-end justify-end justify-center">
|
||||
<Button
|
||||
text="Add Contact"
|
||||
text={t("section-incident:add-contact")}
|
||||
onButtonPressed={openAddIncidentContactModal}
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
@@ -302,7 +306,7 @@ export default function SettingsOrg() {
|
||||
className="pl-2 text-gray-400 rounded-tr-md bg-white/5 w-full h-full outline-none"
|
||||
value={searchIncidentContact}
|
||||
onChange={(e) => setSearchIncidentContact(e.target.value)}
|
||||
placeholder={'Search...'}
|
||||
placeholder={t("common:search")}
|
||||
/>
|
||||
</div>
|
||||
{incidentContacts?.filter((email) =>
|
||||
@@ -330,7 +334,9 @@ export default function SettingsOrg() {
|
||||
))
|
||||
) : (
|
||||
<div className="w-full flex flex-row justify-center mt-6 max-w-4xl ml-6">
|
||||
<p className="text-gray-400">No incident contacts found.</p>
|
||||
<p className="text-gray-400">
|
||||
{t("section-incident:no-incident-contacts")}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -378,3 +384,10 @@ export default function SettingsOrg() {
|
||||
}
|
||||
|
||||
SettingsOrg.requireAuth = true;
|
||||
|
||||
export const getServerSideProps = getTranslatedServerSideProps([
|
||||
"settings",
|
||||
"settings-org",
|
||||
"section-incident",
|
||||
"section-members",
|
||||
]);
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { faCheck, faX } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import InputField from "~/components/basic/InputField";
|
||||
import ListBox from "~/components/basic/Listbox";
|
||||
import NavHeader from "~/components/navigation/NavHeader";
|
||||
import changePassword from "~/components/utilities/cryptography/changePassword";
|
||||
import issueBackupKey from "~/components/utilities/cryptography/issueBackupKey";
|
||||
import passwordCheck from "~/utilities/checks/PasswordCheck";
|
||||
import { getTranslatedServerSideProps } from "~/utilities/withTranslateProps";
|
||||
|
||||
import getUser from "../../api/user/getUser";
|
||||
|
||||
@@ -26,6 +30,15 @@ export default function PersonalSettings() {
|
||||
const [backupKeyIssued, setBackupKeyIssued] = useState(false);
|
||||
const [backupKeyError, setBackupKeyError] = useState(false);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const lang = router.locale ?? "en";
|
||||
|
||||
const setLanguage = async (to) => {
|
||||
router.push(router.asPath, router.asPath, { locale: to });
|
||||
localStorage.setItem("lang", to);
|
||||
};
|
||||
|
||||
useEffect(async () => {
|
||||
let user = await getUser();
|
||||
setPersonalEmail(user.email);
|
||||
@@ -35,19 +48,24 @@ export default function PersonalSettings() {
|
||||
return (
|
||||
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
|
||||
<Head>
|
||||
<title>Personal Settings</title>
|
||||
<title>
|
||||
{t("common:head-title", { title: t("settings-personal:title") })}
|
||||
</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Head>
|
||||
<div className="flex flex-row">
|
||||
<div className="w-full max-h-screen pb-2 overflow-y-auto">
|
||||
<NavHeader pageName="Personal Settings" isProjectRelated={false} />
|
||||
<NavHeader
|
||||
pageName={t("settings-personal:title")}
|
||||
isProjectRelated={false}
|
||||
/>
|
||||
<div className="flex flex-row justify-between items-center ml-6 mt-8 mb-6 text-xl max-w-5xl">
|
||||
<div className="flex flex-col justify-start items-start text-3xl">
|
||||
<p className="font-semibold mr-4 text-gray-200">
|
||||
Personal Settings
|
||||
{t("settings-personal:title")}
|
||||
</p>
|
||||
<p className="font-normal mr-4 text-gray-400 text-base">
|
||||
View and manage your personal information here.
|
||||
{t("settings-personal:description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -100,17 +118,32 @@ export default function PersonalSettings() {
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white/5 rounded-md px-6 pt-6 pb-6 flex flex-col items-start flex flex-col items-start w-full mb-6 mt-4">
|
||||
<p className="text-xl font-semibold self-start">
|
||||
{t("settings-personal:change-language")}
|
||||
</p>
|
||||
<div className="max-h-28 w-ful mt-4">
|
||||
<ListBox
|
||||
selected={lang}
|
||||
onChange={setLanguage}
|
||||
data={["en", "ko"]}
|
||||
width="full"
|
||||
text={`${t("common:language")}: `}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/5 rounded-md px-6 pt-5 pb-6 flex flex-col items-start flex flex-col items-start w-full mb-6">
|
||||
<div className="flex flex-row max-w-5xl justify-between items-center w-full">
|
||||
<div className="flex flex-col justify-between w-full max-w-3xl">
|
||||
<p className="text-xl font-semibold mb-3 min-w-max">
|
||||
Change password
|
||||
{t("section-password:change")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="max-w-xl w-full">
|
||||
<InputField
|
||||
label="Current Password"
|
||||
label={t("section-password:current")}
|
||||
onChangeHandler={(password) => {
|
||||
setCurrentPassword(password);
|
||||
}}
|
||||
@@ -118,13 +151,13 @@ export default function PersonalSettings() {
|
||||
value={currentPassword}
|
||||
isRequired
|
||||
error={currentPasswordError}
|
||||
errorText="The current password may be wrong"
|
||||
errorText={t("section-password:current-wrong")}
|
||||
autoComplete="current-password"
|
||||
id="current-password"
|
||||
/>
|
||||
<div className="py-2"></div>
|
||||
<InputField
|
||||
label="New Password"
|
||||
label={t("section-password:new")}
|
||||
onChangeHandler={(password) => {
|
||||
setNewPassword(password);
|
||||
passwordCheck({
|
||||
@@ -152,7 +185,7 @@ export default function PersonalSettings() {
|
||||
passwordErrorNumber ? (
|
||||
<div className="w-full mt-3 bg-white/5 px-2 flex flex-col items-start py-2 rounded-md max-w-xl mb-2">
|
||||
<div className={`text-gray-400 text-sm mb-1`}>
|
||||
Password should contain at least:
|
||||
{t("section-password:validate-base")}
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorLength ? (
|
||||
@@ -171,7 +204,7 @@ export default function PersonalSettings() {
|
||||
passwordErrorLength ? "text-gray-400" : "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
14 characters
|
||||
{t("section-password:validate-length")}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
@@ -193,7 +226,7 @@ export default function PersonalSettings() {
|
||||
: "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
1 lowercase character
|
||||
{t("section-password:validate-case")}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
@@ -213,7 +246,7 @@ export default function PersonalSettings() {
|
||||
passwordErrorNumber ? "text-gray-400" : "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
1 number
|
||||
{t("section-password:validate-number")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -222,7 +255,7 @@ export default function PersonalSettings() {
|
||||
)}
|
||||
<div className="flex flex-row items-center mt-3 w-52 pr-3">
|
||||
<Button
|
||||
text="Change Password"
|
||||
text={t("section-password:change")}
|
||||
onButtonPressed={() => {
|
||||
if (
|
||||
!passwordErrorLength &&
|
||||
@@ -251,7 +284,7 @@ export default function PersonalSettings() {
|
||||
passwordErrorNumber
|
||||
)
|
||||
}
|
||||
textDisabled="Change Password"
|
||||
textDisabled={t("section-password:change")}
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
@@ -266,34 +299,32 @@ export default function PersonalSettings() {
|
||||
<div className="flex flex-row max-w-5xl justify-between items-center w-full">
|
||||
<div className="flex flex-col justify-between w-full max-w-3xl">
|
||||
<p className="text-xl font-semibold mb-3 min-w-max">
|
||||
Emergency Kit
|
||||
{t("settings-personal:emergency.name")}
|
||||
</p>
|
||||
<p className="text-sm text-mineshaft-300 min-w-max">
|
||||
Your Emergency Kit contains the information you’ll need to
|
||||
sign in to your Infisical account.
|
||||
{t("settings-personal:emergency.text1")}
|
||||
</p>
|
||||
<p className="text-sm text-mineshaft-300 mb-5 min-w-max">
|
||||
Only the latest issued Emergency Kit remains valid. To get a
|
||||
new Emergency Kit, verify your password.
|
||||
{t("settings-personal:emergency.text2")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full max-w-xl mb-4">
|
||||
<InputField
|
||||
label="Current Password"
|
||||
label={t("section-password:current")}
|
||||
onChangeHandler={setBackupPassword}
|
||||
type="password"
|
||||
value={backupPassword}
|
||||
isRequired
|
||||
error={backupKeyError}
|
||||
errorText="The current password is wrong"
|
||||
errorText={t("section-password:current-wrong")}
|
||||
autoComplete="current-password"
|
||||
id="current-password"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row items-center mt-3 w-full w-60">
|
||||
<Button
|
||||
text="Download Emergency Kit"
|
||||
text={t("settings-personal:emergency.download")}
|
||||
onButtonPressed={() => {
|
||||
issueBackupKey({
|
||||
email: personalEmail,
|
||||
@@ -306,7 +337,7 @@ export default function PersonalSettings() {
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
active={backupPassword != ""}
|
||||
textDisabled="Download Emergency Kit"
|
||||
textDisabled={t("settings-personal:emergency.download")}
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
@@ -324,3 +355,9 @@ export default function PersonalSettings() {
|
||||
}
|
||||
|
||||
PersonalSettings.requireAuth = true;
|
||||
|
||||
export const getServerSideProps = getTranslatedServerSideProps([
|
||||
"settings",
|
||||
"settings-personal",
|
||||
"section-password",
|
||||
]);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { faCheck, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
@@ -8,6 +9,7 @@ import AddServiceTokenDialog from "~/components/basic/dialog/AddServiceTokenDial
|
||||
import InputField from "~/components/basic/InputField";
|
||||
import ServiceTokenTable from "~/components/basic/table/ServiceTokenTable";
|
||||
import NavHeader from "~/components/navigation/NavHeader";
|
||||
import { getTranslatedServerSideProps } from "~/utilities/withTranslateProps";
|
||||
|
||||
import getServiceTokens from "../../api/serviceToken/getServiceTokens";
|
||||
import deleteWorkspace from "../../api/workspace/deleteWorkspace";
|
||||
@@ -25,6 +27,8 @@ export default function SettingsBasic() {
|
||||
let [isAddServiceTokenDialogOpen, setIsAddServiceTokenDialogOpen] =
|
||||
useState(false);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(async () => {
|
||||
let userWorkspaces = await getWorkspaces();
|
||||
userWorkspaces.map((userWorkspace) => {
|
||||
@@ -89,7 +93,9 @@ export default function SettingsBasic() {
|
||||
return (
|
||||
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
|
||||
<Head>
|
||||
<title>Settings</title>
|
||||
<title>
|
||||
{t("common:head-title", { title: t("settings-project:title") })}
|
||||
</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Head>
|
||||
<AddServiceTokenDialog
|
||||
@@ -100,14 +106,17 @@ export default function SettingsBasic() {
|
||||
/>
|
||||
<div className="flex flex-row mr-6 max-w-5xl">
|
||||
<div className="w-full max-h-screen pb-2 overflow-y-auto">
|
||||
<NavHeader pageName="Project Settings" isProjectRelated={true} />
|
||||
<NavHeader
|
||||
pageName={t("settings-project:title")}
|
||||
isProjectRelated={true}
|
||||
/>
|
||||
<div className="flex flex-row justify-between items-center ml-6 my-8 text-xl max-w-5xl">
|
||||
<div className="flex flex-col justify-start items-start text-3xl">
|
||||
<p className="font-semibold mr-4 text-gray-200">
|
||||
Project Settings
|
||||
{t("settings-project:title")}
|
||||
</p>
|
||||
<p className="font-normal mr-4 text-gray-400 text-base">
|
||||
These settings only apply to the currently selected Project.
|
||||
{t("settings-project:description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -115,7 +124,9 @@ export default function SettingsBasic() {
|
||||
<div className="flex flex-col">
|
||||
<div className="min-w-md mt-2 flex flex-col items-start">
|
||||
<div className="bg-white/5 rounded-md px-6 pt-6 pb-4 flex flex-col items-start flex flex-col items-start w-full mb-6 pt-2">
|
||||
<p className="text-xl font-semibold mb-4">Display Name</p>
|
||||
<p className="text-xl font-semibold mb-4">
|
||||
{t("common:display-name")}
|
||||
</p>
|
||||
<div className="max-h-28 w-full max-w-md mr-auto">
|
||||
<InputField
|
||||
onChangeHandler={modifyWorkspaceName}
|
||||
@@ -128,7 +139,7 @@ export default function SettingsBasic() {
|
||||
<div className="flex justify-start w-full">
|
||||
<div className={`flex justify-start max-w-sm mt-4 mb-2`}>
|
||||
<Button
|
||||
text="Save Changes"
|
||||
text={t("common:save-changes")}
|
||||
onButtonPressed={() => submitChanges(workspaceName)}
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
@@ -140,15 +151,14 @@ export default function SettingsBasic() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white/5 rounded-md px-6 pt-6 pb-2 flex flex-col items-start flex flex-col items-start w-full mb-6 mt-4">
|
||||
<p className="text-xl font-semibold self-start">Project ID</p>
|
||||
<p className="text-xl font-semibold self-start">
|
||||
{t("common:project-id")}
|
||||
</p>
|
||||
<p className="text-base text-gray-400 font-normal self-start mt-4">
|
||||
To integrate Infisical into your code base and get automatic
|
||||
injection of environmental variables, you should use the
|
||||
following Project ID.
|
||||
{t("settings-project:project-id-description")}
|
||||
</p>
|
||||
<p className="text-base text-gray-400 font-normal self-start">
|
||||
For more guidance, including code snipets for various
|
||||
languages and frameworks, see{" "}
|
||||
{t("settings-project:project-id-description2")}
|
||||
{/* eslint-disable-next-line react/jsx-no-target-blank */}
|
||||
<a
|
||||
href="https://infisical.com/docs/getting-started/introduction"
|
||||
@@ -156,7 +166,7 @@ export default function SettingsBasic() {
|
||||
rel="noopener"
|
||||
className="text-primary hover:opacity-80 duration-200"
|
||||
>
|
||||
Infisical Docs.
|
||||
{t("settings-project:docs")}
|
||||
</a>
|
||||
</p>
|
||||
<div className="max-h-28 w-ful">
|
||||
@@ -166,7 +176,7 @@ export default function SettingsBasic() {
|
||||
placeholder=""
|
||||
isRequired
|
||||
static
|
||||
text="This is your project's auto-generated unique identifier. It can't be changed."
|
||||
text={t("settings-project:auto-generated")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -174,16 +184,15 @@ export default function SettingsBasic() {
|
||||
<div className="flex flex-row justify-between w-full">
|
||||
<div className="flex flex-col w-full">
|
||||
<p className="text-xl font-semibold mb-3">
|
||||
Service Tokens
|
||||
{t("section-token:service-tokens")}
|
||||
</p>
|
||||
<p className="text-base text-gray-400 mb-4">
|
||||
Every service token is specific to you, a certain
|
||||
project and a certain environment within this project.
|
||||
{t("section-token:service-tokens-description")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="w-48">
|
||||
<Button
|
||||
text="Add New Token"
|
||||
text={t("section-token:add-new")}
|
||||
onButtonPressed={() => {
|
||||
setIsAddServiceTokenDialogOpen(true);
|
||||
}}
|
||||
@@ -237,15 +246,15 @@ export default function SettingsBasic() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white/5 rounded-md px-6 pt-6 pb-6 border-l border-red pl-6 flex flex-col items-start flex flex-col items-start w-full mb-6 mt-4 pb-4 pt-2">
|
||||
<p className="text-xl font-bold text-red">Danger Zone</p>
|
||||
<p className="text-xl font-bold text-red">
|
||||
{t("settings-project:danger-zone")}
|
||||
</p>
|
||||
<p className="mt-2 text-md text-gray-400">
|
||||
As soon as you delete this project, you will not be able to undo
|
||||
it. This will immediately remove all the keys. If you still want
|
||||
to do that, please enter the name of the project below.
|
||||
{t("settings-project:danger-zone-note")}
|
||||
</p>
|
||||
<div className="max-h-28 w-full max-w-md mr-auto mt-4">
|
||||
<InputField
|
||||
label="Project to be Deleted"
|
||||
label={t("settings-project:project-to-delete")}
|
||||
onChangeHandler={setWorkspaceToBeDeletedName}
|
||||
type="varName"
|
||||
value={workspaceToBeDeletedName}
|
||||
@@ -258,11 +267,10 @@ export default function SettingsBasic() {
|
||||
className="max-w-md mt-6 w-full inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2.5 text-sm font-medium text-gray-400 hover:bg-red hover:text-white hover:font-semibold hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||
onClick={executeDeletingWorkspace}
|
||||
>
|
||||
Delete Project
|
||||
{t("settings-project:delete-project")}
|
||||
</button>
|
||||
<p className="mt-0.5 ml-1 text-xs text-gray-500">
|
||||
Note: You can only delete a project in case you have more than
|
||||
one.
|
||||
{t("settings-project:delete-project-note")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -273,3 +281,9 @@ export default function SettingsBasic() {
|
||||
}
|
||||
|
||||
SettingsBasic.requireAuth = true;
|
||||
|
||||
export const getServerSideProps = getTranslatedServerSideProps([
|
||||
"settings",
|
||||
"settings-project",
|
||||
"section-token",
|
||||
]);
|
||||
|
||||
+129
-116
@@ -1,71 +1,73 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import ReactCodeInput from 'react-code-input';
|
||||
import Head from 'next/head';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { faCheck, faWarning, faX } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { useEffect, useState } from "react";
|
||||
import ReactCodeInput from "react-code-input";
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { faCheck, faWarning, faX } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import Button from '~/components/basic/buttons/Button';
|
||||
import Error from '~/components/basic/Error';
|
||||
import InputField from '~/components/basic/InputField';
|
||||
import Aes256Gcm from '~/components/utilities/cryptography/aes-256-gcm';
|
||||
import issueBackupKey from '~/components/utilities/cryptography/issueBackupKey';
|
||||
import attemptLogin from '~/utilities/attemptLogin';
|
||||
import passwordCheck from '~/utilities/checks/PasswordCheck';
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import Error from "~/components/basic/Error";
|
||||
import InputField from "~/components/basic/InputField";
|
||||
import Aes256Gcm from "~/components/utilities/cryptography/aes-256-gcm";
|
||||
import issueBackupKey from "~/components/utilities/cryptography/issueBackupKey";
|
||||
import { getTranslatedStaticProps } from "~/components/utilities/withTranslateProps";
|
||||
import attemptLogin from "~/utilities/attemptLogin";
|
||||
import passwordCheck from "~/utilities/checks/PasswordCheck";
|
||||
|
||||
import checkEmailVerificationCode from './api/auth/CheckEmailVerificationCode';
|
||||
import completeAccountInformationSignup from './api/auth/CompleteAccountInformationSignup';
|
||||
import sendVerificationEmail from './api/auth/SendVerificationEmail';
|
||||
import getWorkspaces from './api/workspace/getWorkspaces';
|
||||
import checkEmailVerificationCode from "./api/auth/CheckEmailVerificationCode";
|
||||
import completeAccountInformationSignup from "./api/auth/CompleteAccountInformationSignup";
|
||||
import sendVerificationEmail from "./api/auth/SendVerificationEmail";
|
||||
import getWorkspaces from "./api/workspace/getWorkspaces";
|
||||
|
||||
// const ReactCodeInput = dynamic(import("react-code-input"));
|
||||
const nacl = require('tweetnacl');
|
||||
const jsrp = require('jsrp');
|
||||
nacl.util = require('tweetnacl-util');
|
||||
const nacl = require("tweetnacl");
|
||||
const jsrp = require("jsrp");
|
||||
nacl.util = require("tweetnacl-util");
|
||||
const client = new jsrp.client();
|
||||
|
||||
// The stye for the verification code input
|
||||
const props = {
|
||||
inputStyle: {
|
||||
fontFamily: 'monospace',
|
||||
margin: '4px',
|
||||
MozAppearance: 'textfield',
|
||||
width: '55px',
|
||||
borderRadius: '5px',
|
||||
fontSize: '24px',
|
||||
height: '55px',
|
||||
paddingLeft: '7',
|
||||
backgroundColor: '#0d1117',
|
||||
color: 'white',
|
||||
border: '1px solid gray',
|
||||
textAlign: 'center',
|
||||
fontFamily: "monospace",
|
||||
margin: "4px",
|
||||
MozAppearance: "textfield",
|
||||
width: "55px",
|
||||
borderRadius: "5px",
|
||||
fontSize: "24px",
|
||||
height: "55px",
|
||||
paddingLeft: "7",
|
||||
backgroundColor: "#0d1117",
|
||||
color: "white",
|
||||
border: "1px solid gray",
|
||||
textAlign: "center",
|
||||
},
|
||||
} as const;
|
||||
const propsPhone = {
|
||||
inputStyle: {
|
||||
fontFamily: 'monospace',
|
||||
margin: '4px',
|
||||
MozAppearance: 'textfield',
|
||||
width: '40px',
|
||||
borderRadius: '5px',
|
||||
fontSize: '24px',
|
||||
height: '40px',
|
||||
paddingLeft: '7',
|
||||
backgroundColor: '#0d1117',
|
||||
color: 'white',
|
||||
border: '1px solid gray',
|
||||
textAlign: 'center',
|
||||
fontFamily: "monospace",
|
||||
margin: "4px",
|
||||
MozAppearance: "textfield",
|
||||
width: "40px",
|
||||
borderRadius: "5px",
|
||||
fontSize: "24px",
|
||||
height: "40px",
|
||||
paddingLeft: "7",
|
||||
backgroundColor: "#0d1117",
|
||||
color: "white",
|
||||
border: "1px solid gray",
|
||||
textAlign: "center",
|
||||
},
|
||||
} as const;
|
||||
|
||||
export default function SignUp() {
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [firstName, setFirstName] = useState('');
|
||||
const [lastName, setLastName] = useState('');
|
||||
const [code, setCode] = useState('');
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [firstName, setFirstName] = useState("");
|
||||
const [lastName, setLastName] = useState("");
|
||||
const [code, setCode] = useState("");
|
||||
const [codeError, setCodeError] = useState(false);
|
||||
const [firstNameError, setFirstNameError] = useState(false);
|
||||
const [lastNameError, setLastNameError] = useState(false);
|
||||
@@ -76,23 +78,26 @@ export default function SignUp() {
|
||||
const [passwordErrorSpecialChar, setPasswordErrorSpecialChar] =
|
||||
useState(false);
|
||||
const [emailError, setEmailError] = useState(false);
|
||||
const [emailErrorMessage, setEmailErrorMessage] = useState('');
|
||||
const [emailErrorMessage, setEmailErrorMessage] = useState("");
|
||||
const [step, setStep] = useState(1);
|
||||
const router = useRouter();
|
||||
const [errorLogin, setErrorLogin] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isResendingVerificationEmail, setIsResendingVerificationEmail] = useState(false);
|
||||
const [isResendingVerificationEmail, setIsResendingVerificationEmail] =
|
||||
useState(false);
|
||||
const [backupKeyError, setBackupKeyError] = useState(false);
|
||||
const [verificationToken, setVerificationToken] = useState('');
|
||||
const [verificationToken, setVerificationToken] = useState("");
|
||||
const [backupKeyIssued, setBackupKeyIssued] = useState(false);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
const tryAuth = async () => {
|
||||
try {
|
||||
const userWorkspaces = await getWorkspaces();
|
||||
router.push('/dashboard/' + userWorkspaces[0]._id);
|
||||
router.push("/dashboard/" + userWorkspaces[0]._id);
|
||||
} catch (error) {
|
||||
console.log('Error - Not logged in yet');
|
||||
console.log("Error - Not logged in yet");
|
||||
}
|
||||
};
|
||||
tryAuth();
|
||||
@@ -110,7 +115,7 @@ export default function SignUp() {
|
||||
} else if (step == 2) {
|
||||
// Checking if the code matches the email.
|
||||
const response = await checkEmailVerificationCode({ email, code });
|
||||
if (response.status === 200 || code == '111222') {
|
||||
if (response.status === 200 || code == "111222") {
|
||||
setVerificationToken((await response.json()).token);
|
||||
setStep(3);
|
||||
} else {
|
||||
@@ -128,15 +133,15 @@ export default function SignUp() {
|
||||
let emailCheckBool = false;
|
||||
if (!email) {
|
||||
setEmailError(true);
|
||||
setEmailErrorMessage('Please enter your email.');
|
||||
setEmailErrorMessage("Please enter your email.");
|
||||
emailCheckBool = true;
|
||||
} else if (
|
||||
!email.includes('@') ||
|
||||
!email.includes('.') ||
|
||||
!email.includes("@") ||
|
||||
!email.includes(".") ||
|
||||
!/[a-z]/.test(email)
|
||||
) {
|
||||
setEmailError(true);
|
||||
setEmailErrorMessage('Please enter a valid email.');
|
||||
setEmailErrorMessage("Please enter a valid email.");
|
||||
emailCheckBool = true;
|
||||
} else {
|
||||
setEmailError(false);
|
||||
@@ -188,11 +193,11 @@ export default function SignUp() {
|
||||
.slice(0, 32)
|
||||
.padStart(
|
||||
32 + (password.slice(0, 32).length - new Blob([password]).size),
|
||||
'0',
|
||||
"0"
|
||||
),
|
||||
}) as { ciphertext: string; iv: string; tag: string };
|
||||
|
||||
localStorage.setItem('PRIVATE_KEY', PRIVATE_KEY);
|
||||
localStorage.setItem("PRIVATE_KEY", PRIVATE_KEY);
|
||||
|
||||
client.init(
|
||||
{
|
||||
@@ -206,7 +211,7 @@ export default function SignUp() {
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
organizationName: firstName + '\'s organization',
|
||||
organizationName: firstName + "'s organization",
|
||||
publicKey: PUBLIC_KEY,
|
||||
ciphertext,
|
||||
iv,
|
||||
@@ -220,10 +225,10 @@ export default function SignUp() {
|
||||
if (response.status === 200) {
|
||||
// response = await response.json();
|
||||
|
||||
localStorage.setItem('publicKey', PUBLIC_KEY);
|
||||
localStorage.setItem('encryptedPrivateKey', ciphertext);
|
||||
localStorage.setItem('iv', iv);
|
||||
localStorage.setItem('tag', tag);
|
||||
localStorage.setItem("publicKey", PUBLIC_KEY);
|
||||
localStorage.setItem("encryptedPrivateKey", ciphertext);
|
||||
localStorage.setItem("iv", iv);
|
||||
localStorage.setItem("tag", tag);
|
||||
|
||||
try {
|
||||
await attemptLogin(
|
||||
@@ -232,16 +237,16 @@ export default function SignUp() {
|
||||
setErrorLogin,
|
||||
router,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
);
|
||||
incrementStep();
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
@@ -284,8 +289,7 @@ export default function SignUp() {
|
||||
</div> */}
|
||||
<div className="flex flex-col items-center justify-center w-5/6 md:w-full md:p-2 max-h-28 max-w-xs md:max-w-md mx-auto mt-4 md:mt-4 text-sm text-center md:text-left">
|
||||
<p className="text-gray-400 mt-2 md:mx-0.5">
|
||||
By creating an account, you agree to our Terms and have read and
|
||||
acknowledged the Privacy Policy.
|
||||
{t("signup:step1-privacy")}
|
||||
</p>
|
||||
<div className="text-l mt-6 m-2 md:m-8 px-8 py-1 text-lg">
|
||||
<Button text="Get Started" type="submit" onButtonPressed={emailCheck} size="lg" />
|
||||
@@ -296,7 +300,7 @@ export default function SignUp() {
|
||||
<Link href="/login">
|
||||
<button type="button" className="w-max pb-3 hover:opacity-90 duration-200">
|
||||
<u className="font-normal text-sm text-primary-500">
|
||||
Have an account? Log in
|
||||
{t("signup:already-have-account")}
|
||||
</u>
|
||||
</button>
|
||||
</Link>
|
||||
@@ -309,10 +313,10 @@ export default function SignUp() {
|
||||
const step2 = (
|
||||
<div className="bg-bunker w-max mx-auto h-7/12 pt-10 pb-4 px-8 rounded-xl drop-shadow-xl mb-64 md:mb-16">
|
||||
<p className="text-l flex justify-center text-gray-400">
|
||||
{'We\'ve'} sent a verification email to{' '}
|
||||
{"We've"} sent a verification email to{" "}
|
||||
</p>
|
||||
<p className="text-l flex justify-center font-semibold my-2 text-gray-400">
|
||||
{email}{' '}
|
||||
{email}{" "}
|
||||
</p>
|
||||
<div className="hidden md:block">
|
||||
<ReactCodeInput
|
||||
@@ -336,11 +340,13 @@ export default function SignUp() {
|
||||
className="mt-2 mb-6"
|
||||
/>
|
||||
</div>
|
||||
{codeError && (
|
||||
<Error text="Oops. Your code is wrong. Please try again." />
|
||||
)}
|
||||
<div className="flex max-w-min flex-col items-center justify-center md:p-2 max-h-24 max-w-md mx-auto text-lg px-4 mt-4 mb-2">
|
||||
<Button text="Verify" onButtonPressed={incrementStep} size="lg" />
|
||||
{codeError && <Error text={t("signup:step2-code-error")} />}
|
||||
<div className="flex max-w-max min-w-28 flex-col items-center justify-center md:p-2 max-h-24 mx-auto text-lg px-4 mt-4 mb-2">
|
||||
<Button
|
||||
text={t("signup:verify") ?? ""}
|
||||
onButtonPressed={incrementStep}
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col items-center justify-center w-full max-h-24 max-w-md mx-auto pt-2">
|
||||
<div className="flex flex-row items-baseline gap-1 text-sm">
|
||||
@@ -349,12 +355,12 @@ export default function SignUp() {
|
||||
</span>
|
||||
<u className={`font-normal ${isResendingVerificationEmail ? 'text-gray-400' : 'text-primary-500 hover:opacity-90 duration-200'}`}>
|
||||
<button disabled={isLoading} onClick={resendVerificationEmail}>
|
||||
{isResendingVerificationEmail ? 'Resending...' : 'Resend'}
|
||||
{isResendingVerificationEmail ? "Resending..." : "Resend"}
|
||||
</button>
|
||||
</u>
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 pb-2">
|
||||
Make sure to check your spam inbox.
|
||||
{t("signup:step2-spam-alert")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -364,35 +370,43 @@ export default function SignUp() {
|
||||
const step3 = (
|
||||
<div className="bg-bunker w-max mx-auto h-7/12 py-10 px-8 rounded-xl drop-shadow-xl mb-36 md:mb-16">
|
||||
<p className="text-4xl font-bold flex justify-center mb-6 text-gray-400 mx-8 md:mx-16 text-transparent bg-clip-text bg-gradient-to-br from-sky-400 to-primary">
|
||||
Almost there!
|
||||
{t("signup:step3-message")}
|
||||
</p>
|
||||
<div className="relative z-0 flex items-center justify-end w-full md:p-2 rounded-lg max-h-24">
|
||||
<InputField
|
||||
label="First Name"
|
||||
label={t("common:first-name")}
|
||||
onChangeHandler={setFirstName}
|
||||
type="name"
|
||||
value={firstName}
|
||||
isRequired
|
||||
errorText="Please input your first name."
|
||||
errorText={
|
||||
t("common:validate-required", {
|
||||
name: t("common:first-name"),
|
||||
}) as string
|
||||
}
|
||||
error={firstNameError}
|
||||
autoComplete="given-name"
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2 flex items-center justify-center w-full md:p-2 rounded-lg max-h-24">
|
||||
<InputField
|
||||
label="Last Name"
|
||||
label={t("common:last-name")}
|
||||
onChangeHandler={setLastName}
|
||||
type="name"
|
||||
value={lastName}
|
||||
isRequired
|
||||
errorText="Please input your last name."
|
||||
errorText={
|
||||
t("common:validate-required", {
|
||||
name: t("common:last-name"),
|
||||
}) as string
|
||||
}
|
||||
error={lastNameError}
|
||||
autoComplete="family-name"
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2 flex flex-col items-center justify-center w-full md:p-2 rounded-lg max-h-60">
|
||||
<InputField
|
||||
label="Password"
|
||||
label={t("section-password:password")}
|
||||
onChangeHandler={(password: string) => {
|
||||
setPassword(password);
|
||||
passwordCheck({
|
||||
@@ -417,7 +431,7 @@ export default function SignUp() {
|
||||
passwordErrorNumber ? (
|
||||
<div className="w-full mt-4 bg-white/5 px-2 flex flex-col items-start py-2 rounded-md">
|
||||
<div className={`text-gray-400 text-sm mb-1`}>
|
||||
Password should contain at least:
|
||||
{t("section-password:validate-base")}
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorLength ? (
|
||||
@@ -433,10 +447,10 @@ export default function SignUp() {
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
passwordErrorLength ? 'text-gray-400' : 'text-gray-600'
|
||||
passwordErrorLength ? "text-gray-400" : "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
14 characters
|
||||
{t("section-password:validate-length")}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
@@ -453,10 +467,10 @@ export default function SignUp() {
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
passwordErrorLowerCase ? 'text-gray-400' : 'text-gray-600'
|
||||
passwordErrorLowerCase ? "text-gray-400" : "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
1 lowercase character
|
||||
{t("section-password:validate-case")}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
@@ -473,10 +487,10 @@ export default function SignUp() {
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
passwordErrorNumber ? 'text-gray-400' : 'text-gray-600'
|
||||
passwordErrorNumber ? "text-gray-400" : "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
1 number
|
||||
{t("section-password:validate-number")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -486,7 +500,7 @@ export default function SignUp() {
|
||||
</div>
|
||||
<div className="flex flex-col items-center justify-center md:p-2 max-h-48 max-w-max mx-auto text-lg px-2 py-3">
|
||||
<Button
|
||||
text="Sign Up"
|
||||
text={t("signup:signup") ?? ""}
|
||||
loading={isLoading}
|
||||
onButtonPressed={signupErrorCheck}
|
||||
size="lg"
|
||||
@@ -499,21 +513,15 @@ export default function SignUp() {
|
||||
const step4 = (
|
||||
<div className="bg-bunker flex flex-col items-center w-full max-w-xs md:max-w-lg mx-auto h-7/12 py-8 px-4 md:px-6 mx-1 mb-36 md:mb-16 rounded-xl drop-shadow-xl">
|
||||
<p className="text-4xl text-center font-semibold flex justify-center text-transparent bg-clip-text bg-gradient-to-br from-sky-400 to-primary">
|
||||
Save your Emergency Kit
|
||||
{t("signup:step4-message")}
|
||||
</p>
|
||||
<div className="flex flex-col items-center justify-center w-full mt-4 md:mt-8 max-w-md text-gray-400 text-md rounded-md px-2">
|
||||
<div>
|
||||
If you get locked out of your account, your Emergency Kit is the only
|
||||
way to sign in.
|
||||
</div>
|
||||
<div className="mt-3">
|
||||
We recommend you download it and keep it somewhere safe.
|
||||
</div>
|
||||
<div>{t("signup:step4-description1")}</div>
|
||||
<div className="mt-3">{t("signup:step4-description2")}</div>
|
||||
</div>
|
||||
<div className="w-full p-2 flex flex-row items-center bg-white/10 text-gray-400 rounded-md max-w-xs md:max-w-md mx-auto mt-4">
|
||||
<FontAwesomeIcon icon={faWarning} className="ml-2 mr-4 text-4xl" />
|
||||
It contains your Secret Key which we cannot access or recover for you if
|
||||
you lose it.
|
||||
{t("signup:step4-description3")}
|
||||
</div>
|
||||
<div className="flex flex-col items-center justify-center md:px-4 md:py-5 mt-2 px-2 py-3 max-h-24 max-w-max mx-auto text-lg">
|
||||
<Button
|
||||
@@ -522,11 +530,13 @@ export default function SignUp() {
|
||||
await issueBackupKey({
|
||||
email,
|
||||
password,
|
||||
personalName: firstName + ' ' + lastName,
|
||||
personalName: firstName + " " + lastName,
|
||||
setBackupKeyError,
|
||||
setBackupKeyIssued
|
||||
setBackupKeyIssued,
|
||||
});
|
||||
router.push('/dashboard/');
|
||||
const userWorkspaces = await getWorkspaces();
|
||||
const userWorkspace = userWorkspaces[0]._id;
|
||||
router.push("/home/" + userWorkspace);
|
||||
}}
|
||||
size="lg"
|
||||
/>
|
||||
@@ -549,16 +559,13 @@ export default function SignUp() {
|
||||
return (
|
||||
<div className="bg-bunker-800 h-screen flex flex-col items-center justify-center">
|
||||
<Head>
|
||||
<title>Infiscal | Sign Up</title>
|
||||
<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="Replace .env files with 1 line of code. Sign Up for Infisical in 3 minutes."
|
||||
/>
|
||||
<meta property="og:title" content={t("signup:og-title") as string} />
|
||||
<meta
|
||||
name="og:description"
|
||||
content="Infisical a simple end-to-end encrypted platform that enables teams to sync and manage API-keys and environemntal variables. Works with Node.js, Next.js, Gatsby, Nest.js..."
|
||||
content={t("signup:og-description") as string}
|
||||
/>
|
||||
</Head>
|
||||
<div className="flex flex-col justify-center items-center">
|
||||
@@ -579,3 +586,9 @@ export default function SignUp() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps = getTranslatedStaticProps([
|
||||
"auth",
|
||||
"signup",
|
||||
"section-password",
|
||||
]);
|
||||
|
||||
@@ -159,8 +159,17 @@ export default function SignupInvite() {
|
||||
code: token
|
||||
});
|
||||
if (response.status == 200) {
|
||||
setVerificationToken((await response.json()).token);
|
||||
setStep(2);
|
||||
const res = await response.json();
|
||||
// user will have temp token if doesn't have an account
|
||||
// then continue with account setup workflow
|
||||
if(res?.token){
|
||||
setVerificationToken(res.token);
|
||||
setStep(2);
|
||||
} else {
|
||||
// user will be redirected to dashboard
|
||||
// if not logged in gets kicked out to login
|
||||
router.push("/dashboard")
|
||||
}
|
||||
} else {
|
||||
console.log('ERROR', response);
|
||||
router.push('/requestnewinvite');
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import Head from 'next/head';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/router';
|
||||
import { faMagnifyingGlass, faPlus } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { useEffect, useState } from "react";
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { faMagnifyingGlass, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import Button from '~/components/basic/buttons/Button';
|
||||
import AddProjectMemberDialog from '~/components/basic/dialog/AddProjectMemberDialog';
|
||||
import UserTable from '~/components/basic/table/UserTable';
|
||||
import NavHeader from '~/components/navigation/NavHeader';
|
||||
import guidGenerator from '~/utilities/randomId';
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import AddProjectMemberDialog from "~/components/basic/dialog/AddProjectMemberDialog";
|
||||
import UserTable from "~/components/basic/table/UserTable";
|
||||
import NavHeader from "~/components/navigation/NavHeader";
|
||||
import guidGenerator from "~/utilities/randomId";
|
||||
import { getTranslatedServerSideProps } from "~/utilities/withTranslateProps";
|
||||
|
||||
import getOrganizationUsers from '../api/organization/GetOrgUsers';
|
||||
import getUser from '../api/user/getUser';
|
||||
@@ -35,6 +37,8 @@ export default function Users() {
|
||||
const [personalEmail, setPersonalEmail] = useState('');
|
||||
const [searchUsers, setSearchUsers] = useState('');
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const router = useRouter();
|
||||
let workspaceId;
|
||||
|
||||
@@ -124,14 +128,21 @@ export default function Users() {
|
||||
return userList ? (
|
||||
<div className="bg-bunker-800 md:h-screen flex flex-col justify-start">
|
||||
<Head>
|
||||
<title>Users</title>
|
||||
<title>
|
||||
{t("common:head-title", { title: t("settings-members:title") })}
|
||||
</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Head>
|
||||
<NavHeader pageName="Project Members" isProjectRelated={true} />
|
||||
<NavHeader
|
||||
pageName={t("settings-members:title")}
|
||||
isProjectRelated={true}
|
||||
/>
|
||||
<div className="flex flex-col justify-start items-start px-6 py-6 pb-4 text-3xl">
|
||||
<p className="font-semibold mr-4 text-white">Project Members</p>
|
||||
<p className="font-semibold mr-4 text-white">
|
||||
{t("settings-members:title")}
|
||||
</p>
|
||||
<p className="mr-4 text-base text-gray-400">
|
||||
This pages shows the members of the selected project.
|
||||
{t("settings-members:description")}
|
||||
</p>
|
||||
</div>
|
||||
<AddProjectMemberDialog
|
||||
@@ -159,12 +170,12 @@ export default function Users() {
|
||||
className="pl-2 text-gray-400 rounded-r-md bg-white/5 w-full h-full outline-none"
|
||||
value={searchUsers}
|
||||
onChange={(e) => setSearchUsers(e.target.value)}
|
||||
placeholder={'Search members...'}
|
||||
placeholder={t("section-members:search-members")}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2 ml-2 min-w-max flex flex-row items-start justify-start mr-4">
|
||||
<Button
|
||||
text="Add Member"
|
||||
text={t("section-members:add-member")}
|
||||
onButtonPressed={openAddModal}
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
@@ -201,3 +212,9 @@ export default function Users() {
|
||||
}
|
||||
|
||||
Users.requireAuth = true;
|
||||
|
||||
export const getServerSideProps = getTranslatedServerSideProps([
|
||||
"settings",
|
||||
"settings-members",
|
||||
"section-members",
|
||||
]);
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { useState } from 'react';
|
||||
import Head from 'next/head';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { useState } from "react";
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
import Button from '~/components/basic/buttons/Button';
|
||||
import InputField from '~/components/basic/InputField';
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import InputField from "~/components/basic/InputField";
|
||||
import { getTranslatedStaticProps } from "~/components/utilities/withTranslateProps";
|
||||
|
||||
import SendEmailOnPasswordReset from './api/auth/SendEmailOnPasswordReset';
|
||||
import SendEmailOnPasswordReset from "./api/auth/SendEmailOnPasswordReset";
|
||||
|
||||
export default function VerifyEmail() {
|
||||
const [email, setEmail] = useState('');
|
||||
const [email, setEmail] = useState("");
|
||||
const [step, setStep] = useState(1);
|
||||
|
||||
/**
|
||||
@@ -92,3 +93,5 @@ export default function VerifyEmail() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = getTranslatedStaticProps([]);
|
||||
|
||||
Reference in New Issue
Block a user