mirror of
https://github.com/cupcakearmy/cryptgeon.git
synced 2026-09-26 20:41:45 +00:00
Compare commits
16
Commits
b58bf8af6d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6ea6376e1 | ||
|
|
19e7899309 | ||
|
|
9e69730bfd | ||
|
|
b5e1c4b42a | ||
|
|
7660dd7c30 | ||
|
|
685784c360 | ||
|
|
cc2c45221b | ||
|
|
b113d253a9 | ||
|
|
c99c62cf00 | ||
|
|
7ef162bd83 | ||
|
|
282fdb97d1 | ||
|
|
4dfed492bc | ||
|
|
5084ed59f0 | ||
|
|
055a48a38d | ||
|
|
d1126ace82 | ||
|
|
8034b1a501 |
+6
-2
@@ -11,15 +11,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
- New shared TypeScript package `@cryptgeon/shared` as single source of truth for crypto, content codec and API client (crypto + compression + payload + types).
|
- New shared TypeScript package `@cryptgeon/shared` as single source of truth for crypto, content codec and API client (crypto + compression + payload + types).
|
||||||
- Shared payload codec: `packContent` / `unpackContent` (encode → LZ4 → XChaCha20-Poly1305 and reverse).
|
- Shared payload codec: `packContent` / `unpackContent` (encode → LZ4 → XChaCha20-Poly1305 and reverse).
|
||||||
- New `pg`-backend storage of note hashes in the cache.
|
- Cache-backed note storage using hashes (valkey/redis) with atomic view counting.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Encryption from AES to **XChaCha20-Poly1305** (client-side); dropped `occulto`.
|
- Encryption from AES to **XChaCha20-Poly1305** (client-side); dropped `occulto`.
|
||||||
- All API bodies switched to **MessagePack**.
|
- All API bodies switched to **MessagePack**.
|
||||||
- Frontend migrated to SvelteKit + `@cryptgeon/shared`.
|
- Frontend uses `@cryptgeon/shared` for crypto + payload codec (replacing the previous local `cryptgeon/shared`); heavy pack/unpack runs in a web worker.
|
||||||
- CLI rebuilt with `vite-plus` (bundles all deps) and imports from `@cryptgeon/shared`.
|
- CLI rebuilt with `vite-plus` (bundles all deps) and imports from `@cryptgeon/shared`.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- SPA fallback now serves the app (200) for client-side routes such as `/about` and `/note/<id>`, while unmatched `/api/*` paths still return 404.
|
||||||
|
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
|
|
||||||
- Endpoints moved to `/api/v3/notes/` and `/api/v3/status`; health check to `/healthz`.
|
- Endpoints moved to `/api/v3/notes/` and `/api/v3/status`; health check to `/healthz`.
|
||||||
|
|||||||
@@ -169,6 +169,10 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|||||||
|
|
||||||
Please refer to the security section [here](./SECURITY.md).
|
Please refer to the security section [here](./SECURITY.md).
|
||||||
|
|
||||||
|
## Usage of LLMs
|
||||||
|
|
||||||
|
Starting from V3, I used LLMs heavily to implement _my own ideas_. This means that the direction and architecture choices are human. A lot of the implementation that derives from that, is automated with an LLM.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
_Attributions_
|
_Attributions_
|
||||||
|
|||||||
@@ -151,6 +151,10 @@ Ver [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|||||||
|
|
||||||
Por favor dirígete a la sección de seguridad [aquí](./SECURITY.md).
|
Por favor dirígete a la sección de seguridad [aquí](./SECURITY.md).
|
||||||
|
|
||||||
|
## Uso de LLMs
|
||||||
|
|
||||||
|
A partir de la V3, utilicé LLMs de forma intensiva para implementar _mis propias ideas_. Esto significa que la dirección y las decisiones de arquitectura son humanas. Gran parte de la implementación que deriva de eso está automatizada con un LLM.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
_Atribuciones_
|
_Atribuciones_
|
||||||
|
|||||||
@@ -149,6 +149,10 @@ services:
|
|||||||
|
|
||||||
参见 [CONTRIBUTING.md](./CONTRIBUTING.md)。
|
参见 [CONTRIBUTING.md](./CONTRIBUTING.md)。
|
||||||
|
|
||||||
|
## LLM 的使用
|
||||||
|
|
||||||
|
从 V3 开始,我大量使用 LLM 来实现_我自己的想法_。这意味着项目的方向和架构选择均由人类决定。由此衍生的大部分实现由 LLM 自动完成。
|
||||||
|
|
||||||
###### Attributions
|
###### Attributions
|
||||||
|
|
||||||
- 测试数据:
|
- 测试数据:
|
||||||
|
|||||||
+10
-12
@@ -1,17 +1,17 @@
|
|||||||
services:
|
services:
|
||||||
cache:
|
cache:
|
||||||
image: valkey/valkey:7-alpine
|
image: valkey/valkey:9-alpine
|
||||||
# This is required to stay in RAM only.
|
# This is required to stay in RAM only.
|
||||||
command: valkey-server --save "" --appendonly no
|
command: valkey-server --save "" --appendonly no
|
||||||
# Set a size limit. See link below on how to customise.
|
# Set a size limit. See link below on how to customise.
|
||||||
# https://valkey.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy/
|
# https://valkey.io/topics/lru-cache/
|
||||||
# --maxmemory 1gb --maxmemory-policy allkeys-lrulpine
|
# --maxmemory 1gb --maxmemory-policy allkeys-lru
|
||||||
# This prevents the creation of an anonymous volume.
|
# This prevents the creation of an anonymous volume.
|
||||||
tmpfs:
|
tmpfs:
|
||||||
- /data
|
- /data
|
||||||
|
|
||||||
app:
|
app:
|
||||||
image: cupcakearmy/cryptgeon:v3
|
image: cupcakearmy/cryptgeon:3
|
||||||
depends_on:
|
depends_on:
|
||||||
- cache
|
- cache
|
||||||
environment:
|
environment:
|
||||||
@@ -19,11 +19,9 @@ services:
|
|||||||
SIZE_LIMIT: 4 MiB
|
SIZE_LIMIT: 4 MiB
|
||||||
ports:
|
ports:
|
||||||
- 80:8000
|
- 80:8000
|
||||||
|
healthcheck:
|
||||||
# Optional health checks
|
test: ["CMD", "curl", "--fail", "http://127.0.0.1:8000/healthz"]
|
||||||
# healthcheck:
|
interval: 1m
|
||||||
# test: ["CMD", "curl", "--fail", "http://127.0.0.1:8000/healthz"]
|
timeout: 3s
|
||||||
# interval: 1m
|
retries: 2
|
||||||
# timeout: 3s
|
start_period: 5s
|
||||||
# retries: 2
|
|
||||||
# start_period: 5s
|
|
||||||
|
|||||||
Generated
+1
-1
@@ -252,7 +252,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cryptgeon"
|
name = "cryptgeon"
|
||||||
version = "3.0.0-rc.3"
|
version = "3.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"bs62",
|
"bs62",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cryptgeon"
|
name = "cryptgeon"
|
||||||
version = "3.0.0-rc.3"
|
version = "3.0.0"
|
||||||
authors = ["cupcakearmy <hi@nicco.io>"]
|
authors = ["cupcakearmy <hi@nicco.io>"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.95"
|
rust-version = "1.95"
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ mod note;
|
|||||||
mod status;
|
mod status;
|
||||||
mod store;
|
mod store;
|
||||||
|
|
||||||
|
async fn api_not_found() -> axum::http::StatusCode {
|
||||||
|
axum::http::StatusCode::NOT_FOUND
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
@@ -39,11 +43,13 @@ async fn main() {
|
|||||||
.nest("/notes", notes_routes)
|
.nest("/notes", notes_routes)
|
||||||
.merge(status_routes);
|
.merge(status_routes);
|
||||||
|
|
||||||
let api_routes = Router::new().nest("/v3", v3_routes);
|
let api_routes = Router::new()
|
||||||
|
.nest("/v3", v3_routes)
|
||||||
|
.fallback(api_not_found);
|
||||||
|
|
||||||
let index = format!("{}{}", config::FRONTEND_PATH.to_string(), "/index.html");
|
let index = format!("{}{}", config::FRONTEND_PATH.to_string(), "/index.html");
|
||||||
let serve_dir =
|
let serve_dir =
|
||||||
ServeDir::new(config::FRONTEND_PATH.to_string()).not_found_service(ServeFile::new(index));
|
ServeDir::new(config::FRONTEND_PATH.to_string()).fallback(ServeFile::new(index));
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.nest("/api", api_routes)
|
.nest("/api", api_routes)
|
||||||
.merge(health_routes)
|
.merge(health_routes)
|
||||||
@@ -66,4 +72,4 @@ async fn main() {
|
|||||||
axum::serve(listener, ServiceExt::<Request>::into_make_service(app))
|
axum::serve(listener, ServiceExt::<Request>::into_make_service(app))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cryptgeon",
|
"name": "cryptgeon",
|
||||||
"version": "3.0.0-rc.3",
|
"version": "3.0.0",
|
||||||
"homepage": "https://github.com/cupcakearmy/cryptgeon",
|
"homepage": "https://github.com/cupcakearmy/cryptgeon",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@@ -46,11 +46,11 @@ async function downloadFile(file: FileDTO) {
|
|||||||
files = note.contents
|
files = note.contents
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
let download = $derived(() => {
|
function downloadAll() {
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
downloadFile(file)
|
downloadFile(file)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
let links = $derived(typeof note.contents === 'string' ? note.contents.match(RE_URL) : [])
|
let links = $derived(typeof note.contents === 'string' ? note.contents.match(RE_URL) : [])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ async function downloadFile(file: FileDTO) {
|
|||||||
{/key}
|
{/key}
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
<Button onclick={download}>{$t('show.download_all')}</Button>
|
<Button onclick={downloadAll}>{$t('show.download_all')}</Button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,7 @@ const contract: WorkerConract = {
|
|||||||
return transfer(content, [content.data.buffer, content.extra.buffer, content.key.buffer])
|
return transfer(content, [content.data.buffer, content.extra.buffer, content.key.buffer])
|
||||||
},
|
},
|
||||||
unpack(data, key) {
|
unpack(data, key) {
|
||||||
const content = unpackContent(data, key)
|
return unpackContent(data, key)
|
||||||
return transfer(
|
|
||||||
content,
|
|
||||||
typeof content.data === 'string' ? [] : content.data.map((f) => f.data.buffer)
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
|
test.describe("@web", () => {
|
||||||
|
for (const path of ["/", "/about", "/note/does-not-exist"]) {
|
||||||
|
test(`serves ${path} with status 200`, async ({ request }) => {
|
||||||
|
const response = await request.get(path);
|
||||||
|
expect(response.status()).toBe(200);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test("api still reports a missing note as 404", async ({ request }) => {
|
||||||
|
const response = await request.get("/api/notes/does-not-exist");
|
||||||
|
expect(response.status()).toBe(404);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user