1 extern crate lemmy_server;
3 extern crate diesel_migrations;
6 use actix_files::NamedFile;
8 use actix_web::web::Json;
9 use actix_web_actors::ws;
10 use lemmy_server::db::establish_connection;
11 use lemmy_server::websocket::server::*;
13 use std::time::{Duration, Instant};
18 /// How often heartbeat pings are sent
19 const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
20 /// How long before lack of client response causes a timeout
21 const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
23 /// Entry point for our route
27 chat_server: web::Data<Addr<ChatServer>>,
28 ) -> Result<HttpResponse, Error> {
31 cs_addr: chat_server.get_ref().to_owned(),
37 .unwrap_or("127.0.0.1:12345")
40 .unwrap_or("127.0.0.1")
49 cs_addr: Addr<ChatServer>,
53 /// Client must send ping at least once per 10 seconds (CLIENT_TIMEOUT),
54 /// otherwise we drop connection.
58 impl Actor for WSSession {
59 type Context = ws::WebsocketContext<Self>;
61 /// Method is called on actor start.
62 /// We register ws session with ChatServer
63 fn started(&mut self, ctx: &mut Self::Context) {
64 // we'll start heartbeat process on session start.
67 // register self in chat server. `AsyncContext::wait` register
68 // future within context, but context waits until this future resolves
69 // before processing any other events.
70 // across all routes within application
71 let addr = ctx.address();
75 addr: addr.recipient(),
76 ip: self.ip.to_owned(),
79 .then(|res, act, ctx| {
81 Ok(res) => act.id = res,
82 // something is wrong with chat server
90 fn stopping(&mut self, _ctx: &mut Self::Context) -> Running {
92 self.cs_addr.do_send(Disconnect {
94 ip: self.ip.to_owned(),
100 /// Handle messages from chat server, we simply send it to peer websocket
101 /// These are room messages, IE sent to others in the room
102 impl Handler<WSMessage> for WSSession {
105 fn handle(&mut self, msg: WSMessage, ctx: &mut Self::Context) {
106 // println!("id: {} msg: {}", self.id, msg.0);
111 /// WebSocket message handler
112 impl StreamHandler<ws::Message, ws::ProtocolError> for WSSession {
113 fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
114 // println!("WEBSOCKET MESSAGE: {:?} from id: {}", msg, self.id);
116 ws::Message::Ping(msg) => {
117 self.hb = Instant::now();
120 ws::Message::Pong(_) => {
121 self.hb = Instant::now();
123 ws::Message::Text(text) => {
124 let m = text.trim().to_owned();
125 println!("WEBSOCKET MESSAGE: {:?} from id: {}", &m, self.id);
129 .send(StandardMessage {
134 .then(|res, _, ctx| {
136 Ok(res) => ctx.text(res),
145 ws::Message::Binary(_bin) => println!("Unexpected binary"),
146 ws::Message::Close(_) => {
155 /// helper method that sends ping to client every second.
157 /// also this method checks heartbeats from client
158 fn hb(&self, ctx: &mut ws::WebsocketContext<Self>) {
159 ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
160 // check client heartbeats
161 if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
162 // heartbeat timed out
163 println!("Websocket Client heartbeat failed, disconnecting!");
165 // notify chat server
166 act.cs_addr.do_send(Disconnect {
168 ip: act.ip.to_owned(),
174 // don't try to send a ping
184 let _ = env_logger::init();
185 let sys = actix::System::new("lemmy");
187 // Run the migrations from code
188 let conn = establish_connection();
189 embedded_migrations::run(&conn).unwrap();
191 // Start chat server actor in separate thread
192 let server = ChatServer::default().start();
193 // Create Http server with websocket support
194 HttpServer::new(move || {
196 .data(server.clone())
197 .service(web::resource("/api/v1/ws").to(chat_route))
198 // .service(web::resource("/api/v1/rest").route(web::post().to(||{})))
199 .service(web::resource("/").to(index))
201 .service(actix_files::Files::new("/static", front_end_dir()))
202 .route("/nodeinfo/2.0.json", web::get().to(node_info))
204 .bind("0.0.0.0:8536")
208 println!("Started http server: 0.0.0.0:8536");
234 protocols: [String; 0],
236 openRegistrations: bool,
239 fn node_info() -> Result<Json<NodeInfo>> {
240 // TODO: get info from database
241 // TODO: need to get lemmy version from somewhere else
242 let conn = establish_connection();
243 let userCount = User_::count(conn)
244 let json = Json(NodeInfo {
245 version: "2.0".to_string(),
247 name: "lemmy".to_string(),
248 version: "0.1".to_string()
250 protocols: [], // TODO: activitypub once that is implemented
258 openRegistrations: true });
262 fn index() -> Result<NamedFile, actix_web::error::Error> {
263 Ok(NamedFile::open(front_end_dir() + "/index.html")?)
266 fn front_end_dir() -> String {
267 env::var("LEMMY_FRONT_END_DIR").unwrap_or("../ui/dist".to_string())