Fix merge conflicts

This commit is contained in:
Vladyslav Matsiiako
2022-12-28 13:42:27 -05:00
89 changed files with 3019 additions and 887 deletions
+11 -1
View File
@@ -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
+1 -7
View File
@@ -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;
+32 -19
View File
@@ -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"]);
+6
View File
@@ -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"]);
+20 -8
View File
@@ -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
View File
@@ -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"]);
+4 -2
View File
@@ -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"]);
+39 -36
View File
@@ -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([]);
+17 -7
View File
@@ -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>
);
+35 -24
View File
@@ -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",
]);
+41 -28
View File
@@ -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",
]);
+60 -23
View File
@@ -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",
]);
+40 -26
View File
@@ -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&apos;s auto-generated unique identifier. It can&apos;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
View File
@@ -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",
]);
+11 -2
View File
@@ -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');
+34 -17
View File
@@ -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",
]);
+11 -8
View File
@@ -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([]);