From 9839c0bddab8f6af41bbd81334c968aa158ffbbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Rynning-T=C3=B8nnesen?= Date: Thu, 1 Mar 2018 14:55:23 +0100 Subject: [PATCH] Timeout api, and fixes it lead to - timeout on api-calls with Retry-After present in header - Fixed issue where when server restarts you are sometimes logged out for some reason - README updates --- README.md | 7 + server/handlers/db.js | 2 +- server/handlers/io.js | 12 - server/handlers/list.js | 16 +- server/handlers/list_settings.js | 1 - server/public/assets/js/admin.js | 2 +- server/public/assets/js/channel.js | 4 +- server/public/assets/js/crypt.js | 9 +- server/public/assets/js/functions.js | 12 +- server/public/assets/js/hostcontroller.js | 2 +- server/public/assets/js/list.js | 4 +- server/public/assets/js/listeners.js | 9 +- server/public/assets/js/player.js | 8 +- server/public/assets/js/search.js | 2 +- server/routing/client/api.js | 288 +++++++++++++--------- 15 files changed, 225 insertions(+), 153 deletions(-) diff --git a/README.md b/README.md index 096b1501..e2c98ebd 100755 --- a/README.md +++ b/README.md @@ -28,6 +28,13 @@ If you want to use Google Analytics, have a look at ```analytics.example.js``` i If you have run the server before the table-structures where added, please run ```node server/apps/rewrite.js```. This will fix any crashes that occurs because of faulty document-collectionnames due to moving channel-settings to a separate collection. +Run +``` +db.chat_logs.createIndex({ "createdAt": 1 }, { expireAfterSeconds: X }); +db.timeout_api.createIndex({ "createdAt": 1 }, { expireAfterSeconds: Y }); +``` +in mongo to have chat_logs and api be deleted after X and Y seconds. + Use ```$ npm start``` to start the server. (Alternative you can use the ```pm2.json``` in the project-root, if you prefer pm2 for running the apps.) ### About diff --git a/server/handlers/db.js b/server/handlers/db.js index 8fe2479d..04bd3a63 100644 --- a/server/handlers/db.js +++ b/server/handlers/db.js @@ -10,7 +10,7 @@ var mongojs = require('mongojs'); var db = mongojs('mongodb://' + mongo_config.host + '/' + mongo_config.config); db.collection("chat_logs").createIndex({ "createdAt": 1 }, { expireAfterSeconds: 600 }); - +db.collection("timeout_api").createIndex({ "createdAt": 1 }, { expireAfterSeconds: 5 }); db.on('connected', function(err) { console.log("connected"); }) diff --git a/server/handlers/io.js b/server/handlers/io.js index 25983ddc..4aa70710 100644 --- a/server/handlers/io.js +++ b/server/handlers/io.js @@ -282,18 +282,6 @@ module.exports = function() { socket.on('skip', function(list) { - if(coll !== undefined) { - try { - coll = list.channel; - if(coll.length == 0) return; - coll = emojiStrip(coll).toLowerCase(); - coll = coll.replace("_", ""); - coll = encodeURIComponent(coll).replace(/\W/g, ''); - coll = filter.clean(coll); - } catch(e) { - return; - } - } List.skip(list, guid, coll, offline, socket); }); diff --git a/server/handlers/list.js b/server/handlers/list.js index 37f3c7ba..f31fd5b6 100644 --- a/server/handlers/list.js +++ b/server/handlers/list.js @@ -77,10 +77,18 @@ function skip(list, guid, coll, offline, socket) { if(list !== undefined && list !== null && list !== "") { - - if(coll == "" || coll == undefined || coll == null) { - socket.emit("update_required"); - return; + if(coll == undefined && list.hasOwnProperty('channel')) coll = list.channel; + if(coll !== undefined) { + try { + coll = list.channel; + if(coll.length == 0) return; + coll = emojiStrip(coll).toLowerCase(); + coll = coll.replace("_", ""); + coll = encodeURIComponent(coll).replace(/\W/g, ''); + coll = filter.clean(coll); + } catch(e) { + return; + } } if(typeof(list.pass) != "string" || typeof(list.id) != "string" || typeof(list.channel) != "string" || typeof(list.userpass) != "string") { diff --git a/server/handlers/list_settings.js b/server/handlers/list_settings.js index d7a3d5d9..fda38195 100644 --- a/server/handlers/list_settings.js +++ b/server/handlers/list_settings.js @@ -22,7 +22,6 @@ function password(inp, coll, guid, offline, socket) { uncrypted = pw; pw = Functions.decrypt_string(socket.zoff_id, pw); Functions.check_inlist(coll, guid, socket, offline); - if(inp.oldpass) { opw = inp.oldpass; diff --git a/server/public/assets/js/admin.js b/server/public/assets/js/admin.js index d817bcd7..bf662f50 100755 --- a/server/public/assets/js/admin.js +++ b/server/public/assets/js/admin.js @@ -367,7 +367,7 @@ var Admin = { shuffle: function() { if(!offline) { - emit('shuffle', {adminpass: adminpass !== undefined ? Crypt.crypt_pass(adminpass) : "", channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + emit('shuffle', {adminpass: adminpass !== undefined ? Crypt.crypt_pass(adminpass) : "", channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); } else { for(var x = 0; x < full_playlist.length; x++){ var num = Math.floor(Math.random()*1000000); diff --git a/server/public/assets/js/channel.js b/server/public/assets/js/channel.js index 5816f025..cb0ddec4 100644 --- a/server/public/assets/js/channel.js +++ b/server/public/assets/js/channel.js @@ -135,7 +135,7 @@ var Channel = { if(no_socket || Helper.mobilecheck()){ var add = ""; if(private_channel) add = Crypt.getCookie("_uI") + "_"; - socket.emit("list", {version: parseInt(localStorage.getItem("VERSION")), channel: add + chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + socket.emit("list", {version: parseInt(localStorage.getItem("VERSION")), channel: add + chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); } if((!localStorage.getItem("_jSeen") || localStorage.getItem("_jSeen") != "seen") && !Helper.mobilecheck()) { @@ -478,7 +478,7 @@ var Channel = { var add = ""; w_p = true; if(private_channel) add = Crypt.getCookie("_uI") + "_"; - socket.emit("list", {version: parseInt(localStorage.getItem("VERSION")), channel: add + chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + socket.emit("list", {version: parseInt(localStorage.getItem("VERSION")), channel: add + chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); } else if(url_split[3] === "") { clearTimeout(timed_remove_check); changing_to_frontpage = true; diff --git a/server/public/assets/js/crypt.js b/server/public/assets/js/crypt.js index ab2271dc..fa0ebe32 100755 --- a/server/public/assets/js/crypt.js +++ b/server/public/assets/js/crypt.js @@ -2,6 +2,7 @@ var Crypt = { conf_pass: undefined, user_pass: undefined, + tmp_pass_user: "", tmp_pass: "", init: function() { @@ -216,8 +217,12 @@ var Crypt = { return encrypted.toString() + "$" + iv; }, - crypt_pass: function(pass) { - Crypt.tmp_pass = pass; + crypt_pass: function(pass, userpass) { + if(userpass) { + Crypt.tmp_pass_user = pass; + } else { + Crypt.tmp_pass = pass; + } return Crypt.crypt_chat_pass(pass); }, diff --git a/server/public/assets/js/functions.js b/server/public/assets/js/functions.js index 6f90fc2f..a93e6e2b 100644 --- a/server/public/assets/js/functions.js +++ b/server/public/assets/js/functions.js @@ -97,7 +97,7 @@ function hide_native(way) { $("#chromecast_text").html(""); $("#playing_on").css("display", "none"); if(!offline){ - socket.emit('pos', {channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + socket.emit('pos', {channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); } else { Player.loadVideoById(video_id); } @@ -111,14 +111,14 @@ function chromecastListener(evt, data) { if(offline){ Player.playNext(); } else { - socket.emit("end", {id: json_parsed.videoId, channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + socket.emit("end", {id: json_parsed.videoId, channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); } break; case 0: if(offline){ Player.playNext(); } else { - emit("skip", {error: json_parsed.data_code, id: json_parsed.videoId, pass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), channel: chan.toLowerCase(), userpass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + emit("skip", {error: json_parsed.data_code, id: json_parsed.videoId, pass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), channel: chan.toLowerCase(), userpass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); } break; case 1: @@ -183,7 +183,7 @@ function get_list_listener(){ socket.on("get_list", function(){ var add = ""; if(private_channel) add = Crypt.getCookie("_uI") + "_"; - socket.emit("list", { offline: offline, version: parseInt(localStorage.getItem("VERSION")), channel: add + chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + socket.emit("list", { offline: offline, version: parseInt(localStorage.getItem("VERSION")), channel: add + chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); }); } @@ -361,10 +361,10 @@ function change_offline(enabled, already_offline){ $("#controls").off("click", Channel.seekToClick); $("#seekToDuration").remove(); if(window.location.pathname != "/"){ - socket.emit("pos", {channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + socket.emit("pos", {channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); var add = ""; if(private_channel) add = Crypt.getCookie("_uI") + "_"; - socket.emit("list", {version: parseInt(localStorage.getItem("VERSION")), channel: add + chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + socket.emit("list", {version: parseInt(localStorage.getItem("VERSION")), channel: add + chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); if($("#controls").hasClass("ewresize")) $("#controls").removeClass("ewresize"); } } diff --git a/server/public/assets/js/hostcontroller.js b/server/public/assets/js/hostcontroller.js index abdc334f..efa323f8 100755 --- a/server/public/assets/js/hostcontroller.js +++ b/server/public/assets/js/hostcontroller.js @@ -47,7 +47,7 @@ var Hostcontroller = { w_p = true; var add = ""; if(private_channel) add = Crypt.getCookie("_uI") + "_"; - socket.emit("list", {version: parseInt(localStorage.getItem("VERSION")), channel: add + chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + socket.emit("list", {version: parseInt(localStorage.getItem("VERSION")), channel: add + chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); window.history.pushState("object or string", "Title", "/"+chan.toLowerCase()); } else if(arr.type == "pause") { diff --git a/server/public/assets/js/list.js b/server/public/assets/js/list.js index f2543b06..7421b6f3 100755 --- a/server/public/assets/js/list.js +++ b/server/public/assets/js/list.js @@ -535,7 +535,7 @@ var List = { vote: function(id, vote) { if(!offline || (vote == "del" && (hasadmin && (!w_p && adminpass != "")))){ - emit('vote', {channel: chan, id: id, type: vote, adminpass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + emit('vote', {channel: chan, id: id, type: vote, adminpass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); } else { if(vote == "pos"){ List.voted_song(id, (new Date()).getTime()/1000); @@ -548,7 +548,7 @@ var List = { skip: function(way) { if(!offline){ - emit('skip', {pass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), id:video_id, channel: chan.toLowerCase(), userpass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + emit('skip', {pass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), id:video_id, channel: chan.toLowerCase(), userpass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); } else { if(way) { Player.playNext(); diff --git a/server/public/assets/js/listeners.js b/server/public/assets/js/listeners.js index cee78390..dd28349d 100755 --- a/server/public/assets/js/listeners.js +++ b/server/public/assets/js/listeners.js @@ -258,7 +258,7 @@ initializeCastApi = function() { if(Helper.mobilecheck() && !chromecast_specs_sent) { chromecast_specs_sent = true; - castSession.sendMessage("urn:x-cast:zoff.me", {type: "mobilespecs", guid: guid, socketid: socket.id, adminpass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), channel: chan.toLowerCase(), userpass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}) + castSession.sendMessage("urn:x-cast:zoff.me", {type: "mobilespecs", guid: guid, socketid: socket.id, adminpass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), channel: chan.toLowerCase(), userpass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}) } hide_native(1); if(Helper.mobilecheck()) { @@ -371,7 +371,7 @@ $(document).on("click", ".pagination-results a", function(e) { $(document).on("click", ".accept-delete", function(e) { e.preventDefault(); - emit("delete_all", {channel: chan.toLowerCase(), adminpass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + emit("delete_all", {channel: chan.toLowerCase(), adminpass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); $("#delete_song_alert").modal("close"); }); @@ -464,13 +464,13 @@ $(document).on("click", "#offline-mode", function(e){ $(document).on("submit", "#thumbnail_form", function(e){ e.preventDefault(); - emit("suggest_thumbnail", {channel: chan, thumbnail: $("#chan_thumbnail").val(), adminpass: Crypt.crypt_pass(Crypt.get_pass(chan.toLowerCase())), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + emit("suggest_thumbnail", {channel: chan, thumbnail: $("#chan_thumbnail").val(), adminpass: Crypt.crypt_pass(Crypt.get_pass(chan.toLowerCase())), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); $("#chan_thumbnail").val(""); }); $(document).on("submit", "#description_form", function(e){ e.preventDefault(); - emit("suggest_description", {channel: chan, description: $("#chan_description").val(), adminpass: Crypt.crypt_pass(Crypt.get_pass(chan.toLowerCase())), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + emit("suggest_description", {channel: chan, description: $("#chan_description").val(), adminpass: Crypt.crypt_pass(Crypt.get_pass(chan.toLowerCase())), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); $("#chan_description").val(""); }); @@ -561,6 +561,7 @@ $(document).on("submit", "#user-password-channel-form", function(e) { e.preventDefault(); if(user_auth_started) { temp_user_pass = CryptoJS.SHA256($("#user-pass-input").val()).toString(); + $("#user-pass-input").val(""); socket.emit("list", {version: parseInt(localStorage.getItem("VERSION")), channel: chan.toLowerCase(), pass: Crypt.crypt_pass(temp_user_pass)}); } else { diff --git a/server/public/assets/js/player.js b/server/public/assets/js/player.js index 6635cc34..b1f6591d 100755 --- a/server/public/assets/js/player.js +++ b/server/public/assets/js/player.js @@ -231,7 +231,7 @@ var Player = { paused = false; if(!offline) { - socket.emit("end", {id: video_id, channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + socket.emit("end", {id: video_id, channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); } else { Player.playNext(); } @@ -261,7 +261,7 @@ var Player = { $("#pause").toggleClass("hide"); } if((paused && !offline) || was_stopped) { - socket.emit('pos', {channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + socket.emit('pos', {channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); paused = false; was_stopped = false; } @@ -491,7 +491,7 @@ var Player = { if(!user_auth_started) { if(newState.data == 5 || newState.data == 100 || newState.data == 101 || newState.data == 150) { curr_playing = Player.player.getVideoUrl().replace("https://www.youtube.com/watch?v=", ""); - emit("skip", {error: newState.data, id: video_id, pass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), channel: chan.toLowerCase(), userpass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + emit("skip", {error: newState.data, id: video_id, pass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), channel: chan.toLowerCase(), userpass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); } else if(video_id !== undefined) { Player.loadVideoById(video_id, duration); @@ -680,7 +680,7 @@ var Player = { Player.player.pauseVideo(); if(!offline) { - socket.emit("end", {id: video_id, channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + socket.emit("end", {id: video_id, channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); } else { Player.playNext(); } diff --git a/server/public/assets/js/search.js b/server/public/assets/js/search.js index 6467b216..b2ad9666 100755 --- a/server/public/assets/js/search.js +++ b/server/public/assets/js/search.js @@ -449,7 +449,7 @@ var Search = { List.vote(id, "pos"); } } else { - emit("add", {id: id, start: start, end: end, title: title, adminpass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), list: chan.toLowerCase(), duration: duration, playlist: playlist, num: num, total: full_num, pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()))}); + emit("add", {id: id, start: start, end: end, title: title, adminpass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), list: chan.toLowerCase(), duration: duration, playlist: playlist, num: num, total: full_num, pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)}); }//[id, decodeURIComponent(title), adminpass, duration, playlist]); }, diff --git a/server/routing/client/api.js b/server/routing/client/api.js index f9cbeeab..a3310f9f 100644 --- a/server/routing/client/api.js +++ b/server/routing/client/api.js @@ -147,52 +147,73 @@ router.route('/api/conf/:channel_name').put(function(req, res) { res.sendStatus(400); return; } - - validateLogin(adminpass, userpass, channel_name, "config", res, function(exists) { - if(!exists) { - res.sendStatus(404); + db.collection("timeout_api").find({ + type: "DELETE", + guid: guid, + }, function(err, docs) { + if(docs.length > 0) { + var date = new Date(docs[0].createdAt); + date.setSeconds(date.getSeconds() + 5); + var now = new Date(); + var retry_in = (date.getTime() - now.getTime()) / 1000; + res.header({'Retry-After': retry_in}); + res.sendStatus(429); return; } + validateLogin(adminpass, userpass, channel_name, "config", res, function(exists) { + if(!exists) { + res.sendStatus(404); + return; + } - if((!userpass_changed && frontpage) || (userpass_changed && userpass == "")) { - userpass = ""; - } else if(userpass_changed && userpass != "") { - frontpage = false; - } - var description = ""; + if((!userpass_changed && frontpage) || (userpass_changed && userpass == "")) { + userpass = ""; + } else if(userpass_changed && userpass != "") { + frontpage = false; + } + var description = ""; - var obj = { - addsongs:addsongs, - allvideos:allvideos, - frontpage:frontpage, - skip:skipping, - vote:voting, - removeplay:removeplay, - shuffle:shuffling, - longsongs:longsongs, - adminpass:adminpass, - desc: description, - }; - if(userpass_changed) { - obj["userpass"] = userpass; - } else if (frontpage) { - obj["userpass"] = ""; - } - db.collection(channel_name + "_settings").update({views:{$exists:true}}, { - $set:obj - }, function(err, docs){ + var obj = { + addsongs:addsongs, + allvideos:allvideos, + frontpage:frontpage, + skip:skipping, + vote:voting, + removeplay:removeplay, + shuffle:shuffling, + longsongs:longsongs, + adminpass:adminpass, + desc: description, + }; + if(userpass_changed) { + obj["userpass"] = userpass; + } else if (frontpage) { + obj["userpass"] = ""; + } + db.collection(channel_name + "_settings").update({views:{$exists:true}}, { + $set:obj + }, function(err, docs){ - if(obj.adminpass !== "") obj.adminpass = true; - if(obj.hasOwnProperty("userpass") && obj.userpass != "") obj.userpass = true; - else obj.userpass = false; - io.to(channel_name).emit("conf", [obj]); + if(obj.adminpass !== "") obj.adminpass = true; + if(obj.hasOwnProperty("userpass") && obj.userpass != "") obj.userpass = true; + else obj.userpass = false; + io.to(channel_name).emit("conf", [obj]); - db.collection("frontpage_lists").update({_id: channel_name}, {$set:{ - frontpage:frontpage, accessed: Functions.get_time()} - }, - {upsert:true}, function(err, docs){ - res.header({'Content-Type': 'application/json'}); - res.status(200).send(JSON.stringify(obj)); + db.collection("frontpage_lists").update({_id: channel_name}, {$set:{ + frontpage:frontpage, accessed: Functions.get_time()} + }, + {upsert:true}, function(err, docs){ + db.collection("timeout_api").update({type: "DELETE", guid: guid}, { + $set: { + "createdAt": new Date(), + type: "DELETE", + guid: guid, + }, + }, {upsert: true}, function(err, docs) { + res.header({'Content-Type': 'application/json'}); + res.status(200).send(JSON.stringify(obj)); + }); + }); }); }); }); @@ -206,6 +227,7 @@ router.route('/api/list/:channel_name/:video_id').put(function(req,res) { res.sendStatus(400); return; } + try { var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; var guid = Functions.hash_pass(req.get('User-Agent') + ip + req.headers["accept-language"]); @@ -223,31 +245,54 @@ router.route('/api/list/:channel_name/:video_id').put(function(req,res) { return; } - validateLogin(adminpass, userpass, channel_name, "vote", res, function(exists) { - if(!exists) { - res.sendStatus(404); + db.collection("timeout_api").find({ + type: "PUT", + guid: guid, + }, function(err, docs) { + if(docs.length > 0) { + var date = new Date(docs[0].createdAt); + date.setSeconds(date.getSeconds() + 5); + var now = new Date(); + var retry_in = (date.getTime() - now.getTime()) / 1000; + res.header({'Retry-After': retry_in}); + res.sendStatus(429); return; } - db.collection(channel_name).find({id: video_id, now_playing: false}, function(err, song) { - if(song.length == 0) { + + validateLogin(adminpass, userpass, channel_name, "vote", res, function(exists) { + if(!exists) { res.sendStatus(404); return; - } else if(song[0].guids.indexOf(guid) > -1) { - res.sendStatus(409); - return; - } else { - song[0].votes += 1; - song[0].guids.push(guid); - db.collection(channel_name).update({id: video_id}, {$inc:{votes:1}, $set:{added:Functions.get_time()}, $push :{guids: guid}}, function(err, success) { - io.to(channel_name).emit("channel", {type: "vote", value: video_id, time: Functions.get_time()}); - List.getNextSong(channel_name, function() { - res.header({'Content-Type': 'application/json'}); - res.status(200).send(JSON.stringify(song[0])); - return; - }); - }); } - }) + db.collection(channel_name).find({id: video_id, now_playing: false}, function(err, song) { + if(song.length == 0) { + res.sendStatus(404); + return; + } else if(song[0].guids.indexOf(guid) > -1) { + res.sendStatus(409); + return; + } else { + song[0].votes += 1; + song[0].guids.push(guid); + db.collection(channel_name).update({id: video_id}, {$inc:{votes:1}, $set:{added:Functions.get_time()}, $push :{guids: guid}}, function(err, success) { + io.to(channel_name).emit("channel", {type: "vote", value: video_id, time: Functions.get_time()}); + List.getNextSong(channel_name, function() { + db.collection("timeout_api").update({type: "PUT", guid: guid}, { + $set: { + "createdAt": new Date(), + type: "PUT", + guid: guid, + }, + }, {upsert: true}, function(err, docs) { + res.header({'Content-Type': 'application/json'}); + res.status(200).send(JSON.stringify(song[0])); + return; + }); + }); + }); + } + }) + }); }); }); @@ -284,67 +329,86 @@ router.route('/api/list/:channel_name/:video_id').post(function(req,res) { return; } - validateLogin(adminpass, userpass, channel_name, "add", res, function(exists) { - db.collection(channel_name).find({id: video_id}, function(err, result) { - if(result.length == 0) { - db.collection(channel_name).find({now_playing: true}, function(err, now_playing) { - var set_np = false; - if(now_playing.length == 0) { - set_np = true; - } - var new_song = {"added": Functions.get_time(),"guids":[guid],"id":video_id,"now_playing":set_np,"title":title,"votes":1, "duration":duration, "start": parseInt(start_time), "end": parseInt(end_time)}; - db.collection("frontpage_lists").find({"_id": channel_name}, function(err, count) { - var create_frontpage_lists = false; - if(count.length == 0) { - create_frontpage_lists = true; + db.collection("timeout_api").find({ + type: "POST", + guid: guid, + }, function(err, docs) { + if(docs.length > 0) { + var date = new Date(docs[0].createdAt); + date.setSeconds(date.getSeconds() + 5); + var now = new Date(); + var retry_in = (date.getTime() - now.getTime()) / 1000; + res.header({'Retry-After': retry_in}); + res.sendStatus(429); + return; + } + + validateLogin(adminpass, userpass, channel_name, "add", res, function(exists) { + db.collection(channel_name).find({id: video_id}, function(err, result) { + if(result.length == 0) { + db.collection(channel_name).find({now_playing: true}, function(err, now_playing) { + var set_np = false; + if(now_playing.length == 0) { + set_np = true; } - if(!exists) { - var configs = {"addsongs":false, "adminpass":"", "allvideos":true, "frontpage":true, "longsongs":false, "removeplay": false, "shuffle": true, "skip": false, "skips": [], "startTime":Functions.get_time(), "views": [], "vote": false, "desc": ""}; - db.collection(channel_name + "_settings").insert(configs, function(err, docs){ - io.to(channel_name).emit("conf", configs); - }); - } - db.collection(channel_name).insert(new_song, function(err, success) { - if(create_frontpage_lists) { - db.collection("frontpage_lists").insert({"_id": channel_name, "count" : 1, "frontpage": true, "accessed": Functions.get_time(), "viewers": 1}, function(err, docs) { + var new_song = {"added": Functions.get_time(),"guids":[guid],"id":video_id,"now_playing":set_np,"title":title,"votes":1, "duration":duration, "start": parseInt(start_time), "end": parseInt(end_time)}; + db.collection("frontpage_lists").find({"_id": channel_name}, function(err, count) { + var create_frontpage_lists = false; + if(count.length == 0) { + create_frontpage_lists = true; + } + if(!exists) { + var configs = {"addsongs":false, "adminpass":"", "allvideos":true, "frontpage":true, "longsongs":false, "removeplay": false, "shuffle": true, "skip": false, "skips": [], "startTime":Functions.get_time(), "views": [], "vote": false, "desc": ""}; + db.collection(channel_name + "_settings").insert(configs, function(err, docs){ io.to(channel_name).emit("conf", configs); - io.to(channel_name).emit("channel", {type: "added", value: new_song}); - List.getNextSong(channel_name, function() { - res.header({'Content-Type': 'application/json'}); - res.status(200).send(JSON.stringify(new_song)); - return; - }); - }); - } else if(set_np) { - Frontpage.update_frontpage(channel_name, video_id, title, function() { - io.to(channel_name).emit("np", new_song); - List.getNextSong(channel_name, function() { - res.header({'Content-Type': 'application/json'}); - res.status(200).send(JSON.stringify(new_song)); - return; - }); - }); - } else { - db.collection("frontpage_lists").update({"_id": channel_name}, {$inc: {count: 1}}, function(err, docs) { - io.to(channel_name).emit("channel", {type: "added", value: new_song}); - List.getNextSong(channel_name, function() { - res.header({'Content-Type': 'application/json'}); - res.status(200).send(JSON.stringify(new_song)); - return; - }); }); } - }); + db.collection(channel_name).insert(new_song, function(err, success) { + if(create_frontpage_lists) { + db.collection("frontpage_lists").insert({"_id": channel_name, "count" : 1, "frontpage": true, "accessed": Functions.get_time(), "viewers": 1}, function(err, docs) { + postEnd(channel_name, configs, new_song, guid, res); + }); + } else if(set_np) { + Frontpage.update_frontpage(channel_name, video_id, title, function() { + io.to(channel_name).emit("np", new_song); + postEnd(channel_name, configs, new_song, guid, res); + }); + } else { + db.collection("frontpage_lists").update({"_id": channel_name}, {$inc: {count: 1}}, function(err, docs) { + io.to(channel_name).emit("channel", {type: "added", value: new_song}); + postEnd(channel_name, configs, new_song, guid, res); + }); + } + }); + }) }) - }) - } else { - res.sendStatus(409); - return; - } + } else { + res.sendStatus(409); + return; + } + }); }); }); }); +function postEnd(channel_name, configs, new_song, guid, res) { + io.to(channel_name).emit("conf", configs); + io.to(channel_name).emit("channel", {type: "added", value: new_song}); + List.getNextSong(channel_name, function() { + db.collection("timeout_api").update({type: "POST", guid: guid}, { + $set: { + "createdAt": new Date(), + type: "POST", + guid: guid, + }, + }, {upsert: true}, function(err, docs) { + res.header({'Content-Type': 'application/json'}); + res.status(200).send(JSON.stringify(new_song)); + return; + }); + }); +} + router.route('/api/list/:channel_name').get(function(req, res) { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");