Added url for applying for api-tokens

This commit is contained in:
Kasper Rynning-Tønnesen
2018-03-04 11:24:44 +01:00
parent 785813b842
commit 76e7001e51
15 changed files with 665 additions and 304 deletions

View File

@@ -4,7 +4,7 @@ var gulp = require('gulp'),
concat = require('gulp-concat');
gulp.task('js', function () {
gulp.src(['server/VERSION.js', 'server/config/api_key.js', 'server/public/assets/js/*.js', '!server/public/assets/js/embed*', '!server/public/assets/js/remotecontroller.js', '!server/public/assets/js/callback.js'])
gulp.src(['server/VERSION.js', 'server/config/api_key.js', 'server/public/assets/js/*.js', '!server/public/assets/js/embed*', '!server/public/assets/js/token*', '!server/public/assets/js/remotecontroller.js', '!server/public/assets/js/callback.js'])
.pipe(uglify({
mangle: true,
compress: true,
@@ -25,6 +25,17 @@ gulp.task('embed', function () {
.pipe(gulp.dest('server/public/assets/dist'));
});
gulp.task('token', function() {
gulp.src(['server/public/assets/js/token*', 'server/public/assets/js/helpers.js'])
.pipe(uglify({
mangle: true,
compress: true,
enclose: true
}))
.pipe(concat('token.min.js'))
.pipe(gulp.dest('server/public/assets/dist'));
})
gulp.task('callback', function () {
gulp.src(['server/VERSION.js', 'server/config/api_key.js', 'server/public/assets/js/callback.js'])
.pipe(uglify({
@@ -53,6 +64,7 @@ gulp.task('remotecontroller', function () {
gulp.task('default', function(){
gulp.watch(['server/VERSION.js', 'server/public/assets/js/*.js'], ['js']);
gulp.watch(['server/public/assets/js/token*.js', 'server/public/assets/js/helpers.js'], ['token']);
gulp.watch(['server/VERSION.js', 'server/public/assets/js/*.js'], ['embed']);
gulp.watch(['server/VERSION.js', 'server/public/assets/js/callback.js', 'server/public/assets/js/helpers.js'], ['callback']);
//gulp.watch('server/public/assets/js/*.js', ['nochan']);

View File

@@ -6,7 +6,7 @@ Under ``` /server/apps/ ```, there are two files, ``` admin.js ``` and ``` clien
All PUT, DELETE and POST endpoints have a 2-second waitlimit for each command per client. You'll get a response with Retry-After header for how long you have to wait. Shuffling in a player has a 5-second waitlimit, but per channel instead of per client.
If you want to skip the wait-times, send a mail to the team at contact@zoff.me, and get a token. Tokens are added to all the POST, PUT, DELETE, requests as ``` token: TOKEN ```.
If you want to skip the wait-times, create a token at <a href="https://zoff.me/api/apply">https://zoff.me/api/apply</a>. Tokens are added to all the POST, PUT, DELETE, requests as ``` token: TOKEN ```.
All requests return things on this form (results field is added if successful.)

View File

@@ -12,6 +12,7 @@ var ObjectId = mongojs.ObjectId;
db.collection("chat_logs").createIndex({ "createdAt": 1 }, { expireAfterSeconds: 600 });
db.collection("timeout_api").createIndex({ "createdAt": 1 }, { expireAfterSeconds: 5 });
db.collection("api_links").createIndex({ "createdAt": 1 }, { expireAfterSeconds: 86400 });
db.on('connected', function(err) {
console.log("connected");
})

View File

@@ -9,7 +9,7 @@ function thumbnail(msg, coll, guid, offline, socket) {
if(msg.thumbnail.substring(0,2) != "//") msg.thumbnail = "//" + msg.thumbnail;
var channel = msg.channel.toLowerCase();
var hash = Functions.hash_pass(Functions.decrypt_string(socket.zoff_id, msg.adminpass));
db.collection(channel + "_settings").update({id: "configs"}, function(err, docs){
db.collection(channel + "_settings").find({id: "config"}, 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 !== null && docs.length !== 0 && docs[0].adminpass !== "" && docs[0].adminpass == hash){
db.collection("suggested_thumbnails").update({channel: channel}, {$set:{thumbnail: msg.thumbnail}}, {upsert:true}, function(err, docs){
@@ -35,7 +35,7 @@ function description(msg, coll, guid, offline, socket) {
}
var channel = msg.channel.toLowerCase();
var hash = Functions.hash_pass(Functions.decrypt_string(socket.zoff_id, msg.adminpass));
db.collection(channel + "_settings").update({id: "configs"}, function(err, docs){
db.collection(channel + "_settings").find({id: "config"}, 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 !== null && docs.length !== 0 && docs[0].adminpass !== "" && docs[0].adminpass == hash){
db.collection("suggested_descriptions").update({channel: channel}, {$set:{description: msg.description}}, {upsert:true}, function(err, docs){

View File

@@ -30,17 +30,21 @@ $(document).on("click", "#refresh_all", function(e){
}
$(".header-api-fields").removeClass("hide");
for(var i = 0; i < response.length; i++) {
var to_add = api_token_list;
var to_add = api_token_list.clone();
to_add.find(".api_token_limit").val(response[i].limit);
to_add.attr("id", response[i]._id);
to_add.find(".api_token_name").text(response[i].name);
to_add.find(".api_token_usage").text(response[i].usage);
to_add.find(".api_token_limit").attr("id", response[i]._id + "-limit");
to_add.find("#delete_api_token").attr("data-id", response[i]._id);
to_add.find("#update_api_token").attr("data-id", response[i]._id);
$(".channel_things").append(to_add);
}
},
error: function(err) {
}
});
if(!$(".channel_things").hasClass("hide")) {
$(".channel_things").addClass("hide")
}
@@ -116,11 +120,39 @@ if(!$(".channel_things").hasClass("hide")) {
}
$(".preloader-wrapper").removeClass("hide");
$(document).on("click", "#update_api_token", function(e) {
e.preventDefault();
var id = $(this).attr("data-id");
var limit = $("#" + id + "-limit").val();
var that = this;
$(that).toggleClass("disabled");
$("#delete_api_token").toggleClass("disabled");
$.ajax({
type: "PUT",
url: "api/api_token",
data: {
id: id,
limit: limit,
},
success: function(response) {
if(response == "OK") {
Materialize.toast("Updated limit!", 2000, "green lighten");
} else {
Materialize.toast("Something went wrong...", 2000, "red lighten");
}
$(that).toggleClass("disabled");
$("#delete_api_token").toggleClass("disabled");
}
});
});
$(document).on("click", "#delete_api_token", function(e) {
e.preventDefault();
var id = $(this).attr("data-id");
var that = this;
$(that).toggleClass("disabled");
$("#update_api_token").toggleClass("disabled");
$.ajax({
type: "DELETE",
url: "api/api_token",
@@ -134,6 +166,7 @@ $(document).on("click", "#delete_api_token", function(e) {
} else {
Materialize.toast("Something went wrong...", 2000, "red lighten");
$(that).toggleClass("disabled");
$("#update_api_token").toggleClass("disabled");
}
},
})
@@ -153,10 +186,13 @@ function loaded() {
$(".header-api-fields").removeClass("hide");
for(var i = 0; i < response.length; i++) {
var to_add = api_token_list.clone();
to_add.find(".api_token_limit").val(response[i].limit);
to_add.attr("id", response[i]._id);
to_add.find(".api_token_name").text(response[i].name);
to_add.find(".api_token_usage").text(response[i].usage);
to_add.find(".api_token_limit").attr("id", response[i]._id + "-limit");
to_add.find("#delete_api_token").attr("data-id", response[i]._id);
to_add.find("#update_api_token").attr("data-id", response[i]._id);
$(".channel_things").append(to_add);
}
},

View File

@@ -91,6 +91,27 @@ body {
height: 32px;
}
.token-container {
padding-top: 64px;
}
.token-form {
position: relative;
}
.full-form-token {
background: rgba(0,0,0,.5);
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.center-loader-token {
margin: auto;
}
#send-loader {
position: absolute;
top: 0px;
@@ -405,6 +426,10 @@ li.disabled span {
user-select: text;
}
.auto-pointer {
cursor: auto !important;
}
#chatchannel li:nth-child(even), #chatall li:nth-child(even) {
background: rgba(0,0,0,.1);
}

View File

@@ -0,0 +1 @@
!function(){$(document).ready(function(){$("#about").modal(),$(".help-button-footer").hide(),$("#contact").modal(),$("#contact-container").empty(),$("#contact-container").html("Send a mail to us: <a title='Open in client' href='mailto:contact@zoff.me?Subject=Contact%20Zoff'>contact@zoff.me</a>"),$("#submit-contact-form").hide(),$(".token-form").on("submit",function(e){e.preventDefault();var t=$("#email_address").val();$("#email_address").attr("readonly",!0),$(".submit").toggleClass("disabled"),$(".full-form-token").removeClass("hide");var a=grecaptcha.getResponse();$.ajax({type:"POST",url:"/api/apply",data:{email:t,"g-recaptcha-response":a},success:function(e){$(".full-form-token").addClass("hide"),"OK"!=e?($("#email_address").attr("readonly",!0),$(".submit").toggleClass("disabled"),Materialize.toast("Couldn't send email.",3e3,"red lighten")):(Materialize.toast("Email sent!",3e3,"green lighten"),grecaptcha.reset())},error:function(e){$(".full-form-token").addClass("hide"),$("#email_address").attr("readonly",!1),$(".submit").toggleClass("disabled")}})}),$("#submit-contact-form").on("click",function(e){e.preventDefault(),$("#contact-form").submit()})});Element.prototype.remove=function(){this.parentElement.removeChild(this)},NodeList.prototype.remove=HTMLCollection.prototype.remove=function(){for(var e=0,t=this.length;e<t;e++)this[e]&&this[e].parentElement&&this[e].parentElement.removeChild(this[e])},String.prototype.startsWith=function(e,t){return t=t||0,this.indexOf(e,t)===t}}();

View File

@@ -213,7 +213,7 @@ var Player = {
"New state\nState: ",
newState
]);
if(player.getCurrentTime() > startTime + Player.np.start && !fix_too_far) {
if(player.getCurrentTime() > startTime + Player.np.start && !fix_too_far && autoplay) {
Player.seekTo(seekTo);
Player.playVideo();
fix_too_far = true;

View File

@@ -0,0 +1,47 @@
$(document).ready(function() {
$("#about").modal();
$(".help-button-footer").hide();
$("#contact").modal();
$("#contact-container").empty();
$("#contact-container").html("Send a mail to us: <a title='Open in client' href='mailto:contact@zoff.me?Subject=Contact%20Zoff'>contact@zoff.me</a>");
$("#submit-contact-form").hide();
$(".token-form").on("submit", function(e) {
e.preventDefault();
var email = $("#email_address").val();
$("#email_address").attr("readonly", true);
$(".submit").toggleClass("disabled");
$(".full-form-token").removeClass("hide");
var captcha_response = grecaptcha.getResponse();
$.ajax({
type: "POST",
url: "/api/apply",
data: {
email: email,
"g-recaptcha-response": captcha_response,
},
success: function(response) {
$(".full-form-token").addClass("hide");
if(response != "OK") {
$("#email_address").attr("readonly", true);
$(".submit").toggleClass("disabled");
Materialize.toast("Couldn't send email.", 3000, "red lighten");
} else {
Materialize.toast("Email sent!", 3000, "green lighten");
grecaptcha.reset();
}
},
error: function(response) {
$(".full-form-token").addClass("hide");
$("#email_address").attr("readonly", false);
$(".submit").toggleClass("disabled");
}
});
});
$('#submit-contact-form').on('click', function(e) {
e.preventDefault();
$("#contact-form").submit();
});
});

View File

@@ -111,21 +111,24 @@
</div>
</div>
<div class="row header-api-fields">
<div class="col s4">
<div class="col s3">
Name
</div>
<div class="col s4">
<div class="col s3">
Usage
</div>
<div class="col s1">
Limit
</div>
</div>
<div class="row" id="api_token_list">
<div class="col s4 api_token_name">
<div class="col s3 api_token_name truncate">
</div>
<div class="col s4 api_token_usage">
</div>
<div class="col s2">
<a href="#" id="delete_api_token" class="btn waves-effect red">REMOVE</a>
<div class="col s3 api_token_usage">
</div>
<input class="api_token_limit col s1" type="number" />
<a href="#" id="update_api_token" class="btn waves-effect green col s2">UPDATE</a>
<a href="#" id="delete_api_token" class="btn waves-effect red col s1">X</a>
</div>
</div>
</div>

View File

@@ -0,0 +1,72 @@
<header>
<nav id="fp-nav">
<div class="nav-wrapper">
<a href="#" class="brand-logo">
<img class="zicon" src="/assets/images/z.svg" alt="zoff" title="Zoff" />
</a>
<div id="frontpage-viewer-counter" class="noselect" title="Divided among all channels. Hidden or not"></div>
<ul class="right hide-on-med-and-down">
<li><a class="header-buttons waves-effect waves-green" title="Remote control a Zoff player" href="https://remote.zoff.me">Remote</a></li>
<li><a class="header-buttons modal-trigger waves-effect waves-orange" data-target="about">About</a></li>
</ul>
</div>
</nav>
{{> modal/about}}
<div id="donation" class="modal">
<div class="modal-content">
<h4>Thanks!</h4>
<p>Thanks for your donation, we love you &lt;3
<br><br>
We will use the money for something awesome, just you wait and see!
<br><br>
We might also add your name somewhere in the code as a sign of gratitude, see if you can find it! (Might take a day or two for us to see the donation and implement it..)
</p>
</div>
<div class="modal-footer">
<a href="#" class="modal-action modal-close waves-effect waves-green btn-flat">I'm awesome! (Close)</a>
</div>
</div>
</header>
<div id="main-container" class="token-container">
<main class="center-align container">
<div class="token-info left-align">
{{#if activated}}
<h1 class="center-align">API-token</h1>
<p>Here is your api token</p>
<h4 class="select auto-pointer">{{token}}</h3>
<p>Use it wisely, and don't lose it!</p>
<p>As of now, the tokens have no limit for how many requests you can do is 100 requests a second. If you need a higher limit, just contact the team and we'll set you up for as much as you need.</p>
{{else}}
<h2 class="center-align">API-token</h2>
<p>Apply for a API-token with your email here! You'll get an email on the specified address, with a link. Follow that link, and the token will be shown to you! Take good care of it, and don't lose it. It won't be shown to you again.</p>
<p>If you're wondering anything about how the api works, there is a guide on our GitHub. You can also click <a href="https://zoff.me/api/help">HERE</a> to be taken to the detailed README.</p>
<p>As of now, the tokens have no limit for how many requests you can do is 100 requests a second. If you need a higher limit, just contact the team and we'll set you up for as much as you need.</p>
{{/if}}
</div>
{{#if activated}}
<iframe id="iframe" src="https://zoff.me/_embed#celebrate&808080" width="600px" height="300px"></iframe>
{{else}}
<form class="token-form row" type="post">
<div class="row center">
<div class="input-field col s6 offset-s3">
<label for="email_address" class="noselect">Email</label>
<input type="email" class="validate" id="email_address" />
</div>
</div>
<div class="col offset-s3">
{{{captcha}}}
</div>
<div class="input-field col s12 m2">
<input type="submit" class="btn submit" />
</div>
<div class="full-form-token valign-wrapper hide">
<div class="preloader-wrapper medium active center-loader-token">
{{> spinner}}
</div>
</div>
</form>
{{/if}}
<p>Any lost tokens can easily be deleted by our admins, so just send us an email if something goes awry. Just click the CONTACT button in the footer, and we will be with you as fast as we can!</p>
<br>
</main>
</div>

View File

@@ -15,7 +15,7 @@
</div>
</div>
{{{captcha}}}
<div class="valign hide" id="send-loader">
<div class="valign-wrapper hide" id="send-loader">
<div class="preloader-wrapper small active">
{{> spinner}}
</div>

View File

@@ -171,7 +171,7 @@ router.route('/api/api_token').get(function(req, res) {
if(req.isAuthenticated()) {
token_db.collection("api_token").find({token: {$exists: true}}, function(err, all) {
res.json(all);
})
});
} else {
res.sendStatus(403);
}
@@ -190,6 +190,24 @@ router.route('/api/api_token').delete(function(req, res){
}
});
router.route('/api/api_token').put(function(req, res){
if(req.isAuthenticated()){
var id = req.body.id;
var limit = req.body.limit;
if(limit < 0) {
res.sendStatus(500);
return;
}
token_db.collection("api_token").update({_id: ObjectId(id)}, {$set: {limit: limit}}, function(err, success) {
if(err) {
res.sendStatus(500);
return;
}
res.sendStatus(200);
})
}
});
router.route('/api/api_token').post(function(req, res){
if(req.isAuthenticated()){
var name = req.body.name;

View File

@@ -147,48 +147,50 @@ router.route('/api/list/:channel_name/:video_id').delete(function(req, res) {
if(token_docs.length == 1 && token_docs[0].token == token) {
authorized = true;
}
checkTimeout(guid, res, authorized, "DELETE", function() {
if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "DELETE", function(err, docs) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
});
}
validateLogin(adminpass, userpass, channel_name, "delete", res, function(exists) {
if(!exists) {
res.status(404).send(JSON.stringify(error.not_found.list));
return;
checkOveruseApiToken(authorized, token_docs, res, function() {
checkTimeout(guid, res, authorized, "DELETE", function() {
if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "DELETE", function(err, docs) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
});
}
db.collection(channel_name).find({id:video_id, now_playing: false}, function(err, docs){
if(docs.length == 0) {
res.status(404).send(JSON.stringify(error.not_found.local));
validateLogin(adminpass, userpass, channel_name, "delete", res, function(exists) {
if(!exists) {
res.status(404).send(JSON.stringify(error.not_found.list));
return;
}
var dont_increment = false;
if(docs[0]){
if(docs[0].type == "suggested"){
dont_increment = true;
db.collection(channel_name).find({id:video_id, now_playing: false}, function(err, docs){
if(docs.length == 0) {
res.status(404).send(JSON.stringify(error.not_found.local));
return;
}
db.collection(channel_name).remove({id:video_id}, function(err, docs){
if(authorized) {
incrementToken(token);
var dont_increment = false;
if(docs[0]){
if(docs[0].type == "suggested"){
dont_increment = true;
}
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){
db.collection(channel_name).remove({id:video_id}, function(err, docs){
if(authorized) {
incrementToken(token);
}
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){
updateTimeout(guid, res, authorized, "DELETE", function(err, docs) {
res.status(200).send(JSON.stringify(error.no_error));
return;
});
});
} else {
updateTimeout(guid, res, authorized, "DELETE", function(err, docs) {
res.status(200).send(JSON.stringify(error.no_error));
return;
});
});
} else {
updateTimeout(guid, res, authorized, "DELETE", function(err, docs) {
res.status(200).send(JSON.stringify(error.no_error));
return;
});
}
});
}
}
});
}
});
});
});
});
@@ -247,64 +249,66 @@ router.route('/api/conf/:channel_name').put(function(req, res) {
if(token_docs.length == 1 && token_docs[0].token == token) {
authorized = true;
}
checkTimeout(guid, res, authorized, "CONFIG", function() {
if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "CONFIG", function(err, docs) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
});
}
validateLogin(adminpass, userpass, channel_name, "config", res, function(exists, conf) {
if(!exists && conf.length == 0) {
res.status(404).send(JSON.stringify(error.not_found.list));
return;
checkOveruseApiToken(authorized, token_docs, res, function() {
checkTimeout(guid, res, authorized, "CONFIG", function() {
if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "CONFIG", function(err, docs) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
});
}
validateLogin(adminpass, userpass, channel_name, "config", res, function(exists, conf) {
if(!exists && conf.length == 0) {
res.status(404).send(JSON.stringify(error.not_found.list));
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){
if(authorized) {
incrementToken(token);
}
updateTimeout(guid, res, authorized, "CONFIG", function(err, docs) {
var to_return = error.no_error;
to_return.results = [obj];
res.status(200).send(JSON.stringify(to_return));
return;
db.collection("frontpage_lists").update({_id: channel_name}, {$set:{
frontpage:frontpage, accessed: Functions.get_time()}
},
{upsert:true}, function(err, docs){
if(authorized) {
incrementToken(token);
}
updateTimeout(guid, res, authorized, "CONFIG", function(err, docs) {
var to_return = error.no_error;
to_return.results = [obj];
res.status(200).send(JSON.stringify(to_return));
return;
});
});
});
});
@@ -348,44 +352,46 @@ router.route('/api/list/:channel_name/:video_id').put(function(req,res) {
if(token_docs.length == 1 && token_docs[0].token == token) {
authorized = true;
}
checkTimeout(guid, res, authorized, "PUT", function() {
if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "PUT", function(err, docs) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
});
}
validateLogin(adminpass, userpass, channel_name, "vote", res, function(exists) {
if(!exists) {
res.status(404).send(JSON.stringify(error.not_found.list));
return;
checkOveruseApiToken(authorized, token_docs, res, function() {
checkTimeout(guid, res, authorized, "PUT", function() {
if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "PUT", function(err, docs) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
});
}
db.collection(channel_name).find({id: video_id, now_playing: false, type:"video"}, function(err, song) {
if(song.length == 0) {
res.status(404).send(JSON.stringify(error.not_found.local));
validateLogin(adminpass, userpass, channel_name, "vote", res, function(exists) {
if(!exists) {
res.status(404).send(JSON.stringify(error.not_found.list));
return;
} else if(song[0].guids.indexOf(guid) > -1) {
res.status(409).send(JSON.stringify(error.conflicting));
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(), type: "video"}, $push :{guids: guid}}, function(err, success) {
if(authorized) {
incrementToken(token);
}
io.to(channel_name).emit("channel", {type: "vote", value: video_id, time: Functions.get_time()});
List.getNextSong(channel_name, function() {
updateTimeout(guid, res, authorized, "PUT", function(err, docs) {
var to_return = error.no_error;
to_return.results = song;
res.status(200).send(JSON.stringify(to_return));
return;
}
db.collection(channel_name).find({id: video_id, now_playing: false, type:"video"}, function(err, song) {
if(song.length == 0) {
res.status(404).send(JSON.stringify(error.not_found.local));
return;
} else if(song[0].guids.indexOf(guid) > -1) {
res.status(409).send(JSON.stringify(error.conflicting));
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(), type: "video"}, $push :{guids: guid}}, function(err, success) {
if(authorized) {
incrementToken(token);
}
io.to(channel_name).emit("channel", {type: "vote", value: video_id, time: Functions.get_time()});
List.getNextSong(channel_name, function() {
updateTimeout(guid, res, authorized, "PUT", function(err, docs) {
var to_return = error.no_error;
to_return.results = song;
res.status(200).send(JSON.stringify(to_return));
return;
});
});
});
});
}
})
}
})
});
});
});
});
@@ -420,35 +426,37 @@ router.route('/api/list/:channel_name/__np__').post(function(req, res) {
if(token_docs.length == 1 && token_docs[0].token == token) {
authorized = true;
}
checkTimeout(guid, res, authorized, "POST", function() {
if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "POST", function(err, docs) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
});
}
db.collection(channel_name).find({now_playing: true}, toShowChannel, function(err, list) {
if(list.length > 0) {
db.collection(channel_name + "_settings").find({ id: "config" }, function(err, conf) {
if(authorized) {
incrementToken(token);
}
if(conf.length == 0) {
res.status(404).send(JSON.stringify(error.not_found.list));
return;
} else if(conf[0].userpass != userpass && conf[0].userpass != "") {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
}
updateTimeout(guid, res, authorized, "POST", function(err, docs) {
var to_return = error.no_error;
to_return.results = list;
res.status(200).send(JSON.stringify(to_return));
});
checkOveruseApiToken(authorized, token_docs, res, function() {
checkTimeout(guid, res, authorized, "POST", function() {
if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "POST", function(err, docs) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
});
} else {
res.status(404).send(JSON.stringify(error.not_found.list));
}
db.collection(channel_name).find({now_playing: true}, toShowChannel, function(err, list) {
if(list.length > 0) {
db.collection(channel_name + "_settings").find({ id: "config" }, function(err, conf) {
if(authorized) {
incrementToken(token);
}
if(conf.length == 0) {
res.status(404).send(JSON.stringify(error.not_found.list));
return;
} else if(conf[0].userpass != userpass && conf[0].userpass != "") {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
}
updateTimeout(guid, res, authorized, "POST", function(err, docs) {
var to_return = error.no_error;
to_return.results = list;
res.status(200).send(JSON.stringify(to_return));
});
});
} else {
res.status(404).send(JSON.stringify(error.not_found.list));
}
});
});
});
});
@@ -502,86 +510,88 @@ router.route('/api/list/:channel_name/:video_id').post(function(req,res) {
if(token_docs.length == 1 && token_docs[0].token == token) {
authorized = true;
}
checkTimeout(guid, res, authorized, "POST", function() {
if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "POST", function(err, docs) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
});
}
var type = fetch_only ? "fetch_song" : "add";
validateLogin(adminpass, userpass, channel_name, type, res, function(exists, conf, authenticated) {
db.collection(channel_name).find({id: video_id}, function(err, result) {
if(result.length == 0 || result[0].type == "suggested") {
var song_type = authenticated ? "video" : "suggested";
if(fetch_only && result.length == 0) {
res.status(404).send(JSON.stringify(error.not_found.local));
checkOveruseApiToken(authorized, token_docs, res, function() {
checkTimeout(guid, res, authorized, "POST", function() {
if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "POST", function(err, docs) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
});
}
var type = fetch_only ? "fetch_song" : "add";
validateLogin(adminpass, userpass, channel_name, type, res, function(exists, conf, authenticated) {
db.collection(channel_name).find({id: video_id}, function(err, result) {
if(result.length == 0 || result[0].type == "suggested") {
var song_type = authenticated ? "video" : "suggested";
if(fetch_only && result.length == 0) {
res.status(404).send(JSON.stringify(error.not_found.local));
return;
}
db.collection(channel_name).find({now_playing: true}, function(err, now_playing) {
var set_np = false;
if(now_playing.length == 0 && authenticated) {
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), "type": song_type};
Search.get_correct_info(new_song, channel_name, false, function(element, found) {
if(!found) {
res.status(404).send(JSON.stringify(error.not_found.youtube));
return;
}
new_song = element;
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).update({"id": new_song.id}, new_song, {upsert: true}, function(err, success) {
if(authorized) {
incrementToken(token);
}
if(create_frontpage_lists) {
db.collection("frontpage_lists").update({"_id": channel_name, "count" : (authenticated ? 1 : 0), "frontpage": true, "accessed": Functions.get_time(), "viewers": 1}, {upsert: true}, function(err, docs) {
if(authenticated) {
io.to(channel_name).emit("channel", {type: "added", value: new_song});
} else {
io.to(channel_name).emit("suggested", new_song);
}
postEnd(channel_name, configs, new_song, guid, res, authenticated, authorized);
});
} else if(set_np) {
Frontpage.update_frontpage(channel_name, video_id, title, function() {
io.to(channel_name).emit("np", {np: [new_song], conf: [conf]});
postEnd(channel_name, configs, new_song, guid, res, authenticated, authorized);
});
} else {
db.collection("frontpage_lists").update({"_id": channel_name}, {$inc: {count: (authenticated ? 1 : 0)}}, function(err, docs) {
if(authenticated) {
io.to(channel_name).emit("channel", {type: "added", value: new_song});
} else {
io.to(channel_name).emit("suggested", new_song);
}
postEnd(channel_name, configs, new_song, guid, res, authenticated, authorized);
});
}
});
})
});
});
} else if(fetch_only) {
var to_return = error.no_error;
to_return.results = result;
res.status(200).send(JSON.stringify(to_return));
return;
} else {
res.status(409).send(JSON.stringify(error.conflicting));
return;
}
db.collection(channel_name).find({now_playing: true}, function(err, now_playing) {
var set_np = false;
if(now_playing.length == 0 && authenticated) {
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), "type": song_type};
Search.get_correct_info(new_song, channel_name, false, function(element, found) {
if(!found) {
res.status(404).send(JSON.stringify(error.not_found.youtube));
return;
}
new_song = element;
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).update({"id": new_song.id}, new_song, {upsert: true}, function(err, success) {
if(authorized) {
incrementToken(token);
}
if(create_frontpage_lists) {
db.collection("frontpage_lists").update({"_id": channel_name, "count" : (authenticated ? 1 : 0), "frontpage": true, "accessed": Functions.get_time(), "viewers": 1}, {upsert: true}, function(err, docs) {
if(authenticated) {
io.to(channel_name).emit("channel", {type: "added", value: new_song});
} else {
io.to(channel_name).emit("suggested", new_song);
}
postEnd(channel_name, configs, new_song, guid, res, authenticated, authorized);
});
} else if(set_np) {
Frontpage.update_frontpage(channel_name, video_id, title, function() {
io.to(channel_name).emit("np", {np: [new_song], conf: [conf]});
postEnd(channel_name, configs, new_song, guid, res, authenticated, authorized);
});
} else {
db.collection("frontpage_lists").update({"_id": channel_name}, {$inc: {count: (authenticated ? 1 : 0)}}, function(err, docs) {
if(authenticated) {
io.to(channel_name).emit("channel", {type: "added", value: new_song});
} else {
io.to(channel_name).emit("suggested", new_song);
}
postEnd(channel_name, configs, new_song, guid, res, authenticated, authorized);
});
}
});
})
});
});
} else if(fetch_only) {
var to_return = error.no_error;
to_return.results = result;
res.status(200).send(JSON.stringify(to_return));
return;
} else {
res.status(409).send(JSON.stringify(error.conflicting));
return;
}
});
});
});
});
@@ -707,46 +717,77 @@ router.route('/api/conf/:channel_name').post(function(req, res) {
if(token_docs.length == 1 && token_docs[0].token == token) {
authorized = true;
}
checkTimeout(guid, res, authorized, "POST", function() {
if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "DELETE", function(err, docs) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
});
}
db.collection(channel_name + "_settings").find({ id: "config" }, toShowConfig, function(err, docs) {
if(docs.length > 0 && docs[0].userpass == userpass) {
var conf = docs[0];
if(conf.adminpass != "") {
conf.adminpass = true;
} else {
conf.adminpass = false;
}
if(conf.userpass != "") {
conf.userpass = true;
} else {
conf.userpass = false;
}
if(authorized) {
incrementToken(token);
}
updateTimeout(guid, res, authorized, "POST", function(err, docs) {
var to_return = error.no_error;
to_return.results = conf;
res.status(200).send(JSON.stringify(to_return));
checkOveruseApiToken(authorized, token_docs, res, function() {
checkTimeout(guid, res, authorized, "POST", function() {
if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "DELETE", function(err, docs) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
});
} else if(docs.length > 0 && docs[0].userpass != userpass) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
} else {
res.status(404).send(JSON.stringify(error.not_found.list));
return;
}
db.collection(channel_name + "_settings").find({ id: "config" }, toShowConfig, function(err, docs) {
if(docs.length > 0 && docs[0].userpass == userpass) {
var conf = docs[0];
if(conf.adminpass != "") {
conf.adminpass = true;
} else {
conf.adminpass = false;
}
if(conf.userpass != "") {
conf.userpass = true;
} else {
conf.userpass = false;
}
if(authorized) {
incrementToken(token);
}
updateTimeout(guid, res, authorized, "POST", function(err, docs) {
var to_return = error.no_error;
to_return.results = conf;
res.status(200).send(JSON.stringify(to_return));
});
} else if(docs.length > 0 && docs[0].userpass != userpass) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
} else {
res.status(404).send(JSON.stringify(error.not_found.list));
return;
}
});
});
});
});
});
function checkOveruseApiToken(authorized, token_docs, res, callback) {
if(authorized || (authorized && token_docs[0].limit == 0)) {
callback();
return;
}
db.collection("timeout_api").find({guid: token_docs[0].token}, function(e, doc) {
if(doc.length == 1) {
var this_doc = doc[0];
var date = new Date(this_doc[0].createdAt);
date.setSeconds(date.getSeconds() + 1);
var now = new Date();
var retry_in = (date.getTime() - now.getTime()) / 1000;
if(this_doc.used > token_docs[0].limit && retry_in > 0) {
res.header({'Retry-After': retry_in});
res.status(429).send(JSON.stringify(error.tooMany));
return;
} else {
db.collection("timeout_api").update({guid: token}, {$inc: {used: 1}}, function(e, d) {
callback();
});
}
} else {
db.collection("timeout_api").insert({guid: token, used: 0, createdAt: new Date(), type: "ALL"}, function(e, d) {
callback();
});
}
});
}
router.route('/api/list/:channel_name').post(function(req, res) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
@@ -777,37 +818,39 @@ router.route('/api/list/:channel_name').post(function(req, res) {
if(token_docs.length == 1 && token_docs[0].token == token) {
authorized = true;
}
checkTimeout(guid, res, authorized, "POST", function() {
if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "POST", function(err, docs) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
});
}
db.collection(channel_name).find({views: {$exists: false}}, toShowChannel, function(err, list) {
if(list.length > 0) {
db.collection(channel_name + "_settings").find({ id: "config" }, function(err, conf) {
if(conf.length == 0) {
res.status(404).send(JSON.stringify(error.not_found.list));
return;
} else if(conf[0].userpass != userpass && conf[0].userpass != "") {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
}
if(authorized) {
incrementToken(token);
}
updateTimeout(guid, res, authorized, "POST", function(err, docs) {
var to_return = error.no_error;
to_return.results = list;
res.status(200).send(JSON.stringify(to_return));
return;
});
checkOveruseApiToken(authorized, token_docs, res, function() {
checkTimeout(guid, res, authorized, "POST", function() {
if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "POST", function(err, docs) {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
});
} else {
res.status(404).send(JSON.stringify(error.not_found.list));
return;
}
db.collection(channel_name).find({views: {$exists: false}}, toShowChannel, function(err, list) {
if(list.length > 0) {
db.collection(channel_name + "_settings").find({ id: "config" }, function(err, conf) {
if(conf.length == 0) {
res.status(404).send(JSON.stringify(error.not_found.list));
return;
} else if(conf[0].userpass != userpass && conf[0].userpass != "") {
res.status(403).send(JSON.stringify(error.not_authenticated));
return;
}
if(authorized) {
incrementToken(token);
}
updateTimeout(guid, res, authorized, "POST", function(err, docs) {
var to_return = error.no_error;
to_return.results = list;
res.status(200).send(JSON.stringify(to_return));
return;
});
});
} else {
res.status(404).send(JSON.stringify(error.not_found.list));
return;
}
});
});
});
});
@@ -831,8 +874,8 @@ router.route('/api/imageblob').post(function(req, res) {
});
});
var nodemailer = require('nodemailer');
try {
var nodemailer = require('nodemailer');
var mailconfig = require(path.join(__dirname, '../../config/mailconfig.js'));
var recaptcha_config = require(path.join(__dirname, '../../config/recaptcha.js'));
var Recaptcha = require('express-recaptcha');
@@ -840,6 +883,61 @@ try {
var RECAPTCHA_SECRET_KEY = recaptcha_config.key;
var recaptcha = new Recaptcha(RECAPTCHA_SITE_KEY, RECAPTCHA_SECRET_KEY);
router.route('/api/apply').post(recaptcha.middleware.verify, function(req, res) {
if(req.body.email == "" || req.body.email == undefined) {
res.send("failed");
return;
}
if(req.recaptcha.error == null) {
var name = req.body.email;
var id = crypto.createHash('sha256').update(uniqid()).digest('base64');
var uniqid_link = crypto.createHash('sha256').update(uniqid()).digest('hex');
token_db.collection("api_token").find({name: name}, function(err, results_find) {
if(results_find.length > 0) {
res.send("failed");
return;
}
token_db.collection("api_token").insert({name: name, token: id, usage: 0, active: false}, function(err, docs){
token_db.collection("api_links").insert({id: uniqid_link, token: id, createdAt: new Date()}, function(err, docs) {
let transporter = nodemailer.createTransport(mailconfig);
transporter.verify(function(error, success) {
if (error) {
token_db.collection("api_links").remove({id: uniqid_link}, function(e,d) {
res.send("failed");
return;
})
} else {
var subject = 'ZOFF: API-key';
var message = "Link to API-key: <a href='https://zoff.me/api/apply/" + uniqid_link + "'/>https://zoff.me/api/apply/" + uniqid_link + "</a>\n\nThis link expires in 1 day.";
var msg = {
from: mailconfig.from,
to: name,
subject: subject,
text: message,
html: message,
}
transporter.sendMail(msg, (error, info) => {
if (error) {
res.status(400).send("failed");
transporter.close();
return;
}
res.status(200).send("success");
transporter.close();
return;
});
}
});
})
});
})
} else {
res.send("failed");
return;
}
});
router.route('/api/mail').post(recaptcha.middleware.verify, function(req, res) {
if(req.recaptcha.error == null) {
let transporter = nodemailer.createTransport(mailconfig);
@@ -916,7 +1014,7 @@ function checkTimeout(guid, res, authorized, type, callback) {
}, function(err, docs) {
if(docs.length > 0) {
var date = new Date(docs[0].createdAt);
date.setSeconds(date.getSeconds() + 2);
date.setSeconds(date.getSeconds() + 1);
var now = new Date();
var retry_in = (date.getTime() - now.getTime()) / 1000;
if(retry_in > 0) {

View File

@@ -4,6 +4,8 @@ var path = require('path');
var year = new Date().getYear()+1900;
var path = require('path');
var analytics = "xx";
var mongojs = require('mongojs');
var token_db = mongojs("tokens");
try {
analytics = require(path.join(path.join(__dirname, '../../config/'), 'analytics.js'));
} catch(e) {
@@ -44,6 +46,52 @@ router.route('/').post(function(req, res, next){
root(req, res, next);
});
router.route('/api/apply/:id').get(function(req,res) {
var id = req.params.id;
token_db.collection('api_links').find({id: id}, function(err, result) {
if(result.length == 1) {
token_db.collection('api_links').remove({id: id}, function(e,d) {
token_db.collection('api_token').update({id: result[0].token}, {$set: {active: true }}, function(e,d) {
var data = {
year: year,
javascript_file: "token.min.js",
captcha: res.recaptcha,
analytics: analytics,
activated: true,
token: result[0].token,
correct: true,
}
res.render('layouts/client/token', data);
});
})
} else {
var data = {
year: year,
javascript_file: "token.min.js",
captcha: res.recaptcha,
analytics: analytics,
activated: false,
id:"",
correct: false,
}
res.render('layouts/client/token', data);
}
});
});
router.route('/api/apply').get(function(req, res, next) {
var data = {
year: year,
javascript_file: "token.min.js",
captcha: res.recaptcha,
analytics: analytics,
activated: false,
id: "",
correct: false,
}
res.render('layouts/client/token', data);
});
function root(req, res, next) {
try{