use actix::prelude::*; use actix_files::Files; use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer, Responder}; use actix_web_actors::ws; use log::info; use serde::{Deserialize, Serialize}; use std::{fs, time::Duration}; use chrono::Local; mod landing; #[derive(Serialize)] enum DownMsg { Landing(landing::DownMsg), } #[derive(Deserialize)] enum UpMsg { Landing(landing::UpMsg), } /// WebSocket actor struct MyWebSocket; impl Actor for MyWebSocket { type Context = ws::WebsocketContext; fn started(&mut self, ctx: &mut Self::Context) { info!("WebSocket actor started"); ctx.run_interval(Duration::from_secs(1), |_, ctx| { let current_time = Local::now().format("%H:%M:%S").to_string(); let message = DownMsg::Landing(landing::DownMsg::TimeUpdate(current_time)); if let Ok(json_message) = serde_json::to_string(&message) { ctx.text(json_message); } }); } } impl StreamHandler> for MyWebSocket { fn handle( &mut self, msg: Result, ctx: &mut ws::WebsocketContext, ) { match msg { Ok(ws::Message::Text(text)) => { // Attempt to decode the incoming message if let Ok(up_msg) = serde_json::from_str::(&text) { match up_msg { UpMsg::Landing(landing::UpMsg::RequestGreet(name)) => { let greeting = format!("Hello, {}!", name); let response = DownMsg::Landing(landing::DownMsg::Greeting(greeting)); if let Ok(json_response) = serde_json::to_string(&response) { ctx.text(json_response); } } } } } Ok(ws::Message::Ping(msg)) => { ctx.pong(&msg); } _ => {} } } } async fn websocket_handler(req: HttpRequest, stream: web::Payload) -> Result { ws::start(MyWebSocket {}, &req, stream) } #[actix_web::main] async fn main() -> std::io::Result<()> { env_logger::init(); let address = "127.0.0.1"; let port = std::env::var("EXAMPLE_ELM_APP_PORT") .unwrap_or("8080".to_string()) .parse() .unwrap(); info!("Starting server at http://{}:{}", address, port); HttpServer::new(|| { App::new() .route("/ws/", web::get().to(websocket_handler)) // WebSocket endpoint .service(Files::new("/assets", "./public/assets")) .service(Files::new("/", "./public").index_file("index.html")) // Serve frontend .default_service(web::route().to(|| async { let index_html = fs::read_to_string("./public/index.html") .unwrap_or_else(|_| "404 Not Found".to_string()); HttpResponse::Ok() .content_type("text/html; charset=utf-8") .body(index_html) })) }) .bind((address, port))? .run() .await }