package Web; import Other.SomeCodes; import com.corundumstudio.socketio.Configuration; import com.corundumstudio.socketio.SocketIOClient; import com.corundumstudio.socketio.SocketIOServer; import com.corundumstudio.socketio.listener.ConnectListener; import com.corundumstudio.socketio.listener.DisconnectListener; import io.javalin.Javalin; import io.javalin.http.UploadedFile; import io.javalin.util.FileUtil; import io.javalin.util.JavalinException; import io.javalin.websocket.*; import lombok.Getter; import lombok.Setter; import org.tinylog.Logger; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import static Other.SomeCodes.*; import static io.javalin.apibuilder.ApiBuilder.*; @SuppressWarnings({"unused"}) public class WebServer { private @Getter @Setter String webusername; private @Getter @Setter String webpassword; private final Javalin app; private final Set connectedWebsocketClients = ConcurrentHashMap.newKeySet(); private final SocketIOServer socketServer; private final Configuration socketConfig; private final HashMap socketIOClients = new HashMap<>(); public WebServer(WebsocketEvent event, String webusername, String webpassword){ this.webusername = webusername; this.webpassword = webpassword; app = Javalin.create(config -> { config.staticFiles.add("/html"); config.router.apiBuilder(()-> path("setting", () ->{ get(ctx -> ctx.json(SettingInfo.getInstance())); path("audiofile",()-> post(ctx -> { Logger.info("api /setting/audiofile"); String audiofile1 = ctx.formParam("preset1"); String audiofile2 = ctx.formParam("preset2"); String audiofile3 = ctx.formParam("preset3"); String audiofile4 = ctx.formParam("preset4"); String audiofile5 = ctx.formParam("preset5"); Logger.info("audiofile1: {}", audiofile1); Logger.info("audiofile2: {}", audiofile2); Logger.info("audiofile3: {}", audiofile3); Logger.info("audiofile4: {}", audiofile4); Logger.info("audiofile5: {}", audiofile5); Properties prop = SomeCodes.LoadProperties("config.properties"); prop.setProperty("AudioFile01", audiofile1!=null?audiofile1:""); prop.setProperty("AudioFile02", audiofile2!=null?audiofile2:""); prop.setProperty("AudioFile03", audiofile3!=null?audiofile3:""); prop.setProperty("AudioFile04", audiofile4!=null?audiofile4:""); prop.setProperty("AudioFile05", audiofile5!=null?audiofile5:""); if (SaveProperties(prop, "config.properties")){ Logger.info("audiofile saved"); ctx.status(200); } else { Logger.error("Failed to save audiofile"); ctx.status(400); ctx.result("Failed to save audiofile"); } })); path("uploadaudiofile", ()-> post(ctx -> { List uploadedFileList = ctx.uploadedFiles(); int size = uploadedFileList.size(); if (size>0){ uploadedFileList.forEach(ff ->{ String targetsave = audioPath.resolve(ff.filename()).toString(); FileUtil.streamToFile(ff.content(), targetsave); Logger.info("Uploaded file: {}", targetsave); }); ctx.status(200); ctx.result("UploadedFiles: "+size); } else { ctx.status(400); ctx.result("No file uploaded"); } })); path("weblogin", ()-> post(ctx -> { String username = ctx.formParam("username"); String password = ctx.formParam("password"); Logger.info("api /setting/weblogin"); Logger.info("username: {}", username); Logger.info("password: {}", password); Properties prop = SomeCodes.LoadProperties("config.properties"); prop.setProperty("WebUsername", ValidString(username)?username:"admin"); prop.setProperty("WebPassword", ValidString(password)?password:"bandara"); if (SaveProperties(prop, "config.properties")){ Logger.info("weblogin saved"); //ctx.status(200); this.webusername = username; this.webpassword = password; ctx.redirect("/logout"); } else { Logger.error("Failed to save weblogin"); ctx.status(400); ctx.result("Failed to save weblogin"); } })); path("camera",()-> post(ctx -> { String camera_ip = ctx.formParam("ip"); String camera_port = ctx.formParam("port"); String camera_username = ctx.formParam("username"); String camera_password = ctx.formParam("password"); Logger.info("api /setting/camera"); Logger.info("camera_ip: {}", camera_ip); Logger.info("camera_port: {}", camera_port); Logger.info("camera_username: {}", camera_username); Logger.info("camera_password: {}", camera_password); Properties prop = SomeCodes.LoadProperties("config.properties"); prop.setProperty("Camera_ip", ValidString(camera_ip)?camera_ip:"192.168.0.4"); prop.setProperty("Camera_port", ValidString(camera_port)?camera_port:"80"); prop.setProperty("Camera_user", ValidString(camera_username)?camera_username:"root"); prop.setProperty("Camera_password", ValidString(camera_password)?camera_password:"password"); if (SaveProperties(prop, "config.properties")){ Logger.info("IP camera setting saved"); ctx.status(200); if (event!=null) event.NewCameraConfiguration(); } else { Logger.error("Failed to save IP camera setting"); ctx.status(400); ctx.result("Failed to save IP camera setting"); } })); })); }); app.get("/", ctx-> { if (ctx.sessionAttribute("username")==null) { // belum login ctx.redirect("/login.html"); } else if (Objects.equals(ctx.sessionAttribute("username"), this.webusername)){ // sudah login ctx.redirect("/index.html"); } else { // sudah login tapi bukan username yang benar ctx.redirect("/login.html"); } }); app.before("/index.html", ctx ->{ if (ctx.sessionAttribute("username")==null){ ctx.redirect("/login.html"); } }); app.before("/setting.html", ctx ->{ if (ctx.sessionAttribute("username")==null){ ctx.redirect("/login.html"); } }); app.post("/login", ctx ->{ String username = ctx.formParam("username"); String password = ctx.formParam("password"); if (Objects.equals(username, this.webusername) && Objects.equals(password, this.webpassword)){ ctx.sessionAttribute("username", username); ctx.redirect("/index.html"); } else { ctx.status(400); ctx.redirect("/login.html"); } }); app.get("/logout", ctx ->{ ctx.sessionAttribute("username", null); ctx.redirect("/login.html"); }); /* WebSocket Communication This is temporary, later must choose either WebSocket or SocketIO */ app.ws("/ws", ws -> { ws.onConnect(connectws); ws.onClose(closews); ws.onMessage(ctx -> { try{ //Logger.info("WebSocket message {}", ctx.message()); WebsocketCommand command = ctx.messageAsClass(WebsocketCommand.class); if (event!=null) { WebsocketReply reply = event.onWebsocketCommand(command); if (reply!=null) ctx.sendAsClass(reply, WebsocketReply.class); //if (reply!=null) SendtoAll(reply); } } catch (Exception e){ Logger.error("Failed to parse WebSocketCommand message: {}", e.getMessage()); ctx.closeSession(); connectedWebsocketClients.remove(ctx); } }); }); /* SocketIO Communication This is temporary, later must choose either WebSocket or SocketIO */ socketConfig = new Configuration(); socketConfig.setHostname("0.0.0.0"); socketConfig.setPort(3001); socketServer = new SocketIOServer(socketConfig); socketServer.addConnectListener(connectListener); socketServer.addDisconnectListener(disconnectListener); socketServer.addNamespace("/socketio").addEventListener("message", String.class, (client, data, ackSender) -> { //Logger.info("SocketIO message from namespace /socketio from {}: {}", client.getRemoteAddress(), data); WebsocketCommand command = gson.fromJson(data, WebsocketCommand.class); if (event!=null) { WebsocketReply reply = event.onWebsocketCommand(command); if (reply!=null) client.sendEvent("message", gson.toJson(reply)); } }); } /** * Send Object message to all connected websocket clients * @param obj Object to send */ public void SendtoAll(Object obj){ connectedWebsocketClients .stream() .filter(ws -> ws.session.isOpen()) .forEach(ws -> ws.send(obj)); socketIOClients.forEach((key, client) -> client.sendEvent("message", gson.toJson(obj))); } /** * Start the web server * @param localip Local IP address to bind * @param port Port to bind */ public void Start(String localip, int port){ try{ app.start(localip, port); Logger.info("Web server started at {}:{}", localip, port); socketServer.start(); Logger.info("SocketIO server started at {}:{}", socketConfig.getHostname(), socketConfig.getPort()); } catch (JavalinException e){ Logger.error("Web server failed to start: {}", e.getMessage()); } } /** * Stop the web server */ public void Stop(){ try{ connectedWebsocketClients.forEach(WsContext::closeSession); connectedWebsocketClients.clear(); app.stop(); Logger.info("Web server stopped"); socketIOClients.forEach((key, client) -> client.disconnect()); socketIOClients.clear(); socketServer.stop(); Logger.info("SocketIO server stopped"); } catch (JavalinException e){ Logger.error("Web server failed to stop: {}", e.getMessage()); } } WsConnectHandler connectws = ws ->{ Logger.info("WebSocket connected from {}", ws.sessionId()); //ws.headerMap().forEach((key, value) -> Logger.info("HeaderMap {}: {}", key, value)); connectedWebsocketClients.add(ws); }; WsCloseHandler closews = ws ->{ Logger.info("WebSocket closed from {}, code {}, reason {}", ws.sessionId(), ws.status(), ws.reason()); connectedWebsocketClients.remove(ws); }; ConnectListener connectListener = client -> { Logger.info("SocketIO connected, id {} from {}", client.getSessionId(), client.getRemoteAddress()); socketIOClients.put(client.getSessionId().toString(), client); }; DisconnectListener disconnectListener = client -> { Logger.info("SocketIO disconnected, id {} from {}", client.getSessionId(), client.getRemoteAddress()); socketIOClients.remove(client.getSessionId().toString()); }; }