Compare commits
37 Commits
Author | SHA1 | Date | |
---|---|---|---|
acd488aab0 | |||
5378b7a820 | |||
1bb5d3ecb0 | |||
0d79e9c85e | |||
70382a63ed | |||
b3886cc6fc | |||
cfe525f274 | |||
96e8ec4b67 | |||
7a3397f978 | |||
dc212d7441 | |||
6e04594b4d | |||
b6834aa829 | |||
093a6c5c2b | |||
407d220552 | |||
bf8593080b | |||
d63d529d2b | |||
c4f545d1e8 | |||
b94588ead4 | |||
96657b89d2 | |||
d05b090252 | |||
d262a37db6 | |||
a66e8033df | |||
95ba8d1fed | |||
f93be44449 | |||
f73aa1bcf8 | |||
6e25bb697f | |||
5a105a7b29 | |||
c8dfeaec12 | |||
bf2c95bfb6 | |||
4aef5a1b04 | |||
7520b6b1da | |||
d84475ffe9 | |||
8e6ef5e2f6 | |||
8acf812066 | |||
64d5953f60 | |||
f447cbea2d | |||
0cd49a5c17 |
1
.gitattributes
vendored
@@ -1,2 +1 @@
|
||||
*.afdesign filter=lfs diff=lfs merge=lfs -text
|
||||
*.svg filter=lfs diff=lfs merge=lfs -text
|
||||
|
12
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: cupcakearmy
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
2
.github/workflows/docker.yml
vendored
@@ -22,6 +22,7 @@ jobs:
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
@@ -31,6 +32,7 @@ jobs:
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
# platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
- name: Image digest
|
||||
|
9
.gitignore
vendored
@@ -1 +1,10 @@
|
||||
|
||||
# Backend
|
||||
/target
|
||||
|
||||
# Client
|
||||
.DS_Store
|
||||
node_modules
|
||||
/.svelte
|
||||
/build
|
||||
/functions
|
||||
|
68
CHANGELOG.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.0.8] - 2021-05-05
|
||||
|
||||
### Added
|
||||
|
||||
- Manual theme override option
|
||||
|
||||
### Fixed
|
||||
|
||||
- Removed Arm builds for now
|
||||
- iOS style bugs
|
||||
|
||||
## [1.0.7] - 2021-05-04
|
||||
|
||||
### Added
|
||||
|
||||
- Arm images
|
||||
|
||||
## [1.0.6] - 2021-05-04
|
||||
|
||||
### Added
|
||||
|
||||
- Always use encryption with random passwords included links
|
||||
|
||||
## [1.0.5] - 2021-05-03
|
||||
|
||||
### Fixed
|
||||
|
||||
- Typos
|
||||
|
||||
## [1.0.4] - 2021-05-02
|
||||
|
||||
### Added
|
||||
|
||||
- From scratch docker image
|
||||
|
||||
## [1.0.3] - 2021-05-02
|
||||
|
||||
### Fixed
|
||||
|
||||
- Higher default text area
|
||||
- Mobile touchups
|
||||
|
||||
## [1.0.2] - 2021-05-02
|
||||
|
||||
### Fixed
|
||||
|
||||
- SVG Icons
|
||||
|
||||
## [1.0.1] - 2021-05-02
|
||||
|
||||
### Added
|
||||
|
||||
- Dark mode support
|
||||
|
||||
### Fixed
|
||||
|
||||
- Don't reload data on wrong password
|
||||
|
||||
## [1.0.0] - 2021-05-02
|
||||
|
||||
Initial release
|
@@ -1,7 +1,7 @@
|
||||
FROM node:16-alpine as CLIENT
|
||||
|
||||
WORKDIR /tmp
|
||||
COPY ./client .
|
||||
COPY ./client ./
|
||||
|
||||
RUN npm ci
|
||||
RUN npm run build
|
||||
@@ -10,12 +10,12 @@ FROM rust:1.51-alpine as RUST
|
||||
|
||||
WORKDIR /tmp
|
||||
RUN apk add libc-dev openssl-dev alpine-sdk
|
||||
COPY ./Cargo* .
|
||||
COPY ./Cargo* ./
|
||||
COPY ./src ./src
|
||||
|
||||
RUN cargo build --release
|
||||
|
||||
FROM alpine
|
||||
FROM scratch
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=RUST /tmp/target/release/cryptgeon .
|
||||
|
37
README.md
@@ -1,16 +1,25 @@
|
||||
<p align="center">
|
||||
<img src="./design/github.svg">
|
||||
<img src="./design/Github.png">
|
||||
</p>
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## About?
|
||||
|
||||
_cryptgeon_ is an secure, open source sharing note service inspired by [_PrivNote_](https://privnote.com)
|
||||
_cryptgeon_ is a secure, open source sharing note service inspired by [_PrivNote_](https://privnote.com)
|
||||
|
||||
## Demo
|
||||
|
||||
Check out the demo and see for yourself https://cryptgeon.nicco.io.
|
||||
|
||||
## Features
|
||||
|
||||
- view and time constrains
|
||||
- server cannot decrypt contents due to client side encryption
|
||||
- view and time constraints
|
||||
- in memory, no persistence
|
||||
- in browser encryption → server cannot decrypt contents
|
||||
- obligatory dark mode support
|
||||
|
||||
## How does it work?
|
||||
|
||||
@@ -18,11 +27,11 @@ each note has a 512bit generated <i>id</i> that is used to retrieve the note. da
|
||||
|
||||
## Screenshot
|
||||
|
||||

|
||||

|
||||
|
||||
## Deployment
|
||||
|
||||
Docker is the easiest way.
|
||||
Docker is the easiest way. There is the [official image here](https://hub.docker.com/r/cupcakearmy/cryptgeon).
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
@@ -36,10 +45,26 @@ services:
|
||||
|
||||
app:
|
||||
image: cupcakearmy/cryptgeon:latest
|
||||
depends_on:
|
||||
- memcache
|
||||
ports:
|
||||
- 80:5000
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
1. Clone
|
||||
2. run `npm i` in the root and and client `client/` folders.
|
||||
3. Run `npm run dev` to start development.
|
||||
|
||||
Running `npm run dev` in the root folder will start the following things
|
||||
|
||||
- a memcache docker container
|
||||
- rust backend with hot reload
|
||||
- client with hot reload
|
||||
|
||||
You can see the app under [localhost:3000](http://localhost:3000).
|
||||
|
||||
###### Attributions
|
||||
|
||||
Icons made by <a href="https://www.freepik.com" title="Freepik">freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a>
|
||||
|
@@ -7,18 +7,46 @@
|
||||
:root {
|
||||
font-family: 'Fira Mono', monospace;
|
||||
|
||||
--ui-bg-0: #fefefe;
|
||||
--ui-bg-0-85: #ffffffd9;
|
||||
--ui-bg-1: #eee;
|
||||
--ui-bg-2: #e2e2e2;
|
||||
--ui-text-0: #111;
|
||||
--ui-text-1: #222;
|
||||
--ui-bg-0: #111;
|
||||
--ui-bg-0-85: #111111d9;
|
||||
--ui-bg-1: #333;
|
||||
--ui-bg-2: #444;
|
||||
--ui-text-0: #fefefe;
|
||||
--ui-text-1: #eee;
|
||||
--ui-clr-primary: hsl(186, 65%, 55%);
|
||||
--ui-clr-error: hsl(357, 77%, 51%);
|
||||
|
||||
--ui-anim: all 150ms ease;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
--ui-bg-0: #fefefe;
|
||||
--ui-bg-0-85: #fefefed9;
|
||||
--ui-bg-1: #eee;
|
||||
--ui-bg-2: #e2e2e2;
|
||||
--ui-text-0: #111;
|
||||
--ui-text-1: #333;
|
||||
}
|
||||
}
|
||||
|
||||
:root[theme='dark'] {
|
||||
--ui-bg-0: #111 !important;
|
||||
--ui-bg-0-85: #111111d9 !important;
|
||||
--ui-bg-1: #333 !important;
|
||||
--ui-bg-2: #444 !important;
|
||||
--ui-text-0: #fefefe !important;
|
||||
--ui-text-1: #eee !important;
|
||||
}
|
||||
:root[theme='light'] {
|
||||
--ui-bg-0: #fefefe !important;
|
||||
--ui-bg-0-85: #fefefed9 !important;
|
||||
--ui-bg-1: #eee !important;
|
||||
--ui-bg-2: #e2e2e2 !important;
|
||||
--ui-text-0: #111 !important;
|
||||
--ui-text-1: #333 !important;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
color: var(--ui-clr-error);
|
||||
}
|
||||
@@ -50,9 +78,18 @@ input,
|
||||
textarea,
|
||||
button {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
border-radius: 0;
|
||||
transition: var(--ui-anim);
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
background: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
*:disabled,
|
||||
*[disabled='true'] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
|
@@ -5,11 +5,10 @@ const base = axios.create({ baseURL: dev ? 'http://localhost:5000' : undefined }
|
||||
|
||||
export type Note = {
|
||||
contents: string
|
||||
password: boolean
|
||||
views?: number
|
||||
expiration?: number
|
||||
}
|
||||
export type NoteInfo = Pick<Note, 'password'>
|
||||
export type NoteInfo = {}
|
||||
export type NotePublic = Pick<Note, 'contents'>
|
||||
|
||||
export async function create(note: Note) {
|
||||
|
@@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte'
|
||||
export let icon: string
|
||||
export let icon: string = ''
|
||||
export let href: string = ''
|
||||
|
||||
$: src = `/icons/${icon}.svg`
|
||||
$: src = href || `/icons/${icon}.svg`
|
||||
|
||||
let html = null
|
||||
|
||||
|
@@ -13,15 +13,20 @@
|
||||
<style>
|
||||
textarea {
|
||||
width: 100%;
|
||||
min-height: 8rem;
|
||||
min-height: calc(100vh - 30rem);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 2px solid var(--ui-bg-1);
|
||||
resize: vertical;
|
||||
outline: none;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 30rem) {
|
||||
textarea {
|
||||
min-height: calc(100vh - 25rem);
|
||||
}
|
||||
}
|
||||
|
||||
textarea:hover,
|
||||
textarea:focus {
|
||||
border-color: var(--ui-clr-primary);
|
||||
|
@@ -32,7 +32,7 @@
|
||||
</script>
|
||||
|
||||
<label>
|
||||
<small>
|
||||
<small disabled={$$restProps.disabled}>
|
||||
{label}
|
||||
</small>
|
||||
<input bind:value {...$$restProps} />
|
||||
|
63
client/src/lib/ui/ThemeToggle.svelte
Normal file
@@ -0,0 +1,63 @@
|
||||
<script lang="ts" context="module">
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
export enum Theme {
|
||||
Dark = 'dark',
|
||||
Light = 'light',
|
||||
Auto = 'auto',
|
||||
}
|
||||
|
||||
const NextTheme = {
|
||||
[Theme.Auto]: Theme.Light,
|
||||
[Theme.Light]: Theme.Dark,
|
||||
[Theme.Dark]: Theme.Auto,
|
||||
}
|
||||
|
||||
function init(): Theme {
|
||||
if (typeof window !== 'undefined') {
|
||||
const saved = window.localStorage.getItem('theme') as Theme
|
||||
console.log(Theme, window.localStorage.getItem('theme'))
|
||||
if (Object.values(Theme).includes(saved)) return saved
|
||||
}
|
||||
return Theme.Auto
|
||||
}
|
||||
|
||||
export const theme = writable<Theme>(init())
|
||||
|
||||
theme.subscribe((theme) => {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.localStorage.setItem('theme', theme)
|
||||
const html = window.document.getElementsByTagName('html')[0]
|
||||
html.setAttribute('theme', theme)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import Icon from '$lib/ui/Icon.svelte'
|
||||
|
||||
function change() {
|
||||
theme.update((current) => NextTheme[current])
|
||||
}
|
||||
</script>
|
||||
|
||||
<div on:click={change}>
|
||||
<Icon class="icon" icon="contrast-sharp" />
|
||||
{$theme}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div :global(.icon) {
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { Note } from '$lib/api'
|
||||
import { create } from '$lib/api'
|
||||
import { getKeyFromString, encrypt } from '$lib/crypto'
|
||||
import { getKeyFromString, encrypt, Hex, getRandomBytes } from '$lib/crypto'
|
||||
|
||||
import Button from '$lib/ui/Button.svelte'
|
||||
import Switch from '$lib/ui/Switch.svelte'
|
||||
@@ -10,11 +10,9 @@
|
||||
|
||||
let note: Note = {
|
||||
contents: '',
|
||||
password: false,
|
||||
views: 1,
|
||||
expiration: 60,
|
||||
}
|
||||
let password: string = ''
|
||||
let result: { password: string; id: string } | null = null
|
||||
let advanced = false
|
||||
let type = false
|
||||
@@ -37,18 +35,15 @@
|
||||
try {
|
||||
error = null
|
||||
loading = true
|
||||
const password = Hex.encode(getRandomBytes(32))
|
||||
const key = await getKeyFromString(password)
|
||||
const data: Note = {
|
||||
contents: note.contents,
|
||||
password: !!password,
|
||||
contents: await encrypt(note.contents, key),
|
||||
}
|
||||
// @ts-ignore
|
||||
if (type) data.expiration = parseInt(note.expiration)
|
||||
// @ts-ignore
|
||||
else data.views = parseInt(note.views)
|
||||
if (data.password) {
|
||||
const key = await getKeyFromString(password)
|
||||
data.contents = await encrypt(data.contents, key)
|
||||
}
|
||||
|
||||
const response = await create(data)
|
||||
result = {
|
||||
@@ -68,11 +63,12 @@
|
||||
</script>
|
||||
|
||||
{#if result}
|
||||
{#if result.password}
|
||||
<TextInput type="password" readonly value={result.password} copy />
|
||||
<br />
|
||||
{/if}
|
||||
<TextInput type="text" readonly value="{window.location.origin}/note/{result.id}" copy />
|
||||
<TextInput
|
||||
type="text"
|
||||
readonly
|
||||
value="{window.location.origin}/note/{result.id}/{result.password}"
|
||||
copy
|
||||
/>
|
||||
<br />
|
||||
<Button on:click={reset}>new</Button>
|
||||
{:else}
|
||||
@@ -112,15 +108,6 @@
|
||||
max={360}
|
||||
/>
|
||||
</div>
|
||||
<br />
|
||||
<TextInput
|
||||
type="password"
|
||||
label="password"
|
||||
placeholder="optional"
|
||||
bind:value={password}
|
||||
copy
|
||||
random
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
37
client/src/lib/views/Footer.svelte
Normal file
@@ -0,0 +1,37 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$lib/ui/Icon.svelte'
|
||||
import ThemeToggle from '$lib/ui/ThemeToggle.svelte'
|
||||
</script>
|
||||
|
||||
<footer>
|
||||
<ThemeToggle />
|
||||
<nav>
|
||||
<a href="/">/home</a>
|
||||
<a href="/about">/about</a>
|
||||
<a href="https://github.com/cupcakearmy/cryptgeon" target="_blank" rel="noopener">/code</a>
|
||||
</nav>
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 1rem;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
background-color: var(--ui-bg-0-85);
|
||||
}
|
||||
|
||||
a {
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
82
client/src/lib/views/Header.svelte
Normal file
@@ -0,0 +1,82 @@
|
||||
<header>
|
||||
<a href="/">
|
||||
<svg
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 450 200"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xml:space="preserve"
|
||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"
|
||||
><g
|
||||
><clipPath id="_clip1"><rect x="6.336" y="3.225" width="193.55" height="193.55" /></clipPath
|
||||
><g clip-path="url(#_clip1)"
|
||||
><g
|
||||
><g
|
||||
><path
|
||||
d="M173.425,43.296c-2.087,-0 -3.78,1.693 -3.78,3.78c-0,2.087 1.693,3.78 3.78,3.78c2.087,0 3.78,-1.693 3.78,-3.78c0,-2.087 -1.693,-3.78 -3.78,-3.78Z"
|
||||
style="fill-rule:nonzero;"
|
||||
/></g
|
||||
></g
|
||||
><g
|
||||
><g
|
||||
><path
|
||||
d="M103.112,134.023c-2.087,-0 -3.781,1.693 -3.781,3.78c0,2.087 1.694,3.78 3.781,3.78c2.086,0 3.78,-1.693 3.78,-3.78c-0,-2.087 -1.694,-3.78 -3.78,-3.78Z"
|
||||
style="fill-rule:nonzero;"
|
||||
/></g
|
||||
></g
|
||||
><g
|
||||
><g
|
||||
><path
|
||||
d="M156.036,96.22c-2.088,-0 -3.781,1.692 -3.781,3.78c0,18.76 -15.262,34.023 -34.022,34.023c-2.088,-0 -3.781,1.692 -3.781,3.78c0,2.088 1.693,3.78 3.781,3.78c22.929,0 41.583,-18.654 41.583,-41.583c-0,-2.088 -1.693,-3.78 -3.78,-3.78Z"
|
||||
style="fill-rule:nonzero;"
|
||||
/></g
|
||||
></g
|
||||
><g
|
||||
><g
|
||||
><path
|
||||
d="M199.488,60.507l-9.515,-19.026c-4.102,-8.207 -12.35,-13.306 -21.527,-13.306c-7.479,-0 -14.626,3.547 -19.154,9.498l-10.679,12.016c-2.846,-4.047 -7.021,-7.049 -11.83,-8.421l-19.102,-5.459c-14.623,-4.178 -28.92,-15.441 -39.227,-30.901c-0.924,-1.386 -2.646,-2.003 -4.241,-1.521c-1.594,0.483 -2.684,1.953 -2.684,3.618l-0,20.372c-0,9.468 1.417,18.804 4.219,27.813c-2.936,0.73 -5.896,1.34 -8.843,1.816c-5.772,0.936 -11.653,1.411 -17.48,1.411l-29.308,-0c-1.374,-0 -2.64,0.746 -3.307,1.948c-0.666,1.202 -0.627,2.671 0.101,3.836l22.637,36.219c9.672,15.473 26.329,25.183 44.578,25.983l-36.36,41.158c-5.602,5.728 -3.655,15.315 3.746,18.396l20.017,9.887c0.089,0.044 0.179,0.084 0.271,0.121c5.79,2.313 12.389,-0.496 14.726,-6.279l13.969,-32.982l27.738,0c31.966,0 58.972,-25.967 58.972,-56.704l0,-22.682c0,-6.253 5.088,-11.341 11.341,-11.341l7.56,0c1.311,0 2.528,-0.678 3.216,-1.793c0.689,-1.114 0.752,-2.506 0.166,-3.677Zm-130.399,-42.236c10.418,12.203 23.307,21.034 36.515,24.808l19.103,5.459c3.726,1.063 6.866,3.624 8.666,7.048l-10.048,11.307l-17.652,-10.591c-7.646,-4.588 -16.746,-6.412 -25.776,-4.951c-2.838,0.46 -4.649,1.038 -6.877,1.759c-2.609,-8.332 -3.931,-16.971 -3.931,-25.733l0,-9.106Zm119.457,40.146c-10.422,-0 -18.901,8.479 -18.901,18.901l-0,22.682c-0,26.639 -23.544,49.144 -51.412,49.144l-30.243,-0c-10.77,-0 -20.451,5.983 -25.265,15.615l-0.797,1.596c-0.934,1.867 -0.177,4.137 1.691,5.071c1.867,0.934 4.138,0.176 5.071,-1.691c0.44,-0.586 3.306,-9.102 13.21,-12.125l-12.349,29.159c-0.01,0.024 -0.02,0.047 -0.029,0.071c-0.75,1.877 -2.864,2.851 -4.8,2.148c-21.279,-10.506 -19.997,-9.888 -20.252,-9.99c-2.526,-1.01 -3.191,-4.259 -1.267,-6.182c0.13,-0.131 8.026,-9.078 41.009,-46.411c13.867,-0.617 26.841,-6.319 36.694,-16.172c1.476,-1.476 1.476,-3.87 0,-5.346c-1.476,-1.477 -3.87,-1.476 -5.346,-0c-16.827,16.828 -36.803,13.634 -39.028,14.014c-16.603,0 -31.771,-8.407 -40.573,-22.488l-2.417,-3.868l2.729,1.065c17.307,6.753 38.919,4.347 53.816,-5.586c1.737,-1.158 2.207,-3.505 1.049,-5.242c-1.159,-1.737 -3.505,-2.207 -5.243,-1.048c-13.085,8.724 -31.922,10.666 -46.875,4.832l-12.185,-4.753l-9.896,-15.836l22.488,0c6.231,0 12.519,-0.507 18.689,-1.507c12.711,-2.055 18.051,-4.855 22.992,-5.655c7.182,-1.163 14.516,0.274 20.678,3.97l29.625,17.775c1.789,1.074 4.112,0.494 5.186,-1.296c1.075,-1.79 0.495,-4.113 -1.295,-5.187l-5.378,-3.226c26.56,-29.893 25.14,-28.272 25.32,-28.511c3.101,-4.136 8.038,-6.605 13.204,-6.605c6.294,0 11.951,3.497 14.764,9.127l6.779,13.555l-1.443,-0Z"
|
||||
style="fill-rule:nonzero;"
|
||||
/></g
|
||||
></g
|
||||
></g
|
||||
><text
|
||||
x="197.239px"
|
||||
y="127.131px"
|
||||
style="font-family:'Sofia-Regular', 'Sofia';font-size:60.681px;">cryptgeon</text
|
||||
></g
|
||||
></svg
|
||||
>
|
||||
</a>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
a {
|
||||
border: none;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-top: 4rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 30rem) {
|
||||
header {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
header svg {
|
||||
max-height: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
header svg {
|
||||
width: 100%;
|
||||
max-width: 16rem;
|
||||
transform: translateX(-1rem);
|
||||
fill: currentColor;
|
||||
}
|
||||
</style>
|
BIN
client/src/lib/views/Header/Logo.svg
(Stored with Git LFS)
@@ -1,74 +0,0 @@
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<header>
|
||||
<a href="/">
|
||||
<svg
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 475 200"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xml:space="preserve"
|
||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"
|
||||
fill="currentColor"
|
||||
><g id="Logo"
|
||||
><clipPath id="_clip1"><rect x="4.516" y="3.225" width="193.55" height="193.55" /></clipPath
|
||||
><g clip-path="url(#_clip1)"
|
||||
><g
|
||||
><g
|
||||
><path
|
||||
d="M171.604,43.296c-2.087,-0 -3.78,1.693 -3.78,3.78c-0,2.087 1.693,3.78 3.78,3.78c2.087,0 3.78,-1.693 3.78,-3.78c0,-2.087 -1.693,-3.78 -3.78,-3.78Z"
|
||||
style="fill-rule:nonzero;"
|
||||
/></g
|
||||
></g
|
||||
><g
|
||||
><g
|
||||
><path
|
||||
d="M101.291,134.023c-2.087,-0 -3.78,1.693 -3.78,3.78c-0,2.087 1.693,3.78 3.78,3.78c2.087,0 3.78,-1.693 3.78,-3.78c0,-2.087 -1.693,-3.78 -3.78,-3.78Z"
|
||||
style="fill-rule:nonzero;"
|
||||
/></g
|
||||
></g
|
||||
><g
|
||||
><g
|
||||
><path
|
||||
d="M154.215,96.22c-2.088,-0 -3.78,1.692 -3.78,3.78c-0,18.76 -15.263,34.023 -34.023,34.023c-2.088,-0 -3.78,1.692 -3.78,3.78c-0,2.088 1.692,3.78 3.78,3.78c22.929,0 41.583,-18.654 41.583,-41.583c0,-2.088 -1.692,-3.78 -3.78,-3.78Z"
|
||||
style="fill-rule:nonzero;"
|
||||
/></g
|
||||
></g
|
||||
><g
|
||||
><g
|
||||
><path
|
||||
d="M197.667,60.507l-9.515,-19.026c-4.101,-8.207 -12.349,-13.306 -21.526,-13.306c-7.48,-0 -14.627,3.547 -19.155,9.498l-10.678,12.016c-2.847,-4.047 -7.021,-7.049 -11.831,-8.421l-19.102,-5.459c-14.623,-4.178 -28.92,-15.441 -39.227,-30.901c-0.924,-1.386 -2.646,-2.003 -4.24,-1.521c-1.595,0.483 -2.685,1.953 -2.685,3.618l-0,20.372c-0,9.468 1.418,18.804 4.219,27.813c-2.936,0.73 -5.896,1.34 -8.842,1.816c-5.773,0.936 -11.654,1.411 -17.48,1.411l-29.309,-0c-1.374,-0 -2.64,0.746 -3.306,1.948c-0.666,1.202 -0.628,2.671 0.101,3.836l22.636,36.219c9.672,15.473 26.33,25.183 44.578,25.983l-36.36,41.158c-5.602,5.728 -3.654,15.315 3.746,18.396l20.018,9.887c0.088,0.044 0.179,0.084 0.271,0.121c5.789,2.313 12.389,-0.496 14.725,-6.279l13.969,-32.982l27.738,0c31.966,0 58.972,-25.967 58.972,-56.704l0,-22.682c0,-6.253 5.088,-11.341 11.341,-11.341l7.561,0c1.31,0 2.527,-0.678 3.216,-1.793c0.688,-1.114 0.751,-2.506 0.165,-3.677Zm-130.399,-42.236c10.418,12.203 23.307,21.034 36.515,24.808l19.104,5.459c3.725,1.063 6.865,3.624 8.666,7.048l-10.049,11.307l-17.652,-10.591c-7.646,-4.588 -16.746,-6.412 -25.776,-4.951c-2.837,0.46 -4.648,1.038 -6.877,1.759c-2.609,-8.332 -3.931,-16.971 -3.931,-25.733l0,-9.106Zm119.457,40.146c-10.422,-0 -18.901,8.479 -18.901,18.901l-0,22.682c-0,26.639 -23.544,49.144 -51.412,49.144l-30.242,-0c-10.771,-0 -20.452,5.983 -25.265,15.615l-0.798,1.596c-0.934,1.867 -0.177,4.137 1.691,5.071c1.867,0.934 4.138,0.176 5.072,-1.691c0.44,-0.586 3.306,-9.102 13.21,-12.125l-12.349,29.159c-0.01,0.024 -0.02,0.047 -0.03,0.071c-0.75,1.877 -2.864,2.851 -4.8,2.148c-21.279,-10.506 -19.997,-9.888 -20.252,-9.99c-2.526,-1.01 -3.191,-4.259 -1.267,-6.182c0.131,-0.131 8.026,-9.078 41.009,-46.411c13.867,-0.617 26.842,-6.319 36.694,-16.172c1.477,-1.476 1.477,-3.87 0,-5.346c-1.476,-1.477 -3.869,-1.476 -5.346,-0c-16.827,16.828 -36.803,13.634 -39.027,14.014c-16.604,0 -31.772,-8.407 -40.574,-22.488l-2.417,-3.868l2.729,1.065c17.308,6.753 38.919,4.347 53.817,-5.586c1.737,-1.158 2.206,-3.505 1.048,-5.242c-1.158,-1.737 -3.505,-2.207 -5.243,-1.048c-13.085,8.724 -31.922,10.666 -46.874,4.832l-12.185,-4.753l-9.896,-15.836l22.488,0c6.23,0 12.518,-0.507 18.688,-1.507c12.711,-2.055 18.051,-4.855 22.993,-5.655c7.181,-1.163 14.516,0.274 20.677,3.97l29.625,17.775c1.79,1.074 4.112,0.494 5.187,-1.296c1.074,-1.79 0.494,-4.113 -1.296,-5.187l-5.377,-3.226c26.559,-29.893 25.139,-28.272 25.319,-28.511c3.102,-4.136 8.038,-6.605 13.205,-6.605c6.293,0 11.95,3.497 14.764,9.127l6.779,13.555l-1.444,-0Z"
|
||||
style="fill-rule:nonzero;"
|
||||
/></g
|
||||
></g
|
||||
></g
|
||||
><text
|
||||
x="195.418px"
|
||||
y="127.131px"
|
||||
style="font-family:'Sofia-Regular', 'Sofia';font-size:60.681px;">cryptogeon</text
|
||||
></g
|
||||
></svg
|
||||
>
|
||||
</a>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
a {
|
||||
border: none;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-top: calc(min(15vh, 6rem));
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
header svg {
|
||||
width: 100%;
|
||||
max-width: 16rem;
|
||||
transform: translateX(-1rem);
|
||||
}
|
||||
</style>
|
@@ -1,6 +1,8 @@
|
||||
<script lang="ts">
|
||||
import Footer from '$lib/views/Footer.svelte'
|
||||
import Header from '$lib/views/Header.svelte'
|
||||
|
||||
import '../app.css'
|
||||
import Header from '$lib/views/Header/index.svelte'
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -12,16 +14,9 @@
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<a href="/">/home</a>
|
||||
<a href="/about">/about</a>
|
||||
<a href="https://github.com/cupcakearmy/cryptgeon" target="_blank" rel="noopener">/code</a>
|
||||
</footer>
|
||||
<Footer />
|
||||
|
||||
<style>
|
||||
a {
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
main {
|
||||
padding: 1rem;
|
||||
padding-bottom: 4rem;
|
||||
@@ -29,17 +24,4 @@
|
||||
max-width: 35rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
background-color: var(--ui-bg-0-85);
|
||||
}
|
||||
</style>
|
||||
|
@@ -13,7 +13,7 @@
|
||||
<h1>About</h1>
|
||||
|
||||
<p>
|
||||
<i>cryptgeon</i> is an secure, open source sharing note service inspired by
|
||||
<i>cryptgeon</i> is a secure, open source sharing note service inspired by
|
||||
<a href="https://privnote.com"><i>PrivNote</i></a>.
|
||||
</p>
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
|
||||
<b>▶ Features</b>
|
||||
<ul>
|
||||
<li>view and time constrains</li>
|
||||
<li>server cannot decrypt contents due to client side encryption</li>
|
||||
<li>view and time constraints</li>
|
||||
<li>in memory, no persistence</li>
|
||||
<li>in browser encryption → server cannot decrypt contents</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
|
@@ -17,8 +17,8 @@
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
export let id: string
|
||||
let needPassword = false
|
||||
let password: string = ''
|
||||
export let password: string
|
||||
|
||||
let note: NotePublic | null = null
|
||||
let exists = false
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
try {
|
||||
loading = true
|
||||
error = null
|
||||
const data = await info(id)
|
||||
needPassword = data.password
|
||||
await info(id)
|
||||
exists = true
|
||||
} catch {
|
||||
exists = false
|
||||
@@ -40,17 +39,15 @@
|
||||
})
|
||||
|
||||
async function show() {
|
||||
const data = await get(id)
|
||||
if (needPassword) {
|
||||
try {
|
||||
const key = await getKeyFromString(password)
|
||||
data.contents = await decrypt(data.contents, key)
|
||||
error = false
|
||||
} catch {
|
||||
error = true
|
||||
}
|
||||
try {
|
||||
error = false
|
||||
const data = note || (await get(id)) // Don't get the content twice on wrong password.
|
||||
const key = await getKeyFromString(password)
|
||||
data.contents = await decrypt(data.contents, key)
|
||||
note = data
|
||||
} catch {
|
||||
error = true
|
||||
}
|
||||
note = data
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -67,14 +64,13 @@
|
||||
{:else}
|
||||
<form on:submit|preventDefault={show}>
|
||||
<p>click below to show and delete the note if the counter has reached it's limit</p>
|
||||
{#if needPassword}
|
||||
<TextInput type="password" label="password" bind:value={password} />
|
||||
<br />
|
||||
{/if}
|
||||
<Button type="submit">show note</Button>
|
||||
{#if error}
|
||||
<br />
|
||||
<p class="error-text">wrong password. could not decipher.</p>
|
||||
<p class="error-text">
|
||||
wrong password. could not decipher. probably a broken link. note was destroyed.
|
||||
<br />
|
||||
</p>
|
||||
{/if}
|
||||
</form>
|
||||
{/if}
|
1
client/static/icons/contrast-sharp.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns='http://www.w3.org/2000/svg' class='ionicon' viewBox='0 0 512 512'><title>Contrast</title><path d='M256 32C132.29 32 32 132.29 32 256s100.29 224 224 224 224-100.29 224-224S379.71 32 256 32zM128.72 383.28A180 180 0 01256 76v360a178.82 178.82 0 01-127.28-52.72z'/></svg>
|
After Width: | Height: | Size: 279 B |
BIN
client/static/icons/copy-sharp.svg
(Stored with Git LFS)
Before Width: | Height: | Size: 128 B After Width: | Height: | Size: 313 B |
BIN
client/static/icons/dice-sharp.svg
(Stored with Git LFS)
Before Width: | Height: | Size: 128 B After Width: | Height: | Size: 728 B |
BIN
client/static/icons/eye-off-sharp.svg
(Stored with Git LFS)
Before Width: | Height: | Size: 128 B After Width: | Height: | Size: 720 B |
BIN
client/static/icons/eye-sharp.svg
(Stored with Git LFS)
Before Width: | Height: | Size: 128 B After Width: | Height: | Size: 474 B |
BIN
client/static/icons/lock-closed-sharp.svg
(Stored with Git LFS)
Before Width: | Height: | Size: 128 B After Width: | Height: | Size: 275 B |
BIN
design/Github.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
design/Logo.afdesign
(Stored with Git LFS)
BIN
design/Logo.svg
(Stored with Git LFS)
Before Width: | Height: | Size: 129 B After Width: | Height: | Size: 3.8 KiB |
BIN
design/Screens.afdesign
(Stored with Git LFS)
Normal file
BIN
design/Screens.png
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
design/dove.svg
(Stored with Git LFS)
Before Width: | Height: | Size: 129 B After Width: | Height: | Size: 3.3 KiB |
BIN
design/github.svg
(Stored with Git LFS)
Before Width: | Height: | Size: 60 KiB |
@@ -12,5 +12,7 @@ services:
|
||||
|
||||
app:
|
||||
build: .
|
||||
depends_on:
|
||||
- memcached
|
||||
ports:
|
||||
- 80:5000
|
||||
|
1375
package-lock.json
generated
Normal file
11
package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"scripts": {
|
||||
"dev:docker": "docker-compose up memcached",
|
||||
"dev:backend": "cargo watch -x 'run --bin cryptgeon'",
|
||||
"dev:front": "npm --prefix client run dev",
|
||||
"dev": "run-p dev:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"npm-run-all": "^4.1.5"
|
||||
}
|
||||
}
|
@@ -5,15 +5,12 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct Note {
|
||||
pub contents: String,
|
||||
pub password: bool,
|
||||
pub views: Option<u8>,
|
||||
pub expiration: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct NoteInfo {
|
||||
pub password: bool,
|
||||
}
|
||||
pub struct NoteInfo {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct NotePublic {
|
||||
|
@@ -23,11 +23,7 @@ async fn one(path: web::Path<NotePath>) -> impl Responder {
|
||||
let note = store::get(&p.id);
|
||||
match note {
|
||||
None => return HttpResponse::NotFound().finish(),
|
||||
Some(note) => {
|
||||
return HttpResponse::Ok().json(NoteInfo {
|
||||
password: note.password,
|
||||
})
|
||||
}
|
||||
Some(_) => return HttpResponse::Ok().json(NoteInfo {}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +110,4 @@ pub fn init(cfg: &mut web::ServiceConfig) {
|
||||
.service(delete)
|
||||
.service(one),
|
||||
);
|
||||
// cfg.service(create);
|
||||
// cfg.service(delete);
|
||||
// cfg.service(one);
|
||||
}
|
||||
|