Skip to content

Commit

Permalink
e2e encryption default
Browse files Browse the repository at this point in the history
  • Loading branch information
abhishekg999 committed Sep 5, 2024
1 parent 7784da8 commit aeeebcb
Show file tree
Hide file tree
Showing 13 changed files with 3,889 additions and 155 deletions.
Binary file modified bun.lockb
Binary file not shown.
3,567 changes: 3,567 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 11 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,31 @@
"preview": "vite preview"
},
"dependencies": {
"@radix-ui/react-hover-card": "^1.1.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"lucide-react": "^0.436.0",
"nanoid": "^5.0.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
"@eslint/js": "^9.9.1",
"@types/bun": "^1.1.8",
"@types/react": "^18.3.3",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.20",
"eslint": "^9.9.0",
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
"eslint-plugin-react-refresh": "^0.4.9",
"eslint": "^9.9.1",
"eslint-plugin-react-hooks": "^5.1.0-rc-fb9a90fa48-20240614",
"eslint-plugin-react-refresh": "^0.4.11",
"globals": "^15.9.0",
"postcss": "^8.4.41",
"postcss": "^8.4.45",
"tailwindcss": "^3.4.10",
"typescript": "^5.5.3",
"typescript-eslint": "^8.0.1",
"vite": "^5.4.1"
"typescript": "^5.5.4",
"typescript-eslint": "^8.4.0",
"vite": "^5.4.3"
}
}
Binary file modified secret-worker/bun.lockb
Binary file not shown.
8 changes: 4 additions & 4 deletions secret-worker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
"@hono/zod-validator": "^0.2.2",
"drizzle-orm": "^0.33.0",
"drizzle-zod": "^0.5.1",
"hono": "^4.5.8",
"hono": "^4.5.11",
"zod": "^3.23.8"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20240529.0",
"@cloudflare/workers-types": "^4.20240903.0",
"@libsql/client": "^0.9.0",
"drizzle-kit": "^0.24.1",
"wrangler": "^3.57.2"
"drizzle-kit": "^0.24.2",
"wrangler": "^3.74.0"
}
}
2 changes: 1 addition & 1 deletion secret-worker/src/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const secrets = sqliteTable('secrets', {

export const insertSecretSchema = createInsertSchema(secrets, {
id: (schema) => schema.id.uuid(),
data: (schema) => schema.data.max(4096),
data: (schema) => schema.data.max(22000),
timestamp: (schema) => schema.timestamp.min(0)
});

Expand Down
15 changes: 7 additions & 8 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ import { isValidUUID } from './lib/utils';


const App = () => {
const hash = window.location.hash.substring(1);
if (!isValidUUID(hash) && window.location.pathname !== '/') {
const path = window.location.pathname.endsWith('/') ? window.location.pathname.slice(0, -1) : window.location.pathname;
if (isValidUUID(path.slice(1))) {
return <View hash={path.slice(1)} />;
} else if (path === '') {
return <Home />;
} else {
window.location.replace('/');
}

return (
<>
{isValidUUID(hash) ? <View hash={hash} /> : <Home />}
</>
);
return null;
}
export default App;
218 changes: 124 additions & 94 deletions src/components/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,102 +1,132 @@
import { useState } from 'react';
import { Copy, Link, AlertCircle } from 'lucide-react';
import { Copy, Link, AlertCircle, HelpCircle, GithubIcon } from 'lucide-react';
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card"
import { base64ToUint8, createEncryptionPair, decryptData, uint8ToBase64 } from '@/lib/utils';

const Home = () => {
const [secret, setSecret] = useState('');
const [generatedLink, setGeneratedLink] = useState('');
const [inputEnabled, setInputEnabled] = useState(true);
const [error, setError] = useState('');

const handleCreateLink = () => {
const data = secret.trim();
if (!data.trim()) {
setError('Please enter a secret to create a link.');
return;
}

setInputEnabled(false);

const fetchData = async () => {
const response = await fetch('/api/secret/new', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ data: data.trim() }),
});

if (response.ok) {
const data = await response.json();
setGeneratedLink(`${window.location.origin}/#${data.id}`);
} else {
setGeneratedLink('');
setError('An error occurred while creating the secret link. Please refresh and try again.');
}
};

fetchData();
const [secret, setSecret] = useState('');
const [generatedLink, setGeneratedLink] = useState('');
const [inputEnabled, setInputEnabled] = useState(true);
const [error, setError] = useState('');

const handleCreateLink = () => {
const data = secret.trim();
if (!data.trim()) {
setError('Please enter a secret to create a link.');
return;
}

const handleCopyLink = () => {
navigator.clipboard.writeText(generatedLink);

setInputEnabled(false);

const uploadSecret = async () => {
const [key, enc] = await createEncryptionPair(data, 21);
const response = await fetch('/api/secret/new', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ data: enc }),
});

setSecret((prev) => prev.replace(/./g, '*'));

if (response.ok) {
const data = await response.json();
setGeneratedLink(`${window.location.origin}/${data.id}#${key}`);
} else {
setGeneratedLink('');
setError('An error occurred while creating the secret link. Please refresh and try again.');
}
};

return (
<div className="min-h-screen bg-gradient-to-br from-gray-900 to-gray-800 flex items-center justify-center p-4">
<div className="bg-gray-800 rounded-lg shadow-xl p-6 w-full max-w-md border border-gray-700">
<h1 className="text-2xl font-bold text-gray-100 mb-4 text-center">Create One-Time Secret</h1>
<textarea
className="w-full h-32 p-2 border border-gray-600 rounded-md focus:ring-2 focus:ring-purple-500 focus:border-transparent resize-none bg-gray-700 text-gray-100 disabled:opacity-50 disabled:cursor-not-allowed"
placeholder="Enter your secret here..."
value={secret}
onChange={(e) => setSecret(e.target.value)}
disabled={!inputEnabled}
maxLength={4000}
/>
<button
className={`mt-4 w-full bg-purple-600 text-white font-bold py-2 px-4 rounded-md transition duration-300 ease-in-out flex items-center justify-center ${inputEnabled
? 'hover:bg-purple-700 hover:shadow-lg transform hover:-translate-y-0.5'
: 'opacity-50 cursor-not-allowed'
}`}
onClick={handleCreateLink}
disabled={!inputEnabled}
>
<Link className="mr-2" size={18} />
Create Secret Link
</button>

{generatedLink && (
<div className="mt-4">
<p className="text-sm text-gray-400 mb-2">Your secret link:</p>
<div className="flex items-center bg-gray-700 p-2 rounded-md">
<input
type="text"
readOnly
value={generatedLink}
className="flex-grow bg-transparent text-sm text-gray-300 focus:outline-none"
/>
<button
onClick={handleCopyLink}
className="ml-2 text-purple-400 hover:text-purple-300 transition duration-300 ease-in-out transform hover:scale-110"
title="Copy to clipboard"
>
<Copy size={18} />
</button>
</div>

uploadSecret();
}

const handleCopyLink = () => {
navigator.clipboard.writeText(generatedLink);
};

return (
<>
<div className='absolute top-6 right-6'>
<HoverCard>
<HoverCardTrigger>
<HelpCircle className='text-gray-400 hover:text-white w-8 h-8' />
</HoverCardTrigger>
<HoverCardContent className='bg-white text-gray-800 p-4 shadow-lg rounded-md mx-6'>
<p className='text-sm'>
This site allows you to create one-time links <b className='text-purple-900'>securely</b>. All data is end-to-end encrypted, the key <b className='text-red-800'>never</b> leaves your device. If the link is viewed once, it is <b className='text-orange-900'>permanently deleted</b> from the server.
</p>
</HoverCardContent>
</HoverCard>
</div>
<div className="bg-gray-800 rounded-lg shadow-xl p-6 w-full max-w-md border border-gray-700">
<h1 className="text-2xl font-bold text-gray-100 mb-4 text-center">Create One-Time Link</h1>
<textarea
className="w-full h-32 p-2 border border-gray-600 rounded-md focus:ring-2 focus:ring-purple-500 focus:border-transparent resize-none bg-gray-700 text-gray-100 disabled:opacity-50 disabled:cursor-not-allowed"
placeholder="Enter your secret here..."
value={secret}
onChange={(e) => setSecret(e.target.value)}
disabled={!inputEnabled}
maxLength={8192}
/>
<button
className={`mt-4 w-full bg-purple-600 text-white font-bold py-2 px-4 rounded-md transition duration-300 ease-in-out flex items-center justify-center ${inputEnabled
? 'hover:bg-purple-700 hover:shadow-lg transform hover:-translate-y-0.5'
: 'opacity-50 cursor-not-allowed'
}`}
onClick={handleCreateLink}
disabled={!inputEnabled}
>
<Link className="mr-2" size={18} />
Create Link
</button>

{generatedLink && (
<div className="mt-4">
<p className="text-sm text-gray-400 mb-2">Your secret link:</p>
<div className="flex items-center bg-gray-700 p-2 rounded-md">
<input
type="text"
readOnly
value={generatedLink}
className="flex-grow bg-transparent text-sm text-gray-300 focus:outline-none"
/>
<button
onClick={handleCopyLink}
className="ml-2 text-purple-400 hover:text-purple-300 transition duration-300 ease-in-out transform hover:scale-110"
title="Copy to clipboard"
>
<Copy size={18} />
</button>
</div>
)}

{error && (
<div className="mt-4 bg-red-900 border border-red-700 text-red-100 px-4 py-3 rounded relative" role="alert">
<div className="flex items-center">
<AlertCircle className="mr-2" size={18} />
<span className="block sm:inline pl-2 pr-2">{error}</span>
</div>
</div>
)}

{error && (
<div className="mt-4 bg-red-900 border border-red-700 text-red-100 px-4 py-3 rounded relative" role="alert">
<div className="flex items-center">
<AlertCircle className="mr-2" size={18} />
<span className="block sm:inline pl-2 pr-2">{error}</span>
</div>
)}
</div>
</div>
)}
</div>
);
};

export default Home;

<footer className="sticky bottom-0 flex items-center text-center text-white w-full p-2">
<span className="mx-auto flex gap-4">
View the source on GitHub
<a href="https://github.com/abhishekg999/secret" target="_blank" rel="noopener noreferrer">
<GithubIcon size={24} />
</a>
</span>
</footer>
</>
);
};

export default Home;
15 changes: 15 additions & 0 deletions src/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const Layout = ({
children,
}: Readonly<{
children: React.ReactNode;
}>) => {
return (
<main>
<div className="min-h-screen bg-gradient-to-br from-gray-900 to-gray-800 flex flex-col gap-5 items-center justify-center p-4">
{children}
</div>
</main>
);
}

export default Layout;
Loading

0 comments on commit aeeebcb

Please sign in to comment.