added braces everywhere and formated all files
This commit is contained in:
parent
829e59635d
commit
1d5473f3dd
42 changed files with 1183 additions and 710 deletions
6
.babelrc
6
.babelrc
|
@ -1,7 +1,5 @@
|
|||
{
|
||||
"presets": [
|
||||
"next/babel"
|
||||
],
|
||||
"presets": ["next/babel"],
|
||||
"plugins": [
|
||||
[
|
||||
"styled-components",
|
||||
|
@ -12,4 +10,4 @@
|
|||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,9 +12,8 @@ const Container = styled.div`
|
|||
|
||||
export default Container;
|
||||
|
||||
|
||||
export const DisclaimerContainer = styled.div`
|
||||
margin: 16px 0;
|
||||
margin: 16px 0;
|
||||
color: rgb(158, 150, 137);
|
||||
font-size: 14px;
|
||||
`;
|
||||
|
|
|
@ -1,23 +1,24 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const HeartUI = styled.button<{
|
||||
isClick: boolean,
|
||||
size: number,
|
||||
isClick: boolean;
|
||||
size: number;
|
||||
}>`
|
||||
width: ${props => props.size}px;
|
||||
height: ${props => props.size}px;
|
||||
float:right;
|
||||
background: url("/fav-button.png") no-repeat;
|
||||
cursor: pointer;
|
||||
background-size: cover;
|
||||
border: none;
|
||||
${({ isClick, size }) => isClick && `background-position: -${28 * size}px;transition: background 1s steps(28);`}
|
||||
width: ${(props) => props.size}px;
|
||||
height: ${(props) => props.size}px;
|
||||
float: right;
|
||||
background: url('/fav-button.png') no-repeat;
|
||||
cursor: pointer;
|
||||
background-size: cover;
|
||||
border: none;
|
||||
${({ isClick, size }) =>
|
||||
isClick &&
|
||||
`background-position: -${
|
||||
28 * size
|
||||
}px;transition: background 1s steps(28);`}
|
||||
`;
|
||||
|
||||
|
||||
export default function FavButton({ isClick, onClick, size }) {
|
||||
return (
|
||||
<HeartUI isClick={isClick} onClick={onClick} size={size}/>
|
||||
);
|
||||
}
|
||||
return <HeartUI isClick={isClick} onClick={onClick} size={size} />;
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@ import React, { useRef } from 'react';
|
|||
import styled from 'styled-components';
|
||||
|
||||
const DropDiv = styled.div`
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
type Props = React.PropsWithChildren<{
|
||||
|
@ -12,30 +12,34 @@ type Props = React.PropsWithChildren<{
|
|||
closeModal: () => void;
|
||||
}>;
|
||||
|
||||
export default function FullScreenDropZone({ children, showModal, closeModal }: Props) {
|
||||
export default function FullScreenDropZone({
|
||||
children,
|
||||
showModal,
|
||||
closeModal,
|
||||
}: Props) {
|
||||
const closeTimer = useRef<number>();
|
||||
|
||||
|
||||
const clearTimer = () => {
|
||||
if (closeTimer.current) {
|
||||
clearTimeout(closeTimer.current);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onDragOver = (e) => {
|
||||
e.preventDefault();
|
||||
clearTimer();
|
||||
showModal();
|
||||
}
|
||||
};
|
||||
|
||||
const onDragLeave = (e) => {
|
||||
e.preventDefault();
|
||||
clearTimer();
|
||||
closeTimer.current = setTimeout(closeModal, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DropDiv onDragOver={onDragOver} onDragLeave={onDragLeave}>
|
||||
{children}
|
||||
</DropDiv>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,11 +4,14 @@ import PhotoswipeUIDefault from 'photoswipe/dist/photoswipe-ui-default';
|
|||
import classnames from 'classnames';
|
||||
import events from './events';
|
||||
import FavButton from 'components/FavButton';
|
||||
import { addToFavorites, removeFromFavorites } from 'services/collectionService';
|
||||
import {
|
||||
addToFavorites,
|
||||
removeFromFavorites,
|
||||
} from 'services/collectionService';
|
||||
import { file } from 'services/fileService';
|
||||
|
||||
interface Iprops {
|
||||
isOpen: boolean
|
||||
isOpen: boolean;
|
||||
items: any[];
|
||||
options?: Object;
|
||||
onClose?: () => void;
|
||||
|
@ -17,28 +20,26 @@ interface Iprops {
|
|||
className?: string;
|
||||
favItemIds: Set<number>;
|
||||
setFavItemIds: (favItemIds: Set<number>) => void;
|
||||
};
|
||||
}
|
||||
|
||||
function PhotoSwipe(props: Iprops) {
|
||||
|
||||
let pswpElement;
|
||||
const [photoSwipe, setPhotoSwipe] = useState<Photoswipe<any>>();
|
||||
|
||||
const { isOpen } = props;
|
||||
const [isFav, setIsFav] = useState(false)
|
||||
|
||||
const [isFav, setIsFav] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pswpElement)
|
||||
if (!pswpElement) {
|
||||
return;
|
||||
if (isOpen)
|
||||
}
|
||||
if (isOpen) {
|
||||
openPhotoSwipe();
|
||||
|
||||
}
|
||||
}, [pswpElement]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pswpElement)
|
||||
return;
|
||||
if (!pswpElement) return;
|
||||
if (isOpen) {
|
||||
openPhotoSwipe();
|
||||
}
|
||||
|
@ -47,17 +48,21 @@ function PhotoSwipe(props: Iprops) {
|
|||
}
|
||||
return () => {
|
||||
closePhotoSwipe();
|
||||
}
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
function updateFavButton() {
|
||||
setIsFav(isInFav(this?.currItem));
|
||||
}
|
||||
|
||||
|
||||
const openPhotoSwipe = () => {
|
||||
const { items, options } = props;
|
||||
let photoSwipe = new Photoswipe(pswpElement, PhotoswipeUIDefault, items, options);
|
||||
let photoSwipe = new Photoswipe(
|
||||
pswpElement,
|
||||
PhotoswipeUIDefault,
|
||||
items,
|
||||
options
|
||||
);
|
||||
events.forEach((event) => {
|
||||
const callback = props[event];
|
||||
if (callback || event === 'destroy') {
|
||||
|
@ -75,7 +80,6 @@ function PhotoSwipe(props: Iprops) {
|
|||
photoSwipe.listen('beforeChange', updateFavButton);
|
||||
photoSwipe.init();
|
||||
setPhotoSwipe(photoSwipe);
|
||||
|
||||
};
|
||||
|
||||
const updateItems = (items = []) => {
|
||||
|
@ -88,8 +92,7 @@ function PhotoSwipe(props: Iprops) {
|
|||
};
|
||||
|
||||
const closePhotoSwipe = () => {
|
||||
if (photoSwipe)
|
||||
photoSwipe.close();
|
||||
if (photoSwipe) photoSwipe.close();
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
|
@ -102,10 +105,8 @@ function PhotoSwipe(props: Iprops) {
|
|||
const { favItemIds } = props;
|
||||
if (favItemIds && file) {
|
||||
return favItemIds.has(file.id);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
} else return false;
|
||||
};
|
||||
|
||||
const onFavClick = async (file) => {
|
||||
const { favItemIds, setFavItemIds } = props;
|
||||
|
@ -114,15 +115,13 @@ function PhotoSwipe(props: Iprops) {
|
|||
await addToFavorites(file);
|
||||
setIsFav(true);
|
||||
setFavItemIds(favItemIds);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
favItemIds.delete(file.id);
|
||||
await removeFromFavorites(file)
|
||||
await removeFromFavorites(file);
|
||||
setIsFav(false);
|
||||
setFavItemIds(favItemIds);
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
const { id } = props;
|
||||
let { className } = props;
|
||||
className = classnames(['pswp', className]).trim();
|
||||
|
@ -130,7 +129,7 @@ function PhotoSwipe(props: Iprops) {
|
|||
<div
|
||||
id={id}
|
||||
className={className}
|
||||
tabIndex={Number("-1")}
|
||||
tabIndex={Number('-1')}
|
||||
role="dialog"
|
||||
aria-hidden="true"
|
||||
ref={(node) => {
|
||||
|
@ -160,8 +159,17 @@ function PhotoSwipe(props: Iprops) {
|
|||
className="pswp__button pswp__button--fs"
|
||||
title="Toggle fullscreen"
|
||||
/>
|
||||
<button className="pswp__button pswp__button--zoom" title="Zoom in/out" />
|
||||
<FavButton size={44} isClick={isFav} onClick={() => { onFavClick(photoSwipe?.currItem) }} />
|
||||
<button
|
||||
className="pswp__button pswp__button--zoom"
|
||||
title="Zoom in/out"
|
||||
/>
|
||||
<FavButton
|
||||
size={44}
|
||||
isClick={isFav}
|
||||
onClick={() => {
|
||||
onFavClick(photoSwipe?.currItem);
|
||||
}}
|
||||
/>
|
||||
<div className="pswp__preloader">
|
||||
<div className="pswp__preloader__icn">
|
||||
<div className="pswp__preloader__cut">
|
||||
|
@ -190,4 +198,4 @@ function PhotoSwipe(props: Iprops) {
|
|||
);
|
||||
}
|
||||
|
||||
export default PhotoSwipe;
|
||||
export default PhotoSwipe;
|
||||
|
|
|
@ -15,5 +15,5 @@ export default [
|
|||
'destroy',
|
||||
'updateScrollOffset',
|
||||
'preventDragEvent',
|
||||
'shareLinkClick'
|
||||
];
|
||||
'shareLinkClick',
|
||||
];
|
||||
|
|
|
@ -8,8 +8,8 @@ export default function PlayCircleOutline(props) {
|
|||
viewBox={props.viewBox}
|
||||
width={props.width}
|
||||
>
|
||||
<path d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M10 16.5l6-4.5-6-4.5v9zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>
|
||||
<path d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M10 16.5l6-4.5-6-4.5v9zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
@ -18,4 +18,4 @@ PlayCircleOutline.defaultProps = {
|
|||
height: 24,
|
||||
width: 24,
|
||||
viewBox: '0 0 24 24',
|
||||
}
|
||||
};
|
||||
|
|
|
@ -8,10 +8,10 @@ export default function SadFace(props) {
|
|||
viewBox={props.viewBox}
|
||||
width={props.width}
|
||||
>
|
||||
<path d="M0 0h24v24H0V0z" fill="none"/>
|
||||
<circle cx="15.5" cy="9.5" r="1.5"/>
|
||||
<circle cx="8.5" cy="9.5" r="1.5"/>
|
||||
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm0-6c-2.33 0-4.32 1.45-5.12 3.5h1.67c.69-1.19 1.97-2 3.45-2s2.75.81 3.45 2h1.67c-.8-2.05-2.79-3.5-5.12-3.5z"/>
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<circle cx="15.5" cy="9.5" r="1.5" />
|
||||
<circle cx="8.5" cy="9.5" r="1.5" />
|
||||
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm0-6c-2.33 0-4.32 1.45-5.12 3.5h1.67c.69-1.19 1.97-2 3.45-2s2.75.81 3.45 2h1.67c-.8-2.05-2.79-3.5-5.12-3.5z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
@ -20,4 +20,4 @@ SadFace.defaultProps = {
|
|||
height: 24,
|
||||
width: 24,
|
||||
viewBox: '0 0 24 24',
|
||||
}
|
||||
};
|
||||
|
|
|
@ -8,8 +8,8 @@ export default function PowerSettings(props) {
|
|||
viewBox={props.viewBox}
|
||||
width={props.width}
|
||||
>
|
||||
<path d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M13 3h-2v10h2V3zm4.83 2.17l-1.42 1.42C17.99 7.86 19 9.81 19 12c0 3.87-3.13 7-7 7s-7-3.13-7-7c0-2.19 1.01-4.14 2.58-5.42L6.17 5.17C4.23 6.82 3 9.26 3 12c0 4.97 4.03 9 9 9s9-4.03 9-9c0-2.74-1.23-5.18-3.17-6.83z"/>
|
||||
<path d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M13 3h-2v10h2V3zm4.83 2.17l-1.42 1.42C17.99 7.86 19 9.81 19 12c0 3.87-3.13 7-7 7s-7-3.13-7-7c0-2.19 1.01-4.14 2.58-5.42L6.17 5.17C4.23 6.82 3 9.26 3 12c0 4.97 4.03 9 9 9s9-4.03 9-9c0-2.74-1.23-5.18-3.17-6.83z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
@ -18,4 +18,4 @@ PowerSettings.defaultProps = {
|
|||
height: 24,
|
||||
width: 24,
|
||||
viewBox: '0 0 24 24',
|
||||
}
|
||||
};
|
||||
|
|
|
@ -108,12 +108,12 @@ const GlobalStyles = createGlobalStyle`
|
|||
`;
|
||||
|
||||
const Image = styled.img`
|
||||
max-height: 28px;
|
||||
margin-right: 5px;
|
||||
max-height: 28px;
|
||||
margin-right: 5px;
|
||||
`;
|
||||
|
||||
const FlexContainer = styled.div`
|
||||
flex: 1;
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
export default function App({ Component, pageProps }) {
|
||||
|
@ -163,35 +163,39 @@ export default function App({ Component, pageProps }) {
|
|||
showModal={showUploadModal}
|
||||
>
|
||||
<Head>
|
||||
<title>ente.io | Privacy friendly alternative to Google Photos</title>
|
||||
<title>
|
||||
ente.io | Privacy friendly alternative to Google Photos
|
||||
</title>
|
||||
</Head>
|
||||
<GlobalStyles />
|
||||
<Navbar>
|
||||
<FlexContainer>
|
||||
<Image alt='logo' src='/icon.png' />
|
||||
<Image alt="logo" src="/icon.png" />
|
||||
{constants.COMPANY_NAME}
|
||||
</FlexContainer>
|
||||
{uploadButtonView && <UploadButton showModal={showUploadModal} />}
|
||||
{user &&
|
||||
<Button variant='link' onClick={logout}>
|
||||
{uploadButtonView && (
|
||||
<UploadButton showModal={showUploadModal} />
|
||||
)}
|
||||
{user && (
|
||||
<Button variant="link" onClick={logout}>
|
||||
<PowerSettings />
|
||||
</Button>
|
||||
}
|
||||
)}
|
||||
</Navbar>
|
||||
{loading ? (
|
||||
<Container>
|
||||
<Spinner animation='border' role='status' variant='primary'>
|
||||
<span className='sr-only'>Loading...</span>
|
||||
<Spinner animation="border" role="status" variant="primary">
|
||||
<span className="sr-only">Loading...</span>
|
||||
</Spinner>
|
||||
</Container>
|
||||
) : (
|
||||
<Component
|
||||
uploadModalView={uploadModalView}
|
||||
showUploadModal={showUploadModal}
|
||||
closeUploadModal={closeUploadModal}
|
||||
setUploadButtonView={setUploadButtonView}
|
||||
/>
|
||||
)}
|
||||
<Component
|
||||
uploadModalView={uploadModalView}
|
||||
showUploadModal={showUploadModal}
|
||||
closeUploadModal={closeUploadModal}
|
||||
setUploadButtonView={setUploadButtonView}
|
||||
/>
|
||||
)}
|
||||
</FullScreenDropZone>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
import Document, {
|
||||
Html, Head, Main, NextScript,
|
||||
} from 'next/document';
|
||||
import Document, { Html, Head, Main, NextScript } from 'next/document';
|
||||
import { ServerStyleSheet } from 'styled-components';
|
||||
|
||||
export default class MyDocument extends Document {
|
||||
static async getInitialProps(ctx) {
|
||||
const sheet = new ServerStyleSheet()
|
||||
const originalRenderPage = ctx.renderPage
|
||||
const sheet = new ServerStyleSheet();
|
||||
const originalRenderPage = ctx.renderPage;
|
||||
|
||||
try {
|
||||
ctx.renderPage = () =>
|
||||
originalRenderPage({
|
||||
enhanceApp: (App) => (props) =>
|
||||
sheet.collectStyles(<App {...props} />),
|
||||
})
|
||||
});
|
||||
|
||||
const initialProps = await Document.getInitialProps(ctx)
|
||||
const initialProps = await Document.getInitialProps(ctx);
|
||||
return {
|
||||
...initialProps,
|
||||
styles: (
|
||||
|
@ -24,21 +22,21 @@ export default class MyDocument extends Document {
|
|||
{sheet.getStyleElement()}
|
||||
</>
|
||||
),
|
||||
}
|
||||
};
|
||||
} finally {
|
||||
sheet.seal()
|
||||
sheet.seal();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Html lang='en'>
|
||||
<Html lang="en">
|
||||
<Head>
|
||||
<meta
|
||||
name="description"
|
||||
content="ente is a privacy friendly alternative to Google Photos that supports end-to-end encryption. Because memories are precious."
|
||||
/>
|
||||
<link rel="icon" href="/icon.png" type="image/png"/>
|
||||
<link rel="icon" href="/icon.png" type="image/png" />
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
|
@ -47,4 +45,4 @@ export default class MyDocument extends Document {
|
|||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,12 @@ import { createProxyMiddleware } from 'http-proxy-middleware';
|
|||
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: false,
|
||||
bodyParser: false,
|
||||
},
|
||||
};
|
||||
|
||||
const API_ENDPOINT = process.env.NEXT_PUBLIC_ENTE_ENDPOINT || "https://api.staging.ente.io";
|
||||
const API_ENDPOINT =
|
||||
process.env.NEXT_PUBLIC_ENTE_ENDPOINT || 'https://api.staging.ente.io';
|
||||
|
||||
export default createProxyMiddleware({
|
||||
target: API_ENDPOINT,
|
||||
|
|
|
@ -11,10 +11,11 @@ import { useRouter } from 'next/router';
|
|||
import * as Yup from 'yup';
|
||||
import { keyAttributes } from 'types';
|
||||
import { setKey, SESSION_KEYS, getKey } from 'utils/storage/sessionStorage';
|
||||
import * as Comlink from "comlink";
|
||||
import * as Comlink from 'comlink';
|
||||
|
||||
const CryptoWorker: any = typeof window !== 'undefined'
|
||||
&& Comlink.wrap(new Worker("worker/crypto.worker.js", { type: 'module' }));
|
||||
const CryptoWorker: any =
|
||||
typeof window !== 'undefined' &&
|
||||
Comlink.wrap(new Worker('worker/crypto.worker.js', { type: 'module' }));
|
||||
|
||||
const Image = styled.img`
|
||||
width: 200px;
|
||||
|
@ -41,22 +42,34 @@ export default function Credentials() {
|
|||
} else if (!keyAttributes) {
|
||||
router.push('/generate');
|
||||
} else if (key) {
|
||||
router.push('/gallery')
|
||||
router.push('/gallery');
|
||||
} else {
|
||||
setKeyAttributes(keyAttributes);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const verifyPassphrase = async (values: formValues, { setFieldError }: FormikHelpers<formValues>) => {
|
||||
const verifyPassphrase = async (
|
||||
values: formValues,
|
||||
{ setFieldError }: FormikHelpers<formValues>
|
||||
) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const cryptoWorker = await new CryptoWorker();
|
||||
const { passphrase } = values;
|
||||
const kek: string = await cryptoWorker.deriveKey(passphrase, keyAttributes.kekSalt);
|
||||
const kek: string = await cryptoWorker.deriveKey(
|
||||
passphrase,
|
||||
keyAttributes.kekSalt
|
||||
);
|
||||
|
||||
if (await cryptoWorker.verifyHash(keyAttributes.kekHash, kek)) {
|
||||
const key: string = await cryptoWorker.decryptB64(keyAttributes.encryptedKey, keyAttributes.keyDecryptionNonce, kek);
|
||||
const sessionKeyAttributes = await cryptoWorker.encryptToB64(key);
|
||||
const key: string = await cryptoWorker.decryptB64(
|
||||
keyAttributes.encryptedKey,
|
||||
keyAttributes.keyDecryptionNonce,
|
||||
kek
|
||||
);
|
||||
const sessionKeyAttributes = await cryptoWorker.encryptToB64(
|
||||
key
|
||||
);
|
||||
const sessionKey = sessionKeyAttributes.key;
|
||||
const sessionNonce = sessionKeyAttributes.nonce;
|
||||
const encryptionKey = sessionKeyAttributes.encryptedData;
|
||||
|
@ -67,44 +80,65 @@ export default function Credentials() {
|
|||
setFieldError('passphrase', constants.INCORRECT_PASSPHRASE);
|
||||
}
|
||||
} catch (e) {
|
||||
setFieldError('passphrase', `${constants.UNKNOWN_ERROR} ${e.message}`);
|
||||
setFieldError(
|
||||
'passphrase',
|
||||
`${constants.UNKNOWN_ERROR} ${e.message}`
|
||||
);
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (<Container>
|
||||
<Image alt='vault' src='/vault.svg' />
|
||||
<Card style={{ minWidth: '300px' }}>
|
||||
<Card.Body>
|
||||
<p className="text-center">{constants.ENTER_PASSPHRASE}</p>
|
||||
<Formik<formValues>
|
||||
initialValues={{ passphrase: '' }}
|
||||
onSubmit={verifyPassphrase}
|
||||
validationSchema={Yup.object().shape({
|
||||
passphrase: Yup.string().required(constants.REQUIRED),
|
||||
})}
|
||||
>
|
||||
{({ values, touched, errors, handleChange, handleBlur, handleSubmit }) => (
|
||||
<Form noValidate onSubmit={handleSubmit}>
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
type="password"
|
||||
placeholder={constants.RETURN_PASSPHRASE_HINT}
|
||||
value={values.passphrase}
|
||||
onChange={handleChange('passphrase')}
|
||||
onBlur={handleBlur('passphrase')}
|
||||
isInvalid={Boolean(touched.passphrase && errors.passphrase)}
|
||||
disabled={loading}
|
||||
/>
|
||||
<Form.Control.Feedback type="invalid">
|
||||
{errors.passphrase}
|
||||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Button block type='submit' disabled={loading}>{constants.VERIFY_PASSPHRASE}</Button>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Container>)
|
||||
return (
|
||||
<Container>
|
||||
<Image alt="vault" src="/vault.svg" />
|
||||
<Card style={{ minWidth: '300px' }}>
|
||||
<Card.Body>
|
||||
<p className="text-center">{constants.ENTER_PASSPHRASE}</p>
|
||||
<Formik<formValues>
|
||||
initialValues={{ passphrase: '' }}
|
||||
onSubmit={verifyPassphrase}
|
||||
validationSchema={Yup.object().shape({
|
||||
passphrase: Yup.string().required(
|
||||
constants.REQUIRED
|
||||
),
|
||||
})}
|
||||
>
|
||||
{({
|
||||
values,
|
||||
touched,
|
||||
errors,
|
||||
handleChange,
|
||||
handleBlur,
|
||||
handleSubmit,
|
||||
}) => (
|
||||
<Form noValidate onSubmit={handleSubmit}>
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
type="password"
|
||||
placeholder={
|
||||
constants.RETURN_PASSPHRASE_HINT
|
||||
}
|
||||
value={values.passphrase}
|
||||
onChange={handleChange('passphrase')}
|
||||
onBlur={handleBlur('passphrase')}
|
||||
isInvalid={Boolean(
|
||||
touched.passphrase &&
|
||||
errors.passphrase
|
||||
)}
|
||||
disabled={loading}
|
||||
/>
|
||||
<Form.Control.Feedback type="invalid">
|
||||
{errors.passphrase}
|
||||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Button block type="submit" disabled={loading}>
|
||||
{constants.VERIFY_PASSPHRASE}
|
||||
</Button>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React, { useState } from "react";
|
||||
import { Card } from "react-bootstrap";
|
||||
import styled from "styled-components";
|
||||
import CreateCollection from "./CreateCollection";
|
||||
import DropzoneWrapper from "./DropzoneWrapper";
|
||||
import React, { useState } from 'react';
|
||||
import { Card } from 'react-bootstrap';
|
||||
import styled from 'styled-components';
|
||||
import CreateCollection from './CreateCollection';
|
||||
import DropzoneWrapper from './DropzoneWrapper';
|
||||
|
||||
const ImageContainer = styled.div`
|
||||
min-height: 192px;
|
||||
|
@ -19,7 +19,6 @@ const StyledCard = styled(Card)`
|
|||
`;
|
||||
|
||||
export default function AddCollection(props) {
|
||||
|
||||
const [acceptedFiles, setAcceptedFiles] = useState<File[]>();
|
||||
const [createCollectionView, setCreateCollectionView] = useState(false);
|
||||
|
||||
|
@ -32,7 +31,9 @@ export default function AddCollection(props) {
|
|||
const children = (
|
||||
<StyledCard>
|
||||
<ImageContainer>+</ImageContainer>
|
||||
<Card.Text style={{ textAlign: "center" }}>Create New Album</Card.Text>
|
||||
<Card.Text style={{ textAlign: 'center' }}>
|
||||
Create New Album
|
||||
</Card.Text>
|
||||
</StyledCard>
|
||||
);
|
||||
return (
|
||||
|
@ -51,5 +52,5 @@ export default function AddCollection(props) {
|
|||
acceptedFiles={acceptedFiles}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import UploadService from 'services/uploadService';
|
|||
import { getToken } from 'utils/common/key';
|
||||
import DropzoneWrapper from './DropzoneWrapper';
|
||||
|
||||
|
||||
function CollectionDropZone({
|
||||
children,
|
||||
closeModal,
|
||||
|
@ -12,9 +11,8 @@ function CollectionDropZone({
|
|||
collectionAndItsLatestFile,
|
||||
setProgressView,
|
||||
progressBarProps,
|
||||
setErrorCode
|
||||
setErrorCode,
|
||||
}) {
|
||||
|
||||
const upload = async (acceptedFiles) => {
|
||||
try {
|
||||
const token = getToken();
|
||||
|
@ -22,16 +20,21 @@ function CollectionDropZone({
|
|||
progressBarProps.setPercentComplete(0);
|
||||
setProgressView(true);
|
||||
|
||||
await UploadService.uploadFiles(acceptedFiles, collectionAndItsLatestFile, token, progressBarProps);
|
||||
await UploadService.uploadFiles(
|
||||
acceptedFiles,
|
||||
collectionAndItsLatestFile,
|
||||
token,
|
||||
progressBarProps
|
||||
);
|
||||
refetchData();
|
||||
} catch (err) {
|
||||
if (err.response)
|
||||
if (err.response) {
|
||||
setErrorCode(err.response.status);
|
||||
}
|
||||
finally {
|
||||
}
|
||||
} finally {
|
||||
setProgressView(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
return (
|
||||
<DropzoneWrapper
|
||||
children={children}
|
||||
|
@ -40,6 +43,6 @@ function CollectionDropZone({
|
|||
onDropRejected={closeModal}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default CollectionDropZone;
|
||||
|
|
|
@ -15,17 +15,23 @@ function CollectionSelector(props) {
|
|||
} = props;
|
||||
|
||||
const CollectionIcons = collectionAndItsLatestFile?.map((item) => (
|
||||
<CollectionDropZone key={item.collection.id}
|
||||
<CollectionDropZone
|
||||
key={item.collection.id}
|
||||
{...rest}
|
||||
closeModal={closeUploadModal}
|
||||
showModal={showUploadModal}
|
||||
collectionAndItsLatestFile={item}
|
||||
>
|
||||
<Card>
|
||||
<PreviewCard data={item.file} updateUrl={() => { }} forcedEnable />
|
||||
<Card.Text className="text-center">{item.collection.name}</Card.Text>
|
||||
<PreviewCard
|
||||
data={item.file}
|
||||
updateUrl={() => {}}
|
||||
forcedEnable
|
||||
/>
|
||||
<Card.Text className="text-center">
|
||||
{item.collection.name}
|
||||
</Card.Text>
|
||||
</Card>
|
||||
|
||||
</CollectionDropZone>
|
||||
));
|
||||
|
||||
|
@ -36,11 +42,15 @@ function CollectionSelector(props) {
|
|||
dialogClassName="modal-90w"
|
||||
>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title >
|
||||
{constants.SELECT_COLLECTION}
|
||||
</Modal.Title>
|
||||
<Modal.Title>{constants.SELECT_COLLECTION}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body style={{ display: "flex", justifyContent: "flex-start", flexWrap: "wrap" }}>
|
||||
<Modal.Body
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-start',
|
||||
flexWrap: 'wrap',
|
||||
}}
|
||||
>
|
||||
<AddCollection
|
||||
{...rest}
|
||||
showUploadModal={showUploadModal}
|
||||
|
|
|
@ -34,17 +34,18 @@ const Wrapper = styled.div`
|
|||
white-space: nowrap;
|
||||
overflow: auto;
|
||||
max-width: 100%;
|
||||
`
|
||||
`;
|
||||
const Chip = styled.button<{ active: boolean }>`
|
||||
border-radius: 20px;
|
||||
padding: 2px 10px;
|
||||
margin: 2px 5px 2px 2px;
|
||||
border: none;
|
||||
background-color: ${props => props.active ? '#fff' : 'rgba(255, 255, 255, 0.3)'};
|
||||
background-color: ${(props) =>
|
||||
props.active ? '#fff' : 'rgba(255, 255, 255, 0.3)'};
|
||||
outline: none !important;
|
||||
|
||||
&:focus {
|
||||
box-shadow : 0 0 0 2px #2666cc;
|
||||
box-shadow: 0 0 0 2px #2666cc;
|
||||
background-color: #eee;
|
||||
}
|
||||
`;
|
||||
|
@ -53,14 +54,22 @@ export default function Collections(props: CollectionProps) {
|
|||
const { selected, collections, selectCollection } = props;
|
||||
const clickHandler = (id?: number) => () => selectCollection(id);
|
||||
|
||||
return <Container>
|
||||
<Wrapper>
|
||||
<Chip active={!selected} onClick={clickHandler()}>All</Chip>
|
||||
{collections?.map(item => <Chip
|
||||
key={item.id}
|
||||
active={selected === item.id.toString()}
|
||||
onClick={clickHandler(item.id)}
|
||||
>{item.name}</Chip>)}
|
||||
</Wrapper>
|
||||
</Container>;
|
||||
return (
|
||||
<Container>
|
||||
<Wrapper>
|
||||
<Chip active={!selected} onClick={clickHandler()}>
|
||||
All
|
||||
</Chip>
|
||||
{collections?.map((item) => (
|
||||
<Chip
|
||||
key={item.id}
|
||||
active={selected === item.id.toString()}
|
||||
onClick={clickHandler(item.id)}
|
||||
>
|
||||
{item.name}
|
||||
</Chip>
|
||||
))}
|
||||
</Wrapper>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,28 +2,46 @@ import React, { useEffect, useState } from 'react';
|
|||
import { Button, Form, Modal } from 'react-bootstrap';
|
||||
import { createAlbum } from 'services/collectionService';
|
||||
import UploadService from 'services/uploadService';
|
||||
import { CollectionAndItsLatestFile } from 'services/collectionService'
|
||||
import { CollectionAndItsLatestFile } from 'services/collectionService';
|
||||
import { getToken } from 'utils/common/key';
|
||||
|
||||
export default function CreateCollection(props) {
|
||||
const {
|
||||
acceptedFiles,
|
||||
setProgressView,
|
||||
progressBarProps,
|
||||
refetchData,
|
||||
modalView,
|
||||
closeModal,
|
||||
closeUploadModal,
|
||||
setErrorCode,
|
||||
} = props;
|
||||
const [albumName, setAlbumName] = useState('');
|
||||
|
||||
const { acceptedFiles, setProgressView, progressBarProps, refetchData, modalView, closeModal, closeUploadModal, setErrorCode } = props;
|
||||
const [albumName, setAlbumName] = useState("");
|
||||
|
||||
const handleChange = (event) => { setAlbumName(event.target.value); }
|
||||
const handleChange = (event) => {
|
||||
setAlbumName(event.target.value);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (acceptedFiles == null)
|
||||
if (acceptedFiles == null) {
|
||||
return;
|
||||
}
|
||||
let commonPathPrefix: string = (() => {
|
||||
const paths: string[] = acceptedFiles.map(files => files.path);
|
||||
const paths: string[] = acceptedFiles.map((files) => files.path);
|
||||
paths.sort();
|
||||
let firstPath = paths[0], lastPath = paths[paths.length - 1], L = firstPath.length, i = 0;
|
||||
let firstPath = paths[0],
|
||||
lastPath = paths[paths.length - 1],
|
||||
L = firstPath.length,
|
||||
i = 0;
|
||||
while (i < L && firstPath.charAt(i) === lastPath.charAt(i)) i++;
|
||||
return firstPath.substring(0, i);
|
||||
})();
|
||||
if (commonPathPrefix)
|
||||
commonPathPrefix = commonPathPrefix.substr(1, commonPathPrefix.lastIndexOf('/') - 1);
|
||||
if (commonPathPrefix) {
|
||||
commonPathPrefix = commonPathPrefix.substr(
|
||||
1,
|
||||
commonPathPrefix.lastIndexOf('/') - 1
|
||||
);
|
||||
}
|
||||
setAlbumName(commonPathPrefix);
|
||||
}, [acceptedFiles]);
|
||||
const handleSubmit = async (event) => {
|
||||
|
@ -36,46 +54,54 @@ export default function CreateCollection(props) {
|
|||
|
||||
const collection = await createAlbum(albumName);
|
||||
|
||||
const collectionAndItsLatestFile: CollectionAndItsLatestFile = { collection, file: null }
|
||||
const collectionAndItsLatestFile: CollectionAndItsLatestFile = {
|
||||
collection,
|
||||
file: null,
|
||||
};
|
||||
|
||||
progressBarProps.setPercentComplete(0);
|
||||
setProgressView(true);
|
||||
|
||||
await UploadService.uploadFiles(acceptedFiles, collectionAndItsLatestFile, token, progressBarProps);
|
||||
await UploadService.uploadFiles(
|
||||
acceptedFiles,
|
||||
collectionAndItsLatestFile,
|
||||
token,
|
||||
progressBarProps
|
||||
);
|
||||
refetchData();
|
||||
|
||||
}
|
||||
catch (err) {
|
||||
if (err.response)
|
||||
} catch (err) {
|
||||
if (err.response) {
|
||||
setErrorCode(err.response.status);
|
||||
}
|
||||
finally {
|
||||
}
|
||||
} finally {
|
||||
setProgressView(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Modal
|
||||
show={modalView}
|
||||
onHide={closeModal}
|
||||
centered
|
||||
backdrop="static"
|
||||
>
|
||||
<Modal show={modalView} onHide={closeModal} centered backdrop="static">
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>
|
||||
Create Collection
|
||||
</Modal.Title>
|
||||
<Modal.Title>Create Collection</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Form.Group controlId="formBasicEmail">
|
||||
<Form.Label>Album Name:</Form.Label>
|
||||
<Form.Control type="text" placeholder="Enter Album Name" value={albumName} onChange={handleChange} />
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder="Enter Album Name"
|
||||
value={albumName}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
<Button variant="primary" type="submit" style={{ width: "100%" }}>
|
||||
<Button
|
||||
variant="primary"
|
||||
type="submit"
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Button>
|
||||
</Form>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,20 +17,20 @@ export const getColor = (props) => {
|
|||
export const enableBorder = (props) => (props.isDragActive ? 'dashed' : 'none');
|
||||
|
||||
export const DropDiv = styled.div`
|
||||
width:200px;
|
||||
margin:5px;
|
||||
height:230px;
|
||||
color:black;
|
||||
border-width: 2px;
|
||||
border-radius: 2px;
|
||||
border-color: ${(props) => getColor(props)};
|
||||
border-style: ${(props) => enableBorder(props)};
|
||||
outline: none;
|
||||
transition: border 0.24s ease-in-out;
|
||||
width: 200px;
|
||||
margin: 5px;
|
||||
height: 230px;
|
||||
color: black;
|
||||
border-width: 2px;
|
||||
border-radius: 2px;
|
||||
border-color: ${(props) => getColor(props)};
|
||||
border-style: ${(props) => enableBorder(props)};
|
||||
outline: none;
|
||||
transition: border 0.24s ease-in-out;
|
||||
`;
|
||||
|
||||
export function DropzoneWrapper(props) {
|
||||
const { children, ...callbackProps } = props
|
||||
const { children, ...callbackProps } = props;
|
||||
return (
|
||||
<Dropzone
|
||||
noDragEventsBubbling
|
||||
|
@ -59,6 +59,6 @@ export function DropzoneWrapper(props) {
|
|||
}}
|
||||
</Dropzone>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default DropzoneWrapper;
|
||||
|
|
|
@ -19,4 +19,4 @@ export default function ErrorAlert({ errorCode }) {
|
|||
{errorMessage}
|
||||
</Alert>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@ import styled from 'styled-components';
|
|||
import PlayCircleOutline from 'components/PlayCircleOutline';
|
||||
|
||||
interface IProps {
|
||||
data: file,
|
||||
updateUrl: (url: string) => void,
|
||||
onClick?: () => void,
|
||||
forcedEnable?: boolean,
|
||||
data: file;
|
||||
updateUrl: (url: string) => void;
|
||||
onClick?: () => void;
|
||||
forcedEnable?: boolean;
|
||||
}
|
||||
|
||||
const Cont = styled.div<{ disabled: boolean }>`
|
||||
|
@ -19,7 +19,7 @@ const Cont = styled.div<{ disabled: boolean }>`
|
|||
min-width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};
|
||||
cursor: ${(props) => (props.disabled ? 'not-allowed' : 'pointer')};
|
||||
|
||||
& > img {
|
||||
object-fit: cover;
|
||||
|
@ -36,7 +36,7 @@ const Cont = styled.div<{ disabled: boolean }>`
|
|||
margin-top: 50%;
|
||||
top: -25px;
|
||||
left: -25px;
|
||||
filter: drop-shadow( 3px 3px 2px rgba(0, 0, 0, .7));
|
||||
filter: drop-shadow(3px 3px 2px rgba(0, 0, 0, 0.7));
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -52,7 +52,7 @@ export default function PreviewCard(props: IProps) {
|
|||
setImgSrc(url);
|
||||
data.msrc = url;
|
||||
updateUrl(url);
|
||||
}
|
||||
};
|
||||
main();
|
||||
}
|
||||
}, [data]);
|
||||
|
@ -61,10 +61,15 @@ export default function PreviewCard(props: IProps) {
|
|||
if (data?.msrc || imgSrc) {
|
||||
onClick?.();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return <Cont onClick={handleClick} disabled={!forcedEnable && !data?.msrc && !imgSrc}>
|
||||
<img src={data?.msrc || imgSrc} />
|
||||
{data?.metadata.fileType === 1 && <PlayCircleOutline />}
|
||||
</Cont>;
|
||||
return (
|
||||
<Cont
|
||||
onClick={handleClick}
|
||||
disabled={!forcedEnable && !data?.msrc && !imgSrc}
|
||||
>
|
||||
<img src={data?.msrc || imgSrc} />
|
||||
{data?.metadata.fileType === 1 && <PlayCircleOutline />}
|
||||
</Cont>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import React, { useState } from "react"
|
||||
import { UPLOAD_STAGES } from "services/uploadService";
|
||||
import CollectionSelector from "./CollectionSelector"
|
||||
import UploadProgress from "./UploadProgress"
|
||||
import React, { useState } from 'react';
|
||||
import { UPLOAD_STAGES } from 'services/uploadService';
|
||||
import CollectionSelector from './CollectionSelector';
|
||||
import UploadProgress from './UploadProgress';
|
||||
|
||||
export default function Upload(props) {
|
||||
const [progressView, setProgressView] = useState(false);
|
||||
const [uploadStage, setUploadStage] = useState<UPLOAD_STAGES>(UPLOAD_STAGES.START);
|
||||
const [uploadStage, setUploadStage] = useState<UPLOAD_STAGES>(
|
||||
UPLOAD_STAGES.START
|
||||
);
|
||||
const [fileCounter, setFileCounter] = useState({ current: 0, total: 0 });
|
||||
const [percentComplete, setPercentComplete] = useState(0);
|
||||
const init = () => {
|
||||
|
@ -13,20 +15,25 @@ export default function Upload(props) {
|
|||
setUploadStage(UPLOAD_STAGES.START);
|
||||
setFileCounter({ current: 0, total: 0 });
|
||||
setPercentComplete(0);
|
||||
}
|
||||
return (<>
|
||||
<CollectionSelector
|
||||
{...props}
|
||||
setProgressView={setProgressView}
|
||||
progressBarProps={{ setPercentComplete, setFileCounter, setUploadStage }}
|
||||
/>
|
||||
<UploadProgress
|
||||
now={percentComplete}
|
||||
fileCounter={fileCounter}
|
||||
uploadStage={uploadStage}
|
||||
show={progressView}
|
||||
onHide={init}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<CollectionSelector
|
||||
{...props}
|
||||
setProgressView={setProgressView}
|
||||
progressBarProps={{
|
||||
setPercentComplete,
|
||||
setFileCounter,
|
||||
setUploadStage,
|
||||
}}
|
||||
/>
|
||||
<UploadProgress
|
||||
now={percentComplete}
|
||||
fileCounter={fileCounter}
|
||||
uploadStage={uploadStage}
|
||||
show={progressView}
|
||||
onHide={init}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@ import { Button } from 'react-bootstrap';
|
|||
|
||||
function UploadButton({ showModal }) {
|
||||
return (
|
||||
<Button variant='primary' onClick={showModal}>
|
||||
<Button variant="primary" onClick={showModal}>
|
||||
Upload New Photos
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default UploadButton;
|
||||
|
|
|
@ -1,30 +1,40 @@
|
|||
import React from 'react';
|
||||
import { Alert, Modal, ProgressBar } from 'react-bootstrap';
|
||||
import constants from 'utils/strings/constants'
|
||||
import constants from 'utils/strings/constants';
|
||||
|
||||
export default function UploadProgress({ fileCounter, uploadStage, now, ...props }) {
|
||||
export default function UploadProgress({
|
||||
fileCounter,
|
||||
uploadStage,
|
||||
now,
|
||||
...props
|
||||
}) {
|
||||
return (
|
||||
<Modal
|
||||
{...props}
|
||||
size='lg'
|
||||
aria-labelledby='contained-modal-title-vcenter'
|
||||
size="lg"
|
||||
aria-labelledby="contained-modal-title-vcenter"
|
||||
centered
|
||||
backdrop="static"
|
||||
>
|
||||
<Modal.Header>
|
||||
<Modal.Title id='contained-modal-title-vcenter'>
|
||||
<Modal.Title id="contained-modal-title-vcenter">
|
||||
Uploading Files
|
||||
</Modal.Title>
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
{now === 100 ? (
|
||||
<Alert variant='success'>{constants.UPLOAD[3]}</Alert>
|
||||
<Alert variant="success">{constants.UPLOAD[3]}</Alert>
|
||||
) : (
|
||||
<>
|
||||
<Alert variant='info'>{constants.UPLOAD[uploadStage]} {fileCounter?.total != 0 ? `${fileCounter?.current} ${constants.OF} ${fileCounter?.total}` : ''}</Alert>
|
||||
<ProgressBar animated now={now} />
|
||||
</>
|
||||
)}
|
||||
<>
|
||||
<Alert variant="info">
|
||||
{constants.UPLOAD[uploadStage]}{' '}
|
||||
{fileCounter?.total != 0
|
||||
? `${fileCounter?.current} ${constants.OF} ${fileCounter?.total}`
|
||||
: ''}
|
||||
</Alert>
|
||||
<ProgressBar animated now={now} />
|
||||
</>
|
||||
)}
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -29,7 +29,7 @@ import {
|
|||
getLocalCollections,
|
||||
} from 'services/collectionService';
|
||||
import constants from 'utils/strings/constants';
|
||||
import ErrorAlert from './components/ErrorAlert';
|
||||
import ErrorAlert from './components/ErrorAlert';
|
||||
|
||||
const DATE_CONTAINER_HEIGHT = 45;
|
||||
const IMAGE_CONTAINER_HEIGHT = 200;
|
||||
|
|
|
@ -11,11 +11,12 @@ import { putAttributes } from 'services/userService';
|
|||
import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
|
||||
import { useRouter } from 'next/router';
|
||||
import { getKey, SESSION_KEYS, setKey } from 'utils/storage/sessionStorage';
|
||||
import * as Comlink from "comlink";
|
||||
import * as Comlink from 'comlink';
|
||||
import { keyEncryptionResult } from 'services/uploadService';
|
||||
|
||||
const CryptoWorker: any = typeof window !== 'undefined'
|
||||
&& Comlink.wrap(new Worker("worker/crypto.worker.js", { type: 'module' }));
|
||||
const CryptoWorker: any =
|
||||
typeof window !== 'undefined' &&
|
||||
Comlink.wrap(new Worker('worker/crypto.worker.js', { type: 'module' }));
|
||||
|
||||
const Image = styled.img`
|
||||
width: 200px;
|
||||
|
@ -38,7 +39,7 @@ export default function Generate() {
|
|||
router.prefetch('/gallery');
|
||||
const user = getData(LS_KEYS.USER);
|
||||
if (!user?.token) {
|
||||
router.push("/");
|
||||
router.push('/');
|
||||
} else if (key) {
|
||||
router.push('/gallery');
|
||||
} else {
|
||||
|
@ -46,7 +47,10 @@ export default function Generate() {
|
|||
}
|
||||
}, []);
|
||||
|
||||
const onSubmit = async (values: formValues, { setFieldError }: FormikHelpers<formValues>) => {
|
||||
const onSubmit = async (
|
||||
values: formValues,
|
||||
{ setFieldError }: FormikHelpers<formValues>
|
||||
) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { passphrase, confirm } = values;
|
||||
|
@ -54,11 +58,20 @@ export default function Generate() {
|
|||
const cryptoWorker = await new CryptoWorker();
|
||||
const key: string = await cryptoWorker.generateMasterKey();
|
||||
const kekSalt: string = await cryptoWorker.generateSaltToDeriveKey();
|
||||
const kek: string = await cryptoWorker.deriveKey(passphrase, kekSalt);
|
||||
const kek: string = await cryptoWorker.deriveKey(
|
||||
passphrase,
|
||||
kekSalt
|
||||
);
|
||||
const kekHash: string = await cryptoWorker.hash(kek);
|
||||
const encryptedKeyAttributes: keyEncryptionResult = await cryptoWorker.encryptToB64(key, kek);
|
||||
const encryptedKeyAttributes: keyEncryptionResult = await cryptoWorker.encryptToB64(
|
||||
key,
|
||||
kek
|
||||
);
|
||||
const keyPair = await cryptoWorker.generateKeyPair();
|
||||
const encryptedKeyPairAttributes: keyEncryptionResult = await cryptoWorker.encryptToB64(keyPair.privateKey, key);
|
||||
const encryptedKeyPairAttributes: keyEncryptionResult = await cryptoWorker.encryptToB64(
|
||||
keyPair.privateKey,
|
||||
key
|
||||
);
|
||||
|
||||
const keyAttributes = {
|
||||
kekSalt,
|
||||
|
@ -66,13 +79,20 @@ export default function Generate() {
|
|||
encryptedKey: encryptedKeyAttributes.encryptedData,
|
||||
keyDecryptionNonce: encryptedKeyAttributes.nonce,
|
||||
publicKey: keyPair.publicKey,
|
||||
encryptedSecretKey: encryptedKeyPairAttributes.encryptedData,
|
||||
secretKeyDecryptionNonce: encryptedKeyPairAttributes.nonce
|
||||
encryptedSecretKey:
|
||||
encryptedKeyPairAttributes.encryptedData,
|
||||
secretKeyDecryptionNonce: encryptedKeyPairAttributes.nonce,
|
||||
};
|
||||
await putAttributes(token, getData(LS_KEYS.USER).name, keyAttributes);
|
||||
await putAttributes(
|
||||
token,
|
||||
getData(LS_KEYS.USER).name,
|
||||
keyAttributes
|
||||
);
|
||||
setData(LS_KEYS.KEY_ATTRIBUTES, keyAttributes);
|
||||
|
||||
const sessionKeyAttributes = await cryptoWorker.encryptToB64(key);
|
||||
const sessionKeyAttributes = await cryptoWorker.encryptToB64(
|
||||
key
|
||||
);
|
||||
const sessionKey = sessionKeyAttributes.key;
|
||||
const sessionNonce = sessionKeyAttributes.nonce;
|
||||
const encryptionKey = sessionKeyAttributes.encryptedData;
|
||||
|
@ -83,62 +103,85 @@ export default function Generate() {
|
|||
setFieldError('confirm', constants.PASSPHRASE_MATCH_ERROR);
|
||||
}
|
||||
} catch (e) {
|
||||
setFieldError('passphrase', `${constants.UNKNOWN_ERROR} ${e.message}`);
|
||||
setFieldError(
|
||||
'passphrase',
|
||||
`${constants.UNKNOWN_ERROR} ${e.message}`
|
||||
);
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (<Container>
|
||||
<Image alt='vault' src='/vault.svg' />
|
||||
<Card>
|
||||
<Card.Body>
|
||||
<div className="text-center">
|
||||
<p>{constants.ENTER_ENC_PASSPHRASE}</p>
|
||||
<p>{constants.PASSPHRASE_DISCLAIMER()}</p>
|
||||
</div>
|
||||
<Formik<formValues>
|
||||
initialValues={{ passphrase: '', confirm: '' }}
|
||||
validationSchema={Yup.object().shape({
|
||||
passphrase: Yup.string().required(constants.REQUIRED),
|
||||
confirm: Yup.string().required(constants.REQUIRED),
|
||||
})}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
{({ values, touched, errors, handleChange, handleBlur, handleSubmit }) => (
|
||||
<Form noValidate onSubmit={handleSubmit}>
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder={constants.PASSPHRASE_HINT}
|
||||
value={values.passphrase}
|
||||
onChange={handleChange('passphrase')}
|
||||
onBlur={handleBlur('passphrase')}
|
||||
isInvalid={Boolean(touched.passphrase && errors.passphrase)}
|
||||
disabled={loading}
|
||||
/>
|
||||
<Form.Control.Feedback type='invalid'>
|
||||
{errors.passphrase}
|
||||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder={constants.PASSPHRASE_CONFIRM}
|
||||
value={values.confirm}
|
||||
onChange={handleChange('confirm')}
|
||||
onBlur={handleBlur('confirm')}
|
||||
isInvalid={Boolean(touched.confirm && errors.confirm)}
|
||||
disabled={loading}
|
||||
/>
|
||||
<Form.Control.Feedback type='invalid'>
|
||||
{errors.confirm}
|
||||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Button type="submit" block disabled={loading}>{constants.SET_PASSPHRASE}</Button>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Container>)
|
||||
return (
|
||||
<Container>
|
||||
<Image alt="vault" src="/vault.svg" />
|
||||
<Card>
|
||||
<Card.Body>
|
||||
<div className="text-center">
|
||||
<p>{constants.ENTER_ENC_PASSPHRASE}</p>
|
||||
<p>{constants.PASSPHRASE_DISCLAIMER()}</p>
|
||||
</div>
|
||||
<Formik<formValues>
|
||||
initialValues={{ passphrase: '', confirm: '' }}
|
||||
validationSchema={Yup.object().shape({
|
||||
passphrase: Yup.string().required(
|
||||
constants.REQUIRED
|
||||
),
|
||||
confirm: Yup.string().required(constants.REQUIRED),
|
||||
})}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
{({
|
||||
values,
|
||||
touched,
|
||||
errors,
|
||||
handleChange,
|
||||
handleBlur,
|
||||
handleSubmit,
|
||||
}) => (
|
||||
<Form noValidate onSubmit={handleSubmit}>
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder={constants.PASSPHRASE_HINT}
|
||||
value={values.passphrase}
|
||||
onChange={handleChange('passphrase')}
|
||||
onBlur={handleBlur('passphrase')}
|
||||
isInvalid={Boolean(
|
||||
touched.passphrase &&
|
||||
errors.passphrase
|
||||
)}
|
||||
disabled={loading}
|
||||
/>
|
||||
<Form.Control.Feedback type="invalid">
|
||||
{errors.passphrase}
|
||||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder={
|
||||
constants.PASSPHRASE_CONFIRM
|
||||
}
|
||||
value={values.confirm}
|
||||
onChange={handleChange('confirm')}
|
||||
onBlur={handleBlur('confirm')}
|
||||
isInvalid={Boolean(
|
||||
touched.confirm && errors.confirm
|
||||
)}
|
||||
disabled={loading}
|
||||
/>
|
||||
<Form.Control.Feedback type="invalid">
|
||||
{errors.confirm}
|
||||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Button type="submit" block disabled={loading}>
|
||||
{constants.SET_PASSPHRASE}
|
||||
</Button>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -28,7 +28,10 @@ export default function Home() {
|
|||
}
|
||||
}, []);
|
||||
|
||||
const loginUser = async ({ email }: formValues, { setFieldError }: FormikHelpers<formValues>) => {
|
||||
const loginUser = async (
|
||||
{ email }: formValues,
|
||||
{ setFieldError }: FormikHelpers<formValues>
|
||||
) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
await getOtt(email);
|
||||
|
@ -38,27 +41,36 @@ export default function Home() {
|
|||
setFieldError('email', `${constants.UNKNOWN_ERROR} ${e.message}`);
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const register = () => {
|
||||
router.push('/signup');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Card style={{ minWidth: '300px' }} className="text-center">
|
||||
<Card.Body>
|
||||
<Card.Title style={{ marginBottom: '20px' }}>{constants.LOGIN}</Card.Title>
|
||||
<Card.Title style={{ marginBottom: '20px' }}>
|
||||
{constants.LOGIN}
|
||||
</Card.Title>
|
||||
<Formik<formValues>
|
||||
initialValues={{ email: '' }}
|
||||
validationSchema={Yup.object().shape({
|
||||
email: Yup.string()
|
||||
.email(constants.EMAIL_ERROR)
|
||||
.required(constants.REQUIRED)
|
||||
.required(constants.REQUIRED),
|
||||
})}
|
||||
onSubmit={loginUser}
|
||||
>
|
||||
{({ values, errors, touched, handleChange, handleBlur, handleSubmit }) => (
|
||||
{({
|
||||
values,
|
||||
errors,
|
||||
touched,
|
||||
handleChange,
|
||||
handleBlur,
|
||||
handleSubmit,
|
||||
}) => (
|
||||
<Form noValidate onSubmit={handleSubmit}>
|
||||
<Form.Group controlId="formBasicEmail">
|
||||
<Form.Control
|
||||
|
@ -67,7 +79,9 @@ export default function Home() {
|
|||
value={values.email}
|
||||
onChange={handleChange('email')}
|
||||
onBlur={handleBlur('email')}
|
||||
isInvalid={Boolean(touched.email && errors.email)}
|
||||
isInvalid={Boolean(
|
||||
touched.email && errors.email
|
||||
)}
|
||||
disabled={loading}
|
||||
/>
|
||||
<FormControl.Feedback type="invalid">
|
||||
|
@ -75,7 +89,9 @@ export default function Home() {
|
|||
</FormControl.Feedback>
|
||||
</Form.Group>
|
||||
<Button
|
||||
variant="primary" type="submit" block
|
||||
variant="primary"
|
||||
type="submit"
|
||||
block
|
||||
disabled={loading}
|
||||
style={{ marginBottom: '12px' }}
|
||||
>
|
||||
|
@ -84,9 +100,15 @@ export default function Home() {
|
|||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
<Card.Link href="#" onClick={register} style={{ fontSize: '14px' }}>Don't have an account?</Card.Link>
|
||||
<Card.Link
|
||||
href="#"
|
||||
onClick={register}
|
||||
style={{ fontSize: '14px' }}
|
||||
>
|
||||
Don't have an account?
|
||||
</Card.Link>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Container>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ interface FormValues {
|
|||
email: string;
|
||||
}
|
||||
|
||||
|
||||
export default function Home() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const router = useRouter();
|
||||
|
@ -30,7 +29,10 @@ export default function Home() {
|
|||
}
|
||||
}, []);
|
||||
|
||||
const registerUser = async ({ name, email }: FormValues, { setFieldError }: FormikHelpers<FormValues>) => {
|
||||
const registerUser = async (
|
||||
{ name, email }: FormValues,
|
||||
{ setFieldError }: FormikHelpers<FormValues>
|
||||
) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setData(LS_KEYS.USER, { name, email });
|
||||
|
@ -40,25 +42,33 @@ export default function Home() {
|
|||
setFieldError('email', `${constants.UNKNOWN_ERROR} ${e.message}`);
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Card style={{ minWidth: '300px' }} className="text-center" >
|
||||
<Card style={{ minWidth: '300px' }} className="text-center">
|
||||
<Card.Body>
|
||||
<Card.Title style={{ marginBottom: '20px' }}>{constants.SIGN_UP}</Card.Title>
|
||||
<Card.Title style={{ marginBottom: '20px' }}>
|
||||
{constants.SIGN_UP}
|
||||
</Card.Title>
|
||||
<Formik<FormValues>
|
||||
initialValues={{ name: '', email: '' }}
|
||||
validationSchema={Yup.object().shape({
|
||||
name: Yup.string()
|
||||
.required(constants.REQUIRED),
|
||||
name: Yup.string().required(constants.REQUIRED),
|
||||
email: Yup.string()
|
||||
.email(constants.EMAIL_ERROR)
|
||||
.required(constants.REQUIRED)
|
||||
.required(constants.REQUIRED),
|
||||
})}
|
||||
onSubmit={registerUser}
|
||||
>
|
||||
{({ values, errors, touched, handleChange, handleBlur, handleSubmit }): JSX.Element => (
|
||||
{({
|
||||
values,
|
||||
errors,
|
||||
touched,
|
||||
handleChange,
|
||||
handleBlur,
|
||||
handleSubmit,
|
||||
}): JSX.Element => (
|
||||
<Form noValidate onSubmit={handleSubmit}>
|
||||
<Form.Group controlId="registrationForm.name">
|
||||
<Form.Control
|
||||
|
@ -67,7 +77,9 @@ export default function Home() {
|
|||
value={values.name}
|
||||
onChange={handleChange('name')}
|
||||
onBlur={handleBlur('name')}
|
||||
isInvalid={Boolean(touched.name && errors.name)}
|
||||
isInvalid={Boolean(
|
||||
touched.name && errors.name
|
||||
)}
|
||||
disabled={loading}
|
||||
/>
|
||||
<FormControl.Feedback type="invalid">
|
||||
|
@ -81,7 +93,9 @@ export default function Home() {
|
|||
value={values.email}
|
||||
onChange={handleChange('email')}
|
||||
onBlur={handleBlur('email')}
|
||||
isInvalid={Boolean(touched.email && errors.email)}
|
||||
isInvalid={Boolean(
|
||||
touched.email && errors.email
|
||||
)}
|
||||
disabled={loading}
|
||||
/>
|
||||
<FormControl.Feedback type="invalid">
|
||||
|
@ -92,7 +106,9 @@ export default function Home() {
|
|||
{constants.DATA_DISCLAIMER}
|
||||
</DisclaimerContainer>
|
||||
<Button
|
||||
variant="primary" type="submit" block
|
||||
variant="primary"
|
||||
type="submit"
|
||||
block
|
||||
disabled={loading}
|
||||
>
|
||||
{constants.SUBMIT}
|
||||
|
@ -103,5 +119,5 @@ export default function Home() {
|
|||
</Card.Body>
|
||||
</Card>
|
||||
</Container>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -32,15 +32,18 @@ export default function Verify() {
|
|||
router.prefetch('/generate');
|
||||
const user = getData(LS_KEYS.USER);
|
||||
if (!user?.email) {
|
||||
router.push("/");
|
||||
router.push('/');
|
||||
} else if (user.token) {
|
||||
router.push("/credentials")
|
||||
router.push('/credentials');
|
||||
} else {
|
||||
setEmail(user.email);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onSubmit = async ({ ott }: formValues, { setFieldError }: FormikHelpers<formValues>) => {
|
||||
const onSubmit = async (
|
||||
{ ott }: formValues,
|
||||
{ setFieldError }: FormikHelpers<formValues>
|
||||
) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const resp = await verifyOtt(email, ott);
|
||||
|
@ -52,9 +55,9 @@ export default function Verify() {
|
|||
});
|
||||
setData(LS_KEYS.KEY_ATTRIBUTES, resp.data.keyAttributes);
|
||||
if (resp.data.keyAttributes?.encryptedKey) {
|
||||
router.push("/credentials");
|
||||
router.push('/credentials');
|
||||
} else {
|
||||
router.push("/generate");
|
||||
router.push('/generate');
|
||||
}
|
||||
} catch (e) {
|
||||
if (e?.response?.status === 401) {
|
||||
|
@ -64,60 +67,80 @@ export default function Verify() {
|
|||
}
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const resendEmail = async () => {
|
||||
setResend(1);
|
||||
const resp = await getOtt(email);
|
||||
setResend(2);
|
||||
setTimeout(() => setResend(0), 3000);
|
||||
}
|
||||
};
|
||||
|
||||
if (!email) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (<Container>
|
||||
<Image alt='Email Sent' src='/email_sent.svg' />
|
||||
<Card style={{ minWidth: '300px' }} className="text-center">
|
||||
<Card.Body>
|
||||
<Card.Title>{constants.VERIFY_EMAIL}</Card.Title>
|
||||
{constants.EMAIL_SENT({ email })}
|
||||
{constants.CHECK_INBOX}<br />
|
||||
<br />
|
||||
<Formik<formValues>
|
||||
initialValues={{ ott: '' }}
|
||||
validationSchema={Yup.object().shape({
|
||||
ott: Yup.string().required(constants.REQUIRED),
|
||||
})}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
{({ values, touched, errors, handleChange, handleBlur, handleSubmit }) => (
|
||||
<Form noValidate onSubmit={handleSubmit}>
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
className="text-center"
|
||||
type='text'
|
||||
value={values.ott}
|
||||
onChange={handleChange('ott')}
|
||||
onBlur={handleBlur('ott')}
|
||||
isInvalid={Boolean(touched.ott && errors.ott)}
|
||||
placeholder={constants.ENTER_OTT}
|
||||
disabled={loading}
|
||||
/>
|
||||
<Form.Control.Feedback type='invalid'>
|
||||
{errors.ott}
|
||||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Button type="submit" block disabled={loading}>{constants.VERIFY}</Button>
|
||||
<br />
|
||||
{resend === 0 && <a href="#" onClick={resendEmail}>{constants.RESEND_MAIL}</a>}
|
||||
{resend === 1 && <span>{constants.SENDING}</span>}
|
||||
{resend === 2 && <span>{constants.SENT}</span>}
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Container>)
|
||||
}
|
||||
return (
|
||||
<Container>
|
||||
<Image alt="Email Sent" src="/email_sent.svg" />
|
||||
<Card style={{ minWidth: '300px' }} className="text-center">
|
||||
<Card.Body>
|
||||
<Card.Title>{constants.VERIFY_EMAIL}</Card.Title>
|
||||
{constants.EMAIL_SENT({ email })}
|
||||
{constants.CHECK_INBOX}
|
||||
<br />
|
||||
<br />
|
||||
<Formik<formValues>
|
||||
initialValues={{ ott: '' }}
|
||||
validationSchema={Yup.object().shape({
|
||||
ott: Yup.string().required(constants.REQUIRED),
|
||||
})}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
{({
|
||||
values,
|
||||
touched,
|
||||
errors,
|
||||
handleChange,
|
||||
handleBlur,
|
||||
handleSubmit,
|
||||
}) => (
|
||||
<Form noValidate onSubmit={handleSubmit}>
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
className="text-center"
|
||||
type="text"
|
||||
value={values.ott}
|
||||
onChange={handleChange('ott')}
|
||||
onBlur={handleBlur('ott')}
|
||||
isInvalid={Boolean(
|
||||
touched.ott && errors.ott
|
||||
)}
|
||||
placeholder={constants.ENTER_OTT}
|
||||
disabled={loading}
|
||||
/>
|
||||
<Form.Control.Feedback type="invalid">
|
||||
{errors.ott}
|
||||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Button type="submit" block disabled={loading}>
|
||||
{constants.VERIFY}
|
||||
</Button>
|
||||
<br />
|
||||
{resend === 0 && (
|
||||
<a href="#" onClick={resendEmail}>
|
||||
{constants.RESEND_MAIL}
|
||||
</a>
|
||||
)}
|
||||
{resend === 1 && (
|
||||
<span>{constants.SENDING}</span>
|
||||
)}
|
||||
{resend === 2 && <span>{constants.SENT}</span>}
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -69,7 +69,12 @@ class HTTPService {
|
|||
/**
|
||||
* Get request.
|
||||
*/
|
||||
public get(url: string, params?: IQueryPrams, headers?: IHTTPHeaders, customConfig?: any) {
|
||||
public get(
|
||||
url: string,
|
||||
params?: IQueryPrams,
|
||||
headers?: IHTTPHeaders,
|
||||
customConfig?: any
|
||||
) {
|
||||
return this.request(
|
||||
{
|
||||
headers,
|
||||
|
@ -77,15 +82,20 @@ class HTTPService {
|
|||
params,
|
||||
url,
|
||||
},
|
||||
customConfig,
|
||||
customConfig
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post request
|
||||
*/
|
||||
public post(url: string, data?: any, params?: IQueryPrams,
|
||||
headers?: IHTTPHeaders, customConfig?: any) {
|
||||
public post(
|
||||
url: string,
|
||||
data?: any,
|
||||
params?: IQueryPrams,
|
||||
headers?: IHTTPHeaders,
|
||||
customConfig?: any
|
||||
) {
|
||||
return this.request(
|
||||
{
|
||||
data,
|
||||
|
@ -94,15 +104,20 @@ class HTTPService {
|
|||
params,
|
||||
url,
|
||||
},
|
||||
customConfig,
|
||||
customConfig
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put request
|
||||
*/
|
||||
public put(url: string, data: any, params?: IQueryPrams,
|
||||
headers?: IHTTPHeaders, customConfig?: any) {
|
||||
public put(
|
||||
url: string,
|
||||
data: any,
|
||||
params?: IQueryPrams,
|
||||
headers?: IHTTPHeaders,
|
||||
customConfig?: any
|
||||
) {
|
||||
return this.request(
|
||||
{
|
||||
data,
|
||||
|
@ -111,15 +126,20 @@ class HTTPService {
|
|||
params,
|
||||
url,
|
||||
},
|
||||
customConfig,
|
||||
customConfig
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete request
|
||||
*/
|
||||
public delete(url: string, data: any, params?: IQueryPrams,
|
||||
headers?: IHTTPHeaders, customConfig?: any) {
|
||||
public delete(
|
||||
url: string,
|
||||
data: any,
|
||||
params?: IQueryPrams,
|
||||
headers?: IHTTPHeaders,
|
||||
customConfig?: any
|
||||
) {
|
||||
return this.request(
|
||||
{
|
||||
data,
|
||||
|
@ -128,7 +148,7 @@ class HTTPService {
|
|||
params,
|
||||
url,
|
||||
},
|
||||
customConfig,
|
||||
customConfig
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,36 @@
|
|||
import { getEndpoint } from 'utils/common/apiUtil';
|
||||
import HTTPService from './HTTPService';
|
||||
import * as Comlink from 'comlink';
|
||||
import EXIF from "exif-js";
|
||||
import EXIF from 'exif-js';
|
||||
import { fileAttribute } from './fileService';
|
||||
import { collection, CollectionAndItsLatestFile } from "./collectionService"
|
||||
import { collection, CollectionAndItsLatestFile } from './collectionService';
|
||||
import { FILE_TYPE } from 'pages/gallery';
|
||||
const CryptoWorker: any =
|
||||
typeof window !== 'undefined' &&
|
||||
Comlink.wrap(new Worker('worker/crypto.worker.js', { type: 'module' }));
|
||||
const ENDPOINT = getEndpoint();
|
||||
|
||||
|
||||
interface encryptionResult {
|
||||
file: fileAttribute,
|
||||
key: string
|
||||
file: fileAttribute;
|
||||
key: string;
|
||||
}
|
||||
export interface keyEncryptionResult {
|
||||
encryptedData: string,
|
||||
key: string,
|
||||
nonce: string,
|
||||
encryptedData: string;
|
||||
key: string;
|
||||
nonce: string;
|
||||
}
|
||||
|
||||
interface uploadURL {
|
||||
url: string,
|
||||
objectKey: string
|
||||
url: string;
|
||||
objectKey: string;
|
||||
}
|
||||
|
||||
interface FileinMemory {
|
||||
filedata: Uint8Array,
|
||||
thumbnail: Uint8Array,
|
||||
filename: string
|
||||
filedata: Uint8Array;
|
||||
thumbnail: Uint8Array;
|
||||
filename: string;
|
||||
}
|
||||
|
||||
|
||||
interface encryptedFile {
|
||||
filedata: fileAttribute;
|
||||
thumbnail: fileAttribute;
|
||||
|
@ -40,47 +38,51 @@ interface encryptedFile {
|
|||
}
|
||||
|
||||
interface objectKey {
|
||||
objectKey: string,
|
||||
decryptionHeader: string
|
||||
objectKey: string;
|
||||
decryptionHeader: string;
|
||||
}
|
||||
interface objectKeys {
|
||||
file: objectKey
|
||||
thumbnail: objectKey
|
||||
file: objectKey;
|
||||
thumbnail: objectKey;
|
||||
}
|
||||
|
||||
interface uploadFile extends objectKeys {
|
||||
collectionID: number,
|
||||
collectionID: number;
|
||||
encryptedKey: string;
|
||||
keyDecryptionNonce: string;
|
||||
metadata?: {
|
||||
encryptedData: string | Uint8Array,
|
||||
decryptionHeader: string
|
||||
}
|
||||
encryptedData: string | Uint8Array;
|
||||
decryptionHeader: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface UploadFileWithoutMetaData {
|
||||
tempUploadFile: uploadFile,
|
||||
encryptedFileKey: keyEncryptionResult,
|
||||
fileName: string
|
||||
tempUploadFile: uploadFile;
|
||||
encryptedFileKey: keyEncryptionResult;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
export enum UPLOAD_STAGES {
|
||||
START,
|
||||
ENCRYPTION,
|
||||
UPLOAD,
|
||||
FINISH
|
||||
FINISH,
|
||||
}
|
||||
|
||||
class UploadService {
|
||||
|
||||
private uploadURLs: uploadURL[];
|
||||
private uploadURLFetchInProgress: Promise<any>;
|
||||
private perStepProgress: number
|
||||
private stepsCompleted: number
|
||||
private totalFilesCount: number
|
||||
private perStepProgress: number;
|
||||
private stepsCompleted: number;
|
||||
private totalFilesCount: number;
|
||||
private metadataMap: Map<string, Object>;
|
||||
|
||||
public async uploadFiles(recievedFiles: File[], collectionAndItsLatestFile: CollectionAndItsLatestFile, token: string, progressBarProps) {
|
||||
public async uploadFiles(
|
||||
recievedFiles: File[],
|
||||
collectionAndItsLatestFile: CollectionAndItsLatestFile,
|
||||
token: string,
|
||||
progressBarProps
|
||||
) {
|
||||
try {
|
||||
const worker = await new CryptoWorker();
|
||||
this.stepsCompleted = 0;
|
||||
|
@ -90,11 +92,16 @@ class UploadService {
|
|||
|
||||
let metadataFiles: File[] = [];
|
||||
let actualFiles: File[] = [];
|
||||
recievedFiles.forEach(file => {
|
||||
if (file.type.substr(0, 5) === "image" || file.type.substr(0, 5) === "video")
|
||||
recievedFiles.forEach((file) => {
|
||||
if (
|
||||
file.type.substr(0, 5) === 'image' ||
|
||||
file.type.substr(0, 5) === 'video'
|
||||
) {
|
||||
actualFiles.push(file);
|
||||
if (file.name.slice(-4) == "json")
|
||||
}
|
||||
if (file.name.slice(-4) == 'json') {
|
||||
metadataFiles.push(file);
|
||||
}
|
||||
});
|
||||
this.totalFilesCount = actualFiles.length;
|
||||
this.perStepProgress = 100 / (3 * actualFiles.length);
|
||||
|
@ -107,38 +114,58 @@ class UploadService {
|
|||
while (actualFiles.length > 0) {
|
||||
var promises = [];
|
||||
for (var i = 0; i < 5 && actualFiles.length > 0; i++)
|
||||
promises.push(this.uploadHelper(progressBarProps, actualFiles.pop(), collectionAndItsLatestFile.collection, token));
|
||||
uploadFilesWithoutMetaData.push(...await Promise.all(promises));
|
||||
promises.push(
|
||||
this.uploadHelper(
|
||||
progressBarProps,
|
||||
actualFiles.pop(),
|
||||
collectionAndItsLatestFile.collection,
|
||||
token
|
||||
)
|
||||
);
|
||||
uploadFilesWithoutMetaData.push(
|
||||
...(await Promise.all(promises))
|
||||
);
|
||||
}
|
||||
|
||||
for await (const rawFile of metadataFiles) {
|
||||
await this.updateMetadata(rawFile)
|
||||
};
|
||||
await this.updateMetadata(rawFile);
|
||||
}
|
||||
|
||||
progressBarProps.setUploadStage(UPLOAD_STAGES.ENCRYPTION);
|
||||
const completeUploadFiles: uploadFile[] = await Promise.all(uploadFilesWithoutMetaData.map(async (file: UploadFileWithoutMetaData) => {
|
||||
const { file: encryptedMetaData } = await this.encryptMetadata(worker, file.fileName, file.encryptedFileKey);
|
||||
const completeUploadFile = {
|
||||
...file.tempUploadFile,
|
||||
metadata: {
|
||||
encryptedData: encryptedMetaData.encryptedData,
|
||||
decryptionHeader: encryptedMetaData.decryptionHeader
|
||||
const completeUploadFiles: uploadFile[] = await Promise.all(
|
||||
uploadFilesWithoutMetaData.map(
|
||||
async (file: UploadFileWithoutMetaData) => {
|
||||
const {
|
||||
file: encryptedMetaData,
|
||||
} = await this.encryptMetadata(
|
||||
worker,
|
||||
file.fileName,
|
||||
file.encryptedFileKey
|
||||
);
|
||||
const completeUploadFile = {
|
||||
...file.tempUploadFile,
|
||||
metadata: {
|
||||
encryptedData: encryptedMetaData.encryptedData,
|
||||
decryptionHeader:
|
||||
encryptedMetaData.decryptionHeader,
|
||||
},
|
||||
};
|
||||
this.changeProgressBarProps(progressBarProps);
|
||||
return completeUploadFile;
|
||||
}
|
||||
}
|
||||
this.changeProgressBarProps(progressBarProps);
|
||||
return completeUploadFile;
|
||||
}));
|
||||
)
|
||||
);
|
||||
|
||||
progressBarProps.setUploadStage(UPLOAD_STAGES.UPLOAD);
|
||||
await Promise.all(completeUploadFiles.map(async (uploadFile: uploadFile) => {
|
||||
|
||||
await this.uploadFile(uploadFile, token);
|
||||
this.changeProgressBarProps(progressBarProps);
|
||||
}));
|
||||
await Promise.all(
|
||||
completeUploadFiles.map(async (uploadFile: uploadFile) => {
|
||||
await this.uploadFile(uploadFile, token);
|
||||
this.changeProgressBarProps(progressBarProps);
|
||||
})
|
||||
);
|
||||
|
||||
progressBarProps.setUploadStage(UPLOAD_STAGES.FINISH);
|
||||
progressBarProps.setPercentComplete(100);
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
throw e;
|
||||
|
@ -148,18 +175,29 @@ class UploadService {
|
|||
try {
|
||||
const worker = await new CryptoWorker();
|
||||
let file: FileinMemory = await this.readFile(rawFile);
|
||||
let encryptedFile: encryptedFile = await this.encryptFile(worker, file, collection.key);
|
||||
let objectKeys = await this.uploadtoBucket(encryptedFile, token, 2 * this.totalFilesCount);
|
||||
let uploadFileWithoutMetaData: uploadFile = this.getuploadFile(collection, encryptedFile.fileKey, objectKeys);
|
||||
let encryptedFile: encryptedFile = await this.encryptFile(
|
||||
worker,
|
||||
file,
|
||||
collection.key
|
||||
);
|
||||
let objectKeys = await this.uploadtoBucket(
|
||||
encryptedFile,
|
||||
token,
|
||||
2 * this.totalFilesCount
|
||||
);
|
||||
let uploadFileWithoutMetaData: uploadFile = this.getuploadFile(
|
||||
collection,
|
||||
encryptedFile.fileKey,
|
||||
objectKeys
|
||||
);
|
||||
this.changeProgressBarProps(progressBarProps);
|
||||
|
||||
return {
|
||||
tempUploadFile: uploadFileWithoutMetaData,
|
||||
encryptedFileKey: encryptedFile.fileKey,
|
||||
fileName: file.filename
|
||||
fileName: file.filename,
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
throw e;
|
||||
}
|
||||
|
@ -174,24 +212,28 @@ class UploadService {
|
|||
|
||||
private async readFile(recievedFile: File) {
|
||||
try {
|
||||
const filedata: Uint8Array = await this.getUint8ArrayView(recievedFile);
|
||||
const filedata: Uint8Array = await this.getUint8ArrayView(
|
||||
recievedFile
|
||||
);
|
||||
let fileType;
|
||||
switch (recievedFile.type.split('/')[0]) {
|
||||
case "image":
|
||||
case 'image':
|
||||
fileType = FILE_TYPE.IMAGE;
|
||||
break;
|
||||
case "video":
|
||||
case 'video':
|
||||
fileType = FILE_TYPE.VIDEO;
|
||||
break;
|
||||
default:
|
||||
fileType = FILE_TYPE.OTHERS;
|
||||
}
|
||||
|
||||
const { location, creationTime } = await this.getExifData(recievedFile);
|
||||
const { location, creationTime } = await this.getExifData(
|
||||
recievedFile
|
||||
);
|
||||
this.metadataMap.set(recievedFile.name, {
|
||||
title: recievedFile.name,
|
||||
creationTime: creationTime || (recievedFile.lastModified) * 1000,
|
||||
modificationTime: (recievedFile.lastModified) * 1000,
|
||||
creationTime: creationTime || recievedFile.lastModified * 1000,
|
||||
modificationTime: recievedFile.lastModified * 1000,
|
||||
latitude: location?.latitude,
|
||||
longitude: location?.latitude,
|
||||
fileType,
|
||||
|
@ -199,102 +241,167 @@ class UploadService {
|
|||
return {
|
||||
filedata,
|
||||
filename: recievedFile.name,
|
||||
thumbnail: await this.generateThumbnail(recievedFile)
|
||||
}
|
||||
thumbnail: await this.generateThumbnail(recievedFile),
|
||||
};
|
||||
} catch (e) {
|
||||
console.log("error reading files " + e);
|
||||
console.log('error reading files ' + e);
|
||||
}
|
||||
}
|
||||
private async encryptFile(worker, file: FileinMemory, encryptionKey: string): Promise<encryptedFile> {
|
||||
private async encryptFile(
|
||||
worker,
|
||||
file: FileinMemory,
|
||||
encryptionKey: string
|
||||
): Promise<encryptedFile> {
|
||||
try {
|
||||
const {
|
||||
key: fileKey,
|
||||
file: encryptedFiledata,
|
||||
}: encryptionResult = await worker.encryptFile(file.filedata);
|
||||
|
||||
const { key: fileKey, file: encryptedFiledata }: encryptionResult = await worker.encryptFile(file.filedata);
|
||||
|
||||
const { file: encryptedThumbnail }: encryptionResult = await worker.encryptThumbnail(file.thumbnail, fileKey);
|
||||
|
||||
const encryptedKey: keyEncryptionResult = await worker.encryptToB64(fileKey, encryptionKey);
|
||||
const {
|
||||
file: encryptedThumbnail,
|
||||
}: encryptionResult = await worker.encryptThumbnail(
|
||||
file.thumbnail,
|
||||
fileKey
|
||||
);
|
||||
|
||||
const encryptedKey: keyEncryptionResult = await worker.encryptToB64(
|
||||
fileKey,
|
||||
encryptionKey
|
||||
);
|
||||
|
||||
const result: encryptedFile = {
|
||||
filedata: encryptedFiledata,
|
||||
thumbnail: encryptedThumbnail,
|
||||
fileKey: encryptedKey
|
||||
fileKey: encryptedKey,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Error encrypting files " + e);
|
||||
} catch (e) {
|
||||
console.log('Error encrypting files ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
private async encryptMetadata(worker: any, fileName: string, encryptedFileKey: keyEncryptionResult) {
|
||||
private async encryptMetadata(
|
||||
worker: any,
|
||||
fileName: string,
|
||||
encryptedFileKey: keyEncryptionResult
|
||||
) {
|
||||
const metaData = this.metadataMap.get(fileName);
|
||||
const fileKey = await worker.decryptB64(encryptedFileKey.encryptedData, encryptedFileKey.nonce, encryptedFileKey.key);
|
||||
const encryptedMetaData = await worker.encryptMetadata(metaData, fileKey);
|
||||
const fileKey = await worker.decryptB64(
|
||||
encryptedFileKey.encryptedData,
|
||||
encryptedFileKey.nonce,
|
||||
encryptedFileKey.key
|
||||
);
|
||||
const encryptedMetaData = await worker.encryptMetadata(
|
||||
metaData,
|
||||
fileKey
|
||||
);
|
||||
return encryptedMetaData;
|
||||
}
|
||||
|
||||
private async uploadtoBucket(file: encryptedFile, token, count: number): Promise<objectKeys> {
|
||||
private async uploadtoBucket(
|
||||
file: encryptedFile,
|
||||
token,
|
||||
count: number
|
||||
): Promise<objectKeys> {
|
||||
try {
|
||||
const fileUploadURL = await this.getUploadURL(token, count);
|
||||
const fileObjectKey = await this.putFile(fileUploadURL, file.filedata.encryptedData)
|
||||
const fileObjectKey = await this.putFile(
|
||||
fileUploadURL,
|
||||
file.filedata.encryptedData
|
||||
);
|
||||
|
||||
const thumbnailUploadURL = await this.getUploadURL(token, count);
|
||||
const thumbnailObjectKey = await this.putFile(thumbnailUploadURL, file.thumbnail.encryptedData)
|
||||
const thumbnailObjectKey = await this.putFile(
|
||||
thumbnailUploadURL,
|
||||
file.thumbnail.encryptedData
|
||||
);
|
||||
|
||||
return {
|
||||
file: { objectKey: fileObjectKey, decryptionHeader: file.filedata.decryptionHeader },
|
||||
thumbnail: { objectKey: thumbnailObjectKey, decryptionHeader: file.thumbnail.decryptionHeader }
|
||||
file: {
|
||||
objectKey: fileObjectKey,
|
||||
decryptionHeader: file.filedata.decryptionHeader,
|
||||
},
|
||||
thumbnail: {
|
||||
objectKey: thumbnailObjectKey,
|
||||
decryptionHeader: file.thumbnail.decryptionHeader,
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
console.log("error uploading to bucket " + e);
|
||||
console.log('error uploading to bucket ' + e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private getuploadFile(collection: collection, encryptedKey: keyEncryptionResult, objectKeys: objectKeys): uploadFile {
|
||||
private getuploadFile(
|
||||
collection: collection,
|
||||
encryptedKey: keyEncryptionResult,
|
||||
objectKeys: objectKeys
|
||||
): uploadFile {
|
||||
const uploadFile: uploadFile = {
|
||||
collectionID: collection.id,
|
||||
encryptedKey: encryptedKey.encryptedData,
|
||||
keyDecryptionNonce: encryptedKey.nonce,
|
||||
...objectKeys
|
||||
}
|
||||
...objectKeys,
|
||||
};
|
||||
return uploadFile;
|
||||
}
|
||||
|
||||
private async uploadFile(uploadFile: uploadFile, token) {
|
||||
try {
|
||||
const response = await HTTPService.post(`${ENDPOINT}/files`, uploadFile, null, { 'X-Auth-Token': token });
|
||||
const response = await HTTPService.post(
|
||||
`${ENDPOINT}/files`,
|
||||
uploadFile,
|
||||
null,
|
||||
{ 'X-Auth-Token': token }
|
||||
);
|
||||
|
||||
return response.data;
|
||||
} catch (e) {
|
||||
console.log("upload Files Failed " + e);
|
||||
console.log('upload Files Failed ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
private async updateMetadata(recievedFile: File) {
|
||||
try {
|
||||
const metadataJSON: object = await new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => {
|
||||
var result = typeof reader.result !== "string" ? new TextDecoder().decode(reader.result) : reader.result
|
||||
resolve(JSON.parse(result));
|
||||
const metadataJSON: object = await new Promise(
|
||||
(resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
var result =
|
||||
typeof reader.result !== 'string'
|
||||
? new TextDecoder().decode(reader.result)
|
||||
: reader.result;
|
||||
resolve(JSON.parse(result));
|
||||
};
|
||||
reader.readAsText(recievedFile);
|
||||
}
|
||||
reader.readAsText(recievedFile)
|
||||
});
|
||||
if (!this.metadataMap.has(metadataJSON['title']))
|
||||
);
|
||||
if (!this.metadataMap.has(metadataJSON['title'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
const metaDataObject = this.metadataMap.get(metadataJSON['title']);
|
||||
metaDataObject['creationTime'] = metadataJSON['photoTakenTime']['timestamp'] * 1000000;
|
||||
metaDataObject['modificationTime'] = metadataJSON['modificationTime']['timestamp'] * 1000000;
|
||||
metaDataObject['creationTime'] =
|
||||
metadataJSON['photoTakenTime']['timestamp'] * 1000000;
|
||||
metaDataObject['modificationTime'] =
|
||||
metadataJSON['modificationTime']['timestamp'] * 1000000;
|
||||
|
||||
if (metaDataObject['latitude'] == null || (metaDataObject['latitude'] == 0.0 && metaDataObject['longitude'] == 0.0)) {
|
||||
if (
|
||||
metaDataObject['latitude'] == null ||
|
||||
(metaDataObject['latitude'] == 0.0 &&
|
||||
metaDataObject['longitude'] == 0.0)
|
||||
) {
|
||||
var locationData = null;
|
||||
if (metadataJSON['geoData']['latitude'] != 0.0 || metadataJSON['geoData']['longitude'] != 0.0) {
|
||||
if (
|
||||
metadataJSON['geoData']['latitude'] != 0.0 ||
|
||||
metadataJSON['geoData']['longitude'] != 0.0
|
||||
) {
|
||||
locationData = metadataJSON['geoData'];
|
||||
}
|
||||
else if (metadataJSON['geoDataExif']['latitude'] != 0.0 || metadataJSON['geoDataExif']['longitude'] != 0.0) {
|
||||
} else if (
|
||||
metadataJSON['geoDataExif']['latitude'] != 0.0 ||
|
||||
metadataJSON['geoDataExif']['longitude'] != 0.0
|
||||
) {
|
||||
locationData = metadataJSON['geoDataExif'];
|
||||
}
|
||||
if (locationData != null) {
|
||||
|
@ -303,31 +410,34 @@ class UploadService {
|
|||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("error reading metaData Files " + e);
|
||||
|
||||
console.log('error reading metaData Files ' + e);
|
||||
}
|
||||
}
|
||||
private async generateThumbnail(file: File): Promise<Uint8Array> {
|
||||
try {
|
||||
let canvas = document.createElement("canvas");
|
||||
let canvas_CTX = canvas.getContext("2d");
|
||||
let canvas = document.createElement('canvas');
|
||||
let canvas_CTX = canvas.getContext('2d');
|
||||
let imageURL = null;
|
||||
if (file.type.match("image")) {
|
||||
if (file.type.match('image')) {
|
||||
let image = new Image();
|
||||
imageURL = URL.createObjectURL(file);
|
||||
image.setAttribute("src", imageURL);
|
||||
image.setAttribute('src', imageURL);
|
||||
await new Promise((resolve) => {
|
||||
image.onload = () => {
|
||||
canvas.width = image.width;
|
||||
canvas.height = image.height;
|
||||
canvas_CTX.drawImage(image, 0, 0, image.width, image.height);
|
||||
canvas_CTX.drawImage(
|
||||
image,
|
||||
0,
|
||||
0,
|
||||
image.width,
|
||||
image.height
|
||||
);
|
||||
image = undefined;
|
||||
resolve(null);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
await new Promise(async (resolve) => {
|
||||
let video = document.createElement('video');
|
||||
imageURL = URL.createObjectURL(file);
|
||||
|
@ -347,7 +457,13 @@ class UploadService {
|
|||
var snapImage = function () {
|
||||
canvas.width = video.videoWidth;
|
||||
canvas.height = video.videoHeight;
|
||||
canvas_CTX.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||
canvas_CTX.drawImage(
|
||||
video,
|
||||
0,
|
||||
0,
|
||||
canvas.width,
|
||||
canvas.height
|
||||
);
|
||||
var image = canvas.toDataURL();
|
||||
var success = image.length > 100000;
|
||||
return success;
|
||||
|
@ -362,34 +478,39 @@ class UploadService {
|
|||
});
|
||||
}
|
||||
URL.revokeObjectURL(imageURL);
|
||||
var thumbnailBlob = await new Promise(resolve => {
|
||||
var thumbnailBlob = await new Promise((resolve) => {
|
||||
canvas.toBlob(function (blob) {
|
||||
resolve(blob);
|
||||
}), 'image/jpeg', 0.4
|
||||
}),
|
||||
'image/jpeg',
|
||||
0.4;
|
||||
});
|
||||
const thumbnail = this.getUint8ArrayView(thumbnailBlob);
|
||||
return thumbnail;
|
||||
} catch (e) {
|
||||
console.log("Error generatin thumbnail " + e);
|
||||
console.log('Error generatin thumbnail ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
private async getUint8ArrayView(file): Promise<Uint8Array> {
|
||||
try {
|
||||
return await new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onabort = () => reject('file reading was aborted')
|
||||
reader.onerror = () => reject('file reading has failed')
|
||||
reader.onabort = () => reject('file reading was aborted');
|
||||
reader.onerror = () => reject('file reading has failed');
|
||||
reader.onload = () => {
|
||||
// Do whatever you want with the file contents
|
||||
const result = typeof reader.result === "string" ? new TextEncoder().encode(reader.result) : new Uint8Array(reader.result);
|
||||
const result =
|
||||
typeof reader.result === 'string'
|
||||
? new TextEncoder().encode(reader.result)
|
||||
: new Uint8Array(reader.result);
|
||||
resolve(result);
|
||||
}
|
||||
reader.readAsArrayBuffer(file)
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("error readinf file to bytearray " + e);
|
||||
console.log('error readinf file to bytearray ' + e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -404,26 +525,34 @@ class UploadService {
|
|||
private async fetchUploadURLs(token: string, count: number): Promise<void> {
|
||||
try {
|
||||
if (!this.uploadURLFetchInProgress) {
|
||||
this.uploadURLFetchInProgress = HTTPService.get(`${ENDPOINT}/files/upload-urls`,
|
||||
this.uploadURLFetchInProgress = HTTPService.get(
|
||||
`${ENDPOINT}/files/upload-urls`,
|
||||
{
|
||||
count: Math.min(50, count).toString() //m4gic number
|
||||
}, { 'X-Auth-Token': token })
|
||||
count: Math.min(50, count).toString(), //m4gic number
|
||||
},
|
||||
{ 'X-Auth-Token': token }
|
||||
);
|
||||
const response = await this.uploadURLFetchInProgress;
|
||||
|
||||
this.uploadURLFetchInProgress = null;
|
||||
this.uploadURLs.push(...response.data["urls"]);
|
||||
this.uploadURLs.push(...response.data['urls']);
|
||||
}
|
||||
return this.uploadURLFetchInProgress;
|
||||
} catch (e) {
|
||||
console.log("fetch upload-url failed " + e);
|
||||
console.log('fetch upload-url failed ' + e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private async putFile(fileUploadURL: uploadURL, file: Uint8Array | string): Promise<string> {
|
||||
private async putFile(
|
||||
fileUploadURL: uploadURL,
|
||||
file: Uint8Array | string
|
||||
): Promise<string> {
|
||||
try {
|
||||
const fileSize = file.length.toString();
|
||||
await HTTPService.put(fileUploadURL.url, file, null, { contentLengthHeader: fileSize })
|
||||
await HTTPService.put(fileUploadURL.url, file, null, {
|
||||
contentLengthHeader: fileSize,
|
||||
});
|
||||
return fileUploadURL.objectKey;
|
||||
} catch (e) {
|
||||
console.log('putFile to dataStore failed ' + e);
|
||||
|
@ -434,41 +563,52 @@ class UploadService {
|
|||
private async getExifData(recievedFile) {
|
||||
try {
|
||||
const exifData: any = await new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
resolve(EXIF.readFromBinaryFile(reader.result));
|
||||
}
|
||||
reader.readAsArrayBuffer(recievedFile)
|
||||
};
|
||||
reader.readAsArrayBuffer(recievedFile);
|
||||
});
|
||||
if (!exifData)
|
||||
if (!exifData) {
|
||||
return { location: null, creationTime: null };
|
||||
}
|
||||
return {
|
||||
location: this.getLocation(exifData),
|
||||
creationTime: this.getUNIXTime(exifData)
|
||||
creationTime: this.getUNIXTime(exifData),
|
||||
};
|
||||
} catch (e) {
|
||||
console.log("error reading exif data");
|
||||
console.log('error reading exif data');
|
||||
}
|
||||
}
|
||||
private getUNIXTime(exifData: any) {
|
||||
if (!exifData.DateTimeOriginal)
|
||||
if (!exifData.DateTimeOriginal) {
|
||||
return null;
|
||||
}
|
||||
let dateString: string = exifData.DateTimeOriginal;
|
||||
var parts = dateString.split(' ')[0].split(":");
|
||||
var date = new Date(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]));
|
||||
var parts = dateString.split(' ')[0].split(':');
|
||||
var date = new Date(
|
||||
Number(parts[0]),
|
||||
Number(parts[1]) - 1,
|
||||
Number(parts[2])
|
||||
);
|
||||
return date.getTime() * 1000;
|
||||
}
|
||||
|
||||
private getLocation(exifData) {
|
||||
|
||||
if (!exifData.GPSLatitude)
|
||||
if (!exifData.GPSLatitude) {
|
||||
return null;
|
||||
}
|
||||
var latDegree = exifData.GPSLatitude[0].numerator;
|
||||
var latMinute = exifData.GPSLatitude[1].numerator;
|
||||
var latSecond = exifData.GPSLatitude[2].numerator;
|
||||
var latDirection = exifData.GPSLatitudeRef;
|
||||
|
||||
var latFinal = this.convertDMSToDD(latDegree, latMinute, latSecond, latDirection);
|
||||
var latFinal = this.convertDMSToDD(
|
||||
latDegree,
|
||||
latMinute,
|
||||
latSecond,
|
||||
latDirection
|
||||
);
|
||||
|
||||
// Calculate longitude decimal
|
||||
var lonDegree = exifData.GPSLongitude[0].numerator;
|
||||
|
@ -476,16 +616,20 @@ class UploadService {
|
|||
var lonSecond = exifData.GPSLongitude[2].numerator;
|
||||
var lonDirection = exifData.GPSLongitudeRef;
|
||||
|
||||
var lonFinal = this.convertDMSToDD(lonDegree, lonMinute, lonSecond, lonDirection);
|
||||
var lonFinal = this.convertDMSToDD(
|
||||
lonDegree,
|
||||
lonMinute,
|
||||
lonSecond,
|
||||
lonDirection
|
||||
);
|
||||
|
||||
return { latitude: latFinal * 1.0, longitude: lonFinal * 1.0 };
|
||||
}
|
||||
|
||||
private convertDMSToDD(degrees, minutes, seconds, direction) {
|
||||
var dd = degrees + minutes / 60 + seconds / 3600;
|
||||
|
||||
var dd = degrees + (minutes / 60) + (seconds / 3600);
|
||||
|
||||
if (direction == "S" || direction == "W") {
|
||||
if (direction == 'S' || direction == 'W') {
|
||||
dd = dd * -1;
|
||||
}
|
||||
|
||||
|
@ -494,4 +638,3 @@ class UploadService {
|
|||
}
|
||||
|
||||
export default new UploadService();
|
||||
|
||||
|
|
|
@ -5,16 +5,28 @@ import { getEndpoint } from 'utils/common/apiUtil';
|
|||
const ENDPOINT = getEndpoint();
|
||||
|
||||
export const getOtt = (email: string) => {
|
||||
return HTTPService.get(`${ENDPOINT}/users/ott`, { 'email': email, 'client': 'web' })
|
||||
}
|
||||
return HTTPService.get(`${ENDPOINT}/users/ott`, {
|
||||
email: email,
|
||||
client: 'web',
|
||||
});
|
||||
};
|
||||
|
||||
export const verifyOtt = (email: string, ott: string) => {
|
||||
return HTTPService.get(`${ENDPOINT}/users/credentials`, { email, ott });
|
||||
}
|
||||
};
|
||||
|
||||
export const putAttributes = (token: string, name: string, keyAttributes: keyAttributes) => {
|
||||
console.log("name " + name);
|
||||
return HTTPService.put(`${ENDPOINT}/users/attributes`, { 'name': name, 'keyAttributes': keyAttributes }, null, {
|
||||
'X-Auth-Token': token,
|
||||
});
|
||||
}
|
||||
export const putAttributes = (
|
||||
token: string,
|
||||
name: string,
|
||||
keyAttributes: keyAttributes
|
||||
) => {
|
||||
console.log('name ' + name);
|
||||
return HTTPService.put(
|
||||
`${ENDPOINT}/users/attributes`,
|
||||
{ name: name, keyAttributes: keyAttributes },
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,4 +3,4 @@ export interface keyAttributes {
|
|||
kekHash: string;
|
||||
encryptedKey: string;
|
||||
keyDecryptionNonce: string;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export const getEndpoint = () => {
|
||||
const endPoint = process.env.NEXT_PUBLIC_ENTE_ENDPOINT ?? "https://api.ente.io";
|
||||
const endPoint =
|
||||
process.env.NEXT_PUBLIC_ENTE_ENDPOINT ?? 'https://api.ente.io';
|
||||
return endPoint;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,20 +1,26 @@
|
|||
import { getData, LS_KEYS } from "utils/storage/localStorage";
|
||||
import { getKey, SESSION_KEYS } from "utils/storage/sessionStorage";
|
||||
import * as Comlink from "comlink";
|
||||
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||
import { getKey, SESSION_KEYS } from 'utils/storage/sessionStorage';
|
||||
import * as Comlink from 'comlink';
|
||||
|
||||
const CryptoWorker: any = typeof window !== 'undefined'
|
||||
&& Comlink.wrap(new Worker("worker/crypto.worker.js", { type: 'module' }));
|
||||
const CryptoWorker: any =
|
||||
typeof window !== 'undefined' &&
|
||||
Comlink.wrap(new Worker('worker/crypto.worker.js', { type: 'module' }));
|
||||
|
||||
export const getActualKey = async () => {
|
||||
const session = getData(LS_KEYS.SESSION);
|
||||
if (session == null)
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
const cryptoWorker = await new CryptoWorker();
|
||||
const encryptedKey = getKey(SESSION_KEYS.ENCRYPTION_KEY)?.encryptionKey;
|
||||
const key: string = await cryptoWorker.decryptB64(encryptedKey, session.sessionNonce, session.sessionKey);
|
||||
const key: string = await cryptoWorker.decryptB64(
|
||||
encryptedKey,
|
||||
session.sessionNonce,
|
||||
session.sessionKey
|
||||
);
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
export const getToken = () => {
|
||||
return getData(LS_KEYS.USER)?.token;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,18 +2,37 @@ import sodium from 'libsodium-wrappers';
|
|||
|
||||
const encryptionChunkSize = 4 * 1024 * 1024;
|
||||
|
||||
export async function decryptChaChaOneShot(data: Uint8Array, header: Uint8Array, key: string) {
|
||||
export async function decryptChaChaOneShot(
|
||||
data: Uint8Array,
|
||||
header: Uint8Array,
|
||||
key: string
|
||||
) {
|
||||
await sodium.ready;
|
||||
const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(header, await fromB64(key));
|
||||
const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(pullState, data, null);
|
||||
const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(
|
||||
header,
|
||||
await fromB64(key)
|
||||
);
|
||||
const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(
|
||||
pullState,
|
||||
data,
|
||||
null
|
||||
);
|
||||
return pullResult.message;
|
||||
}
|
||||
|
||||
export async function decryptChaCha(data: Uint8Array, header: Uint8Array, key: string) {
|
||||
export async function decryptChaCha(
|
||||
data: Uint8Array,
|
||||
header: Uint8Array,
|
||||
key: string
|
||||
) {
|
||||
await sodium.ready;
|
||||
const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(header, await fromB64(key));
|
||||
const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(
|
||||
header,
|
||||
await fromB64(key)
|
||||
);
|
||||
const decryptionChunkSize =
|
||||
encryptionChunkSize + sodium.crypto_secretstream_xchacha20poly1305_ABYTES;
|
||||
encryptionChunkSize +
|
||||
sodium.crypto_secretstream_xchacha20poly1305_ABYTES;
|
||||
var bytesRead = 0;
|
||||
var decryptedData = [];
|
||||
var tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE;
|
||||
|
@ -23,7 +42,10 @@ export async function decryptChaCha(data: Uint8Array, header: Uint8Array, key: s
|
|||
chunkSize = data.length - bytesRead;
|
||||
}
|
||||
const buffer = data.slice(bytesRead, bytesRead + chunkSize);
|
||||
const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(pullState, buffer);
|
||||
const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(
|
||||
pullState,
|
||||
buffer
|
||||
);
|
||||
for (var index = 0; index < pullResult.message.length; index++) {
|
||||
decryptedData.push(pullResult.message[index]);
|
||||
}
|
||||
|
@ -36,27 +58,41 @@ export async function decryptChaCha(data: Uint8Array, header: Uint8Array, key: s
|
|||
export async function encryptChaChaOneShot(data: Uint8Array, key?: string) {
|
||||
await sodium.ready;
|
||||
|
||||
const uintkey: Uint8Array = key ? await fromB64(key) : sodium.crypto_secretstream_xchacha20poly1305_keygen();
|
||||
let initPushResult = sodium.crypto_secretstream_xchacha20poly1305_init_push(uintkey);
|
||||
const uintkey: Uint8Array = key
|
||||
? await fromB64(key)
|
||||
: sodium.crypto_secretstream_xchacha20poly1305_keygen();
|
||||
let initPushResult = sodium.crypto_secretstream_xchacha20poly1305_init_push(
|
||||
uintkey
|
||||
);
|
||||
let [pushState, header] = [initPushResult.state, initPushResult.header];
|
||||
|
||||
const pushResult = sodium.crypto_secretstream_xchacha20poly1305_push(pushState, data, null, sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL);
|
||||
const pushResult = sodium.crypto_secretstream_xchacha20poly1305_push(
|
||||
pushState,
|
||||
data,
|
||||
null,
|
||||
sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
|
||||
);
|
||||
return {
|
||||
key: await toB64(uintkey), file: {
|
||||
key: await toB64(uintkey),
|
||||
file: {
|
||||
encryptedData: pushResult,
|
||||
decryptionHeader: await toB64(header),
|
||||
creationTime: Date.now(),
|
||||
fileType: 0
|
||||
}
|
||||
}
|
||||
fileType: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function encryptChaCha(data: Uint8Array, key?: string) {
|
||||
await sodium.ready;
|
||||
|
||||
const uintkey: Uint8Array = key ? await fromB64(key) : sodium.crypto_secretstream_xchacha20poly1305_keygen();
|
||||
const uintkey: Uint8Array = key
|
||||
? await fromB64(key)
|
||||
: sodium.crypto_secretstream_xchacha20poly1305_keygen();
|
||||
|
||||
let initPushResult = sodium.crypto_secretstream_xchacha20poly1305_init_push(uintkey);
|
||||
let initPushResult = sodium.crypto_secretstream_xchacha20poly1305_init_push(
|
||||
uintkey
|
||||
);
|
||||
let [pushState, header] = [initPushResult.state, initPushResult.header];
|
||||
let bytesRead = 0;
|
||||
let tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE;
|
||||
|
@ -72,46 +108,59 @@ export async function encryptChaCha(data: Uint8Array, key?: string) {
|
|||
|
||||
const buffer = data.slice(bytesRead, bytesRead + chunkSize);
|
||||
bytesRead += chunkSize;
|
||||
const pushResult = sodium.crypto_secretstream_xchacha20poly1305_push(pushState, buffer, null, tag);
|
||||
const pushResult = sodium.crypto_secretstream_xchacha20poly1305_push(
|
||||
pushState,
|
||||
buffer,
|
||||
null,
|
||||
tag
|
||||
);
|
||||
for (var index = 0; index < pushResult.length; index++) {
|
||||
encryptedData.push(pushResult[index]);
|
||||
}
|
||||
}
|
||||
return {
|
||||
key: await toB64(uintkey), file: {
|
||||
key: await toB64(uintkey),
|
||||
file: {
|
||||
encryptedData: new Uint8Array(encryptedData),
|
||||
decryptionHeader: await toB64(header),
|
||||
creationTime: Date.now(),
|
||||
fileType: 0
|
||||
}
|
||||
}
|
||||
fileType: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function encryptToB64(data: string, key?: string) {
|
||||
await sodium.ready;
|
||||
const encrypted = await encrypt(await fromB64(data), (key ? await fromB64(key) : null));
|
||||
const encrypted = await encrypt(
|
||||
await fromB64(data),
|
||||
key ? await fromB64(key) : null
|
||||
);
|
||||
|
||||
return {
|
||||
encryptedData: await toB64(encrypted.encryptedData),
|
||||
key: await toB64(encrypted.key),
|
||||
nonce: await toB64(encrypted.nonce),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export async function decryptB64(data: string, nonce: string, key: string) {
|
||||
await sodium.ready;
|
||||
const decrypted = await decrypt(await fromB64(data),
|
||||
const decrypted = await decrypt(
|
||||
await fromB64(data),
|
||||
await fromB64(nonce),
|
||||
await fromB64(key));
|
||||
await fromB64(key)
|
||||
);
|
||||
|
||||
return await toB64(decrypted);
|
||||
}
|
||||
|
||||
export async function decryptString(data: string, nonce: string, key: string) {
|
||||
await sodium.ready;
|
||||
const decrypted = await decrypt(await fromB64(data),
|
||||
const decrypted = await decrypt(
|
||||
await fromB64(data),
|
||||
await fromB64(nonce),
|
||||
await fromB64(key));
|
||||
await fromB64(key)
|
||||
);
|
||||
|
||||
return sodium.to_string(decrypted);
|
||||
}
|
||||
|
@ -125,10 +174,14 @@ export async function encrypt(data: Uint8Array, key?: Uint8Array) {
|
|||
encryptedData: encryptedData,
|
||||
key: uintkey,
|
||||
nonce: nonce,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export async function decrypt(data: Uint8Array, nonce: Uint8Array, key: Uint8Array) {
|
||||
export async function decrypt(
|
||||
data: Uint8Array,
|
||||
nonce: Uint8Array,
|
||||
key: Uint8Array
|
||||
) {
|
||||
await sodium.ready;
|
||||
return sodium.crypto_secretbox_open_easy(data, nonce, key);
|
||||
}
|
||||
|
@ -143,20 +196,22 @@ export async function hash(input: string) {
|
|||
return sodium.crypto_pwhash_str(
|
||||
await fromB64(input),
|
||||
sodium.crypto_pwhash_OPSLIMIT_SENSITIVE,
|
||||
sodium.crypto_pwhash_MEMLIMIT_MODERATE,
|
||||
sodium.crypto_pwhash_MEMLIMIT_MODERATE
|
||||
);
|
||||
}
|
||||
|
||||
export async function deriveKey(passphrase: string, salt: string) {
|
||||
await sodium.ready;
|
||||
return await toB64(sodium.crypto_pwhash(
|
||||
sodium.crypto_secretbox_KEYBYTES,
|
||||
await fromString(passphrase),
|
||||
await fromB64(salt),
|
||||
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
||||
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
||||
sodium.crypto_pwhash_ALG_DEFAULT,
|
||||
));
|
||||
return await toB64(
|
||||
sodium.crypto_pwhash(
|
||||
sodium.crypto_secretbox_KEYBYTES,
|
||||
await fromString(passphrase),
|
||||
await fromB64(salt),
|
||||
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
||||
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
||||
sodium.crypto_pwhash_ALG_DEFAULT
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export async function generateMasterKey() {
|
||||
|
@ -172,12 +227,25 @@ export async function generateSaltToDeriveKey() {
|
|||
export async function generateKeyPair() {
|
||||
await sodium.ready;
|
||||
const keyPair: sodium.KeyPair = sodium.crypto_box_keypair();
|
||||
return { privateKey: await toB64(keyPair.privateKey), publicKey: await toB64(keyPair.publicKey) }
|
||||
return {
|
||||
privateKey: await toB64(keyPair.privateKey),
|
||||
publicKey: await toB64(keyPair.publicKey),
|
||||
};
|
||||
}
|
||||
|
||||
export async function boxSealOpen(input: string, publicKey: string, secretKey: string) {
|
||||
export async function boxSealOpen(
|
||||
input: string,
|
||||
publicKey: string,
|
||||
secretKey: string
|
||||
) {
|
||||
await sodium.ready;
|
||||
return await toB64(sodium.crypto_box_seal_open(await fromB64(input), await fromB64(publicKey), await fromB64(secretKey)));
|
||||
return await toB64(
|
||||
sodium.crypto_box_seal_open(
|
||||
await fromB64(input),
|
||||
await fromB64(publicKey),
|
||||
await fromB64(secretKey)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export async function fromB64(input: string) {
|
||||
|
@ -187,9 +255,7 @@ export async function fromB64(input: string) {
|
|||
result = sodium.from_base64(input, sodium.base64_variants.ORIGINAL);
|
||||
} catch (e) {
|
||||
result = await fromB64(await toB64(await fromString(input)));
|
||||
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -202,4 +268,4 @@ export async function toB64(input: Uint8Array) {
|
|||
export async function fromString(input: string) {
|
||||
await sodium.ready;
|
||||
return sodium.from_string(input);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export enum LS_KEYS {
|
||||
USER='user',
|
||||
SESSION='session',
|
||||
KEY_ATTRIBUTES='keyAttributes',
|
||||
USER = 'user',
|
||||
SESSION = 'session',
|
||||
KEY_ATTRIBUTES = 'keyAttributes',
|
||||
}
|
||||
|
||||
export const setData = (key: LS_KEYS, value: object) => {
|
||||
|
@ -9,18 +9,18 @@ export const setData = (key: LS_KEYS, value: object) => {
|
|||
return null;
|
||||
}
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
};
|
||||
|
||||
export const getData = (key: LS_KEYS) => {
|
||||
if (typeof localStorage === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
return JSON.parse(localStorage.getItem(key));
|
||||
}
|
||||
};
|
||||
|
||||
export const clearData = () => {
|
||||
if (typeof localStorage === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
localStorage.clear();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export enum SESSION_KEYS {
|
||||
ENCRYPTION_KEY='encryptionKey',
|
||||
ENCRYPTION_KEY = 'encryptionKey',
|
||||
}
|
||||
|
||||
export const setKey = (key: SESSION_KEYS, value: object) => {
|
||||
|
@ -7,18 +7,18 @@ export const setKey = (key: SESSION_KEYS, value: object) => {
|
|||
return null;
|
||||
}
|
||||
sessionStorage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
};
|
||||
|
||||
export const getKey = (key: SESSION_KEYS) => {
|
||||
if (typeof sessionStorage === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
return JSON.parse(sessionStorage.getItem(key));
|
||||
}
|
||||
};
|
||||
|
||||
export const clearKeys = () => {
|
||||
if (typeof sessionStorage === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
sessionStorage.clear();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { getConstantValue } from "./vernacularStrings";
|
||||
import { getConstantValue } from './vernacularStrings';
|
||||
|
||||
const constants = getConstantValue();
|
||||
export default constants;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { template } from "./vernacularStrings";
|
||||
import { template } from './vernacularStrings';
|
||||
|
||||
/**
|
||||
* Global English constants.
|
||||
|
@ -16,7 +16,11 @@ const englishConstants = {
|
|||
EMAIL_ERROR: 'Enter a valid email address',
|
||||
REQUIRED: 'Required',
|
||||
VERIFY_EMAIL: 'Verify Email',
|
||||
EMAIL_SENT: ({ email }) => (<p>We have sent a mail to <b>{email}</b>.</p>),
|
||||
EMAIL_SENT: ({ email }) => (
|
||||
<p>
|
||||
We have sent a mail to <b>{email}</b>.
|
||||
</p>
|
||||
),
|
||||
CHECK_INBOX: 'Please check your inbox (and spam) to complete verification.',
|
||||
ENTER_OTT: 'Enter verification code here',
|
||||
RESEND_MAIL: 'Did not get email?',
|
||||
|
@ -30,7 +34,8 @@ const englishConstants = {
|
|||
SET_PASSPHRASE: 'Set Passphrase',
|
||||
VERIFY_PASSPHRASE: 'Verify Passphrase',
|
||||
INCORRECT_PASSPHRASE: 'Incorrect Passphrase',
|
||||
ENTER_ENC_PASSPHRASE: 'Please enter a passphrase that we can use to encrypt your data.',
|
||||
ENTER_ENC_PASSPHRASE:
|
||||
'Please enter a passphrase that we can use to encrypt your data.',
|
||||
PASSPHRASE_DISCLAIMER: () => (
|
||||
<p>
|
||||
We don't store your passphrase, so if you forget,
|
||||
|
@ -46,14 +51,16 @@ const englishConstants = {
|
|||
CLOSE: 'Close',
|
||||
NOTHING_HERE: `nothing to see here! 👀`,
|
||||
UPLOAD: {
|
||||
0: "Preparing to upload",
|
||||
1: "Encryting your files",
|
||||
2: "Uploading your Files",
|
||||
3: "Files Uploaded Successfully !!!"
|
||||
0: 'Preparing to upload',
|
||||
1: 'Encryting your files',
|
||||
2: 'Uploading your Files',
|
||||
3: 'Files Uploaded Successfully !!!',
|
||||
},
|
||||
OF: 'of',
|
||||
SUBSCRIPTION_EXPIRED: 'You don\'t have a active subscription plan!! Please get one in the mobile app',
|
||||
STORAGE_QUOTA_EXCEEDED:'You have exceeded your designated storage Quota, please upgrade your plan to add more files',
|
||||
SUBSCRIPTION_EXPIRED:
|
||||
"You don't have a active subscription plan!! Please get one in the mobile app",
|
||||
STORAGE_QUOTA_EXCEEDED:
|
||||
'You have exceeded your designated storage Quota, please upgrade your plan to add more files',
|
||||
};
|
||||
|
||||
export default englishConstants;
|
||||
|
|
|
@ -2,8 +2,8 @@ import englishConstants from './englishConstants';
|
|||
|
||||
/** Enums of supported locale */
|
||||
export enum locale {
|
||||
en='en',
|
||||
hi='hi',
|
||||
en = 'en',
|
||||
hi = 'hi',
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,7 +16,7 @@ export enum locale {
|
|||
* @param keys
|
||||
*/
|
||||
export function template(strings: TemplateStringsArray, ...keys: string[]) {
|
||||
return ((...values: any[]) => {
|
||||
return (...values: any[]) => {
|
||||
const dict = values[values.length - 1] || {};
|
||||
const result = [strings[0]];
|
||||
keys.forEach((key, i) => {
|
||||
|
@ -24,16 +24,16 @@ export function template(strings: TemplateStringsArray, ...keys: string[]) {
|
|||
result.push(value, strings[i + 1]);
|
||||
});
|
||||
return result.join('');
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/** Type for vernacular string constants */
|
||||
export type VernacularConstants<T> = {
|
||||
[locale.en]: T,
|
||||
[locale.en]: T;
|
||||
[locale.hi]?: {
|
||||
[x in keyof T]?: string | ReturnType<typeof template>;
|
||||
},
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a valid locale from string and defaults
|
||||
|
@ -43,10 +43,10 @@ export type VernacularConstants<T> = {
|
|||
*/
|
||||
export const getLocale = (lang: string) => {
|
||||
switch (lang) {
|
||||
case locale.hi:
|
||||
return locale.hi;
|
||||
default:
|
||||
return locale.en;
|
||||
case locale.hi:
|
||||
return locale.hi;
|
||||
default:
|
||||
return locale.en;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -62,7 +62,8 @@ const globalConstants: VernacularConstants<typeof englishConstants> = {
|
|||
* @param localConstants
|
||||
*/
|
||||
export function getConstantValue<T>(localConstants?: VernacularConstants<T>) {
|
||||
const searchParam = typeof window !== 'undefined' ? window.location.search : '';
|
||||
const searchParam =
|
||||
typeof window !== 'undefined' ? window.location.search : '';
|
||||
const query = new URLSearchParams(searchParam);
|
||||
const currLocale = getLocale(query.get('lang'));
|
||||
|
||||
|
|
|
@ -6,44 +6,39 @@ export class Crypto {
|
|||
const encodedMetadata = await libsodium.decryptChaChaOneShot(
|
||||
await libsodium.fromB64(file.metadata.encryptedData),
|
||||
await libsodium.fromB64(file.metadata.decryptionHeader),
|
||||
file.key);
|
||||
file.key
|
||||
);
|
||||
return JSON.parse(new TextDecoder().decode(encodedMetadata));
|
||||
}
|
||||
|
||||
async decryptThumbnail(fileData, header, key) {
|
||||
return libsodium.decryptChaChaOneShot(
|
||||
fileData,
|
||||
header,
|
||||
key);
|
||||
return libsodium.decryptChaChaOneShot(fileData, header, key);
|
||||
}
|
||||
|
||||
async decryptFile(fileData, header, key) {
|
||||
return libsodium.decryptChaCha(
|
||||
fileData,
|
||||
header,
|
||||
key);
|
||||
return libsodium.decryptChaCha(fileData, header, key);
|
||||
}
|
||||
|
||||
async encryptMetadata(metadata, key) {
|
||||
const encodedMetadata = new TextEncoder().encode(JSON.stringify(metadata));
|
||||
const encodedMetadata = new TextEncoder().encode(
|
||||
JSON.stringify(metadata)
|
||||
);
|
||||
|
||||
const { file: encryptedMetadata } = await libsodium.encryptChaChaOneShot(
|
||||
encodedMetadata,
|
||||
key);
|
||||
const { encryptedData, ...other } = encryptedMetadata
|
||||
const {
|
||||
file: encryptedMetadata,
|
||||
} = await libsodium.encryptChaChaOneShot(encodedMetadata, key);
|
||||
const { encryptedData, ...other } = encryptedMetadata;
|
||||
return {
|
||||
file: {
|
||||
encryptedData: await libsodium.toB64(encryptedData),
|
||||
...other
|
||||
...other,
|
||||
},
|
||||
key
|
||||
key,
|
||||
};
|
||||
}
|
||||
|
||||
async encryptThumbnail(fileData, key) {
|
||||
return libsodium.encryptChaChaOneShot(
|
||||
fileData,
|
||||
key);
|
||||
return libsodium.encryptChaChaOneShot(fileData, key);
|
||||
}
|
||||
|
||||
async encryptFile(fileData, key) {
|
||||
|
@ -71,11 +66,11 @@ export class Crypto {
|
|||
}
|
||||
|
||||
async decryptB64(data, nonce, key) {
|
||||
return libsodium.decryptB64(data, nonce, key)
|
||||
return libsodium.decryptB64(data, nonce, key);
|
||||
}
|
||||
|
||||
async decryptString(data, nonce, key) {
|
||||
return libsodium.decryptString(data, nonce, key)
|
||||
return libsodium.decryptString(data, nonce, key);
|
||||
}
|
||||
|
||||
async encryptToB64(data, key) {
|
||||
|
@ -99,7 +94,7 @@ export class Crypto {
|
|||
}
|
||||
|
||||
async boxSealOpen(input, publicKey, secretKey) {
|
||||
return libsodium.boxSealOpen(input, publicKey, secretKey)
|
||||
return libsodium.boxSealOpen(input, publicKey, secretKey);
|
||||
}
|
||||
|
||||
async fromString(string) {
|
||||
|
|
Loading…
Add table
Reference in a new issue