mirror of
https://github.com/cupcakearmy/cryptgeon.git
synced 2025-09-06 17:30:39 +00:00
Compare commits
10 Commits
v2.8.3
...
567a0bed68
Author | SHA1 | Date | |
---|---|---|---|
567a0bed68 | |||
63c16a797b | |||
ea50590532 | |||
b22c3122d7 | |||
|
18af2b2f45 | ||
c2b557246b | |||
df9cd08473 | |||
|
0b8e1d1b2e | ||
70481341b9 | |||
6271ec1ee9 |
@@ -63,6 +63,8 @@ client side with the <code>key</code> and then sent to the server. data is store
|
|||||||
never persisted to disk. the server never sees the encryption key and cannot decrypt the contents
|
never persisted to disk. the server never sees the encryption key and cannot decrypt the contents
|
||||||
of the notes even if it tried to.
|
of the notes even if it tried to.
|
||||||
|
|
||||||
|
> View counts are guaranteed with one running instance of cryptgeon. Multiple instances connected to the same Redis instance can run into race conditions, where a note might be retrieved more than the view count allows.
|
||||||
|
|
||||||
## Screenshot
|
## Screenshot
|
||||||
|
|
||||||

|

|
||||||
|
@@ -2,7 +2,7 @@ services:
|
|||||||
redis:
|
redis:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
# Set a size limit. See link below on how to customise.
|
# Set a size limit. See link below on how to customise.
|
||||||
# https://redis.io/docs/manual/eviction/
|
# https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy/
|
||||||
# command: redis-server --maxmemory 1gb --maxmemory-policy allkeys-lru
|
# command: redis-server --maxmemory 1gb --maxmemory-policy allkeys-lru
|
||||||
|
|
||||||
app:
|
app:
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Assumptions:
|
Assumptions:
|
||||||
|
|
||||||
- Traefik 2 installed.
|
- Traefik 2/3 installed.
|
||||||
- External proxy docker network `proxy`.
|
- External proxy docker network `proxy`.
|
||||||
- A certificate resolver `le`.
|
- A certificate resolver `le`.
|
||||||
- A https entrypoint `secure`.
|
- A https entrypoint `secure`.
|
||||||
@@ -34,3 +34,43 @@ services:
|
|||||||
- traefik.http.routers.cryptgeon.entrypoints=secure
|
- traefik.http.routers.cryptgeon.entrypoints=secure
|
||||||
- traefik.http.routers.cryptgeon.tls.certresolver=le
|
- traefik.http.routers.cryptgeon.tls.certresolver=le
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## With basic auth
|
||||||
|
|
||||||
|
Some times it's useful to hide the service behind auth. This is easily achieved with traefik middleware. Many reverse proxies support similar features, so while traefik is used in this example, other reverse proxies can do the same.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
traefik:
|
||||||
|
image: traefik:v3.0
|
||||||
|
command:
|
||||||
|
- "--api.insecure=true"
|
||||||
|
- "--providers.docker=true"
|
||||||
|
- "--providers.docker.exposedbydefault=false"
|
||||||
|
- "--entrypoints.web.address=:80"
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
volumes:
|
||||||
|
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
|
||||||
|
cryptgeon:
|
||||||
|
image: cupcakearmy/cryptgeon
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.cryptgeon.rule=Host(`cryptgeon.localhost`)"
|
||||||
|
- "traefik.http.routers.cryptgeon.entrypoints=web"
|
||||||
|
- "traefik.http.routers.cryptgeon.middlewares=cryptgeon-auth"
|
||||||
|
- "traefik.http.middlewares.cryptgeon-auth.basicauth.users=user:$$2y$$05$$juUw0zgc5ebvJ00MFPVVLujF6P.rcEMbGZ99Jfq6ZWEa1dgetacEq"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Open http://cryptgeon.localhost
|
||||||
|
2. Log in with `user` and `secret`
|
||||||
|
651
packages/backend/Cargo.lock
generated
651
packages/backend/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cryptgeon"
|
name = "cryptgeon"
|
||||||
version = "2.8.3"
|
version = "2.8.4"
|
||||||
authors = ["cupcakearmy <hi@nicco.io>"]
|
authors = ["cupcakearmy <hi@nicco.io>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.80"
|
rust-version = "1.80"
|
||||||
|
10
packages/backend/src/lock.rs
Normal file
10
packages/backend/src/lock.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SharedState {
|
||||||
|
pub locks: LockMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type LockMap = Arc<Mutex<HashMap<String, Arc<Mutex<()>>>>>;
|
@@ -1,9 +1,13 @@
|
|||||||
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{DefaultBodyLimit, Request},
|
extract::{DefaultBodyLimit, Request},
|
||||||
routing::{delete, get, post},
|
routing::{delete, get, post},
|
||||||
Router, ServiceExt,
|
Router, ServiceExt,
|
||||||
};
|
};
|
||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
|
use lock::SharedState;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
use tower::Layer;
|
use tower::Layer;
|
||||||
use tower_http::{
|
use tower_http::{
|
||||||
compression::CompressionLayer,
|
compression::CompressionLayer,
|
||||||
@@ -16,6 +20,7 @@ extern crate lazy_static;
|
|||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod health;
|
mod health;
|
||||||
|
mod lock;
|
||||||
mod note;
|
mod note;
|
||||||
mod status;
|
mod status;
|
||||||
mod store;
|
mod store;
|
||||||
@@ -24,9 +29,13 @@ mod store;
|
|||||||
async fn main() {
|
async fn main() {
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
|
|
||||||
|
let shared_state = SharedState {
|
||||||
|
locks: Arc::new(Mutex::new(HashMap::new())),
|
||||||
|
};
|
||||||
|
|
||||||
if !store::can_reach_redis() {
|
if !store::can_reach_redis() {
|
||||||
println!("cannot reach redis");
|
println!("cannot reach redis");
|
||||||
panic!("canont reach redis");
|
panic!("cannot reach redis");
|
||||||
}
|
}
|
||||||
|
|
||||||
let notes_routes = Router::new()
|
let notes_routes = Router::new()
|
||||||
@@ -53,7 +62,8 @@ async fn main() {
|
|||||||
.deflate(true)
|
.deflate(true)
|
||||||
.gzip(true)
|
.gzip(true)
|
||||||
.zstd(true),
|
.zstd(true),
|
||||||
);
|
)
|
||||||
|
.with_state(shared_state);
|
||||||
|
|
||||||
let app = NormalizePathLayer::trim_trailing_slash().layer(app);
|
let app = NormalizePathLayer::trim_trailing_slash().layer(app);
|
||||||
|
|
||||||
|
@@ -5,11 +5,12 @@ use axum::{
|
|||||||
Json,
|
Json,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::time::SystemTime;
|
use std::{sync::Arc, time::SystemTime};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::config;
|
|
||||||
use crate::note::{generate_id, Note, NoteInfo};
|
use crate::note::{generate_id, Note, NoteInfo};
|
||||||
use crate::store;
|
use crate::store;
|
||||||
|
use crate::{config, lock::SharedState};
|
||||||
|
|
||||||
use super::NotePublic;
|
use super::NotePublic;
|
||||||
|
|
||||||
@@ -80,11 +81,20 @@ pub async fn create(Json(mut n): Json<Note>) -> Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete(Path(OneNoteParams { id }): Path<OneNoteParams>) -> Response {
|
pub async fn delete(
|
||||||
|
Path(OneNoteParams { id }): Path<OneNoteParams>,
|
||||||
|
state: axum::extract::State<SharedState>,
|
||||||
|
) -> Response {
|
||||||
|
let mut locks_map = state.locks.lock().await;
|
||||||
|
let lock = locks_map
|
||||||
|
.entry(id.clone())
|
||||||
|
.or_insert_with(|| Arc::new(Mutex::new(())))
|
||||||
|
.clone();
|
||||||
|
drop(locks_map);
|
||||||
|
let _guard = lock.lock().await;
|
||||||
|
|
||||||
let note = store::get(&id);
|
let note = store::get(&id);
|
||||||
match note {
|
match note {
|
||||||
// Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
|
|
||||||
// Ok(None) => return HttpResponse::NotFound().finish(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||||
Ok(None) => (StatusCode::NOT_FOUND).into_response(),
|
Ok(None) => (StatusCode::NOT_FOUND).into_response(),
|
||||||
Ok(Some(note)) => {
|
Ok(Some(note)) => {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cryptgeon",
|
"name": "cryptgeon",
|
||||||
"version": "2.8.3",
|
"version": "2.8.4",
|
||||||
"homepage": "https://github.com/cupcakearmy/cryptgeon",
|
"homepage": "https://github.com/cupcakearmy/cryptgeon",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
58
packages/frontend/locales/zh-TW.json
Normal file
58
packages/frontend/locales/zh-TW.json
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"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": "複製到剪貼板",
|
||||||
|
"copied_to_clipboard": "已複製到剪貼板",
|
||||||
|
"encrypting": "加密中",
|
||||||
|
"decrypting": "解密中",
|
||||||
|
"uploading": "上傳中",
|
||||||
|
"downloading": "下載中",
|
||||||
|
"qr_code": "QR 碼",
|
||||||
|
"password": "密碼"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"intro": "輕鬆地以一鍵傳送<i>完全加密</i>的安全筆記或檔案。只需創建筆記並分享連結。",
|
||||||
|
"explanation": "筆記將在 {type} 後過期並被銷毀。",
|
||||||
|
"new_note": "新筆記",
|
||||||
|
"new_note_notice": "<b>可用性:</b><br />筆記不保證被儲存,因為所有內容都保留在 RAM 中,如果 RAM 填滿,最舊的筆記將被移除。<br />(您可能會沒事,只是提醒一下。)",
|
||||||
|
"errors": {
|
||||||
|
"note_to_big": "無法創建筆記。筆記過大",
|
||||||
|
"note_error": "無法創建筆記。請再試一次。",
|
||||||
|
"max": "最大值:{n}",
|
||||||
|
"empty_content": "筆記內容為空。"
|
||||||
|
},
|
||||||
|
"messages": {
|
||||||
|
"note_created": "筆記已創建。"
|
||||||
|
},
|
||||||
|
"advanced": {
|
||||||
|
"explanation": "預設情況下,每個筆記都會使用安全生成的密碼。您也可以選擇自己的密碼,該密碼不會包含在連結中。",
|
||||||
|
"custom_password": "自定義密碼"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"show": {
|
||||||
|
"errors": {
|
||||||
|
"not_found": "筆記未找到或已被刪除。",
|
||||||
|
"decryption_failed": "密碼錯誤。無法解密。可能是連結已損壞。筆記已被銷毀。",
|
||||||
|
"unsupported_type": "不支持的筆記類型。"
|
||||||
|
},
|
||||||
|
"explanation": "如果計數器達到限制,請點擊下方以顯示並刪除筆記",
|
||||||
|
"show_note": "顯示筆記",
|
||||||
|
"warning_will_not_see_again": "您將<b>無法</b>再次查看筆記。",
|
||||||
|
"download_all": "全部下載",
|
||||||
|
"links_found": "在筆記中找到的連結:"
|
||||||
|
},
|
||||||
|
"file_upload": {
|
||||||
|
"selected_files": "已選擇的檔案",
|
||||||
|
"no_files_selected": "未選擇檔案",
|
||||||
|
"clear": "重置"
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user