changes
This commit is contained in:
230
foftickets.js
230
foftickets.js
@@ -10,6 +10,7 @@ app.set('view engine','ejs');
|
||||
app.use(express.json());
|
||||
app.use(express.static('public'));
|
||||
const PORT = 3000;
|
||||
const MainURL ="http://localhost:3000";
|
||||
const PWSalt ="!SaltyMagic7283715374";
|
||||
const EmailSalt="!SaltyMagic3562196239";
|
||||
const QRSalt ="!SaltyMagic5392370662";
|
||||
@@ -26,8 +27,13 @@ const QRSalt ="!SaltyMagic5392370662";
|
||||
// + Claim Any Tickets
|
||||
// + Purge Revoked Function (Admin)
|
||||
// + Issue Tickets from Camp Admin Page
|
||||
// See how much each camp/ticket has paid (Admin)
|
||||
// Make one Update button on manytickets (User)
|
||||
// Make one Update button on editcamp (Admin)
|
||||
// Send email when new tickets are issued/offered
|
||||
// Magic-link Login System
|
||||
// Deactivate individual magic links (User)
|
||||
// Deactivate individual magic links (Admin)
|
||||
// See how much each camp/ticket has paid (Admin)
|
||||
//
|
||||
// ToDo, Later
|
||||
// + Use a templating engine
|
||||
@@ -53,7 +59,7 @@ const QRSalt ="!SaltyMagic5392370662";
|
||||
|
||||
function hashEmail(email) {
|
||||
const hash0=crypto.createHash('sha256');
|
||||
const usersalt=email in users ? (linksalt in users[email] ? users[email].linksalt : "") : "";
|
||||
const usersalt=email in users ? (users[email].linksalt ? users[email].linksalt : "") : "";
|
||||
const hash1=hash0.update(email+EmailSalt+usersalt);
|
||||
const hash=hash1.digest("base64");
|
||||
return(hash);
|
||||
@@ -73,25 +79,46 @@ function hashQR(t,ownername) {
|
||||
return(hash);
|
||||
}
|
||||
|
||||
function GetMagicLink(email) {
|
||||
return MainURL+"/login?u="+email+"&h="+hashEmail(email);
|
||||
}
|
||||
|
||||
function MagicLinkValid(email,hash) {
|
||||
if (HasPW(email)) return false;
|
||||
return hashEmail(email)==hash;
|
||||
}
|
||||
|
||||
//
|
||||
// In-memory data structures
|
||||
//
|
||||
// There are two ways to log in: with a username/password or with a token.
|
||||
//
|
||||
const users = { "teppy@egenesis.com" : { password: hashPW("x6321872X.1"), superuser:true, linksalt:"" },
|
||||
"fallsonfire@gmail.com" : { password: hashPW("indiantreeonfire"), superuser:false, linksalt:"boobsarefun" }
|
||||
};
|
||||
|
||||
//let users = { "teppy@egenesis.com" : { password: hashPW("x6321872X.1"), superuser:true, linksalt:"" },
|
||||
// "fallsonfire@gmail.com" : { password: hashPW("indiantreeonfire"), superuser:false, linksalt:"boobsarefun" }
|
||||
// };
|
||||
//
|
||||
// Status can be "i"=issued, "u"=used, "r"=revoked"
|
||||
const tickets = { "habitat-1" : { owner: "teppyx@egenesis.com" , offered: "", paid: 0.00, status:"i" },
|
||||
"habitat-2" : { owner: "teppyx@egenesis.com" , offered: "", paid: 0.00, status:"u" },
|
||||
"habitat-3" : { owner: "ginachen94@gmail.com", offered: "teppyx@egenesis.com", paid: 0.00, status:"i" },
|
||||
"habitat-4" : { owner: "teppyx@egenesis.com" , offered: "noahstern00@gmail.com", paid: 0.00, status:"i" },
|
||||
"habitat-5" : { owner: "teppyx@egenesis.com" , offered: "", paid: 0.00, status:"r" },
|
||||
"habitat-6" : { owner: "teppyx@egenesis.com" , offered: "", paid: 0.00, status:"i" },
|
||||
};
|
||||
// const tickets = { "habitat-1" : { owner: "teppy@egenesis.com" , offered: "" , paid: 0.00, status:"i" },
|
||||
// "habitat-2" : { owner: "teppy@egenesis.com" , offered: "" , paid: 0.00, status:"u" },
|
||||
// "habitat-3" : { owner: "ginachen94@gmail.com", offered: "teppy@egenesis.com" , paid: 0.00, status:"i" },
|
||||
// "habitat-4" : { owner: "teppy@egenesis.com" , offered: "noahstern00@gmail.com", paid: 0.00, status:"i" },
|
||||
// "habitat-5" : { owner: "teppy@egenesis.com" , offered: "" , paid: 0.00, status:"r" },
|
||||
// "habitat-6" : { owner: "teppy@egenesis.com" , offered: "" , paid: 0.00, status:"i" },
|
||||
// };
|
||||
// const camps = { "habitat": { leader: "teppy@egenesis.com", lastid:6 } };
|
||||
|
||||
const camps = { "habitat": { leader: "teppy@egenesis.com", lastid:6 } };
|
||||
const users={};
|
||||
const tickets={};
|
||||
const camps={};
|
||||
|
||||
function InitDatabase() {
|
||||
for (const key in users ) delete users[key];
|
||||
for (const key in tickets) delete tickets[key];
|
||||
for (const key in camps ) delete camps[key];
|
||||
users["teppy@egenesis.com" ]= { password: hashPW("x6321872X.1"), superuser:true, linksalt:"" };
|
||||
users["fallsonfire@gmail.com"]= { password: hashPW("indiantreeonfire"), superuser:false, linksalt:"" };
|
||||
}
|
||||
InitDatabase();
|
||||
|
||||
// Middleware setup
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
@@ -164,7 +191,6 @@ app.get('/camps',requireSuperUser, (req,res) => {
|
||||
})
|
||||
|
||||
app.post("/camps",requireSuperUser,(req,res) => {
|
||||
console.log("New camp: ");
|
||||
const campname=req.body.campname;
|
||||
const leader=req.body.leader;
|
||||
const qty=Number(req.body.qty);
|
||||
@@ -173,23 +199,11 @@ app.post("/camps",requireSuperUser,(req,res) => {
|
||||
camps[campname].lastid+=1; // LOG
|
||||
tickets[campname+'-'+camps[campname].lastid]={ owner: "", offered: leader, status:"i" }; // LOG
|
||||
}
|
||||
EmailTickets(leader);
|
||||
return res.redirect("/camps");
|
||||
})
|
||||
|
||||
|
||||
app.get('/camplead', requireLogin, (req,res) => {
|
||||
let username=req.session.username;
|
||||
const edit={ username:username, superuser:req.session.superuser, tickets: {} };
|
||||
for (const t in tickets) if (tickets[t].owner==username && tickets[t].status!='r') {
|
||||
edit.tickets[t]={};
|
||||
edit.tickets[t].owner=tickets[t].owner;
|
||||
edit.tickets[t].offered=tickets[t].offered;
|
||||
edit.tickets[t].used=tickets[t].status=='u';
|
||||
}
|
||||
return res.render("camplead",edit);
|
||||
})
|
||||
|
||||
|
||||
app.get('/editcamp', requireSuperUser, (req,res) => {
|
||||
let campname=req.query.campname;
|
||||
const edit={ username:req.session.username, superuser:req.session.superuser, campname:campname, leader:camps[campname].leader, tickets: {} };
|
||||
@@ -210,7 +224,6 @@ app.get('/editcamp', requireSuperUser, (req,res) => {
|
||||
app.post('/moretickets', requireSuperUser, (req,res) => {
|
||||
const qty=Number(req.body.qty);
|
||||
const campname=req.body.campname;
|
||||
console.log("More tickets! ",campname in camps,qty);
|
||||
if (campname in camps) for (let i=0; i<qty; i++) {
|
||||
camps[campname].lastid++;
|
||||
tickets[campname+'-'+camps[campname].lastid]={ owner:"", offered:camps[campname].leader, paid:0, status:"i" };
|
||||
@@ -232,7 +245,6 @@ app.get('/manytickets', requireLogin, (req,res) => {
|
||||
|
||||
|
||||
app.get('/mytickets',requireLogin, async (req,res)=> {
|
||||
console.log("In mytickets req.session is ",req.session);
|
||||
let username=req.session.username;
|
||||
let claimed=0;
|
||||
let owned=0;
|
||||
@@ -247,43 +259,46 @@ app.get('/mytickets',requireLogin, async (req,res)=> {
|
||||
edit.tickets[t].owner=tickets[t].owner;
|
||||
edit.tickets[t].offered=tickets[t].offered;
|
||||
}
|
||||
}
|
||||
if (owned==0) return res.render("message",{ username:username, superuser:req.session.superuser, message:"You have no unused tickets" });
|
||||
}
|
||||
let message="";
|
||||
if (claimed>0) message="You have claimed "+claimed+" tickets.";
|
||||
edit.message=message;
|
||||
if (owned==0) return res.render("zerotickets",{ username:username, superuser:req.session.superuser, message:message });
|
||||
else if (owned==1) {
|
||||
const hash0=crypto.createHash('sha256');
|
||||
const hash1=hash0.update(theticket+QRSalt);
|
||||
const hash=hash1.digest("base64").slice(0,6);
|
||||
const dataURL=await QRCode.toDataURL('localhost:3000/useticket?t='+theticket+'&h='+hashQR(theticket,username));
|
||||
return res.render("oneticket",{ username:username, superuser:req.session.superuser, ticket:theticket, offered:tickets[theticket].offered, qrcode:dataURL });
|
||||
const useurl='/useticket?t='+theticket+'&h='+hashQR(theticket,username);
|
||||
const dataURL=await QRCode.toDataURL(useurl);
|
||||
return res.render("oneticket",{ username:username, superuser:req.session.superuser, message:message, ticket:theticket, offered:tickets[theticket].offered, qrcode:dataURL, useurl:useurl });
|
||||
}
|
||||
else return res.render("manytickets",edit);
|
||||
});
|
||||
|
||||
app.post('/mytickets',requireLogin, async (req,res)=> {
|
||||
app.post('/oneticket',requireLogin, async (req,res)=> {
|
||||
let username=req.session.username;
|
||||
let theticket=req.body.ticket;
|
||||
let offered=req.body.offered;
|
||||
let message="";
|
||||
if (tickets[theticket].owner==username && tickets[theticket].status=="i") {
|
||||
tickets[theticket].offered=offered; // LOG
|
||||
}
|
||||
const hash0=crypto.createHash('sha256');
|
||||
const hash1=hash0.update(theticket+QRSalt);
|
||||
const hash=hash1.digest("base64").slice(0,6);
|
||||
const dataURL=await QRCode.toDataURL('localhost:3000/useticket?t='+theticket+'&h='+hashQR(theticket,username));
|
||||
return res.render("oneticket", { username:username, superuser:req.session.superuser, ticket:theticket, offered:tickets[theticket].offered, qrcode:dataURL });
|
||||
const dataURL=await QRCode.toDataURL('/useticket?t='+theticket+'&h='+hashQR(theticket,username));
|
||||
return res.render("oneticket", { username:username, superuser:req.session.superuser, message:message, magiclink:GetMagicLink(username), ticket:theticket, offered:tickets[theticket].offered, qrcode:dataURL });
|
||||
});
|
||||
|
||||
|
||||
|
||||
app.post("/changestatus", requireSuperUser, (req,res) => {
|
||||
const ticket=req.body.ticket;
|
||||
console.log("Changestatus ",ticket);
|
||||
tickets[ticket].status=req.body.status; // LOG
|
||||
res.json({ message: 'Status received', status: req.body.status });
|
||||
})
|
||||
|
||||
app.post("/updateoffered", requireLogin, (req,res) => {
|
||||
console.log("UpdateOffered being rubn");
|
||||
const ticket=req.body.ticket;
|
||||
const offered=req.body.offered;
|
||||
if (tickets[ticket].owner!=req.session.username) res.status(500).send("Ticket "+ticket+" owned by someone else");
|
||||
@@ -295,6 +310,20 @@ app.post("/updateoffered", requireLogin, (req,res) => {
|
||||
}
|
||||
})
|
||||
|
||||
app.post("/updateoffered2", requireLogin, (req,res) => {
|
||||
const changes={};
|
||||
for (ticket in req.body) if (tickets[ticket] && tickets[ticket].owner==req.session.username && req.body[ticket]!=tickets[ticket].offered) {
|
||||
if (!(req.body[ticket] in changes)) changes[req.body[ticket]]=0;
|
||||
changes[req.body[ticket]]++;
|
||||
}
|
||||
// Ok to bail here if we need to
|
||||
for (ticket in req.body) if (tickets[ticket] && tickets[ticket].owner==req.session.username && req.body[ticket]!=tickets[ticket].offered) {
|
||||
tickets[ticket].offered=req.body[ticket];
|
||||
}
|
||||
for (email in changes) EmailTickets(email);
|
||||
return res.redirect("manytickets");
|
||||
})
|
||||
|
||||
|
||||
app.post("/updateticketsu", requireSuperUser, (req,res) => {
|
||||
const ticket=req.body.ticket;
|
||||
@@ -307,43 +336,30 @@ app.post("/updateticketsu", requireSuperUser, (req,res) => {
|
||||
|
||||
|
||||
app.get("/useticket",(req,res) => {
|
||||
let ticket=req.t;
|
||||
let hash=req.h;
|
||||
if (hashQR(ticket,req.session.username)!=hash) res.status(500).send("Ticket "+ticket+" was transferred to "+tickets[ticket].username);
|
||||
else if (tickets[ticket].status!="i") res.status(500).send("Ticket "+ticket+" has already been used.");
|
||||
else {
|
||||
tickets[ticket].status="u"; // LOG
|
||||
res.send("<h1>Welcome, "+tickets[ticket].owner+" to Falls on Fire! Ticket "+ticket+" has now been used.</h1>");
|
||||
}
|
||||
let ticket=req.query.t;
|
||||
let hash=req.query.h;
|
||||
if (!tickets[ticket]) return res.status(500).send("Ticket "+ticket+" not found.");
|
||||
if (hashQR(ticket,tickets[ticket].owner)!=hash) return res.status(500).send("Ticket "+ticket+" was transferred to "+tickets[ticket].owner);
|
||||
if (tickets[ticket].status!="i") return res.status(500).send("Ticket "+ticket+" has already been used.");
|
||||
tickets[ticket].status="u"; // LOG
|
||||
return res.send("<h1>Welcome, "+tickets[ticket].owner+" to Falls on Fire! Ticket "+ticket+" has now been used.</h1>");
|
||||
})
|
||||
|
||||
|
||||
|
||||
function knownEmail(email) {
|
||||
if (users[email]) return true;
|
||||
for (const key in tickets)
|
||||
if (tickets[key].owner==email || tickets[key].offered==email) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
app.post('emaillink',(req,res) => {
|
||||
if (!knownUser(req.email)) return res.send("Unknown User");
|
||||
const tok=createtoken(email);
|
||||
client.sendEmail({ From: 'tickets@fallsonfire.net',
|
||||
To: 'teppy@egenesis.com',
|
||||
Subject: 'Falls on Fire Tickets, Login Link',
|
||||
TextBody: 'Your Login Link: http://www.fallonfire.net/longinlink?token='+tok,
|
||||
HtmlBody: '<p>Your Login Link: </p>',
|
||||
}).then(() => {
|
||||
console.log('Email sent');
|
||||
res.send("<h1>Email sent</h1>");
|
||||
}).catch((error) => {
|
||||
console.error('Error sending email:', error);
|
||||
res.send("<h1>Email failed</h1>");
|
||||
});
|
||||
res.send("Email link sent");
|
||||
})
|
||||
|
||||
async function EmailTickets(email) {
|
||||
let offered=0;
|
||||
for (const ticket in tickets) if (tickets[ticket].offered==email) offered++;
|
||||
if (offered==0) return;
|
||||
const textbody="You have been offered "+offered+" tickets to Falls On Fire! To claim them, visit this link:\n"+GetMagicLink(email);
|
||||
const htmlbody="You have been offered "+offered+" tickets to Falls On Fire! To claim them, <a href=\""+GetMagicLink(email)+"\">click here.</a>";
|
||||
await client.sendEmail({ From: "tickets@fallsonfire.net",
|
||||
To: email,
|
||||
Subject: "Falls on Fire: You've Got Tickets!",
|
||||
TextBody: textbody,
|
||||
HTMLBody: htmlbody
|
||||
});
|
||||
}
|
||||
|
||||
app.get('/testemail',
|
||||
(req,res) => {
|
||||
@@ -369,10 +385,42 @@ app.get('/', (req, res) => {
|
||||
return res.render("login", { superuser:false });
|
||||
});
|
||||
|
||||
|
||||
function HasPW(username) {
|
||||
if (!(username in users)) return false;
|
||||
if (!users[username]) return false;
|
||||
if (!users[username].password) return false;
|
||||
if (users[username].password==0) return false;
|
||||
if (users[username].password=="") return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
app.get('/login',(req,res) => {
|
||||
return res.render("login",{ superuser:false });
|
||||
const username=req.query.u;
|
||||
const hash=req.query.h;
|
||||
if (MagicLinkValid(req.query.u,req.query.h)) return res.redirect("/mytickets");
|
||||
return res.render("login",{ message: "Normal Login Required." });
|
||||
});
|
||||
|
||||
app.post('/login', (req, res) => {
|
||||
const { username, password, superuser } = req.body;
|
||||
if (users[username] && users[username].password === hashPW(password)) {
|
||||
req.session.username = username;
|
||||
req.session.superuser = users[username].superuser;
|
||||
const redir=req.session.returnTo;
|
||||
delete req.session.returnTo;
|
||||
return res.redirect(redir || "/mytickets");
|
||||
}
|
||||
res.send('Invalid username or password. <a href="/login">Try again</a>');
|
||||
});
|
||||
|
||||
app.get('/logout', (req, res) => {
|
||||
req.session.destroy(() => {
|
||||
res.redirect('/');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.get('/signup', (req, res) => {
|
||||
res.send(`
|
||||
<h1>Sign Up</h1>
|
||||
@@ -423,55 +471,35 @@ app.post('/changepassword', (req, res) => {
|
||||
});
|
||||
|
||||
|
||||
app.post('/login', (req, res) => {
|
||||
const { username, password, superuser } = req.body;
|
||||
if (users[username] && users[username].password === hashPW(password)) {
|
||||
req.session.username = username;
|
||||
req.session.superuser = users[username].superuser;
|
||||
console.log("In post /login",req.session);
|
||||
const redir=req.session.returnTo;
|
||||
delete req.session.returnTo;
|
||||
return res.redirect(redir || "/mytickets");
|
||||
}
|
||||
res.send('Invalid username or password. <a href="/login">Try again</a>');
|
||||
});
|
||||
|
||||
app.get('/logout', (req, res) => {
|
||||
req.session.destroy(() => {
|
||||
res.redirect('/');
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/qrcode',requireLogin,async (req,res) => {
|
||||
const username=req.session.username;
|
||||
const ticket=req.body.ticket;
|
||||
if (tickets[ticket].owner!=username) return res.status(500).send("Only a ticket owner can generate a QR code");
|
||||
const URL=await QRCode.toDataURL('localhost:3000/useticket?t='+ticket+'&h='+hashQR(ticket,username));
|
||||
return res.send({ owner:tickets[ticket].owner, qrcode: URL });
|
||||
const URL=await QRCode.toDataURL('/useticket?t='+ticket+'&h='+hashQR(ticket,username));
|
||||
return res.send({ owner:username, qrcode: URL, magiclink:GetMagicLink(username) });
|
||||
})
|
||||
|
||||
app.post('/qrcodesu',requireSuperUser,async (req,res) => {
|
||||
const username=req.session.username;
|
||||
const ticket=req.body.ticket;
|
||||
const URL=await QRCode.toDataURL('localhost:3000/useticket?t='+ticket+'&h='+hashQR(ticket,username));
|
||||
return res.send({ owner:tickets[ticket].owner, qrcode: URL });
|
||||
const username=tickets[ticket].owner;
|
||||
const URL=await QRCode.toDataURL('/useticket?t='+ticket+'&h='+hashQR(ticket,username));
|
||||
return res.send({ owner:username, qrcode: URL, magiclink:GetMagicLink(username) });
|
||||
})
|
||||
|
||||
|
||||
app.get('/settings',requireSuperUser, (req,res) => {
|
||||
res.render('settings',{ username:req.session.username, superuser:req.session.superuser, message: "" })
|
||||
});
|
||||
|
||||
app.post('/wipedb',requireSuperUser, (req,res) => {
|
||||
for (const key in users ) delete users[key];
|
||||
for (const key in tickets) delete tickets[key];
|
||||
for (const key in camps ) delete camps[key];
|
||||
InitDatabase();
|
||||
res.render('settings',{ username:req.session.username, superuser:req.session.superuser, message: "Wiped the database, but not the logfile." })
|
||||
});
|
||||
|
||||
app.post('/purge',requireSuperUser, (req,res) => {
|
||||
let count=0;
|
||||
for (const t in tickets) if (tickets[t].status=='r') { count++; delete tickets[t]; }
|
||||
res.render('message',{ username:req.session.username, superuser:req.session.superuser, message: "Purged "+count+" revoked tickets" })
|
||||
res.render('settings',{ username:req.session.username, superuser:req.session.superuser, message: "Purged "+count+" revoked tickets" })
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user