S5: WebSocket real-time — per-channel broadcast, auto-reconnect
Backend: - AppState with per-channel broadcast::Sender map - WS handler: auth via first message, keepalive pings, broadcast forwarding - post_message broadcasts WsEvent::Message to all subscribers Frontend: - useChannelSocket hook: connects, auths, appends messages, auto-reconnects - Removed 3s polling — WebSocket is primary, initial load via REST - Deduplication on WS messages (sender also fetches after post) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
mod db;
|
||||
mod routes;
|
||||
mod state;
|
||||
mod ws;
|
||||
|
||||
use axum::{routing::get, Router};
|
||||
use sqlx::sqlite::SqlitePoolOptions;
|
||||
use state::AppState;
|
||||
use std::env;
|
||||
use tower_http::services::{ServeDir, ServeFile};
|
||||
|
||||
@@ -19,18 +22,18 @@ async fn main() {
|
||||
|
||||
eprintln!("colony: connected to {}", db_url);
|
||||
|
||||
// Enable WAL mode
|
||||
sqlx::query("PRAGMA journal_mode=WAL")
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Run embedded migrations
|
||||
sqlx::migrate!("./migrations")
|
||||
.run(&pool)
|
||||
.await
|
||||
.expect("Failed to run migrations");
|
||||
|
||||
let state = AppState::new(pool);
|
||||
|
||||
eprintln!("colony: migrations done, starting on port {}", port);
|
||||
|
||||
let app = Router::new()
|
||||
@@ -46,11 +49,11 @@ async fn main() {
|
||||
"/api/channels/{channel_id}/messages",
|
||||
get(routes::list_messages).post(routes::post_message),
|
||||
)
|
||||
// Serve frontend static files, fallback to index.html for SPA routing
|
||||
.route("/ws/{channel_id}", get(ws::ws_handler))
|
||||
.fallback_service(
|
||||
ServeDir::new("static").fallback(ServeFile::new("static/index.html")),
|
||||
)
|
||||
.with_state(pool);
|
||||
.with_state(state);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", port))
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user