mirror of
				https://github.com/KevinMidboe/zoff.git
				synced 2025-10-29 18:00:23 +00:00 
			
		
		
		
	Origin for api-tokens
This commit is contained in:
		| @@ -38,7 +38,9 @@ var bodyParser = require('body-parser'); | |||||||
| var cookieParser = require("cookie-parser"); | var cookieParser = require("cookie-parser"); | ||||||
| var cookies = require("cookie"); | var cookies = require("cookie"); | ||||||
| var helmet = require('helmet') | var helmet = require('helmet') | ||||||
| app.use(helmet()) | app.use(helmet({ | ||||||
|  |   frameguard: false | ||||||
|  | })); | ||||||
| app.use( bodyParser.json() );       // to support JSON-encoded bodies | app.use( bodyParser.json() );       // to support JSON-encoded bodies | ||||||
| app.use(bodyParser.urlencoded({     // to support URL-encoded bodies | app.use(bodyParser.urlencoded({     // to support URL-encoded bodies | ||||||
| 	extended: true | 	extended: true | ||||||
|   | |||||||
| @@ -168,6 +168,7 @@ function get_list_ajax() { | |||||||
|         type: "POST", |         type: "POST", | ||||||
|         data: { |         data: { | ||||||
|             userpass: "", |             userpass: "", | ||||||
|  |             token: zoff_api_token, | ||||||
|         }, |         }, | ||||||
|         url: "/api/list/" + chan.toLowerCase(), |         url: "/api/list/" + chan.toLowerCase(), | ||||||
|         success: function(response) { |         success: function(response) { | ||||||
| @@ -242,7 +243,8 @@ function get_np_ajax() { | |||||||
|         type: "POST", |         type: "POST", | ||||||
|         data: { |         data: { | ||||||
|             userpass: "", |             userpass: "", | ||||||
|             fetch_song: true |             fetch_song: true, | ||||||
|  |             token: zoff_api_token | ||||||
|         }, |         }, | ||||||
|         url: "/api/list/" + chan.toLowerCase() + "/__np__", |         url: "/api/list/" + chan.toLowerCase() + "/__np__", | ||||||
|         success: function(response) { |         success: function(response) { | ||||||
| @@ -269,7 +271,8 @@ function del_ajax(id) { | |||||||
|         type: "DELETE", |         type: "DELETE", | ||||||
|         data: { |         data: { | ||||||
|             adminpass: "", |             adminpass: "", | ||||||
|             userpass: "" |             userpass: "", | ||||||
|  |             token: zoff_api_token | ||||||
|         }, |         }, | ||||||
|         url: "/api/list/" + chan.toLowerCase() + "/" + id, |         url: "/api/list/" + chan.toLowerCase() + "/" + id, | ||||||
|         success: function(response) { |         success: function(response) { | ||||||
| @@ -301,6 +304,7 @@ function add_ajax(id, title, duration, playlist, num, full_num, start, end) { | |||||||
|             duration: duration, |             duration: duration, | ||||||
|             end_time: end, |             end_time: end, | ||||||
|             start_time: start, |             start_time: start, | ||||||
|  |             token: zoff_api_token | ||||||
|         }, |         }, | ||||||
|         url: "/api/list/" + chan.toLowerCase() + "/" + id, |         url: "/api/list/" + chan.toLowerCase() + "/" + id, | ||||||
|         success: function(response) { |         success: function(response) { | ||||||
| @@ -327,7 +331,8 @@ function vote_ajax(id) { | |||||||
|         type: "PUT", |         type: "PUT", | ||||||
|         data: { |         data: { | ||||||
|             adminpass: "", |             adminpass: "", | ||||||
|             userpass: "" |             userpass: "", | ||||||
|  |             token: zoff_api_token | ||||||
|         }, |         }, | ||||||
|         url: "/api/list/" + chan.toLowerCase() + "/" + id, |         url: "/api/list/" + chan.toLowerCase() + "/" + id, | ||||||
|         success: function(response) { |         success: function(response) { | ||||||
|   | |||||||
| @@ -29,6 +29,7 @@ var showDiscovery						= false; | |||||||
| var player_ready 	   	  		= false; | var player_ready 	   	  		= false; | ||||||
| var viewers 			  		= 1; | var viewers 			  		= 1; | ||||||
| var temp_user_pass 				= ""; | var temp_user_pass 				= ""; | ||||||
|  | var zoff_api_token = "AhmC4Yg2BhaWPZBXeoWK96DAiAVfbou8TUG2IXtD3ZQ="; | ||||||
| var retry_frontpage; | var retry_frontpage; | ||||||
| var chromecast_specs_sent = false; | var chromecast_specs_sent = false; | ||||||
| var dragging 					= false; | var dragging 					= false; | ||||||
|   | |||||||
| @@ -16,6 +16,8 @@ $(document).ready(function() { | |||||||
|     $(".token-form").on("submit", function(e) { |     $(".token-form").on("submit", function(e) { | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|         var email = $("#email_address").val(); |         var email = $("#email_address").val(); | ||||||
|  |         var origin = $("#origin").val(); | ||||||
|  |         $("#origin").attr("readonly", true); | ||||||
|         $("#email_address").attr("readonly", true); |         $("#email_address").attr("readonly", true); | ||||||
|         $(".submit").toggleClass("disabled"); |         $(".submit").toggleClass("disabled"); | ||||||
|         $(".full-form-token").removeClass("hide"); |         $(".full-form-token").removeClass("hide"); | ||||||
| @@ -24,6 +26,7 @@ $(document).ready(function() { | |||||||
|             type: "POST", |             type: "POST", | ||||||
|             url: "/api/apply", |             url: "/api/apply", | ||||||
|             data: { |             data: { | ||||||
|  |                 origin: origin, | ||||||
|                 email: email, |                 email: email, | ||||||
|                 "g-recaptcha-response": captcha_response, |                 "g-recaptcha-response": captcha_response, | ||||||
|             }, |             }, | ||||||
| @@ -34,6 +37,7 @@ $(document).ready(function() { | |||||||
|                 } else { |                 } else { | ||||||
|                     $("#email_address").attr("readonly", false); |                     $("#email_address").attr("readonly", false); | ||||||
|                     $(".submit").toggleClass("disabled"); |                     $(".submit").toggleClass("disabled"); | ||||||
|  |                     $("#origin").attr("readonly", false); | ||||||
|                     grecaptcha.reset(); |                     grecaptcha.reset(); | ||||||
|                     Materialize.toast("Something went wrong. Sure that email hasn't been used for another token?", 3000, "red lighten"); |                     Materialize.toast("Something went wrong. Sure that email hasn't been used for another token?", 3000, "red lighten"); | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -11,8 +11,6 @@ | |||||||
|             {{/if}} |             {{/if}} | ||||||
|             {{> channel/tabs}} |             {{> channel/tabs}} | ||||||
|         </div> |         </div> | ||||||
|         <!--<div id="playbar"> |  | ||||||
|         </div>--> |  | ||||||
|     </main> |     </main> | ||||||
| </div> | </div> | ||||||
| <ul class="context-menu-list context-menu-root hide"> | <ul class="context-menu-list context-menu-root hide"> | ||||||
|   | |||||||
| @@ -12,20 +12,6 @@ | |||||||
|         </div> |         </div> | ||||||
|     </nav> |     </nav> | ||||||
|     {{> modal/about}} |     {{> modal/about}} | ||||||
|     <div id="donation" class="modal"> |  | ||||||
|         <div class="modal-content"> |  | ||||||
|             <h4>Thanks!</h4> |  | ||||||
|             <p>Thanks for your donation, we love you <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> | </header> | ||||||
| <div id="main-container" class="token-container"> | <div id="main-container" class="token-container"> | ||||||
|     <main class="center-align container"> |     <main class="center-align container"> | ||||||
| @@ -41,6 +27,7 @@ | |||||||
|                 <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>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>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 a limit of 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> |                 <p>As of now, the tokens have a limit of 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> | ||||||
|  |                 <p>If you want to restrict the token for use on one domain only, you can change the Origin from * to the website of your choice (if you want for https://zoff.me, then you'd input <b>zoff.me</b>).</p> | ||||||
|             {{/if}} |             {{/if}} | ||||||
|         </div> |         </div> | ||||||
|         {{#if activated}} |         {{#if activated}} | ||||||
| @@ -50,7 +37,11 @@ | |||||||
|             <div class="row center"> |             <div class="row center"> | ||||||
|               <div class="input-field col s6 offset-s3"> |               <div class="input-field col s6 offset-s3"> | ||||||
|                   <label for="email_address" class="noselect">Email</label> |                   <label for="email_address" class="noselect">Email</label> | ||||||
|                 <input type="email" autocomplete="off" class="validate" id="email_address" /> |                   <input type="email" autocomplete="off" class="validate" id="email_address" /> | ||||||
|  |               </div> | ||||||
|  |               <div class="input-field col s6 offset-s3"> | ||||||
|  |                   <label for="origin" class="noselect">Origin</label> | ||||||
|  |                   <input type="text" autocomplete="off" id="origin" value="*" /> | ||||||
|               </div> |               </div> | ||||||
|             </div> |             </div> | ||||||
|             <div class="col offset-m3"> |             <div class="col offset-m3"> | ||||||
|   | |||||||
| @@ -75,7 +75,7 @@ var error = { | |||||||
|         results: [], |         results: [], | ||||||
|     }, |     }, | ||||||
|     wrong_token: { |     wrong_token: { | ||||||
|         status: 403, |         status: 400, | ||||||
|         error: "You're using a faulty token. Try getting a new token, or send the request without the token.", |         error: "You're using a faulty token. Try getting a new token, or send the request without the token.", | ||||||
|         success: false, |         success: false, | ||||||
|         results: [], |         results: [], | ||||||
| @@ -185,14 +185,18 @@ router.route('/api/list/:channel_name/:video_id').delete(function(req, res) { | |||||||
|         } |         } | ||||||
|         token_db.collection("api_token").find({token: token}, function(err, token_docs) { |         token_db.collection("api_token").find({token: token}, function(err, token_docs) { | ||||||
|             var authorized = false; |             var authorized = false; | ||||||
|             if(token_docs.length == 1 && token_docs[0].token == token) { |             var origin; | ||||||
|  |             try { | ||||||
|  |                 origin = req.headers.referer.split("/")[2]; | ||||||
|  |             } catch(e) { origin = ""; } | ||||||
|  |             if(token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || token_docs[0].origin == origin)) { | ||||||
|                 authorized = true; |                 authorized = true; | ||||||
|             } |             } | ||||||
|             checkOveruseApiToken(authorized, token_docs, res, function() { |             checkOveruseApiToken(authorized, token_docs, res, function() { | ||||||
|                 checkTimeout(guid, res, authorized, "DELETE", function() { |                 checkTimeout(guid, res, authorized, "DELETE", function() { | ||||||
|                     if(token != "" && !authorized) { |                     if(token != "" && !authorized) { | ||||||
|                         updateTimeout(guid, res, authorized, "DELETE", function(err, docs) { |                         updateTimeout(guid, res, authorized, "DELETE", function(err, docs) { | ||||||
|                             res.status(403).send(JSON.stringify(error.wrong_token)); |                             res.status(400).send(JSON.stringify(error.wrong_token)); | ||||||
|                             return; |                             return; | ||||||
|                         }); |                         }); | ||||||
|                     } else { |                     } else { | ||||||
| @@ -336,14 +340,18 @@ router.route('/api/conf/:channel_name').put(function(req, res) { | |||||||
|         } |         } | ||||||
|         token_db.collection("api_token").find({token: token}, function(err, token_docs) { |         token_db.collection("api_token").find({token: token}, function(err, token_docs) { | ||||||
|             var authorized = false; |             var authorized = false; | ||||||
|             if(token_docs.length == 1 && token_docs[0].token == token) { |             var origin; | ||||||
|  |             try { | ||||||
|  |                 origin = req.headers.referer.split("/")[2]; | ||||||
|  |             } catch(e) { origin = ""; } | ||||||
|  |             if(token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || token_docs[0].origin == origin)) { | ||||||
|                 authorized = true; |                 authorized = true; | ||||||
|             } |             } | ||||||
|             checkOveruseApiToken(authorized, token_docs, res, function() { |             checkOveruseApiToken(authorized, token_docs, res, function() { | ||||||
|                 checkTimeout(guid, res, authorized, "CONFIG", function() { |                 checkTimeout(guid, res, authorized, "CONFIG", function() { | ||||||
|                     if(token != "" && !authorized) { |                     if(token != "" && !authorized) { | ||||||
|                         updateTimeout(guid, res, authorized, "CONFIG", function(err, docs) { |                         updateTimeout(guid, res, authorized, "CONFIG", function(err, docs) { | ||||||
|                             res.status(403).send(JSON.stringify(error.wrong_token)); |                             res.status(400).send(JSON.stringify(error.wrong_token)); | ||||||
|                             return; |                             return; | ||||||
|                         }); |                         }); | ||||||
|                     } else { |                     } else { | ||||||
| @@ -459,14 +467,18 @@ router.route('/api/list/:channel_name/:video_id').put(function(req,res) { | |||||||
|         } |         } | ||||||
|         token_db.collection("api_token").find({token: token}, function(err, token_docs) { |         token_db.collection("api_token").find({token: token}, function(err, token_docs) { | ||||||
|             var authorized = false; |             var authorized = false; | ||||||
|             if(token_docs.length == 1 && token_docs[0].token == token) { |             var origin; | ||||||
|  |             try { | ||||||
|  |                 origin = req.headers.referer.split("/")[2]; | ||||||
|  |             } catch(e) { origin = ""; } | ||||||
|  |             if(token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || token_docs[0].origin == origin)) { | ||||||
|                 authorized = true; |                 authorized = true; | ||||||
|             } |             } | ||||||
|             checkOveruseApiToken(authorized, token_docs, res, function() { |             checkOveruseApiToken(authorized, token_docs, res, function() { | ||||||
|                 checkTimeout(guid, res, authorized, "PUT", function() { |                 checkTimeout(guid, res, authorized, "PUT", function() { | ||||||
|                     if(token != "" && !authorized) { |                     if(token != "" && !authorized) { | ||||||
|                         updateTimeout(guid, res, authorized, "PUT", function(err, docs) { |                         updateTimeout(guid, res, authorized, "PUT", function(err, docs) { | ||||||
|                             res.status(403).send(JSON.stringify(error.wrong_token)); |                             res.status(400).send(JSON.stringify(error.wrong_token)); | ||||||
|                             return; |                             return; | ||||||
|                         }); |                         }); | ||||||
|                     } else { |                     } else { | ||||||
| @@ -549,14 +561,18 @@ router.route('/api/list/:channel_name/__np__').post(function(req, res) { | |||||||
|         } |         } | ||||||
|         token_db.collection("api_token").find({token: token}, function(err, token_docs) { |         token_db.collection("api_token").find({token: token}, function(err, token_docs) { | ||||||
|             var authorized = false; |             var authorized = false; | ||||||
|             if(token_docs.length == 1 && token_docs[0].token == token) { |             var origin; | ||||||
|  |             try { | ||||||
|  |                 origin = req.headers.referer.split("/")[2]; | ||||||
|  |             } catch(e) { origin = ""; } | ||||||
|  |             if(token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || token_docs[0].origin == origin)) { | ||||||
|                 authorized = true; |                 authorized = true; | ||||||
|             } |             } | ||||||
|             checkOveruseApiToken(authorized, token_docs, res, function() { |             checkOveruseApiToken(authorized, token_docs, res, function() { | ||||||
|                 checkTimeout(guid, res, authorized, "POST", function() { |                 checkTimeout(guid, res, authorized, "POST", function() { | ||||||
|                     if(token != "" && !authorized) { |                     if(token != "" && !authorized) { | ||||||
|                         updateTimeout(guid, res, authorized, "POST", function(err, docs) { |                         updateTimeout(guid, res, authorized, "POST", function(err, docs) { | ||||||
|                             res.status(403).send(JSON.stringify(error.wrong_token)); |                             res.status(400).send(JSON.stringify(error.wrong_token)); | ||||||
|                             return; |                             return; | ||||||
|                         }); |                         }); | ||||||
|                     } else { |                     } else { | ||||||
| @@ -672,14 +688,18 @@ router.route('/api/list/:channel_name/:video_id').post(function(req,res) { | |||||||
|         } |         } | ||||||
|         token_db.collection("api_token").find({token: token}, function(err, token_docs) { |         token_db.collection("api_token").find({token: token}, function(err, token_docs) { | ||||||
|             var authorized = false; |             var authorized = false; | ||||||
|             if(token_docs.length == 1 && token_docs[0].token == token) { |             var origin; | ||||||
|  |             try { | ||||||
|  |                 origin = req.headers.referer.split("/")[2]; | ||||||
|  |             } catch(e) { origin = ""; } | ||||||
|  |             if(token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || token_docs[0].origin == origin)) { | ||||||
|                 authorized = true; |                 authorized = true; | ||||||
|             } |             } | ||||||
|             checkOveruseApiToken(authorized, token_docs, res, function() { |             checkOveruseApiToken(authorized, token_docs, res, function() { | ||||||
|                 checkTimeout(guid, res, authorized, "POST", function() { |                 checkTimeout(guid, res, authorized, "POST", function() { | ||||||
|                     if(token != "" && !authorized) { |                     if(token != "" && !authorized) { | ||||||
|                         updateTimeout(guid, res, authorized, "POST", function(err, docs) { |                         updateTimeout(guid, res, authorized, "POST", function(err, docs) { | ||||||
|                             res.status(403).send(JSON.stringify(error.wrong_token)); |                             res.status(400).send(JSON.stringify(error.wrong_token)); | ||||||
|                             return; |                             return; | ||||||
|                         }); |                         }); | ||||||
|                     } else { |                     } else { | ||||||
| @@ -897,14 +917,18 @@ router.route('/api/conf/:channel_name').post(function(req, res) { | |||||||
|  |  | ||||||
|         token_db.collection("api_token").find({token: token}, function(err, token_docs) { |         token_db.collection("api_token").find({token: token}, function(err, token_docs) { | ||||||
|             var authorized = false; |             var authorized = false; | ||||||
|             if(token_docs.length == 1 && token_docs[0].token == token) { |             var origin; | ||||||
|  |             try { | ||||||
|  |                 origin = req.headers.referer.split("/")[2]; | ||||||
|  |             } catch(e) { origin = ""; } | ||||||
|  |             if(token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || token_docs[0].origin == origin)) { | ||||||
|                 authorized = true; |                 authorized = true; | ||||||
|             } |             } | ||||||
|             checkOveruseApiToken(authorized, token_docs, res, function() { |             checkOveruseApiToken(authorized, token_docs, res, function() { | ||||||
|                 checkTimeout(guid, res, authorized, "POST", function() { |                 checkTimeout(guid, res, authorized, "POST", function() { | ||||||
|                     if(token != "" && !authorized) { |                     if(token != "" && !authorized) { | ||||||
|                         updateTimeout(guid, res, authorized, "DELETE", function(err, docs) { |                         updateTimeout(guid, res, authorized, "DELETE", function(err, docs) { | ||||||
|                             res.status(403).send(JSON.stringify(error.wrong_token)); |                             res.status(400).send(JSON.stringify(error.wrong_token)); | ||||||
|                             return; |                             return; | ||||||
|                         }); |                         }); | ||||||
|                     } else { |                     } else { | ||||||
| @@ -1016,14 +1040,18 @@ router.route('/api/list/:channel_name').post(function(req, res) { | |||||||
|  |  | ||||||
|         token_db.collection("api_token").find({token: token}, function(err, token_docs) { |         token_db.collection("api_token").find({token: token}, function(err, token_docs) { | ||||||
|             var authorized = false; |             var authorized = false; | ||||||
|             if(token_docs.length == 1 && token_docs[0].token == token) { |             var origin; | ||||||
|  |             try { | ||||||
|  |                 origin = req.headers.referer.split("/")[2]; | ||||||
|  |             } catch(e) { origin = ""; } | ||||||
|  |             if(token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || token_docs[0].origin == origin)) { | ||||||
|                 authorized = true; |                 authorized = true; | ||||||
|             } |             } | ||||||
|             checkOveruseApiToken(authorized, token_docs, res, function() { |             checkOveruseApiToken(authorized, token_docs, res, function() { | ||||||
|                 checkTimeout(guid, res, authorized, "POST", function() { |                 checkTimeout(guid, res, authorized, "POST", function() { | ||||||
|                     if(token != "" && !authorized) { |                     if(token != "" && !authorized) { | ||||||
|                         updateTimeout(guid, res, authorized, "POST", function(err, docs) { |                         updateTimeout(guid, res, authorized, "POST", function(err, docs) { | ||||||
|                             res.status(403).send(JSON.stringify(error.wrong_token)); |                             res.status(400).send(JSON.stringify(error.wrong_token)); | ||||||
|                             return; |                             return; | ||||||
|                         }); |                         }); | ||||||
|                     } else { |                     } else { | ||||||
| @@ -1092,6 +1120,8 @@ try { | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         if(req.recaptcha.error == null) { |         if(req.recaptcha.error == null) { | ||||||
|  |             var origin = "*"; | ||||||
|  |             if(req.body.origin != undefined && req.body.origin != "") origin = req.body.origin; | ||||||
|             var name = req.body.email; |             var name = req.body.email; | ||||||
|             var id = crypto.createHash('sha256').update(uniqid()).digest('base64'); |             var id = crypto.createHash('sha256').update(uniqid()).digest('base64'); | ||||||
|             var uniqid_link = crypto.createHash('sha256').update(uniqid()).digest('hex'); |             var uniqid_link = crypto.createHash('sha256').update(uniqid()).digest('hex'); | ||||||
| @@ -1102,7 +1132,7 @@ try { | |||||||
|                 } |                 } | ||||||
|                 token_db.collection("api_links").find({token: token}, function(e, d) { |                 token_db.collection("api_links").find({token: token}, function(e, d) { | ||||||
|                     if(results_find.length == 0 || (d.length == 0 && results_find.length > 0 && !results_find[0].active)) { |                     if(results_find.length == 0 || (d.length == 0 && results_find.length > 0 && !results_find[0].active)) { | ||||||
|                         token_db.collection("api_token").insert({name: name, token: id, usage: 0, active: false, limit: 100}, function(err, docs){ |                         token_db.collection("api_token").insert({name: name, origin: origin, token: id, usage: 0, active: false, limit: 100}, function(err, docs){ | ||||||
|                            token_db.collection("api_links").insert({id: uniqid_link, token: id, createdAt: new Date()}, 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); |                                let transporter = nodemailer.createTransport(mailconfig); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user