Server (kez-chat/src/) - push.rs: VAPID (PEM/PKCS#8) auto-generated on first run; StoredSubscription store table; PushSender using IsahcWebPushClient; fanout drops 410/404 subs automatically. Push payload carries metadata only ({type,to,seq}) — never plaintext or ciphertext. - api.rs: GET /v1/push/vapid-public-key, POST /v1/push/subscribe/:handle, POST /v1/push/unsubscribe/:handle. Auth via X-KEZ-Auth: <ts>:<sig>, canonical message binds the endpoint URL so headers can't be replayed against other subs. - messages.rs: after broker.publish, fire-and-forget push.fanout for offline recipients. - config.rs: --vapid-key-path, --vapid-subject (env-backed). - main.rs: load_or_generate_vapid on startup. Web client (kez-chat/web/src/) - vite.config.ts: switched vite-plugin-pwa to injectManifest mode. - sw.ts: custom service worker with workbox precache, NetworkOnly for /v1/*, NavigationRoute SPA fallback, push + notificationclick handlers (focus existing tab via postMessage, or open a new one). - lib/push.ts: enablePush / disablePush / isPushSubscribed + iOS PWA-install detection. - routes/Settings.svelte: "Background notifications (Web Push)" section with toggle and iOS Add-to-Home-Screen nudge. - main.ts: bridge from SW navigate message to svelte-spa-router via location.hash. Chat UX (routes/Messages.svelte) - Bubbles now shrink-wrap to content with WhatsApp-style asymmetric corners and inline bottom-right timestamps. Old layout used nested block-level divs inside max-w-[78%], which stretched every bubble to full width regardless of content. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
34 lines
1.1 KiB
TOML
34 lines
1.1 KiB
TOML
[package]
|
|
name = "kez-chat-server"
|
|
version = "0.1.0"
|
|
edition = "2024"
|
|
license = "MIT OR Apache-2.0"
|
|
description = "Home server for kez-chat: handle registry + NATS auth callout + WebFinger + static SPA host. Designed in kez-chat/document.md."
|
|
|
|
[dependencies]
|
|
anyhow = "1"
|
|
axum = "0.7"
|
|
chrono = { version = "0.4", features = ["serde"] }
|
|
clap = { version = "4.5", features = ["derive", "env"] }
|
|
hex = "0.4"
|
|
kez-core = { path = "../rust/crates/kez-core" }
|
|
rusqlite = { version = "0.32", features = ["bundled"] }
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = "1"
|
|
thiserror = "2"
|
|
tokio = { version = "1.48", features = ["macros", "rt-multi-thread", "sync", "signal"] }
|
|
tokio-stream = { version = "0.1", features = ["sync"] }
|
|
futures = "0.3"
|
|
web-push = "0.10"
|
|
base64 = "0.22"
|
|
p256 = { version = "0.13", features = ["pem"] }
|
|
rand = "0.8"
|
|
tower-http = { version = "0.6", features = ["trace", "cors", "fs"] }
|
|
tracing = "0.1"
|
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|
|
|
[dev-dependencies]
|
|
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] }
|
|
sha2 = "0.10"
|
|
tempfile = "3"
|