"use strict"; var cmn=require("../$common.js"), persist=require("node-persist"), bcrypt=require("bcrypt"), basicAuth=require("basic-auth"), fs=require("fs"); var bcryptHashRounds=10; var moddir=null; persist=persist.create({ dir:"persist/todo", continuous:false, interval:false }); persist.initSync(); //tasks: {"user": [{id: Int,subject: String,repweeks: Int,date: Date}]} //accounts: {"user": hash (String)} //repweeks==0 implies no repetition var nextid=persist.getItemSync("nextid"); if(nextid==null){ nextid=1; persist.setItemSync("nextid",nextid); } var tasks=persist.getItemSync("tasks"); (function(){ if(tasks==null){ tasks={}; persist.setItemSync("tasks",tasks); } else { for(var user in tasks){ for(var task of tasks[user]){ task.date=new Date(task.date); if(nextid<=task.id)nextid=task.id+1; } } persist.setItemSync("nextid",nextid); } })(); var accounts=persist.getItemSync("accounts"); if(accounts==null){ accounts={}; persist.setItemSync("accounts",accounts); } var naccounts=0; (function(){ var user; for(user in accounts)naccounts++; })(); function shiftDate(date,repweeks){ var Y=date.getFullYear(), M=date.getMonth(), D=date.getDate(), h=date.getHours(), m=date.getMinutes(), s=date.getSeconds(), ms=date.getMilliseconds(); return new Date(Y,M,D+7*repweeks,h,m,s,ms); } function sendUnauth(res){ res.set("WWW-Authenticate","Basic realm=Authorization required"); return res.sendStatus(401); } function unknownUserHandler(req,res,next){ res.sendFile(moddir+"/unknownuser.html"); } function authMiddleware(req,res,next){ var user=basicAuth(req); req.authuser=null; if(!user||!user.name){ sendUnauth(res); return; } req.authuser=user.name; if(accounts[req.authuser]){ bcrypt.compare(user.pass,accounts[req.authuser],function(err,ok){ if(ok)next(); else sendUnauth(res); }); } else { unknownUserHandler(req,res,next); } } function asciiValid(str){ var i,c; for(i=0;i=127)return false; } return true; } module.exports=function(app,io,_moddir){ moddir=_moddir; //first the endpoints that need to bypass authMiddleware app.get("/todo/authfail",function(req,res){ sendUnauth(res); }); app.post("/todo/createuser",function(req,res){ var user=basicAuth(req); if(!user||!user.name){ res.status(400).send("No credentials sent"); return; } if(user.name.length<3||user.name.length>32||!asciiValid(user.name)){ res.status(400).send("Invalid username"); return; } if(user.pass.length<3||user.pass.length>32||!asciiValid(user.pass)){ res.status(400).send("Invalid password"); return; } if(accounts[user.name]){ res.status(400).send("User already exists"); return; } if(naccounts>=20){ res.status(500).send("Too many accounts created, please contact Tom..."); return; } bcrypt.hash(user.pass,bcryptHashRounds,function(err,hash){ if(!hash){ res.status(500).send("Something went wrong..."); console.log(err); return; } accounts[user.name]=hash; tasks[user.name]=[]; persist.setItemSync("accounts",accounts); persist.setItemSync("tasks",tasks); res.status(200).end(); }); }); app.all(["/todo","/todo/*"],authMiddleware); //for all the other endpoints app.get("/todo",function(req,res){ var contents=fs.readFileSync(moddir+"/todo.html","utf8"); var replaced=contents.replace("/*REPLACEME*/null/*TODOLIST*/", JSON.stringify(tasks[req.authuser])); res.send(replaced); }); app.get("/todo/list",function(req,res){ res.json(tasks[req.authuser]); }); app.delete("/todo/task",function(req,res){ var id=+req.body; var i; var fail=false; var usertasks=tasks[req.authuser]; if(id<0||~~id!=id||isNaN(id)||!usertasks){ fail=true; } else { for(i=0;i=40){ res.status(400).send("Isn't 40 tasks enough for you?"); return; } tasks[req.authuser].push({ id:nextid++, subject:subject, repweeks:repweeks, date:date }); persist.setItemSync("tasks",tasks); persist.setItemSync("nextid",nextid); res.status(200).end(); }); };