mirror of
https://github.com/morten-olsen/parcel.git
synced 2026-02-08 01:36:24 +01:00
Updates
This commit is contained in:
@@ -5,8 +5,10 @@ import {
|
|||||||
Route,
|
Route,
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
|
|
||||||
|
import Intro from './screens/Intro';
|
||||||
import Encrypt from './screens/Encrypt';
|
import Encrypt from './screens/Encrypt';
|
||||||
import Decrypt from './screens/Decrypt';
|
import Decrypt from './screens/Decrypt';
|
||||||
|
import SetupKey from './screens/SetupKey';
|
||||||
import Welcome from './screens/Welcome';
|
import Welcome from './screens/Welcome';
|
||||||
import Debug from './screens/Debug';
|
import Debug from './screens/Debug';
|
||||||
|
|
||||||
@@ -19,12 +21,18 @@ const AppRouter: React.FC = () => (
|
|||||||
<Route path="/welcome">
|
<Route path="/welcome">
|
||||||
<Welcome />
|
<Welcome />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/decrypt">
|
<Route path="/key">
|
||||||
|
<SetupKey />
|
||||||
|
</Route>
|
||||||
|
<Route path="/receive">
|
||||||
<Decrypt />
|
<Decrypt />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/">
|
<Route path="/send">
|
||||||
<Encrypt />
|
<Encrypt />
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="/">
|
||||||
|
<Intro />
|
||||||
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -19,15 +19,22 @@ interface Props {
|
|||||||
file: FileType;
|
file: FileType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const iconStyle = {
|
||||||
|
style: {
|
||||||
|
fontSize: 18,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const icons: {[name: string]: any} = {
|
const icons: {[name: string]: any} = {
|
||||||
processing: <SyncOutlined spin />,
|
processing: <SyncOutlined spin {...iconStyle} />,
|
||||||
failed: <IssuesCloseOutlined />,
|
failed: <IssuesCloseOutlined {...iconStyle} />,
|
||||||
success: <LockOutlined />,
|
success: <LockOutlined {...iconStyle} />,
|
||||||
};
|
};
|
||||||
|
|
||||||
const IconText = ({ icon, text, ...props }) => (
|
const IconText = ({ icon, text, ...props }) => (
|
||||||
<Button
|
<Button
|
||||||
{...props}
|
{...props}
|
||||||
|
shape="round"
|
||||||
icon={React.createElement(icon)}
|
icon={React.createElement(icon)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { createFile } from '../helpers/files';
|
|||||||
|
|
||||||
interface DecryptionContextType {
|
interface DecryptionContextType {
|
||||||
publicKey: string | undefined;
|
publicKey: string | undefined;
|
||||||
|
createKey: (name: string, email: string) => void;
|
||||||
files: {[id: string]: FileType};
|
files: {[id: string]: FileType};
|
||||||
addFile: (file: File) => Promise<void>;
|
addFile: (file: File) => Promise<void>;
|
||||||
deleteFile: (id: string) => void;
|
deleteFile: (id: string) => void;
|
||||||
@@ -14,6 +15,7 @@ interface DecryptionContextType {
|
|||||||
const DecryptionContext = createContext<DecryptionContextType>({
|
const DecryptionContext = createContext<DecryptionContextType>({
|
||||||
publicKey: undefined,
|
publicKey: undefined,
|
||||||
files: {},
|
files: {},
|
||||||
|
createKey: async () => { throw new Error('Not using provider'); },
|
||||||
addFile: async () => { throw new Error('Not using provider'); },
|
addFile: async () => { throw new Error('Not using provider'); },
|
||||||
deleteFile: async () => { throw new Error('Not using provider'); },
|
deleteFile: async () => { throw new Error('Not using provider'); },
|
||||||
});
|
});
|
||||||
@@ -55,21 +57,23 @@ const DecryptionProvider: React.FC = ({
|
|||||||
setPrivateKey(currentRawKey);
|
setPrivateKey(currentRawKey);
|
||||||
const key = await openpgp.key.readArmored(currentRawKey);
|
const key = await openpgp.key.readArmored(currentRawKey);
|
||||||
setPublicKey(key.keys[0].toPublic().armor());
|
setPublicKey(key.keys[0].toPublic().armor());
|
||||||
} else {
|
|
||||||
const key = await openpgp.generateKey({
|
|
||||||
userIds: [{ name: 'unknown unknown', email: 'unknown@unknown.foo'}],
|
|
||||||
curve: 'ed25519',
|
|
||||||
});
|
|
||||||
|
|
||||||
setPrivateKey(key.privateKeyArmored);
|
|
||||||
setPublicKey(key.publicKeyArmored);
|
|
||||||
localStorage.setItem('key', key.privateKeyArmored);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
run();
|
run();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const createKey = async () => {
|
||||||
|
const key = await openpgp.generateKey({
|
||||||
|
userIds: [{ name: 'unknown unknown', email: 'unknown@unknown.foo'}],
|
||||||
|
curve: 'ed25519',
|
||||||
|
});
|
||||||
|
|
||||||
|
setPrivateKey(key.privateKeyArmored);
|
||||||
|
setPublicKey(key.publicKeyArmored);
|
||||||
|
localStorage.setItem('key', key.privateKeyArmored);
|
||||||
|
}
|
||||||
|
|
||||||
const addFile = useCallback(async (file: File) => {
|
const addFile = useCallback(async (file: File) => {
|
||||||
if (!keys || !privateKey) return;
|
if (!keys || !privateKey) return;
|
||||||
const addedFile = createFile(setFiles, file.name);
|
const addedFile = createFile(setFiles, file.name);
|
||||||
@@ -89,6 +93,7 @@ const DecryptionProvider: React.FC = ({
|
|||||||
<DecryptionContext.Provider
|
<DecryptionContext.Provider
|
||||||
value={{
|
value={{
|
||||||
publicKey,
|
publicKey,
|
||||||
|
createKey,
|
||||||
files,
|
files,
|
||||||
addFile,
|
addFile,
|
||||||
deleteFile,
|
deleteFile,
|
||||||
|
|||||||
@@ -1,32 +1,14 @@
|
|||||||
import React, { useContext, useEffect, useCallback } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { Divider, Button } from 'antd';
|
import { Divider, Button } from 'antd';
|
||||||
import { useHistory } from 'react-router';
|
|
||||||
import FileList from '../components/FileList';
|
import FileList from '../components/FileList';
|
||||||
import Add from '../components/decrypt/AddFile';
|
import Add from '../components/decrypt/AddFile';
|
||||||
import DecryptionContext from '../contexts/Decryption';
|
import DecryptionContext from '../contexts/Decryption';
|
||||||
import { downloadLink } from '../helpers/files';
|
|
||||||
|
|
||||||
const Decrypt: React.FC = () => {
|
const Decrypt: React.FC = () => {
|
||||||
const history = useHistory();
|
const { files, deleteFile } = useContext(DecryptionContext);
|
||||||
const { publicKey, files, deleteFile } = useContext(DecryptionContext);
|
|
||||||
useEffect(() => {
|
|
||||||
if (localStorage.getItem('welcome') !== 'seen') {
|
|
||||||
history.replace('/welcome');
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const downloadPublicKey = useCallback(() => {
|
|
||||||
const publicKeyBlob = new Blob([publicKey!]);
|
|
||||||
downloadLink('public-key.asc', publicKeyBlob);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
|
||||||
onClick={downloadPublicKey}
|
|
||||||
>
|
|
||||||
Download you sharing key
|
|
||||||
</Button>
|
|
||||||
<Add />
|
<Add />
|
||||||
{Object.keys(files).length > 0 && (
|
{Object.keys(files).length > 0 && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,18 +1,11 @@
|
|||||||
import React, { useContext, useEffect } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { Divider } from 'antd';
|
import { Divider } from 'antd';
|
||||||
import { useHistory } from 'react-router';
|
|
||||||
import Add from '../components/encrypt/Add';
|
import Add from '../components/encrypt/Add';
|
||||||
import FileList from '../components/FileList';
|
import FileList from '../components/FileList';
|
||||||
import EncryptionContext from '../contexts/Encryption';
|
import EncryptionContext from '../contexts/Encryption';
|
||||||
|
|
||||||
const Encrypt: React.FC = () => {
|
const Encrypt: React.FC = () => {
|
||||||
const history = useHistory();
|
|
||||||
const { files, deleteFile } = useContext(EncryptionContext);
|
const { files, deleteFile } = useContext(EncryptionContext);
|
||||||
useEffect(() => {
|
|
||||||
if (localStorage.getItem('welcome') !== 'seen') {
|
|
||||||
history.replace('/welcome');
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
60
src/screens/Intro.tsx
Normal file
60
src/screens/Intro.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useHistory } from 'react-router';
|
||||||
|
import Welcome from './Welcome';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Space,
|
||||||
|
} from 'antd';
|
||||||
|
import {
|
||||||
|
UploadOutlined,
|
||||||
|
DownloadOutlined,
|
||||||
|
KeyOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
|
||||||
|
|
||||||
|
const Thumb: React.FC = ({
|
||||||
|
title,
|
||||||
|
Icon,
|
||||||
|
link,
|
||||||
|
}) => {
|
||||||
|
const history = useHistory();
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
shape="round"
|
||||||
|
type="link"
|
||||||
|
icon={<Icon />}
|
||||||
|
onClick={() => history.push(link)}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Intro = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Welcome />
|
||||||
|
<Space style={{ width: '100%' }} align="center" direction="vertical">
|
||||||
|
<b>What do you want to do?</b>
|
||||||
|
<Thumb
|
||||||
|
title="I want to send a text/file"
|
||||||
|
link="/send"
|
||||||
|
Icon={UploadOutlined}
|
||||||
|
/>
|
||||||
|
<Thumb
|
||||||
|
link="/key"
|
||||||
|
title="I want to receive a file"
|
||||||
|
Icon={KeyOutlined}
|
||||||
|
/>
|
||||||
|
<Thumb
|
||||||
|
link="/receive"
|
||||||
|
title="I have received a file"
|
||||||
|
Icon={DownloadOutlined}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Intro;
|
||||||
101
src/screens/SetupKey.tsx
Normal file
101
src/screens/SetupKey.tsx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import React, { useContext, useState, useCallback } from 'react';
|
||||||
|
import { Space, Typography, Input, Button, Form } from 'antd';
|
||||||
|
import DecryptionContext from '../contexts/Decryption';
|
||||||
|
import { downloadLink } from '../helpers/files';
|
||||||
|
import {
|
||||||
|
RocketTwoTone,
|
||||||
|
LockTwoTone,
|
||||||
|
UserOutlined,
|
||||||
|
MailOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
|
||||||
|
const SetupKey: React.FC = () => {
|
||||||
|
const {
|
||||||
|
createKey,
|
||||||
|
publicKey,
|
||||||
|
} = useContext(DecryptionContext);
|
||||||
|
|
||||||
|
const [name, setName] = useState('');
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
|
||||||
|
const downloadPublicKey = useCallback(() => {
|
||||||
|
const publicKeyBlob = new Blob([publicKey!]);
|
||||||
|
downloadLink('public-key.asc', publicKeyBlob);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setupKey = useCallback(() => {
|
||||||
|
createKey(name, email);
|
||||||
|
}, [name, email]);
|
||||||
|
|
||||||
|
if (!publicKey) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Space direction="vertical" style={{ textAlign: 'center' }} >
|
||||||
|
<LockTwoTone style={{ fontSize: 150 }} />
|
||||||
|
<Typography.Title>Create your sharing key</Typography.Title>
|
||||||
|
<p>
|
||||||
|
Before I can send protected information to you I need a "sharing" key, which is a key that gets stored this device, allowing this device (and this device only) to read the informations I am sending.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
After creating it you need to send it to me
|
||||||
|
</p>
|
||||||
|
</Space>
|
||||||
|
<Form>
|
||||||
|
<Form.Item>
|
||||||
|
<Input
|
||||||
|
placeholder="Your name"
|
||||||
|
size="large"
|
||||||
|
prefix={<UserOutlined />}
|
||||||
|
value={name}
|
||||||
|
onChange={evt => setName(evt.target.value)}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item>
|
||||||
|
<Input
|
||||||
|
placeholder="Your e-mail"
|
||||||
|
size="large"
|
||||||
|
prefix={<MailOutlined />}
|
||||||
|
value={email}
|
||||||
|
onChange={evt => setEmail(evt.target.value)}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item style={{ textAlign: 'center' }} >
|
||||||
|
<Button
|
||||||
|
disabled={!name || !email}
|
||||||
|
type="primary"
|
||||||
|
onClick={setupKey}
|
||||||
|
size="large"
|
||||||
|
shape="round"
|
||||||
|
>
|
||||||
|
Create sharing key
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div style={{ textAlign: 'center' }}>
|
||||||
|
<RocketTwoTone style={{ fontSize: 150 }} />
|
||||||
|
<Typography.Title>Okay, you are all set.</Typography.Title>
|
||||||
|
<p>
|
||||||
|
Just send me your sharing key, and I will send files using it.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Remember that you need to go to this website on this device to decrypt the files after receiving them
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={downloadPublicKey}
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
shape="round"
|
||||||
|
>
|
||||||
|
Download sharing key
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SetupKey;
|
||||||
@@ -1,51 +1,23 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React from 'react';
|
||||||
import { Space, Layout, Button, Typography, notification } from 'antd';
|
import { Space, Layout, Typography } from 'antd';
|
||||||
import { AlignLeftOutlined, EyeInvisibleTwoTone, ArrowRightOutlined } from '@ant-design/icons';
|
import { EyeInvisibleTwoTone } from '@ant-design/icons';
|
||||||
import { useHistory } from 'react-router';
|
|
||||||
|
|
||||||
const openNotification = () => {
|
|
||||||
notification.warn({
|
|
||||||
message: 'Slow down!',
|
|
||||||
description: 'I am still working on this, but thanks for the interrest.'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const Welcome: React.FC = () => {
|
const Welcome: React.FC = () => {
|
||||||
const history = useHistory();
|
|
||||||
useEffect(() => {
|
|
||||||
localStorage.setItem('welcome', 'seen');
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout style={{ maxWidth: 800, margin: 'auto', textAlign: 'center' }}>
|
<Layout style={{ maxWidth: 800, margin: 'auto', textAlign: 'center' }}>
|
||||||
<Space direction="vertical">
|
<Space direction="vertical">
|
||||||
<EyeInvisibleTwoTone style={{ fontSize: 200 }} />
|
<EyeInvisibleTwoTone style={{ fontSize: 200 }} />
|
||||||
<Typography.Title level={1}>Protect before sending</Typography.Title>
|
<Typography.Title level={1}>Protect before sending</Typography.Title>
|
||||||
<p>
|
<p>
|
||||||
The internet can seem like a scary place, especially if you want to send sensitiv information across it.
|
The internet can seem like a scary place...
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
The truth is that a lot of systems, including e-mails, was not build for the internet that we have today.
|
Especially because a lot of the tools we use everyday (such as e-mail) wasn't build for the internet that we have today.
|
||||||
|
This is why it is important to have an additional layer of security when sending sensitive information.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
This is why it is so important to make sure your documents are well protected before sharing.
|
This is a tool that will help you have that extra layer of security when sharing files with me.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
|
||||||
This tool can be used to protect information before sharing them with me. The documents will be encrypted so that only I can ever unlock them, so no snoppy man in the middle...
|
|
||||||
</p>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
icon={<ArrowRightOutlined />}
|
|
||||||
onClick={() => history.push('/')}
|
|
||||||
>
|
|
||||||
Start protecting!
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
icon={<AlignLeftOutlined />}
|
|
||||||
onClick={openNotification}
|
|
||||||
>
|
|
||||||
Read all the technical stuff
|
|
||||||
</Button>
|
|
||||||
</Space>
|
</Space>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user