Compare commits

..

28 Commits

Author SHA1 Message Date
a315e58284 Merge pull request #26 from cupcakearmy/1.4.1
fallback route & dep updates
2022-03-05 12:47:55 +01:00
d576b71bc5 fallback route & dep updates 2022-03-05 12:47:11 +01:00
e02f7f59c6 changelog 2022-03-02 16:59:25 +01:00
e8c6467faa dont' allow empty notes 2022-03-02 16:55:10 +01:00
43f67c795d don't remove already selected files 2022-03-02 16:55:04 +01:00
83f0902291 lokalise 2022-03-02 16:32:36 +01:00
11a6621bd7 script to download locales 2022-03-02 16:15:25 +01:00
36fa451249 enforce limits 2022-03-01 16:16:02 +01:00
d112eba8fe add config for max views, expiration and advanced 2022-03-01 15:24:17 +01:00
ef39f9ec0b cleanup 2022-03-01 02:04:43 +01:00
8517c20e6c remove unused code 2022-03-01 02:01:57 +01:00
728ad56b33 enforce strict typescript 2022-03-01 02:00:01 +01:00
f185ccee03 add svelte check 2022-03-01 01:52:20 +01:00
284bbcbae2 better typings 2022-03-01 01:52:09 +01:00
7eba454f1b visual improvements 2022-03-01 01:52:04 +01:00
dcd9efaeba use native icons 2022-03-01 01:51:43 +01:00
f13bcbaf3f update robots 2022-03-01 01:51:21 +01:00
8e7e0414a6 add copy to clipboard note 2022-03-01 01:16:31 +01:00
229c8d8368 update actix to version 4 2022-03-01 00:53:47 +01:00
1adf87b884 set memcached size 2022-03-01 00:53:34 +01:00
a061b540b1 make top margin smaller 2022-01-21 17:46:13 +01:00
824603ff4a add locales 2022-01-16 14:39:45 +01:00
539d99d35f version bump 2022-01-16 14:04:49 +01:00
716034660c translate the app 2022-01-16 14:02:53 +01:00
bab65bcdad bug due to dep update 2022-01-03 18:16:54 +01:00
a0732a4593 move folder 2022-01-02 23:46:08 +01:00
835f7df0f6 update dep 2022-01-02 18:43:31 +01:00
2def365cae quality of life improvemnts 2021-12-30 22:36:28 +01:00
87 changed files with 2909 additions and 2035 deletions

View File

@@ -1,2 +1,2 @@
target /**/target
node_modules /**/node_modules

BIN
.github/lokalise.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

2
.gitignore vendored
View File

@@ -1,6 +1,6 @@
# Backend # Backend
/target target
# Client # Client
.DS_Store .DS_Store

10
.vscode/settings.json vendored
View File

@@ -1,6 +1,6 @@
{ {
"cSpell.words": [ "cSpell.words": ["ciphertext", "cryptgeon"],
"ciphertext", "i18n-ally.localesPaths": ["frontend/locales"],
"cryptgeon" "i18n-ally.enabledFrameworks": ["svelte"],
] "i18n-ally.keystyle": "nested"
} }

View File

@@ -5,6 +5,51 @@ 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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.4.1] - 2022-03-05
### Fixed
- Router in prod build.
## [1.4.0] - 2022-03-02
### Added
- Support for multiple languages.
- Select multiple files without removing already selected ones.
- Tooltip for copy action.
- Configure maximum views, expiration and advanced options for the server.
### Changed
- Use native SVGs instead of images.
- Update robots.txt file to allow only root.
- Stronger frontend types.
## [1.3.3] - 2022-01-03
### Fixed
- Bug fix due to dependency update.
## [1.3.2] - 2022-01-02
### Changed
- Dependencies updates.
- Folder structure.
## [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 ## [1.3.0] - 2021-12-22
### Added ### Added

View File

@@ -1,26 +1,28 @@
# Frontend
FROM node:16-alpine as CLIENT FROM node:16-alpine as CLIENT
WORKDIR /tmp WORKDIR /tmp
COPY ./client ./ COPY ./frontend ./
RUN npm install -g pnpm RUN npm install -g pnpm
RUN pnpm install RUN pnpm install
RUN pnpm run build RUN pnpm run build
FROM rust:1.51-alpine as RUST # Backend
FROM rust:1.59-alpine as RUST
WORKDIR /tmp WORKDIR /tmp
RUN apk add libc-dev openssl-dev alpine-sdk RUN apk add libc-dev openssl-dev alpine-sdk
COPY ./Cargo* ./ COPY ./backend ./
COPY ./src ./src
RUN cargo build --release RUN cargo build --release
FROM scratch # Server
FROM alpine
WORKDIR /app WORKDIR /app
COPY --from=RUST /tmp/target/release/cryptgeon . COPY --from=RUST /tmp/target/release/cryptgeon .
COPY --from=CLIENT /tmp/build ./client/build COPY --from=CLIENT /tmp/build ./frontend/build
ENV MEMCACHE=memcached:11211 ENV MEMCACHE=memcached:11211

View File

@@ -10,13 +10,18 @@
</a> </a>
<br/> <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> <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" height="50" /></a>
<a href=""><img src="./.github/lokalise.png" height="50">
<br/> <br/>
## About? ## About?
_cryptgeon_ is a secure, open source sharing note or file service inspired by [_PrivNote_](https://privnote.com) _cryptgeon_ is a secure, open source sharing note or file service inspired by [_PrivNote_](https://privnote.com)
> 🌍 If you want to translate the project feel free to reach out to me.
>
> Thanks to [Lokalise](https://lokalise.com/) for providing free access to their platform.
## Demo ## Demo
Check out the demo and see for yourself https://cryptgeon.nicco.io. Check out the demo and see for yourself https://cryptgeon.nicco.io.
@@ -30,7 +35,12 @@ Check out the demo and see for yourself https://cryptgeon.nicco.io.
## How does it work? ## 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

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
[package] [package]
name = "cryptgeon" name = "cryptgeon"
version = "1.3.0" version = "1.4.1"
authors = ["cupcakearmy <hi@nicco.io>"] authors = ["cupcakearmy <hi@nicco.io>"]
edition = "2018" edition = "2021"
[[bin]] [[bin]]
name = "cryptgeon" name = "cryptgeon"
@@ -11,9 +11,9 @@ path = "src/main.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
actix-web = "3" actix-web = "4"
actix-files = "0.5" actix-files = "0.6"
serde = "1" serde = { version = "1.0", features = ["derive"] }
serde_json = "1" serde_json = "1"
lazy_static = "1" lazy_static = "1"
ring = "0.16" ring = "0.16"

12
backend/src/api.rs Normal file
View File

@@ -0,0 +1,12 @@
use actix_web::web;
use crate::note;
use crate::status;
pub fn init(cfg: &mut web::ServiceConfig) {
cfg.service(
web::scope("/api")
.service(note::init())
.service(status::init()),
);
}

14
backend/src/client.rs Normal file
View File

@@ -0,0 +1,14 @@
use actix_files::{Files, NamedFile};
use actix_web::{web, Result};
pub fn init(cfg: &mut web::ServiceConfig) {
cfg.service(
Files::new("/", "./frontend/build")
.index_file("index.html")
.use_etag(true),
);
}
pub async fn index() -> Result<NamedFile> {
Ok(NamedFile::open("./frontend/build/index.html")?)
}

23
backend/src/config.rs Normal file
View File

@@ -0,0 +1,23 @@
use byte_unit::Byte;
lazy_static! {
pub static ref VERSION: String = option_env!("CARGO_PKG_VERSION")
.unwrap_or("Unknown")
.to_string();
pub static ref LIMIT: u32 =
Byte::from_str(std::env::var("SIZE_LIMIT").unwrap_or("1 KiB".to_string()))
.unwrap()
.get_bytes() as u32;
pub static ref MAX_VIEWS: u32 = std::env::var("MAX_VIEWS")
.unwrap_or("100".to_string())
.parse()
.unwrap();
pub static ref MAX_EXPIRATION: u32 = std::env::var("MAX_EXPIRATION")
.unwrap_or("360".to_string()) // 6 hours in minutes
.parse()
.unwrap();
pub static ref ALLOW_ADVANCED: bool = std::env::var("ALLOW_ADVANCED")
.unwrap_or("true".to_string())
.parse()
.unwrap();
}

View File

@@ -4,9 +4,12 @@ use dotenv::dotenv;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
mod api;
mod client; mod client;
mod config;
mod note; mod note;
mod size; mod size;
mod status;
mod store; mod store;
#[actix_web::main] #[actix_web::main]
@@ -17,9 +20,9 @@ async fn main() -> std::io::Result<()> {
.wrap(middleware::Compress::default()) .wrap(middleware::Compress::default())
.wrap(middleware::DefaultHeaders::default()) .wrap(middleware::DefaultHeaders::default())
.configure(size::init) .configure(size::init)
.configure(note::init) .configure(api::init)
.configure(client::init) .configure(client::init)
.default_service(web::resource("").route(web::get().to(client::fallback_fn))) .default_service(web::to(client::index))
}) })
.bind("0.0.0.0:5000")? .bind("0.0.0.0:5000")?
.run() .run()

View File

@@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
pub struct Note { pub struct Note {
pub meta: String, pub meta: String,
pub contents: String, pub contents: String,
pub views: Option<u8>, pub views: Option<u32>,
pub expiration: Option<u32>, pub expiration: Option<u32>,
} }
@@ -20,7 +20,7 @@ pub struct NotePublic {
} }
pub fn generate_id() -> 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 = ring::rand::SystemRandom::new();
let _ = sr.fill(&mut id); let _ = sr.fill(&mut id);
return bs62::encode_data(&id); return bs62::encode_data(&id);

View File

@@ -1,9 +1,9 @@
use actix_web::{delete, get, post, web, HttpResponse, Responder}; use actix_web::{delete, get, post, web, HttpResponse, Responder, Scope};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::time::SystemTime; use std::time::SystemTime;
use crate::config;
use crate::note::{generate_id, Note, NoteInfo, NotePublic}; use crate::note::{generate_id, Note, NoteInfo, NotePublic};
use crate::size::LIMIT;
use crate::store; use crate::store;
pub fn now() -> u32 { pub fn now() -> u32 {
@@ -41,17 +41,22 @@ async fn create(note: web::Json<Note>) -> impl Responder {
if n.views == None && n.expiration == None { if n.views == None && n.expiration == None {
return bad_req; return bad_req;
} }
if !*config::ALLOW_ADVANCED {
n.views = Some(1);
n.expiration = None;
}
match n.views { match n.views {
Some(v) => { Some(v) => {
if v > 100 { if v > *config::MAX_VIEWS {
return bad_req; return bad_req;
} }
n.expiration = None; // views overrides expiration
} }
_ => {} _ => {}
} }
match n.expiration { match n.expiration {
Some(e) => { Some(e) => {
if e > 360 { if e > *config::MAX_EXPIRATION {
return bad_req; return bad_req;
} }
let expiration = now() + (e * 60); let expiration = now() + (e * 60);
@@ -111,27 +116,9 @@ struct Status {
max_size: usize, max_size: usize,
} }
#[get("/status")] pub fn init() -> Scope {
async fn status() -> impl Responder { web::scope("/notes")
println!("Limit: {}", *LIMIT); .service(one)
return HttpResponse::Ok().json(Status { .service(create)
version: option_env!("CARGO_PKG_VERSION") .service(delete)
.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(status),
)
.service(status),
);
} }

View File

@@ -10,11 +10,9 @@ lazy_static! {
} }
pub fn init(cfg: &mut web::ServiceConfig) { pub fn init(cfg: &mut web::ServiceConfig) {
println!("Limit: {}", *LIMIT);
let json = web::JsonConfig::default().limit(*LIMIT); let json = web::JsonConfig::default().limit(*LIMIT);
let plain = web::PayloadConfig::default() let plain = web::PayloadConfig::default()
.limit(*LIMIT) .limit(*LIMIT)
.mimetype(mime::STAR_STAR); .mimetype(mime::STAR_STAR);
cfg.data(json); cfg.app_data(json).app_data(plain);
cfg.data(plain);
} }

View File

@@ -0,0 +1,5 @@
mod model;
mod routes;
pub use model::*;
pub use routes::*;

View File

@@ -0,0 +1,10 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct Status {
pub version: String,
pub max_size: u32,
pub max_views: u32,
pub max_expiration: u32,
pub allow_advanced: bool,
}

View File

@@ -0,0 +1,19 @@
use actix_web::{get, web, HttpResponse, Responder, Scope};
use crate::config;
use crate::status::Status;
#[get("/")]
async fn get_status() -> impl Responder {
return HttpResponse::Ok().json(Status {
version: config::VERSION.to_string(),
max_size: *config::LIMIT,
max_views: *config::MAX_VIEWS,
max_expiration: *config::MAX_EXPIRATION,
allow_advanced: *config::ALLOW_ADVANCED,
});
}
pub fn init() -> Scope {
web::scope("/status").service(get_status)
}

View File

@@ -1,38 +0,0 @@
# create-svelte
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte);
## Creating a project
If you're seeing this, you've probably already done this step. Congrats!
```bash
# create a new project in the current directory
npm init svelte@next
# create a new project in my-app
npm init svelte@next my-app
```
> Note: the `@next` is temporary
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
Before creating a production version of your app, install an [adapter](https://kit.svelte.dev/docs#adapters) for your target environment. Then:
```bash
npm run build
```
> You can preview the built app with `npm run preview`, regardless of whether you installed an adapter. This should _not_ be used to serve your app in production.

View File

@@ -1,25 +0,0 @@
{
"private": true,
"scripts": {
"dev": "svelte-kit dev",
"build": "svelte-kit build",
"preview": "svelte-kit preview",
"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.5.0",
"copy-to-clipboard": "^3.3.1",
"file-saver": "^2.0.5",
"pretty-bytes": "^5.6.0"
}
}

638
client/pnpm-lock.yaml generated
View File

@@ -1,638 +0,0 @@
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

@@ -1,34 +0,0 @@
<script lang="ts">
import { onMount } from 'svelte'
export let icon: string = ''
export let href: string = ''
$: src = href || `/icons/${icon}.svg`
let html = null
onMount(async () => {
html = await fetch(src).then((res) => res.text())
})
</script>
{#if html === null}
<img on:click {...$$restProps} {src} alt={icon} />
{:else}
<div on:click {...$$restProps}>
{@html html}
</div>
{/if}
<style>
img,
div {
display: inline-block;
contain: strict;
box-sizing: content-box;
}
div > :global(svg) {
display: block;
fill: currentColor;
}
</style>

View File

@@ -1,32 +0,0 @@
<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>
<title>cryptgeon</title>
</svelte:head>
<main>
<Header />
<slot />
</main>
<Footer />
<style>
main {
padding: 1rem;
padding-bottom: 4rem;
width: 100%;
max-width: 35rem;
margin: 0 auto;
}
</style>

View File

@@ -1 +0,0 @@
<svg xmlns='http://www.w3.org/2000/svg' class='ionicon' viewBox='0 0 512 512'><title>Contrast</title><path d='M256 32C132.29 32 32 132.29 32 256s100.29 224 224 224 224-100.29 224-224S379.71 32 256 32zM128.72 383.28A180 180 0 01256 76v360a178.82 178.82 0 01-127.28-52.72z'/></svg>

Before

Width:  |  Height:  |  Size: 279 B

View File

@@ -1 +0,0 @@
<svg xmlns='http://www.w3.org/2000/svg' class='ionicon' viewBox='0 0 512 512'><title>Copy</title><path d='M456 480H136a24 24 0 01-24-24V128a16 16 0 0116-16h328a24 24 0 0124 24v320a24 24 0 01-24 24z'/><path d='M112 80h288V56a24 24 0 00-24-24H60a28 28 0 00-28 28v316a24 24 0 0024 24h24V112a32 32 0 0132-32z'/></svg>

Before

Width:  |  Height:  |  Size: 313 B

View File

@@ -1 +0,0 @@
<svg xmlns='http://www.w3.org/2000/svg' class='ionicon' viewBox='0 0 512 512'><title>Dice</title><path d='M48 366.92L240 480V284L48 170zM192 288c8.84 0 16 10.75 16 24s-7.16 24-16 24-16-10.75-16-24 7.16-24 16-24zm-96 32c8.84 0 16 10.75 16 24s-7.16 24-16 24-16-10.75-16-24 7.16-24 16-24zM272 284v196l192-113.08V170zm48 140c-8.84 0-16-10.75-16-24s7.16-24 16-24 16 10.75 16 24-7.16 24-16 24zm0-88c-8.84 0-16-10.75-16-24s7.16-24 16-24 16 10.75 16 24-7.16 24-16 24zm96 32c-8.84 0-16-10.75-16-24s7.16-24 16-24 16 10.75 16 24-7.16 24-16 24zm0-88c-8.84 0-16-10.75-16-24s7.16-24 16-24 16 10.75 16 24-7.16 24-16 24zm32 77.64zM256 32L64 144l192 112 192-112zm0 120c-13.25 0-24-7.16-24-16s10.75-16 24-16 24 7.16 24 16-10.75 16-24 16z'/></svg>

Before

Width:  |  Height:  |  Size: 728 B

View File

@@ -1 +0,0 @@
<svg xmlns='http://www.w3.org/2000/svg' class='ionicon' viewBox='0 0 512 512'><title>Eye Off</title><path d='M63.998 86.004l21.998-21.998L448 426.01l-21.998 21.998zM259.34 192.09l60.57 60.57a64.07 64.07 0 00-60.57-60.57zM252.66 319.91l-60.57-60.57a64.07 64.07 0 0060.57 60.57z'/><path d='M256 352a96 96 0 01-92.6-121.34l-69.07-69.08C66.12 187.42 39.24 221.14 16 256c26.42 44 62.56 89.24 100.2 115.18C159.38 400.92 206.33 416 255.76 416A233.47 233.47 0 00335 402.2l-53.61-53.6A95.84 95.84 0 01256 352zM256 160a96 96 0 0192.6 121.34L419.26 352c29.15-26.25 56.07-61.56 76.74-96-26.38-43.43-62.9-88.56-101.18-114.82C351.1 111.2 304.31 96 255.76 96a222.92 222.92 0 00-78.21 14.29l53.11 53.11A95.84 95.84 0 01256 160z'/></svg>

Before

Width:  |  Height:  |  Size: 720 B

View File

@@ -1 +0,0 @@
<svg xmlns='http://www.w3.org/2000/svg' class='ionicon' viewBox='0 0 512 512'><title>Eye</title><circle cx='256' cy='256' r='64'/><path d='M394.82 141.18C351.1 111.2 304.31 96 255.76 96c-43.69 0-86.28 13-126.59 38.48C88.52 160.23 48.67 207 16 256c26.42 44 62.56 89.24 100.2 115.18C159.38 400.92 206.33 416 255.76 416c49 0 95.85-15.07 139.3-44.79C433.31 345 469.71 299.82 496 256c-26.38-43.43-62.9-88.56-101.18-114.82zM256 352a96 96 0 1196-96 96.11 96.11 0 01-96 96z'/></svg>

Before

Width:  |  Height:  |  Size: 474 B

View File

@@ -1 +0,0 @@
<svg xmlns='http://www.w3.org/2000/svg' class='ionicon' viewBox='0 0 512 512'><title>Lock Closed</title><path d='M420 192h-68v-80a96 96 0 10-192 0v80H92a12 12 0 00-12 12v280a12 12 0 0012 12h328a12 12 0 0012-12V204a12 12 0 00-12-12zm-106 0H198v-80.75a58 58 0 11116 0z'/></svg>

Before

Width:  |  Height:  |  Size: 275 B

View File

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

View File

@@ -7,7 +7,7 @@ services:
memcached: memcached:
image: memcached:1-alpine image: memcached:1-alpine
restart: unless-stopped restart: unless-stopped
entrypoint: memcached -m 128M -I 4M entrypoint: memcached -m 256M -I 128M
ports: ports:
- 11211:11211 - 11211:11211
@@ -16,6 +16,6 @@ services:
depends_on: depends_on:
- memcached - memcached
environment: environment:
SIZE_LIMIT: 4M SIZE_LIMIT: 128M
ports: ports:
- 80:5000 - 80:5000

View File

@@ -3,7 +3,7 @@ version: '3.8'
services: services:
memcached: memcached:
image: memcached:1-alpine image: memcached:1-alpine
entrypoint: memcached -m 128 # Limit to 128 MB Ram, customize at free will. entrypoint: memcached -m 256 -I 128 # Limit to 128 MB Ram, customize at free will. -m must be at least double than -I.
app: app:
image: cupcakearmy/cryptgeon:latest image: cupcakearmy/cryptgeon:latest

18
frontend/README.md Normal file
View File

@@ -0,0 +1,18 @@
# Cryptgeon Frontend
## Locale
Download with these settings:
```json
{
"format": "json",
"indentation": "tab",
"json_unescaped_slashes": true,
"export_sort": "first_added",
"original_filenames": false,
"export_empty_as": "skip",
"add_newline_eof": true,
"replace_breaks": false
}
```

42
frontend/locales/de.json Normal file
View File

@@ -0,0 +1,42 @@
{
"common": {
"note": "Hinweis",
"file": "Datei",
"advanced": "erweitert",
"create": "erstellen",
"loading": "Läd...",
"mode": "Modus",
"views": "{n, plural, =0 {Ansichten} =1 {1 Ansicht} other {# Ansichten}}",
"minutes": "{n, plural, =0 {Minuten} =1 {1 Minute} other {# Minuten}}",
"max": "max",
"share_link": "Link teilen",
"copy_clipboard": "in die Zwischenablage kopieren"
},
"home": {
"intro": "Senden Sie ganz einfach <i>vollständig verschlüsselte</i>, sichere Notizen oder Dateien mit einem Klick. Erstellen Sie einfach eine Notiz und teilen Sie den Link.",
"explanation": "die Notiz verfällt und wird nach {type} zerstört.",
"new_note": "neue Note",
"new_note_notice": "<b>Verfügbarkeit:</b><br />es ist nicht garantiert, dass die Notiz gespeichert wird, da alles im Speicher gehalten wird. Wenn dieser voll ist, werden die ältesten Notizen entfernt.<br />(Sie werden wahrscheinlich keine Probleme haben, seien Sie nur gewarnt).",
"errors": {
"note_to_big": "Notiz konnte nicht erstellt werden. Notiz ist zu groß",
"note_error": "konnte keine Notiz erstellen. Bitte versuchen Sie es erneut.",
"max": "max: {n}",
"empty_content": "Notiz ist leer."
},
"copied_to_clipboard": "in die Zwischenablage kopiert 🔗"
},
"show": {
"errors": {
"not_found": "wurde nicht gefunden oder wurde bereits gelöscht.",
"decryption_failed": "falsches Passwort. konnte nicht entziffert werden. wahrscheinlich ein defekter Link. Notiz wurde zerstört."
},
"explanation": "Klicken Sie unten, um die Notiz anzuzeigen und zu löschen, wenn der Zähler sein Limit erreicht hat",
"show_note": "Notiz anzeigen",
"warning_will_not_see_again": "haben Sie <b>keine</b> Gelegenheit, die Notiz noch einmal zu sehen.",
"download_all": "alle herunterladen"
},
"file_upload": {
"selected_files": "Ausgewählte Dateien",
"no_files_selected": "Keine Dateien ausgewählt"
}
}

42
frontend/locales/en.json Normal file
View File

@@ -0,0 +1,42 @@
{
"common": {
"note": "note",
"file": "file",
"advanced": "advanced",
"create": "create",
"loading": "loading...",
"mode": "mode",
"views": "{n, plural, =0 {views} =1 {1 view} other {# views}}",
"minutes": "{n, plural, =0 {minutes} =1 {1 minute} other {# minutes}}",
"max": "max",
"share_link": "share link",
"copy_clipboard": "copy to clipboard"
},
"home": {
"intro": "Easily send <i>fully encrypted</i>, secure notes or files with one click. Just create a note and share the link.",
"explanation": "the note will expire and be destroyed after {type}.",
"new_note": "new note",
"new_note_notice": "<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.)",
"errors": {
"note_to_big": "could not create note. note is to big",
"note_error": "could not create note. please try again.",
"max": "max: {n}",
"empty_content": "note is empty."
},
"copied_to_clipboard": "copied to clipboard 🔗"
},
"show": {
"errors": {
"not_found": "note was not found or was already deleted.",
"decryption_failed": "wrong password. could not decipher. probably a broken link. note was destroyed."
},
"explanation": "click below to show and delete the note if the counter has reached it's limit",
"show_note": "show note",
"warning_will_not_see_again": "you will <b>not</b> get the chance to see the note again.",
"download_all": "download all"
},
"file_upload": {
"selected_files": "Selected Files",
"no_files_selected": "No Files Selected"
}
}

42
frontend/locales/es.json Normal file
View File

@@ -0,0 +1,42 @@
{
"common": {
"note": "nota",
"file": "archivo",
"advanced": "avanzado",
"create": "crear",
"loading": "cargando...",
"mode": "modo",
"views": "{n, plural, =0 {vistas} =1 {1 vista} other {# vistas}}",
"minutes": "{n, plural, =0 {minutos} =1 {1 minuto} other {# minutos}}",
"max": "max",
"share_link": "compartir enlace",
"copy_clipboard": "copiar al portapapeles"
},
"home": {
"intro": "Envía fácilmente notas o archivos <i>totalmente encriptados</i> y seguros con un solo clic. Solo tienes que crear una nota y compartir el enlace.",
"explanation": "la nota expirará y se destruirá después de {type}.",
"new_note": "nueva nota",
"new_note_notice": "<b>disponibilidad:</b><br />no se garantiza que la nota se almacene, ya que todo se guarda en la memoria RAM, si se llena se eliminarán las notas más antiguas.<br />(probablemente estará bien, sólo está advertido.)",
"errors": {
"note_to_big": "no se pudo crear la nota. la nota es demasiado grande",
"note_error": "No se ha podido crear la nota. Por favor, inténtelo de nuevo.",
"max": "max: {n}",
"empty_content": "la nota está vacía."
},
"copied_to_clipboard": "copiado al portapapeles 🔗"
},
"show": {
"errors": {
"not_found": "la nota no se encontró o ya fue borrada.",
"decryption_failed": "contraseña incorrecta. no se pudo descifrar. probablemente un enlace roto. la nota fue destruida."
},
"explanation": "pulse abajo para mostrar y borrar la nota si el contador ha llegado a su límite",
"show_note": "mostrar nota",
"warning_will_not_see_again": " <b>no</b> tendrás la oportunidad de volver a ver la nota.",
"download_all": "descargar todo"
},
"file_upload": {
"selected_files": "Archivos seleccionados",
"no_files_selected": "No hay archivos seleccionados"
}
}

42
frontend/locales/fr.json Normal file
View File

@@ -0,0 +1,42 @@
{
"common": {
"note": "note",
"file": "fichier",
"advanced": "avancé",
"create": "créer",
"loading": "chargement...",
"mode": "mode",
"views": "{n, plural, =0 {vues} =1 {1 vue} other {# vues}}",
"minutes": "{n, plural, =0 {minutes} =1 {1 minute} other {# minutes}}",
"max": "max",
"share_link": "partager le lien",
"copy_clipboard": "copier dans le presse-papiers"
},
"home": {
"intro": "Envoyez facilement des notes ou des fichiers <i>entièrement cryptés</i> et sécurisés en un seul clic. Il suffit de créer une note et de partager le lien.",
"explanation": "la note expirera et sera détruite après {type}.",
"new_note": "nouvelle note",
"new_note_notice": "<b>disponibilité :</b><br />la note n'est pas garantie d'être stockée car tout est conservé dans la mémoire vive, si elle se remplit les notes les plus anciennes seront supprimées.<br />(vous serez probablement bien, soyez juste averti.)",
"errors": {
"note_to_big": "Impossible de créer une note. La note est trop grande",
"note_error": "n'a pas pu créer de note. Veuillez réessayer.",
"max": "max: {n}",
"empty_content": "La note est vide."
},
"copied_to_clipboard": "copié dans le presse-papiers 🔗"
},
"show": {
"errors": {
"not_found": "La note n'a pas été trouvée ou a déjà été supprimée.",
"decryption_failed": "mauvais mot de passe. impossible à déchiffrer. probablement un lien brisé. la note a été détruite."
},
"explanation": "Cliquez ci-dessous pour afficher et supprimer la note si le compteur a atteint sa limite.",
"show_note": "note de présentation",
"warning_will_not_see_again": "vous <b>n'aurez pas</b> la chance de revoir la note.",
"download_all": "télécharger tout"
},
"file_upload": {
"selected_files": "Fichiers sélectionnés",
"no_files_selected": "Aucun fichier sélectionné"
}
}

42
frontend/locales/it.json Normal file
View File

@@ -0,0 +1,42 @@
{
"common": {
"note": "nota",
"file": "file",
"advanced": "avanzato",
"create": "crea",
"loading": "carica...",
"mode": "modalita",
"views": "{n, plural, =0 {viste} =1 {1 vista} other {# viste}}",
"minutes": "{n, plural, =0 {minuti} =1 {1 minuto} other {# minuti}}",
"max": "max",
"share_link": "condividi link",
"copy_clipboard": "copia negli appunti"
},
"home": {
"intro": "Invia facilmente note o file <i>completamente criptati</i> e sicuri con un solo clic. Basta creare una nota e condividere il link.",
"explanation": "la nota scadrà e sarà distrutta dopo {type}.",
"new_note": "nuova nota",
"new_note_notice": "<b>disponibilità:</b><br />la nota non è garantita per essere memorizzata come tutto è tenuto in ram, se si riempie le note più vecchie saranno rimosse.<br />(probabilmente andrà bene, basta essere avvertiti).",
"errors": {
"note_to_big": "impossibile creare una nota. la nota è troppo grande",
"note_error": "Impossibile creare la nota. Riprova.",
"max": "max: {n}",
"empty_content": "la nota è vuota."
},
"copied_to_clipboard": "copiato negli appunti 🔗"
},
"show": {
"errors": {
"not_found": "non è stata trovata o è stata già cancellata.",
"decryption_failed": "password sbagliata. non ha potuto decifrare. probabilmente un link rotto. la nota è stata distrutta."
},
"explanation": "clicca sotto per mostrare e cancellare la nota se il contatore ha raggiunto il suo limite",
"show_note": "mostra la nota",
"warning_will_not_see_again": " <b>non</b> avrete la possibilità di rivedere la nota.",
"download_all": "scarica tutti"
},
"file_upload": {
"selected_files": "File selezionati",
"no_files_selected": "Nessun file selezionato"
}
}

33
frontend/package.json Normal file
View File

@@ -0,0 +1,33 @@
{
"private": true,
"scripts": {
"dev": "svelte-kit dev",
"build": "svelte-kit build",
"preview": "svelte-kit preview",
"check": "svelte-check --tsconfig tsconfig.json",
"licenses": "license-checker --summary > licenses.csv",
"locale:download": "node scripts/locale.js"
},
"type": "module",
"devDependencies": {
"@lokalise/node-api": "^7.1.1",
"@sveltejs/adapter-static": "^1.0.0-next.28",
"@sveltejs/kit": "1.0.0-next.288",
"@types/file-saver": "^2.0.5",
"adm-zip": "^0.5.9",
"dotenv": "^16.0.0",
"svelte": "^3.46.4",
"svelte-check": "^2.4.5",
"svelte-intl-precompile": "^0.8.1",
"svelte-preprocess": "^4.10.4",
"tslib": "^2.3.1",
"typescript": "^4.6.2",
"vite": "^2.8.6"
},
"dependencies": {
"@fontsource/fira-mono": "^4.5.3",
"copy-to-clipboard": "^3.3.1",
"file-saver": "^2.0.5",
"pretty-bytes": "^5.6.0"
}
}

1551
frontend/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
import dotenv from 'dotenv'
import { LokaliseApi } from '@lokalise/node-api'
import https from 'https'
import AdmZip from 'adm-zip'
dotenv.config()
const apiKey = process.env.LOKALISE_API_KEY
const project_id = process.env.LOKALISE_PROJECT
if (!apiKey) throw new Error('No API Key set for Lokalize! Set with "LOKALISE_API_KEY"')
if (!project_id) throw new Error('No project id set for Lokalize! Set with "LOKALISE_PROJECT"')
const client = new LokaliseApi({ apiKey })
const WGet = (url) =>
new Promise((done) => {
https
.get(url, (res) => {
const data = []
res
.on('data', (chunk) => {
data.push(chunk)
})
.on('end', () => {
let buffer = Buffer.concat(data)
done(buffer)
})
})
.on('error', (err) => {
console.log('download error:', err)
})
})
async function download() {
// For details see: https://app.lokalise.com/api2docs/curl/#transition-download-files-post
const download = await client.files().download(project_id, {
format: 'json',
indentation: 'tab',
json_unescaped_slashes: true,
original_filenames: false,
bundle_structure: '%LANG_ISO%.%FORMAT%',
export_sort: 'first_added',
export_empty_as: 'skip',
add_newline_eof: true,
replace_breaks: false,
})
const buffered = await WGet(download.bundle_url)
const zip = new AdmZip(buffered)
zip.extractAllTo('./locales', true)
}
download().catch((e) => {
console.error(e)
process.exit(1)
})

View File

@@ -7,7 +7,7 @@ export class Files {
}) })
} }
static fromString(s: string): Promise<Blob> { static async fromString(s: string): Promise<Blob> {
return fetch(s).then((r) => r.blob()) return fetch(s).then((r) => r.blob())
} }
} }

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"
><title>Contrast</title><path
d="M256 32C132.29 32 32 132.29 32 256s100.29 224 224 224 224-100.29 224-224S379.71 32 256 32zM128.72 383.28A180 180 0 01256 76v360a178.82 178.82 0 01-127.28-52.72z"
/></svg
>

After

Width:  |  Height:  |  Size: 287 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"
><title>Copy</title><path
d="M456 480H136a24 24 0 01-24-24V128a16 16 0 0116-16h328a24 24 0 0124 24v320a24 24 0 01-24 24z"
/><path
d="M112 80h288V56a24 24 0 00-24-24H60a28 28 0 00-28 28v316a24 24 0 0024 24h24V112a32 32 0 0132-32z"
/></svg
>

After

Width:  |  Height:  |  Size: 325 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"
><title>Dice</title><path
d="M48 366.92L240 480V284L48 170zM192 288c8.84 0 16 10.75 16 24s-7.16 24-16 24-16-10.75-16-24 7.16-24 16-24zm-96 32c8.84 0 16 10.75 16 24s-7.16 24-16 24-16-10.75-16-24 7.16-24 16-24zM272 284v196l192-113.08V170zm48 140c-8.84 0-16-10.75-16-24s7.16-24 16-24 16 10.75 16 24-7.16 24-16 24zm0-88c-8.84 0-16-10.75-16-24s7.16-24 16-24 16 10.75 16 24-7.16 24-16 24zm96 32c-8.84 0-16-10.75-16-24s7.16-24 16-24 16 10.75 16 24-7.16 24-16 24zm0-88c-8.84 0-16-10.75-16-24s7.16-24 16-24 16 10.75 16 24-7.16 24-16 24zm32 77.64zM256 32L64 144l192 112 192-112zm0 120c-13.25 0-24-7.16-24-16s10.75-16 24-16 24 7.16 24 16-10.75 16-24 16z"
/></svg
>

After

Width:  |  Height:  |  Size: 736 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"
><title>Eye</title><circle cx="256" cy="256" r="64" /><path
d="M394.82 141.18C351.1 111.2 304.31 96 255.76 96c-43.69 0-86.28 13-126.59 38.48C88.52 160.23 48.67 207 16 256c26.42 44 62.56 89.24 100.2 115.18C159.38 400.92 206.33 416 255.76 416c49 0 95.85-15.07 139.3-44.79C433.31 345 469.71 299.82 496 256c-26.38-43.43-62.9-88.56-101.18-114.82zM256 352a96 96 0 1196-96 96.11 96.11 0 01-96 96z"
/></svg
>

After

Width:  |  Height:  |  Size: 483 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"
><title>Eye Off</title><path
d="M63.998 86.004l21.998-21.998L448 426.01l-21.998 21.998zM259.34 192.09l60.57 60.57a64.07 64.07 0 00-60.57-60.57zM252.66 319.91l-60.57-60.57a64.07 64.07 0 0060.57 60.57z"
/><path
d="M256 352a96 96 0 01-92.6-121.34l-69.07-69.08C66.12 187.42 39.24 221.14 16 256c26.42 44 62.56 89.24 100.2 115.18C159.38 400.92 206.33 416 255.76 416A233.47 233.47 0 00335 402.2l-53.61-53.6A95.84 95.84 0 01256 352zM256 160a96 96 0 0192.6 121.34L419.26 352c29.15-26.25 56.07-61.56 76.74-96-26.38-43.43-62.9-88.56-101.18-114.82C351.1 111.2 304.31 96 255.76 96a222.92 222.92 0 00-78.21 14.29l53.11 53.11A95.84 95.84 0 01256 160z"
/></svg
>

After

Width:  |  Height:  |  Size: 732 B

View File

@@ -4,13 +4,16 @@ import { writable } from 'svelte/store'
export type Status = { export type Status = {
version: string version: string
max_size: number max_size: number
max_views: number
max_expiration: number
allow_advanced: boolean
} }
export const status = writable<null | Status>(null) export const status = writable<null | Status>(null)
export async function init() { export async function init() {
const data = await call({ const data = await call({
url: 'status', url: 'status/',
method: 'get', method: 'get',
}) })
status.set(data) status.set(data)

View File

@@ -2,6 +2,8 @@
import type { FileDTO } from '$lib/api' import type { FileDTO } from '$lib/api'
import { Files } from '$lib/files' import { Files } from '$lib/files'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import { t } from 'svelte-intl-precompile'
import Button from './Button.svelte'
import MaxSize from './MaxSize.svelte' import MaxSize from './MaxSize.svelte'
export let label: string = '' export let label: string = ''
@@ -11,8 +13,8 @@
async function onInput(e: Event) { async function onInput(e: Event) {
const input = e.target as HTMLInputElement const input = e.target as HTMLInputElement
if (input.files.length) { if (input?.files?.length) {
files = Array.from(input.files) files = [...files, ...Array.from(input.files)]
const data: FileDTO[] = await Promise.all( const data: FileDTO[] = await Promise.all(
files.map(async (file) => ({ files.map(async (file) => ({
name: file.name, name: file.name,
@@ -21,15 +23,17 @@
contents: await Files.toString(file), contents: await Files.toString(file),
})) }))
) )
console.debug(
'files',
data.map((d) => d.contents.length)
)
dispatch('file', JSON.stringify(data)) dispatch('file', JSON.stringify(data))
} else { } else {
dispatch('file', '') dispatch('file', '')
} }
} }
function clear(e: Event) {
e.preventDefault()
files = []
dispatch('file', '')
}
</script> </script>
<label> <label>
@@ -40,18 +44,20 @@
<div class="box"> <div class="box">
{#if files.length} {#if files.length}
<div> <div>
<b>Selected Files</b> <b>{$t('file_upload.selected_files')}</b>
{#each files as file} {#each files as file}
<div class="file"> <div class="file">
{file.name} {file.name}
</div> </div>
{/each} {/each}
<div class="spacer" />
<Button on:click={clear}>Clear</Button>
</div> </div>
{:else} {:else}
<div> <div>
<b>No Files Selected</b> <b>{$t('file_upload.no_files_selected')}</b>
<br /> <br />
<small>max: <MaxSize /></small> <small>{$t('common.max')}: <MaxSize /></small>
</div> </div>
{/if} {/if}
</div> </div>
@@ -69,4 +75,8 @@
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
} }
.spacer {
margin-top: 1rem;
}
</style> </style>

View File

@@ -0,0 +1,37 @@
<script lang="ts" context="module">
import IconContrast from '$lib/icons/IconContrast.svelte'
import IconCopy from '$lib/icons/IconCopy.svelte'
import IconDice from '$lib/icons/IconDice.svelte'
import IconEye from '$lib/icons/IconEye.svelte'
import IconEyeOff from '$lib/icons/IconEyeOff.svelte'
const map = {
contrast: IconContrast,
copy: IconCopy,
dice: IconDice,
eye: IconEye,
'eye-off': IconEyeOff,
}
</script>
<script lang="ts">
export let icon: keyof typeof map
</script>
<div on:click {...$$restProps}>
{#if map[icon]}
<svelte:component this={map[icon]} />
{/if}
</div>
<style>
div {
display: inline-block;
contain: strict;
box-sizing: content-box;
}
div > :global(svg) {
display: block;
fill: currentColor;
}
</style>

View File

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

View File

@@ -4,6 +4,7 @@
import copy from 'copy-to-clipboard' import copy from 'copy-to-clipboard'
import { saveAs } from 'file-saver' import { saveAs } from 'file-saver'
import prettyBytes from 'pretty-bytes' import prettyBytes from 'pretty-bytes'
import { t } from 'svelte-intl-precompile'
import Button from './Button.svelte' import Button from './Button.svelte'
export let note: NotePublic export let note: NotePublic
@@ -28,20 +29,20 @@
} }
</script> </script>
<p class="error-text">you will <b>not</b> get the chance to see the note again.</p> <p class="error-text">{@html $t('show.warning_will_not_see_again')}</p>
{#if note.meta.type === 'text'} {#if note.meta.type === 'text'}
<div class="note" data-testid="note-result"> <div class="note">
{note.contents} {note.contents}
</div> </div>
<Button on:click={() => copy(note.contents)}>copy to clipboard</Button> <Button on:click={() => copy(note.contents)}>{$t('common.copy_clipboard')}</Button>
{:else} {:else}
{#each files as file} {#each files as file}
<div class="note file" data-testid="note-result"> <div class="note file">
<b on:click={() => downloadFile(file)}> {file.name}</b> <b on:click={() => downloadFile(file)}> {file.name}</b>
<small> {file.type} {prettyBytes(file.size)}</small> <small> {file.type} {prettyBytes(file.size)}</small>
</div> </div>
{/each} {/each}
<Button on:click={download}>download all</Button> <Button on:click={download}>{$t('show.download_all')}</Button>
{/if} {/if}
<style> <style>
@@ -59,6 +60,9 @@
.note b { .note b {
cursor: pointer; cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.note.file { .note.file {
@@ -66,4 +70,8 @@
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.note.file small {
padding-left: 1rem;
}
</style> </style>

View File

@@ -49,7 +49,7 @@
height: 2rem; height: 2rem;
width: 1.25rem; width: 1.25rem;
left: 0.125rem; left: 0.125rem;
bottom: 0.1rem; bottom: 0.125rem;
background-color: var(--ui-bg-1); background-color: var(--ui-bg-1);
-webkit-transition: 0.4s; -webkit-transition: 0.4s;
transition: var(--ui-anim); transition: var(--ui-anim);

View File

@@ -1,19 +1,23 @@
<script lang="ts"> <script lang="ts">
import { getRandomBytes, Hex } from '$lib/crypto' import { getRandomBytes, Hex } from '$lib/crypto'
import copyToClipboard from 'copy-to-clipboard' import copyToClipboard from 'copy-to-clipboard'
import { t } from 'svelte-intl-precompile'
import { fade } from 'svelte/transition'
import Icon from './Icon.svelte' import Icon from './Icon.svelte'
export let label: string = '' export let label: string = ''
export let value export let value: any
export let validate: (value: any) => boolean | string = () => true
export let copy: boolean = false export let copy: boolean = false
export let random: boolean = false export let random: boolean = false
const initialType = $$restProps.type const initialType = $$restProps.type
const isPassword = initialType === 'password' const isPassword = initialType === 'password'
let hidden = true let hidden = true
let notification: string | null = null
let notificationTimeout: NodeJS.Timeout | null = null
$: valid = validate(value)
$: if (isPassword) { $: if (isPassword) {
value value
@@ -24,29 +28,46 @@
hidden = !hidden hidden = !hidden
} }
function copyFN() { function copyFN() {
copyToClipboard(value) copyToClipboard(value.toString())
notify($t('home.copied_to_clipboard'))
} }
function randomFN() { function randomFN() {
value = Hex.encode(getRandomBytes(20)) value = Hex.encode(getRandomBytes(20))
} }
function notify(msg: string, delay: number = 2000) {
if (notificationTimeout) {
clearTimeout(notificationTimeout)
}
notificationTimeout = setTimeout(() => {
notification = null
}, delay)
notification = msg
}
</script> </script>
<label> <label>
<small disabled={$$restProps.disabled}> <small disabled={$$restProps.disabled}>
{label} {label}
{#if valid !== true}
<span class="error-text">{valid}</span>
{/if}
</small> </small>
<input bind:value {...$$restProps} /> <input bind:value {...$$restProps} class:valid={valid === true} />
<div class="icons"> <div class="icons">
{#if isPassword} {#if isPassword}
<Icon class="icon" icon={hidden ? 'eye-sharp' : 'eye-off-sharp'} on:click={toggle} /> <Icon class="icon" icon={hidden ? 'eye' : 'eye-off'} on:click={toggle} />
{/if} {/if}
{#if random} {#if random}
<Icon class="icon" icon="dice-sharp" on:click={randomFN} /> <Icon class="icon" icon="dice" on:click={randomFN} />
{/if} {/if}
{#if copy} {#if copy}
<Icon class="icon" icon="copy-sharp" on:click={copyFN} /> <Icon class="icon" icon="copy" on:click={copyFN} />
{/if} {/if}
</div> </div>
{#if notification}
<div class="notification" transition:fade><small>{notification}</small></div>
{/if}
</label> </label>
<style> <style>
@@ -55,6 +76,10 @@
display: block; display: block;
} }
label > small {
display: block;
}
input { input {
width: 100%; width: 100%;
margin: 0; margin: 0;
@@ -68,6 +93,10 @@
border-color: var(--ui-clr-primary); border-color: var(--ui-clr-primary);
} }
input:not(.valid) {
border-color: var(--ui-clr-error);
}
.icons { .icons {
border: 1px red; border: 1px red;
position: absolute; position: absolute;
@@ -88,4 +117,11 @@
.icons > :global(.icon:hover) { .icons > :global(.icon:hover) {
border-color: var(--ui-clr-primary); border-color: var(--ui-clr-primary);
} }
.notification {
text-align: right;
position: absolute;
right: 0;
bottom: -1.5em;
}
</style> </style>

View File

@@ -41,7 +41,7 @@
</script> </script>
<div on:click={change}> <div on:click={change}>
<Icon class="icon" icon="contrast-sharp" /> <Icon class="icon" icon="contrast" />
{$theme} {$theme}
</div> </div>

View File

@@ -1,12 +1,14 @@
<script lang="ts"> <script lang="ts">
import { create, Note, PayloadToLargeError } from '$lib/api' import { create, Note, PayloadToLargeError } from '$lib/api'
import { encrypt, getKeyFromString, getRandomBytes, Hex } from '$lib/crypto' import { encrypt, getKeyFromString, getRandomBytes, Hex } from '$lib/crypto'
import { status } from '$lib/stores/status'
import Button from '$lib/ui/Button.svelte' import Button from '$lib/ui/Button.svelte'
import FileUpload from '$lib/ui/FileUpload.svelte' import FileUpload from '$lib/ui/FileUpload.svelte'
import MaxSize from '$lib/ui/MaxSize.svelte' import MaxSize from '$lib/ui/MaxSize.svelte'
import Switch from '$lib/ui/Switch.svelte' import Switch from '$lib/ui/Switch.svelte'
import TextArea from '$lib/ui/TextArea.svelte' import TextArea from '$lib/ui/TextArea.svelte'
import TextInput from '$lib/ui/TextInput.svelte' import TextInput from '$lib/ui/TextInput.svelte'
import { t } from 'svelte-intl-precompile'
import { blur } from 'svelte/transition' import { blur } from 'svelte/transition'
let note: Note = { let note: Note = {
@@ -18,20 +20,24 @@
let result: { password: string; id: string } | null = null let result: { password: string; id: string } | null = null
let advanced = false let advanced = false
let file = false let file = false
let type = false let timeExpiration = false
let message = '' let message = ''
let loading = false let loading = false
let error: string | null = null let error: string | null = null
$: if (!advanced) { $: if (!advanced) {
note.views = 1 note.views = 1
type = false timeExpiration = false
} }
$: { $: {
let fraction: string message = $t('home.explanation', {
fraction = type ? `${note.expiration} minutes` : `${note.views} views` values: {
message = 'the note will expire and be destroyed after ' + fraction type: $t(timeExpiration ? 'common.minutes' : 'common.views', {
values: { n: (timeExpiration ? note.expiration : note.views) ?? '?' },
}),
},
})
} }
$: note.meta.type = file ? 'file' : 'text' $: note.meta.type = file ? 'file' : 'text'
@@ -40,20 +46,21 @@
note.contents = '' note.contents = ''
} }
class EmptyContentError extends Error {}
async function submit() { async function submit() {
try { try {
error = null error = null
loading = true loading = true
const password = Hex.encode(getRandomBytes(32)) const password = Hex.encode(getRandomBytes(32))
const key = await getKeyFromString(password) const key = await getKeyFromString(password)
if (note.contents === '') throw new EmptyContentError()
const data: Note = { const data: Note = {
contents: await encrypt(note.contents, key), contents: await encrypt(note.contents, key),
meta: note.meta, meta: note.meta,
} }
// @ts-ignore if (timeExpiration) data.expiration = parseInt(note.expiration as any)
if (type) data.expiration = parseInt(note.expiration) else data.views = parseInt(note.views as any)
// @ts-ignore
else data.views = parseInt(note.views)
const response = await create(data) const response = await create(data)
result = { result = {
@@ -62,9 +69,11 @@
} }
} catch (e) { } catch (e) {
if (e instanceof PayloadToLargeError) { if (e instanceof PayloadToLargeError) {
error = 'could not create not. note is to big' error = $t('home.errors.note_to_big')
} else if (e instanceof EmptyContentError) {
error = $t('home.errors.empty_content')
} else { } else {
error = 'could not create note. please try again.' error = $t('home.errors.note_error')
} }
} finally { } finally {
loading = false loading = false
@@ -80,44 +89,38 @@
<TextInput <TextInput
type="text" type="text"
readonly readonly
label="share link" label={$t('common.share_link')}
value="{window.location.origin}/note/{result.id}#{result.password}" value="{window.location.origin}/note/{result.id}#{result.password}"
copy copy
data-testid="note-share-link"
/> />
<br /> <br />
<p> <p>
<b>availability:</b> {@html $t('home.new_note_notice')}
<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> </p>
<br /> <br />
<Button on:click={reset}>new note</Button> <Button on:click={reset}>{$t('home.new_note')}</Button>
{:else} {:else}
<p>
{@html $t('home.intro')}
</p>
<form on:submit|preventDefault={submit}> <form on:submit|preventDefault={submit}>
<fieldset disabled={loading}> <fieldset disabled={loading}>
{#if file} {#if file}
<FileUpload label="file" on:file={(f) => (note.contents = f.detail)} /> <FileUpload label={$t('common.file')} on:file={(f) => (note.contents = f.detail)} />
{:else} {:else}
<TextArea <TextArea label={$t('common.note')} bind:value={note.contents} placeholder="..." />
label="note"
bind:value={note.contents}
placeholder="..."
data-testid="input-note"
/>
{/if} {/if}
<div class="bottom"> <div class="bottom">
<Switch class="file" label="file" bind:value={file} /> <Switch class="file" label={$t('common.file')} bind:value={file} />
<Switch label="advanced" bind:value={advanced} /> {#if $status?.allow_advanced}
<Switch label={$t('common.advanced')} bind:value={advanced} />
{/if}
<div class="grow" /> <div class="grow" />
<div class="tr"> <div class="tr">
<small>max: <MaxSize /> </small> <small>{$t('common.max')}: <MaxSize /> </small>
<br /> <br />
<Button type="submit" data-testid="button-create">create</Button> <Button type="submit">{$t('common.create')}</Button>
</div> </div>
</div> </div>
@@ -128,7 +131,7 @@
<p> <p>
<br /> <br />
{#if loading} {#if loading}
loading... {$t('common.loading')}
{:else} {:else}
{message} {message}
{/if} {/if}
@@ -140,20 +143,26 @@
<div class="fields"> <div class="fields">
<TextInput <TextInput
type="number" type="number"
label="views" label={$t('common.views', { values: { n: 0 } })}
bind:value={note.views} bind:value={note.views}
disabled={type} disabled={timeExpiration}
max={100} max={$status?.max_views}
validate={(v) =>
($status && v < $status?.max_views) ||
$t('home.errors.max', { values: { n: $status?.max_views ?? 0 } })}
/> />
<div class="middle-switch"> <div class="middle-switch">
<Switch label="mode" bind:value={type} color={false} /> <Switch label={$t('common.mode')} bind:value={timeExpiration} color={false} />
</div> </div>
<TextInput <TextInput
type="number" type="number"
label="minutes" label={$t('common.minutes', { values: { n: 0 } })}
bind:value={note.expiration} bind:value={note.expiration}
disabled={!type} disabled={!timeExpiration}
max={360} max={$status?.max_expiration}
validate={(v) =>
($status && v < $status?.max_expiration) ||
$t('home.errors.max', { values: { n: $status?.max_expiration ?? 0 } })}
/> />
</div> </div>
</div> </div>

View File

@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import Icon from '$lib/ui/Icon.svelte'
import ThemeToggle from '$lib/ui/ThemeToggle.svelte' import ThemeToggle from '$lib/ui/ThemeToggle.svelte'
</script> </script>

View File

@@ -83,7 +83,7 @@
header { header {
text-align: center; text-align: center;
margin-top: 4rem; margin-top: 3rem;
margin-bottom: 2rem; margin-bottom: 2rem;
} }

View File

@@ -0,0 +1,42 @@
<script lang="ts" context="module">
import { init, waitLocale, getLocaleFromNavigator } from 'svelte-intl-precompile'
// @ts-ignore
import { registerAll } from '$locales'
registerAll()
init({ initialLocale: getLocaleFromNavigator() ?? undefined, fallbackLocale: 'en' })
</script>
<script lang="ts">
import { init as initStores } 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(() => {
initStores()
})
</script>
<svelte:head>
<title>cryptgeon</title>
</svelte:head>
{#await waitLocale() then _}
<main>
<Header />
<slot />
</main>
<Footer />
{/await}
<style>
main {
padding: 1rem;
padding-bottom: 4rem;
width: 100%;
max-width: 35rem;
margin: 0 auto;
}
</style>

View File

@@ -5,7 +5,6 @@
export const hydrate = dev export const hydrate = dev
export const router = browser export const router = browser
export const prerender = true
</script> </script>
<svelte:head> <svelte:head>
@@ -22,10 +21,12 @@
<AboutParagraph title="how does it work?"> <AboutParagraph title="how does it work?">
<span> <span>
each note has a 512bit generated <i>id</i> that is used to retrieve the note. the note is then each note has a generated <code>id (256bit)</code> and <code>key 256(bit)</code>. The
encrypted with aes in gcm mode on the client side and then sent to the server. data is stored in <code>id</code>
memory and never persisted to disk. the server never sees the encryption key and cannot decrypt is used to save & retrieve the note. the note is then encrypted with aes in gcm mode on the client
the contents of the notes even if it tried to. 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> </span>
</AboutParagraph> </AboutParagraph>
@@ -48,6 +49,14 @@
</span> </span>
</AboutParagraph> </AboutParagraph>
<AboutParagraph title="translations">
<span
>translations are managed on <a href="https://lokalise.com/" target="_blank">Lokalise</a>,
which granted an open source license to use the paid version. If you are interested in helping
translating don't hesitate to contact me!
</span>
</AboutParagraph>
<AboutParagraph title="attribution"> <AboutParagraph title="attribution">
<span> <span>
icons made by <a href="https://www.freepik.com" title="Freepik">freepik</a> from icons made by <a href="https://www.freepik.com" title="Freepik">freepik</a> from

View File

@@ -1,7 +1,3 @@
<script context="module" lang="ts">
export const prerender = true
</script>
<script lang="ts"> <script lang="ts">
import Create from '$lib/views/Create.svelte' import Create from '$lib/views/Create.svelte'
</script> </script>

View File

@@ -1,18 +1,22 @@
<script context="module" lang="ts"> <script context="module" lang="ts">
export async function load({ page }) { import type { Load } from '@sveltejs/kit'
export const load: Load = async ({ params }) => {
return { return {
props: page.params, props: params,
} }
} }
</script> </script>
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'
import { t } from 'svelte-intl-precompile'
import type { NotePublic } from '$lib/api' import type { NotePublic } from '$lib/api'
import { get, info } from '$lib/api' import { get, info } from '$lib/api'
import { decrypt, getKeyFromString } from '$lib/crypto' import { decrypt, getKeyFromString } from '$lib/crypto'
import Button from '$lib/ui/Button.svelte' import Button from '$lib/ui/Button.svelte'
import ShowNote from '$lib/ui/ShowNote.svelte' import ShowNote from '$lib/ui/ShowNote.svelte'
import { onMount } from 'svelte'
export let id: string export let id: string
@@ -26,7 +30,7 @@
onMount(async () => { onMount(async () => {
try { try {
loading = true loading = true
error = null error = false
password = window.location.hash.slice(1) password = window.location.hash.slice(1)
await info(id) await info(id)
exists = true exists = true
@@ -55,20 +59,18 @@
{#if !loading} {#if !loading}
{#if !exists} {#if !exists}
<p class="error-text" data-testid="note-not-found"> <p class="error-text">{$t('show.errors.not_found')}</p>
note was not found or was already deleted.
</p>
{:else if note && !error} {:else if note && !error}
<ShowNote {note} /> <ShowNote {note} />
{:else} {:else}
<form on:submit|preventDefault={show}> <form on:submit|preventDefault={show}>
<fieldset> <fieldset>
<p>click below to show and delete the note if the counter has reached it's limit</p> <p>{$t('show.explanation')}</p>
<Button type="submit" data-testid="button-show">show note</Button> <Button type="submit">{$t('show.show_note')}</Button>
{#if error} {#if error}
<br /> <br />
<p class="error-text"> <p class="error-text">
wrong password. could not decipher. probably a broken link. note was destroyed. {$t('show.errors.decryption_failed')}
<br /> <br />
</p> </p>
{/if} {/if}
@@ -77,5 +79,5 @@
{/if} {/if}
{/if} {/if}
{#if loading} {#if loading}
<p>loading...</p> <p>{$t('common.loading')}</p>
{/if} {/if}

View File

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@@ -1,3 +1,4 @@
# https://www.robotstxt.org/robotstxt.html # https://www.robotstxt.org/robotstxt.html
User-agent: * User-agent: *
Disallow: Allow: /$
Disallow: /

View File

@@ -1,5 +1,6 @@
import preprocess from 'svelte-preprocess' import preprocess from 'svelte-preprocess'
import adapter from '@sveltejs/adapter-static' import adapter from '@sveltejs/adapter-static'
import precompileIntl from 'svelte-intl-precompile/sveltekit-plugin'
export default { export default {
preprocess: preprocess(), preprocess: preprocess(),
@@ -8,6 +9,10 @@ export default {
adapter: adapter({ adapter: adapter({
fallback: 'index.html', fallback: 'index.html',
}), }),
target: '#svelte', vite: {
plugins: [
precompileIntl('locales'), // if your translations are defined in /locales/[lang].json
],
},
}, },
} }

View File

@@ -24,7 +24,8 @@
"checkJs": true, "checkJs": true,
"paths": { "paths": {
"$lib/*": ["src/lib/*"] "$lib/*": ["src/lib/*"]
} },
"strict": true
}, },
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"] "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"]
} }

View File

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

207
pnpm-lock.yaml generated
View File

@@ -1,14 +1,8 @@
lockfileVersion: 5.3 lockfileVersion: 5.3
specifiers: specifiers:
file-saver: ^2.0.5
http-proxy: ^1.18.1 http-proxy: ^1.18.1
npm-run-all: ^4.1.5 npm-run-all: ^4.1.5
pretty-bytes: ^5.6.0
dependencies:
file-saver: 2.0.5
pretty-bytes: 5.6.0
devDependencies: devDependencies:
http-proxy: 1.18.1 http-proxy: 1.18.1
@@ -88,21 +82,25 @@ packages:
is-arrayish: 0.2.1 is-arrayish: 0.2.1
dev: true dev: true
/es-abstract/1.18.0: /es-abstract/1.19.1:
resolution: {integrity: sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==} resolution: {integrity: sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
es-to-primitive: 1.2.1 es-to-primitive: 1.2.1
function-bind: 1.1.1 function-bind: 1.1.1
get-intrinsic: 1.1.1 get-intrinsic: 1.1.1
get-symbol-description: 1.0.0
has: 1.0.3 has: 1.0.3
has-symbols: 1.0.2 has-symbols: 1.0.2
is-callable: 1.2.3 internal-slot: 1.0.3
is-negative-zero: 2.0.1 is-callable: 1.2.4
is-regex: 1.1.2 is-negative-zero: 2.0.2
is-string: 1.0.5 is-regex: 1.1.4
object-inspect: 1.10.2 is-shared-array-buffer: 1.0.1
is-string: 1.0.7
is-weakref: 1.0.2
object-inspect: 1.12.0
object-keys: 1.1.1 object-keys: 1.1.1
object.assign: 4.1.2 object.assign: 4.1.2
string.prototype.trimend: 1.0.4 string.prototype.trimend: 1.0.4
@@ -114,9 +112,9 @@ packages:
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies: dependencies:
is-callable: 1.2.3 is-callable: 1.2.4
is-date-object: 1.0.2 is-date-object: 1.0.5
is-symbol: 1.0.3 is-symbol: 1.0.4
dev: true dev: true
/escape-string-regexp/1.0.5: /escape-string-regexp/1.0.5:
@@ -128,12 +126,8 @@ packages:
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
dev: true dev: true
/file-saver/2.0.5: /follow-redirects/1.14.7:
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} resolution: {integrity: sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==}
dev: false
/follow-redirects/1.14.6:
resolution: {integrity: sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==}
engines: {node: '>=4.0'} engines: {node: '>=4.0'}
peerDependencies: peerDependencies:
debug: '*' debug: '*'
@@ -154,8 +148,16 @@ packages:
has-symbols: 1.0.2 has-symbols: 1.0.2
dev: true dev: true
/graceful-fs/4.2.6: /get-symbol-description/1.0.0:
resolution: {integrity: sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==} resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
get-intrinsic: 1.1.1
dev: true
/graceful-fs/4.2.9:
resolution: {integrity: sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==}
dev: true dev: true
/has-bigints/1.0.1: /has-bigints/1.0.1:
@@ -172,6 +174,13 @@ packages:
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dev: true dev: true
/has-tostringtag/1.0.0:
resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==}
engines: {node: '>= 0.4'}
dependencies:
has-symbols: 1.0.2
dev: true
/has/1.0.3: /has/1.0.3:
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
engines: {node: '>= 0.4.0'} engines: {node: '>= 0.4.0'}
@@ -188,73 +197,101 @@ packages:
engines: {node: '>=8.0.0'} engines: {node: '>=8.0.0'}
dependencies: dependencies:
eventemitter3: 4.0.7 eventemitter3: 4.0.7
follow-redirects: 1.14.6 follow-redirects: 1.14.7
requires-port: 1.0.0 requires-port: 1.0.0
transitivePeerDependencies: transitivePeerDependencies:
- debug - debug
dev: true dev: true
/internal-slot/1.0.3:
resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==}
engines: {node: '>= 0.4'}
dependencies:
get-intrinsic: 1.1.1
has: 1.0.3
side-channel: 1.0.4
dev: true
/is-arrayish/0.2.1: /is-arrayish/0.2.1:
resolution: {integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=} resolution: {integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=}
dev: true dev: true
/is-bigint/1.0.1: /is-bigint/1.0.4:
resolution: {integrity: sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==} resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
dependencies:
has-bigints: 1.0.1
dev: true dev: true
/is-boolean-object/1.1.0: /is-boolean-object/1.1.2:
resolution: {integrity: sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==} resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
has-tostringtag: 1.0.0
dev: true dev: true
/is-callable/1.2.3: /is-callable/1.2.4:
resolution: {integrity: sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==} resolution: {integrity: sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dev: true dev: true
/is-core-module/2.3.0: /is-core-module/2.8.1:
resolution: {integrity: sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==} resolution: {integrity: sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==}
dependencies: dependencies:
has: 1.0.3 has: 1.0.3
dev: true dev: true
/is-date-object/1.0.2: /is-date-object/1.0.5:
resolution: {integrity: sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==} resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
engines: {node: '>= 0.4'}
dependencies:
has-tostringtag: 1.0.0
dev: true
/is-negative-zero/2.0.2:
resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dev: true dev: true
/is-negative-zero/2.0.1: /is-number-object/1.0.6:
resolution: {integrity: sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==} resolution: {integrity: sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies:
has-tostringtag: 1.0.0
dev: true dev: true
/is-number-object/1.0.4: /is-regex/1.1.4:
resolution: {integrity: sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==} resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
engines: {node: '>= 0.4'}
dev: true
/is-regex/1.1.2:
resolution: {integrity: sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
has-symbols: 1.0.2 has-tostringtag: 1.0.0
dev: true dev: true
/is-string/1.0.5: /is-shared-array-buffer/1.0.1:
resolution: {integrity: sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==} resolution: {integrity: sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==}
dev: true
/is-string/1.0.7:
resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies:
has-tostringtag: 1.0.0
dev: true dev: true
/is-symbol/1.0.3: /is-symbol/1.0.4:
resolution: {integrity: sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==} resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies: dependencies:
has-symbols: 1.0.2 has-symbols: 1.0.2
dev: true dev: true
/is-weakref/1.0.2:
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
dependencies:
call-bind: 1.0.2
dev: true
/isexe/2.0.0: /isexe/2.0.0:
resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=} resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=}
dev: true dev: true
@@ -267,7 +304,7 @@ packages:
resolution: {integrity: sha1-L19Fq5HjMhYjT9U62rZo607AmTs=} resolution: {integrity: sha1-L19Fq5HjMhYjT9U62rZo607AmTs=}
engines: {node: '>=4'} engines: {node: '>=4'}
dependencies: dependencies:
graceful-fs: 4.2.6 graceful-fs: 4.2.9
parse-json: 4.0.0 parse-json: 4.0.0
pify: 3.0.0 pify: 3.0.0
strip-bom: 3.0.0 strip-bom: 3.0.0
@@ -292,7 +329,7 @@ packages:
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
dependencies: dependencies:
hosted-git-info: 2.8.9 hosted-git-info: 2.8.9
resolve: 1.20.0 resolve: 1.21.0
semver: 5.7.1 semver: 5.7.1
validate-npm-package-license: 3.0.4 validate-npm-package-license: 3.0.4
dev: true dev: true
@@ -309,12 +346,12 @@ packages:
minimatch: 3.0.4 minimatch: 3.0.4
pidtree: 0.3.1 pidtree: 0.3.1
read-pkg: 3.0.0 read-pkg: 3.0.0
shell-quote: 1.7.2 shell-quote: 1.7.3
string.prototype.padend: 3.1.2 string.prototype.padend: 3.1.3
dev: true dev: true
/object-inspect/1.10.2: /object-inspect/1.12.0:
resolution: {integrity: sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA==} resolution: {integrity: sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==}
dev: true dev: true
/object-keys/1.1.1: /object-keys/1.1.1:
@@ -345,8 +382,8 @@ packages:
engines: {node: '>=4'} engines: {node: '>=4'}
dev: true dev: true
/path-parse/1.0.6: /path-parse/1.0.7:
resolution: {integrity: sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==} resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
dev: true dev: true
/path-type/3.0.0: /path-type/3.0.0:
@@ -367,11 +404,6 @@ packages:
engines: {node: '>=4'} engines: {node: '>=4'}
dev: true dev: true
/pretty-bytes/5.6.0:
resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
engines: {node: '>=6'}
dev: false
/read-pkg/3.0.0: /read-pkg/3.0.0:
resolution: {integrity: sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=} resolution: {integrity: sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=}
engines: {node: '>=4'} engines: {node: '>=4'}
@@ -385,11 +417,13 @@ packages:
resolution: {integrity: sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=} resolution: {integrity: sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=}
dev: true dev: true
/resolve/1.20.0: /resolve/1.21.0:
resolution: {integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==} resolution: {integrity: sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==}
hasBin: true
dependencies: dependencies:
is-core-module: 2.3.0 is-core-module: 2.8.1
path-parse: 1.0.6 path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
dev: true dev: true
/semver/5.7.1: /semver/5.7.1:
@@ -409,15 +443,23 @@ packages:
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: true dev: true
/shell-quote/1.7.2: /shell-quote/1.7.3:
resolution: {integrity: sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==} resolution: {integrity: sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==}
dev: true
/side-channel/1.0.4:
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
dependencies:
call-bind: 1.0.2
get-intrinsic: 1.1.1
object-inspect: 1.12.0
dev: true dev: true
/spdx-correct/3.1.1: /spdx-correct/3.1.1:
resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==} resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
dependencies: dependencies:
spdx-expression-parse: 3.0.1 spdx-expression-parse: 3.0.1
spdx-license-ids: 3.0.7 spdx-license-ids: 3.0.11
dev: true dev: true
/spdx-exceptions/2.3.0: /spdx-exceptions/2.3.0:
@@ -428,20 +470,20 @@ packages:
resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
dependencies: dependencies:
spdx-exceptions: 2.3.0 spdx-exceptions: 2.3.0
spdx-license-ids: 3.0.7 spdx-license-ids: 3.0.11
dev: true dev: true
/spdx-license-ids/3.0.7: /spdx-license-ids/3.0.11:
resolution: {integrity: sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==} resolution: {integrity: sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==}
dev: true dev: true
/string.prototype.padend/3.1.2: /string.prototype.padend/3.1.3:
resolution: {integrity: sha512-/AQFLdYvePENU3W5rgurfWSMU6n+Ww8n/3cUt7E+vPBB/D7YDG8x+qjoFs4M/alR2bW7Qg6xMjVwWUOvuQ0XpQ==} resolution: {integrity: sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
define-properties: 1.1.3 define-properties: 1.1.3
es-abstract: 1.18.0 es-abstract: 1.19.1
dev: true dev: true
/string.prototype.trimend/1.0.4: /string.prototype.trimend/1.0.4:
@@ -470,6 +512,11 @@ packages:
has-flag: 3.0.0 has-flag: 3.0.0
dev: true dev: true
/supports-preserve-symlinks-flag/1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
dev: true
/unbox-primitive/1.0.1: /unbox-primitive/1.0.1:
resolution: {integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==} resolution: {integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==}
dependencies: dependencies:
@@ -489,11 +536,11 @@ packages:
/which-boxed-primitive/1.0.2: /which-boxed-primitive/1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
dependencies: dependencies:
is-bigint: 1.0.1 is-bigint: 1.0.4
is-boolean-object: 1.1.0 is-boolean-object: 1.1.2
is-number-object: 1.0.4 is-number-object: 1.0.6
is-string: 1.0.5 is-string: 1.0.7
is-symbol: 1.0.3 is-symbol: 1.0.4
dev: true dev: true
/which/1.3.1: /which/1.3.1:

View File

@@ -1,19 +1,15 @@
import http from 'http' import http from 'http'
import httpProxy from 'http-proxy' import httpProxy from 'http-proxy'
function start() { const proxy = httpProxy.createProxyServer()
try { proxy.on('error', function (err, req, res) {
const proxy = httpProxy.createProxyServer({}) res.writeHead(500, { 'Content-Type': 'text/plain' })
const server = http.createServer(function (req, res) { res.end('500 Internal Server Error')
const target = req.url.startsWith('/api/') ? 'http://localhost:5000' : 'http://localhost:3000' })
proxy.web(req, res, { target })
})
server.listen(1234)
server.on('error', () => start()) const server = http.createServer(function (req, res) {
} catch (e) { const target = req.url.startsWith('/api/') ? 'http://localhost:5000' : 'http://localhost:3000'
start() proxy.web(req, res, { target })
} })
} server.listen(1234)
console.log('Proxy on http://localhost:1234')
start()

View File

@@ -1,10 +0,0 @@
use actix_files::{Files, NamedFile};
use actix_web::{web, Responder};
pub fn init(cfg: &mut web::ServiceConfig) {
cfg.service(Files::new("/", "./client/build").index_file("index.html"));
}
pub async fn fallback_fn() -> impl Responder {
NamedFile::open("./client/build/index.html")
}