mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-28 15:29:21 +00:00
.github
.husky
backend
cli
docs
frontend
.storybook
public
scripts
src
components
config
context
ee
api
components
ActivitySideBar.tsx
ActivityTable.tsx
PITRecoverySidebar.tsx
SecretVersionList.tsx
utilities
hooks
layouts
pages
styles
views
const.ts
reactQuery.ts
.dockerignore
.eslintrc.js
.gitignore
.prettierrc
Dockerfile
Dockerfile.dev
README.md
next-env.d.ts
next-i18next.config.js
next.config.js
package-lock.json
package.json
postcss.config.js
tailwind.config.js
tsconfig.json
helm-charts
i18n
img
k8-operator
nginx
.env.example
.eslintignore
.gitignore
.goreleaser.yaml
CODE_OF_CONDUCT.md
CONTRIBUTING.md
LICENSE
Makefile
README.md
SECURITY.md
docker-compose.dev.yml
docker-compose.yml
package-lock.json
package.json
139 lines
5.3 KiB
TypeScript
139 lines
5.3 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import Image from 'next/image';
|
|
import { useRouter } from 'next/router';
|
|
import { useTranslation } from 'next-i18next';
|
|
import { faCircle, faDotCircle } from '@fortawesome/free-solid-svg-icons';
|
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
|
|
import {
|
|
decryptAssymmetric,
|
|
decryptSymmetric
|
|
} from '@app/components/utilities/cryptography/crypto';
|
|
import getSecretVersions from '@app/ee/api/secrets/GetSecretVersions';
|
|
import getLatestFileKey from '@app/pages/api/workspace/getLatestFileKey';
|
|
|
|
interface DecryptedSecretVersionListProps {
|
|
createdAt: string;
|
|
value: string;
|
|
}
|
|
|
|
interface EncrypetedSecretVersionListProps {
|
|
createdAt: string;
|
|
secretValueCiphertext: string;
|
|
secretValueIV: string;
|
|
secretValueTag: string;
|
|
}
|
|
|
|
/**
|
|
* @param {string} secretId - the id of a secret for which are querying version history
|
|
* @returns a list of versions for a specific secret
|
|
*/
|
|
const SecretVersionList = ({ secretId }: { secretId: string }) => {
|
|
const router = useRouter();
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const { t } = useTranslation();
|
|
const [secretVersions, setSecretVersions] = useState<DecryptedSecretVersionListProps[]>([]);
|
|
|
|
useEffect(() => {
|
|
const getSecretVersionHistory = async () => {
|
|
setIsLoading(true);
|
|
try {
|
|
const encryptedSecretVersions = await getSecretVersions({ secretId, offset: 0, limit: 10 });
|
|
const latestKey = await getLatestFileKey({ workspaceId: String(router.query.id) });
|
|
|
|
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY');
|
|
|
|
let decryptedLatestKey: string;
|
|
if (latestKey) {
|
|
// assymmetrically decrypt symmetric key with local private key
|
|
decryptedLatestKey = decryptAssymmetric({
|
|
ciphertext: latestKey.latestKey.encryptedKey,
|
|
nonce: latestKey.latestKey.nonce,
|
|
publicKey: latestKey.latestKey.sender.publicKey,
|
|
privateKey: String(PRIVATE_KEY)
|
|
});
|
|
}
|
|
|
|
const decryptedSecretVersions = encryptedSecretVersions?.secretVersions.map(
|
|
(encryptedSecretVersion: EncrypetedSecretVersionListProps) => ({
|
|
createdAt: encryptedSecretVersion.createdAt,
|
|
value: decryptSymmetric({
|
|
ciphertext: encryptedSecretVersion.secretValueCiphertext,
|
|
iv: encryptedSecretVersion.secretValueIV,
|
|
tag: encryptedSecretVersion.secretValueTag,
|
|
key: decryptedLatestKey
|
|
})
|
|
})
|
|
);
|
|
|
|
setSecretVersions(decryptedSecretVersions);
|
|
setIsLoading(false);
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
};
|
|
getSecretVersionHistory();
|
|
}, [secretId]);
|
|
|
|
return (
|
|
<div className="w-full min-w-40 h-[12.4rem] px-4 mt-4 text-sm text-bunker-300 overflow-x-none">
|
|
<p className="">{t('dashboard:sidebar.version-history')}</p>
|
|
<div className="pl-1 py-0.5 rounded-md bg-bunker-800 border border-mineshaft-500 overflow-x-none h-full">
|
|
{isLoading ? (
|
|
<div className="flex items-center justify-center h-full">
|
|
<Image
|
|
src="/images/loading/loading.gif"
|
|
height={60}
|
|
width={100}
|
|
alt="infisical loading indicator"
|
|
/>
|
|
</div>
|
|
) : (
|
|
<div className="h-48 overflow-y-auto overflow-x-none dark:[color-scheme:dark]">
|
|
{secretVersions ? (
|
|
secretVersions
|
|
?.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
|
|
.map((version: DecryptedSecretVersionListProps, index: number) => (
|
|
<div key={`${version.createdAt}.${index + 1}`} className="flex flex-row">
|
|
<div className="pr-1 flex flex-col items-center">
|
|
<div className="p-1">
|
|
<FontAwesomeIcon icon={index === 0 ? faDotCircle : faCircle} />
|
|
</div>
|
|
<div className="w-0 h-full border-l border-bunker-300 mt-1" />
|
|
</div>
|
|
<div className="flex flex-col w-full max-w-[calc(100%-2.3rem)]">
|
|
<div className="pr-2 pt-1 text-bunker-300/90">
|
|
{new Date(version.createdAt).toLocaleDateString('en-US', {
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit'
|
|
})}
|
|
</div>
|
|
<div className="">
|
|
<p className="break-words ph-no-capture">
|
|
<span className="py-0.5 px-1 rounded-sm bg-primary-500/30 mr-1.5">
|
|
Value:
|
|
</span>
|
|
<span className='font-mono'>{version.value}</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))
|
|
) : (
|
|
<div className="w-full h-full flex items-center justify-center text-bunker-400">
|
|
No version history yet.
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default SecretVersionList;
|