REST-endpoints and readme

This commit is contained in:
Kasper Rynning-Tønnesen
2018-03-01 13:09:44 +01:00
parent 2e8ef6739c
commit 97e06e3726
7 changed files with 588 additions and 30 deletions

View File

@@ -2,57 +2,197 @@
Under ``` /server/apps/ ```, there are two files, ``` admin.js ``` and ``` client.js ```.``` admin.js ``` are for the adminpanel, and ``` client.js ``` are for zoff itself. Under ``` /server/apps/ ```, there are two files, ``` admin.js ``` and ``` client.js ```.``` admin.js ``` are for the adminpanel, and ``` client.js ``` are for zoff itself.
## REST
Add song
```
POST /api/list/:channel_name/:video_id
{
"title": TITLE,
"duration": END_TIME - START_TIME,
"end_time": END_TIME,
"start_time": START_TIME,
"adminpass": PASSWORD,
"userpass": USER_PASSWORD
}
Returns 403 for bad authentication
Returns 409 if the song exists
Returns 200 and the added song object if successful
```
Delete song
```
DELETE /api/list/:channel_name/:video_id
{
"adminpass": PASSWORD,
"userpass": USER_PASSWORD
}
Returns 403 for bad authentication
Returns 404 if the song doesnt exist
Returns 200 if successful
```
Vote on song
```
PUT /api/list/:channel_name/:video_id
{
"adminpass": PASSWORD,
"userpass": USER_PASSWORD
}
Returns 403 for bad authentication
Returns 404 if the song doesnt exist
Returns 409 if you've already voted on that song
Returns 200 and the added song object if successful
```
Change channel configurations
```
PUT /api/conf/:channel_name
{
"userpass": USER_PASSWORD,
"adminpass": PASSWORD,
"voting": BOOLEAN,
"addsongs": BOOLEAN,
"longsongs": BOOLEAN,
"frontpage": BOOLEAN (if you want to set userpassword, this MUST be false for it to work),
"allvideos": BOOLEAN,
"removeplay": BOOLEAN,
"skipping": BOOLEAN,
"shuffling": BOOLEAN,
"userpass_changed": BOOLEAN (this must be true if you want to keep the userpassword you're sending)
}
Returns 403 for bad authentication
Returns 404 if the list doesn't exist
Returns 200 and the newly added configuration if successful
```
Still to come: SKIP and SHUFFLE RESTApi calls..
## Events ## Events
### To server ### To server
``` ```
// Tells the server the song is clientside // Tells the server the song is over
'end', {id: video_id, channel: channel_name, pass: channel_pass} 'end', {
id: video_id,
channel: channel_name,
pass: channel_pass
}
// Asks server where in the song it should be // Asks server where in the song it should be
'pos', {channel: channel_name, pass: channel_pass} 'pos', {
channel: channel_name,
pass: channel_pass
}
// Tells the server the client wants the list // Tells the server the client wants the list
'list', {channel: channel_name, pass: channel_pass, version: system_version (now 3)} 'list', {
channel: channel_name,
pass: channel_pass,
version: system_version (now 3)
}
// Sends info about a song the client wants to add // Sends info about a song the client wants to add
'add', {id: VIDEO_ID, title: VIDEO_TITLE, adminpass: sha256(PASSWORD), duration: VIDEO_DURATION, list: channel_name, playlist: true_if_importing_playlist, num: current_number_of_sending_songs, total: total_number_of_sending_songs, pass: channel_pass} 'add', {
id: VIDEO_ID,
title: VIDEO_TITLE,
adminpass: AES-CBC-Pkcs7 with Base64 IV(PASSWORD),
duration: VIDEO_DURATION,
list: channel_name,
playlist: true_if_importing_playlist,
num: current_number_of_sending_songs,
total: total_number_of_sending_songs,
pass: channel_pass
}
// Tells the server to disconnect the user from the current channel, is used for remote controlling on the host side // Tells the server to disconnect the user from the current channel, is used for remote controlling on the host side
'change_channel', {channel: channel_name} 'change_channel', {
channel: channel_name
}
// Sends chat text to all chat // Sends chat text to all chat
'all,chat', {channel: channel_name, data: input} 'all,chat', {
channel: channel_name,
data: input
}
// Sends chat text to channelchat // Sends chat text to channelchat
'chat',{channel: channel_name, data: input, pass: channel_pass} 'chat',{
channel: channel_name,
data: input,
pass: channel_pass
}
// Sends info about song the user wants to vote on. If VOTE_TYPE is del, its deleting the song, if its pos, its just voting // Sends info about song the user wants to vote on. If VOTE_TYPE is del, its deleting the song, if its pos, its just voting
'vote', {channel: CHANNEL_NAME, id: VIDEO_ID, type: VOTE_TYPE, adminpass: PASSWORD} 'vote', {
channel: CHANNEL_NAME,
id: VIDEO_ID,
type: VOTE_TYPE,
adminpass: AES-CBC-Pkcs7 with Base64 IV(PASSWORD)
}
// Sends skip message to server // Sends skip message to server
'skip', {pass: adminpass, id:video_id, channel: chan, userpass: channel_pass} 'skip', {
pass: AES-CBC-Pkcs7 with Base64 IV(PASSWORD),
id:video_id,
channel: chan,
userpass: channel_pass
}
// Sends password for instant log in to server // Sends password for instant log in to server
'password', {password: PASSWORD, channel: CHANNEL_NAME, oldpass: old_pass_if_changing_password} 'password', {
password: PASSWORD,
channel: CHANNEL_NAME,
oldpass: old_pass_if_changing_password
}
// Sends message to the host channel for play // Sends message to the host channel for play
'id', {id: CHANNEL_ID, type: "play", value: "mock"} 'id', {
id: CHANNEL_ID,
type: "play",
value: "mock"
}
// Sends message to the host channel for pause // Sends message to the host channel for pause
'id', {id: CHANNEL_ID, type: "pause", value: "mock"} 'id', {
id: CHANNEL_ID,
type: "pause",
value: "mock"
}
// Sends message to the host channel for skip // Sends message to the host channel for skip
'id', {id: CHANNEL_ID, type: "skip", value: "mock"} 'id', {
id: CHANNEL_ID,
type: "skip",
value: "mock"
}
// Sends message to the host channel to change volume // Sends message to the host channel to change volume
'id', {id: CHANNEL_ID, type: "volume", value: VALUE} 'id', {
id: CHANNEL_ID,
type: "volume",
value: VALUE
}
// Sends message to the host channel to change channel // Sends message to the host channel to change channel
'id', {id: CHANNEL_ID, type: "channel", value: NEW_CHANNEL_NAME} 'id', {
id: CHANNEL_ID,
type: "channel",
value: NEW_CHANNEL_NAME
}
// Sends a video that triggered an error // Sends a video that triggered an error
'error_video', {channel: CHANNE_NAME, id: VIDEO_ID, title: VIDEO_TITLE} 'error_video', {
channel: CHANNE_NAME,
id: VIDEO_ID,
title: VIDEO_TITLE
}
``` ```
### From server ### From server
@@ -60,36 +200,65 @@ Under ``` /server/apps/ ```, there are two files, ``` admin.js ``` and ``` clien
// Receives a string from server for what type of toast to be triggered // Receives a string from server for what type of toast to be triggered
'toast', STRING 'toast', STRING
// Receives the password for the channel if the user sent the right in the first place // Receives a boolean if the password was correct
'pw', STRING 'pw', BOOLEAN
// Receives configuration array from server // Receives configuration array from server
'conf', [ARRAY] 'conf', [ARRAY]
// Receives chat message from allchat // Receives chat message from allchat
'chat.all', {from: name, msg: message, channel: channel, icon: icon_src} 'chat.all', {
from: name,
msg: message,
channel: channel,
icon: icon_src
}
// Receives chat-history for all and for current channel
'chat_history', {
all: BOOLEAN (if true, it is for all-chat),
data: CHAT_HISTORY
}
// Receives chat message from channelchat // Receives chat message from channelchat
'chat', {from: name, msg: message, icon: icon_src} 'chat', {
from: name,
msg: message,
icon: icon_src
}
// Receives the ID of the current client, used for remote listening // Receives the ID of the current client, used for remote listening
'id', STRING 'id', STRING
// Receives the messages sent on CHANNEL_ID above // Receives the messages sent on CHANNEL_ID above
id, {type: STRING, value: VALUE} id, {
type: STRING,
value: VALUE
}
// Receives updates from channel. type is one of the following: list, added, deleted, vote, song_change, changed_values (see further down for better explanation here) // Receives updates from channel. type is one of the following: list, added, deleted, vote, song_change, changed_values (see further down for better explanation here)
'channel', {type: TYPE, value: value, time: time_of_occurence} 'channel', {
type: TYPE,
value: value,
time: time_of_occurence
}
// Receives message from the server that its ready to send the playlist and info // Receives message from the server that its ready to send the playlist and info
'get_list' 'get_list'
// Receives array of now playing song. Is triggered on song-change // Receives array of now playing song. Is triggered on song-change
'np', {np: NOW_PLAYING, conf: CONFIGURATION, time: SERVER_TIME} 'np', {
np: NOW_PLAYING,
conf: CONFIGURATION,
time: SERVER_TIME
}
// Receives number of viewers on the current channel // Receives number of viewers on the current channel
'viewers', VALUE 'viewers', VALUE
// Receives a newly updated video, that was checked for errors (song_generated contains .id which is the current id of the video, and a .new_id for the new video to change the video to) // Receives a newly updated video, that was checked for errors (song_generated contains .id which is the current id of the video, and a .new_id for the new video to change the video to)
'channel', {type: "changed_values", value: song_generated} 'channel', {
type: "changed_values",
value: song_generated
}
``` ```

View File

@@ -10,12 +10,14 @@ function frontpage_lists(msg, socket) {
}); });
} }
function update_frontpage(coll, id, title) { function update_frontpage(coll, id, title, callback) {
db.collection("frontpage_lists").update({_id: coll}, {$set: { db.collection("frontpage_lists").update({_id: coll}, {$set: {
id: id, id: id,
title: title, title: title,
accessed: Functions.get_time()} accessed: Functions.get_time()}
},{upsert: true}, function(err, returnDocs){}); },{upsert: true}, function(err, returnDocs){
if(typeof(callback) == "function") callback();
});
} }
module.exports.frontpage_lists = frontpage_lists; module.exports.frontpage_lists = frontpage_lists;

View File

@@ -74,6 +74,7 @@ function list(msg, guid, coll, offline, socket) {
function skip(list, guid, coll, offline, socket) { function skip(list, guid, coll, offline, socket) {
var socketid = socket.zoff_id; var socketid = socket.zoff_id;
if(list !== undefined && list !== null && list !== "") if(list !== undefined && list !== null && list !== "")
{ {
@@ -81,7 +82,11 @@ function skip(list, guid, coll, offline, socket) {
socket.emit("update_required"); socket.emit("update_required");
return; return;
} }
if(typeof(list.pass) != "string" || typeof(list.id) != "string" ||
typeof(list.channel) != "string" || typeof(list.userpass) != "string") {
socket.emit("toast", "update_required");
return;
}
db.collection(coll + "_settings").find(function(err, docs){ db.collection(coll + "_settings").find(function(err, docs){
if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (list.hasOwnProperty('userpass') && docs[0].userpass == Functions.decrypt_string(socketid, list.userpass)))) { if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (list.hasOwnProperty('userpass') && docs[0].userpass == Functions.decrypt_string(socketid, list.userpass)))) {
@@ -401,6 +406,7 @@ function end(obj, coll, guid, offline, socket) {
return; return;
} }
id = obj.id; id = obj.id;
if(id !== undefined && id !== null && id !== "") { if(id !== undefined && id !== null && id !== "") {
if(coll == "" || coll == undefined || coll == null) { if(coll == "" || coll == undefined || coll == null) {
@@ -408,6 +414,12 @@ function end(obj, coll, guid, offline, socket) {
return; return;
} }
if(typeof(obj.id) != "string" || typeof(obj.channel) != "string" ||
typeof(obj.pass) != "string") {
socket.emit("toast", "update_required");
return;
}
db.collection(coll + "_settings").find(function(err, docs){ db.collection(coll + "_settings").find(function(err, docs){
if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (obj.hasOwnProperty('pass') && docs[0].userpass == Functions.decrypt_string(socketid, obj.pass)))) { if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (obj.hasOwnProperty('pass') && docs[0].userpass == Functions.decrypt_string(socketid, obj.pass)))) {
@@ -491,7 +503,7 @@ function sendColor(coll, socket, id) {
}); });
} }
function getNextSong(coll) { function getNextSong(coll, callback) {
db.collection(coll).aggregate([{ db.collection(coll).aggregate([{
$match:{ $match:{
views:{ views:{
@@ -514,6 +526,7 @@ function getNextSong(coll) {
if(doc.length == 1) { if(doc.length == 1) {
io.to(coll).emit("next_song", {videoId: doc[0].id, title: doc[0].title}); io.to(coll).emit("next_song", {videoId: doc[0].id, title: doc[0].title});
} }
if(typeof(callback) == "function") callback();
}); });
} }

View File

@@ -27,6 +27,17 @@ function add_function(arr, coll, guid, offline, socket) {
return; return;
} }
if(typeof(arr.id) != "string" || typeof(arr.start) != "number" ||
typeof(arr.end) != "number" || typeof(arr.title) != "string" ||
typeof(arr.list) != "string" || typeof(arr.duration) != "number" ||
typeof(arr.playlist) != "boolean" || typeof(arr.num) != "number" ||
typeof(arr.total) != "number" || typeof(arr.pass) != "string" ||
typeof(arr.adminpass) != "string") {
socket.emit("toast", "update_required");
return;
}
db.collection(coll + "_settings").find(function(err, docs){ db.collection(coll + "_settings").find(function(err, docs){
if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (arr.hasOwnProperty('pass') && docs[0].userpass == Functions.decrypt_string(socketid, arr.pass)))) { if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (arr.hasOwnProperty('pass') && docs[0].userpass == Functions.decrypt_string(socketid, arr.pass)))) {
@@ -179,6 +190,13 @@ function voteUndecided(msg, coll, guid, offline, socket) {
return; return;
} }
if(typeof(msg.channel) != "string" || typeof(msg.id) != "string" ||
typeof(msg.type) != "string" || typeof(msg.adminpass) != "string" ||
typeof(msg.pass) != "string") {
socket.emit("toast", "update_required");
return;
}
db.collection(coll + "_settings").find(function(err, docs){ db.collection(coll + "_settings").find(function(err, docs){
if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (msg.hasOwnProperty('pass') && docs[0].userpass == Functions.decrypt_string(socketid, msg.pass)))) { if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (msg.hasOwnProperty('pass') && docs[0].userpass == Functions.decrypt_string(socketid, msg.pass)))) {
@@ -218,6 +236,12 @@ function shuffle(msg, coll, guid, offline, socket) {
return; return;
} }
if(typeof(msg.adminpass) != "string" || typeof(msg.channel) != "string" ||
typeof(msg.pass) != "string") {
socket.emit("toast", "update_required");
return;
}
Functions.check_inlist(coll, guid, socket, offline); Functions.check_inlist(coll, guid, socket, offline);
var hash; var hash;
if(msg.adminpass === "") hash = msg.adminpass; if(msg.adminpass === "") hash = msg.adminpass;
@@ -290,6 +314,11 @@ function delete_all(msg, coll, guid, offline, socket) {
var hash = Functions.hash_pass(Functions.decrypt_string(socketid, msg.adminpass)); var hash = Functions.hash_pass(Functions.decrypt_string(socketid, msg.adminpass));
var hash_userpass = Functions.decrypt_string(socketid, msg.pass); var hash_userpass = Functions.decrypt_string(socketid, msg.pass);
if(typeof(msg.channel) != "string" || typeof(msg.adminpass) != "string" ||
typeof(msg.pass) != "string") {
socket.emit("toast", "update_required");
return;
}
db.collection(coll + "_settings").find(function(err, conf) { db.collection(coll + "_settings").find(function(err, conf) {
if(conf.length == 1 && conf) { if(conf.length == 1 && conf) {
conf = conf[0]; conf = conf[0];

View File

@@ -21,7 +21,6 @@ function password(inp, coll, guid, offline, socket) {
uncrypted = pw; uncrypted = pw;
pw = Functions.decrypt_string(socket.zoff_id, pw); pw = Functions.decrypt_string(socket.zoff_id, pw);
Functions.check_inlist(coll, guid, socket, offline); Functions.check_inlist(coll, guid, socket, offline);
if(inp.oldpass) if(inp.oldpass)
@@ -44,6 +43,7 @@ function password(inp, coll, guid, offline, socket) {
}); });
}else }else
socket.emit("toast", "wrongpass"); socket.emit("toast", "wrongpass");
socket.emit("pw", false);
} }
}); });
} else { } else {
@@ -94,6 +94,15 @@ function conf_function(params, coll, guid, offline, socket) {
var skipping = params.skipping; var skipping = params.skipping;
var shuffling = params.shuffling; var shuffling = params.shuffling;
var userpass = Functions.decrypt_string(socket.zoff_id, params.userpass); var userpass = Functions.decrypt_string(socket.zoff_id, params.userpass);
if(typeof(userpass) != "string" || typeof(adminpass) != "string" ||
typeof(voting) != "boolean" || typeof(addsongs) != "boolean" ||
typeof(longsongs) != "boolean" || typeof(frontpage) != "boolean" ||
typeof(allvideos) != "boolean" || typeof(removeplay) != "boolean" ||
typeof(skipping) != "boolean" || typeof(shuffling) != "boolean" ||
typeof(params.userpass_changed) != "boolean") {
socket.emit("toast", "wrongpass");
return;
}
if((!params.userpass_changed && frontpage) || (params.userpass_changed && userpass == "")) { if((!params.userpass_changed && frontpage) || (params.userpass_changed && userpass == "")) {
userpass = ""; userpass = "";

View File

@@ -1,5 +1,10 @@
function thumbnail(msg, coll, guid, offline, socket) { function thumbnail(msg, coll, guid, offline, socket) {
if(msg.thumbnail && msg.channel && msg.adminpass && msg.thumbnail.indexOf("i.imgur.com") > -1){ if(msg.thumbnail && msg.channel && msg.adminpass && msg.thumbnail.indexOf("i.imgur.com") > -1){
if(typeof(msg.channel) != "string" || typeof(msg.thumbnail) != "string" ||
typeof(msg.adminpass) != "string" || typeof(msg.pass) != "string") {
socket.emit("toast", "update_required");
return;
}
msg.thumbnail = msg.thumbnail.replace(/^https?\:\/\//i, ""); msg.thumbnail = msg.thumbnail.replace(/^https?\:\/\//i, "");
if(msg.thumbnail.substring(0,2) != "//") msg.thumbnail = "//" + msg.thumbnail; if(msg.thumbnail.substring(0,2) != "//") msg.thumbnail = "//" + msg.thumbnail;
var channel = msg.channel.toLowerCase(); var channel = msg.channel.toLowerCase();
@@ -23,6 +28,11 @@ function thumbnail(msg, coll, guid, offline, socket) {
function description(msg, coll, guid, offline, socket) { function description(msg, coll, guid, offline, socket) {
if(msg.description && msg.channel && msg.adminpass && msg.description.length < 100){ if(msg.description && msg.channel && msg.adminpass && msg.description.length < 100){
if(typeof(msg.channel) != "string" || typeof(msg.description) != "string" ||
typeof(msg.adminpass) != "string" || typeof(msg.pass) != "string") {
socket.emit("toast", "update_required");
return;
}
var channel = msg.channel.toLowerCase(); var channel = msg.channel.toLowerCase();
var hash = Functions.hash_pass(Functions.decrypt_string(socket.zoff_id, msg.adminpass)); var hash = Functions.hash_pass(Functions.decrypt_string(socket.zoff_id, msg.adminpass));
db.collection(channel + "_settings").update({views: {$exists: true}}, function(err, docs){ db.collection(channel + "_settings").update({views: {$exists: true}}, function(err, docs){

View File

@@ -19,6 +19,332 @@ router.route('/api/generate_name').get(function(req, res) {
Functions.generate_channel_name(res); Functions.generate_channel_name(res);
}); });
router.route('/api/list/:channel_name/:video_id').delete(function(req, res) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
if(!req.body.hasOwnProperty('adminpass') || !req.body.hasOwnProperty('userpass') ||
!req.params.hasOwnProperty('channel_name') || !req.params.hasOwnProperty('video_id')) {
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"]);
var adminpass = req.body.adminpass == "" ? "" : Functions.hash_pass(crypto.createHash('sha256').update(req.body.adminpass, 'utf8').digest("hex"));
req.body.userpass = crypto.createHash('sha256').update(req.body.userpass, 'utf8').digest("hex");
var userpass = req.body.userpass;
var channel_name = cleanChannelName(req.params.channel_name);
var video_id = req.params.video_id;
if(typeof(userpass) != "string" || typeof(adminpass) != "string") {
throw "Wrong format";
}
} catch(e) {
res.sendStatus(400);
return;
}
validateLogin(adminpass, userpass, channel_name, "delete", res, function(exists) {
if(!exists) {
res.sendStatus(404);
return;
}
db.collection(channel_name).find({id:video_id, now_playing: false}, function(err, docs){
if(docs.length == 0) {
res.sendStatus(404);
return;
}
dont_increment = true;
if(docs[0]){
if(docs[0].type == "suggested"){
dont_increment = false;
}
db.collection(channel_name).remove({id:video_id}, function(err, docs){
io.to(channel_name).emit("channel", {type:"deleted", value: video_id});
if(dont_increment) {
db.collection("frontpage_lists").update({_id: channel_name, count: {$gt: 0}}, {$inc: {count: -1}, $set:{accessed: Functions.get_time()}}, {upsert: true}, function(err, docs){
res.sendStatus(200);
return;
});
} else {
res.sendStatus(200);
return;
}
});
}
});
});
});
function cleanChannelName(channel_name) {
var coll = emojiStrip(channel_name).toLowerCase();
coll = coll.replace("_", "");
coll = encodeURIComponent(coll).replace(/\W/g, '');
coll = filter.clean(coll);
return coll;
}
function validateLogin(adminpass, userpass, channel_name, type, res, callback) {
db.collection(channel_name + "_settings").find({views: {$exists: true}}, function(err, conf) {
var exists = false;
if(conf.length > 0 && ((conf[0].userpass == undefined || conf[0].userpass == "" || conf[0].userpass == userpass))) {
exists = true;
} else if(conf.length > 0) {
res.sendStatus(403);
return;
}
if(
(type == "add" && ((conf[0].addsongs && (conf[0].adminpass == "" || conf[0].adminpass == undefined || conf[0].adminpass == adminpass)) || !conf[0].addsongs)) ||
(type == "delete" && (conf[0].adminpass == "" || conf[0].adminpass == undefined || conf[0].adminpass == adminpass)) ||
(type == "vote" && ((conf[0].vote && (conf[0].adminpass == "" || conf[0].adminpass == undefined || conf[0].adminpass == adminpass)) || !conf[0].vote)) ||
(type == "config" && (conf[0].adminpass == "" || conf[0].adminpass == undefined || conf[0].adminpass == adminpass))
) {
callback(exists);
} else {
res.sendStatus(403);
return;
}
});
}
router.route('/api/conf/:channel_name').put(function(req, res) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
if(!req.body.hasOwnProperty('adminpass') || !req.body.hasOwnProperty('userpass') ||
!req.params.hasOwnProperty('channel_name') || !req.body.hasOwnProperty('voting') ||
!req.body.hasOwnProperty('addsongs') || !req.body.hasOwnProperty('longsongs') ||
!req.body.hasOwnProperty('frontpage') || !req.body.hasOwnProperty('allvideos') ||
!req.body.hasOwnProperty('skipping') || !req.body.hasOwnProperty('shuffling') ||
!req.body.hasOwnProperty('userpass_changed')) {
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"]);
var adminpass = req.body.adminpass == "" ? "" : Functions.hash_pass(crypto.createHash('sha256').update(req.body.adminpass, 'utf8').digest("hex"));
req.body.userpass = crypto.createHash('sha256').update(req.body.userpass, 'utf8').digest("hex");
var userpass = req.body.userpass;
var voting = req.body.voting;
var addsongs = req.body.addsongs;
var longsongs = req.body.longsongs;
var frontpage = req.body.frontpage;
var allvideos = req.body.allvideos;
var removeplay = req.body.removeplay;
var skipping = req.body.skipping;
var shuffling = req.body.shuffling;
var userpass_changed = req.body.userpass_changed;
var channel_name = cleanChannelName(req.params.channel_name);
if(typeof(userpass) != "string" || typeof(adminpass) != "string" ||
typeof(voting) != "boolean" || typeof(addsongs) != "boolean" ||
typeof(longsongs) != "boolean" || typeof(frontpage) != "boolean" ||
typeof(allvideos) != "boolean" || typeof(removeplay) != "boolean" ||
typeof(skipping) != "boolean" || typeof(shuffling) != "boolean" ||
typeof(userpass_changed) != "boolean") {
throw "Wrong format";
}
} catch(e) {
res.send(e);
res.sendStatus(400);
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 = "";
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]);
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));
});
});
});
})
router.route('/api/list/:channel_name/:video_id').put(function(req,res) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
if(!req.body.hasOwnProperty('adminpass') || !req.body.hasOwnProperty('userpass') ||
!req.params.hasOwnProperty('channel_name') || !req.params.hasOwnProperty('video_id')) {
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"]);
var adminpass = req.body.adminpass == "" ? "" : Functions.hash_pass(crypto.createHash('sha256').update(req.body.adminpass, 'utf8').digest("hex"));
req.body.userpass = crypto.createHash('sha256').update(req.body.userpass, 'utf8').digest("hex");
var userpass = req.body.userpass;
var channel_name = cleanChannelName(req.params.channel_name);
var video_id = req.params.video_id;
if(typeof(userpass) != "string" || typeof(adminpass) != "string") {
throw "Wrong format";
}
} catch(e) {
res.send(e);
res.sendStatus(400);
return;
}
validateLogin(adminpass, userpass, channel_name, "vote", res, function(exists) {
if(!exists) {
res.sendStatus(404);
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() {
res.header({'Content-Type': 'application/json'});
res.status(200).send(JSON.stringify(song[0]));
return;
});
});
}
})
});
});
router.route('/api/list/:channel_name/:video_id').post(function(req,res) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
if(!req.body.hasOwnProperty('adminpass') || !req.body.hasOwnProperty('userpass') ||
!req.params.hasOwnProperty('channel_name') || !req.params.hasOwnProperty('video_id') ||
!req.body.hasOwnProperty('duration') || !req.body.hasOwnProperty('start_time') ||
!req.body.hasOwnProperty('end_time') || !req.body.hasOwnProperty('title')) {
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"]);
var adminpass = req.body.adminpass == "" ? "" : Functions.hash_pass(crypto.createHash('sha256').update(req.body.adminpass, 'utf8').digest("hex"));
req.body.userpass = crypto.createHash('sha256').update(req.body.userpass, 'utf8').digest("hex");
var userpass = req.body.userpass;
var channel_name = cleanChannelName(req.params.channel_name);
var video_id = req.params.video_id;
var duration = parseInt(req.body.duration);
var start_time = parseInt(req.body.start_time);
var end_time = parseInt(req.body.end_time);
if(duration != end_time - start_time) duration = end_time - start_time;
var title = req.body.title;
if(typeof(userpass) != "string" || typeof(adminpass) != "string" ||
typeof(title) != "string") {
throw "Wrong format";
}
} catch(e) {
res.send(e);
res.sendStatus(400);
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;
}
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) {
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;
});
});
}
});
})
})
} else {
res.sendStatus(409);
return;
}
});
});
});
router.route('/api/list/:channel_name').get(function(req, res) { router.route('/api/list/:channel_name').get(function(req, res) {
res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");