Compare commits

...

50 Commits

Author SHA1 Message Date
2def365cae quality of life improvemnts 2021-12-30 22:36:28 +01:00
c8b2539414 add version in about page 2021-12-22 15:20:30 +01:00
c8a25eb9bf release date 2021-12-22 14:57:58 +01:00
15bceb1715 add env 2021-12-22 14:56:33 +01:00
8acc4108ae Update README.md 2021-12-22 14:54:02 +01:00
0f708f53c0 changelog 2021-12-22 14:48:48 +01:00
8d03ad8e15 changelog 2021-12-22 14:47:43 +01:00
33829768eb time based fix 2021-12-22 14:46:06 +01:00
8cee6579e2 file upload 2021-12-22 13:10:08 +01:00
8eeb2a8de7 forgot missing packages 2021-12-21 10:35:02 +01:00
e4ce767444 add support for files 2021-12-21 00:15:04 +01:00
00fd514da5 feedback on to big error 2021-12-20 18:22:10 +01:00
ba38d2b819 proxy for cors 2021-12-20 18:14:59 +01:00
d0f83e6148 changelog 2021-12-20 17:54:17 +01:00
a040ad469e remove println 2021-12-20 17:54:09 +01:00
0c01866344 Merge pull request #15 from cupcakearmy/size-limit
Size limit
2021-12-20 17:45:05 +01:00
048c5198a2 Merge remote-tracking branch 'origin/main' into size-limit 2021-12-20 17:44:53 +01:00
f606916d97 Merge pull request #14 from cupcakearmy/pnpm
Pnpm
2021-12-20 17:43:11 +01:00
aea85c3b73 env docs 2021-12-20 17:42:35 +01:00
5f904b3971 cargo file 2021-12-20 17:42:30 +01:00
ac5d52a010 middleware to handle json payloads 2021-12-20 17:42:16 +01:00
8644a937d0 remove hardcoded limit 2021-12-20 17:42:08 +01:00
a0ebb97bc5 pnpm 2021-12-20 17:41:03 +01:00
19cd9b8507 examples on deployment 2021-12-16 13:54:15 +01:00
fe653e91c8 pnpm 2021-12-16 13:40:50 +01:00
a78ec72687 readme 2021-11-23 15:43:57 +01:00
a462bed948 1.2.0 2021-11-11 13:37:21 +01:00
325518ba15 Update README.md 2021-09-01 11:10:02 +02:00
4b80912727 Merge pull request #11 from stiivo/patch-1
Update README.md
2021-09-01 10:58:13 +02:00
Steven Günther
c78ad636c3 Update README.md
typo
2021-09-01 10:19:15 +02:00
4fe7833977 Update README.md 2021-06-01 12:23:08 +02:00
24f9aeb229 arm images 2021-05-19 11:29:33 +02:00
976413e11b limit min height 2021-05-17 09:45:01 +02:00
2480d875b4 about page 2021-05-16 12:47:52 +02:00
5dff12ea70 use hash instead of path for key 2021-05-16 11:16:25 +02:00
e332dc63e8 on push 2021-05-14 15:09:55 +02:00
a18e9bcc88 Merge pull request #7 from cupcakearmy/testing
use docker-compose
2021-05-14 14:48:56 +02:00
4b43edf54a use docker-compose 2021-05-14 14:48:34 +02:00
e3aa2dd5ff Update test.yml 2021-05-10 10:13:05 +02:00
98a03c25e6 Merge pull request #6 from cupcakearmy/testing
Testing
2021-05-10 10:11:59 +02:00
7f618e7e45 ci 2021-05-10 10:11:31 +02:00
84a7be4549 config 2021-05-10 10:04:19 +02:00
b2bad5f64c cypress runner 2021-05-10 09:58:21 +02:00
41f55c0920 test ids 2021-05-10 09:58:13 +02:00
edbf8a8ecf changelog 2021-05-08 21:47:13 +02:00
4852804581 time bug 2021-05-08 21:47:08 +02:00
22b1c35b3e loading state 2021-05-08 21:46:52 +02:00
d1e9ffd89b up the iterations 2021-05-08 21:46:43 +02:00
9c675ba48c notes about availability 2021-05-08 21:46:33 +02:00
ef3d3d5bde changelog 2021-05-08 10:34:18 +02:00
41 changed files with 2237 additions and 2755 deletions

View File

@@ -14,6 +14,8 @@ jobs:
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
with:
install: true
- name: Docker Labels
id: meta
uses: crazy-max/ghaction-docker-meta@v2
@@ -32,7 +34,7 @@ jobs:
id: docker_build
uses: docker/build-push-action@v2
with:
# platforms: linux/amd64,linux/arm64
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
- name: Image digest

1
.gitignore vendored
View File

@@ -8,3 +8,4 @@ node_modules
/.svelte
/build
/functions
.env

View File

@@ -5,76 +5,137 @@ 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.9] - 2021-05-08
## [1.3.1] - 2021-12-30
### Added
- Short explanation in the home page.
### Changed
- Explanation in about & readme.
- Shorten server ids from 512 to 256bit.
## [1.3.0] - 2021-12-22
### Added
- Option to set a custom size limit.
- Options to share files.
### Changed
- Don't delete note if time is not expired yet
- Use pnpm instead of npm.
## [1.2.0] - 2021-11-11
### Changed
- Switch to pnpm.
### Security
- Dependencies updated.
## [1.1.1] - 2021-05-17
### Fixed
- API endpoint was not reachable
- Height on big displays.
- About page.
## [1.1.0] - 2021-05-16
### Security
- Using hash `#` instead of path.
## [1.0.11] - 2021-05-08
### Added
- loading text.
- description for created notes about availability.
### Changed
- iterations from 100 to 100k.
### Fixed
- time based view bug.
## [1.0.10] - 2021-05-08
### Fixed
- API endpoint was not reachable.
## [1.0.9] - 2021-05-07
## Changed
- Removed a dependency
- Removed a dependency.
## [1.0.8] - 2021-05-05
### Added
- Manual theme override option
- Manual theme override option.
### Fixed
- Removed Arm builds for now
- iOS style bugs
- Removed Arm builds for now.
- iOS style bugs.
## [1.0.7] - 2021-05-04
### Added
- Arm images
- Arm images.
## [1.0.6] - 2021-05-04
### Added
- Always use encryption with random passwords included links
- Always use encryption with random passwords included links.
## [1.0.5] - 2021-05-03
### Fixed
- Typos
- Typos.
## [1.0.4] - 2021-05-02
### Added
- From scratch docker image
- From scratch docker image.
## [1.0.3] - 2021-05-02
### Fixed
- Higher default text area
- Mobile touchups
- Higher default text area.
- Mobile touchups.
## [1.0.2] - 2021-05-02
### Fixed
- SVG Icons
- SVG Icons.
## [1.0.1] - 2021-05-02
### Added
- Dark mode support
- Dark mode support.
### Fixed
- Don't reload data on wrong password
- Don't reload data on wrong password.
## [1.0.0] - 2021-05-02
Initial release
Initial release.

512
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "cryptgeon"
version = "1.0.0"
version = "1.3.0"
authors = ["cupcakearmy <hi@nicco.io>"]
edition = "2018"
@@ -18,4 +18,7 @@ serde_json = "1"
lazy_static = "1"
ring = "0.16"
bs62 = "0.1"
memcache = "0.15"
memcache = "0.16"
byte-unit = "4"
dotenv = "0.15"
mime = "0.3"

View File

@@ -3,8 +3,9 @@ FROM node:16-alpine as CLIENT
WORKDIR /tmp
COPY ./client ./
RUN npm ci
RUN npm run build
RUN npm install -g pnpm
RUN pnpm install
RUN pnpm run build
FROM rust:1.51-alpine as RUST

View File

@@ -1,14 +1,21 @@
<p align="center">
<img src="./design/Github.png">
<img src="./design/Github.png" alt="logo">
</p>
![Docker pulls badge](https://img.shields.io/docker/pulls/cupcakearmy/cryptgeon)
![Docker image size badge](https://img.shields.io/docker/image-size/cupcakearmy/cryptgeon)
![Latest version](https://img.shields.io/github/v/release/cupcakearmy/cryptgeon)
<a href="https://discord.gg/nuby6RnxZt">
<img alt="discord" src="https://img.shields.io/discord/252403122348097536?style=for-the-badge" />
<img alt="docker pulls" src="https://img.shields.io/docker/pulls/cupcakearmy/cryptgeon?style=for-the-badge" />
<img alt="Docker image size badge" src="https://img.shields.io/docker/image-size/cupcakearmy/cryptgeon?style=for-the-badge" />
<img alt="Latest version" src="https://img.shields.io/github/v/release/cupcakearmy/cryptgeon?style=for-the-badge" />
</a>
<br/>
<a href="https://www.producthunt.com/posts/cryptgeon?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-cryptgeon" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=295189&theme=light" alt="Cryptgeon - Securely share self-destructing notes | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<br/>
## About?
_cryptgeon_ is a secure, open source sharing note service inspired by [_PrivNote_](https://privnote.com)
_cryptgeon_ is a secure, open source sharing note or file service inspired by [_PrivNote_](https://privnote.com)
## Demo
@@ -17,20 +24,36 @@ Check out the demo and see for yourself https://cryptgeon.nicco.io.
## Features
- server cannot decrypt contents due to client side encryption
- view and time constraints
- view or time constraints
- in memory, no persistence
- obligatory dark mode support
## How does it work?
each note has a 512bit generated <i>id</i> that is used to retrieve the note. data is stored in memory and never persisted to disk.
each note has a generated <code>id (256bit)</code> and <code>key 256(bit)</code>. The
<code>id</code>
is used to save & retrieve the note. the note is then encrypted with aes in gcm mode on the
client side with the <code>key</code> and then sent to the server. data is stored in memory and
never persisted to disk. the server never sees the encryption key and cannot decrypt the contents
of the notes even if it tried to.
## Screenshot
![screenshot](./design/Screens.png)
## Environment Variables
| Variable | Default | Description |
| ------------ | ----------------- | --------------------------------------------------------------------------------------- |
| `MEMCACHE` | `memcached:11211` | Memcached URL to connect to. |
| `SIZE_LIMIT` | `1 KiB` | Max size for body. Accepted values according to [byte-unit](https://docs.rs/byte-unit/) |
## Deployment
`https` is required otherwise browsers will not support the cryptographic functions.
### Docker
Docker is the easiest way. There is the [official image here](https://hub.docker.com/r/cupcakearmy/cryptgeon).
```yaml
@@ -41,21 +64,64 @@ version: '3.7'
services:
memcached:
image: memcached:1-alpine
entrypoint: memcached -m 128 # Limit to 128 MB Ram, customize at free will.
entrypoint: memcached -m 128M -I 4M # Limit to 128 MB Ram, 4M per entry, customize at free will.
app:
image: cupcakearmy/cryptgeon:latest
depends_on:
- memcache
- memcached
environment:
SIZE_LIMIT: 4M
ports:
- 80:5000
```
### NGINX Proxy
See the [examples/nginx](https://github.com/cupcakearmy/cryptgeon/tree/main/examples/nginx) folder. There an example with a simple proxy, and one with https. You need to specify the server names and certificates.
### Traefik 2
Assumptions:
- External proxy docker network `proxy`
- A certificate resolver `le`
- A https entrypoint `secure`
- Domain name `example.org`
```yaml
version: '3.8'
networks:
proxy:
external: true
services:
memcached:
image: memcached:1-alpine
restart: unless-stopped
entrypoint: memcached -m 128M -I 4M # Limit to 128 MB Ram, 4M per entry, customize at free will.
app:
image: cupcakearmy/cryptgeon:latest
restart: unless-stopped
depends_on:
- memcached
networks:
- default
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.cryptgeon.rule=Host(`example.org`)
- traefik.http.routers.cryptgeon.entrypoints=secure
- traefik.http.routers.cryptgeon.tls.certresolver=le
```
## Development
1. Clone
2. run `npm i` in the root and and client `client/` folders.
3. Run `npm run dev` to start development.
2. run `pnpm i` in the root and and client `client/` folders.
3. Run `pnpm run dev` to start development.
Running `npm run dev` in the root folder will start the following things
@@ -63,7 +129,7 @@ Running `npm run dev` in the root folder will start the following things
- rust backend with hot reload
- client with hot reload
You can see the app under [localhost:3000](http://localhost:3000).
You can see the app under [localhost:1234](http://localhost:1234).
###### Attributions

View File

@@ -1,4 +1,4 @@
├─ MIT: 43
├─ MIT: 46
├─ MIT*: 2
├─ BSD-3-Clause: 2
├─ ISC: 1
1 ├─ MIT: 43 ├─ MIT: 46
2 ├─ MIT*: 2 ├─ MIT*: 2
3 ├─ BSD-3-Clause: 2 ├─ BSD-3-Clause: 2
4 ├─ ISC: 1 ├─ ISC: 1

955
client/package-lock.json generated
View File

@@ -1,955 +0,0 @@
{
"name": "client",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"dependencies": {
"@fontsource/fira-mono": "^4.2.2",
"copy-to-clipboard": "^3.3.1"
},
"devDependencies": {
"@sveltejs/adapter-static": "next",
"@sveltejs/kit": "next",
"svelte": "^3.34.0",
"svelte-preprocess": "^4.0.0",
"tslib": "^2.0.0",
"typescript": "^4.0.0",
"vite": "^2.1.0"
}
},
"node_modules/@fontsource/fira-mono": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@fontsource/fira-mono/-/fira-mono-4.2.2.tgz",
"integrity": "sha512-t2WRThg+eLkQNQCtPG2sCCq40lz3xeb7nsL7P8l4+wfSRbdLQXAY5IebMftI2YEZR4MRRhdgrg0p5fi/2yXypA=="
},
"node_modules/@rollup/pluginutils": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.0.tgz",
"integrity": "sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ==",
"dev": true,
"dependencies": {
"estree-walker": "^2.0.1",
"picomatch": "^2.2.2"
},
"engines": {
"node": ">= 8.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0"
}
},
"node_modules/@sveltejs/adapter-static": {
"version": "1.0.0-next.8",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-1.0.0-next.8.tgz",
"integrity": "sha512-goE3v68y+pn+fayPDJCCQ7W1QQpSMl86pZS9RvjuG64+TEI/PGsZwYR1RRgvwIiDqu33wBEHj+3ZYHEpJO/gwg==",
"dev": true
},
"node_modules/@sveltejs/kit": {
"version": "1.0.0-next.101",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.101.tgz",
"integrity": "sha512-SwUImLhFmyaDsq7LKRJXPJRIOPa06SWENG7heko5FTRRLMpI/UDFcijjT2ln0Fp+AL9XfSbTHO8QrOflCMbfiQ==",
"dev": true,
"dependencies": {
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.9",
"cheap-watch": "^1.0.3",
"sade": "^1.7.4"
},
"bin": {
"svelte-kit": "svelte-kit.js"
},
"engines": {
"node": ">= 12.17.0"
},
"peerDependencies": {
"svelte": "^3.34.0",
"vite": "^2.2.3"
}
},
"node_modules/@sveltejs/vite-plugin-svelte": {
"version": "1.0.0-next.9",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.9.tgz",
"integrity": "sha512-ySB/GJsZV3h3jqjq5WIiaxVFkJK6vqtG9gS7Iw6SfUH9ZiFNw5JjQF69g68j9cNep3q4yRIYiG5/pI3YIdXEuA==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^4.1.0",
"chalk": "^4.1.1",
"debug": "^4.3.2",
"hash-sum": "^2.0.0",
"require-relative": "^0.8.7",
"slash": "^4.0.0",
"source-map": "^0.7.3",
"svelte-hmr": "^0.14.2"
},
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"svelte": "^3.37.0",
"vite": "^2.2.3"
}
},
"node_modules/@types/node": {
"version": "15.0.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz",
"integrity": "sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==",
"dev": true
},
"node_modules/@types/pug": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.4.tgz",
"integrity": "sha1-h3L80EGOPNLMFxVV1zAHQVBR9LI=",
"dev": true
},
"node_modules/@types/sass": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/@types/sass/-/sass-1.16.0.tgz",
"integrity": "sha512-2XZovu4NwcqmtZtsBR5XYLw18T8cBCnU2USFHTnYLLHz9fkhnoEMoDsqShJIOFsFhn5aJHjweiUUdTrDGujegA==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/chalk": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
"integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
"dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/cheap-watch": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/cheap-watch/-/cheap-watch-1.0.3.tgz",
"integrity": "sha512-xC5CruMhLzjPwJ5ecUxGu1uGmwJQykUhqd2QrCrYbwvsFYdRyviu6jG9+pccwDXJR/OpmOTOJ9yLFunVgQu9wg==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"node_modules/colorette": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
"integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==",
"dev": true
},
"node_modules/copy-to-clipboard": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz",
"integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==",
"dependencies": {
"toggle-selection": "^1.0.6"
}
},
"node_modules/debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"dev": true,
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/detect-indent": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz",
"integrity": "sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/esbuild": {
"version": "0.9.7",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.9.7.tgz",
"integrity": "sha512-VtUf6aQ89VTmMLKrWHYG50uByMF4JQlVysb8dmg6cOgW8JnFCipmz7p+HNBl+RR3LLCuBxFGVauAe2wfnF9bLg==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/hash-sum": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz",
"integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==",
"dev": true
},
"node_modules/is-core-module": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.3.0.tgz",
"integrity": "sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==",
"dev": true,
"dependencies": {
"has": "^1.0.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/mri": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.1.6.tgz",
"integrity": "sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"node_modules/nanoid": {
"version": "3.1.22",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz",
"integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==",
"dev": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
},
"node_modules/picomatch": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz",
"integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==",
"dev": true,
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/postcss": {
"version": "8.2.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.14.tgz",
"integrity": "sha512-+jD0ZijcvyCqPQo/m/CW0UcARpdFylq04of+Q7RKX6f/Tu+dvpUI/9Sp81+i6/vJThnOBX09Quw0ZLOVwpzX3w==",
"dev": true,
"dependencies": {
"colorette": "^1.2.2",
"nanoid": "^3.1.22",
"source-map": "^0.6.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
}
},
"node_modules/postcss/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/require-relative": {
"version": "0.8.7",
"resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
"integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=",
"dev": true
},
"node_modules/resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
"dev": true,
"dependencies": {
"is-core-module": "^2.2.0",
"path-parse": "^1.0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/rollup": {
"version": "2.47.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.47.0.tgz",
"integrity": "sha512-rqBjgq9hQfW0vRmz+0S062ORRNJXvwRpzxhFXORvar/maZqY6za3rgQ/p1Glg+j1hnc1GtYyQCPiAei95uTElg==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=10.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.1"
}
},
"node_modules/sade": {
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/sade/-/sade-1.7.4.tgz",
"integrity": "sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA==",
"dev": true,
"dependencies": {
"mri": "^1.1.0"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/slash": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz",
"integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true,
"engines": {
"node": ">= 8"
}
},
"node_modules/strip-indent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
"integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
"dev": true,
"dependencies": {
"min-indent": "^1.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/svelte": {
"version": "3.38.2",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.38.2.tgz",
"integrity": "sha512-q5Dq0/QHh4BLJyEVWGe7Cej5NWs040LWjMbicBGZ+3qpFWJ1YObRmUDZKbbovddLC9WW7THTj3kYbTOFmU9fbg==",
"dev": true,
"engines": {
"node": ">= 8"
}
},
"node_modules/svelte-hmr": {
"version": "0.14.3",
"resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.14.3.tgz",
"integrity": "sha512-N56xX405zLMw2tpGHKRx5h+kmdeZwxI21pvyC6OyBHJDCF6DlwWBm9TifdQmSD4dloWSmpDPzHWYa3CSjfopUg==",
"dev": true,
"peerDependencies": {
"svelte": ">=3.19.0"
}
},
"node_modules/svelte-preprocess": {
"version": "4.7.3",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.7.3.tgz",
"integrity": "sha512-Zx1/xLeGOIBlZMGPRCaXtlMe4ZA0faato5Dc3CosEqwu75MIEPuOstdkH6cy+RYTUYynoxzNaDxkPX4DbrPwRA==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@types/pug": "^2.0.4",
"@types/sass": "^1.16.0",
"detect-indent": "^6.0.0",
"strip-indent": "^3.0.0"
},
"engines": {
"node": ">= 9.11.2"
},
"peerDependencies": {
"@babel/core": "^7.10.2",
"coffeescript": "^2.5.1",
"less": "^3.11.3",
"postcss": "^7 || ^8",
"postcss-load-config": "^2.1.0 || ^3.0.0",
"pug": "^3.0.0",
"sass": "^1.26.8",
"stylus": "^0.54.7",
"sugarss": "^2.0.0",
"svelte": "^3.23.0",
"typescript": "^3.9.5 || ^4.0.0"
},
"peerDependenciesMeta": {
"@babel/core": {
"optional": true
},
"coffeescript": {
"optional": true
},
"less": {
"optional": true
},
"node-sass": {
"optional": true
},
"postcss": {
"optional": true
},
"postcss-load-config": {
"optional": true
},
"pug": {
"optional": true
},
"sass": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"typescript": {
"optional": true
}
}
},
"node_modules/toggle-selection": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
"integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
},
"node_modules/tslib": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==",
"dev": true
},
"node_modules/typescript": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
"integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/vite": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.2.4.tgz",
"integrity": "sha512-vnIwSNci+phFMp6krhy+FbYzKL0R67Sdt9mVZ96S27AewrApSJjTqncJcalk8sf60BgcbW4+1C6DFIWkxquO9g==",
"dev": true,
"dependencies": {
"esbuild": "^0.9.3",
"postcss": "^8.2.1",
"resolve": "^1.19.0",
"rollup": "^2.38.5"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": ">=12.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.1"
}
}
},
"dependencies": {
"@fontsource/fira-mono": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@fontsource/fira-mono/-/fira-mono-4.2.2.tgz",
"integrity": "sha512-t2WRThg+eLkQNQCtPG2sCCq40lz3xeb7nsL7P8l4+wfSRbdLQXAY5IebMftI2YEZR4MRRhdgrg0p5fi/2yXypA=="
},
"@rollup/pluginutils": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.0.tgz",
"integrity": "sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ==",
"dev": true,
"requires": {
"estree-walker": "^2.0.1",
"picomatch": "^2.2.2"
}
},
"@sveltejs/adapter-static": {
"version": "1.0.0-next.8",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-1.0.0-next.8.tgz",
"integrity": "sha512-goE3v68y+pn+fayPDJCCQ7W1QQpSMl86pZS9RvjuG64+TEI/PGsZwYR1RRgvwIiDqu33wBEHj+3ZYHEpJO/gwg==",
"dev": true
},
"@sveltejs/kit": {
"version": "1.0.0-next.101",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.101.tgz",
"integrity": "sha512-SwUImLhFmyaDsq7LKRJXPJRIOPa06SWENG7heko5FTRRLMpI/UDFcijjT2ln0Fp+AL9XfSbTHO8QrOflCMbfiQ==",
"dev": true,
"requires": {
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.9",
"cheap-watch": "^1.0.3",
"sade": "^1.7.4"
}
},
"@sveltejs/vite-plugin-svelte": {
"version": "1.0.0-next.9",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.9.tgz",
"integrity": "sha512-ySB/GJsZV3h3jqjq5WIiaxVFkJK6vqtG9gS7Iw6SfUH9ZiFNw5JjQF69g68j9cNep3q4yRIYiG5/pI3YIdXEuA==",
"dev": true,
"requires": {
"@rollup/pluginutils": "^4.1.0",
"chalk": "^4.1.1",
"debug": "^4.3.2",
"hash-sum": "^2.0.0",
"require-relative": "^0.8.7",
"slash": "^4.0.0",
"source-map": "^0.7.3",
"svelte-hmr": "^0.14.2"
}
},
"@types/node": {
"version": "15.0.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz",
"integrity": "sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==",
"dev": true
},
"@types/pug": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.4.tgz",
"integrity": "sha1-h3L80EGOPNLMFxVV1zAHQVBR9LI=",
"dev": true
},
"@types/sass": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/@types/sass/-/sass-1.16.0.tgz",
"integrity": "sha512-2XZovu4NwcqmtZtsBR5XYLw18T8cBCnU2USFHTnYLLHz9fkhnoEMoDsqShJIOFsFhn5aJHjweiUUdTrDGujegA==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
"integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"cheap-watch": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/cheap-watch/-/cheap-watch-1.0.3.tgz",
"integrity": "sha512-xC5CruMhLzjPwJ5ecUxGu1uGmwJQykUhqd2QrCrYbwvsFYdRyviu6jG9+pccwDXJR/OpmOTOJ9yLFunVgQu9wg==",
"dev": true
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"colorette": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
"integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==",
"dev": true
},
"copy-to-clipboard": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz",
"integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==",
"requires": {
"toggle-selection": "^1.0.6"
}
},
"debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"dev": true,
"requires": {
"ms": "2.1.2"
}
},
"detect-indent": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz",
"integrity": "sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==",
"dev": true
},
"esbuild": {
"version": "0.9.7",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.9.7.tgz",
"integrity": "sha512-VtUf6aQ89VTmMLKrWHYG50uByMF4JQlVysb8dmg6cOgW8JnFCipmz7p+HNBl+RR3LLCuBxFGVauAe2wfnF9bLg==",
"dev": true
},
"estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"optional": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"hash-sum": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz",
"integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==",
"dev": true
},
"is-core-module": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.3.0.tgz",
"integrity": "sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==",
"dev": true,
"requires": {
"has": "^1.0.3"
}
},
"min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
"dev": true
},
"mri": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.1.6.tgz",
"integrity": "sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==",
"dev": true
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"nanoid": {
"version": "3.1.22",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz",
"integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==",
"dev": true
},
"path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
},
"picomatch": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz",
"integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==",
"dev": true
},
"postcss": {
"version": "8.2.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.14.tgz",
"integrity": "sha512-+jD0ZijcvyCqPQo/m/CW0UcARpdFylq04of+Q7RKX6f/Tu+dvpUI/9Sp81+i6/vJThnOBX09Quw0ZLOVwpzX3w==",
"dev": true,
"requires": {
"colorette": "^1.2.2",
"nanoid": "^3.1.22",
"source-map": "^0.6.1"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"require-relative": {
"version": "0.8.7",
"resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
"integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=",
"dev": true
},
"resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
"dev": true,
"requires": {
"is-core-module": "^2.2.0",
"path-parse": "^1.0.6"
}
},
"rollup": {
"version": "2.47.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.47.0.tgz",
"integrity": "sha512-rqBjgq9hQfW0vRmz+0S062ORRNJXvwRpzxhFXORvar/maZqY6za3rgQ/p1Glg+j1hnc1GtYyQCPiAei95uTElg==",
"dev": true,
"requires": {
"fsevents": "~2.3.1"
}
},
"sade": {
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/sade/-/sade-1.7.4.tgz",
"integrity": "sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA==",
"dev": true,
"requires": {
"mri": "^1.1.0"
}
},
"slash": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz",
"integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==",
"dev": true
},
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true
},
"strip-indent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
"integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
"dev": true,
"requires": {
"min-indent": "^1.0.0"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"svelte": {
"version": "3.38.2",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.38.2.tgz",
"integrity": "sha512-q5Dq0/QHh4BLJyEVWGe7Cej5NWs040LWjMbicBGZ+3qpFWJ1YObRmUDZKbbovddLC9WW7THTj3kYbTOFmU9fbg==",
"dev": true
},
"svelte-hmr": {
"version": "0.14.3",
"resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.14.3.tgz",
"integrity": "sha512-N56xX405zLMw2tpGHKRx5h+kmdeZwxI21pvyC6OyBHJDCF6DlwWBm9TifdQmSD4dloWSmpDPzHWYa3CSjfopUg==",
"dev": true,
"requires": {}
},
"svelte-preprocess": {
"version": "4.7.3",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.7.3.tgz",
"integrity": "sha512-Zx1/xLeGOIBlZMGPRCaXtlMe4ZA0faato5Dc3CosEqwu75MIEPuOstdkH6cy+RYTUYynoxzNaDxkPX4DbrPwRA==",
"dev": true,
"requires": {
"@types/pug": "^2.0.4",
"@types/sass": "^1.16.0",
"detect-indent": "^6.0.0",
"strip-indent": "^3.0.0"
}
},
"toggle-selection": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
"integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
},
"tslib": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==",
"dev": true
},
"typescript": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
"integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==",
"dev": true
},
"vite": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.2.4.tgz",
"integrity": "sha512-vnIwSNci+phFMp6krhy+FbYzKL0R67Sdt9mVZ96S27AewrApSJjTqncJcalk8sf60BgcbW4+1C6DFIWkxquO9g==",
"dev": true,
"requires": {
"esbuild": "^0.9.3",
"fsevents": "~2.3.1",
"postcss": "^8.2.1",
"resolve": "^1.19.0",
"rollup": "^2.38.5"
}
}
}
}

View File

@@ -4,20 +4,22 @@
"dev": "svelte-kit dev",
"build": "svelte-kit build",
"preview": "svelte-kit preview",
"licenses": "npx license-checker --summary > licenses.csv"
},
"devDependencies": {
"@sveltejs/adapter-static": "next",
"@sveltejs/kit": "next",
"svelte": "^3.34.0",
"svelte-preprocess": "^4.0.0",
"tslib": "^2.0.0",
"typescript": "^4.0.0",
"vite": "^2.1.0"
"licenses": "license-checker --summary > licenses.csv"
},
"type": "module",
"devDependencies": {
"@sveltejs/adapter-static": "^1.0.0-next.22",
"@sveltejs/kit": "^1.0.0-next.202",
"svelte": "^3.44.3",
"svelte-preprocess": "^4.10.1",
"tslib": "^2.3.1",
"typescript": "^4.5.4",
"vite": "^2.7.4"
},
"dependencies": {
"@fontsource/fira-mono": "^4.2.2",
"copy-to-clipboard": "^3.3.1"
"@fontsource/fira-mono": "^4.5.0",
"copy-to-clipboard": "^3.3.1",
"file-saver": "^2.0.5",
"pretty-bytes": "^5.6.0"
}
}

638
client/pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,638 @@
lockfileVersion: 5.3
specifiers:
'@fontsource/fira-mono': ^4.5.0
'@sveltejs/adapter-static': ^1.0.0-next.22
'@sveltejs/kit': ^1.0.0-next.202
copy-to-clipboard: ^3.3.1
file-saver: ^2.0.5
pretty-bytes: ^5.6.0
svelte: ^3.44.3
svelte-preprocess: ^4.10.1
tslib: ^2.3.1
typescript: ^4.5.4
vite: ^2.7.4
dependencies:
'@fontsource/fira-mono': 4.5.0
copy-to-clipboard: 3.3.1
file-saver: 2.0.5
pretty-bytes: 5.6.0
devDependencies:
'@sveltejs/adapter-static': 1.0.0-next.22
'@sveltejs/kit': 1.0.0-next.202_svelte@3.44.3
svelte: 3.44.3
svelte-preprocess: 4.10.1_svelte@3.44.3+typescript@4.5.4
tslib: 2.3.1
typescript: 4.5.4
vite: 2.7.4
packages:
/@fontsource/fira-mono/4.5.0:
resolution: {integrity: sha512-KE+d3wmgq/YKM0BqgUF7p2yeBNi805Nfof1lC1wJ7E9i2EWoC363sGdKG+MQBVm+ei3GYZu+Bo8Xha1w1pkB7g==}
dev: false
/@rollup/pluginutils/4.1.2:
resolution: {integrity: sha512-ROn4qvkxP9SyPeHaf7uQC/GPFY6L/OWy9+bd9AwcjOAWQwxRscoEyAUD8qCY5o5iL4jqQwoLk2kaTKJPb/HwzQ==}
engines: {node: '>= 8.0.0'}
dependencies:
estree-walker: 2.0.2
picomatch: 2.3.0
dev: true
/@sveltejs/adapter-static/1.0.0-next.22:
resolution: {integrity: sha512-Dc1V9Z72dA7caVwNxxzl9Jhcq4uN9N1udA2GKNTLMu3aWX3Cq+v6C2CddY9Aazr+F9h6J0vi9AienuH+ySRXzQ==}
dev: true
/@sveltejs/kit/1.0.0-next.202_svelte@3.44.3:
resolution: {integrity: sha512-rXmJ0FplkWvD1CaeCfejRYhOJYrlmeUm5Fkw7gIKDdWPQev5rqOhd9B9ZvRpq35oMqCAwaOfK+e5S6k+83feEQ==}
engines: {node: '>=14.13'}
hasBin: true
peerDependencies:
svelte: ^3.44.0
dependencies:
'@sveltejs/vite-plugin-svelte': 1.0.0-next.32_svelte@3.44.3+vite@2.7.4
cheap-watch: 1.0.4
sade: 1.7.4
svelte: 3.44.3
vite: 2.7.4
transitivePeerDependencies:
- diff-match-patch
- less
- sass
- stylus
- supports-color
dev: true
/@sveltejs/vite-plugin-svelte/1.0.0-next.32_svelte@3.44.3+vite@2.7.4:
resolution: {integrity: sha512-Lhf5BxVylosHIW6U2s6WDQA39ycd+bXivC8gHsXCJeLzxoHj7Pv7XAOk25xRSXT4wHg9DWFMBQh2DFU0DxHZ2g==}
engines: {node: ^14.13.1 || >= 16}
peerDependencies:
diff-match-patch: ^1.0.5
svelte: ^3.44.0
vite: ^2.7.0
peerDependenciesMeta:
diff-match-patch:
optional: true
dependencies:
'@rollup/pluginutils': 4.1.2
debug: 4.3.3
kleur: 4.1.4
magic-string: 0.25.7
require-relative: 0.8.7
svelte: 3.44.3
svelte-hmr: 0.14.7_svelte@3.44.3
vite: 2.7.4
transitivePeerDependencies:
- supports-color
dev: true
/@types/node/17.0.1:
resolution: {integrity: sha512-NXKvBVUzIbs6ylBwmOwHFkZS2EXCcjnqr8ZCRNaXBkHAf+3mn/rPcJxwrzuc6movh8fxQAsUUfYklJ/EG+hZqQ==}
dev: true
/@types/pug/2.0.5:
resolution: {integrity: sha512-LOnASQoeNZMkzexRuyqcBBDZ6rS+rQxUMkmj5A0PkhhiSZivLIuz6Hxyr1mkGoEZEkk66faROmpMi4fFkrKsBA==}
dev: true
/@types/sass/1.43.1:
resolution: {integrity: sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==}
dependencies:
'@types/node': 17.0.1
dev: true
/balanced-match/1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
dev: true
/brace-expansion/1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
dev: true
/buffer-crc32/0.2.13:
resolution: {integrity: sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=}
dev: true
/cheap-watch/1.0.4:
resolution: {integrity: sha512-QR/9FrtRL5fjfUJBhAKCdi0lSRQ3rVRRum3GF9wDKp2TJbEIMGhUEr2yU8lORzm9Isdjx7/k9S0DFDx+z5VGtw==}
engines: {node: '>=8'}
dev: true
/concat-map/0.0.1:
resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
dev: true
/copy-to-clipboard/3.3.1:
resolution: {integrity: sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==}
dependencies:
toggle-selection: 1.0.6
dev: false
/debug/4.3.3:
resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.2
dev: true
/detect-indent/6.1.0:
resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
engines: {node: '>=8'}
dev: true
/es6-promise/3.3.1:
resolution: {integrity: sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=}
dev: true
/esbuild-android-arm64/0.13.15:
resolution: {integrity: sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg==}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-64/0.13.15:
resolution: {integrity: sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ==}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-arm64/0.13.15:
resolution: {integrity: sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ==}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-64/0.13.15:
resolution: {integrity: sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA==}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-arm64/0.13.15:
resolution: {integrity: sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ==}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-32/0.13.15:
resolution: {integrity: sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g==}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-64/0.13.15:
resolution: {integrity: sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA==}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm/0.13.15:
resolution: {integrity: sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA==}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm64/0.13.15:
resolution: {integrity: sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA==}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-mips64le/0.13.15:
resolution: {integrity: sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg==}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-ppc64le/0.13.15:
resolution: {integrity: sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ==}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-netbsd-64/0.13.15:
resolution: {integrity: sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w==}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-openbsd-64/0.13.15:
resolution: {integrity: sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g==}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-sunos-64/0.13.15:
resolution: {integrity: sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw==}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-32/0.13.15:
resolution: {integrity: sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw==}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-64/0.13.15:
resolution: {integrity: sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-arm64/0.13.15:
resolution: {integrity: sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA==}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild/0.13.15:
resolution: {integrity: sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==}
hasBin: true
requiresBuild: true
optionalDependencies:
esbuild-android-arm64: 0.13.15
esbuild-darwin-64: 0.13.15
esbuild-darwin-arm64: 0.13.15
esbuild-freebsd-64: 0.13.15
esbuild-freebsd-arm64: 0.13.15
esbuild-linux-32: 0.13.15
esbuild-linux-64: 0.13.15
esbuild-linux-arm: 0.13.15
esbuild-linux-arm64: 0.13.15
esbuild-linux-mips64le: 0.13.15
esbuild-linux-ppc64le: 0.13.15
esbuild-netbsd-64: 0.13.15
esbuild-openbsd-64: 0.13.15
esbuild-sunos-64: 0.13.15
esbuild-windows-32: 0.13.15
esbuild-windows-64: 0.13.15
esbuild-windows-arm64: 0.13.15
dev: true
/estree-walker/2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
dev: true
/file-saver/2.0.5:
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
dev: false
/fs.realpath/1.0.0:
resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=}
dev: true
/fsevents/2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
dev: true
optional: true
/function-bind/1.1.1:
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
dev: true
/glob/7.2.0:
resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==}
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
inherits: 2.0.4
minimatch: 3.0.4
once: 1.4.0
path-is-absolute: 1.0.1
dev: true
/graceful-fs/4.2.8:
resolution: {integrity: sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==}
dev: true
/has/1.0.3:
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
engines: {node: '>= 0.4.0'}
dependencies:
function-bind: 1.1.1
dev: true
/inflight/1.0.6:
resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=}
dependencies:
once: 1.4.0
wrappy: 1.0.2
dev: true
/inherits/2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
dev: true
/is-core-module/2.8.0:
resolution: {integrity: sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==}
dependencies:
has: 1.0.3
dev: true
/kleur/4.1.4:
resolution: {integrity: sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==}
engines: {node: '>=6'}
dev: true
/magic-string/0.25.7:
resolution: {integrity: sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==}
dependencies:
sourcemap-codec: 1.4.8
dev: true
/min-indent/1.0.1:
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
engines: {node: '>=4'}
dev: true
/minimatch/3.0.4:
resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==}
dependencies:
brace-expansion: 1.1.11
dev: true
/minimist/1.2.5:
resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==}
dev: true
/mkdirp/0.5.5:
resolution: {integrity: sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==}
hasBin: true
dependencies:
minimist: 1.2.5
dev: true
/mri/1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
engines: {node: '>=4'}
dev: true
/ms/2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true
/nanoid/3.1.30:
resolution: {integrity: sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
dev: true
/once/1.4.0:
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
dependencies:
wrappy: 1.0.2
dev: true
/path-is-absolute/1.0.1:
resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=}
engines: {node: '>=0.10.0'}
dev: true
/path-parse/1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
dev: true
/picocolors/1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
dev: true
/picomatch/2.3.0:
resolution: {integrity: sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==}
engines: {node: '>=8.6'}
dev: true
/postcss/8.4.5:
resolution: {integrity: sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.1.30
picocolors: 1.0.0
source-map-js: 1.0.1
dev: true
/pretty-bytes/5.6.0:
resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
engines: {node: '>=6'}
dev: false
/require-relative/0.8.7:
resolution: {integrity: sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=}
dev: true
/resolve/1.20.0:
resolution: {integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==}
dependencies:
is-core-module: 2.8.0
path-parse: 1.0.7
dev: true
/rimraf/2.7.1:
resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
hasBin: true
dependencies:
glob: 7.2.0
dev: true
/rollup/2.61.1:
resolution: {integrity: sha512-BbTXlEvB8d+XFbK/7E5doIcRtxWPRiqr0eb5vQ0+2paMM04Ye4PZY5nHOQef2ix24l/L0SpLd5hwcH15QHPdvA==}
engines: {node: '>=10.0.0'}
hasBin: true
optionalDependencies:
fsevents: 2.3.2
dev: true
/sade/1.7.4:
resolution: {integrity: sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA==}
engines: {node: '>= 6'}
dependencies:
mri: 1.2.0
dev: true
/sander/0.5.1:
resolution: {integrity: sha1-dB4kXiMfB8r7b98PEzrfohalAq0=}
dependencies:
es6-promise: 3.3.1
graceful-fs: 4.2.8
mkdirp: 0.5.5
rimraf: 2.7.1
dev: true
/sorcery/0.10.0:
resolution: {integrity: sha1-iukK19fLBfxZ8asMY3hF1cFaUrc=}
hasBin: true
dependencies:
buffer-crc32: 0.2.13
minimist: 1.2.5
sander: 0.5.1
sourcemap-codec: 1.4.8
dev: true
/source-map-js/1.0.1:
resolution: {integrity: sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==}
engines: {node: '>=0.10.0'}
dev: true
/sourcemap-codec/1.4.8:
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
dev: true
/strip-indent/3.0.0:
resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
engines: {node: '>=8'}
dependencies:
min-indent: 1.0.1
dev: true
/svelte-hmr/0.14.7_svelte@3.44.3:
resolution: {integrity: sha512-pDrzgcWSoMaK6AJkBWkmgIsecW0GChxYZSZieIYfCP0v2oPyx2CYU/zm7TBIcjLVUPP714WxmViE9Thht4etog==}
peerDependencies:
svelte: '>=3.19.0'
dependencies:
svelte: 3.44.3
dev: true
/svelte-preprocess/4.10.1_svelte@3.44.3+typescript@4.5.4:
resolution: {integrity: sha512-NSNloaylf+o9UeyUR2KvpdxrAyMdHl3U7rMnoP06/sG0iwJvlUM4TpMno13RaNqovh4AAoGsx1jeYcIyuGUXMw==}
engines: {node: '>= 9.11.2'}
requiresBuild: true
peerDependencies:
'@babel/core': ^7.10.2
coffeescript: ^2.5.1
less: ^3.11.3
node-sass: '*'
postcss: ^7 || ^8
postcss-load-config: ^2.1.0 || ^3.0.0
pug: ^3.0.0
sass: ^1.26.8
stylus: ^0.54.7
sugarss: ^2.0.0
svelte: ^3.23.0
typescript: ^4.5.2
peerDependenciesMeta:
'@babel/core':
optional: true
coffeescript:
optional: true
less:
optional: true
node-sass:
optional: true
postcss:
optional: true
postcss-load-config:
optional: true
pug:
optional: true
sass:
optional: true
stylus:
optional: true
sugarss:
optional: true
typescript:
optional: true
dependencies:
'@types/pug': 2.0.5
'@types/sass': 1.43.1
detect-indent: 6.1.0
magic-string: 0.25.7
sorcery: 0.10.0
strip-indent: 3.0.0
svelte: 3.44.3
typescript: 4.5.4
dev: true
/svelte/3.44.3:
resolution: {integrity: sha512-aGgrNCip5PQFNfq9e9tmm7EYxWLVHoFsEsmKrtOeRD8dmoGDdyTQ+21xd7qgFd8MNdKGSYvg7F9dr+Tc0yDymg==}
engines: {node: '>= 8'}
dev: true
/toggle-selection/1.0.6:
resolution: {integrity: sha1-bkWxJj8gF/oKzH2J14sVuL932jI=}
dev: false
/tslib/2.3.1:
resolution: {integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==}
dev: true
/typescript/4.5.4:
resolution: {integrity: sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: true
/vite/2.7.4:
resolution: {integrity: sha512-f+0426k9R/roz5mRNwJlQ+6UOnhCwIypJSbfgCmsVzVJe9jTTM5iRX2GWYUean+iqPBWaU/dYLryx9AoH2pmrw==}
engines: {node: '>=12.2.0'}
hasBin: true
peerDependencies:
less: '*'
sass: '*'
stylus: '*'
peerDependenciesMeta:
less:
optional: true
sass:
optional: true
stylus:
optional: true
dependencies:
esbuild: 0.13.15
postcss: 8.4.5
resolve: 1.20.0
rollup: 2.61.1
optionalDependencies:
fsevents: 2.3.2
dev: true
/wrappy/1.0.2:
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
dev: true

View File

@@ -97,3 +97,28 @@ fieldset {
padding: 0;
border: none;
}
.box {
width: 100%;
min-height: min(calc(100vh - 30rem), 20rem);
margin: 0;
border: 2px solid var(--ui-bg-1);
resize: vertical;
outline: none;
padding: 0.5rem;
}
@media screen and (max-width: 30rem) {
.box {
min-height: calc(100vh - 25rem);
}
}
.box:hover,
.box:focus {
border-color: var(--ui-clr-primary);
}
.tr {
text-align: right;
}

View File

@@ -1,51 +1,74 @@
import { dev } from '$app/env'
export type NoteMeta = { type: 'text' | 'file' }
export type Note = {
contents: string
meta: NoteMeta
views?: number
expiration?: number
}
export type NoteInfo = {}
export type NotePublic = Pick<Note, 'contents'>
export type NotePublic = Pick<Note, 'contents' | 'meta'>
export type NoteCreate = Omit<Note, 'meta'> & { meta: string }
export type FileDTO = Pick<File, 'name' | 'size' | 'type'> & {
contents: string
}
type CallOptions = {
url: string
method: string
body?: any
}
const base = dev ? 'http://localhost:5000/api/' : '/api/'
async function call(options: CallOptions) {
return fetch(base + options.url, {
export class PayloadToLargeError extends Error {}
export async function call(options: CallOptions) {
const response = await fetch('/api/' + options.url, {
method: options.method,
body: options.body === undefined ? undefined : JSON.stringify(options.body),
mode: 'cors',
headers: {
'Content-Type': 'application/json',
},
}).then((r) => r.json())
})
if (!response.ok) {
if (response.status === 413) throw new PayloadToLargeError()
else throw new Error('API call failed')
}
return response.json()
}
export async function create(note: Note) {
const { meta, ...rest } = note
const body: NoteCreate = {
...rest,
meta: JSON.stringify(meta),
}
const data = await call({
url: 'notes',
url: 'notes/',
method: 'post',
body: note,
body,
})
return data as { id: string }
}
export async function get(id: string) {
export async function get(id: string): Promise<NotePublic> {
const data = await call({
url: `notes/${id}`,
method: 'delete',
})
return data as NotePublic
const { contents, meta } = data
return {
contents,
meta: JSON.parse(meta) as NoteMeta,
}
}
export async function info(id: string) {
export async function info(id: string): Promise<NoteInfo> {
const data = await call({
url: `notes/${id}`,
method: 'get',
})
return data as NoteInfo
return data
}

View File

@@ -36,7 +36,7 @@ export function getKeyFromString(password: string) {
}
export async function getDerivedForKey(key: CryptoKey, salt: ArrayBuffer) {
const iterations = 1_000
const iterations = 100_000
return window.crypto.subtle.deriveKey(
{
name: 'PBKDF2',

13
client/src/lib/files.ts Normal file
View File

@@ -0,0 +1,13 @@
export class Files {
static toString(f: File | Blob): Promise<string> {
const reader = new window.FileReader()
reader.readAsDataURL(f)
return new Promise((resolve) => {
reader.onloadend = () => resolve(reader.result as string)
})
}
static fromString(s: string): Promise<Blob> {
return fetch(s).then((r) => r.blob())
}
}

View File

@@ -0,0 +1,17 @@
import { call } from '$lib/api'
import { writable } from 'svelte/store'
export type Status = {
version: string
max_size: number
}
export const status = writable<null | Status>(null)
export async function init() {
const data = await call({
url: 'status',
method: 'get',
})
status.set(data)
}

View File

@@ -0,0 +1,19 @@
<script lang="ts">
export let title: string
</script>
<p>
<b>{title}</b>
<slot />
</p>
<style>
b {
display: block;
margin-bottom: 0.25rem;
}
p > :global(span) {
padding-left: 1.25em;
}
</style>

View File

@@ -0,0 +1,72 @@
<script lang="ts">
import type { FileDTO } from '$lib/api'
import { Files } from '$lib/files'
import { createEventDispatcher } from 'svelte'
import MaxSize from './MaxSize.svelte'
export let label: string = ''
let files: File[] = []
const dispatch = createEventDispatcher<{ file: string }>()
async function onInput(e: Event) {
const input = e.target as HTMLInputElement
if (input.files.length) {
files = Array.from(input.files)
const data: FileDTO[] = await Promise.all(
files.map(async (file) => ({
name: file.name,
type: file.type,
size: file.size,
contents: await Files.toString(file),
}))
)
console.debug(
'files',
data.map((d) => d.contents.length)
)
dispatch('file', JSON.stringify(data))
} else {
dispatch('file', '')
}
}
</script>
<label>
<small>
{label}
</small>
<input type="file" on:change={onInput} multiple />
<div class="box">
{#if files.length}
<div>
<b>Selected Files</b>
{#each files as file}
<div class="file">
{file.name}
</div>
{/each}
</div>
{:else}
<div>
<b>No Files Selected</b>
<br />
<small>max: <MaxSize /></small>
</div>
{/if}
</div>
</label>
<style>
input {
display: none;
}
.box {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
}
</style>

View File

@@ -0,0 +1,12 @@
<script lang="ts">
import { status } from '$lib/stores/status'
import prettyBytes from 'pretty-bytes'
</script>
<span>
{#if $status !== null}
{prettyBytes($status.max_size, { binary: true })}
{:else}
loading...
{/if}
</span>

View File

@@ -0,0 +1,69 @@
<script lang="ts">
import type { FileDTO, NotePublic } from '$lib/api'
import { Files } from '$lib/files'
import copy from 'copy-to-clipboard'
import { saveAs } from 'file-saver'
import prettyBytes from 'pretty-bytes'
import Button from './Button.svelte'
export let note: NotePublic
let files: FileDTO[] = []
$: if (note.meta.type === 'file') {
files = JSON.parse(note.contents) as FileDTO[]
}
$: download = () => {
for (const file of files) {
downloadFile(file)
}
}
async function downloadFile(file: FileDTO) {
const f = new File([await Files.fromString(file.contents)], file.name, {
type: file.type,
})
saveAs(f)
}
</script>
<p class="error-text">you will <b>not</b> get the chance to see the note again.</p>
{#if note.meta.type === 'text'}
<div class="note" data-testid="note-result">
{note.contents}
</div>
<Button on:click={() => copy(note.contents)}>copy to clipboard</Button>
{:else}
{#each files as file}
<div class="note file" data-testid="note-result">
<b on:click={() => downloadFile(file)}> {file.name}</b>
<small> {file.type} {prettyBytes(file.size)}</small>
</div>
{/each}
<Button on:click={download}>download all</Button>
{/if}
<style>
.note {
width: 100%;
margin: 0;
padding: 0;
border: 2px solid var(--ui-bg-1);
outline: none;
padding: 0.5rem;
white-space: pre;
overflow: auto;
margin-bottom: 0.5rem;
}
.note b {
cursor: pointer;
}
.note.file {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>

View File

@@ -4,7 +4,7 @@
export let color = true
</script>
<div>
<div {...$$restProps}>
<label class="switch">
<small>{label}</small>
<input type="checkbox" bind:checked={value} />

View File

@@ -7,28 +7,5 @@
<small>
{label}
</small>
<textarea {...$$restProps} bind:value />
<textarea class="box" {...$$restProps} bind:value />
</label>
<style>
textarea {
width: 100%;
min-height: calc(100vh - 30rem);
margin: 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);
}
</style>

View File

@@ -1,7 +1,7 @@
<script lang="ts" context="module">
import { writable } from 'svelte/store'
export enum Theme {
enum Theme {
Dark = 'dark',
Light = 'light',
Auto = 'auto',

View File

@@ -1,20 +1,23 @@
<script lang="ts">
import type { Note } from '$lib/api'
import { create } from '$lib/api'
import { getKeyFromString, encrypt, Hex, getRandomBytes } from '$lib/crypto'
import { create, Note, PayloadToLargeError } from '$lib/api'
import { encrypt, getKeyFromString, getRandomBytes, Hex } from '$lib/crypto'
import Button from '$lib/ui/Button.svelte'
import FileUpload from '$lib/ui/FileUpload.svelte'
import MaxSize from '$lib/ui/MaxSize.svelte'
import Switch from '$lib/ui/Switch.svelte'
import TextArea from '$lib/ui/TextArea.svelte'
import TextInput from '$lib/ui/TextInput.svelte'
import { blur } from 'svelte/transition'
let note: Note = {
contents: '',
meta: { type: 'text' },
views: 1,
expiration: 60,
}
let result: { password: string; id: string } | null = null
let advanced = false
let file = false
let type = false
let message = ''
let loading = false
@@ -31,6 +34,12 @@
message = 'the note will expire and be destroyed after ' + fraction
}
$: note.meta.type = file ? 'file' : 'text'
$: if (!file) {
note.contents = ''
}
async function submit() {
try {
error = null
@@ -39,6 +48,7 @@
const key = await getKeyFromString(password)
const data: Note = {
contents: await encrypt(note.contents, key),
meta: note.meta,
}
// @ts-ignore
if (type) data.expiration = parseInt(note.expiration)
@@ -50,8 +60,12 @@
password: password,
id: response.id,
}
} catch {
error = 'could not create note.'
} catch (e) {
if (e instanceof PayloadToLargeError) {
error = 'could not create not. note is to big'
} else {
error = 'could not create note. please try again.'
}
} finally {
loading = false
}
@@ -66,28 +80,66 @@
<TextInput
type="text"
readonly
value="{window.location.origin}/note/{result.id}/{result.password}"
label="share link"
value="{window.location.origin}/note/{result.id}#{result.password}"
copy
data-testid="note-share-link"
/>
<br />
<Button on:click={reset}>new</Button>
<p>
<b>availability:</b>
<br />
the note is not guaranteed to be stored as everything is kept in ram, if it fills up the oldest notes
will be removed.
<br />
(you probably will be fine, just be warned.)
</p>
<br />
<Button on:click={reset}>new note</Button>
{:else}
<p>
Easily send <i>fully encrypted</i>, secure notes or files with one click. Just create a note and
share the link.
</p>
<form on:submit|preventDefault={submit}>
<fieldset disabled={loading}>
<TextArea label="note" bind:value={note.contents} placeholder="..." />
{#if file}
<FileUpload label="file" on:file={(f) => (note.contents = f.detail)} />
{:else}
<TextArea
label="note"
bind:value={note.contents}
placeholder="..."
data-testid="input-note"
/>
{/if}
<div class="bottom">
<Switch class="file" label="file" bind:value={file} />
<Switch label="advanced" bind:value={advanced} />
<Button type="submit">create</Button>
<div class="grow" />
<div class="tr">
<small>max: <MaxSize /> </small>
<br />
<Button type="submit" data-testid="button-create">create</Button>
</div>
</div>
{#if error}
<div class="error-text">{error}</div>
{/if}
<p><br />{message}</p>
<p>
<br />
{#if loading}
loading...
{:else}
{message}
{/if}
</p>
<div class="advanced" class:hidden={!advanced}>
{#if advanced}
<div transition:blur={{ duration: 250 }}>
<br />
<div class="fields">
<TextInput
@@ -109,15 +161,7 @@
/>
</div>
</div>
<style>
.fields {
display: flex;
}
.spacer {
width: 3rem;
}
</style>
{/if}
</fieldset>
</form>
{/if}
@@ -125,22 +169,27 @@
<style>
.bottom {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-top: 0.5rem;
}
.bottom :global(.file) {
margin-right: 0.5rem;
}
.grow {
flex: 1;
}
.middle-switch {
margin: 0 1rem;
}
.advanced {
max-height: 14em;
overflow: hidden;
transition: var(--ui-anim);
.error-text {
margin-top: 0.5rem;
}
.advanced.hidden {
max-height: 0;
.fields {
display: flex;
}
</style>

View File

@@ -1,8 +1,13 @@
<script lang="ts">
import { init } from '$lib/stores/status'
import Footer from '$lib/views/Footer.svelte'
import Header from '$lib/views/Header.svelte'
import { onMount } from 'svelte'
import '../app.css'
onMount(() => {
init()
})
</script>
<svelte:head>

View File

@@ -1,5 +1,8 @@
<script context="module">
import { browser, dev } from '$app/env'
import { status } from '$lib/stores/status'
import AboutParagraph from '$lib/ui/AboutParagraph.svelte'
export const hydrate = dev
export const router = browser
export const prerender = true
@@ -13,43 +16,54 @@
<h1>About</h1>
<p>
<i>cryptgeon</i> is a secure, open source sharing note service inspired by
<i>cryptgeon</i> is a secure, open source sharing note / file service inspired by
<a href="https://privnote.com"><i>PrivNote</i></a>.
</p>
<p>
<b>▶ how does it work?</b>
<br />
each note has a 512bit generated <i>id</i> that is used to retrieve the note. data is stored in memory
and never persisted to disk.
</p>
<AboutParagraph title="how does it work?">
<span>
each note has a generated <code>id (256bit)</code> and <code>key 256(bit)</code>. The
<code>id</code>
is used to save & retrieve the note. the note is then encrypted with aes in gcm mode on the client
side with the <code>key</code> and then sent to the server. data is stored in memory and never
persisted to disk. the server never sees the encryption key and cannot decrypt the contents of
the notes even if it tried to.
</span>
</AboutParagraph>
<b>▶ Features</b>
<AboutParagraph title="features">
<ul>
<li>server cannot decrypt contents due to client side encryption</li>
<li>view and time constraints</li>
<li>in memory, no persistence</li>
</ul>
</AboutParagraph>
<p>
<b>▶ tech stack</b>
<br />
<AboutParagraph title="tech stack">
<span>
the backend is written in rust and the frontend is svelte and typescript.
<br />
you are welcomed to check & audit the
<a href="https://github.com/cupcakearmy/cryptgeon" target="_blank" rel="noopener">source code</a
>.
</p>
<a href="https://github.com/cupcakearmy/cryptgeon" target="_blank" rel="noopener">
source code
</a>.
</span>
</AboutParagraph>
<p>
<br />
<b>▶ attributions</b>
<br />
<small>
<AboutParagraph title="attribution">
<span>
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>
</small>
</p>
</span>
</AboutParagraph>
<AboutParagraph title="version">
<span>
{#if $status}
<code>v{$status.version}</code>
{/if}
</span>
</AboutParagraph>
</section>
<style>

View File

@@ -8,17 +8,15 @@
<script lang="ts">
import type { NotePublic } from '$lib/api'
import { info, get } from '$lib/api'
import { get, info } from '$lib/api'
import { decrypt, getKeyFromString } from '$lib/crypto'
import Button from '$lib/ui/Button.svelte'
import TextInput from '$lib/ui/TextInput.svelte'
import copy from 'copy-to-clipboard'
import ShowNote from '$lib/ui/ShowNote.svelte'
import { onMount } from 'svelte'
export let id: string
export let password: string
let password: string
let note: NotePublic | null = null
let exists = false
@@ -29,6 +27,7 @@
try {
loading = true
error = null
password = window.location.hash.slice(1)
await info(id)
exists = true
} catch {
@@ -41,30 +40,31 @@
async function show() {
try {
error = false
loading = true
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
} finally {
loading = false
}
}
</script>
{#if !loading}
{#if !exists}
<p class="error-text">note was not found or was already deleted.</p>
<p class="error-text" data-testid="note-not-found">
note was not found or was already deleted.
</p>
{:else if note && !error}
<p class="error-text">you will not get the chance to see the note again.</p>
<div class="note">
{note.contents}
</div>
<br />
<Button on:click={() => copy(note.contents)}>copy to clipboard</Button>
<ShowNote {note} />
{:else}
<form on:submit|preventDefault={show}>
<fieldset>
<p>click below to show and delete the note if the counter has reached it's limit</p>
<Button type="submit">show note</Button>
<Button type="submit" data-testid="button-show">show note</Button>
{#if error}
<br />
<p class="error-text">
@@ -72,19 +72,10 @@
<br />
</p>
{/if}
</fieldset>
</form>
{/if}
{/if}
<style>
.note {
width: 100%;
margin: 0;
padding: 0;
border: 2px solid var(--ui-bg-1);
outline: none;
padding: 0.5rem;
white-space: pre;
overflow: auto;
}
</style>
{#if loading}
<p>loading...</p>
{/if}

5
cypress.json Normal file
View File

@@ -0,0 +1,5 @@
{
"fixturesFolder": false,
"pluginsFile": false,
"supportFile": false
}

View File

@@ -6,7 +6,8 @@ version: '3.7'
services:
memcached:
image: memcached:1-alpine
entrypoint: memcached -m 128
restart: unless-stopped
entrypoint: memcached -m 128M -I 4M
ports:
- 11211:11211
@@ -14,5 +15,7 @@ services:
build: .
depends_on:
- memcached
environment:
SIZE_LIMIT: 4M
ports:
- 80:5000

View File

@@ -0,0 +1,22 @@
version: '3.8'
services:
memcached:
image: memcached:1-alpine
entrypoint: memcached -m 128 # Limit to 128 MB Ram, customize at free will.
app:
image: cupcakearmy/cryptgeon:latest
depends_on:
- memcached
proxy:
image: nginx:alpine
depends_on:
- app
volumes:
- ./nginx-plain.conf:/etc/nginx/conf.d/default.conf
# Or with tls
# - ./nginx-tls.conf:/etc/nginx/conf.d/default.conf
ports:
- 80:80

View File

@@ -0,0 +1,13 @@
server {
listen 80;
listen [::]:80;
server_name _;
location / {
proxy_pass http://app:5000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

View File

@@ -0,0 +1,29 @@
# You should change the server_name to something sensible.
# Also you need to specify the path to the ssl certificates.
server {
listen 80;
listen [::]:80;
server_name _;
# Enforce HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name _;
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
ssl_trusted_certificate /path/to/fullchain.pem;
location / {
proxy_pass http://app:5000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

1375
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,10 +2,16 @@
"scripts": {
"dev:docker": "docker-compose up memcached",
"dev:backend": "cargo watch -x 'run --bin cryptgeon'",
"dev:front": "npm --prefix client run dev",
"dev:front": "pnpm --prefix client run dev",
"dev:proxy": "node proxy.mjs",
"dev": "run-p dev:*"
},
"devDependencies": {
"http-proxy": "^1.18.1",
"npm-run-all": "^4.1.5"
},
"dependencies": {
"file-saver": "^2.0.5",
"pretty-bytes": "^5.6.0"
}
}

504
pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,504 @@
lockfileVersion: 5.3
specifiers:
file-saver: ^2.0.5
http-proxy: ^1.18.1
npm-run-all: ^4.1.5
pretty-bytes: ^5.6.0
dependencies:
file-saver: 2.0.5
pretty-bytes: 5.6.0
devDependencies:
http-proxy: 1.18.1
npm-run-all: 4.1.5
packages:
/ansi-styles/3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
dependencies:
color-convert: 1.9.3
dev: true
/balanced-match/1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
dev: true
/brace-expansion/1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
dev: true
/call-bind/1.0.2:
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
dependencies:
function-bind: 1.1.1
get-intrinsic: 1.1.1
dev: true
/chalk/2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
dependencies:
ansi-styles: 3.2.1
escape-string-regexp: 1.0.5
supports-color: 5.5.0
dev: true
/color-convert/1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies:
color-name: 1.1.3
dev: true
/color-name/1.1.3:
resolution: {integrity: sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=}
dev: true
/concat-map/0.0.1:
resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
dev: true
/cross-spawn/6.0.5:
resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==}
engines: {node: '>=4.8'}
dependencies:
nice-try: 1.0.5
path-key: 2.0.1
semver: 5.7.1
shebang-command: 1.2.0
which: 1.3.1
dev: true
/define-properties/1.1.3:
resolution: {integrity: sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==}
engines: {node: '>= 0.4'}
dependencies:
object-keys: 1.1.1
dev: true
/error-ex/1.3.2:
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
dependencies:
is-arrayish: 0.2.1
dev: true
/es-abstract/1.18.0:
resolution: {integrity: sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
es-to-primitive: 1.2.1
function-bind: 1.1.1
get-intrinsic: 1.1.1
has: 1.0.3
has-symbols: 1.0.2
is-callable: 1.2.3
is-negative-zero: 2.0.1
is-regex: 1.1.2
is-string: 1.0.5
object-inspect: 1.10.2
object-keys: 1.1.1
object.assign: 4.1.2
string.prototype.trimend: 1.0.4
string.prototype.trimstart: 1.0.4
unbox-primitive: 1.0.1
dev: true
/es-to-primitive/1.2.1:
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
engines: {node: '>= 0.4'}
dependencies:
is-callable: 1.2.3
is-date-object: 1.0.2
is-symbol: 1.0.3
dev: true
/escape-string-regexp/1.0.5:
resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=}
engines: {node: '>=0.8.0'}
dev: true
/eventemitter3/4.0.7:
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
dev: true
/file-saver/2.0.5:
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
dev: false
/follow-redirects/1.14.6:
resolution: {integrity: sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
dev: true
/function-bind/1.1.1:
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
dev: true
/get-intrinsic/1.1.1:
resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==}
dependencies:
function-bind: 1.1.1
has: 1.0.3
has-symbols: 1.0.2
dev: true
/graceful-fs/4.2.6:
resolution: {integrity: sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==}
dev: true
/has-bigints/1.0.1:
resolution: {integrity: sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==}
dev: true
/has-flag/3.0.0:
resolution: {integrity: sha1-tdRU3CGZriJWmfNGfloH87lVuv0=}
engines: {node: '>=4'}
dev: true
/has-symbols/1.0.2:
resolution: {integrity: sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==}
engines: {node: '>= 0.4'}
dev: true
/has/1.0.3:
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
engines: {node: '>= 0.4.0'}
dependencies:
function-bind: 1.1.1
dev: true
/hosted-git-info/2.8.9:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
dev: true
/http-proxy/1.18.1:
resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==}
engines: {node: '>=8.0.0'}
dependencies:
eventemitter3: 4.0.7
follow-redirects: 1.14.6
requires-port: 1.0.0
transitivePeerDependencies:
- debug
dev: true
/is-arrayish/0.2.1:
resolution: {integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=}
dev: true
/is-bigint/1.0.1:
resolution: {integrity: sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==}
dev: true
/is-boolean-object/1.1.0:
resolution: {integrity: sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
dev: true
/is-callable/1.2.3:
resolution: {integrity: sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==}
engines: {node: '>= 0.4'}
dev: true
/is-core-module/2.3.0:
resolution: {integrity: sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==}
dependencies:
has: 1.0.3
dev: true
/is-date-object/1.0.2:
resolution: {integrity: sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==}
engines: {node: '>= 0.4'}
dev: true
/is-negative-zero/2.0.1:
resolution: {integrity: sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==}
engines: {node: '>= 0.4'}
dev: true
/is-number-object/1.0.4:
resolution: {integrity: sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==}
engines: {node: '>= 0.4'}
dev: true
/is-regex/1.1.2:
resolution: {integrity: sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
has-symbols: 1.0.2
dev: true
/is-string/1.0.5:
resolution: {integrity: sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==}
engines: {node: '>= 0.4'}
dev: true
/is-symbol/1.0.3:
resolution: {integrity: sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==}
engines: {node: '>= 0.4'}
dependencies:
has-symbols: 1.0.2
dev: true
/isexe/2.0.0:
resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=}
dev: true
/json-parse-better-errors/1.0.2:
resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==}
dev: true
/load-json-file/4.0.0:
resolution: {integrity: sha1-L19Fq5HjMhYjT9U62rZo607AmTs=}
engines: {node: '>=4'}
dependencies:
graceful-fs: 4.2.6
parse-json: 4.0.0
pify: 3.0.0
strip-bom: 3.0.0
dev: true
/memorystream/0.3.1:
resolution: {integrity: sha1-htcJCzDORV1j+64S3aUaR93K+bI=}
engines: {node: '>= 0.10.0'}
dev: true
/minimatch/3.0.4:
resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==}
dependencies:
brace-expansion: 1.1.11
dev: true
/nice-try/1.0.5:
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
dev: true
/normalize-package-data/2.5.0:
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
dependencies:
hosted-git-info: 2.8.9
resolve: 1.20.0
semver: 5.7.1
validate-npm-package-license: 3.0.4
dev: true
/npm-run-all/4.1.5:
resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==}
engines: {node: '>= 4'}
hasBin: true
dependencies:
ansi-styles: 3.2.1
chalk: 2.4.2
cross-spawn: 6.0.5
memorystream: 0.3.1
minimatch: 3.0.4
pidtree: 0.3.1
read-pkg: 3.0.0
shell-quote: 1.7.2
string.prototype.padend: 3.1.2
dev: true
/object-inspect/1.10.2:
resolution: {integrity: sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA==}
dev: true
/object-keys/1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
dev: true
/object.assign/4.1.2:
resolution: {integrity: sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
define-properties: 1.1.3
has-symbols: 1.0.2
object-keys: 1.1.1
dev: true
/parse-json/4.0.0:
resolution: {integrity: sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=}
engines: {node: '>=4'}
dependencies:
error-ex: 1.3.2
json-parse-better-errors: 1.0.2
dev: true
/path-key/2.0.1:
resolution: {integrity: sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=}
engines: {node: '>=4'}
dev: true
/path-parse/1.0.6:
resolution: {integrity: sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==}
dev: true
/path-type/3.0.0:
resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==}
engines: {node: '>=4'}
dependencies:
pify: 3.0.0
dev: true
/pidtree/0.3.1:
resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==}
engines: {node: '>=0.10'}
hasBin: true
dev: true
/pify/3.0.0:
resolution: {integrity: sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=}
engines: {node: '>=4'}
dev: true
/pretty-bytes/5.6.0:
resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
engines: {node: '>=6'}
dev: false
/read-pkg/3.0.0:
resolution: {integrity: sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=}
engines: {node: '>=4'}
dependencies:
load-json-file: 4.0.0
normalize-package-data: 2.5.0
path-type: 3.0.0
dev: true
/requires-port/1.0.0:
resolution: {integrity: sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=}
dev: true
/resolve/1.20.0:
resolution: {integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==}
dependencies:
is-core-module: 2.3.0
path-parse: 1.0.6
dev: true
/semver/5.7.1:
resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
hasBin: true
dev: true
/shebang-command/1.2.0:
resolution: {integrity: sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=}
engines: {node: '>=0.10.0'}
dependencies:
shebang-regex: 1.0.0
dev: true
/shebang-regex/1.0.0:
resolution: {integrity: sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=}
engines: {node: '>=0.10.0'}
dev: true
/shell-quote/1.7.2:
resolution: {integrity: sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==}
dev: true
/spdx-correct/3.1.1:
resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
dependencies:
spdx-expression-parse: 3.0.1
spdx-license-ids: 3.0.7
dev: true
/spdx-exceptions/2.3.0:
resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==}
dev: true
/spdx-expression-parse/3.0.1:
resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
dependencies:
spdx-exceptions: 2.3.0
spdx-license-ids: 3.0.7
dev: true
/spdx-license-ids/3.0.7:
resolution: {integrity: sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==}
dev: true
/string.prototype.padend/3.1.2:
resolution: {integrity: sha512-/AQFLdYvePENU3W5rgurfWSMU6n+Ww8n/3cUt7E+vPBB/D7YDG8x+qjoFs4M/alR2bW7Qg6xMjVwWUOvuQ0XpQ==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
define-properties: 1.1.3
es-abstract: 1.18.0
dev: true
/string.prototype.trimend/1.0.4:
resolution: {integrity: sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==}
dependencies:
call-bind: 1.0.2
define-properties: 1.1.3
dev: true
/string.prototype.trimstart/1.0.4:
resolution: {integrity: sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==}
dependencies:
call-bind: 1.0.2
define-properties: 1.1.3
dev: true
/strip-bom/3.0.0:
resolution: {integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=}
engines: {node: '>=4'}
dev: true
/supports-color/5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
dependencies:
has-flag: 3.0.0
dev: true
/unbox-primitive/1.0.1:
resolution: {integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==}
dependencies:
function-bind: 1.1.1
has-bigints: 1.0.1
has-symbols: 1.0.2
which-boxed-primitive: 1.0.2
dev: true
/validate-npm-package-license/3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
dependencies:
spdx-correct: 3.1.1
spdx-expression-parse: 3.0.1
dev: true
/which-boxed-primitive/1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
dependencies:
is-bigint: 1.0.1
is-boolean-object: 1.1.0
is-number-object: 1.0.4
is-string: 1.0.5
is-symbol: 1.0.3
dev: true
/which/1.3.1:
resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
hasBin: true
dependencies:
isexe: 2.0.0
dev: true

19
proxy.mjs Normal file
View File

@@ -0,0 +1,19 @@
import http from 'http'
import httpProxy from 'http-proxy'
function start() {
try {
const proxy = httpProxy.createProxyServer({})
const server = http.createServer(function (req, res) {
const target = req.url.startsWith('/api/') ? 'http://localhost:5000' : 'http://localhost:3000'
proxy.web(req, res, { target })
})
server.listen(1234)
server.on('error', () => start())
} catch (e) {
start()
}
}
start()

View File

@@ -1,23 +1,27 @@
use actix_web::{middleware, web, App, HttpServer};
use dotenv::dotenv;
#[macro_use]
extern crate lazy_static;
mod client;
mod note;
mod size;
mod store;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
dotenv().ok();
return HttpServer::new(|| {
App::new()
.wrap(middleware::Compress::default())
.wrap(middleware::DefaultHeaders::default())
.configure(size::init)
.configure(note::init)
.configure(client::init)
.default_service(web::resource("").route(web::get().to(client::fallback_fn)))
})
.bind("0.0.0.0:5000")?
.run()
.await
.await;
}

View File

@@ -4,9 +4,10 @@ use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone)]
pub struct Note {
pub meta: String,
pub contents: String,
pub views: Option<u8>,
pub expiration: Option<u64>,
pub expiration: Option<u32>,
}
#[derive(Serialize, Deserialize, Clone)]
@@ -14,11 +15,12 @@ pub struct NoteInfo {}
#[derive(Serialize, Deserialize, Clone)]
pub struct NotePublic {
pub meta: String,
pub contents: String,
}
pub fn generate_id() -> String {
let mut id: [u8; 64] = [0; 64];
let mut id: [u8; 32] = [0; 32];
let sr = ring::rand::SystemRandom::new();
let _ = sr.fill(&mut id);
return bs62::encode_data(&id);

View File

@@ -3,13 +3,14 @@ use serde::{Deserialize, Serialize};
use std::time::SystemTime;
use crate::note::{generate_id, Note, NoteInfo, NotePublic};
use crate::size::LIMIT;
use crate::store;
fn now() -> u64 {
pub fn now() -> u32 {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs()
.as_secs() as u32
}
#[derive(Serialize, Deserialize)]
@@ -17,7 +18,7 @@ struct NotePath {
id: String,
}
#[get("/notes/{id}")]
#[get("/{id}")]
async fn one(path: web::Path<NotePath>) -> impl Responder {
let p = path.into_inner();
let note = store::get(&p.id);
@@ -32,14 +33,11 @@ struct CreateResponse {
id: String,
}
#[post("/notes")]
#[post("/")]
async fn create(note: web::Json<Note>) -> impl Responder {
let mut n = note.into_inner();
let id = generate_id();
let bad_req = HttpResponse::BadRequest().finish();
if n.contents.chars().count() > 8192 {
return bad_req;
}
if n.views == None && n.expiration == None {
return bad_req;
}
@@ -56,7 +54,8 @@ async fn create(note: web::Json<Note>) -> impl Responder {
if e > 360 {
return bad_req;
}
n.expiration = Some(now() + (e * 60))
let expiration = now() + (e * 60);
n.expiration = Some(expiration);
}
_ => {}
}
@@ -64,7 +63,7 @@ async fn create(note: web::Json<Note>) -> impl Responder {
return HttpResponse::Ok().json(CreateResponse { id: id });
}
#[delete("/notes/{id}")]
#[delete("/{id}")]
async fn delete(path: web::Path<NotePath>) -> impl Responder {
let p = path.into_inner();
let note = store::get(&p.id);
@@ -87,9 +86,11 @@ async fn delete(path: web::Path<NotePath>) -> impl Responder {
}
_ => {}
}
let n = now();
match changed.expiration {
Some(e) => {
if e > now() {
if e < n {
store::del(&p.id.clone());
return HttpResponse::BadRequest().finish();
}
@@ -98,16 +99,39 @@ async fn delete(path: web::Path<NotePath>) -> impl Responder {
}
return HttpResponse::Ok().json(NotePublic {
contents: changed.contents,
meta: changed.meta,
});
}
}
}
#[derive(Serialize, Deserialize)]
struct Status {
version: String,
max_size: usize,
}
#[get("/status")]
async fn status() -> impl Responder {
println!("Limit: {}", *LIMIT);
return HttpResponse::Ok().json(Status {
version: option_env!("CARGO_PKG_VERSION")
.unwrap_or("Unknown")
.to_string(),
max_size: *LIMIT,
});
}
pub fn init(cfg: &mut web::ServiceConfig) {
cfg.service(
web::scope("/api")
.service(
web::scope("/notes")
.service(one)
.service(create)
.service(delete)
.service(one),
.service(status),
)
.service(status),
);
}

20
src/size.rs Normal file
View File

@@ -0,0 +1,20 @@
use actix_web::web;
use byte_unit::Byte;
use mime;
lazy_static! {
pub static ref LIMIT: usize =
Byte::from_str(std::env::var("SIZE_LIMIT").unwrap_or("1 KiB".to_string()))
.unwrap()
.get_bytes() as usize;
}
pub fn init(cfg: &mut web::ServiceConfig) {
println!("Limit: {}", *LIMIT);
let json = web::JsonConfig::default().limit(*LIMIT);
let plain = web::PayloadConfig::default()
.limit(*LIMIT)
.mimetype(mime::STAR_STAR);
cfg.data(json);
cfg.data(plain);
}

View File

@@ -1,5 +1,6 @@
use memcache;
use crate::note::now;
use crate::note::Note;
lazy_static! {
@@ -12,7 +13,11 @@ lazy_static! {
pub fn set(id: &String, note: &Note) {
let serialized = serde_json::to_string(&note.clone()).unwrap();
CLIENT.set(id, serialized, 0).unwrap();
let expiration: u32 = match note.expiration {
Some(e) => e - now(),
None => 0,
};
CLIENT.set(id, serialized, expiration).unwrap();
}
pub fn get(id: &String) -> Option<Note> {