Compare commits
41 Commits
Author | SHA1 | Date | |
---|---|---|---|
a257d2cefb | |||
35ba25ba9e | |||
724dca0e69 | |||
9029f72a02 | |||
1d55d7f2d2 | |||
d09bb4e0c6 | |||
53c7c9d9e2 | |||
df9c60c29e | |||
f29b6b23f0 | |||
cc88fa6763 | |||
19022e7cb5 | |||
44f43dbc2c | |||
45f6f3af32 | |||
9bd544f0d5 | |||
a315e58284 | |||
d576b71bc5 | |||
e02f7f59c6 | |||
e8c6467faa | |||
43f67c795d | |||
83f0902291 | |||
11a6621bd7 | |||
36fa451249 | |||
d112eba8fe | |||
ef39f9ec0b | |||
8517c20e6c | |||
728ad56b33 | |||
f185ccee03 | |||
284bbcbae2 | |||
7eba454f1b | |||
dcd9efaeba | |||
f13bcbaf3f | |||
8e7e0414a6 | |||
229c8d8368 | |||
1adf87b884 | |||
a061b540b1 | |||
824603ff4a | |||
539d99d35f | |||
716034660c | |||
bab65bcdad | |||
a0732a4593 | |||
835f7df0f6 |
@@ -1,2 +1,15 @@
|
|||||||
target
|
*
|
||||||
node_modules
|
!/entry.sh
|
||||||
|
|
||||||
|
!/backend/src
|
||||||
|
!/backend/Cargo.lock
|
||||||
|
!/backend/Cargo.toml
|
||||||
|
|
||||||
|
!/frontend/locales
|
||||||
|
!/frontend/src
|
||||||
|
!/frontend/static
|
||||||
|
!/frontend/.npmrc
|
||||||
|
!/frontend/package.json
|
||||||
|
!/frontend/pnpm-lock.yaml
|
||||||
|
!/frontend/svelte.config.js
|
||||||
|
!/frontend/tsconfig.json
|
||||||
|
BIN
.github/lokalise.png
vendored
Normal file
After Width: | Height: | Size: 30 KiB |
2
.gitignore
vendored
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
# Backend
|
# Backend
|
||||||
/target
|
target
|
||||||
|
|
||||||
# Client
|
# Client
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
8
.vscode/settings.json
vendored
@@ -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"
|
||||||
}
|
}
|
62
CHANGELOG.md
@@ -5,6 +5,68 @@ 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.5.3] - 2022-06-07
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Use the value from the `MEMCACHE` env variable in startup script.
|
||||||
|
|
||||||
|
## [1.5.2] - 2022-06-07
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Wait for script for memecached.
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- Updated dependencies.
|
||||||
|
|
||||||
|
## [1.5.1] - 2022-05-15
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Remove double note content.
|
||||||
|
|
||||||
|
## [1.5.0] - 2022-05-14
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Links in notes are not highlighted and can be directly clicked #30.
|
||||||
|
|
||||||
|
## [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
|
## [1.3.1] - 2021-12-30
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
2126
Cargo.lock
generated
27
Dockerfile
@@ -1,29 +1,26 @@
|
|||||||
FROM node:16-alpine as CLIENT
|
# FRONTEND
|
||||||
|
FROM node:16-alpine as client
|
||||||
WORKDIR /tmp
|
WORKDIR /tmp
|
||||||
COPY ./client ./
|
|
||||||
|
|
||||||
RUN npm install -g pnpm
|
RUN npm install -g pnpm
|
||||||
|
COPY ./frontend ./
|
||||||
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.61-alpine as backend
|
||||||
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
|
|
||||||
|
|
||||||
|
# RUNNER
|
||||||
|
FROM alpine
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=RUST /tmp/target/release/cryptgeon .
|
COPY ./entry.sh .
|
||||||
COPY --from=CLIENT /tmp/build ./client/build
|
COPY --from=backend /tmp/target/release/cryptgeon .
|
||||||
|
COPY --from=client /tmp/build ./frontend/build
|
||||||
ENV MEMCACHE=memcached:11211
|
ENV MEMCACHE=memcached:11211
|
||||||
|
|
||||||
EXPOSE 5000
|
EXPOSE 5000
|
||||||
|
ENTRYPOINT [ "/app/entry.sh" ]
|
||||||
ENTRYPOINT [ "/app/cryptgeon" ]
|
|
||||||
|
44
README.md
@@ -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.
|
||||||
@@ -44,9 +49,12 @@ of the notes even if it tried to.
|
|||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|
||||||
| Variable | Default | Description |
|
| Variable | Default | Description |
|
||||||
| ------------ | ----------------- | --------------------------------------------------------------------------------------- |
|
| ---------------- | ----------------- | --------------------------------------------------------------------------------------- |
|
||||||
| `MEMCACHE` | `memcached:11211` | Memcached URL to connect to. |
|
| `MEMCACHE` | `memcached:11211` | Memcached URL to connect to. |
|
||||||
| `SIZE_LIMIT` | `1 KiB` | Max size for body. Accepted values according to [byte-unit](https://docs.rs/byte-unit/) |
|
| `SIZE_LIMIT` | `1 KiB` | Max size for body. Accepted values according to [byte-unit](https://docs.rs/byte-unit/) |
|
||||||
|
| `MAX_VIEWS` | `100` | Maximal number of views. |
|
||||||
|
| `MAX_EXPIRATION` | `360` | Maximal expiration in minutes. |
|
||||||
|
| `ALLOW_ADVANCED` | `true` | Allow custom configuration. If set to `false` all notes will be one view only. |
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
@@ -119,11 +127,35 @@ services:
|
|||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
1. Clone
|
**Requirements**
|
||||||
2. run `pnpm i` in the root and and client `client/` folders.
|
|
||||||
3. Run `pnpm run dev` to start development.
|
|
||||||
|
|
||||||
Running `npm run dev` in the root folder will start the following things
|
- `pnpm`: `>=6`
|
||||||
|
- `node`: `>=14`
|
||||||
|
- `rust`: edition `2021`
|
||||||
|
|
||||||
|
**Install**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm install
|
||||||
|
pnpm --prefix frontend install
|
||||||
|
|
||||||
|
# Also you need cargo watch if you don't already have it installed.
|
||||||
|
# https://lib.rs/crates/cargo-watch
|
||||||
|
cargo install cargo-watch
|
||||||
|
```
|
||||||
|
|
||||||
|
**Run**
|
||||||
|
|
||||||
|
Make sure you have docker running.
|
||||||
|
|
||||||
|
> If you are on `macOS` you might need to disable AirPlay Receiver as it uses port 5000 (So stupid...)
|
||||||
|
> https://developer.apple.com/forums/thread/682332
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Running `pnpm run dev` in the root folder will start the following things:
|
||||||
|
|
||||||
- a memcache docker container
|
- a memcache docker container
|
||||||
- rust backend with hot reload
|
- rust backend with hot reload
|
||||||
|
1551
backend/Cargo.lock
generated
Normal file
@@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cryptgeon"
|
name = "cryptgeon"
|
||||||
version = "1.3.0"
|
version = "1.5.3"
|
||||||
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
@@ -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
@@ -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
@@ -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();
|
||||||
|
}
|
@@ -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()
|
@@ -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>,
|
||||||
}
|
}
|
||||||
|
|
124
backend/src/note/routes.rs
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
use actix_web::{delete, get, post, web, HttpResponse, Responder, Scope};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
use crate::config;
|
||||||
|
use crate::note::{generate_id, Note, NoteInfo, NotePublic};
|
||||||
|
use crate::store;
|
||||||
|
|
||||||
|
pub fn now() -> u32 {
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct NotePath {
|
||||||
|
id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/{id}")]
|
||||||
|
async fn one(path: web::Path<NotePath>) -> impl Responder {
|
||||||
|
let p = path.into_inner();
|
||||||
|
let note = store::get(&p.id);
|
||||||
|
match note {
|
||||||
|
None => return HttpResponse::NotFound().finish(),
|
||||||
|
Some(_) => return HttpResponse::Ok().json(NoteInfo {}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct CreateResponse {
|
||||||
|
id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/")]
|
||||||
|
async fn create(note: web::Json<Note>) -> impl Responder {
|
||||||
|
let mut n = note.into_inner();
|
||||||
|
let id = generate_id();
|
||||||
|
let bad_req = HttpResponse::BadRequest().finish();
|
||||||
|
if n.views == None && n.expiration == None {
|
||||||
|
return bad_req;
|
||||||
|
}
|
||||||
|
if !*config::ALLOW_ADVANCED {
|
||||||
|
n.views = Some(1);
|
||||||
|
n.expiration = None;
|
||||||
|
}
|
||||||
|
match n.views {
|
||||||
|
Some(v) => {
|
||||||
|
if v > *config::MAX_VIEWS {
|
||||||
|
return bad_req;
|
||||||
|
}
|
||||||
|
n.expiration = None; // views overrides expiration
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
match n.expiration {
|
||||||
|
Some(e) => {
|
||||||
|
if e > *config::MAX_EXPIRATION {
|
||||||
|
return bad_req;
|
||||||
|
}
|
||||||
|
let expiration = now() + (e * 60);
|
||||||
|
n.expiration = Some(expiration);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
store::set(&id.clone(), &n.clone());
|
||||||
|
return HttpResponse::Ok().json(CreateResponse { id: id });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[delete("/{id}")]
|
||||||
|
async fn delete(path: web::Path<NotePath>) -> impl Responder {
|
||||||
|
let p = path.into_inner();
|
||||||
|
let note = store::get(&p.id);
|
||||||
|
match note {
|
||||||
|
None => return HttpResponse::NotFound().finish(),
|
||||||
|
Some(note) => {
|
||||||
|
let mut changed = note.clone();
|
||||||
|
if changed.views == None && changed.expiration == None {
|
||||||
|
return HttpResponse::BadRequest().finish();
|
||||||
|
}
|
||||||
|
match changed.views {
|
||||||
|
Some(v) => {
|
||||||
|
changed.views = Some(v - 1);
|
||||||
|
let id = p.id.clone();
|
||||||
|
if v <= 1 {
|
||||||
|
store::del(&id);
|
||||||
|
} else {
|
||||||
|
store::set(&id, &changed.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let n = now();
|
||||||
|
match changed.expiration {
|
||||||
|
Some(e) => {
|
||||||
|
if e < n {
|
||||||
|
store::del(&p.id.clone());
|
||||||
|
return HttpResponse::BadRequest().finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
return HttpResponse::Ok().json(NotePublic {
|
||||||
|
contents: changed.contents,
|
||||||
|
meta: changed.meta,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct Status {
|
||||||
|
version: String,
|
||||||
|
max_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init() -> Scope {
|
||||||
|
web::scope("/notes")
|
||||||
|
.service(one)
|
||||||
|
.service(create)
|
||||||
|
.service(delete)
|
||||||
|
}
|
18
backend/src/size.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
use actix_web::web;
|
||||||
|
use byte_unit::Byte;
|
||||||
|
use mime;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref LIMIT: usize =
|
||||||
|
Byte::from_str(std::env::var("SIZE_LIMIT").unwrap_or("1 KiB".to_string()))
|
||||||
|
.unwrap()
|
||||||
|
.get_bytes() as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(cfg: &mut web::ServiceConfig) {
|
||||||
|
let json = web::JsonConfig::default().limit(*LIMIT);
|
||||||
|
let plain = web::PayloadConfig::default()
|
||||||
|
.limit(*LIMIT)
|
||||||
|
.mimetype(mime::STAR_STAR);
|
||||||
|
cfg.app_data(json).app_data(plain);
|
||||||
|
}
|
5
backend/src/status/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
mod model;
|
||||||
|
mod routes;
|
||||||
|
|
||||||
|
pub use model::*;
|
||||||
|
pub use routes::*;
|
10
backend/src/status/model.rs
Normal 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,
|
||||||
|
}
|
19
backend/src/status/routes.rs
Normal 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)
|
||||||
|
}
|
36
backend/src/store.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
use memcache;
|
||||||
|
|
||||||
|
use crate::note::now;
|
||||||
|
use crate::note::Note;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref CLIENT: memcache::Client = memcache::connect(format!(
|
||||||
|
"memcache://{}?timeout=10&tcp_nodelay=true",
|
||||||
|
std::env::var("MEMCACHE").unwrap_or("127.0.0.1:11211".to_string())
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(id: &String, note: &Note) {
|
||||||
|
let serialized = serde_json::to_string(¬e.clone()).unwrap();
|
||||||
|
let expiration: u32 = match note.expiration {
|
||||||
|
Some(e) => e - now(),
|
||||||
|
None => 0,
|
||||||
|
};
|
||||||
|
CLIENT.set(id, serialized, expiration).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(id: &String) -> Option<Note> {
|
||||||
|
let value: Option<String> = CLIENT.get(&id).unwrap();
|
||||||
|
match value {
|
||||||
|
None => return None,
|
||||||
|
Some(s) => {
|
||||||
|
let deserialize: Note = serde_json::from_str(&s).unwrap();
|
||||||
|
return Some(deserialize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn del(id: &String) {
|
||||||
|
CLIENT.delete(id).unwrap();
|
||||||
|
}
|
@@ -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.
|
|
@@ -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
@@ -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
|
|
@@ -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>
|
|
@@ -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>
|
|
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"module": "es2020",
|
|
||||||
"lib": ["es2020"],
|
|
||||||
"target": "es2019",
|
|
||||||
/**
|
|
||||||
svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
|
|
||||||
to enforce using \`import type\` instead of \`import\` for Types.
|
|
||||||
*/
|
|
||||||
"importsNotUsedAsValues": "error",
|
|
||||||
"isolatedModules": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
/**
|
|
||||||
To have warnings/errors of the Svelte compiler at the correct position,
|
|
||||||
enable source maps by default.
|
|
||||||
*/
|
|
||||||
"sourceMap": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"baseUrl": ".",
|
|
||||||
"allowJs": true,
|
|
||||||
"checkJs": true,
|
|
||||||
"paths": {
|
|
||||||
"$lib/*": ["src/lib/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"]
|
|
||||||
}
|
|
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"fixturesFolder": false,
|
|
||||||
"pluginsFile": false,
|
|
||||||
"supportFile": false
|
|
||||||
}
|
|
@@ -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
|
||||||
|
13
entry.sh
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
|
||||||
|
HOST=$(echo $MEMCACHE | cut -d: -f1)
|
||||||
|
PORT=$(echo $MEMCACHE | cut -d: -f2)
|
||||||
|
echo "Waiting for memcached at $HOST:$PORT"
|
||||||
|
while ! nc -z -w 1 $HOST $PORT; do
|
||||||
|
sleep 1
|
||||||
|
echo "retrying..."
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Starting server"
|
||||||
|
/app/cryptgeon
|
@@ -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
|
||||||
|
161
examples/scratch/README.md
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
# Install from scratch.
|
||||||
|
|
||||||
|
This is a tiny guide to install cryptgeon on (probably) any unix system (and maybe windows?) from scratch using traefik as the proxy, which will manage certificates and handle https for us.
|
||||||
|
|
||||||
|
1. Install Docker & Docker Compose.
|
||||||
|
2. Install Traefik.
|
||||||
|
3. Run the cryptgeon.
|
||||||
|
4. [Optional] install watchtower to keep up to date.
|
||||||
|
|
||||||
|
## Install Docker & DOcker Compose
|
||||||
|
|
||||||
|
- [Docker](https://docs.docker.com/engine/install/)
|
||||||
|
- [Compose](https://docs.docker.com/compose/install/)
|
||||||
|
|
||||||
|
## Install Traefik 2.0
|
||||||
|
|
||||||
|
[Traefik](https://doc.traefik.io/traefik/) is a router & proxy that makes deployment of containers incredibly easy. It will manage all the https certificates, routing, etc.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/foo/bar/traefik/
|
||||||
|
├── docker-compose.yaml
|
||||||
|
└── traefik.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yaml
|
||||||
|
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
traefik:
|
||||||
|
image: traefik:2.6
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- '80:80'
|
||||||
|
- '443:443'
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- ./traefik.yaml:/etc/traefik/traefik.yaml:ro
|
||||||
|
- ./data:/data
|
||||||
|
labels:
|
||||||
|
- 'traefik.enable=true'
|
||||||
|
|
||||||
|
# HTTP to HTTPS redirection
|
||||||
|
- 'traefik.http.routers.http_catchall.rule=HostRegexp(`{any:.+}`)'
|
||||||
|
- 'traefik.http.routers.http_catchall.entrypoints=insecure'
|
||||||
|
- 'traefik.http.routers.http_catchall.middlewares=https_redirect'
|
||||||
|
- 'traefik.http.middlewares.https_redirect.redirectscheme.scheme=https'
|
||||||
|
- 'traefik.http.middlewares.https_redirect.redirectscheme.permanent=true'
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
external: true
|
||||||
|
name: proxy
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# traefik.yaml
|
||||||
|
|
||||||
|
api:
|
||||||
|
dashboard: true
|
||||||
|
|
||||||
|
# Define HTTP and HTTPS entrypoint
|
||||||
|
entryPoints:
|
||||||
|
insecure:
|
||||||
|
address: ':80'
|
||||||
|
secure:
|
||||||
|
address: ':443'
|
||||||
|
|
||||||
|
# Dynamic configuration will come from docker labels
|
||||||
|
providers:
|
||||||
|
docker:
|
||||||
|
endpoint: 'unix:///var/run/docker.sock'
|
||||||
|
network: 'proxy'
|
||||||
|
exposedByDefault: false
|
||||||
|
|
||||||
|
# Enable acme with http file challenge
|
||||||
|
certificatesResolvers:
|
||||||
|
le:
|
||||||
|
acme:
|
||||||
|
email: me@example.org
|
||||||
|
storage: /data/acme.json
|
||||||
|
httpChallenge:
|
||||||
|
entryPoint: insecure
|
||||||
|
```
|
||||||
|
|
||||||
|
**Run**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker network create proxy
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cryptgeon
|
||||||
|
|
||||||
|
Create another docker-compose.yaml file in another folder. We will assume that the domain is `cryptgeon.example.org`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/foo/bar/cryptgeon/
|
||||||
|
└── docker-compose.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
networks:
|
||||||
|
proxy:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
services:
|
||||||
|
memcached:
|
||||||
|
image: memcached:1-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
entrypoint: memcached -m 256M -I 4M # Limit to 128 MB Ram, customize at free will.
|
||||||
|
|
||||||
|
app:
|
||||||
|
image: cupcakearmy/cryptgeon:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- memcached
|
||||||
|
environment:
|
||||||
|
SIZE_LIMIT: 4 MiB
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
- proxy
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.http.routers.cryptgeon.rule=Host(`cryptgeon.example.org`)
|
||||||
|
- traefik.http.routers.cryptgeon.entrypoints=secure
|
||||||
|
- traefik.http.routers.cryptgeon.tls.certresolver=le
|
||||||
|
```
|
||||||
|
|
||||||
|
**Run**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Watchtower
|
||||||
|
|
||||||
|
> A container-based solution for automating Docker container base image updates.
|
||||||
|
|
||||||
|
[Watchtower](https://containrrr.dev/watchtower/) will keep our containers up to date. The interval is set to once a day and also configured to delete old images to prevent cluttering.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/foo/bar/watchtower/
|
||||||
|
└── docker-compose.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yaml
|
||||||
|
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
watchtower:
|
||||||
|
image: containrrr/watchtower
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
command: --cleanup --interval 86400
|
||||||
|
```
|
0
client/.gitignore → frontend/.gitignore
vendored
18
frontend/README.md
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
35
frontend/package.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"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.2.0",
|
||||||
|
"@sveltejs/adapter-static": "^1.0.0-next.34",
|
||||||
|
"@sveltejs/kit": "^1.0.0-next.348",
|
||||||
|
"@types/file-saver": "^2.0.5",
|
||||||
|
"@types/sanitize-html": "^2.6.2",
|
||||||
|
"adm-zip": "^0.5.9",
|
||||||
|
"dotenv": "^16.0.1",
|
||||||
|
"svelte": "^3.48.0",
|
||||||
|
"svelte-check": "^2.7.2",
|
||||||
|
"svelte-intl-precompile": "^0.10.1",
|
||||||
|
"svelte-preprocess": "^4.10.7",
|
||||||
|
"tslib": "^2.4.0",
|
||||||
|
"typescript": "^4.7.3",
|
||||||
|
"vite": "^2.9.10"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@fontsource/fira-mono": "^4.5.8",
|
||||||
|
"copy-to-clipboard": "^3.3.1",
|
||||||
|
"file-saver": "^2.0.5",
|
||||||
|
"pretty-bytes": "^5.6.0",
|
||||||
|
"sanitize-html": "^2.7.0"
|
||||||
|
}
|
||||||
|
}
|
1644
frontend/pnpm-lock.yaml
generated
Normal file
54
frontend/scripts/locale.js
Normal 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)
|
||||||
|
})
|
@@ -5,9 +5,9 @@
|
|||||||
<link rel="icon" href="/favicon.png" />
|
<link rel="icon" href="/favicon.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
%svelte.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="svelte">%svelte.body%</div>
|
<div id="svelte">%sveltekit.body%</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
5
frontend/src/lib/icons/IconContrast.svelte
Normal 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 |
7
frontend/src/lib/icons/IconCopy.svelte
Normal 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 |
5
frontend/src/lib/icons/IconDice.svelte
Normal 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 |
5
frontend/src/lib/icons/IconEye.svelte
Normal 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 |
7
frontend/src/lib/icons/IconEyeOff.svelte
Normal 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 |
@@ -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)
|
@@ -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>
|
37
frontend/src/lib/ui/Icon.svelte
Normal 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>
|
@@ -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>
|
@@ -4,10 +4,13 @@
|
|||||||
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 sanitize from 'sanitize-html'
|
||||||
|
import { t } from 'svelte-intl-precompile'
|
||||||
import Button from './Button.svelte'
|
import Button from './Button.svelte'
|
||||||
|
|
||||||
export let note: NotePublic
|
export let note: NotePublic
|
||||||
|
|
||||||
|
const RE_URL = /[A-Za-z]+:\/\/([A-Z a-z0-9\-._~:\/?#\[\]@!$&'()*+,;%=])+/g
|
||||||
let files: FileDTO[] = []
|
let files: FileDTO[] = []
|
||||||
|
|
||||||
$: if (note.meta.type === 'file') {
|
$: if (note.meta.type === 'file') {
|
||||||
@@ -26,22 +29,30 @@
|
|||||||
})
|
})
|
||||||
saveAs(f)
|
saveAs(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function contentWithLinks(content: string): string {
|
||||||
|
const replaced = note.contents.replace(
|
||||||
|
RE_URL,
|
||||||
|
(url) => `<a href="${url}" rel="noreferrer">${url}</a>`
|
||||||
|
)
|
||||||
|
return sanitize(replaced, { allowedTags: ['a'], allowedAttributes: { a: ['href', 'rel'] } })
|
||||||
|
}
|
||||||
</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}
|
{@html contentWithLinks(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 +70,9 @@
|
|||||||
|
|
||||||
.note b {
|
.note b {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note.file {
|
.note.file {
|
||||||
@@ -66,4 +80,8 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.note.file small {
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@@ -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);
|
@@ -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>
|
@@ -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>
|
||||||
|
|
@@ -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,48 +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>
|
<p>
|
||||||
Easily send <i>fully encrypted</i>, secure notes or files with one click. Just create a note and
|
{@html $t('home.intro')}
|
||||||
share the link.
|
|
||||||
</p>
|
</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>
|
||||||
|
|
||||||
@@ -132,7 +131,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<br />
|
<br />
|
||||||
{#if loading}
|
{#if loading}
|
||||||
loading...
|
{$t('common.loading')}
|
||||||
{:else}
|
{:else}
|
||||||
{message}
|
{message}
|
||||||
{/if}
|
{/if}
|
||||||
@@ -144,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>
|
@@ -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>
|
||||||
|
|
@@ -83,7 +83,7 @@
|
|||||||
|
|
||||||
header {
|
header {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-top: 4rem;
|
margin-top: 3rem;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
42
frontend/src/routes/__layout.svelte
Normal 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>
|
@@ -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>
|
||||||
@@ -50,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
|
@@ -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>
|
@@ -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}
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
@@ -1,3 +1,4 @@
|
|||||||
# https://www.robotstxt.org/robotstxt.html
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
User-agent: *
|
User-agent: *
|
||||||
Disallow:
|
Allow: /$
|
||||||
|
Disallow: /
|
@@ -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
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
3
frontend/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "./.svelte-kit/tsconfig.json"
|
||||||
|
}
|
@@ -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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
332
pnpm-lock.yaml
generated
@@ -1,14 +1,8 @@
|
|||||||
lockfileVersion: 5.3
|
lockfileVersion: 5.4
|
||||||
|
|
||||||
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
|
||||||
@@ -57,7 +51,7 @@ packages:
|
|||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/color-name/1.1.3:
|
/color-name/1.1.3:
|
||||||
resolution: {integrity: sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=}
|
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/concat-map/0.0.1:
|
/concat-map/0.0.1:
|
||||||
@@ -75,10 +69,11 @@ packages:
|
|||||||
which: 1.3.1
|
which: 1.3.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/define-properties/1.1.3:
|
/define-properties/1.1.4:
|
||||||
resolution: {integrity: sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==}
|
resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
has-property-descriptors: 1.0.0
|
||||||
object-keys: 1.1.1
|
object-keys: 1.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@@ -88,39 +83,46 @@ packages:
|
|||||||
is-arrayish: 0.2.1
|
is-arrayish: 0.2.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/es-abstract/1.18.0:
|
/es-abstract/1.20.1:
|
||||||
resolution: {integrity: sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==}
|
resolution: {integrity: sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==}
|
||||||
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
|
||||||
|
function.prototype.name: 1.1.5
|
||||||
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-property-descriptors: 1.0.0
|
||||||
is-callable: 1.2.3
|
has-symbols: 1.0.3
|
||||||
is-negative-zero: 2.0.1
|
internal-slot: 1.0.3
|
||||||
is-regex: 1.1.2
|
is-callable: 1.2.4
|
||||||
is-string: 1.0.5
|
is-negative-zero: 2.0.2
|
||||||
object-inspect: 1.10.2
|
is-regex: 1.1.4
|
||||||
|
is-shared-array-buffer: 1.0.2
|
||||||
|
is-string: 1.0.7
|
||||||
|
is-weakref: 1.0.2
|
||||||
|
object-inspect: 1.12.2
|
||||||
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
|
regexp.prototype.flags: 1.4.3
|
||||||
string.prototype.trimstart: 1.0.4
|
string.prototype.trimend: 1.0.5
|
||||||
unbox-primitive: 1.0.1
|
string.prototype.trimstart: 1.0.5
|
||||||
|
unbox-primitive: 1.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/es-to-primitive/1.2.1:
|
/es-to-primitive/1.2.1:
|
||||||
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:
|
||||||
resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=}
|
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
|
||||||
engines: {node: '>=0.8.0'}
|
engines: {node: '>=0.8.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@@ -128,12 +130,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
|
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/file-saver/2.0.5:
|
/follow-redirects/1.15.1:
|
||||||
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
|
resolution: {integrity: sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==}
|
||||||
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: '*'
|
||||||
@@ -146,32 +144,67 @@ packages:
|
|||||||
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/function.prototype.name/1.1.5:
|
||||||
|
resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
dependencies:
|
||||||
|
call-bind: 1.0.2
|
||||||
|
define-properties: 1.1.4
|
||||||
|
es-abstract: 1.20.1
|
||||||
|
functions-have-names: 1.2.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/functions-have-names/1.2.3:
|
||||||
|
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/get-intrinsic/1.1.1:
|
/get-intrinsic/1.1.1:
|
||||||
resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==}
|
resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==}
|
||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.1
|
function-bind: 1.1.1
|
||||||
has: 1.0.3
|
has: 1.0.3
|
||||||
has-symbols: 1.0.2
|
has-symbols: 1.0.3
|
||||||
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
|
dev: true
|
||||||
|
|
||||||
/has-bigints/1.0.1:
|
/graceful-fs/4.2.10:
|
||||||
resolution: {integrity: sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==}
|
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/has-bigints/1.0.2:
|
||||||
|
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/has-flag/3.0.0:
|
/has-flag/3.0.0:
|
||||||
resolution: {integrity: sha1-tdRU3CGZriJWmfNGfloH87lVuv0=}
|
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/has-symbols/1.0.2:
|
/has-property-descriptors/1.0.0:
|
||||||
resolution: {integrity: sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==}
|
resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
|
||||||
|
dependencies:
|
||||||
|
get-intrinsic: 1.1.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/has-symbols/1.0.3:
|
||||||
|
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
|
||||||
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.3
|
||||||
|
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,75 +221,105 @@ 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.15.1
|
||||||
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: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
|
||||||
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.2
|
||||||
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.9.0:
|
||||||
resolution: {integrity: sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==}
|
resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==}
|
||||||
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.7:
|
||||||
resolution: {integrity: sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==}
|
resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
|
||||||
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.2:
|
||||||
resolution: {integrity: sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==}
|
resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
|
||||||
engines: {node: '>= 0.4'}
|
dependencies:
|
||||||
|
call-bind: 1.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/is-symbol/1.0.3:
|
/is-string/1.0.7:
|
||||||
resolution: {integrity: sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==}
|
resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dependencies:
|
dependencies:
|
||||||
has-symbols: 1.0.2
|
has-tostringtag: 1.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/is-symbol/1.0.4:
|
||||||
|
resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
dependencies:
|
||||||
|
has-symbols: 1.0.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/is-weakref/1.0.2:
|
||||||
|
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
|
||||||
|
dependencies:
|
||||||
|
call-bind: 1.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/isexe/2.0.0:
|
/isexe/2.0.0:
|
||||||
resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=}
|
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/json-parse-better-errors/1.0.2:
|
/json-parse-better-errors/1.0.2:
|
||||||
@@ -264,22 +327,22 @@ packages:
|
|||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/load-json-file/4.0.0:
|
/load-json-file/4.0.0:
|
||||||
resolution: {integrity: sha1-L19Fq5HjMhYjT9U62rZo607AmTs=}
|
resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
dependencies:
|
dependencies:
|
||||||
graceful-fs: 4.2.6
|
graceful-fs: 4.2.10
|
||||||
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
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/memorystream/0.3.1:
|
/memorystream/0.3.1:
|
||||||
resolution: {integrity: sha1-htcJCzDORV1j+64S3aUaR93K+bI=}
|
resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==}
|
||||||
engines: {node: '>= 0.10.0'}
|
engines: {node: '>= 0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/minimatch/3.0.4:
|
/minimatch/3.1.2:
|
||||||
resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==}
|
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion: 1.1.11
|
brace-expansion: 1.1.11
|
||||||
dev: true
|
dev: true
|
||||||
@@ -292,7 +355,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.22.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
|
||||||
@@ -306,15 +369,15 @@ packages:
|
|||||||
chalk: 2.4.2
|
chalk: 2.4.2
|
||||||
cross-spawn: 6.0.5
|
cross-spawn: 6.0.5
|
||||||
memorystream: 0.3.1
|
memorystream: 0.3.1
|
||||||
minimatch: 3.0.4
|
minimatch: 3.1.2
|
||||||
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.2:
|
||||||
resolution: {integrity: sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA==}
|
resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/object-keys/1.1.1:
|
/object-keys/1.1.1:
|
||||||
@@ -327,13 +390,13 @@ packages:
|
|||||||
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.4
|
||||||
has-symbols: 1.0.2
|
has-symbols: 1.0.3
|
||||||
object-keys: 1.1.1
|
object-keys: 1.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/parse-json/4.0.0:
|
/parse-json/4.0.0:
|
||||||
resolution: {integrity: sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=}
|
resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
dependencies:
|
dependencies:
|
||||||
error-ex: 1.3.2
|
error-ex: 1.3.2
|
||||||
@@ -341,12 +404,12 @@ packages:
|
|||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/path-key/2.0.1:
|
/path-key/2.0.1:
|
||||||
resolution: {integrity: sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=}
|
resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
|
||||||
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:
|
||||||
@@ -363,17 +426,12 @@ packages:
|
|||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/pify/3.0.0:
|
/pify/3.0.0:
|
||||||
resolution: {integrity: sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=}
|
resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==}
|
||||||
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: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
dependencies:
|
dependencies:
|
||||||
load-json-file: 4.0.0
|
load-json-file: 4.0.0
|
||||||
@@ -381,15 +439,26 @@ packages:
|
|||||||
path-type: 3.0.0
|
path-type: 3.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/requires-port/1.0.0:
|
/regexp.prototype.flags/1.4.3:
|
||||||
resolution: {integrity: sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=}
|
resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
dependencies:
|
||||||
|
call-bind: 1.0.2
|
||||||
|
define-properties: 1.1.4
|
||||||
|
functions-have-names: 1.2.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/resolve/1.20.0:
|
/requires-port/1.0.0:
|
||||||
resolution: {integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==}
|
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/resolve/1.22.0:
|
||||||
|
resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==}
|
||||||
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
is-core-module: 2.3.0
|
is-core-module: 2.9.0
|
||||||
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 +478,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.2
|
||||||
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,34 +505,36 @@ 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.4
|
||||||
es-abstract: 1.18.0
|
es-abstract: 1.20.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/string.prototype.trimend/1.0.4:
|
/string.prototype.trimend/1.0.5:
|
||||||
resolution: {integrity: sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==}
|
resolution: {integrity: sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==}
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.2
|
call-bind: 1.0.2
|
||||||
define-properties: 1.1.3
|
define-properties: 1.1.4
|
||||||
|
es-abstract: 1.20.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/string.prototype.trimstart/1.0.4:
|
/string.prototype.trimstart/1.0.5:
|
||||||
resolution: {integrity: sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==}
|
resolution: {integrity: sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.2
|
call-bind: 1.0.2
|
||||||
define-properties: 1.1.3
|
define-properties: 1.1.4
|
||||||
|
es-abstract: 1.20.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/strip-bom/3.0.0:
|
/strip-bom/3.0.0:
|
||||||
@@ -470,12 +549,17 @@ packages:
|
|||||||
has-flag: 3.0.0
|
has-flag: 3.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/unbox-primitive/1.0.1:
|
/supports-preserve-symlinks-flag/1.0.0:
|
||||||
resolution: {integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==}
|
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/unbox-primitive/1.0.2:
|
||||||
|
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.1
|
call-bind: 1.0.2
|
||||||
has-bigints: 1.0.1
|
has-bigints: 1.0.2
|
||||||
has-symbols: 1.0.2
|
has-symbols: 1.0.3
|
||||||
which-boxed-primitive: 1.0.2
|
which-boxed-primitive: 1.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@@ -489,11 +573,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.7
|
||||||
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:
|
||||||
|
24
proxy.mjs
@@ -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 server = http.createServer(function (req, res) {
|
||||||
const target = req.url.startsWith('/api/') ? 'http://localhost:5000' : 'http://localhost:3000'
|
const target = req.url.startsWith('/api/') ? 'http://localhost:5000' : 'http://localhost:3000'
|
||||||
proxy.web(req, res, { target })
|
proxy.web(req, res, { target })
|
||||||
})
|
})
|
||||||
server.listen(1234)
|
server.listen(1234)
|
||||||
|
console.log('Proxy on http://localhost:1234')
|
||||||
server.on('error', () => start())
|
|
||||||
} catch (e) {
|
|
||||||
start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
start()
|
|
||||||
|
@@ -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")
|
|
||||||
}
|
|
@@ -1,137 +0,0 @@
|
|||||||
use actix_web::{delete, get, post, web, HttpResponse, Responder};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
use crate::note::{generate_id, Note, NoteInfo, NotePublic};
|
|
||||||
use crate::size::LIMIT;
|
|
||||||
use crate::store;
|
|
||||||
|
|
||||||
pub fn now() -> u32 {
|
|
||||||
SystemTime::now()
|
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
.as_secs() as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
struct NotePath {
|
|
||||||
id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/{id}")]
|
|
||||||
async fn one(path: web::Path<NotePath>) -> impl Responder {
|
|
||||||
let p = path.into_inner();
|
|
||||||
let note = store::get(&p.id);
|
|
||||||
match note {
|
|
||||||
None => return HttpResponse::NotFound().finish(),
|
|
||||||
Some(_) => return HttpResponse::Ok().json(NoteInfo {}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
struct CreateResponse {
|
|
||||||
id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/")]
|
|
||||||
async fn create(note: web::Json<Note>) -> impl Responder {
|
|
||||||
let mut n = note.into_inner();
|
|
||||||
let id = generate_id();
|
|
||||||
let bad_req = HttpResponse::BadRequest().finish();
|
|
||||||
if n.views == None && n.expiration == None {
|
|
||||||
return bad_req;
|
|
||||||
}
|
|
||||||
match n.views {
|
|
||||||
Some(v) => {
|
|
||||||
if v > 100 {
|
|
||||||
return bad_req;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
match n.expiration {
|
|
||||||
Some(e) => {
|
|
||||||
if e > 360 {
|
|
||||||
return bad_req;
|
|
||||||
}
|
|
||||||
let expiration = now() + (e * 60);
|
|
||||||
n.expiration = Some(expiration);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
store::set(&id.clone(), &n.clone());
|
|
||||||
return HttpResponse::Ok().json(CreateResponse { id: id });
|
|
||||||
}
|
|
||||||
|
|
||||||
#[delete("/{id}")]
|
|
||||||
async fn delete(path: web::Path<NotePath>) -> impl Responder {
|
|
||||||
let p = path.into_inner();
|
|
||||||
let note = store::get(&p.id);
|
|
||||||
match note {
|
|
||||||
None => return HttpResponse::NotFound().finish(),
|
|
||||||
Some(note) => {
|
|
||||||
let mut changed = note.clone();
|
|
||||||
if changed.views == None && changed.expiration == None {
|
|
||||||
return HttpResponse::BadRequest().finish();
|
|
||||||
}
|
|
||||||
match changed.views {
|
|
||||||
Some(v) => {
|
|
||||||
changed.views = Some(v - 1);
|
|
||||||
let id = p.id.clone();
|
|
||||||
if v <= 1 {
|
|
||||||
store::del(&id);
|
|
||||||
} else {
|
|
||||||
store::set(&id, &changed.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let n = now();
|
|
||||||
match changed.expiration {
|
|
||||||
Some(e) => {
|
|
||||||
if e < n {
|
|
||||||
store::del(&p.id.clone());
|
|
||||||
return HttpResponse::BadRequest().finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
return HttpResponse::Ok().json(NotePublic {
|
|
||||||
contents: changed.contents,
|
|
||||||
meta: changed.meta,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
struct Status {
|
|
||||||
version: String,
|
|
||||||
max_size: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/status")]
|
|
||||||
async fn status() -> impl Responder {
|
|
||||||
println!("Limit: {}", *LIMIT);
|
|
||||||
return HttpResponse::Ok().json(Status {
|
|
||||||
version: option_env!("CARGO_PKG_VERSION")
|
|
||||||
.unwrap_or("Unknown")
|
|
||||||
.to_string(),
|
|
||||||
max_size: *LIMIT,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(cfg: &mut web::ServiceConfig) {
|
|
||||||
cfg.service(
|
|
||||||
web::scope("/api")
|
|
||||||
.service(
|
|
||||||
web::scope("/notes")
|
|
||||||
.service(one)
|
|
||||||
.service(create)
|
|
||||||
.service(delete)
|
|
||||||
.service(status),
|
|
||||||
)
|
|
||||||
.service(status),
|
|
||||||
);
|
|
||||||
}
|
|
20
src/size.rs
@@ -1,20 +0,0 @@
|
|||||||
use actix_web::web;
|
|
||||||
use byte_unit::Byte;
|
|
||||||
use mime;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref LIMIT: usize =
|
|
||||||
Byte::from_str(std::env::var("SIZE_LIMIT").unwrap_or("1 KiB".to_string()))
|
|
||||||
.unwrap()
|
|
||||||
.get_bytes() as usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(cfg: &mut web::ServiceConfig) {
|
|
||||||
println!("Limit: {}", *LIMIT);
|
|
||||||
let json = web::JsonConfig::default().limit(*LIMIT);
|
|
||||||
let plain = web::PayloadConfig::default()
|
|
||||||
.limit(*LIMIT)
|
|
||||||
.mimetype(mime::STAR_STAR);
|
|
||||||
cfg.data(json);
|
|
||||||
cfg.data(plain);
|
|
||||||
}
|
|
36
src/store.rs
@@ -1,36 +0,0 @@
|
|||||||
use memcache;
|
|
||||||
|
|
||||||
use crate::note::now;
|
|
||||||
use crate::note::Note;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref CLIENT: memcache::Client = memcache::connect(format!(
|
|
||||||
"memcache://{}?timeout=10&tcp_nodelay=true",
|
|
||||||
std::env::var("MEMCACHE").unwrap_or("127.0.0.1:11211".to_string())
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set(id: &String, note: &Note) {
|
|
||||||
let serialized = serde_json::to_string(¬e.clone()).unwrap();
|
|
||||||
let expiration: u32 = match note.expiration {
|
|
||||||
Some(e) => e - now(),
|
|
||||||
None => 0,
|
|
||||||
};
|
|
||||||
CLIENT.set(id, serialized, expiration).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(id: &String) -> Option<Note> {
|
|
||||||
let value: Option<String> = CLIENT.get(&id).unwrap();
|
|
||||||
match value {
|
|
||||||
None => return None,
|
|
||||||
Some(s) => {
|
|
||||||
let deserialize: Note = serde_json::from_str(&s).unwrap();
|
|
||||||
return Some(deserialize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn del(id: &String) {
|
|
||||||
CLIENT.delete(id).unwrap();
|
|
||||||
}
|
|