Compare commits

..

6 Commits

Author SHA1 Message Date
a5d98b76bd 2.0.1 (#40)
* locale from lokalise

* version bump

* update dependencies

* show size with overhead

* use base64 instead of hex and refactor a bit

* changelog & readme

* size limit

* locale

* add sync for svelte

* refarcor create & add loading animation

* changelog
2022-07-19 10:27:23 +02:00
9590c9b567 2 (#38)
* use redis

* update frontend and switch sanitize library

* changelog

* theming

* docker image

* documentation

* changelog

* clear up limit sizes

* version bump

* version bump
2022-07-16 14:16:54 +02:00
hash070
0913a8ad0c created and made a Chinese translation JSON file at ./frontend/locales/cn.json (#37) 2022-07-12 14:30:47 +02:00
d13c712e95 Update README.md 2022-07-08 10:45:40 +02:00
6230d2dbd0 Merge pull request #36 from Hash070/cn-translate
Completed the Chinese translation of README
2022-07-08 10:44:17 +02:00
hash070
dbfb383c73 Completed the Chinese translation of README 2022-07-08 16:18:12 +08:00
49 changed files with 1475 additions and 1007 deletions

View File

@@ -1,5 +1,4 @@
* *
!/entry.sh
!/backend/src !/backend/src
!/backend/Cargo.lock !/backend/Cargo.lock
@@ -13,3 +12,4 @@
!/frontend/pnpm-lock.yaml !/frontend/pnpm-lock.yaml
!/frontend/svelte.config.js !/frontend/svelte.config.js
!/frontend/tsconfig.json !/frontend/tsconfig.json
!/frontend/vite.config.js

View File

@@ -5,6 +5,41 @@ 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).
## [2.0.1] - 2022-07-18
### Added
- Max file size on the client now.
- Loading information.
### Changed
- Changed encoding from hex to base64.
- Chinese language code.
- Notable speed improvements for big files.
## [2.0.0] - 2022-07-16
### Added
- Theming for logo and description text.
### Changed
- Moved to redis.
- New html sanitizing library.
## [2.0.0-rc.0] - 2022-07-15
### Added
- Theming for logo and description text.
### Changed
- Moved to redis.
- New html sanitizing library.
## [1.5.3] - 2022-06-07 ## [1.5.3] - 2022-06-07
### Changed ### Changed

View File

@@ -1,9 +1,10 @@
# FRONTEND # FRONTEND
FROM node:16-alpine as client FROM node:16-alpine as client
WORKDIR /tmp WORKDIR /tmp
RUN npm install -g pnpm RUN npm install -g pnpm@7
COPY ./frontend ./ COPY ./frontend ./
RUN pnpm install RUN pnpm install
RUN pnpm exec svelte-kit sync
RUN pnpm run build RUN pnpm run build
@@ -18,9 +19,8 @@ RUN cargo build --release
# RUNNER # RUNNER
FROM alpine FROM alpine
WORKDIR /app WORKDIR /app
COPY ./entry.sh .
COPY --from=backend /tmp/target/release/cryptgeon . COPY --from=backend /tmp/target/release/cryptgeon .
COPY --from=client /tmp/build ./frontend/build COPY --from=client /tmp/build ./frontend/build
ENV MEMCACHE=memcached:11211 ENV REDIS=redis://redis/
EXPOSE 5000 EXPOSE 5000
ENTRYPOINT [ "/app/entry.sh" ] ENTRYPOINT [ "/app/cryptgeon" ]

View File

@@ -9,10 +9,12 @@
<img alt="Latest version" src="https://img.shields.io/github/v/release/cupcakearmy/cryptgeon?style=for-the-badge" /> <img alt="Latest version" src="https://img.shields.io/github/v/release/cupcakearmy/cryptgeon?style=for-the-badge" />
</a> </a>
<br/> <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" height="50" /></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"> <a href=""><img src="./.github/lokalise.png" height="50">
<br/> <br/><br/>
EN | [简体中文](README_zh-CN.md)
## About? ## About?
@@ -48,13 +50,15 @@ 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. | | `REDIS` | `redis://redis/` | Redis 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/). <br> `512 MiB` is the maximum allowed. <br> The frontend will show that number including the ~35% encoding overhead. |
| `MAX_VIEWS` | `100` | Maximal number of views. | | `MAX_VIEWS` | `100` | Maximal number of views. |
| `MAX_EXPIRATION` | `360` | Maximal expiration in minutes. | | `MAX_EXPIRATION` | `360` | Maximal expiration in minutes. |
| `ALLOW_ADVANCED` | `true` | Allow custom configuration. If set to `false` all notes will be one view only. | | `ALLOW_ADVANCED` | `true` | Allow custom configuration. If set to `false` all notes will be one view only. |
| `THEME_IMAGE` | `""` | Custom image for replacing the logo. Must be publicly reachable |
| `THEME_TEXT` | `""` | Custom text for replacing the description below the logo |
## Deployment ## Deployment
@@ -67,19 +71,18 @@ Docker is the easiest way. There is the [official image here](https://hub.docker
```yaml ```yaml
# docker-compose.yml # docker-compose.yml
version: '3.7' version: '3.8'
services: services:
memcached: redis:
image: memcached:1-alpine image: redis:7-alpine
entrypoint: memcached -m 128M -I 4M # Limit to 128 MB Ram, 4M per entry, customize at free will.
app: app:
image: cupcakearmy/cryptgeon:latest image: cupcakearmy/cryptgeon:latest
depends_on: depends_on:
- memcached - redis
environment: environment:
SIZE_LIMIT: 4M SIZE_LIMIT: 4 MiB
ports: ports:
- 80:5000 - 80:5000
``` ```
@@ -105,16 +108,15 @@ networks:
external: true external: true
services: services:
memcached: redis:
image: memcached:1-alpine image: redis:7-alpine
restart: unless-stopped restart: unless-stopped
entrypoint: memcached -m 128M -I 4M # Limit to 128 MB Ram, 4M per entry, customize at free will.
app: app:
image: cupcakearmy/cryptgeon:latest image: cupcakearmy/cryptgeon:latest
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
- memcached - redis
networks: networks:
- default - default
- proxy - proxy
@@ -130,7 +132,7 @@ services:
**Requirements** **Requirements**
- `pnpm`: `>=6` - `pnpm`: `>=6`
- `node`: `>=14` - `node`: `>=16`
- `rust`: edition `2021` - `rust`: edition `2021`
**Install** **Install**
@@ -157,9 +159,9 @@ pnpm run dev
Running `pnpm run dev` in the root folder will start the following things: Running `pnpm run dev` in the root folder will start the following things:
- a memcache docker container - redis docker container
- rust backend with hot reload - rust backend
- client with hot reload - client
You can see the app under [localhost:1234](http://localhost:1234). You can see the app under [localhost:1234](http://localhost:1234).

166
README_zh-CN.md Normal file
View File

@@ -0,0 +1,166 @@
<p align="center">
<img src="./design/Github_zh-CN.png" alt="logo">
</p>
<a href="https://discord.gg/nuby6RnxZt">
<img alt="discord" src="https://img.shields.io/discord/252403122348097536?style=for-the-badge" />
<img alt="docker pulls" src="https://img.shields.io/docker/pulls/cupcakearmy/cryptgeon?style=for-the-badge" />
<img alt="Docker image size badge" src="https://img.shields.io/docker/image-size/cupcakearmy/cryptgeon?style=for-the-badge" />
<img alt="Latest version" src="https://img.shields.io/github/v/release/cupcakearmy/cryptgeon?style=for-the-badge" />
</a>
<br/>
<a href="https://www.producthunt.com/posts/cryptgeon?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-cryptgeon" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=295189&theme=light" alt="Cryptgeon - Securely share self-destructing notes | Product Hunt" height="50" /></a>
<a href=""><img src="./.github/lokalise.png" height="50">
<br/>
[EN](README.md) | 简体中文
## 关于本项目
_加密鸽_ 是一个受 [_PrivNote_](https://privnote.com)项目启发的安全、开源共享密信和文件共享服务器
> 🌍 如果你想翻译此项目请随时与我联系.
>
> 感谢 [Lokalise](https://lokalise.com/) 提供免费的平台服务支持
## 演示示例
查看加密鸽的在线演示 demo https://cryptgeon.nicco.io.
## 功能
- 服务端无法解密和查看客户端加密的内容
- 查看次数或时间限制,阅后即焚
- 您发送的数据将存放于内存中,不会写入到磁盘中
- 黑暗模式支持
## 加密鸽是如何工作的?
加密鸽会为每条笔记都生成一个独立的 <code>id (256bit)</code><code>key 256(bit)</code>
其中<code>id</code>用于保存和提取密信, 在这之后这封密信将会被客户端使用 AES 算法的 GCM 模式和`key`进行加密然后发送至服务器,数据将会保存在服务器的内存中且永远不会被持久化到硬盘上,服务端永远不会得到密钥并且无法解读密信的内容。
## 屏幕截图
![screenshot](./design/Screens.png)
## 环境变量
| 变量名称 | 默认值 | 描述 |
| ----------------- | ---------------- | --------------------------------------------------------------------------------- |
| `REDIS` | `redis://redis/` | Redis URL to connect to. |
| `SIZE_LIMIT` | `1 KiB` | 最大请求体(body)限制。有关支持的数值请查看 [字节单位](https://docs.rs/byte-unit/) |
| `MAX_VIEWS` | `100` | 密信最多查看次数限制 |
| ` MAX_EXPIRATION` | `360` | 密信最长过期时间限制(分钟) |
| `ALLOW_ADVANCED` | `true` | 是否允许自定义设置,该项如果设为`false`,则不会显示自定义设置模块 |
## 部署
加密鸽必须使用`https`,否则浏览器可能将不会支援加密鸽的加密算法。
### Docker
Docker 是最简单的部署方式。这里是[官方镜像的地址](https://hub.docker.com/r/cupcakearmy/cryptgeon)。
附:译者的[部署笔记](https://www.hash070.top/archives/cryptgeon-docker-deploy.html)
```yaml
# docker-compose.yml
version: '3.8'
services:
redis:
image: redis:7-alpine
app:
image: cupcakearmy/cryptgeon:latest
depends_on:
- redis
environment:
SIZE_LIMIT: 4 MiB
ports:
- 80:5000
```
### NGINX 反向代理
查看 [examples/nginx](https://github.com/cupcakearmy/cryptgeon/tree/main/examples/nginx) 目录。那里有几个示例反代配置文件模板,其中一个是带 https 配置的反代配置模板,你需要指定服务器的名称和证书才能生效。
### Traefik 2
假设:
- 外部 Docker 代理网络 `proxy`
- 证书解析器 `le`
- 一个 https 入站点 `secure`
- 域名 `example.org`
```yaml
version: '3.8'
networks:
proxy:
external: true
services:
redis:
image: redis:7-alpine
restart: unless-stopped
app:
image: cupcakearmy/cryptgeon:latest
restart: unless-stopped
depends_on:
- redis
networks:
- default
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.cryptgeon.rule=Host(`example.org`)
- traefik.http.routers.cryptgeon.entrypoints=secure
- traefik.http.routers.cryptgeon.tls.certresolver=le
```
## 开发
**环境要求**
- `pnpm`: `>=6`
- `node`: `>=14`
- `rust`: edition `2021`
**安装**
```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
```
**运行**
确保你的 Docker 正在运行
> 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
```
在根目录执行 `pnpm run dev` 会开启下列服务:
- 一个 redis docker 容器
- 无热重载的 rust 后端
- 可热重载的客户端
你可以通过 1234 端口进入该应用,即 [localhost:1234](http://localhost:1234).
###### Attributions
本项目所使用的图标由<a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com 的<a href="https://www.freepik.com" title="Freepik">freepik</a>制作</a>

436
backend/Cargo.lock generated
View File

@@ -21,9 +21,9 @@ dependencies = [
[[package]] [[package]]
name = "actix-files" name = "actix-files"
version = "0.6.0" version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d81bde9a79336aa51ebed236e91fc1a0528ff67cfdf4f68ca4c61ede9fd26fb5" checksum = "e04dcf7654254676d434b0285e2298d577ed4826f67f536e7a39bb0f64721164"
dependencies = [ dependencies = [
"actix-http", "actix-http",
"actix-service", "actix-service",
@@ -44,9 +44,9 @@ dependencies = [
[[package]] [[package]]
name = "actix-http" name = "actix-http"
version = "3.0.4" version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5885cb81a0d4d0d322864bea1bb6c2a8144626b4fdc625d4c51eba197e7797a" checksum = "6f9ffb6db08c1c3a1f4aef540f1a63193adc73c4fbd40b75a95fc8c5258f6e51"
dependencies = [ dependencies = [
"actix-codec", "actix-codec",
"actix-rt", "actix-rt",
@@ -66,16 +66,16 @@ dependencies = [
"http", "http",
"httparse", "httparse",
"httpdate", "httpdate",
"itoa", "itoa 1.0.2",
"language-tags", "language-tags",
"local-channel", "local-channel",
"log",
"mime", "mime",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"rand", "rand",
"sha-1", "sha1 0.10.1",
"smallvec", "smallvec",
"tracing",
"zstd", "zstd",
] ]
@@ -154,9 +154,9 @@ dependencies = [
[[package]] [[package]]
name = "actix-web" name = "actix-web"
version = "4.0.1" version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4e5ebffd51d50df56a3ae0de0e59487340ca456f05dd0b90c0a7a6dd6a74d31" checksum = "a27e8fe9ba4ae613c21f677c2cfaf0696c3744030c6f485b34634e502d6bb379"
dependencies = [ dependencies = [
"actix-codec", "actix-codec",
"actix-http", "actix-http",
@@ -176,7 +176,7 @@ dependencies = [
"encoding_rs", "encoding_rs",
"futures-core", "futures-core",
"futures-util", "futures-util",
"itoa", "itoa 1.0.2",
"language-tags", "language-tags",
"log", "log",
"mime", "mime",
@@ -194,9 +194,9 @@ dependencies = [
[[package]] [[package]]
name = "actix-web-codegen" name = "actix-web-codegen"
version = "4.0.0" version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7525bedf54704abb1d469e88d7e7e9226df73778798a69cea5022d53b2ae91bc" checksum = "5f270541caec49c15673b0af0e9a00143421ad4f118d2df7edcb68b627632f56"
dependencies = [ dependencies = [
"actix-router", "actix-router",
"proc-macro2", "proc-macro2",
@@ -251,6 +251,28 @@ version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
[[package]]
name = "async-trait"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@@ -325,12 +347,6 @@ dependencies = [
"utf8-width", "utf8-width",
] ]
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.1.0" version = "1.1.0"
@@ -339,9 +355,9 @@ checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]] [[package]]
name = "bytestring" name = "bytestring"
version = "1.0.0" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d" checksum = "86b6a75fd3048808ef06af5cd79712be8111960adaf89d90250974b38fc3928a"
dependencies = [ dependencies = [
"bytes", "bytes",
] ]
@@ -361,6 +377,16 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "combine"
version = "4.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948"
dependencies = [
"bytes",
"memchr",
]
[[package]] [[package]]
name = "convert_case" name = "convert_case"
version = "0.4.0" version = "0.4.0"
@@ -398,16 +424,17 @@ dependencies = [
[[package]] [[package]]
name = "cryptgeon" name = "cryptgeon"
version = "1.5.3" version = "2.0.1"
dependencies = [ dependencies = [
"actix-files", "actix-files",
"actix-web", "actix-web",
"bs62", "bs62",
"byte-unit", "byte-unit",
"dotenv", "dotenv",
"env_logger",
"lazy_static", "lazy_static",
"memcache",
"mime", "mime",
"redis",
"ring", "ring",
"serde", "serde",
"serde_json", "serde_json",
@@ -415,9 +442,9 @@ dependencies = [
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.3" version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" checksum = "5999502d32b9c48d492abe66392408144895020ec4709e549e840799f3bb74c0"
dependencies = [ dependencies = [
"generic-array", "generic-array",
"typenum", "typenum",
@@ -452,6 +479,12 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
name = "dtoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.31" version = "0.8.31"
@@ -462,15 +495,16 @@ dependencies = [
] ]
[[package]] [[package]]
name = "enum_dispatch" name = "env_logger"
version = "0.3.8" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eb359f1476bf611266ac1f5355bc14aeca37b299d0ebccc038ee7058891c9cb" checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
dependencies = [ dependencies = [
"once_cell", "atty",
"proc-macro2", "humantime",
"quote", "log",
"syn", "regex",
"termcolor",
] ]
[[package]] [[package]]
@@ -495,21 +529,6 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.0.1" version = "1.0.1"
@@ -562,13 +581,13 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.6" version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"wasi 0.10.2+wasi-snapshot-preview1", "wasi",
] ]
[[package]] [[package]]
@@ -592,9 +611,9 @@ dependencies = [
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.11.2" version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
@@ -613,7 +632,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
"itoa", "itoa 1.0.2",
] ]
[[package]] [[package]]
@@ -634,6 +653,12 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.2.3" version = "0.2.3"
@@ -647,22 +672,19 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.8.2" version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown", "hashbrown",
] ]
[[package]] [[package]]
name = "instant" name = "itoa"
version = "0.1.12" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
@@ -681,9 +703,9 @@ dependencies = [
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.57" version = "0.3.58"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
dependencies = [ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
@@ -749,20 +771,6 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "memcache"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "757b4de02817c63ff4efc98a282b305977a7468b2bdf2085c140cdab1604fa61"
dependencies = [
"byteorder",
"enum_dispatch",
"openssl",
"r2d2",
"rand",
"url",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.5.0" version = "2.5.0"
@@ -796,13 +804,13 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.3" version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
"wasi 0.11.0+wasi-snapshot-preview1", "wasi",
"windows-sys", "windows-sys",
] ]
@@ -857,59 +865,9 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.12.0" version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
[[package]]
name = "openssl"
version = "0.10.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e"
dependencies = [
"bitflags",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "openssl-sys"
version = "0.9.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1"
dependencies = [
"autocfg",
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core 0.8.5",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
@@ -918,21 +876,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [ dependencies = [
"lock_api", "lock_api",
"parking_lot_core 0.9.3", "parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [
"cfg-if",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi",
] ]
[[package]] [[package]]
@@ -972,12 +916,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.16" version = "0.2.16"
@@ -986,33 +924,22 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.39" version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.18" version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "r2d2"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f"
dependencies = [
"log",
"parking_lot 0.11.2",
"scheduled-thread-pool",
]
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.8.5" version = "0.8.5"
@@ -1043,6 +970,21 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "redis"
version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a80b5f38d7f5a020856a0e16e40a9cfabf88ae8f0e4c2dcd8a3114c1e470852"
dependencies = [
"async-trait",
"combine",
"dtoa",
"itoa 0.4.8",
"percent-encoding",
"sha1 0.6.1",
"url",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.13" version = "0.2.13"
@@ -1054,9 +996,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.5.6" version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@@ -1065,9 +1007,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.6.26" version = "0.6.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
[[package]] [[package]]
name = "ring" name = "ring"
@@ -1099,15 +1041,6 @@ version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
[[package]]
name = "scheduled-thread-pool"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "977a7519bff143a44f842fd07e80ad1329295bd71686457f18e496736f4bf9bf"
dependencies = [
"parking_lot 0.12.1",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.1.0"
@@ -1116,24 +1049,24 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.9" version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd" checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.137" version = "1.0.138"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.137" version = "1.0.138"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1142,11 +1075,11 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.81" version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
dependencies = [ dependencies = [
"itoa", "itoa 1.0.2",
"ryu", "ryu",
"serde", "serde",
] ]
@@ -1158,22 +1091,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [ dependencies = [
"form_urlencoded", "form_urlencoded",
"itoa", "itoa 1.0.2",
"ryu", "ryu",
"serde", "serde",
] ]
[[package]] [[package]]
name = "sha-1" name = "sha1"
version = "0.10.0" version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
dependencies = [
"sha1_smol",
]
[[package]]
name = "sha1"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures",
"digest", "digest",
] ]
[[package]]
name = "sha1_smol"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.0" version = "1.4.0"
@@ -1191,9 +1139,9 @@ checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.8.0" version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
[[package]] [[package]]
name = "socket2" name = "socket2"
@@ -1213,9 +1161,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.96" version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf" checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1223,12 +1171,21 @@ dependencies = [
] ]
[[package]] [[package]]
name = "time" name = "termcolor"
version = "0.3.9" version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [ dependencies = [
"itoa", "winapi-util",
]
[[package]]
name = "time"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217"
dependencies = [
"itoa 1.0.2",
"libc", "libc",
"num_threads", "num_threads",
"time-macros", "time-macros",
@@ -1266,7 +1223,7 @@ dependencies = [
"memchr", "memchr",
"mio", "mio",
"once_cell", "once_cell",
"parking_lot 0.12.1", "parking_lot",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
"socket2", "socket2",
@@ -1289,9 +1246,9 @@ dependencies = [
[[package]] [[package]]
name = "tracing" name = "tracing"
version = "0.1.34" version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"log", "log",
@@ -1301,11 +1258,11 @@ dependencies = [
[[package]] [[package]]
name = "tracing-core" name = "tracing-core"
version = "0.1.26" version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7"
dependencies = [ dependencies = [
"lazy_static", "once_cell",
] ]
[[package]] [[package]]
@@ -1331,15 +1288,15 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.0" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
[[package]] [[package]]
name = "unicode-normalization" name = "unicode-normalization"
version = "0.1.19" version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6"
dependencies = [ dependencies = [
"tinyvec", "tinyvec",
] ]
@@ -1368,24 +1325,12 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1" checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
@@ -1394,9 +1339,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.80" version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"wasm-bindgen-macro", "wasm-bindgen-macro",
@@ -1404,9 +1349,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.80" version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"lazy_static", "lazy_static",
@@ -1419,9 +1364,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.80" version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@@ -1429,9 +1374,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.80" version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1442,15 +1387,15 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.80" version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.57" version = "0.3.58"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@@ -1472,6 +1417,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
@@ -1523,18 +1477,18 @@ checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]] [[package]]
name = "zstd" name = "zstd"
version = "0.10.2+zstd.1.5.2" version = "0.11.2+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f4a6bd64f22b5e3e94b4e238669ff9f10815c27a5180108b849d24174a83847" checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
dependencies = [ dependencies = [
"zstd-safe", "zstd-safe",
] ]
[[package]] [[package]]
name = "zstd-safe" name = "zstd-safe"
version = "4.1.6+zstd.1.5.2" version = "5.0.2+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94b61c51bb270702d6167b8ce67340d2754b088d0c091b06e593aa772c3ee9bb" checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db"
dependencies = [ dependencies = [
"libc", "libc",
"zstd-sys", "zstd-sys",
@@ -1542,9 +1496,9 @@ dependencies = [
[[package]] [[package]]
name = "zstd-sys" name = "zstd-sys"
version = "1.6.3+zstd.1.5.2" version = "2.0.1+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc49afa5c8d634e75761feda8c592051e7eeb4683ba827211eb0d731d3402ea8" checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "cryptgeon" name = "cryptgeon"
version = "1.5.3" version = "2.0.1"
authors = ["cupcakearmy <hi@nicco.io>"] authors = ["cupcakearmy <hi@nicco.io>"]
edition = "2021" edition = "2021"
@@ -18,7 +18,8 @@ serde_json = "1"
lazy_static = "1" lazy_static = "1"
ring = "0.16" ring = "0.16"
bs62 = "0.1" bs62 = "0.1"
memcache = "0.16"
byte-unit = "4" byte-unit = "4"
dotenv = "0.15" dotenv = "0.15"
mime = "0.3" mime = "0.3"
env_logger = "0.9"
redis = "0.21.5"

View File

@@ -1,13 +1,18 @@
use byte_unit::Byte; use byte_unit::Byte;
// General
lazy_static! { lazy_static! {
pub static ref VERSION: String = option_env!("CARGO_PKG_VERSION") pub static ref VERSION: String = option_env!("CARGO_PKG_VERSION")
.unwrap_or("Unknown") .unwrap_or("Unknown")
.to_string(); .to_string();
pub static ref LIMIT: u32 = }
// CONFIG
lazy_static! {
pub static ref LIMIT: usize =
Byte::from_str(std::env::var("SIZE_LIMIT").unwrap_or("1 KiB".to_string())) Byte::from_str(std::env::var("SIZE_LIMIT").unwrap_or("1 KiB".to_string()))
.unwrap() .unwrap()
.get_bytes() as u32; .get_bytes() as usize;
pub static ref MAX_VIEWS: u32 = std::env::var("MAX_VIEWS") pub static ref MAX_VIEWS: u32 = std::env::var("MAX_VIEWS")
.unwrap_or("100".to_string()) .unwrap_or("100".to_string())
.parse() .parse()
@@ -21,3 +26,15 @@ lazy_static! {
.parse() .parse()
.unwrap(); .unwrap();
} }
// THEME
lazy_static! {
pub static ref THEME_IMAGE: String = std::env::var("THEME_IMAGE")
.unwrap_or("".to_string())
.parse()
.unwrap();
pub static ref THEME_TEXT: String = std::env::var("THEME_TEXT")
.unwrap_or("".to_string())
.parse()
.unwrap();
}

View File

@@ -1,4 +1,7 @@
use actix_web::{middleware, web, App, HttpServer}; use actix_web::{
middleware::{self, Logger},
web, App, HttpServer,
};
use dotenv::dotenv; use dotenv::dotenv;
#[macro_use] #[macro_use]
@@ -15,8 +18,10 @@ mod store;
#[actix_web::main] #[actix_web::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
dotenv().ok(); dotenv().ok();
env_logger::init_from_env(env_logger::Env::new().default_filter_or("warning"));
return HttpServer::new(|| { return HttpServer::new(|| {
App::new() App::new()
.wrap(Logger::new("%a \"%r\" %s %b %T"))
.wrap(middleware::Compress::default()) .wrap(middleware::Compress::default())
.wrap(middleware::DefaultHeaders::default()) .wrap(middleware::DefaultHeaders::default())
.configure(size::init) .configure(size::init)

View File

@@ -22,9 +22,11 @@ struct NotePath {
async fn one(path: web::Path<NotePath>) -> impl Responder { async fn one(path: web::Path<NotePath>) -> impl Responder {
let p = path.into_inner(); let p = path.into_inner();
let note = store::get(&p.id); let note = store::get(&p.id);
match note { match note {
None => return HttpResponse::NotFound().finish(), Ok(Some(_)) => HttpResponse::Ok().json(NoteInfo {}),
Some(_) => return HttpResponse::Ok().json(NoteInfo {}), Ok(None) => HttpResponse::NotFound().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
} }
} }
@@ -64,8 +66,10 @@ async fn create(note: web::Json<Note>) -> impl Responder {
} }
_ => {} _ => {}
} }
store::set(&id.clone(), &n.clone()); match store::set(&id.clone(), &n.clone()) {
return HttpResponse::Ok().json(CreateResponse { id: id }); Ok(_) => return HttpResponse::Ok().json(CreateResponse { id: id }),
Err(e) => return HttpResponse::InternalServerError().body(e.to_string()),
}
} }
#[delete("/{id}")] #[delete("/{id}")]
@@ -73,8 +77,9 @@ async fn delete(path: web::Path<NotePath>) -> impl Responder {
let p = path.into_inner(); let p = path.into_inner();
let note = store::get(&p.id); let note = store::get(&p.id);
match note { match note {
None => return HttpResponse::NotFound().finish(), Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
Some(note) => { Ok(None) => return HttpResponse::NotFound().finish(),
Ok(Some(note)) => {
let mut changed = note.clone(); let mut changed = note.clone();
if changed.views == None && changed.expiration == None { if changed.views == None && changed.expiration == None {
return HttpResponse::BadRequest().finish(); return HttpResponse::BadRequest().finish();
@@ -84,9 +89,19 @@ async fn delete(path: web::Path<NotePath>) -> impl Responder {
changed.views = Some(v - 1); changed.views = Some(v - 1);
let id = p.id.clone(); let id = p.id.clone();
if v <= 1 { if v <= 1 {
store::del(&id); match store::del(&id) {
Err(e) => {
return HttpResponse::InternalServerError().body(e.to_string())
}
_ => {}
}
} else { } else {
store::set(&id, &changed.clone()); match store::set(&id, &changed.clone()) {
Err(e) => {
return HttpResponse::InternalServerError().body(e.to_string())
}
_ => {}
}
} }
} }
_ => {} _ => {}
@@ -96,8 +111,12 @@ async fn delete(path: web::Path<NotePath>) -> impl Responder {
match changed.expiration { match changed.expiration {
Some(e) => { Some(e) => {
if e < n { if e < n {
store::del(&p.id.clone()); match store::del(&p.id.clone()) {
return HttpResponse::BadRequest().finish(); Ok(_) => return HttpResponse::BadRequest().finish(),
Err(e) => {
return HttpResponse::InternalServerError().body(e.to_string())
}
}
} }
} }
_ => {} _ => {}

View File

@@ -1,18 +1,11 @@
use crate::config;
use actix_web::web; use actix_web::web;
use byte_unit::Byte;
use mime; 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) { pub fn init(cfg: &mut web::ServiceConfig) {
let json = web::JsonConfig::default().limit(*LIMIT); let json = web::JsonConfig::default().limit(*config::LIMIT);
let plain = web::PayloadConfig::default() let plain = web::PayloadConfig::default()
.limit(*LIMIT) .limit(*config::LIMIT)
.mimetype(mime::STAR_STAR); .mimetype(mime::STAR_STAR);
cfg.app_data(json).app_data(plain); cfg.app_data(json).app_data(plain);
} }

View File

@@ -2,9 +2,14 @@ use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Status { pub struct Status {
// General
pub version: String, pub version: String,
// Config
pub max_size: u32, pub max_size: u32,
pub max_views: u32, pub max_views: u32,
pub max_expiration: u32, pub max_expiration: u32,
pub allow_advanced: bool, pub allow_advanced: bool,
// Theme
pub theme_image: String,
pub theme_text: String,
} }

View File

@@ -7,10 +7,12 @@ use crate::status::Status;
async fn get_status() -> impl Responder { async fn get_status() -> impl Responder {
return HttpResponse::Ok().json(Status { return HttpResponse::Ok().json(Status {
version: config::VERSION.to_string(), version: config::VERSION.to_string(),
max_size: *config::LIMIT, max_size: *config::LIMIT as u32,
max_views: *config::MAX_VIEWS, max_views: *config::MAX_VIEWS,
max_expiration: *config::MAX_EXPIRATION, max_expiration: *config::MAX_EXPIRATION,
allow_advanced: *config::ALLOW_ADVANCED, allow_advanced: *config::ALLOW_ADVANCED,
theme_image: config::THEME_IMAGE.to_string(),
theme_text: config::THEME_TEXT.to_string(),
}); });
} }

View File

@@ -1,36 +1,55 @@
use memcache; use redis;
use redis::Commands;
use crate::note::now; use crate::note::now;
use crate::note::Note; use crate::note::Note;
lazy_static! { lazy_static! {
static ref CLIENT: memcache::Client = memcache::connect(format!( static ref REDIS_CLIENT: String = std::env::var("REDIS")
"memcache://{}?timeout=10&tcp_nodelay=true", .unwrap_or("redis://127.0.0.1/".to_string())
std::env::var("MEMCACHE").unwrap_or("127.0.0.1:11211".to_string()) .parse()
)) .unwrap();
.unwrap();
} }
pub fn set(id: &String, note: &Note) { fn get_connection() -> Result<redis::Connection, &'static str> {
let client =
redis::Client::open(REDIS_CLIENT.to_string()).map_err(|_| "Unable to connect to redis")?;
client
.get_connection()
.map_err(|_| "Unable to connect to redis")
}
pub fn set(id: &String, note: &Note) -> Result<(), &'static str> {
let serialized = serde_json::to_string(&note.clone()).unwrap(); let serialized = serde_json::to_string(&note.clone()).unwrap();
let expiration: u32 = match note.expiration { let mut conn = get_connection()?;
Some(e) => e - now(),
None => 0, conn.set(id, serialized)
.map_err(|_| "Unable to set note in redis")?;
match note.expiration {
Some(e) => {
let seconds = e - now();
conn.expire(id, seconds as usize)
.map_err(|_| "Unable to set expiration on notion")?
}
None => {}
}; };
CLIENT.set(id, serialized, expiration).unwrap(); Ok(())
} }
pub fn get(id: &String) -> Option<Note> { pub fn get(id: &String) -> Result<Option<Note>, &'static str> {
let value: Option<String> = CLIENT.get(&id).unwrap(); let mut conn = get_connection()?;
let value: Option<String> = conn.get(id).map_err(|_| "Could not load note in redis")?;
match value { match value {
None => return None, None => return Ok(None),
Some(s) => { Some(s) => {
let deserialize: Note = serde_json::from_str(&s).unwrap(); let deserialize: Note = serde_json::from_str(&s).unwrap();
return Some(deserialize); return Ok(Some(deserialize));
} }
} }
} }
pub fn del(id: &String) { pub fn del(id: &String) -> Result<(), &'static str> {
CLIENT.delete(id).unwrap(); let mut conn = get_connection()?;
conn.del(id).map_err(|_| "Unable to delete note in redis")?;
Ok(())
} }

BIN
design/Github_zh-CN.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

@@ -1,21 +1,19 @@
# DEV Compose file. # DEV Compose file.
# For a production file see: README.md # For a production file see: README.md
version: '3.7' version: '3.8'
services: services:
memcached: redis:
image: memcached:1-alpine image: redis:7-alpine
restart: unless-stopped
entrypoint: memcached -m 256M -I 128M
ports: ports:
- 11211:11211 - 6379:6379
app: app:
build: . build: .
depends_on: depends_on:
- memcached - redis
environment: environment:
SIZE_LIMIT: 128M SIZE_LIMIT: 128 MiB
ports: ports:
- 80:5000 - 80:5000

View File

@@ -1,13 +0,0 @@
#!/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

View File

@@ -1,14 +1,13 @@
version: '3.8' version: '3.8'
services: services:
memcached: redis:
image: memcached:1-alpine image: redis:7-alpine
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
depends_on: depends_on:
- memcached - redis
proxy: proxy:
image: nginx:alpine image: nginx:alpine

View File

@@ -7,7 +7,7 @@ This is a tiny guide to install cryptgeon on (probably) any unix system (and may
3. Run the cryptgeon. 3. Run the cryptgeon.
4. [Optional] install watchtower to keep up to date. 4. [Optional] install watchtower to keep up to date.
## Install Docker & DOcker Compose ## Install Docker & Docker Compose
- [Docker](https://docs.docker.com/engine/install/) - [Docker](https://docs.docker.com/engine/install/)
- [Compose](https://docs.docker.com/compose/install/) - [Compose](https://docs.docker.com/compose/install/)
@@ -107,16 +107,15 @@ networks:
external: true external: true
services: services:
memcached: redis:
image: memcached:1-alpine image: redis:7-alpine
restart: unless-stopped restart: unless-stopped
entrypoint: memcached -m 256M -I 4M # Limit to 128 MB Ram, customize at free will.
app: app:
image: cupcakearmy/cryptgeon:latest image: cupcakearmy/cryptgeon:latest
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
- memcached - redis
environment: environment:
SIZE_LIMIT: 4 MiB SIZE_LIMIT: 4 MiB
networks: networks:

View File

@@ -1,6 +1,7 @@
├─ MIT: 46 ├─ MIT: 12
├─ MIT*: 2 ├─ BSD-3-Clause: 1
├─ BSD-3-Clause: 2 ├─ (MPL-2.0 OR Apache-2.0): 1
├─ BSD-2-Clause: 1
├─ ISC: 1 ├─ ISC: 1
├─ 0BSD: 1 ├─ 0BSD: 1
└─ Apache-2.0: 1 └─ Apache-2.0: 1
1 ├─ MIT: 46 ├─ MIT: 12
2 ├─ MIT*: 2 ├─ BSD-3-Clause: 1
3 ├─ BSD-3-Clause: 2 ├─ (MPL-2.0 OR Apache-2.0): 1
4 ├─ BSD-2-Clause: 1
5 ├─ ISC: 1 ├─ ISC: 1
6 ├─ 0BSD: 1 ├─ 0BSD: 1
7 └─ Apache-2.0: 1 └─ Apache-2.0: 1

View File

@@ -4,13 +4,17 @@
"file": "Datei", "file": "Datei",
"advanced": "erweitert", "advanced": "erweitert",
"create": "erstellen", "create": "erstellen",
"loading": "Läd...", "loading": "läd",
"mode": "Modus", "mode": "Modus",
"views": "{n, plural, =0 {Ansichten} =1 {1 Ansicht} other {# Ansichten}}", "views": "{n, plural, =0 {Ansichten} =1 {1 Ansicht} other {# Ansichten}}",
"minutes": "{n, plural, =0 {Minuten} =1 {1 Minute} other {# Minuten}}", "minutes": "{n, plural, =0 {Minuten} =1 {1 Minute} other {# Minuten}}",
"max": "max", "max": "max",
"share_link": "Link teilen", "share_link": "Link teilen",
"copy_clipboard": "in die Zwischenablage kopieren" "copy_clipboard": "in die Zwischenablage kopieren",
"encrypting": "verschlüsseln",
"decrypting": "entschlüsselt",
"uploading": "hochladen",
"downloading": "wird heruntergeladen"
}, },
"home": { "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.", "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.",
@@ -28,7 +32,8 @@
"show": { "show": {
"errors": { "errors": {
"not_found": "wurde nicht gefunden oder wurde bereits gelöscht.", "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." "decryption_failed": "falsches Passwort. konnte nicht entziffert werden. wahrscheinlich ein defekter Link. Notiz wurde zerstört.",
"unsupported_type": "nicht unterstützter Notiztyp."
}, },
"explanation": "Klicken Sie unten, um die Notiz anzuzeigen und zu löschen, wenn der Zähler sein Limit erreicht hat", "explanation": "Klicken Sie unten, um die Notiz anzuzeigen und zu löschen, wenn der Zähler sein Limit erreicht hat",
"show_note": "Notiz anzeigen", "show_note": "Notiz anzeigen",

View File

@@ -4,13 +4,17 @@
"file": "file", "file": "file",
"advanced": "advanced", "advanced": "advanced",
"create": "create", "create": "create",
"loading": "loading...", "loading": "loading",
"mode": "mode", "mode": "mode",
"views": "{n, plural, =0 {views} =1 {1 view} other {# views}}", "views": "{n, plural, =0 {views} =1 {1 view} other {# views}}",
"minutes": "{n, plural, =0 {minutes} =1 {1 minute} other {# minutes}}", "minutes": "{n, plural, =0 {minutes} =1 {1 minute} other {# minutes}}",
"max": "max", "max": "max",
"share_link": "share link", "share_link": "share link",
"copy_clipboard": "copy to clipboard" "copy_clipboard": "copy to clipboard",
"encrypting": "encrypting",
"decrypting": "decrypting",
"uploading": "uploading",
"downloading": "downloading"
}, },
"home": { "home": {
"intro": "Easily send <i>fully encrypted</i>, secure notes or files with one click. Just create a note and share the link.", "intro": "Easily send <i>fully encrypted</i>, secure notes or files with one click. Just create a note and share the link.",
@@ -28,7 +32,8 @@
"show": { "show": {
"errors": { "errors": {
"not_found": "note was not found or was already deleted.", "not_found": "note was not found or was already deleted.",
"decryption_failed": "wrong password. could not decipher. probably a broken link. note was destroyed." "decryption_failed": "wrong password. could not decipher. probably a broken link. note was destroyed.",
"unsupported_type": "unsupported note type."
}, },
"explanation": "click below to show and delete the note if the counter has reached it's limit", "explanation": "click below to show and delete the note if the counter has reached it's limit",
"show_note": "show note", "show_note": "show note",

View File

@@ -4,13 +4,17 @@
"file": "archivo", "file": "archivo",
"advanced": "avanzado", "advanced": "avanzado",
"create": "crear", "create": "crear",
"loading": "cargando...", "loading": "cargando",
"mode": "modo", "mode": "modo",
"views": "{n, plural, =0 {vistas} =1 {1 vista} other {# vistas}}", "views": "{n, plural, =0 {vistas} =1 {1 vista} other {# vistas}}",
"minutes": "{n, plural, =0 {minutos} =1 {1 minuto} other {# minutos}}", "minutes": "{n, plural, =0 {minutos} =1 {1 minuto} other {# minutos}}",
"max": "max", "max": "max",
"share_link": "compartir enlace", "share_link": "compartir enlace",
"copy_clipboard": "copiar al portapapeles" "copy_clipboard": "copiar al portapapeles",
"encrypting": "encriptando",
"decrypting": "descifrando",
"uploading": "cargando",
"downloading": "descargando"
}, },
"home": { "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.", "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.",
@@ -28,7 +32,8 @@
"show": { "show": {
"errors": { "errors": {
"not_found": "la nota no se encontró o ya fue borrada.", "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." "decryption_failed": "contraseña incorrecta. no se pudo descifrar. probablemente un enlace roto. la nota fue destruida.",
"unsupported_type": "tipo de nota no compatible."
}, },
"explanation": "pulse abajo para mostrar y borrar la nota si el contador ha llegado a su límite", "explanation": "pulse abajo para mostrar y borrar la nota si el contador ha llegado a su límite",
"show_note": "mostrar nota", "show_note": "mostrar nota",

View File

@@ -4,13 +4,17 @@
"file": "fichier", "file": "fichier",
"advanced": "avancé", "advanced": "avancé",
"create": "créer", "create": "créer",
"loading": "chargement...", "loading": "chargement",
"mode": "mode", "mode": "mode",
"views": "{n, plural, =0 {vues} =1 {1 vue} other {# vues}}", "views": "{n, plural, =0 {vues} =1 {1 vue} other {# vues}}",
"minutes": "{n, plural, =0 {minutes} =1 {1 minute} other {# minutes}}", "minutes": "{n, plural, =0 {minutes} =1 {1 minute} other {# minutes}}",
"max": "max", "max": "max",
"share_link": "partager le lien", "share_link": "partager le lien",
"copy_clipboard": "copier dans le presse-papiers" "copy_clipboard": "copier dans le presse-papiers",
"encrypting": "cryptage",
"decrypting": "déchiffrer",
"uploading": "téléchargement",
"downloading": "téléchargement"
}, },
"home": { "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.", "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.",
@@ -28,7 +32,8 @@
"show": { "show": {
"errors": { "errors": {
"not_found": "La note n'a pas été trouvée ou a déjà été supprimée.", "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." "decryption_failed": "mauvais mot de passe. impossible à déchiffrer. probablement un lien brisé. la note a été détruite.",
"unsupported_type": "type de note non supporté."
}, },
"explanation": "Cliquez ci-dessous pour afficher et supprimer la note si le compteur a atteint sa limite.", "explanation": "Cliquez ci-dessous pour afficher et supprimer la note si le compteur a atteint sa limite.",
"show_note": "note de présentation", "show_note": "note de présentation",

View File

@@ -4,13 +4,17 @@
"file": "file", "file": "file",
"advanced": "avanzato", "advanced": "avanzato",
"create": "crea", "create": "crea",
"loading": "carica...", "loading": "carica",
"mode": "modalita", "mode": "modalita",
"views": "{n, plural, =0 {viste} =1 {1 vista} other {# viste}}", "views": "{n, plural, =0 {viste} =1 {1 vista} other {# viste}}",
"minutes": "{n, plural, =0 {minuti} =1 {1 minuto} other {# minuti}}", "minutes": "{n, plural, =0 {minuti} =1 {1 minuto} other {# minuti}}",
"max": "max", "max": "max",
"share_link": "condividi link", "share_link": "condividi link",
"copy_clipboard": "copia negli appunti" "copy_clipboard": "copia negli appunti",
"encrypting": "criptando",
"decrypting": "decifrando",
"uploading": "caricamento",
"downloading": "scaricando"
}, },
"home": { "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.", "intro": "Invia facilmente note o file <i>completamente criptati</i> e sicuri con un solo clic. Basta creare una nota e condividere il link.",
@@ -28,7 +32,8 @@
"show": { "show": {
"errors": { "errors": {
"not_found": "non è stata trovata o è stata già cancellata.", "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." "decryption_failed": "password sbagliata. non ha potuto decifrare. probabilmente un link rotto. la nota è stata distrutta.",
"unsupported_type": "tipo di nota non supportato."
}, },
"explanation": "clicca sotto per mostrare e cancellare la nota se il contatore ha raggiunto il suo limite", "explanation": "clicca sotto per mostrare e cancellare la nota se il contatore ha raggiunto il suo limite",
"show_note": "mostra la nota", "show_note": "mostra la nota",

View File

@@ -0,0 +1,47 @@
{
"common": {
"note": "密信",
"file": "上传文件",
"advanced": "高级设置",
"create": "创建",
"loading": "加载中",
"mode": "模式",
"views": "{n, plural, =0 {可查看次数} =1 {1 次查看} other {# 次查看}}",
"minutes": "{n, plural, =0 {有效期(分钟)} =1 {1 分钟} other {# 分钟}}",
"max": "最大值",
"share_link": "分享链接",
"copy_clipboard": "复制到剪切版",
"encrypting": "加密",
"decrypting": "解密",
"uploading": "上传",
"downloading": "下载"
},
"home": {
"intro": "一键轻松发送 <i>完全加密的</i> 密信或者文件。只需创建一个密信然后分享链接。",
"explanation": "该密信会在{type}后失效。",
"new_note": "新建密信",
"new_note_notice": "<b>可用性警示:</b><br />由于加密鸽的所有数据是全部保存在内存中的,所以如果加密鸽的可用内存被用光了那么它将会删除最早的密信以释放内存,因此不保证该密信的可用性。<br />(一般情况下是您应该是不会遇到这个问题,只是警示一下。)",
"errors": {
"note_to_big": "无法创建密信,这个密信太大了!",
"note_error": "无法创建密信,请再试一遍。",
"max": "最大文件大小: {n}",
"empty_content": "密信为空!"
},
"copied_to_clipboard": "已复制到剪切板 🔗"
},
"show": {
"errors": {
"not_found": "该密信无法被找到或者它已经被删除了!",
"decryption_failed": "密钥错误!您可能不小心粘贴了一个不完整的链接或者正在尝试破解该密信!但无论如何,该密信已被销毁!",
"unsupported_type": "不支持的票据类型。"
},
"explanation": "点击下方的按钮可以查看密信,如果它到达了限制将会被删除",
"show_note": "查看密信",
"warning_will_not_see_again": "您将<b>无法</b>再次查看该密信",
"download_all": "下载全部"
},
"file_upload": {
"selected_files": "已选中的文件",
"no_files_selected": "没有文件被选中"
}
}

View File

@@ -1,8 +1,8 @@
{ {
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "svelte-kit dev", "dev": "vite dev",
"build": "svelte-kit build", "build": "vite build",
"preview": "svelte-kit preview", "preview": "svelte-kit preview",
"check": "svelte-check --tsconfig tsconfig.json", "check": "svelte-check --tsconfig tsconfig.json",
"licenses": "license-checker --summary > licenses.csv", "licenses": "license-checker --summary > licenses.csv",
@@ -10,26 +10,26 @@
}, },
"type": "module", "type": "module",
"devDependencies": { "devDependencies": {
"@lokalise/node-api": "^7.2.0", "@lokalise/node-api": "^7.3.1",
"@sveltejs/adapter-static": "^1.0.0-next.34", "@sveltejs/adapter-static": "^1.0.0-next.37",
"@sveltejs/kit": "^1.0.0-next.348", "@sveltejs/kit": "^1.0.0-next.377",
"@types/dompurify": "^2.3.3",
"@types/file-saver": "^2.0.5", "@types/file-saver": "^2.0.5",
"@types/sanitize-html": "^2.6.2",
"adm-zip": "^0.5.9", "adm-zip": "^0.5.9",
"dotenv": "^16.0.1", "dotenv": "^16.0.1",
"svelte": "^3.48.0", "svelte": "^3.49.0",
"svelte-check": "^2.7.2", "svelte-check": "^2.8.0",
"svelte-intl-precompile": "^0.10.1", "svelte-intl-precompile": "^0.10.1",
"svelte-preprocess": "^4.10.7", "svelte-preprocess": "^4.10.7",
"tslib": "^2.4.0", "tslib": "^2.4.0",
"typescript": "^4.7.3", "typescript": "^4.7.4",
"vite": "^2.9.10" "vite": "^3.0.1"
}, },
"dependencies": { "dependencies": {
"@fontsource/fira-mono": "^4.5.8", "@fontsource/fira-mono": "^4.5.8",
"copy-to-clipboard": "^3.3.1", "copy-to-clipboard": "^3.3.1",
"dompurify": "^2.3.9",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"pretty-bytes": "^5.6.0", "pretty-bytes": "^5.6.0"
"sanitize-html": "^2.7.0"
} }
} }

659
frontend/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,61 @@
import type { EncryptedFileDTO, FileDTO } from './api'
import { Crypto } from './crypto'
abstract class CryptAdapter<T> {
abstract encrypt(plaintext: T, key: CryptoKey): Promise<string>
abstract decrypt(ciphertext: string, key: CryptoKey): Promise<T>
}
class CryptTextAdapter implements CryptAdapter<string> {
async encrypt(plaintext: string, key: CryptoKey) {
return await Crypto.encrypt(new TextEncoder().encode(plaintext), key)
}
async decrypt(ciphertext: string, key: CryptoKey) {
const plaintext = await Crypto.decrypt(ciphertext, key)
return new TextDecoder().decode(plaintext)
}
}
class CryptBlobAdapter implements CryptAdapter<Blob> {
async encrypt(plaintext: Blob, key: CryptoKey) {
return await Crypto.encrypt(await plaintext.arrayBuffer(), key)
}
async decrypt(ciphertext: string, key: CryptoKey) {
const plaintext = await Crypto.decrypt(ciphertext, key)
return new Blob([plaintext], { type: 'application/octet-stream' })
}
}
class CryptFilesAdapter implements CryptAdapter<FileDTO[]> {
async encrypt(plaintext: FileDTO[], key: CryptoKey) {
const adapter = new CryptBlobAdapter()
const data: Promise<EncryptedFileDTO>[] = plaintext.map(async (file) => ({
name: file.name,
size: file.size,
type: file.type,
contents: await adapter.encrypt(file.contents, key),
}))
return JSON.stringify(await Promise.all(data))
}
async decrypt(ciphertext: string, key: CryptoKey) {
const adapter = new CryptBlobAdapter()
const data: EncryptedFileDTO[] = JSON.parse(ciphertext)
const files: FileDTO[] = await Promise.all(
data.map(async (file) => ({
name: file.name,
size: file.size,
type: file.type,
contents: await adapter.decrypt(file.contents, key),
}))
)
return files
}
}
export const Adapters = {
Text: new CryptTextAdapter(),
Blob: new CryptBlobAdapter(),
Files: new CryptFilesAdapter(),
}

View File

@@ -11,6 +11,10 @@ export type NotePublic = Pick<Note, 'contents' | 'meta'>
export type NoteCreate = Omit<Note, 'meta'> & { meta: string } export type NoteCreate = Omit<Note, 'meta'> & { meta: string }
export type FileDTO = Pick<File, 'name' | 'size' | 'type'> & { export type FileDTO = Pick<File, 'name' | 'size' | 'type'> & {
contents: Blob
}
export type EncryptedFileDTO = Omit<FileDTO, 'contents'> & {
contents: string contents: string
} }

View File

@@ -19,53 +19,79 @@ export class Hex {
} }
} }
const ALG = 'AES-GCM' export class ArrayBufferUtils {
static async toString(buffer: ArrayBuffer): Promise<string> {
const reader = new window.FileReader()
reader.readAsDataURL(new Blob([buffer]))
return new Promise((resolve) => {
reader.onloadend = () => resolve(reader.result as string)
})
}
export function getRandomBytes(size = 16): Uint8Array { static async fromString(s: string): Promise<ArrayBuffer> {
return window.crypto.getRandomValues(new Uint8Array(size)) return fetch(s)
.then((r) => r.blob())
.then((b) => b.arrayBuffer())
}
} }
export function getKeyFromString(password: string) { export class Crypto {
return window.crypto.subtle.importKey( private static ALG = 'AES-GCM'
'raw', private static DELIMITER = ':::'
new TextEncoder().encode(password),
'PBKDF2',
false,
['deriveBits', 'deriveKey']
)
}
export async function getDerivedForKey(key: CryptoKey, salt: ArrayBuffer) { public static getRandomBytes(size: number): Uint8Array {
const iterations = 100_000 return window.crypto.getRandomValues(new Uint8Array(size))
return window.crypto.subtle.deriveKey( }
{
name: 'PBKDF2',
salt,
iterations,
hash: 'SHA-512',
},
key,
{ name: ALG, length: 256 },
true,
['encrypt', 'decrypt']
)
}
export async function encrypt(plaintext: string, key: CryptoKey) { public static getKeyFromString(password: string) {
const salt = getRandomBytes(16) return window.crypto.subtle.importKey(
const derived = await getDerivedForKey(key, salt) 'raw',
const iv = getRandomBytes(16) new TextEncoder().encode(password),
const encrypted = await window.crypto.subtle.encrypt( 'PBKDF2',
{ name: ALG, iv }, false,
derived, ['deriveBits', 'deriveKey']
new TextEncoder().encode(plaintext) )
) }
return [salt, iv, encrypted].map(Hex.encode).join(':') public static async getDerivedForKey(key: CryptoKey, salt: ArrayBuffer) {
} const iterations = 100_000
return window.crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt,
iterations,
hash: 'SHA-512',
},
key,
{ name: this.ALG, length: 256 },
true,
['encrypt', 'decrypt']
)
}
export async function decrypt(ciphertext: string, key: CryptoKey) { public static async encrypt(plaintext: ArrayBuffer, key: CryptoKey): Promise<string> {
const [salt, iv, encrypted] = ciphertext.split(':').map(Hex.decode) const salt = this.getRandomBytes(16)
const derived = await getDerivedForKey(key, salt) const derived = await this.getDerivedForKey(key, salt)
const plaintext = await window.crypto.subtle.decrypt({ name: ALG, iv }, derived, encrypted) const iv = this.getRandomBytes(16)
return new TextDecoder().decode(plaintext) const encrypted: ArrayBuffer = await window.crypto.subtle.encrypt(
{ name: this.ALG, iv },
derived,
plaintext
)
const data = [
Hex.encode(salt),
Hex.encode(iv),
await ArrayBufferUtils.toString(encrypted),
].join(this.DELIMITER)
return data
}
public static async decrypt(ciphertext: string, key: CryptoKey): Promise<ArrayBuffer> {
const splitted = ciphertext.split(this.DELIMITER)
const salt = Hex.decode(splitted[0])
const iv = Hex.decode(splitted[1])
const encrypted = await ArrayBufferUtils.fromString(splitted[2])
const derived = await this.getDerivedForKey(key, salt)
const plaintext = await window.crypto.subtle.decrypt({ name: this.ALG, iv }, derived, encrypted)
return plaintext
}
} }

View File

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

View File

@@ -7,6 +7,8 @@ export type Status = {
max_views: number max_views: number
max_expiration: number max_expiration: number
allow_advanced: boolean allow_advanced: boolean
theme_image: string
theme_text: string
} }
export const status = writable<null | Status>(null) export const status = writable<null | Status>(null)

View File

@@ -0,0 +1,47 @@
<script lang="ts">
import { t } from 'svelte-intl-precompile'
import type { Note } from '$lib/api'
import { status } from '$lib/stores/status'
import Switch from '$lib/ui/Switch.svelte'
import TextInput from '$lib/ui/TextInput.svelte'
export let note: Note
export let timeExpiration = false
</script>
<div class="fields">
<TextInput
type="number"
label={$t('common.views', { values: { n: 0 } })}
bind:value={note.views}
disabled={timeExpiration}
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">
<Switch label={$t('common.mode')} bind:value={timeExpiration} color={false} />
</div>
<TextInput
type="number"
label={$t('common.minutes', { values: { n: 0 } })}
bind:value={note.expiration}
disabled={!timeExpiration}
max={$status?.max_expiration}
validate={(v) =>
($status && v < $status?.max_expiration) ||
$t('home.errors.max', { values: { n: $status?.max_expiration ?? 0 } })}
/>
</div>
<style>
.middle-switch {
margin: 0 1rem;
}
.fields {
display: flex;
}
</style>

View File

@@ -1,38 +1,32 @@
<script lang="ts"> <script lang="ts">
import type { FileDTO } from '$lib/api'
import { Files } from '$lib/files'
import { createEventDispatcher } from 'svelte'
import { t } from 'svelte-intl-precompile' import { t } from 'svelte-intl-precompile'
import Button from './Button.svelte'
import MaxSize from './MaxSize.svelte' import type { FileDTO } from '$lib/api'
import Button from '$lib/ui/Button.svelte'
import MaxSize from '$lib/ui/MaxSize.svelte'
export let label: string = '' export let label: string = ''
let files: File[] = [] export let files: FileDTO[] = []
const dispatch = createEventDispatcher<{ file: string }>() function fileToDTO(file: File): FileDTO {
return {
name: file.name,
size: file.size,
type: file.type,
contents: file,
}
}
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 = [...files, ...Array.from(input.files)] files = [...files, ...Array.from(input.files).map(fileToDTO)]
const data: FileDTO[] = await Promise.all(
files.map(async (file) => ({
name: file.name,
type: file.type,
size: file.size,
contents: await Files.toString(file),
}))
)
dispatch('file', JSON.stringify(data))
} else {
dispatch('file', '')
} }
} }
function clear(e: Event) { function clear(e: Event) {
e.preventDefault() e.preventDefault()
files = [] files = []
dispatch('file', '')
} }
</script> </script>
@@ -57,7 +51,9 @@
<div> <div>
<b>{$t('file_upload.no_files_selected')}</b> <b>{$t('file_upload.no_files_selected')}</b>
<br /> <br />
<small>{$t('common.max')}: <MaxSize /></small> <small>
{$t('common.max')}: <MaxSize />
</small>
</div> </div>
{/if} {/if}
</div> </div>

View File

@@ -0,0 +1,41 @@
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 100 100"
xml:space="preserve"
>
<rect fill="none" stroke="currentColor" stroke-width="4" x="25" y="25" width="50" height="50">
<animateTransform
attributeName="transform"
dur="0.5s"
from="0 50 50"
to="180 50 50"
type="rotate"
id="strokeBox"
attributeType="XML"
begin="rectBox.end"
/>
</rect>
<rect x="27" y="27" fill="currentColor" width="46" height="50">
<animate
attributeName="height"
dur="1.3s"
attributeType="XML"
from="50"
to="0"
id="rectBox"
fill="freeze"
begin="0s;strokeBox.end"
/>
</rect>
</svg>
<style>
svg {
height: 2em;
position: relative;
top: 0.6em;
pointer-events: none;
}
</style>

After

Width:  |  Height:  |  Size: 784 B

View File

@@ -1,12 +1,17 @@
<script lang="ts"> <script lang="ts">
import { status } from '$lib/stores/status'
import prettyBytes from 'pretty-bytes' import prettyBytes from 'pretty-bytes'
import { _ } from 'svelte-intl-precompile' import { _ } from 'svelte-intl-precompile'
import { status } from '$lib/stores/status'
// Due to encoding overhead (~35%) with base64
// https://en.wikipedia.org/wiki/Base64
const overhead = 1 / 1.35
</script> </script>
<span> <span>
{#if $status !== null} {#if $status !== null}
{prettyBytes($status.max_size, { binary: true })} {prettyBytes($status.max_size * overhead, { binary: true })}
{:else} {:else}
{$_('common.loading')} {$_('common.loading')}
{/if} {/if}

View File

@@ -0,0 +1,36 @@
<script lang="ts" context="module">
export type NoteResult = {
password: string
id: string
}
</script>
<script lang="ts">
import { t } from 'svelte-intl-precompile'
import Button from '$lib/ui/Button.svelte'
import TextInput from '$lib/ui/TextInput.svelte'
export let result: NoteResult
function reset() {
window.location.reload()
}
</script>
<TextInput
type="text"
readonly
label={$t('common.share_link')}
value="{window.location.origin}/note/{result.id}#{result.password}"
copy
/>
<br />
<p>
{@html $t('home.new_note_notice')}
</p>
<br />
<Button on:click={reset}>{$t('home.new_note')}</Button>
<style>
</style>

View File

@@ -1,20 +1,24 @@
<script lang="ts" context="module">
export type DecryptedNote = Omit<NotePublic, 'contents'> & { contents: any }
</script>
<script lang="ts"> <script lang="ts">
import type { FileDTO, NotePublic } from '$lib/api'
import { Files } from '$lib/files'
import copy from 'copy-to-clipboard' import copy from 'copy-to-clipboard'
import DOMPurify from 'dompurify'
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 { t } from 'svelte-intl-precompile'
import Button from './Button.svelte'
export let note: NotePublic import type { FileDTO, NotePublic } from '$lib/api'
import Button from '$lib/ui/Button.svelte'
export let note: DecryptedNote
const RE_URL = /[A-Za-z]+:\/\/([A-Z a-z0-9\-._~:\/?#\[\]@!$&'()*+,;%=])+/g 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') {
files = JSON.parse(note.contents) as FileDTO[] files = note.contents
} }
$: download = () => { $: download = () => {
@@ -24,18 +28,18 @@
} }
async function downloadFile(file: FileDTO) { async function downloadFile(file: FileDTO) {
const f = new File([await Files.fromString(file.contents)], file.name, { const f = new File([file.contents], file.name, {
type: file.type, type: file.type,
}) })
saveAs(f) saveAs(f)
} }
function contentWithLinks(content: string): string { function contentWithLinks(content: string): string {
const replaced = note.contents.replace( const replaced = content.replace(
RE_URL, RE_URL,
(url) => `<a href="${url}" rel="noreferrer">${url}</a>` (url) => `<a href="${url}" rel="noreferrer">${url}</a>`
) )
return sanitize(replaced, { allowedTags: ['a'], allowedAttributes: { a: ['href', 'rel'] } }) return DOMPurify.sanitize(replaced, { USE_PROFILES: { html: true } })
} }
</script> </script>

View File

@@ -1,9 +1,10 @@
<script lang="ts"> <script lang="ts">
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 { t } from 'svelte-intl-precompile'
import { fade } from 'svelte/transition' import { fade } from 'svelte/transition'
import Icon from './Icon.svelte'
import { Crypto, Hex } from '$lib/crypto'
import Icon from '$lib/ui/Icon.svelte'
export let label: string = '' export let label: string = ''
export let value: any export let value: any
@@ -32,7 +33,7 @@
notify($t('home.copied_to_clipboard')) notify($t('home.copied_to_clipboard'))
} }
function randomFN() { function randomFN() {
value = Hex.encode(getRandomBytes(20)) value = Hex.encode(Crypto.getRandomBytes(20))
} }
function notify(msg: string, delay: number = 2000) { function notify(msg: string, delay: number = 2000) {

View File

@@ -1,28 +1,34 @@
<script lang="ts"> <script lang="ts">
import { create, Note, PayloadToLargeError } from '$lib/api'
import { encrypt, getKeyFromString, getRandomBytes, Hex } from '$lib/crypto'
import { status } from '$lib/stores/status'
import Button from '$lib/ui/Button.svelte'
import FileUpload from '$lib/ui/FileUpload.svelte'
import MaxSize from '$lib/ui/MaxSize.svelte'
import Switch from '$lib/ui/Switch.svelte'
import TextArea from '$lib/ui/TextArea.svelte'
import TextInput from '$lib/ui/TextInput.svelte'
import { t } from 'svelte-intl-precompile' import { t } from 'svelte-intl-precompile'
import { blur } from 'svelte/transition' import { blur } from 'svelte/transition'
import { Adapters } from '$lib/adapters'
import type { FileDTO, Note } from '$lib/api'
import { create, PayloadToLargeError } from '$lib/api'
import { Crypto, Hex } from '$lib/crypto'
import { status } from '$lib/stores/status'
import AdvancedParameters from '$lib/ui/AdvancedParameters.svelte'
import Button from '$lib/ui/Button.svelte'
import FileUpload from '$lib/ui/FileUpload.svelte'
import Loader from '$lib/ui/Loader.svelte'
import MaxSize from '$lib/ui/MaxSize.svelte'
import Result, { type NoteResult } from '$lib/ui/NoteResult.svelte'
import Switch from '$lib/ui/Switch.svelte'
import TextArea from '$lib/ui/TextArea.svelte'
let note: Note = { let note: Note = {
contents: '', contents: '',
meta: { type: 'text' }, meta: { type: 'text' },
views: 1, views: 1,
expiration: 60, expiration: 60,
} }
let result: { password: string; id: string } | null = null let files: FileDTO[]
let result: NoteResult | null = null
let advanced = false let advanced = false
let file = false let isFile = false
let timeExpiration = false let timeExpiration = false
let message = '' let description = ''
let loading = false let loading: string | null = null
let error: string | null = null let error: string | null = null
$: if (!advanced) { $: if (!advanced) {
@@ -31,7 +37,7 @@
} }
$: { $: {
message = $t('home.explanation', { description = $t('home.explanation', {
values: { values: {
type: $t(timeExpiration ? 'common.minutes' : 'common.views', { type: $t(timeExpiration ? 'common.minutes' : 'common.views', {
values: { n: (timeExpiration ? note.expiration : note.views) ?? '?' }, values: { n: (timeExpiration ? note.expiration : note.views) ?? '?' },
@@ -40,9 +46,9 @@
}) })
} }
$: note.meta.type = file ? 'file' : 'text' $: note.meta.type = isFile ? 'file' : 'text'
$: if (!file) { $: if (!isFile) {
note.contents = '' note.contents = ''
} }
@@ -51,17 +57,26 @@
async function submit() { async function submit() {
try { try {
error = null error = null
loading = true loading = $t('common.encrypting')
const password = Hex.encode(getRandomBytes(32))
const key = await getKeyFromString(password) const password = Hex.encode(Crypto.getRandomBytes(32))
if (note.contents === '') throw new EmptyContentError() const key = await Crypto.getKeyFromString(password)
const data: Note = { const data: Note = {
contents: await encrypt(note.contents, key), contents: '',
meta: note.meta, meta: note.meta,
} }
if (isFile) {
if (files.length === 0) throw new EmptyContentError()
data.contents = await Adapters.Files.encrypt(files, key)
} else {
if (note.contents === '') throw new EmptyContentError()
data.contents = await Adapters.Text.encrypt(note.contents, key)
}
if (timeExpiration) data.expiration = parseInt(note.expiration as any) if (timeExpiration) data.expiration = parseInt(note.expiration as any)
else data.views = parseInt(note.views as any) else data.views = parseInt(note.views as any)
loading = $t('common.uploading')
const response = await create(data) const response = await create(data)
result = { result = {
password: password, password: password,
@@ -73,46 +88,31 @@
} else if (e instanceof EmptyContentError) { } else if (e instanceof EmptyContentError) {
error = $t('home.errors.empty_content') error = $t('home.errors.empty_content')
} else { } else {
console.error(e)
error = $t('home.errors.note_error') error = $t('home.errors.note_error')
} }
} finally { } finally {
loading = false loading = null
} }
} }
function reset() {
window.location.reload()
}
</script> </script>
{#if result} {#if result}
<TextInput <Result {result} />
type="text"
readonly
label={$t('common.share_link')}
value="{window.location.origin}/note/{result.id}#{result.password}"
copy
/>
<br />
<p>
{@html $t('home.new_note_notice')}
</p>
<br />
<Button on:click={reset}>{$t('home.new_note')}</Button>
{:else} {:else}
<p> <p>
{@html $t('home.intro')} {@html $status?.theme_text || $t('home.intro')}
</p> </p>
<form on:submit|preventDefault={submit}> <form on:submit|preventDefault={submit}>
<fieldset disabled={loading}> <fieldset disabled={loading !== null}>
{#if file} {#if isFile}
<FileUpload label={$t('common.file')} on:file={(f) => (note.contents = f.detail)} /> <FileUpload label={$t('common.file')} bind:files />
{:else} {:else}
<TextArea label={$t('common.note')} bind:value={note.contents} placeholder="..." /> <TextArea label={$t('common.note')} bind:value={note.contents} placeholder="..." />
{/if} {/if}
<div class="bottom"> <div class="bottom">
<Switch class="file" label={$t('common.file')} bind:value={file} /> <Switch class="file" label={$t('common.file')} bind:value={isFile} />
{#if $status?.allow_advanced} {#if $status?.allow_advanced}
<Switch label={$t('common.advanced')} bind:value={advanced} /> <Switch label={$t('common.advanced')} bind:value={advanced} />
{/if} {/if}
@@ -131,40 +131,16 @@
<p> <p>
<br /> <br />
{#if loading} {#if loading}
{$t('common.loading')} {loading} <Loader />
{:else} {:else}
{message} {description}
{/if} {/if}
</p> </p>
{#if advanced} {#if advanced}
<div transition:blur={{ duration: 250 }}> <div transition:blur={{ duration: 250 }}>
<br /> <br />
<div class="fields"> <AdvancedParameters bind:note bind:timeExpiration />
<TextInput
type="number"
label={$t('common.views', { values: { n: 0 } })}
bind:value={note.views}
disabled={timeExpiration}
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">
<Switch label={$t('common.mode')} bind:value={timeExpiration} color={false} />
</div>
<TextInput
type="number"
label={$t('common.minutes', { values: { n: 0 } })}
bind:value={note.expiration}
disabled={!timeExpiration}
max={$status?.max_expiration}
validate={(v) =>
($status && v < $status?.max_expiration) ||
$t('home.errors.max', { values: { n: $status?.max_expiration ?? 0 } })}
/>
</div>
</div> </div>
{/if} {/if}
</fieldset> </fieldset>
@@ -186,15 +162,7 @@
flex: 1; flex: 1;
} }
.middle-switch {
margin: 0 1rem;
}
.error-text { .error-text {
margin-top: 0.5rem; margin-top: 0.5rem;
} }
.fields {
display: flex;
}
</style> </style>

View File

@@ -1,78 +1,87 @@
<script lang="ts">
import { status } from '$lib/stores/status'
</script>
<header> <header>
<a href="/"> <a href="/">
<svg {#if $status?.theme_image}
width="100%" <img alt="logo" src={$status.theme_image} />
height="100%" {:else}
viewBox="0 0 450 200" <svg
version="1.1" width="100%"
xmlns="http://www.w3.org/2000/svg" height="100%"
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 450 200"
xml:space="preserve" version="1.1"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;" xmlns="http://www.w3.org/2000/svg"
><g id="Logo" xmlns:xlink="http://www.w3.org/1999/xlink"
><clipPath id="_clip1"><rect x="6.336" y="3.225" width="193.55" height="193.55" /></clipPath xml:space="preserve"
><g clip-path="url(#_clip1)" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"
><g ><g id="Logo"
><clipPath id="_clip1"
><rect x="6.336" y="3.225" width="193.55" height="193.55" /></clipPath
><g clip-path="url(#_clip1)"
><g ><g
><path ><g
d="M173.425,43.296c-2.087,-0 -3.78,1.693 -3.78,3.78c-0,2.087 1.693,3.78 3.78,3.78c2.087,0 3.78,-1.693 3.78,-3.78c0,-2.087 -1.693,-3.78 -3.78,-3.78Z" ><path
style="fill-rule:nonzero;" d="M173.425,43.296c-2.087,-0 -3.78,1.693 -3.78,3.78c-0,2.087 1.693,3.78 3.78,3.78c2.087,0 3.78,-1.693 3.78,-3.78c0,-2.087 -1.693,-3.78 -3.78,-3.78Z"
/></g style="fill-rule:nonzero;"
/></g
></g
><g
><g
><path
d="M103.112,134.023c-2.087,-0 -3.781,1.693 -3.781,3.78c0,2.087 1.694,3.78 3.781,3.78c2.086,0 3.78,-1.693 3.78,-3.78c-0,-2.087 -1.694,-3.78 -3.78,-3.78Z"
style="fill-rule:nonzero;"
/></g
></g
><g
><g
><path
d="M156.036,96.22c-2.088,-0 -3.781,1.692 -3.781,3.78c0,18.76 -15.262,34.023 -34.022,34.023c-2.088,-0 -3.781,1.692 -3.781,3.78c0,2.088 1.693,3.78 3.781,3.78c22.929,0 41.583,-18.654 41.583,-41.583c-0,-2.088 -1.693,-3.78 -3.78,-3.78Z"
style="fill-rule:nonzero;"
/></g
></g
><g
><g
><path
d="M199.488,60.507l-9.515,-19.026c-4.102,-8.207 -12.35,-13.306 -21.527,-13.306c-7.479,-0 -14.626,3.547 -19.154,9.498l-10.679,12.016c-2.846,-4.047 -7.021,-7.049 -11.83,-8.421l-19.102,-5.459c-14.623,-4.178 -28.92,-15.441 -39.227,-30.901c-0.924,-1.386 -2.646,-2.003 -4.241,-1.521c-1.594,0.483 -2.684,1.953 -2.684,3.618l-0,20.372c-0,9.468 1.417,18.804 4.219,27.813c-2.936,0.73 -5.896,1.34 -8.843,1.816c-5.772,0.936 -11.653,1.411 -17.48,1.411l-29.308,-0c-1.374,-0 -2.64,0.746 -3.307,1.948c-0.666,1.202 -0.627,2.671 0.101,3.836l22.637,36.219c9.672,15.473 26.329,25.183 44.578,25.983l-36.36,41.158c-5.602,5.728 -3.655,15.315 3.746,18.396l20.017,9.887c0.089,0.044 0.179,0.084 0.271,0.121c5.79,2.313 12.389,-0.496 14.726,-6.279l13.969,-32.982l27.738,0c31.966,0 58.972,-25.967 58.972,-56.704l0,-22.682c0,-6.253 5.088,-11.341 11.341,-11.341l7.56,0c1.311,0 2.528,-0.678 3.216,-1.793c0.689,-1.114 0.752,-2.506 0.166,-3.677Zm-130.399,-42.236c10.418,12.203 23.307,21.034 36.515,24.808l19.103,5.459c3.726,1.063 6.866,3.624 8.666,7.048l-10.048,11.307l-17.652,-10.591c-7.646,-4.588 -16.746,-6.412 -25.776,-4.951c-2.838,0.46 -4.649,1.038 -6.877,1.759c-2.609,-8.332 -3.931,-16.971 -3.931,-25.733l0,-9.106Zm119.457,40.146c-10.422,-0 -18.901,8.479 -18.901,18.901l-0,22.682c-0,26.639 -23.544,49.144 -51.412,49.144l-30.243,-0c-10.77,-0 -20.451,5.983 -25.265,15.615l-0.797,1.596c-0.934,1.867 -0.177,4.137 1.691,5.071c1.867,0.934 4.138,0.176 5.071,-1.691c0.44,-0.586 3.306,-9.102 13.21,-12.125l-12.349,29.159c-0.01,0.024 -0.02,0.047 -0.029,0.071c-0.75,1.877 -2.864,2.851 -4.8,2.148c-21.279,-10.506 -19.997,-9.888 -20.252,-9.99c-2.526,-1.01 -3.191,-4.259 -1.267,-6.182c0.13,-0.131 8.026,-9.078 41.009,-46.411c13.867,-0.617 26.841,-6.319 36.694,-16.172c1.476,-1.476 1.476,-3.87 0,-5.346c-1.476,-1.477 -3.87,-1.476 -5.346,-0c-16.827,16.828 -36.803,13.634 -39.028,14.014c-16.603,0 -31.771,-8.407 -40.573,-22.488l-2.417,-3.868l2.729,1.065c17.307,6.753 38.919,4.347 53.816,-5.586c1.737,-1.158 2.207,-3.505 1.049,-5.242c-1.159,-1.737 -3.505,-2.207 -5.243,-1.048c-13.085,8.724 -31.922,10.666 -46.875,4.832l-12.185,-4.753l-9.896,-15.836l22.488,0c6.231,0 12.519,-0.507 18.689,-1.507c12.711,-2.055 18.051,-4.855 22.992,-5.655c7.182,-1.163 14.516,0.274 20.678,3.97l29.625,17.775c1.789,1.074 4.112,0.494 5.186,-1.296c1.075,-1.79 0.495,-4.113 -1.295,-5.187l-5.378,-3.226c26.56,-29.893 25.14,-28.272 25.32,-28.511c3.101,-4.136 8.038,-6.605 13.204,-6.605c6.294,0 11.951,3.497 14.764,9.127l6.779,13.555l-1.443,-0Z"
style="fill-rule:nonzero;"
/></g
></g
></g ></g
><g ><g
><g ><path
><path d="M222.664,114.692c0.85,-0 1.274,0.586 1.274,1.759c0,1.174 -0.242,2.347 -0.728,3.52c-0.485,1.173 -1.183,2.326 -2.093,3.459c-0.91,1.132 -2.185,2.063 -3.823,2.791c-1.639,0.728 -3.489,1.092 -5.552,1.092c-4.531,0 -8.081,-1.143 -10.65,-3.428c-2.569,-2.286 -3.853,-5.644 -3.853,-10.073c-0,-4.43 1.133,-7.889 3.398,-10.377c2.265,-2.488 5.158,-3.732 8.677,-3.732c2.549,0 4.41,0.678 5.583,2.033c1.173,1.355 1.76,2.964 1.76,4.824c-0,1.012 -0.304,1.821 -0.91,2.428c-0.607,0.606 -1.194,0.91 -1.76,0.91c-1.052,-0 -1.74,-0.243 -2.063,-0.728c0.242,-0.607 0.364,-1.477 0.364,-2.61c-0,-1.132 -0.385,-2.164 -1.153,-3.094c-0.769,-0.931 -1.699,-1.396 -2.792,-1.396c-1.901,0 -3.266,0.93 -4.095,2.791c-0.83,1.861 -1.244,4.784 -1.244,8.769c-0,3.984 0.778,6.867 2.336,8.647c1.557,1.78 3.732,2.67 6.523,2.67c2.791,-0 5.138,-0.86 7.039,-2.579c1.901,-1.72 2.852,-4.157 2.852,-7.312c0.04,-0.243 0.344,-0.364 0.91,-0.364Z"
d="M103.112,134.023c-2.087,-0 -3.781,1.693 -3.781,3.78c0,2.087 1.694,3.78 3.781,3.78c2.086,0 3.78,-1.693 3.78,-3.78c-0,-2.087 -1.694,-3.78 -3.78,-3.78Z" style="fill-rule:nonzero;"
style="fill-rule:nonzero;" /><path
/></g d="M227.337,116.815l-0,9.649c-0.405,0.566 -1.113,0.849 -2.124,0.849c-1.012,0 -1.821,-0.313 -2.428,-0.94c-0.606,-0.627 -0.91,-1.548 -0.91,-2.761l0,-23.059c0.405,-0.566 1.113,-0.85 2.124,-0.85c1.011,0 1.821,0.314 2.427,0.941c0.607,0.627 0.911,1.547 0.911,2.761l-0,4.612c2.022,-5.017 4.814,-7.525 8.373,-7.525c2.549,0 4.41,0.678 5.583,2.033c1.173,1.355 1.76,2.963 1.76,4.824c-0,1.012 -0.304,1.821 -0.91,2.427c-0.607,0.607 -1.194,0.911 -1.76,0.911c-1.052,-0 -1.74,-0.243 -2.063,-0.728c0.242,-0.607 0.364,-1.477 0.364,-2.61c-0,-1.132 -0.384,-2.164 -1.153,-3.094c-0.769,-0.931 -1.699,-1.396 -2.791,-1.396c-1.902,-0 -3.611,1.729 -5.128,5.188c-1.517,3.459 -2.275,6.382 -2.275,8.768Z"
></g style="fill-rule:nonzero;"
><g /><path
><g d="M245.419,103.405c0,-1.214 0.304,-2.134 0.911,-2.761c0.606,-0.627 1.375,-0.941 2.306,-0.941c0.93,0 1.678,0.284 2.245,0.85l-0,17.415c-0,4.814 1.213,7.221 3.641,7.221c2.184,0 3.974,-1.153 5.37,-3.458c1.395,-2.306 2.093,-6.19 2.093,-11.651l0,-6.675c0,-1.214 0.304,-2.134 0.91,-2.761c0.607,-0.627 1.376,-0.941 2.306,-0.941c0.931,0 1.679,0.284 2.246,0.85l-0,30.826c0.485,-0.041 1.254,-0.061 2.305,-0.061c1.052,0 1.578,0.445 1.578,1.335l-0.364,0.91c-1.537,0 -2.71,0.021 -3.519,0.061l-0,3.034c-0,4.167 -0.941,7.454 -2.822,9.861c-1.881,2.407 -4.399,3.61 -7.555,3.61c-1.861,0 -3.509,-0.556 -4.945,-1.669c-1.436,-1.112 -2.155,-2.862 -2.155,-5.248c0,-3.075 1.093,-5.522 3.277,-7.343c2.185,-1.82 5.097,-3.095 8.738,-3.823l0,-9.466c-1.699,3.156 -4.429,4.733 -8.192,4.733c-2.589,0 -4.632,-0.829 -6.128,-2.488c-1.497,-1.658 -2.246,-4.045 -2.246,-7.16l0,-14.26Zm7.646,39.989c0,1.416 0.385,2.498 1.153,3.246c0.769,0.748 1.699,1.123 2.791,1.123c1.093,-0 1.983,-0.324 2.67,-0.971c1.538,-1.376 2.306,-5.522 2.306,-12.44c-5.946,1.335 -8.92,4.349 -8.92,9.042Z"
><path style="fill-rule:nonzero;"
d="M156.036,96.22c-2.088,-0 -3.781,1.692 -3.781,3.78c0,18.76 -15.262,34.023 -34.022,34.023c-2.088,-0 -3.781,1.692 -3.781,3.78c0,2.088 1.693,3.78 3.781,3.78c22.929,0 41.583,-18.654 41.583,-41.583c-0,-2.088 -1.693,-3.78 -3.78,-3.78Z" /><path
style="fill-rule:nonzero;" d="M298.576,104.861c1.335,2.306 2.002,5.411 2.002,9.315c0,3.904 -0.981,7.059 -2.943,9.466c-1.962,2.407 -4.52,3.611 -7.676,3.611c-1.861,-0 -3.509,-0.648 -4.945,-1.942c-1.436,-1.295 -2.155,-3.358 -2.155,-6.19c0,-6.634 3.297,-11.59 9.891,-14.866c-1.051,-1.457 -2.518,-2.185 -4.399,-2.185c-1.881,0 -3.671,0.93 -5.37,2.791c-1.699,1.861 -2.549,4.653 -2.549,8.374l0,36.105c-0.404,0.567 -1.112,0.85 -2.124,0.85c-1.011,-0 -1.82,-0.314 -2.427,-0.941c-0.607,-0.627 -0.91,-1.547 -0.91,-2.761l-0,-45.935c0.405,-0.566 1.112,-0.85 2.124,-0.85c1.011,0 1.82,0.314 2.427,0.941c0.607,0.627 0.91,1.547 0.91,2.761l0,1.335c2.104,-3.358 4.885,-5.037 8.344,-5.037c3.459,0 6.159,0.971 8.101,2.913c4.247,-1.375 9.001,-2.063 14.26,-2.063c1.092,-0 1.638,0.445 1.638,1.335l-0.364,0.91c-4.895,0 -9.507,0.688 -13.835,2.063Zm-8.738,20.025c3.317,-0 4.976,-3.297 4.976,-9.891c-0,-3.479 -0.284,-6.21 -0.85,-8.192c-5.34,2.913 -8.01,7.08 -8.01,12.5c0,1.78 0.385,3.156 1.153,4.127c0.769,0.971 1.679,1.456 2.731,1.456Z"
/></g style="fill-rule:nonzero;"
></g /><path
><g d="M308.285,94.424c-0,-1.213 0.303,-2.134 0.91,-2.761c0.607,-0.627 1.375,-0.94 2.306,-0.94c0.93,-0 1.679,0.283 2.245,0.849l0,8.981l8.313,-0c0.85,-0 1.406,0.162 1.669,0.485c0.263,0.324 0.394,0.87 0.394,1.639l-10.376,-0l0,15.291c0,2.225 0.637,3.945 1.911,5.158c1.275,1.214 2.741,1.821 4.4,1.821c2.508,-0 4.349,-0.931 5.522,-2.792c1.173,-1.861 1.76,-4.874 1.76,-9.041c0.08,-0.243 0.384,-0.364 0.91,-0.364c0.728,-0 1.092,1.072 1.092,3.216c-0,3.277 -0.819,5.987 -2.458,8.131c-1.638,2.144 -4.156,3.216 -7.554,3.216c-3.398,0 -6.089,-0.89 -8.071,-2.67c-1.982,-1.78 -2.973,-4.429 -2.973,-7.949l-0,-22.27Z"
><g style="fill-rule:nonzero;"
><path /><path
d="M199.488,60.507l-9.515,-19.026c-4.102,-8.207 -12.35,-13.306 -21.527,-13.306c-7.479,-0 -14.626,3.547 -19.154,9.498l-10.679,12.016c-2.846,-4.047 -7.021,-7.049 -11.83,-8.421l-19.102,-5.459c-14.623,-4.178 -28.92,-15.441 -39.227,-30.901c-0.924,-1.386 -2.646,-2.003 -4.241,-1.521c-1.594,0.483 -2.684,1.953 -2.684,3.618l-0,20.372c-0,9.468 1.417,18.804 4.219,27.813c-2.936,0.73 -5.896,1.34 -8.843,1.816c-5.772,0.936 -11.653,1.411 -17.48,1.411l-29.308,-0c-1.374,-0 -2.64,0.746 -3.307,1.948c-0.666,1.202 -0.627,2.671 0.101,3.836l22.637,36.219c9.672,15.473 26.329,25.183 44.578,25.983l-36.36,41.158c-5.602,5.728 -3.655,15.315 3.746,18.396l20.017,9.887c0.089,0.044 0.179,0.084 0.271,0.121c5.79,2.313 12.389,-0.496 14.726,-6.279l13.969,-32.982l27.738,0c31.966,0 58.972,-25.967 58.972,-56.704l0,-22.682c0,-6.253 5.088,-11.341 11.341,-11.341l7.56,0c1.311,0 2.528,-0.678 3.216,-1.793c0.689,-1.114 0.752,-2.506 0.166,-3.677Zm-130.399,-42.236c10.418,12.203 23.307,21.034 36.515,24.808l19.103,5.459c3.726,1.063 6.866,3.624 8.666,7.048l-10.048,11.307l-17.652,-10.591c-7.646,-4.588 -16.746,-6.412 -25.776,-4.951c-2.838,0.46 -4.649,1.038 -6.877,1.759c-2.609,-8.332 -3.931,-16.971 -3.931,-25.733l0,-9.106Zm119.457,40.146c-10.422,-0 -18.901,8.479 -18.901,18.901l-0,22.682c-0,26.639 -23.544,49.144 -51.412,49.144l-30.243,-0c-10.77,-0 -20.451,5.983 -25.265,15.615l-0.797,1.596c-0.934,1.867 -0.177,4.137 1.691,5.071c1.867,0.934 4.138,0.176 5.071,-1.691c0.44,-0.586 3.306,-9.102 13.21,-12.125l-12.349,29.159c-0.01,0.024 -0.02,0.047 -0.029,0.071c-0.75,1.877 -2.864,2.851 -4.8,2.148c-21.279,-10.506 -19.997,-9.888 -20.252,-9.99c-2.526,-1.01 -3.191,-4.259 -1.267,-6.182c0.13,-0.131 8.026,-9.078 41.009,-46.411c13.867,-0.617 26.841,-6.319 36.694,-16.172c1.476,-1.476 1.476,-3.87 0,-5.346c-1.476,-1.477 -3.87,-1.476 -5.346,-0c-16.827,16.828 -36.803,13.634 -39.028,14.014c-16.603,0 -31.771,-8.407 -40.573,-22.488l-2.417,-3.868l2.729,1.065c17.307,6.753 38.919,4.347 53.816,-5.586c1.737,-1.158 2.207,-3.505 1.049,-5.242c-1.159,-1.737 -3.505,-2.207 -5.243,-1.048c-13.085,8.724 -31.922,10.666 -46.875,4.832l-12.185,-4.753l-9.896,-15.836l22.488,0c6.231,0 12.519,-0.507 18.689,-1.507c12.711,-2.055 18.051,-4.855 22.992,-5.655c7.182,-1.163 14.516,0.274 20.678,3.97l29.625,17.775c1.789,1.074 4.112,0.494 5.186,-1.296c1.075,-1.79 0.495,-4.113 -1.295,-5.187l-5.378,-3.226c26.56,-29.893 25.14,-28.272 25.32,-28.511c3.101,-4.136 8.038,-6.605 13.204,-6.605c6.294,0 11.951,3.497 14.764,9.127l6.779,13.555l-1.443,-0Z" d="M345.543,101.463c0.526,-1.173 1.365,-1.76 2.518,-1.76c1.153,0 2.013,0.284 2.579,0.85l-0,30.826c0.485,-0.041 1.254,-0.061 2.306,-0.061c1.052,0 1.578,0.445 1.578,1.335l-0.365,0.91c-1.537,0 -2.71,0.021 -3.519,0.061l-0,3.155c-0.041,4.167 -1.001,7.434 -2.882,9.8c-1.882,2.367 -4.38,3.55 -7.494,3.55c-1.861,0 -3.51,-0.556 -4.946,-1.669c-1.436,-1.112 -2.154,-2.862 -2.154,-5.248c-0,-3.075 1.092,-5.522 3.277,-7.343c2.184,-1.82 5.097,-3.095 8.738,-3.823l-0,-8.92c-1.699,2.792 -4.046,4.187 -7.039,4.187c-7.08,0 -10.619,-4.632 -10.619,-13.896c-0,-4.692 1.102,-8.151 3.307,-10.376c2.204,-2.225 5.127,-3.338 8.768,-3.338c2.791,0 4.774,0.587 5.947,1.76Zm-0.364,12.986l-0,-4.308c-0,-2.873 -0.486,-4.936 -1.457,-6.19c-0.971,-1.254 -2.093,-1.881 -3.367,-1.881c-1.275,0 -2.276,0.121 -3.004,0.364c-0.728,0.243 -1.436,0.748 -2.124,1.517c-1.295,1.456 -1.942,4.612 -1.942,9.466c0,3.196 0.283,5.684 0.85,7.464c0.566,1.78 1.213,2.903 1.942,3.368c0.728,0.465 1.749,0.698 3.064,0.698c1.315,-0 2.65,-0.84 4.005,-2.519c1.355,-1.678 2.033,-4.338 2.033,-7.979Zm-0,22.209l-0,-2.306c-5.947,1.335 -8.92,4.349 -8.92,9.042c-0,1.416 0.384,2.498 1.153,3.246c0.768,0.748 1.678,1.123 2.73,1.123c1.699,-0 2.963,-0.789 3.793,-2.367c0.829,-1.578 1.244,-4.49 1.244,-8.738Z"
style="fill-rule:nonzero;" style="fill-rule:nonzero;"
/></g /><path
></g d="M381.891,113.478c0.849,-0 1.274,0.647 1.274,1.942c-0,2.993 -1.042,5.724 -3.125,8.192c-2.084,2.467 -5.249,3.701 -9.497,3.701c-4.247,0 -7.656,-1.143 -10.224,-3.428c-2.569,-2.286 -3.854,-5.644 -3.854,-10.073c0,-4.43 1.133,-7.889 3.398,-10.377c2.266,-2.488 5.158,-3.732 8.678,-3.732c2.548,0 4.409,0.678 5.582,2.033c1.174,1.355 1.76,2.964 1.76,4.824c0,3.034 -1.264,5.553 -3.792,7.555c-2.529,2.003 -5.755,3.004 -9.679,3.004c0.364,2.589 1.305,4.541 2.822,5.856c1.517,1.314 3.519,1.972 6.007,1.972c2.488,-0 4.733,-0.941 6.736,-2.822c2.002,-1.881 3.003,-4.642 3.003,-8.283c0.041,-0.243 0.344,-0.364 0.911,-0.364Zm-19.661,0.85l-0,0.546c2.508,-0.324 4.682,-1.113 6.523,-2.367c1.841,-1.254 2.761,-3.115 2.761,-5.583c0,-1.577 -0.344,-2.781 -1.031,-3.61c-0.688,-0.829 -1.659,-1.244 -2.913,-1.244c-1.901,0 -3.267,0.93 -4.096,2.791c-0.829,1.861 -1.244,5.017 -1.244,9.467Z"
style="fill-rule:nonzero;"
/><path
d="M393.359,99.703c3.601,0 6.534,1.194 8.799,3.581c2.265,2.386 3.398,5.633 3.398,9.739c0,4.106 -1.062,7.514 -3.186,10.225c-2.124,2.71 -4.965,4.065 -8.525,4.065c-3.56,0 -6.524,-1.224 -8.89,-3.671c-2.367,-2.447 -3.55,-5.846 -3.55,-10.194c0,-4.349 1.102,-7.727 3.307,-10.134c2.205,-2.407 5.087,-3.611 8.647,-3.611Zm0.061,2.367c-1.092,0 -2.033,0.273 -2.822,0.819c-0.789,0.546 -1.183,1.325 -1.183,2.336c-0,2.589 1.011,5.067 3.034,7.434c2.023,2.366 4.43,3.934 7.221,4.703c0.081,-1.538 0.121,-2.852 0.121,-3.945c0,-4.207 -0.445,-7.15 -1.335,-8.829c-0.89,-1.679 -2.568,-2.518 -5.036,-2.518Zm-6.25,11.347c-0,4.127 0.516,7.08 1.547,8.86c1.032,1.78 2.721,2.67 5.067,2.67c1.618,-0 2.862,-0.415 3.732,-1.244c0.87,-0.83 1.466,-2.175 1.79,-4.036c-2.63,-0.566 -5.047,-1.759 -7.251,-3.58c-2.205,-1.82 -3.813,-3.843 -4.825,-6.068c-0.04,0.688 -0.06,1.821 -0.06,3.398Z"
style="fill-rule:nonzero;"
/><path
d="M442.571,112.75c0.729,-0 1.093,1.072 1.093,3.216c-0,3.438 -0.739,6.189 -2.215,8.252c-1.477,2.064 -3.631,3.095 -6.463,3.095c-2.832,0 -5.006,-0.971 -6.523,-2.912c-1.517,-1.942 -2.275,-4.693 -2.275,-8.253l-0,-7.1c-0,-4.814 -1.214,-7.221 -3.641,-7.221c-2.144,0 -3.904,1.093 -5.28,3.277c-1.375,2.185 -2.063,5.846 -2.063,10.983l0,10.377c-0.404,0.566 -1.112,0.849 -2.124,0.849c-1.011,0 -1.82,-0.313 -2.427,-0.94c-0.607,-0.627 -0.91,-1.548 -0.91,-2.761l0,-23.059c0.405,-0.566 1.173,-0.85 2.306,-0.85c2.103,0 3.155,1.517 3.155,4.552c1.699,-3.035 4.39,-4.552 8.071,-4.552c2.629,0 4.723,0.83 6.28,2.488c1.558,1.659 2.337,4.046 2.337,7.161l-0,8.616c-0,2.306 0.364,4.046 1.092,5.219c0.728,1.173 1.81,1.76 3.246,1.76c1.436,-0 2.7,-0.9 3.793,-2.701c1.092,-1.8 1.638,-4.844 1.638,-9.132c0.041,-0.243 0.344,-0.364 0.91,-0.364Z"
style="fill-rule:nonzero;"
/></g
></g ></g
><g ></svg
><path >
d="M222.664,114.692c0.85,-0 1.274,0.586 1.274,1.759c0,1.174 -0.242,2.347 -0.728,3.52c-0.485,1.173 -1.183,2.326 -2.093,3.459c-0.91,1.132 -2.185,2.063 -3.823,2.791c-1.639,0.728 -3.489,1.092 -5.552,1.092c-4.531,0 -8.081,-1.143 -10.65,-3.428c-2.569,-2.286 -3.853,-5.644 -3.853,-10.073c-0,-4.43 1.133,-7.889 3.398,-10.377c2.265,-2.488 5.158,-3.732 8.677,-3.732c2.549,0 4.41,0.678 5.583,2.033c1.173,1.355 1.76,2.964 1.76,4.824c-0,1.012 -0.304,1.821 -0.91,2.428c-0.607,0.606 -1.194,0.91 -1.76,0.91c-1.052,-0 -1.74,-0.243 -2.063,-0.728c0.242,-0.607 0.364,-1.477 0.364,-2.61c-0,-1.132 -0.385,-2.164 -1.153,-3.094c-0.769,-0.931 -1.699,-1.396 -2.792,-1.396c-1.901,0 -3.266,0.93 -4.095,2.791c-0.83,1.861 -1.244,4.784 -1.244,8.769c-0,3.984 0.778,6.867 2.336,8.647c1.557,1.78 3.732,2.67 6.523,2.67c2.791,-0 5.138,-0.86 7.039,-2.579c1.901,-1.72 2.852,-4.157 2.852,-7.312c0.04,-0.243 0.344,-0.364 0.91,-0.364Z" {/if}
style="fill-rule:nonzero;"
/><path
d="M227.337,116.815l-0,9.649c-0.405,0.566 -1.113,0.849 -2.124,0.849c-1.012,0 -1.821,-0.313 -2.428,-0.94c-0.606,-0.627 -0.91,-1.548 -0.91,-2.761l0,-23.059c0.405,-0.566 1.113,-0.85 2.124,-0.85c1.011,0 1.821,0.314 2.427,0.941c0.607,0.627 0.911,1.547 0.911,2.761l-0,4.612c2.022,-5.017 4.814,-7.525 8.373,-7.525c2.549,0 4.41,0.678 5.583,2.033c1.173,1.355 1.76,2.963 1.76,4.824c-0,1.012 -0.304,1.821 -0.91,2.427c-0.607,0.607 -1.194,0.911 -1.76,0.911c-1.052,-0 -1.74,-0.243 -2.063,-0.728c0.242,-0.607 0.364,-1.477 0.364,-2.61c-0,-1.132 -0.384,-2.164 -1.153,-3.094c-0.769,-0.931 -1.699,-1.396 -2.791,-1.396c-1.902,-0 -3.611,1.729 -5.128,5.188c-1.517,3.459 -2.275,6.382 -2.275,8.768Z"
style="fill-rule:nonzero;"
/><path
d="M245.419,103.405c0,-1.214 0.304,-2.134 0.911,-2.761c0.606,-0.627 1.375,-0.941 2.306,-0.941c0.93,0 1.678,0.284 2.245,0.85l-0,17.415c-0,4.814 1.213,7.221 3.641,7.221c2.184,0 3.974,-1.153 5.37,-3.458c1.395,-2.306 2.093,-6.19 2.093,-11.651l0,-6.675c0,-1.214 0.304,-2.134 0.91,-2.761c0.607,-0.627 1.376,-0.941 2.306,-0.941c0.931,0 1.679,0.284 2.246,0.85l-0,30.826c0.485,-0.041 1.254,-0.061 2.305,-0.061c1.052,0 1.578,0.445 1.578,1.335l-0.364,0.91c-1.537,0 -2.71,0.021 -3.519,0.061l-0,3.034c-0,4.167 -0.941,7.454 -2.822,9.861c-1.881,2.407 -4.399,3.61 -7.555,3.61c-1.861,0 -3.509,-0.556 -4.945,-1.669c-1.436,-1.112 -2.155,-2.862 -2.155,-5.248c0,-3.075 1.093,-5.522 3.277,-7.343c2.185,-1.82 5.097,-3.095 8.738,-3.823l0,-9.466c-1.699,3.156 -4.429,4.733 -8.192,4.733c-2.589,0 -4.632,-0.829 -6.128,-2.488c-1.497,-1.658 -2.246,-4.045 -2.246,-7.16l0,-14.26Zm7.646,39.989c0,1.416 0.385,2.498 1.153,3.246c0.769,0.748 1.699,1.123 2.791,1.123c1.093,-0 1.983,-0.324 2.67,-0.971c1.538,-1.376 2.306,-5.522 2.306,-12.44c-5.946,1.335 -8.92,4.349 -8.92,9.042Z"
style="fill-rule:nonzero;"
/><path
d="M298.576,104.861c1.335,2.306 2.002,5.411 2.002,9.315c0,3.904 -0.981,7.059 -2.943,9.466c-1.962,2.407 -4.52,3.611 -7.676,3.611c-1.861,-0 -3.509,-0.648 -4.945,-1.942c-1.436,-1.295 -2.155,-3.358 -2.155,-6.19c0,-6.634 3.297,-11.59 9.891,-14.866c-1.051,-1.457 -2.518,-2.185 -4.399,-2.185c-1.881,0 -3.671,0.93 -5.37,2.791c-1.699,1.861 -2.549,4.653 -2.549,8.374l0,36.105c-0.404,0.567 -1.112,0.85 -2.124,0.85c-1.011,-0 -1.82,-0.314 -2.427,-0.941c-0.607,-0.627 -0.91,-1.547 -0.91,-2.761l-0,-45.935c0.405,-0.566 1.112,-0.85 2.124,-0.85c1.011,0 1.82,0.314 2.427,0.941c0.607,0.627 0.91,1.547 0.91,2.761l0,1.335c2.104,-3.358 4.885,-5.037 8.344,-5.037c3.459,0 6.159,0.971 8.101,2.913c4.247,-1.375 9.001,-2.063 14.26,-2.063c1.092,-0 1.638,0.445 1.638,1.335l-0.364,0.91c-4.895,0 -9.507,0.688 -13.835,2.063Zm-8.738,20.025c3.317,-0 4.976,-3.297 4.976,-9.891c-0,-3.479 -0.284,-6.21 -0.85,-8.192c-5.34,2.913 -8.01,7.08 -8.01,12.5c0,1.78 0.385,3.156 1.153,4.127c0.769,0.971 1.679,1.456 2.731,1.456Z"
style="fill-rule:nonzero;"
/><path
d="M308.285,94.424c-0,-1.213 0.303,-2.134 0.91,-2.761c0.607,-0.627 1.375,-0.94 2.306,-0.94c0.93,-0 1.679,0.283 2.245,0.849l0,8.981l8.313,-0c0.85,-0 1.406,0.162 1.669,0.485c0.263,0.324 0.394,0.87 0.394,1.639l-10.376,-0l0,15.291c0,2.225 0.637,3.945 1.911,5.158c1.275,1.214 2.741,1.821 4.4,1.821c2.508,-0 4.349,-0.931 5.522,-2.792c1.173,-1.861 1.76,-4.874 1.76,-9.041c0.08,-0.243 0.384,-0.364 0.91,-0.364c0.728,-0 1.092,1.072 1.092,3.216c-0,3.277 -0.819,5.987 -2.458,8.131c-1.638,2.144 -4.156,3.216 -7.554,3.216c-3.398,0 -6.089,-0.89 -8.071,-2.67c-1.982,-1.78 -2.973,-4.429 -2.973,-7.949l-0,-22.27Z"
style="fill-rule:nonzero;"
/><path
d="M345.543,101.463c0.526,-1.173 1.365,-1.76 2.518,-1.76c1.153,0 2.013,0.284 2.579,0.85l-0,30.826c0.485,-0.041 1.254,-0.061 2.306,-0.061c1.052,0 1.578,0.445 1.578,1.335l-0.365,0.91c-1.537,0 -2.71,0.021 -3.519,0.061l-0,3.155c-0.041,4.167 -1.001,7.434 -2.882,9.8c-1.882,2.367 -4.38,3.55 -7.494,3.55c-1.861,0 -3.51,-0.556 -4.946,-1.669c-1.436,-1.112 -2.154,-2.862 -2.154,-5.248c-0,-3.075 1.092,-5.522 3.277,-7.343c2.184,-1.82 5.097,-3.095 8.738,-3.823l-0,-8.92c-1.699,2.792 -4.046,4.187 -7.039,4.187c-7.08,0 -10.619,-4.632 -10.619,-13.896c-0,-4.692 1.102,-8.151 3.307,-10.376c2.204,-2.225 5.127,-3.338 8.768,-3.338c2.791,0 4.774,0.587 5.947,1.76Zm-0.364,12.986l-0,-4.308c-0,-2.873 -0.486,-4.936 -1.457,-6.19c-0.971,-1.254 -2.093,-1.881 -3.367,-1.881c-1.275,0 -2.276,0.121 -3.004,0.364c-0.728,0.243 -1.436,0.748 -2.124,1.517c-1.295,1.456 -1.942,4.612 -1.942,9.466c0,3.196 0.283,5.684 0.85,7.464c0.566,1.78 1.213,2.903 1.942,3.368c0.728,0.465 1.749,0.698 3.064,0.698c1.315,-0 2.65,-0.84 4.005,-2.519c1.355,-1.678 2.033,-4.338 2.033,-7.979Zm-0,22.209l-0,-2.306c-5.947,1.335 -8.92,4.349 -8.92,9.042c-0,1.416 0.384,2.498 1.153,3.246c0.768,0.748 1.678,1.123 2.73,1.123c1.699,-0 2.963,-0.789 3.793,-2.367c0.829,-1.578 1.244,-4.49 1.244,-8.738Z"
style="fill-rule:nonzero;"
/><path
d="M381.891,113.478c0.849,-0 1.274,0.647 1.274,1.942c-0,2.993 -1.042,5.724 -3.125,8.192c-2.084,2.467 -5.249,3.701 -9.497,3.701c-4.247,0 -7.656,-1.143 -10.224,-3.428c-2.569,-2.286 -3.854,-5.644 -3.854,-10.073c0,-4.43 1.133,-7.889 3.398,-10.377c2.266,-2.488 5.158,-3.732 8.678,-3.732c2.548,0 4.409,0.678 5.582,2.033c1.174,1.355 1.76,2.964 1.76,4.824c0,3.034 -1.264,5.553 -3.792,7.555c-2.529,2.003 -5.755,3.004 -9.679,3.004c0.364,2.589 1.305,4.541 2.822,5.856c1.517,1.314 3.519,1.972 6.007,1.972c2.488,-0 4.733,-0.941 6.736,-2.822c2.002,-1.881 3.003,-4.642 3.003,-8.283c0.041,-0.243 0.344,-0.364 0.911,-0.364Zm-19.661,0.85l-0,0.546c2.508,-0.324 4.682,-1.113 6.523,-2.367c1.841,-1.254 2.761,-3.115 2.761,-5.583c0,-1.577 -0.344,-2.781 -1.031,-3.61c-0.688,-0.829 -1.659,-1.244 -2.913,-1.244c-1.901,0 -3.267,0.93 -4.096,2.791c-0.829,1.861 -1.244,5.017 -1.244,9.467Z"
style="fill-rule:nonzero;"
/><path
d="M393.359,99.703c3.601,0 6.534,1.194 8.799,3.581c2.265,2.386 3.398,5.633 3.398,9.739c0,4.106 -1.062,7.514 -3.186,10.225c-2.124,2.71 -4.965,4.065 -8.525,4.065c-3.56,0 -6.524,-1.224 -8.89,-3.671c-2.367,-2.447 -3.55,-5.846 -3.55,-10.194c0,-4.349 1.102,-7.727 3.307,-10.134c2.205,-2.407 5.087,-3.611 8.647,-3.611Zm0.061,2.367c-1.092,0 -2.033,0.273 -2.822,0.819c-0.789,0.546 -1.183,1.325 -1.183,2.336c-0,2.589 1.011,5.067 3.034,7.434c2.023,2.366 4.43,3.934 7.221,4.703c0.081,-1.538 0.121,-2.852 0.121,-3.945c0,-4.207 -0.445,-7.15 -1.335,-8.829c-0.89,-1.679 -2.568,-2.518 -5.036,-2.518Zm-6.25,11.347c-0,4.127 0.516,7.08 1.547,8.86c1.032,1.78 2.721,2.67 5.067,2.67c1.618,-0 2.862,-0.415 3.732,-1.244c0.87,-0.83 1.466,-2.175 1.79,-4.036c-2.63,-0.566 -5.047,-1.759 -7.251,-3.58c-2.205,-1.82 -3.813,-3.843 -4.825,-6.068c-0.04,0.688 -0.06,1.821 -0.06,3.398Z"
style="fill-rule:nonzero;"
/><path
d="M442.571,112.75c0.729,-0 1.093,1.072 1.093,3.216c-0,3.438 -0.739,6.189 -2.215,8.252c-1.477,2.064 -3.631,3.095 -6.463,3.095c-2.832,0 -5.006,-0.971 -6.523,-2.912c-1.517,-1.942 -2.275,-4.693 -2.275,-8.253l-0,-7.1c-0,-4.814 -1.214,-7.221 -3.641,-7.221c-2.144,0 -3.904,1.093 -5.28,3.277c-1.375,2.185 -2.063,5.846 -2.063,10.983l0,10.377c-0.404,0.566 -1.112,0.849 -2.124,0.849c-1.011,0 -1.82,-0.313 -2.427,-0.94c-0.607,-0.627 -0.91,-1.548 -0.91,-2.761l0,-23.059c0.405,-0.566 1.173,-0.85 2.306,-0.85c2.103,0 3.155,1.517 3.155,4.552c1.699,-3.035 4.39,-4.552 8.071,-4.552c2.629,0 4.723,0.83 6.28,2.488c1.558,1.659 2.337,4.046 2.337,7.161l-0,8.616c-0,2.306 0.364,4.046 1.092,5.219c0.728,1.173 1.81,1.76 3.246,1.76c1.436,-0 2.7,-0.9 3.793,-2.701c1.092,-1.8 1.638,-4.844 1.638,-9.132c0.041,-0.243 0.344,-0.364 0.91,-0.364Z"
style="fill-rule:nonzero;"
/></g
></g
></svg
>
</a> </a>
</header> </header>
@@ -87,20 +96,26 @@
margin-bottom: 2rem; margin-bottom: 2rem;
} }
img {
object-fit: contain;
}
@media screen and (max-width: 30rem) { @media screen and (max-width: 30rem) {
header { header {
margin-top: 1rem; margin-top: 1rem;
margin-bottom: 1rem; margin-bottom: 1rem;
} }
header svg { header svg,
header img {
max-height: 4rem; max-height: 4rem;
} }
} }
header svg { header svg,
header img {
width: 100%; width: 100%;
max-width: 16rem; max-height: 8rem;
transform: translateX(-1rem); transform: translateX(-1rem);
fill: currentColor; fill: currentColor;
} }

View File

@@ -1,5 +1,5 @@
<script lang="ts" context="module"> <script lang="ts" context="module">
import { init, waitLocale, getLocaleFromNavigator } from 'svelte-intl-precompile' import { getLocaleFromNavigator, init, waitLocale } from 'svelte-intl-precompile'
// @ts-ignore // @ts-ignore
import { registerAll } from '$locales' import { registerAll } from '$locales'
registerAll() registerAll()

View File

@@ -12,47 +12,67 @@
import { onMount } from 'svelte' import { onMount } from 'svelte'
import { t } from 'svelte-intl-precompile' import { t } from 'svelte-intl-precompile'
import type { NotePublic } from '$lib/api' import { Adapters } from '$lib/adapters'
import { get, info } from '$lib/api' import { get, info } from '$lib/api'
import { decrypt, getKeyFromString } from '$lib/crypto' import { Crypto } 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 Loader from '$lib/ui/Loader.svelte'
import ShowNote, { type DecryptedNote } from '$lib/ui/ShowNote.svelte'
export let id: string export let id: string
let password: string let password: string
let note: NotePublic | null = null let note: DecryptedNote | null = null
let exists = false let exists = false
let loading = true let loading: string | null = null
let error = false let error: string | null = null
onMount(async () => { onMount(async () => {
// Check if note exists
try { try {
loading = true loading = $t('common.loading')
error = false
password = window.location.hash.slice(1) password = window.location.hash.slice(1)
await info(id) await info(id)
exists = true exists = true
} catch { } catch {
exists = false exists = false
} finally { } finally {
loading = false loading = null
} }
}) })
/**
* Get the actual contents of the note and decrypt it.
*/
async function show() { async function show() {
try { try {
error = false error = null
loading = true loading = $t('common.downloading')
const data = note || (await get(id)) // Don't get the content twice on wrong password. const data = await get(id)
const key = await getKeyFromString(password) loading = $t('common.decrypting')
data.contents = await decrypt(data.contents, key) const key = await Crypto.getKeyFromString(password)
note = data switch (data.meta.type) {
case 'text':
note = {
meta: { type: 'text' },
contents: await Adapters.Text.decrypt(data.contents, key),
}
break
case 'file':
note = {
meta: { type: 'file' },
contents: await Adapters.Files.decrypt(data.contents, key),
}
break
default:
error = $t('show.errors.unsupported_type')
return
}
} catch { } catch {
error = true error = $t('show.errors.decryption_failed')
} finally { } finally {
loading = false loading = null
} }
} }
</script> </script>
@@ -70,7 +90,7 @@
{#if error} {#if error}
<br /> <br />
<p class="error-text"> <p class="error-text">
{$t('show.errors.decryption_failed')} {error}
<br /> <br />
</p> </p>
{/if} {/if}
@@ -79,5 +99,11 @@
{/if} {/if}
{/if} {/if}
{#if loading} {#if loading}
<p>{$t('common.loading')}</p> <p class="loader">{loading} <Loader /></p>
{/if} {/if}
<style>
.loader {
text-align: center;
}
</style>

View File

@@ -1,6 +1,5 @@
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' import preprocess from 'svelte-preprocess'
export default { export default {
preprocess: preprocess(), preprocess: preprocess(),
@@ -9,10 +8,5 @@ export default {
adapter: adapter({ adapter: adapter({
fallback: 'index.html', fallback: 'index.html',
}), }),
vite: {
plugins: [
precompileIntl('locales'), // if your translations are defined in /locales/[lang].json
],
},
}, },
} }

View File

@@ -1,3 +1,7 @@
{ {
"extends": "./.svelte-kit/tsconfig.json" "extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"strict": true,
"allowSyntheticDefaultImports": true
}
} }

12
frontend/vite.config.js Normal file
View File

@@ -0,0 +1,12 @@
import { sveltekit } from '@sveltejs/kit/vite'
import precompileIntl from 'svelte-intl-precompile/sveltekit-plugin'
/** @type {import('vite').UserConfig} */
const config = {
server: {
port: 3000,
},
plugins: [sveltekit(), precompileIntl('locales')],
}
export default config

View File

@@ -1,6 +1,6 @@
{ {
"scripts": { "scripts": {
"dev:docker": "docker-compose up memcached", "dev:docker": "docker-compose up redis",
"dev:backend": "cd backend && cargo watch -x 'run --bin cryptgeon'", "dev:backend": "cd backend && cargo watch -x 'run --bin cryptgeon'",
"dev:front": "pnpm --prefix frontend run dev", "dev:front": "pnpm --prefix frontend run dev",
"dev:proxy": "node proxy.mjs", "dev:proxy": "node proxy.mjs",

28
pnpm-lock.yaml generated
View File

@@ -32,7 +32,7 @@ packages:
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
dependencies: dependencies:
function-bind: 1.1.1 function-bind: 1.1.1
get-intrinsic: 1.1.1 get-intrinsic: 1.1.2
dev: true dev: true
/chalk/2.4.2: /chalk/2.4.2:
@@ -91,7 +91,7 @@ packages:
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 function.prototype.name: 1.1.5
get-intrinsic: 1.1.1 get-intrinsic: 1.1.2
get-symbol-description: 1.0.0 get-symbol-description: 1.0.0
has: 1.0.3 has: 1.0.3
has-property-descriptors: 1.0.0 has-property-descriptors: 1.0.0
@@ -158,8 +158,8 @@ packages:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
dev: true dev: true
/get-intrinsic/1.1.1: /get-intrinsic/1.1.2:
resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==} resolution: {integrity: sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==}
dependencies: dependencies:
function-bind: 1.1.1 function-bind: 1.1.1
has: 1.0.3 has: 1.0.3
@@ -171,7 +171,7 @@ packages:
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
get-intrinsic: 1.1.1 get-intrinsic: 1.1.2
dev: true dev: true
/graceful-fs/4.2.10: /graceful-fs/4.2.10:
@@ -190,7 +190,7 @@ packages:
/has-property-descriptors/1.0.0: /has-property-descriptors/1.0.0:
resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
dependencies: dependencies:
get-intrinsic: 1.1.1 get-intrinsic: 1.1.2
dev: true dev: true
/has-symbols/1.0.3: /has-symbols/1.0.3:
@@ -231,7 +231,7 @@ packages:
resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==} resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies: dependencies:
get-intrinsic: 1.1.1 get-intrinsic: 1.1.2
has: 1.0.3 has: 1.0.3
side-channel: 1.0.4 side-channel: 1.0.4
dev: true dev: true
@@ -355,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.22.0 resolve: 1.22.1
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
@@ -452,8 +452,8 @@ packages:
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
dev: true dev: true
/resolve/1.22.0: /resolve/1.22.1:
resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==} resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
hasBin: true hasBin: true
dependencies: dependencies:
is-core-module: 2.9.0 is-core-module: 2.9.0
@@ -467,14 +467,14 @@ packages:
dev: true dev: true
/shebang-command/1.2.0: /shebang-command/1.2.0:
resolution: {integrity: sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=} resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dependencies: dependencies:
shebang-regex: 1.0.0 shebang-regex: 1.0.0
dev: true dev: true
/shebang-regex/1.0.0: /shebang-regex/1.0.0:
resolution: {integrity: sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=} resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: true dev: true
@@ -486,7 +486,7 @@ packages:
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
get-intrinsic: 1.1.1 get-intrinsic: 1.1.2
object-inspect: 1.12.2 object-inspect: 1.12.2
dev: true dev: true
@@ -538,7 +538,7 @@ packages:
dev: true dev: true
/strip-bom/3.0.0: /strip-bom/3.0.0:
resolution: {integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=} resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
engines: {node: '>=4'} engines: {node: '>=4'}
dev: true dev: true