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
+1
View File
@@ -48,6 +48,7 @@ export default function RouteGuard({ children }) {
// Check if the user is authenticated
const response = await checkAuth();
// #TODO: figure our why sometimes it doesn't output a response
// ANS(akhilmhdh): Because inside the security client the await token() doesn't have try/catch
if (!publicPaths.includes(path)) {
try {
if (response.status !== 200) {
+111 -107
View File
@@ -1,8 +1,9 @@
/* eslint-disable no-unexpected-multiline */
/* eslint-disable react-hooks/exhaustive-deps */
import { useEffect, useState } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useEffect, useMemo, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/router";
import { useTranslation } from "next-i18next";
import {
faBookOpen,
faGear,
@@ -10,30 +11,30 @@ import {
faMobile,
faPlug,
faTimeline,
faUser
} from '@fortawesome/free-solid-svg-icons';
import { faPlus } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
faUser,
} from "@fortawesome/free-solid-svg-icons";
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import getOrganizations from '~/pages/api/organization/getOrgs';
import getOrganizationUserProjects from '~/pages/api/organization/GetOrgUserProjects';
import getOrganizationUsers from '~/pages/api/organization/GetOrgUsers';
import checkUserAction from '~/pages/api/userActions/checkUserAction';
import addUserToWorkspace from '~/pages/api/workspace/addUserToWorkspace';
import createWorkspace from '~/pages/api/workspace/createWorkspace';
import getWorkspaces from '~/pages/api/workspace/getWorkspaces';
import uploadKeys from '~/pages/api/workspace/uploadKeys';
import getOrganizations from "~/pages/api/organization/getOrgs";
import getOrganizationUserProjects from "~/pages/api/organization/GetOrgUserProjects";
import getOrganizationUsers from "~/pages/api/organization/GetOrgUsers";
import checkUserAction from "~/pages/api/userActions/checkUserAction";
import addUserToWorkspace from "~/pages/api/workspace/addUserToWorkspace";
import createWorkspace from "~/pages/api/workspace/createWorkspace";
import getWorkspaces from "~/pages/api/workspace/getWorkspaces";
import uploadKeys from "~/pages/api/workspace/uploadKeys";
import NavBarDashboard from '../navigation/NavBarDashboard';
import onboardingCheck from '../utilities/checks/OnboardingCheck';
import { tempLocalStorage } from '../utilities/checks/tempLocalStorage';
import NavBarDashboard from "../navigation/NavBarDashboard";
import onboardingCheck from "../utilities/checks/OnboardingCheck";
import { tempLocalStorage } from "../utilities/checks/tempLocalStorage";
import {
decryptAssymmetric,
encryptAssymmetric
} from '../utilities/cryptography/crypto';
import Button from './buttons/Button';
import AddWorkspaceDialog from './dialog/AddWorkspaceDialog';
import Listbox from './Listbox';
encryptAssymmetric,
} from "../utilities/cryptography/crypto";
import Button from "./buttons/Button";
import AddWorkspaceDialog from "./dialog/AddWorkspaceDialog";
import Listbox from "./Listbox";
interface LayoutProps {
children: React.ReactNode;
@@ -42,15 +43,17 @@ interface LayoutProps {
export default function Layout({ children }: LayoutProps) {
const router = useRouter();
const [workspaceList, setWorkspaceList] = useState([]);
const [workspaceMapping, setWorkspaceMapping] = useState([{ '1': '2' }]);
const [workspaceSelected, setWorkspaceSelected] = useState('∞');
const [newWorkspaceName, setNewWorkspaceName] = useState('');
const [workspaceMapping, setWorkspaceMapping] = useState([{ "1": "2" }]);
const [workspaceSelected, setWorkspaceSelected] = useState("∞");
const [newWorkspaceName, setNewWorkspaceName] = useState("");
const [isOpen, setIsOpen] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const [totalOnboardingActionsDone, setTotalOnboardingActionsDone] =
useState(0);
const { t } = useTranslation();
function closeModal() {
setIsOpen(false);
}
@@ -76,35 +79,35 @@ export default function Layout({ children }: LayoutProps) {
if (!currentWorkspaces.includes(workspaceName)) {
const newWorkspace = await createWorkspace({
workspaceName,
organizationId: tempLocalStorage('orgData.id')
organizationId: tempLocalStorage("orgData.id"),
});
const newWorkspaceId = newWorkspace._id;
if (addAllUsers) {
const orgUsers = await getOrganizationUsers({
orgId: tempLocalStorage('orgData.id')
orgId: tempLocalStorage("orgData.id"),
});
orgUsers.map(async (user: any) => {
if (user.status == 'accepted') {
if (user.status == "accepted") {
const result = await addUserToWorkspace(
user.user.email,
newWorkspaceId
);
if (result?.invitee && result?.latestKey) {
const PRIVATE_KEY = tempLocalStorage('PRIVATE_KEY');
const PRIVATE_KEY = tempLocalStorage("PRIVATE_KEY");
// assymmetrically decrypt symmetric key with local private key
const key = decryptAssymmetric({
ciphertext: result.latestKey.encryptedKey,
nonce: result.latestKey.nonce,
publicKey: result.latestKey.sender.publicKey,
privateKey: PRIVATE_KEY
privateKey: PRIVATE_KEY,
});
const { ciphertext, nonce } = encryptAssymmetric({
plaintext: key,
publicKey: result.invitee.publicKey,
privateKey: PRIVATE_KEY
privateKey: PRIVATE_KEY,
}) as { ciphertext: string; nonce: string };
uploadKeys(
@@ -117,11 +120,11 @@ export default function Layout({ children }: LayoutProps) {
}
});
}
router.push('/dashboard/' + newWorkspaceId + '?Development');
router.push("/dashboard/" + newWorkspaceId + "?Development");
setIsOpen(false);
setNewWorkspaceName('');
setNewWorkspaceName("");
} else {
console.error('A project with this name already exists.');
console.error("A project with this name already exists.");
setError(true);
setLoading(false);
}
@@ -132,67 +135,70 @@ export default function Layout({ children }: LayoutProps) {
}
}
const menuItems = [
{
href:
'/dashboard/' +
workspaceMapping[workspaceSelected as any] +
'?Development',
title: 'Secrets',
emoji: <FontAwesomeIcon icon={faKey} />
},
{
href: '/users/' + workspaceMapping[workspaceSelected as any],
title: 'Members',
emoji: <FontAwesomeIcon icon={faUser} />
},
{
href: '/integrations/' + workspaceMapping[workspaceSelected as any],
title: 'Integrations',
emoji: <FontAwesomeIcon icon={faPlug} />
},
{
href: '/activity/' + workspaceMapping[workspaceSelected as any],
title: 'Activity Logs',
emoji: <FontAwesomeIcon icon={faTimeline} />
},
{
href: '/settings/project/' + workspaceMapping[workspaceSelected as any],
title: 'Project Settings',
emoji: <FontAwesomeIcon icon={faGear} />
}
];
const menuItems = useMemo(
() => [
{
href:
"/dashboard/" +
workspaceMapping[workspaceSelected as any] +
"?Development",
title: t("nav:menu.secrets"),
emoji: <FontAwesomeIcon icon={faKey} />,
},
{
href: "/users/" + workspaceMapping[workspaceSelected as any],
title: t("nav:menu.members"),
emoji: <FontAwesomeIcon icon={faUser} />,
},
{
href: "/integrations/" + workspaceMapping[workspaceSelected as any],
title: t("nav:menu.integrations"),
emoji: <FontAwesomeIcon icon={faPlug} />,
},
{
href: '/activity/' + workspaceMapping[workspaceSelected as any],
title: 'Activity Logs',
emoji: <FontAwesomeIcon icon={faTimeline} />
},
{
href: "/settings/project/" + workspaceMapping[workspaceSelected as any],
title: t("nav:menu.project-settings"),
emoji: <FontAwesomeIcon icon={faGear} />,
},
],
[t, workspaceMapping, workspaceSelected]
);
useEffect(() => {
// Put a user in a workspace if they're not in one yet
const putUserInWorkSpace = async () => {
if (tempLocalStorage('orgData.id') === '') {
if (tempLocalStorage("orgData.id") === "") {
const userOrgs = await getOrganizations();
localStorage.setItem('orgData.id', userOrgs[0]._id);
localStorage.setItem("orgData.id", userOrgs[0]._id);
}
const orgUserProjects = await getOrganizationUserProjects({
orgId: tempLocalStorage('orgData.id')
orgId: tempLocalStorage("orgData.id"),
});
const userWorkspaces = orgUserProjects;
if (
userWorkspaces.length == 0 &&
router.asPath != '/noprojects' &&
!router.asPath.includes('settings')
router.asPath != "/noprojects" &&
!router.asPath.includes("settings")
) {
router.push('/noprojects');
} else if (router.asPath != '/noprojects') {
router.push("/noprojects");
} else if (router.asPath != "/noprojects") {
const intendedWorkspaceId = router.asPath
.split('/')
[router.asPath.split('/').length - 1].split('?')[0];
.split("/")
[router.asPath.split("/").length - 1].split("?")[0];
// If a user is not a member of a workspace they are trying to access, just push them to one of theirs
if (
intendedWorkspaceId != 'heroku' &&
intendedWorkspaceId != "heroku" &&
!userWorkspaces
.map((workspace: { _id: string }) => workspace._id)
.includes(intendedWorkspaceId)
) {
router.push('/dashboard/' + userWorkspaces[0]._id + '?Development');
router.push("/dashboard/" + userWorkspaces[0]._id + "?Development");
} else {
setWorkspaceList(
userWorkspaces.map((workspace: any) => workspace.name)
@@ -201,7 +207,7 @@ export default function Layout({ children }: LayoutProps) {
Object.fromEntries(
userWorkspaces.map((workspace: any) => [
workspace.name,
workspace._id
workspace._id,
])
) as any
);
@@ -209,12 +215,12 @@ export default function Layout({ children }: LayoutProps) {
Object.fromEntries(
userWorkspaces.map((workspace: any) => [
workspace._id,
workspace.name
workspace.name,
])
)[
router.asPath
.split('/')
[router.asPath.split('/').length - 1].split('?')[0]
.split("/")
[router.asPath.split("/").length - 1].split("?")[0]
]
);
}
@@ -230,16 +236,16 @@ export default function Layout({ children }: LayoutProps) {
workspaceMapping[workspaceSelected as any] &&
`${workspaceMapping[workspaceSelected as any]}` !==
router.asPath
.split('/')
[router.asPath.split('/').length - 1].split('?')[0]
.split("/")
[router.asPath.split("/").length - 1].split("?")[0]
) {
router.push(
'/dashboard/' +
"/dashboard/" +
workspaceMapping[workspaceSelected as any] +
'?Development'
"?Development"
);
localStorage.setItem(
'projectData.id',
"projectData.id",
`${workspaceMapping[workspaceSelected as any]}`
);
}
@@ -263,7 +269,7 @@ export default function Layout({ children }: LayoutProps) {
<div>
<div className="flex justify-center w-full mt-[4.5rem] mb-6 bg-bunker-600 h-20 flex-col items-center px-4">
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
PROJECT
{t("nav:menu.project")}
</div>
{workspaceList.length > 0 ? (
<Listbox
@@ -288,11 +294,11 @@ export default function Layout({ children }: LayoutProps) {
{workspaceList.length > 0 &&
menuItems.map(({ href, title, emoji }) => (
<li className="mt-0.5 mx-2" key={title}>
{router.asPath.split('/')[1] === href.split('/')[1] &&
(['project', 'billing', 'org', 'personal'].includes(
router.asPath.split('/')[2]
{router.asPath.split("/")[1] === href.split("/")[1] &&
(["project", "billing", "org", "personal"].includes(
router.asPath.split("/")[2]
)
? router.asPath.split('/')[2] === href.split('/')[2]
? router.asPath.split("/")[2] === href.split("/")[2]
: true) ? (
<div
className={`flex relative px-0.5 py-2.5 text-white text-sm rounded cursor-pointer bg-primary-50/10`}
@@ -303,7 +309,7 @@ export default function Layout({ children }: LayoutProps) {
</p>
{title}
</div>
) : router.asPath == '/noprojects' ? (
) : router.asPath == "/noprojects" ? (
<div
className={`flex p-2.5 text-white text-sm rounded`}
>
@@ -329,7 +335,7 @@ export default function Layout({ children }: LayoutProps) {
</ul>
</div>
<div className="w-full mt-40 mb-4 px-2">
{router.asPath.split('/')[1] === 'home' ? (
{router.asPath.split("/")[1] === "home" ? (
<div
className={`flex relative px-0.5 py-2.5 text-white text-sm rounded cursor-pointer bg-primary-50/10`}
>
@@ -340,12 +346,12 @@ export default function Layout({ children }: LayoutProps) {
Infisical Guide
<img
src={`/images/progress-${
totalOnboardingActionsDone == 0 ? '0' : ''
}${totalOnboardingActionsDone == 1 ? '14' : ''}${
totalOnboardingActionsDone == 2 ? '28' : ''
}${totalOnboardingActionsDone == 3 ? '43' : ''}${
totalOnboardingActionsDone == 4 ? '57' : ''
}${totalOnboardingActionsDone == 5 ? '71' : ''}.svg`}
totalOnboardingActionsDone == 0 ? "0" : ""
}${totalOnboardingActionsDone == 1 ? "14" : ""}${
totalOnboardingActionsDone == 2 ? "28" : ""
}${totalOnboardingActionsDone == 3 ? "43" : ""}${
totalOnboardingActionsDone == 4 ? "57" : ""
}${totalOnboardingActionsDone == 5 ? "71" : ""}.svg`}
height={58}
width={58}
alt="progress bar"
@@ -365,12 +371,12 @@ export default function Layout({ children }: LayoutProps) {
Infisical Guide
<img
src={`/images/progress-${
totalOnboardingActionsDone == 0 ? '0' : ''
}${totalOnboardingActionsDone == 1 ? '14' : ''}${
totalOnboardingActionsDone == 2 ? '28' : ''
}${totalOnboardingActionsDone == 3 ? '43' : ''}${
totalOnboardingActionsDone == 4 ? '57' : ''
}${totalOnboardingActionsDone == 5 ? '71' : ''}.svg`}
totalOnboardingActionsDone == 0 ? "0" : ""
}${totalOnboardingActionsDone == 1 ? "14" : ""}${
totalOnboardingActionsDone == 2 ? "28" : ""
}${totalOnboardingActionsDone == 3 ? "43" : ""}${
totalOnboardingActionsDone == 4 ? "57" : ""
}${totalOnboardingActionsDone == 5 ? "71" : ""}.svg`}
height={58}
width={58}
alt="progress bar"
@@ -400,9 +406,7 @@ export default function Layout({ children }: LayoutProps) {
className="text-gray-300 text-7xl mb-8"
/>
<p className="text-gray-200 px-6 text-center text-lg max-w-sm">
{' '}
To use Infisical, please log in through a device with larger
dimensions.{' '}
{` ${t("common:no-mobile")} `}
</p>
</div>
</>
-1
View File
@@ -64,7 +64,6 @@ export default function Toggle ({
id
])
} else {
setSharedToHide(sharedToHide!.filter(tempId => tempId != id))
deleteOverride(id);
}
setEnabled(!enabled);
@@ -1,12 +1,7 @@
import { Fragment } from "react";
import { useTranslation } from "next-i18next";
import { Dialog, Transition } from "@headlessui/react";
import setBotActiveStatus from "../../../pages/api/bot/setBotActiveStatus";
import getLatestFileKey from "../../../pages/api/workspace/getLatestFileKey";
import {
decryptAssymmetric,
encryptAssymmetric
} from "../../utilities/cryptography/crypto";
import Button from "../buttons/Button";
const ActivateBotDialog = ({
@@ -16,6 +11,7 @@ const ActivateBotDialog = ({
handleBotActivate,
handleIntegrationOption
}) => {
const { t } = useTranslation();
const submit = async () => {
try {
@@ -64,18 +60,18 @@ const ActivateBotDialog = ({
as="h3"
className="text-lg font-medium leading-6 text-gray-400"
>
Grant Infisical access to your secrets
{t("integrations:grant-access-to-secrets")}
</Dialog.Title>
<div className="mt-2 mb-2">
<p className="text-sm text-gray-500">
Most cloud integrations require Infisical to be able to decrypt your secrets so they can be forwarded over.
{t("integrations:why-infisical-needs-access")}
</p>
</div>
<div className="mt-6 max-w-max">
<Button
onButtonPressed={submit}
color="mineshaft"
text="Grant access"
text={t("integrations:grant-access-button")}
size="md"
/>
</div>
@@ -1,4 +1,5 @@
import { Fragment, useState } from "react";
import { useTranslation } from "next-i18next";
import { Dialog, Transition } from "@headlessui/react";
import addIncidentContact from "~/pages/api/organization/addIncidentContact";
@@ -14,6 +15,7 @@ const AddIncidentContactDialog = ({
setIncidentContacts,
}) => {
let [incidentContactEmail, setIncidentContactEmail] = useState("");
const { t } = useTranslation();
const submit = () => {
setIncidentContacts(
@@ -59,17 +61,16 @@ const AddIncidentContactDialog = ({
as="h3"
className="text-lg font-medium leading-6 text-gray-400"
>
Add an Incident Contact
{t("section-incident:add-dialog.title")}
</Dialog.Title>
<div className="mt-2 mb-2">
<p className="text-sm text-gray-500">
This contact will be notified in the unlikely event of a
severe incident.
{t("section-incident:add-dialog.description")}
</p>
</div>
<div className="max-h-28">
<InputField
label="Email"
label={t("common:email")}
onChangeHandler={setIncidentContactEmail}
type="varName"
value={incidentContactEmail}
@@ -81,7 +82,7 @@ const AddIncidentContactDialog = ({
<Button
onButtonPressed={submit}
color="mineshaft"
text="Add Incident Contact"
text={t("section-incident:add-dialog.add-incident")}
size="md"
/>
</div>
@@ -1,5 +1,6 @@
import { Fragment, useState } from "react";
import { useRouter } from "next/router";
import { Trans, useTranslation } from "next-i18next";
import { Dialog, Transition } from "@headlessui/react";
import Button from "../buttons/Button";
@@ -15,6 +16,7 @@ const AddProjectMemberDialog = ({
setEmail,
}) => {
const router = useRouter();
const { t } = useTranslation();
return (
<div className="z-50">
@@ -49,48 +51,55 @@ const AddProjectMemberDialog = ({
as="h3"
className="text-lg font-medium leading-6 text-gray-400 z-50"
>
Add a member to your project
{t("section-members:add-dialog.add-member-to-project")}
</Dialog.Title>
) : (
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-gray-400 z-50"
>
All the users in your organization are already invited.
{t("section-members:add-dialog.already-all-invited")}
</Dialog.Title>
)}
<div className="mt-2 mb-4">
{data?.length > 0 ? (
<div className="flex flex-col">
<p className="text-sm text-gray-500">
The user will receive an email with the instructions.
{t("section-members:add-dialog.user-will-email")}
</p>
<div className="">
<button
type="button"
className="inline-flex justify-center rounded-md py-1 text-sm text-gray-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
onClick={() =>
router.push("/settings/org/" + router.query.id)
}
>
If you are looking to add users to your org,
</button>
<button
type="button"
className="ml-1 inline-flex justify-center rounded-md py-1 text-sm text-gray-500 hover:text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
onClick={() =>
router.push(
"/settings/org/" + router.query.id + "?invite"
)
}
>
click here.
</button>
<Trans
i18nKey="section-members:add-dialog.looking-add"
components={[
// eslint-disable-next-line react/jsx-key
<button
type="button"
className="inline-flex justify-center rounded-md py-1 text-sm text-gray-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
onClick={() =>
router.push(
"/settings/org/" + router.query.id
)
}
/>,
// eslint-disable-next-line react/jsx-key
<button
type="button"
className="ml-1 inline-flex justify-center rounded-md py-1 text-sm text-gray-500 hover:text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
onClick={() =>
router.push(
"/settings/org/" +
router.query.id +
"?invite"
)
}
/>,
]}
/>
</div>
</div>
) : (
<p className="text-sm text-gray-500">
Add more users to the organization first.
{t("section-members:add-dialog.add-user-org-first")}
</p>
)}
</div>
@@ -110,7 +119,7 @@ const AddProjectMemberDialog = ({
<Button
onButtonPressed={submitModal}
color="mineshaft"
text="Add Member"
text={t("section-members:add-member")}
size="md"
/>
</div>
@@ -120,7 +129,7 @@ const AddProjectMemberDialog = ({
router.push("/settings/org/" + router.query.id)
}
color="mineshaft"
text="Add Users to Organization"
text={t("section-members:add-dialog.add-user-to-org")}
size="md"
/>
)}
@@ -1,40 +1,42 @@
import { Fragment, useState } from 'react';
import { faCheck, faCopy } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Dialog, Transition } from '@headlessui/react';
import nacl from 'tweetnacl';
import { Fragment, useState } from "react";
import { useTranslation } from "next-i18next";
import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Dialog, Transition } from "@headlessui/react";
import nacl from "tweetnacl";
import addServiceToken from '~/pages/api/serviceToken/addServiceToken';
import getLatestFileKey from '~/pages/api/workspace/getLatestFileKey';
import addServiceToken from "~/pages/api/serviceToken/addServiceToken";
import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey";
import { envMapping } from '../../../public/data/frequentConstants';
import { envMapping } from "../../../public/data/frequentConstants";
import {
decryptAssymmetric,
encryptAssymmetric
} from '../../utilities/cryptography/crypto';
import Button from '../buttons/Button';
import InputField from '../InputField';
import ListBox from '../Listbox';
encryptAssymmetric,
} from "../../utilities/cryptography/crypto";
import Button from "../buttons/Button";
import InputField from "../InputField";
import ListBox from "../Listbox";
const expiryMapping = {
'1 day': 86400,
'7 days': 604800,
'1 month': 2592000,
'6 months': 15552000,
'12 months': 31104000
"1 day": 86400,
"7 days": 604800,
"1 month": 2592000,
"6 months": 15552000,
"12 months": 31104000,
};
const AddServiceTokenDialog = ({
isOpen,
closeModal,
workspaceId,
workspaceName
workspaceName,
}) => {
const [serviceToken, setServiceToken] = useState('');
const [serviceTokenName, setServiceTokenName] = useState('');
const [serviceTokenEnv, setServiceTokenEnv] = useState('Development');
const [serviceTokenExpiresIn, setServiceTokenExpiresIn] = useState('1 day');
const [serviceToken, setServiceToken] = useState("");
const [serviceTokenName, setServiceTokenName] = useState("");
const [serviceTokenEnv, setServiceTokenEnv] = useState("Development");
const [serviceTokenExpiresIn, setServiceTokenExpiresIn] = useState("1 day");
const [serviceTokenCopied, setServiceTokenCopied] = useState(false);
const { t } = useTranslation();
const generateServiceToken = async () => {
const latestFileKey = await getLatestFileKey({ workspaceId });
@@ -43,7 +45,7 @@ const AddServiceTokenDialog = ({
ciphertext: latestFileKey.latestKey.encryptedKey,
nonce: latestFileKey.latestKey.nonce,
publicKey: latestFileKey.latestKey.sender.publicKey,
privateKey: localStorage.getItem('PRIVATE_KEY')
privateKey: localStorage.getItem("PRIVATE_KEY"),
});
// generate new public/private key pair
@@ -55,7 +57,7 @@ const AddServiceTokenDialog = ({
const { ciphertext: encryptedKey, nonce } = encryptAssymmetric({
plaintext: key,
publicKey,
privateKey
privateKey,
});
let newServiceToken = await addServiceToken({
@@ -65,16 +67,16 @@ const AddServiceTokenDialog = ({
expiresIn: expiryMapping[serviceTokenExpiresIn],
publicKey,
encryptedKey,
nonce
nonce,
});
const serviceToken = newServiceToken + ',' + privateKey;
const serviceToken = newServiceToken + "," + privateKey;
setServiceToken(serviceToken);
};
function copyToClipboard() {
// Get the text field
var copyText = document.getElementById('serviceToken');
var copyText = document.getElementById("serviceToken");
// Select the text field
copyText.select();
@@ -91,8 +93,8 @@ const AddServiceTokenDialog = ({
const closeAddServiceTokenModal = () => {
closeModal();
setServiceTokenName('');
setServiceToken('');
setServiceTokenName("");
setServiceToken("");
};
return (
@@ -122,27 +124,26 @@ const AddServiceTokenDialog = ({
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
{serviceToken == '' ? (
{serviceToken == "" ? (
<Dialog.Panel className="w-full max-w-md transform rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-gray-400 z-50"
>
Add a service token for {workspaceName}
{t("section-token:add-dialog.title", {
target: workspaceName,
})}
</Dialog.Title>
<div className="mt-2 mb-4">
<div className="flex flex-col">
<p className="text-sm text-gray-500">
Specify the name, environment, and expiry period. When
a token is generated, you will only be able to see it
once before it disappears. Make sure to save it
somewhere.
{t("section-token:add-dialog.description")}
</p>
</div>
</div>
<div className="max-h-28 mb-2">
<InputField
label="Service Token Name"
label={t("section-token:add-dialog.name")}
onChangeHandler={setServiceTokenName}
type="varName"
value={serviceTokenName}
@@ -155,13 +156,13 @@ const AddServiceTokenDialog = ({
selected={serviceTokenEnv}
onChange={setServiceTokenEnv}
data={[
'Development',
'Staging',
'Production',
'Testing'
"Development",
"Staging",
"Production",
"Testing",
]}
isFull={true}
text="Environment: "
width="full"
text={`${t("common:environment")}: `}
/>
</div>
<div className="max-h-28">
@@ -169,14 +170,14 @@ const AddServiceTokenDialog = ({
selected={serviceTokenExpiresIn}
onChange={setServiceTokenExpiresIn}
data={[
'1 day',
'7 days',
'1 month',
'6 months',
'12 months'
"1 day",
"7 days",
"1 month",
"6 months",
"12 months",
]}
isFull={true}
text="Expires in: "
width="full"
text={`${t("common:expired-in")}: `}
/>
</div>
<div className="max-w-max">
@@ -184,10 +185,10 @@ const AddServiceTokenDialog = ({
<Button
onButtonPressed={() => generateServiceToken()}
color="mineshaft"
text="Add Service Token"
textDisabled="Add Service Token"
text={t("section-token:add-dialog.add")}
textDisabled={t("section-token:add-dialog.add")}
size="md"
active={serviceTokenName == '' ? false : true}
active={serviceTokenName == "" ? false : true}
/>
</div>
</div>
@@ -198,13 +199,14 @@ const AddServiceTokenDialog = ({
as="h3"
className="text-lg font-medium leading-6 text-gray-400 z-50"
>
Copy your service token
{t("section-token:add-dialog.copy-service-token")}
</Dialog.Title>
<div className="mt-2 mb-4">
<div className="flex flex-col">
<p className="text-sm text-gray-500">
Once you close this popup, you will never see your
service token again
{t(
"section-token:add-dialog.copy-service-token-description"
)}
</p>
</div>
</div>
@@ -234,7 +236,7 @@ const AddServiceTokenDialog = ({
)}
</button>
<span className="absolute hidden group-hover:flex group-hover:animate-popup duration-300 w-28 -left-8 -top-20 translate-y-full px-3 py-2 bg-chicago-900 rounded-md text-center text-gray-400 text-sm">
Click to Copy
{t("common.click-to-copy")}
</span>
</div>
</div>
@@ -1,9 +1,13 @@
import { useTranslation } from "next-i18next";
/**
* This is the text field where people can add comments to particular secrets.
*/
const CommentField = ({ comment, modifyComment, position }: { comment: string; modifyComment: (value: string, posistion: number) => void; position: number;}) => {
const { t } = useTranslation();
return <div className={`relative mt-4 px-4 pt-4`}>
<p className='text-sm text-bunker-300'>Comments & notes</p>
<p className='text-sm text-bunker-300'>{t("dashboard:sidebar.comments")}</p>
<textarea
className="bg-bunker-800 h-32 w-full bg-bunker-800 p-2 rounded-md border border-mineshaft-500 text-sm text-bunker-300 outline-none focus:ring-2 ring-primary-800 ring-opacity-70"
value={comment}
+22 -21
View File
@@ -1,12 +1,13 @@
import { type ChangeEvent, type DragEvent, useState } from 'react';
import Image from 'next/image';
import { faUpload } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { type ChangeEvent, type DragEvent, useState } from "react";
import Image from "next/image";
import { useTranslation } from "next-i18next";
import { faUpload } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import Button from '../basic/buttons/Button';
import Error from '../basic/Error';
import parse from '../utilities/file';
import guidGenerator from '../utilities/randomId';
import Button from "../basic/buttons/Button";
import Error from "../basic/Error";
import parse from "../utilities/file";
import guidGenerator from "../utilities/randomId";
interface DropZoneProps {
// TODO: change Data type from any
@@ -26,8 +27,10 @@ const DropZone = ({
errorDragAndDrop,
setButtonReady,
keysExist,
numCurrentRows
numCurrentRows,
}: DropZoneProps) => {
const { t } = useTranslation();
const handleDragEnter = (e: DragEvent) => {
e.preventDefault();
e.stopPropagation();
@@ -43,7 +46,7 @@ const DropZone = ({
e.stopPropagation();
// set dropEffect to copy i.e copy of the source item
e.dataTransfer.dropEffect = 'copy';
e.dataTransfer.dropEffect = "copy";
};
const [loading, setLoading] = useState(false);
@@ -54,7 +57,7 @@ const DropZone = ({
setTimeout(() => setLoading(false), 5000);
e.preventDefault();
e.stopPropagation();
e.dataTransfer.dropEffect = 'copy';
e.dataTransfer.dropEffect = "copy";
const file = e.dataTransfer.files[0];
const reader = new FileReader();
@@ -69,7 +72,7 @@ const DropZone = ({
pos: numCurrentRows + index,
key: key,
value: keyPairs[key as keyof typeof keyPairs],
type: 'shared'
type: "shared",
};
});
setData(newData);
@@ -96,16 +99,16 @@ const DropZone = ({
reader.onload = (event) => {
if (event.target === null || event.target.result === null) return;
const { result } = event.target;
if (typeof result === 'string') {
if (typeof result === "string") {
const newData = result
.split('\n')
.split("\n")
.map((line: string, index: number) => {
return {
id: guidGenerator(),
pos: numCurrentRows + index,
key: line.split('=')[0],
value: line.split('=').slice(1, line.split('=').length).join('='),
type: 'shared'
key: line.split("=")[0],
value: line.split("=").slice(1, line.split("=").length).join("="),
type: "shared",
};
});
setData(newData);
@@ -149,9 +152,7 @@ const DropZone = ({
icon={faUpload}
className="text-bunker-300 text-3xl mr-6"
/>
<p className="text-bunker-300 mt-1">
Drag and drop your .env file here to add more keys.
</p>
<p className="text-bunker-300 mt-1">{t("common:drop-zone-keys")}</p>
</div>
{errorDragAndDrop ? (
<div className="mt-8 max-w-xl opacity-80">
@@ -170,7 +171,7 @@ const DropZone = ({
onDrop={handleDrop}
>
<FontAwesomeIcon icon={faUpload} className="text-7xl mb-8" />
<p className="">Drag and drop your .env file here.</p>
<p className="">{t("common:drop-zone")}</p>
<input
id="fileSelect"
type="file"
@@ -1,4 +1,5 @@
import { Fragment,useState } from 'react';
import { useTranslation } from "next-i18next";
import { faShuffle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Menu, Transition } from '@headlessui/react';
@@ -10,6 +11,7 @@ import { Menu, Transition } from '@headlessui/react';
*/
const GenerateSecretMenu = ({ modifyValue, position }: { modifyValue: (value: string, position: number) => void; position: number; }) => {
const [randomStringLength, setRandomStringLength] = useState(32);
const { t } = useTranslation();
return <Menu as="div" className="relative inline-block text-left">
<div>
@@ -51,8 +53,8 @@ const GenerateSecretMenu = ({ modifyValue, position }: { modifyValue: (value: st
icon={faShuffle}
/>
<div className="text-sm justify-between flex flex-row w-full">
<p>Generate Random Hex</p>
<p>digits</p>
<p>{t("dashboard:sidebar.generate-random-hex")}</p>
<p>{t("dashboard:sidebar.digits")}</p>
</div>
</div>
<div className="flex flex-row absolute bottom-[0.4rem] right-[3.3rem] w-16 bg-bunker-800 border border-chicago-700 rounded-md text-bunker-200 ">
+21 -18
View File
@@ -1,4 +1,5 @@
import { useState } from 'react';
import { useTranslation } from "next-i18next";
import { faX } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import SecretVersionList from 'ee/components/SecretVersionList';
@@ -69,29 +70,30 @@ const SideBar = ({
setSharedToHide
}: SideBarProps) => {
const [overrideEnabled, setOverrideEnabled] = useState(data.map(secret => secret.type).includes("personal"));
const { t } = useTranslation();
return <div className='absolute border-l border-mineshaft-500 bg-bunker fixed h-full w-96 top-14 right-0 z-50 shadow-xl flex flex-col justify-between'>
<div className='h-min overflow-y-auto'>
<div className="flex flex-row px-4 py-3 border-b border-mineshaft-500 justify-between items-center">
<p className="font-semibold text-lg text-bunker-200">Secret</p>
<p className="font-semibold text-lg text-bunker-200">{t("dashboard:sidebar.secret")}</p>
<div className='p-1' onClick={() => toggleSidebar("None")}>
<FontAwesomeIcon icon={faX} className='w-4 h-4 text-bunker-300 cursor-pointer'/>
</div>
</div>
<div className='mt-4 px-4 pointer-events-none'>
<p className='text-sm text-bunker-300'>Key</p>
<p className='text-sm text-bunker-300'>{t("dashboard:sidebar.key")}</p>
<DashboardInputField
onChangeHandler={modifyKey}
type="varName"
position={data[0].pos}
value={data[0].key}
position={data[0]?.pos}
value={data[0]?.key}
isDuplicate={false}
blurred={false}
/>
</div>
{data.filter(secret => secret.type == "shared")[0]?.value
? <div className={`relative mt-2 px-4 ${overrideEnabled && "opacity-40 pointer-events-none"} duration-200`}>
<p className='text-sm text-bunker-300'>Value</p>
<p className='text-sm text-bunker-300'>{t("dashboard:sidebar.value")}</p>
<DashboardInputField
onChangeHandler={modifyValue}
type="value"
@@ -105,22 +107,22 @@ const SideBar = ({
</div>
</div>
: <div className='px-4 text-sm text-bunker-300 pt-4'>
<span className='py-0.5 px-1 rounded-md bg-primary-200/10 mr-1'>Note:</span>
This secret is personal. It is not shared with any of your teammates.
<span className='py-0.5 px-1 rounded-md bg-primary-200/10 mr-1'>{t("common:note")}:</span>
{t("dashboard:sidebar.personal-explanation")}
</div>}
<div className='mt-4 px-4'>
{data.filter(secret => secret.type == "shared")[0]?.value &&
<div className='flex flex-row items-center justify-between my-2 pl-1 pr-2'>
<p className='text-sm text-bunker-300'>Override value with a personal value</p>
<p className='text-sm text-bunker-300'>{t("dashboard:sidebar.override")}</p>
<Toggle
enabled={overrideEnabled}
setEnabled={setOverrideEnabled}
addOverride={addOverride}
keyName={data[0].key}
value={data[0].value}
pos={data[0].pos}
id={data[0].id}
comment={data[0].comment}
keyName={data[0]?.key}
value={data[0]?.value}
pos={data[0]?.pos}
id={data[0]?.id}
comment={data[0]?.comment}
deleteOverride={deleteOverride}
sharedToHide={sharedToHide}
setSharedToHide={setSharedToHide}
@@ -130,13 +132,13 @@ const SideBar = ({
<DashboardInputField
onChangeHandler={modifyValue}
type="value"
position={overrideEnabled ? data.filter(secret => secret.type == "personal")[0].pos : data[0].pos}
value={overrideEnabled ? data.filter(secret => secret.type == "personal")[0].value : data[0].value}
position={overrideEnabled ? data.filter(secret => secret.type == "personal")[0]?.pos : data[0]?.pos}
value={overrideEnabled ? data.filter(secret => secret.type == "personal")[0]?.value : data[0]?.value}
isDuplicate={false}
blurred={true}
/>
<div className='absolute right-[0.57rem] top-[0.3rem] z-50'>
<GenerateSecretMenu modifyValue={modifyValue} position={overrideEnabled ? data.filter(secret => secret.type == "personal")[0].pos : data[0].pos} />
<GenerateSecretMenu modifyValue={modifyValue} position={overrideEnabled ? data.filter(secret => secret.type == "personal")[0]?.pos : data[0]?.pos} />
</div>
</div>
</div>
@@ -149,11 +151,12 @@ const SideBar = ({
isFull={true}
/>
</div> */}
<CommentField comment={data.filter(secret => secret.type == "shared")[0]?.comment} modifyComment={modifyComment} position={data[0].pos} />
<SecretVersionList secretId={data[0]?.id} />
<CommentField comment={data.filter(secret => secret.type == "shared")[0]?.comment} modifyComment={modifyComment} position={data[0]?.pos} />
</div>
<div className={`flex justify-start max-w-sm mt-4 px-4 mt-full mb-[4.7rem]`}>
<Button
text="Save Changes"
text={String(t("common:save-changes"))}
onButtonPressed={savePush}
color="primary"
size="md"
@@ -1,4 +1,5 @@
import React from "react";
import { useTranslation } from "next-i18next";
import CloudIntegration from "./CloudIntegration";
@@ -24,12 +25,14 @@ const CloudIntegrationSection = ({
integrationOptionPress,
integrationAuths
}: Props) => {
const { t } = useTranslation();
return (
<>
<div className={`flex flex-col justify-between items-start m-4 mt-7 text-xl max-w-5xl px-2`}>
<h1 className="font-semibold text-3xl">Cloud Integrations</h1>
<h1 className="font-semibold text-3xl">{t("integrations:cloud-integrations")}</h1>
<p className="text-base text-gray-400">
Click on an integration to begin syncing secrets to it.
{t("integrations:click-to-start")}
</p>
</div>
<div className="grid gap-4 grid-cols-4 grid-rows-2 mx-6 max-w-5xl">
@@ -1,4 +1,5 @@
import React from "react";
import { useTranslation } from "next-i18next";
import FrameworkIntegration from "./FrameworkIntegration";
@@ -15,24 +16,26 @@ interface Props {
}
const FrameworkIntegrationSection = ({ frameworks }: Props) => {
return (
<>
<div className="flex flex-col justify-between items-start mx-4 mt-12 mb-4 text-xl max-w-5xl px-2">
<h1 className="font-semibold text-3xl">Framework Integrations</h1>
<p className="text-base text-gray-400">
Click on a framework to get the setup instructions.
</p>
</div>
<div className="grid gap-4 grid-cols-7 grid-rows-2 mx-6 mt-4 max-w-5xl">
{frameworks.map((framework) => (
<FrameworkIntegration
framework={framework}
key={`framework-integration-${framework.slug}`}
/>
))}
</div>
</>
);
const { t } = useTranslation();
return (
<>
<div className="flex flex-col justify-between items-start mx-4 mt-12 mb-4 text-xl max-w-5xl px-2">
<h1 className="font-semibold text-3xl">{t("integrations:framework-integrations")}</h1>
<p className="text-base text-gray-400">
{t("integrations:click-to-setup")}
</p>
</div>
<div className="grid gap-4 grid-cols-7 grid-rows-2 mx-6 mt-4 max-w-5xl">
{frameworks.map((framework) => (
<FrameworkIntegration
framework={framework}
key={`framework-integration-${framework.slug}`}
/>
))}
</div>
</>
);
}
export default FrameworkIntegrationSection;
@@ -1,10 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable react/jsx-key */
import { Fragment, useEffect, useState } from 'react';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { faGithub, faSlack } from '@fortawesome/free-brands-svg-icons';
import { faCircleQuestion } from '@fortawesome/free-regular-svg-icons';
import { Fragment, useEffect, useMemo, useState } from "react";
import Image from "next/image";
import { useRouter } from "next/router";
import { TFunction, useTranslation } from "next-i18next";
import { faGithub, faSlack } from "@fortawesome/free-brands-svg-icons";
import { faCircleQuestion } from "@fortawesome/free-regular-svg-icons";
import {
faAngleDown,
faBook,
@@ -12,39 +12,39 @@ import {
faEnvelope,
faGear,
faPlus,
faRightFromBracket
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Menu, Transition } from '@headlessui/react';
faRightFromBracket,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Menu, Transition } from "@headlessui/react";
import logout from '~/pages/api/auth/Logout';
import getOrganization from '~/pages/api/organization/GetOrg';
import getOrganizations from '~/pages/api/organization/getOrgs';
import getUser from '~/pages/api/user/getUser';
import logout from "~/pages/api/auth/Logout";
import guidGenerator from '../utilities/randomId';
import getOrganization from "../../pages/api/organization/GetOrg";
import getOrganizations from "../../pages/api/organization/getOrgs";
import getUser from "../../pages/api/user/getUser";
import guidGenerator from "../utilities/randomId";
const supportOptions = [
const supportOptions = (t: TFunction) => [
[
<FontAwesomeIcon className="text-lg pl-1.5 pr-3" icon={faSlack} />,
'Join Slack Forum',
'https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g'
t("nav:support.slack"),
"https://join.slack.com/t/infisical/shared_invite/zt-1dgg63ln8-G7PCNJdCymAT9YF3j1ewVA",
],
[
<FontAwesomeIcon className="text-lg pl-1.5 pr-3" icon={faBook} />,
'Read Docs',
'https://infisical.com/docs/getting-started/introduction'
t("nav:support.docs"),
"https://infisical.com/docs/getting-started/introduction",
],
[
<FontAwesomeIcon className="text-lg pl-1.5 pr-3" icon={faGithub} />,
'Open a GitHub Issue',
'https://github.com/Infisical/infisical-cli/issues'
t("nav:support.issue"),
"https://github.com/Infisical/infisical-cli/issues",
],
[
<FontAwesomeIcon className="text-lg pl-1.5 pr-3" icon={faEnvelope} />,
'Send us an Email',
'mailto:[email protected]'
]
t("nav:support.email"),
"mailto:[email protected]",
],
];
export interface ICurrentOrg {
@@ -68,6 +68,10 @@ export default function Navbar() {
const [orgs, setOrgs] = useState([]);
const [currentOrg, setCurrentOrg] = useState<ICurrentOrg | undefined>();
const { t } = useTranslation();
const supportOptionsList = useMemo(() => supportOptions(t), [t]);
useEffect(() => {
(async () => {
const userData = await getUser();
@@ -75,16 +79,16 @@ export default function Navbar() {
const orgsData = await getOrganizations();
setOrgs(orgsData);
const currentOrg = await getOrganization({
orgId: String(localStorage.getItem('orgData.id'))
orgId: String(localStorage.getItem("orgData.id")),
});
setCurrentOrg(currentOrg);
})();
}, []);
const closeApp = async () => {
console.log('Logging out...');
console.log("Logging out...");
await logout();
router.push('/login');
router.push("/login");
};
return (
@@ -119,18 +123,17 @@ export default function Navbar() {
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute right-0 mt-0.5 w-64 origin-top-right rounded-md bg-bunker border border-mineshaft-700 shadow-lg ring-1 ring-black z-20 ring-opacity-5 focus:outline-none px-2 py-1.5">
{supportOptions.map((option) => (
// eslint-disable-next-line react/jsx-no-target-blank
{supportOptionsList.map(([icon, text, url]) => (
<a
key={guidGenerator()}
target="_blank"
rel="noopener"
href={String(option[2])}
rel="noopener noreferrer"
href={String(url)}
className="font-normal text-gray-300 duration-200 rounded-md w-full flex items-center py-0.5"
>
<div className="relative flex justify-start items-center cursor-pointer select-none py-2 px-2 rounded-md text-gray-400 hover:bg-white/10 duration-200 hover:text-gray-200 w-full">
{option[0]}
<div className="text-sm">{option[1]}</div>
{icon}
<div className="text-sm">{text}</div>
</div>
</a>
))}
@@ -159,11 +162,11 @@ export default function Navbar() {
<Menu.Items className="absolute right-0 mt-0.5 w-64 origin-top-right divide-y divide-gray-700 rounded-md bg-bunker border border-mineshaft-700 shadow-lg ring-1 ring-black z-20 ring-opacity-5 focus:outline-none">
<div className="px-1 py-1 ">
<div className="text-gray-400 self-start ml-2 mt-2 text-xs font-semibold tracking-wide">
SIGNED IN AS
{t("nav:user.signed-in-as")}
</div>
<div
onClick={() =>
router.push('/settings/personal/' + router.query.id)
router.push("/settings/personal/" + router.query.id)
}
className="flex flex-row items-center px-1 mx-1 my-1 hover:bg-white/5 cursor-pointer rounded-md"
>
@@ -173,11 +176,11 @@ export default function Navbar() {
<div className="flex items-center justify-between w-full">
<div>
<p className="text-gray-300 px-2 pt-1 text-sm">
{' '}
{" "}
{user?.firstName} {user?.lastName}
</p>
<p className="text-gray-400 px-2 pb-1 text-xs">
{' '}
{" "}
{user?.email}
</p>
</div>
@@ -190,11 +193,11 @@ export default function Navbar() {
</div>
<div className="px-2 pt-2">
<div className="text-gray-400 self-start ml-2 mt-2 text-xs font-semibold tracking-wide">
CURRENT ORGANIZATION
{t("nav:user.current-organization")}
</div>
<div
onClick={() =>
router.push('/settings/org/' + router.query.id)
router.push("/settings/org/" + router.query.id)
}
className="flex flex-row items-center px-2 mt-2 py-1 hover:bg-white/5 cursor-pointer rounded-md"
>
@@ -217,7 +220,7 @@ export default function Navbar() {
>
<div
onClick={() =>
router.push('/settings/billing/' + router.query.id)
router.push("/settings/billing/" + router.query.id)
}
className="mt-1 relative flex justify-start cursor-pointer select-none py-2 px-2 rounded-md text-gray-400 hover:bg-white/5 duration-200 hover:text-gray-200"
>
@@ -225,7 +228,7 @@ export default function Navbar() {
className="text-lg pl-1.5 pr-3"
icon={faCoins}
/>
<div className="text-sm">Usage & Billing</div>
<div className="text-sm">{t("nav:user.usage-billing")}</div>
</div>
</button>
<button
@@ -235,7 +238,7 @@ export default function Navbar() {
<div
onClick={() =>
router.push(
'/settings/org/' + router.query.id + '?invite'
"/settings/org/" + router.query.id + "?invite"
)
}
className="relative flex justify-start cursor-pointer select-none py-2 pl-10 pr-4 rounded-md text-gray-400 hover:bg-primary/100 duration-200 hover:text-black hover:font-semibold mt-1"
@@ -243,26 +246,26 @@ export default function Navbar() {
<span className="rounded-lg absolute inset-y-0 left-0 flex items-center pl-3 pr-4">
<FontAwesomeIcon icon={faPlus} className="ml-1" />
</span>
<div className="text-sm ml-1">Invite Members</div>
<div className="text-sm ml-1">{t("nav:user.invite")}</div>
</div>
</button>
</div>
{orgs?.length > 1 && (
<div className="px-1 pt-1">
<div className="text-gray-400 self-start ml-2 mt-2 text-xs font-semibold tracking-wide">
OTHER ORGANIZATIONS
{t("nav:user.other-organizations")}
</div>
<div className="flex flex-col items-start px-1 mt-3 mb-2">
{orgs
.filter(
(org: { _id: string }) =>
org._id != localStorage.getItem('orgData.id')
org._id != localStorage.getItem("orgData.id")
)
.map((org: { _id: string; name: string }) => (
<div
key={guidGenerator()}
onClick={() => {
localStorage.setItem('orgData.id', org._id);
localStorage.setItem("orgData.id", org._id);
router.reload();
}}
className="flex flex-row justify-start items-center hover:bg-white/5 w-full p-1.5 cursor-pointer rounded-md"
@@ -287,8 +290,8 @@ export default function Navbar() {
onClick={closeApp}
className={`${
active
? 'bg-red font-semibold text-white'
: 'text-gray-400'
? "bg-red font-semibold text-white"
: "text-gray-400"
} group flex w-full items-center rounded-md px-2 py-2 text-sm`}
>
<div className="relative flex justify-start items-center cursor-pointer select-none">
@@ -296,7 +299,7 @@ export default function Navbar() {
className="text-lg ml-1.5 mr-3"
icon={faRightFromBracket}
/>
Log Out
{t("common:logout")}
</div>
</button>
)}
@@ -16,12 +16,19 @@ export default class SecurityClient {
const req = new Request(resource, options);
if (this.#token == '') {
this.setToken(await token());
try {
// TODO: This should be moved to a context to do it only once when app loads
// this try catch saves route guard from a stuck state
this.setToken(await token());
} catch (error) {
console.error("Unauthorized access");
}
}
if (this.#token) {
req.headers.set('Authorization', 'Bearer ' + this.#token);
return fetch(req);
}
return fetch(req);
}
}
@@ -108,15 +108,16 @@ const attemptLogin = async (
if (isSignUp) {
await pushKeys({
obj: {
DATABASE_URL: [
sDATABASE_URL: [
'mongodb+srv://${DB_USERNAME}:${DB_PASSWORD}@mongodb.net',
'personal'
'This is an example of secret referencing.'
],
DB_USERNAME: ['user1234', 'personal'],
DB_PASSWORD: ['example_password', 'personal'],
TWILIO_AUTH_TOKEN: ['example_twillion_token', 'shared'],
WEBSITE_URL: ['http://localhost:3000', 'shared'],
STRIPE_SECRET_KEY: ['sk_test_7348oyho4hfq398HIUOH78', 'shared']
sDB_USERNAME: ['OVERRIDE_THIS', ''],
sDB_PASSWORD: ['OVERRIDE_THIS', ''],
pDB_USERNAME: ['user1234', 'This is an example of secret overriding. Your team can have a shared value of a secret, while you can override it to whatever value you need.'],
pDB_PASSWORD: ['example_password', 'This is an example of secret overriding. Your team can have a shared value of a secret, while you can override it to whatever value you need.'],
sTWILIO_AUTH_TOKEN: ['example_twillio_token', ''],
sWEBSITE_URL: ['http://localhost:3000', ''],
},
workspaceId: projectToLogin,
env: 'Development'
@@ -15,6 +15,7 @@ interface SecretProps {
value: string;
type: 'personal' | 'shared';
comment: string;
id: string;
}
interface Props {
@@ -85,6 +86,7 @@ const getSecretsForProject = async ({
}
tempFileState.push({
id: secretPair._id,
key: plainTextKey,
value: plainTextValue,
type: secretPair.type,
@@ -97,7 +99,7 @@ const getSecretsForProject = async ({
setData(
tempFileState.map((line, index) => {
return {
id: guidGenerator(),
id: line['id'],
pos: index,
key: line['key'],
value: line['value'],
@@ -109,7 +111,7 @@ const getSecretsForProject = async ({
return tempFileState.map((line, index) => {
return {
id: guidGenerator(),
id: line['id'],
pos: index,
key: line['key'],
value: line['value'],
@@ -0,0 +1,52 @@
import { GetServerSideProps, GetStaticProps } from "next";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
const DefaultNamespaces = ["common", "nav"];
type GetTranslatedStaticProps = (
namespaces: string[],
getStaticProps?: GetStaticProps<any>
) => GetStaticProps;
type GetTranslatedServerSideProps = (
namespaces: string[],
getStaticProps?: GetServerSideProps<any>
) => GetServerSideProps;
export const getTranslatedStaticProps: GetTranslatedStaticProps =
(namespaces, getStaticProps) =>
async ({ locale, ...context }) => {
let staticProps = { props: {} };
if (typeof getStaticProps === "function") {
staticProps = (await getStaticProps(context)) as any;
}
return {
...staticProps,
props: {
...(await serverSideTranslations(locale ?? "en", [
...DefaultNamespaces,
...namespaces,
])),
...staticProps.props,
},
};
};
export const getTranslatedServerSideProps: GetTranslatedServerSideProps =
(namespaces, getServerSideProps) =>
async ({ locale, ...context }) => {
let staticProps = { props: {} };
if (typeof getServerSideProps === "function") {
staticProps = (await getServerSideProps(context)) as any;
}
return {
...staticProps,
props: {
...(await serverSideTranslations(locale ?? "en", [
...DefaultNamespaces,
...namespaces,
])),
...staticProps.props,
},
};
};