From 9c6cd2c2310d2e3ce3d1a6e3350a97e7ba0ca657 Mon Sep 17 00:00:00 2001 From: RblSb Date: Mon, 24 Feb 2020 07:58:56 +0300 Subject: Start working on user folder --- src/server/HttpServer.hx | 79 +++++++++++++++++++++++++++++++++--------------- src/server/Main.hx | 66 ++++++++++++++++++++++++++-------------- src/server/Utils.hx | 11 +++++++ 3 files changed, 109 insertions(+), 47 deletions(-) (limited to 'src/server') diff --git a/src/server/HttpServer.hx b/src/server/HttpServer.hx index b49301b..a5b752b 100644 --- a/src/server/HttpServer.hx +++ b/src/server/HttpServer.hx @@ -1,12 +1,11 @@ package server; +import sys.FileSystem; import js.node.Buffer; import haxe.io.Path; import js.node.Fs; -import sys.io.File; import js.node.http.IncomingMessage; import js.node.http.ServerResponse; -import js.Node.__dirname; import js.node.Path as JsPath; using StringTools; @@ -33,18 +32,31 @@ class HttpServer { ]; static var dir:String; + static var customDir:String; + static var hasCustomRes = false; + static var allowedLocalFiles:Map = []; - public static function init(directory:String):Void { - dir = directory; + public static function init(dir:String, ?customDir:String):Void { + HttpServer.dir = dir; + if (customDir == null) return; + HttpServer.customDir = customDir; + hasCustomRes = FileSystem.exists(customDir); } public static function serveFiles(req:IncomingMessage, res:ServerResponse):Void { - var filePath = dir + req.url; - if (req.url == "/") filePath = '$dir/index.html'; + var url = req.url; + if (url == "/") url = "/index.html"; + var filePath = dir + url; final extension = Path.extension(filePath).toLowerCase(); final contentType = getMimeType(extension); + if (req.connection.remoteAddress == req.connection.localAddress + || allowedLocalFiles[url]) { + final isExists = serveLocalFile(res, url, extension, contentType); + if (isExists) return; + } + if (!isChildOf(dir, filePath)) { res.statusCode = 500; var rel = JsPath.relative(dir, filePath); @@ -52,21 +64,14 @@ class HttpServer { return; } - // load client code from build folder - if (filePath == '$dir/client.js') { - filePath = '$__dirname/client.js'; + if (hasCustomRes) { + final path = customDir + url; + if (Fs.existsSync(path)) filePath = path; } - Fs.readFile(filePath, function(err:Dynamic, data:Buffer) { + Fs.readFile(filePath, (err:Dynamic, data:Buffer) -> { if (err != null) { - if (err.code == "ENOENT") { - res.statusCode = 404; - var rel = JsPath.relative(dir, filePath); - res.end('File $rel not found.'); - } else { - res.statusCode = 500; - res.end('Error getting the file: $err.'); - } + readFileError(err, res, filePath); return; } res.setHeader("Content-Type", contentType); @@ -78,13 +83,40 @@ class HttpServer { }); } + static function readFileError(err:Dynamic, res:ServerResponse, filePath:String):Void { + if (err.code == "ENOENT") { + res.statusCode = 404; + var rel = JsPath.relative(dir, filePath); + res.end('File $rel not found.'); + } else { + res.statusCode = 500; + res.end('Error getting the file: $err.'); + } + } + + static function serveLocalFile(res:ServerResponse, filePath:String, ext:String, contentType:String):Bool { + if (ext != "mp4" && ext != "mp3" && ext != "wav") return false; + if (!Fs.existsSync(filePath)) return false; + allowedLocalFiles[filePath] = true; + Fs.readFile(filePath, (err:Dynamic, data:Buffer) -> { + if (err != null) { + readFileError(err, res, filePath); + return; + } + res.setHeader("Content-Type", contentType); + res.end(data); + }); + return true; + } + static final matchLang = ~/^[A-z]+/; + static final matchVarString = ~/\${([A-z_]+)}/g; static function localizeHtml(data:String, lang:String):String { if (lang != null && matchLang.match(lang)) { lang = matchLang.matched(0); } else lang = "en"; - data = ~/\${([A-z_]+)}/g.map(data, (regExp) -> { + data = matchVarString.map(data, (regExp) -> { final key = regExp.matched(1); return Lang.get(lang, key); }); @@ -92,14 +124,13 @@ class HttpServer { } static function isChildOf(parent:String, child:String):Bool { - final path = JsPath; - final relative = path.relative(parent, child); - return relative.length > 0 && !relative.startsWith('..') && !path.isAbsolute(relative); + final rel = JsPath.relative(parent, child); + return rel.length > 0 && !rel.startsWith('..') && !JsPath.isAbsolute(rel); } static function getMimeType(ext:String):String { - var contentType = mimeTypes[ext]; - if (contentType == null) contentType = "application/octet-stream"; + final contentType = mimeTypes[ext]; + if (contentType == null) return "application/octet-stream"; return contentType; } diff --git a/src/server/Main.hx b/src/server/Main.hx index e2b9b18..442abc8 100644 --- a/src/server/Main.hx +++ b/src/server/Main.hx @@ -10,8 +10,10 @@ import js.Node.process; import js.Node.__dirname; import js.npm.ws.Server as WSServer; import js.npm.ws.WebSocket; +import js.node.http.IncomingMessage; import js.node.Http; import Types; +using StringTools; using ClientTools; using Lambda; @@ -19,6 +21,9 @@ class Main { final rootDir = '$__dirname/..'; final wss:WSServer; + final localIp:String; + var globalIp:String; + final port:Int; final config:Config; final clients:Array = []; final freeIds:Array = []; @@ -33,25 +38,31 @@ class Main { wss = new WSServer({port: wsPort}); wss.on("connection", onConnect); function exit() { + // TODO save state process.exit(); } process.on("exit", exit); process.on("SIGINT", exit); // ctrl+c + process.on("SIGUSR1", exit); // kill pid + process.on("SIGUSR2", exit); process.on("uncaughtException", (log) -> { trace(log); }); process.on("unhandledRejection", (reason, promise) -> { trace("Unhandled Rejection at:", reason); }); + localIp = Utils.getLocalIp(); + globalIp = localIp; + this.port = port; Utils.getGlobalIp(ip -> { - final local = Utils.getLocalIp(); - trace('Local: http://$local:$port'); - trace('Global: http://$ip:$port'); + globalIp = ip; + trace('Local: http://$localIp:$port'); + trace('Global: http://$globalIp:$port'); }); final dir = '$rootDir/res'; - HttpServer.init(dir); + HttpServer.init(dir, '$rootDir/user/res'); Lang.init('$dir/langs'); Http.createServer((req, res) -> { @@ -61,7 +72,7 @@ class Main { function getUserConfig():Config { final config:Config = Json.parse(File.getContent('$rootDir/default-config.json')); - final customPath = '$rootDir/config.json'; + final customPath = '$rootDir/user/config.json'; if (!FileSystem.exists(customPath)) return config; final customConfig:Config = Json.parse(File.getContent(customPath)); for (field in Reflect.fields(customConfig)) { @@ -71,13 +82,13 @@ class Main { return config; } - function onConnect(ws:WebSocket, req):Void { + function onConnect(ws:WebSocket, req:IncomingMessage):Void { final ip = req.connection.remoteAddress; final id = freeIds.length > 0 ? freeIds.shift() : clients.length; final name = 'Guest ${id + 1}'; trace('$name connected ($ip)'); final isAdmin = req.connection.localAddress == ip; - final client = new Client(ws, id, name, 0); + final client = new Client(ws, req, id, name, 0); if (isAdmin) client.group.set(Admin); clients.push(client); if (clients.length == 1 && videoList.length > 0) @@ -93,7 +104,8 @@ class Main { clients: [ for (client in clients) client.getData() ], - videoList: videoList + videoList: videoList, + globalIp: globalIp } }); sendClientList(); @@ -103,7 +115,7 @@ class Main { }); ws.on("close", err -> { trace('Client ${client.name} disconnected'); - sortedPush(freeIds, client.id); + Utils.sortedPush(freeIds, client.id); clients.remove(client); sendClientList(); if (client.isLeader) { @@ -116,17 +128,6 @@ class Main { }); } - function sortedPush(ids:Array, id:Int):Void { - for (i in 0...ids.length) { - final n = ids[i]; - if (id < n) { - ids.insert(i, id); - return; - } - } - ids.push(id); - } - function onMessage(client:Client, data:WsEvent):Void { switch (data.type) { case Connected: @@ -149,6 +150,7 @@ class Main { } }); sendClientList(); + case LoginError: case Logout: final oldName = client.name; @@ -163,6 +165,7 @@ class Main { } }); sendClientList(); + case Message: var text = data.message.text; if (text.length == 0) return; @@ -175,15 +178,23 @@ class Main { messages.push({text: text, name: client.name, time: time}); if (messages.length > config.serverChatHistory) messages.shift(); broadcast(data); + case AddVideo: - if (data.addVideo.atEnd) videoList.push(data.addVideo.item); - else videoList.insert(1, data.addVideo.item); + final item = data.addVideo.item; + final localOrigin = '$localIp:$port'; + if (item.url.indexOf(localOrigin) != -1) { + item.url = item.url.replace(localOrigin, '$globalIp:$port'); + } + if (data.addVideo.atEnd) videoList.push(item); + else videoList.insert(1, item); broadcast(data); // Initial timer start if VideoLoaded is not happen if (videoList.length == 1) restartWaitTimer(); + case VideoLoaded: // Called if client loads next video and can play it prepareVideoPlayback(); + case RemoveVideo: if (videoList.length == 0) return; final url = data.removeVideo.url; @@ -193,16 +204,19 @@ class Main { ); broadcast(data); if (videoList.length > 0) restartWaitTimer(); + case Pause: if (videoList.length == 0) return; if (!client.isLeader) return; videoTimer.pause(); broadcast(data); + case Play: if (videoList.length == 0) return; if (!client.isLeader) return; videoTimer.play(); broadcast(data); + case GetTime: if (videoList.length == 0) return; if (videoTimer.getTime() > videoList[0].duration) { @@ -220,11 +234,13 @@ class Main { time: videoTimer.getTime(), paused: videoTimer.isPaused() }}); + case SetTime: if (videoList.length == 0) return; if (!client.isLeader) return; videoTimer.setTime(data.setTime.time); broadcastExcept(client, data); + case Rewind: if (videoList.length == 0) return; // TODO permission @@ -232,6 +248,7 @@ class Main { if (data.rewind.time < 0) data.rewind.time = 0; videoTimer.setTime(data.rewind.time); broadcast(data); + case SetLeader: clients.setLeader(data.setLeader.clientName); broadcast({ @@ -248,12 +265,15 @@ class Main { } }); } + case ClearChat: if (client.isAdmin) broadcast(data); + case ClearPlaylist: videoTimer.stop(); videoList.resize(0); broadcast(data); + case ShufflePlaylist: if (videoList.length == 0) return; final first = videoList.shift(); @@ -262,7 +282,7 @@ class Main { broadcast({type: UpdatePlaylist, updatePlaylist: { videoList: videoList }}); - case UpdatePlaylist: + case UpdatePlaylist: // client-only } } diff --git a/src/server/Utils.hx b/src/server/Utils.hx index 2ecbd42..22ddc77 100644 --- a/src/server/Utils.hx +++ b/src/server/Utils.hx @@ -28,6 +28,17 @@ class Utils { return "127.0.0.1"; } + public static function sortedPush(ids:Array, id:Int):Void { + for (i in 0...ids.length) { + final n = ids[i]; + if (id < n) { + ids.insert(i, id); + return; + } + } + ids.push(id); + } + public static function shuffle(arr:Array):Void { for (i in 0...arr.length) { final n = Std.random(arr.length); -- cgit v1.2.3