diff --git a/foftickets.js b/foftickets.js index 559058e..bf00ff5 100644 --- a/foftickets.js +++ b/foftickets.js @@ -27,9 +27,11 @@ const QRSalt ="!SaltyMagic5392370662"; // + Claim Any Tickets // + Purge Revoked Function (Admin) // + Issue Tickets from Camp Admin Page -// Make one Update button on manytickets (User) -// Make one Update button on editcamp (Admin) -// Send email when new tickets are issued/offered +// + Send email when new tickets are issued/offered +// + Make one Update button on editcamp (Admin) +// + Make one Update button on manytickets (User) +// Turn ticket use on/off from Settings (Admin) +// Turn email on/off from Settings (Admin) // Magic-link Login System // Deactivate individual magic links (User) // Deactivate individual magic links (Admin) @@ -39,8 +41,9 @@ const QRSalt ="!SaltyMagic5392370662"; // + Use a templating engine // + Store password hashed and salted // Make all HTML look nice -// Logging and Replay system +// Logging and Replay system(?) // Stripe Integration +// More efficent data structure: TicketsByCamp, TicketsByOffered, TicketsByOwner // // @@ -110,6 +113,7 @@ function MagicLinkValid(email,hash) { const users={}; const tickets={}; const camps={}; +const settings={}; function InitDatabase() { for (const key in users ) delete users[key]; @@ -206,6 +210,7 @@ app.post("/camps",requireSuperUser,(req,res) => { app.get('/editcamp', requireSuperUser, (req,res) => { let campname=req.query.campname; + if (!camps[campname]) return res.redirect("/"); const edit={ username:req.session.username, superuser:req.session.superuser, campname:campname, leader:camps[campname].leader, tickets: {} }; for (const t in tickets) { const parts=t.split("-"); @@ -328,6 +333,27 @@ app.post("/updateoffered2", requireLogin, (req,res) => { }) +app.post("/updateoffered2su", requireSuperUser, (req,res) => { + const emaillist={}; + for (name in req.body) { + let ticket=0; + if (name.endsWith("-owner" )) { ticket=name.slice(0,-6); tickets[ticket].owner =req.body[name]; } + else if (name.endsWith("-status" )) { ticket=name.slice(0,-7); tickets[ticket].status =req.body[name]; } + else if (name.endsWith("-offered")) { + ticket=name.slice(0,-8); + if (tickets[ticket].offered!=req.body[name]) { + tickets[ticket].offered=req.body[name]; + emaillist[req.body[name]]=1; + } + } + } + for (email in emaillist) EmailTickets(email); + const referer = req.get('Referer'); + if (referer) return res.redirect(referer); + else return res.redirect('/'); + }) + + app.post("/updateticketsu", requireSuperUser, (req,res) => { const ticket=req.body.ticket; const owner=req.body.owner; @@ -344,13 +370,16 @@ app.get("/useticket",(req,res) => { 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("

Welcome, "+tickets[ticket].owner+" to Falls on Fire! Ticket "+ticket+" has now been used.

"); -}) + if (settings['enable-email']) { + tickets[ticket].status="u"; // LOG + return res.send("

Welcome, "+tickets[ticket].owner+" to Falls on Fire! Ticket "+ticket+" has now been used.

"); + } else return res.send("

Your ticket is good, "+tickets[ticket].owner+", but the server is not in Event Mode, so Ticket "+ticket+" is still valid."); + }) async function EmailTickets(email) { + if (!settings['enable-email']) return; let offered=0; for (const ticket in tickets) if (tickets[ticket].offered==email) offered++; if (offered==0) return; @@ -505,6 +534,11 @@ app.post('/purge',requireSuperUser, (req,res) => { res.render('settings',{ username:req.session.username, superuser:req.session.superuser, message: "Purged "+count+" revoked tickets" }) }); +app.post('/update-setting', requireSuperUser, (req, res) => { + settings[req.body.name]=req.body.checked; + res.json({ success: true, message: 'Checkbox state updated successfully' }); +}); + // Start the server app.listen(PORT, () => { diff --git a/views/editcamp.ejs b/views/editcamp.ejs index 83b4731..ff248f3 100644 --- a/views/editcamp.ejs +++ b/views/editcamp.ejs @@ -13,12 +13,10 @@
-
+
Server Ready
-
- Camp Name: <%=campname%>
+ Camp Namex: <%=campname%>
Camp Leader: <%=leader%> - @@ -30,10 +28,10 @@ <% for (const t in tickets) { %> - - + +
Ticket#
<%=t%> - @@ -45,10 +43,13 @@
-
-
+ +
+
+
Issue Tickets (Qty):
+
@@ -59,66 +60,28 @@ // JavaScript to change the form element const blankimage=document.getElementById("QRCodeImage").src; -const ResponseDisplay = document.getElementById("server-response"); -let ResponseStack = 0; -let ResponseError=""; -let ooEdits={}; // Owner or Offer has been edited - -function UpdateSR(delta) { - ResponseStack+=delta; - if (ResponseError!="") ResponseDisplay.textContent=ResponseError; - else if (ResponseStack>0) ResponseDisplay.textContent="Waiting for Server"; - else ResponseDisplay.textContent="Server Ready"; - } - - -function changeStatus(el) { - console.log("Change Status: ",el.target.value); - const id=el.target.id.slice(0,-7); - const js=JSON.stringify( { ticket: id, status: el.target.value } ); - const fetchtable={ method:'POST', headers: { 'Content-Type': 'application/json' }, body: js }; - UpdateSR(1); - fetch('/changestatus',fetchtable) - .then( response => { if (!response.ok) throw new Error(`Server responded with status ${response.status}`); else return response.json(); } ) - .then( data => UpdateSR(-1) ) - .catch( error => { console.log("Here is the error!"+error); ResponseError=error; UpdateSR(-1); }) - } document.body.addEventListener("click", event => { const id=event.target.id; console.log("Click event on id ",id); if (id.endsWith("-action")) { const id0=id.slice(0,-7); - if (ooEdits[id0]) { - UpdateSR(1); - const ownerEdit=document.getElementById(id0+"-owner"); - const offeredEdit=document.getElementById(id0+"-offered"); - const js=JSON.stringify( { ticket: id0, owner:ownerEdit.value, offered: offeredEdit.value } ); - const fetchtable={ method:'POST', headers: { 'Content-Type': 'application/json' }, body: js }; - fetch('/updateticketsu',fetchtable) - .then( response => { if (!response.ok) throw new Error(`Server responded with status ${response.status}`); else return response.json(); } ) - .then( data => { UpdateSR(-1); event.target.textContent="QRCode"; ooEdits[id0]=false; } ) - .catch( error => { console.log("Here is the error!"+error); ResponseError=error; UpdateSR(-1); }) - } else { - const modal = document.getElementById('QRShow'); - const qrbanner=document.getElementById('QRBanner'); - const qrcodeimage=document.getElementById('QRCodeImage'); - const closeModalSpan = document.querySelector('.close'); - modal.style.display = 'flex'; - const js=JSON.stringify( { ticket: id0 } ); - const fetchtable={ method:'POST', headers: { 'Content-Type': 'application/json' }, body: js }; - qrcodeimage.src=blankimage; - fetch('/qrcodesu',fetchtable) - .then( response => { if (!response.ok) throw new Error(`Server responded with status ${response.status}`); else return response.json(); } ) - .then( data => { - console.log("Data is: ",data); - qrbanner.innerText="Ticket: "+id0+" Owner: "+data.owner; - qrcodeimage.src=data.qrcode; - } ) - .catch( error => { console.log("Here is the error!"+error); ResponseError=error; UpdateSR(-1); }) - - console.log("QRCode ",id0); - } + const modal = document.getElementById('QRShow'); + const qrbanner=document.getElementById('QRBanner'); + const qrcodeimage=document.getElementById('QRCodeImage'); + const closeModalSpan = document.querySelector('.close'); + modal.style.display = 'flex'; + const js=JSON.stringify( { ticket: id0 } ); + const fetchtable={ method:'POST', headers: { 'Content-Type': 'application/json' }, body: js }; + qrcodeimage.src=blankimage; + fetch('/qrcodesu',fetchtable) + .then( response => { if (!response.ok) throw new Error(`Server responded with status ${response.status}`); else return response.json(); } ) + .then( data => { + console.log("Data is: ",data); + qrbanner.innerText="Ticket: "+id0+" Owner: "+data.owner; + qrcodeimage.src=data.qrcode; + } ) + .catch( error => { console.log("Here is the error!"+error); ResponseError=error; }) } else if (["QRShow","QRCodeImage","QRBackground","QRBanner"].includes(id)) { const modal = document.getElementById('QRShow'); modal.style.display="none"; @@ -128,21 +91,14 @@ document.body.addEventListener("click", event => { -const pulldowns = document.querySelectorAll("[id$=-status]"); -pulldowns.forEach(el => { - const pd = document.getElementById(el.id); - pd.addEventListener("change",changeStatus); - }); +const MessageArea=document.getElementById("message"); +const pulldowns = document.getElementsByClassName("status"); +for (let i=0; i MessageArea.textContent="Be sure to use the Update Tickets button."); +const owners = document.getElementsByClassName("owner"); +for (let i=0; iMessageArea.textContent= "Be sure to use the Update Tickets button."); +const offereds = document.getElementsByClassName("offered"); +for (let i=0; iMessageArea.textContent= "Be sure to use the Update Tickets button."); -const owners = document.querySelectorAll("[id$=-owner]"); -owners.forEach(el => { - const id0=el.id.slice(0,-6); - const OwnerEdit = document.getElementById(id0+"-owner" ); - const OfferedEdit = document.getElementById(id0+"-offered"); - const ActionButton = document.getElementById(id0+"-action" ); - OwnerEdit .addEventListener('input', () => { ActionButton.textContent = "Update"; ooEdits[id0]=true; console.log("Changed OfferedEdit:",OfferedEdit.value); }); - OfferedEdit.addEventListener('input', () => { ActionButton.textContent = "Update"; ooEdits[id0]=true; console.log("Changed OfferedEdit:",OfferedEdit.value); }); - }); diff --git a/views/manytickets.ejs b/views/manytickets.ejs index 6a04351..5af908b 100644 --- a/views/manytickets.ejs +++ b/views/manytickets.ejs @@ -46,60 +46,40 @@ let ResponseStack = 0; let ResponseError=""; let ooEdits={}; // Owner or Offer has been edited -function UpdateSR(delta) { - ResponseStack+=delta; - if (ResponseError!="") ResponseDisplay.textContent=ResponseError; - else if (ResponseStack>0) ResponseDisplay.textContent="Waiting for Server"; - else ResponseDisplay.textContent="Server Ready"; - } - document.body.addEventListener("click", event => { const id=event.target.id; console.log("Click event on id ",id); if (id.endsWith("-action")) { const id0=id.slice(0,-7); - if (ooEdits[id0]) { - UpdateSR(1); - const offeredEdit=document.getElementById(id0+"-offered"); - const js=JSON.stringify( { ticket: id0, offered: offeredEdit.value } ); - const fetchtable={ method:'POST', headers: { 'Content-Type': 'application/json' }, body: js }; - fetch('/updateoffered',fetchtable) - .then( response => { if (!response.ok) throw new Error(`Server responded with status ${response.status}`); else return response.json(); } ) - .then( data => { UpdateSR(-1); event.target.textContent="QRCode"; ooEdits[id0]=false; } ) - .catch( error => { console.log("Here is the error!"+error); ResponseError=error; UpdateSR(-1); }) - } else { - const modal = document.getElementById('QRShow'); - const qrbanner=document.getElementById('QRBanner'); - const qrcodeimage=document.getElementById('QRCodeImage'); - const closeModalSpan = document.querySelector('.close'); - modal.style.display = 'flex'; - const js=JSON.stringify( { ticket: id0 } ); - const fetchtable={ method:'POST', headers: { 'Content-Type': 'application/json' }, body: js }; - qrcodeimage.src=blankimage; - fetch('/qrcode',fetchtable) - .then( response => { if (!response.ok) throw new Error(`Server responded with status ${response.status}`); else return response.json(); } ) - .then( data => { - console.log("Data is: ",data); - qrbanner.innerText="Ticket: "+id0+" Owner: "+data.owner; - qrcodeimage.src=data.qrcode; - } ) - .catch( error => { console.log("Here is the error!"+error); ResponseError=error; UpdateSR(-1); }) - - console.log("QRCode ",id0); - } + const modal = document.getElementById('QRShow'); + const qrbanner=document.getElementById('QRBanner'); + const qrcodeimage=document.getElementById('QRCodeImage'); + const closeModalSpan = document.querySelector('.close'); + modal.style.display = 'flex'; + const js=JSON.stringify( { ticket: id0 } ); + const fetchtable={ method:'POST', headers: { 'Content-Type': 'application/json' }, body: js }; + qrcodeimage.src=blankimage; + fetch('/qrcode',fetchtable) + .then( response => { if (!response.ok) throw new Error(`Server responded with status ${response.status}`); else return response.json(); } ) + .then( data => { + console.log("Data is: ",data); + qrbanner.innerText="Ticket: "+id0+" Owner: "+data.owner; + qrcodeimage.src=data.qrcode; + } ) + .catch( error => { console.log("Here is the error!"+error); ResponseError=error; }) } else if (["QRShow","QRCodeImage","QRBackground","QRBanner"].includes(id)) { const modal = document.getElementById('QRShow'); modal.style.display="none"; - } -}) + } +}); + -const offereds = document.getElementsByClassName("offered"); const MessageArea=document.getElementById("message"); -console.log("Offereds is ",offereds[0]); +const offereds = document.getElementsByClassName("offered"); for (let i=0; iMessageArea.textContent= "Be sure to use the Update Offered button."); diff --git a/views/settings.ejs b/views/settings.ejs index fc18dc4..80acf37 100644 --- a/views/settings.ejs +++ b/views/settings.ejs @@ -7,6 +7,9 @@ <%- include('partials/nav') %>
+ Enable Email
+ Enable Ticket Use
+ Enable Credit Cards
@@ -15,5 +18,22 @@
+