diff --git a/foftickets.js b/foftickets.js
index 64dafda..940ce60 100644
--- a/foftickets.js
+++ b/foftickets.js
@@ -8,6 +8,7 @@ const path=require('path');
const app = express();
app.set('view engine','ejs');
app.use(express.json());
+app.use(express.static('public'));
const PORT = 3000;
const PWSalt="!SaltyMagic7283715374";
const QRSalt="!SaltyMagic5392370662";
@@ -19,15 +20,15 @@ const QRSalt="!SaltyMagic5392370662";
// + Email Link
// + Signup
// Change Password
-// Issue Tickets
-// Transfer Tickets Complicated page. (One ticket: static with transfer and QR code. Multi: buttons to transfer or show QR code)
+// + Issue Tickets
+// + Transfer Tickets Complicated page. (One ticket: static with transfer and QR code. Multi: buttons to transfer or show QR code)
+// Split Transfer into admin (change owner, unused/used/cancelled pulldown) and public (no Used column) versions
// Claim Tickets
// + Use Ticket
-// Edit Tickets
//
// ToDo, Later
-// Use a templating engine
-// Store password hashed and salted
+// + Use a templating engine
+// + Store password hashed and salted
// Make all HTML look nice
// Logging and Replay system
//
@@ -63,7 +64,7 @@ const tickets = { "habitat-1" : { owner: "teppy@egenesis.com" , offered: "", pa
"habitat-6" : { owner: "teppy@egenesis.com" , offered: "", paid: 0.00 },
};
-const camps = { "habitat": 0 };
+const camps = { "habitat": { issued: 6 } };
const tokens = { "abc" : { username: "teppy@egenesis.com", expires: 0 }
};
@@ -80,14 +81,15 @@ app.use(session({
// Middleware to protect routes
function requireLogin(req, res, next) {
- if (!req.session.username) return res.redirect('/login');
- next();
+ if (req.session.username) return next();
+ return res.redirect('/login');
}
function requireSuperUser(req,res,next) {
- console.log(req.session);
- if (!req.session.superuser) return res.status(403).send(JSON.stringify( { error: "Forbidden without superuser flag" } ));
- else next();
+ if (req.session.superuser) return next();
+ req.session.returnTo=req.originalUrl;
+ console.log("In requireSuperUser: ",req.session);
+ return res.redirect('/login');
}
@@ -110,23 +112,6 @@ function generateSecureToken(length = 8) {
const postmark = require('postmark');
const client = new postmark.ServerClient('f45bcb9f-6556-4420-9e21-05d16739b5e8');
-app.get('/loginpassword',(req,res) => {
- res.send(`
Sign Up
- `);
-})
-
-app.post('/loginpassword', (req, res) => {
- const { username, password } = req.body;
- if (username in users && hashPW(password)==users[username].password) {
- req.session.username=username;
- req.session.superuser=users[username].superuser;
- res.redirect('/transfer');
- } else { res.redirect('/transfer'); }
-})
app.get('/logintoken', (req, res) => {
const tok=req.query.token;
@@ -144,7 +129,7 @@ app.get('/emaillink',(req,res) => {
`
)
-})
+ });
// These can be owned, offered, of offering
function categorizeTickets(username) {
@@ -161,7 +146,6 @@ function categorizeTickets(username) {
if (tickets[t].owner==username || tickets[t].offered==username) numtickets+=1;
if (numtickets==1) thet=t; else thet="";
}
- console.log("Simpleowner: "+simpleowner+" Offering "+offering+" SimpleOffered "+simpleoffered+" Numtickets "+numtickets);
if (numtickets==0) return [ "none", ""];
if (numtickets >1) return [ "complex", ""];
if (simpleowner+offering+simpleoffered>1)
@@ -173,6 +157,33 @@ function categorizeTickets(username) {
}
+app.get('/issue', requireSuperUser, (req,res) => {
+ const camplist={};
+ for (const t in tickets) {
+ const parts=t.split("-");
+ const campname=parts[0];
+ const ticketnum=Number(parts[1]);
+ camplist[campname]??={ issued:0, claimed:0, used:0 };
+ camplist[campname].issued+=1;
+ if (tickets[t].owner!="") camplist[campname].claimed+=1;
+ if (tickets[t].used) camplist[campname].used+=1;
+ }
+ return res.render("issue",{ username:"Teppy", camps:camplist });
+ })
+
+app.post("/issue",(req,res) => {
+ const campname=req.body.campname;
+ const email=req.body.email;
+ const qty=Number(req.body.qty);
+ camps[campname]??={ issued:0 };
+ for (let i=0; i {
let username=req.session.username;
const [ cat, ticket ] = categorizeTickets(username);
const simpledata={ username: username, ticket: ticket };
- console.log("Transfer type "+cat);
if (cat=="none") return res.render("notickets",simpledata);
if (cat=="error") return res.render("error",simpledata);
if (cat=="simpleoffered") return res.render("claimone",simpledata);
@@ -225,7 +235,6 @@ app.post("/toggle", requireSuperUser, (req,res) => {
app.post("/updateoffered", requireLogin, (req,res) => {
const ticket=req.body.ticket;
const offered=req.body.offered;
- console.log("Ticket: ",ticket);
if (tickets[ticket].owner!=req.session.username) res.status(500).send("Ticket "+ticket+" owned by someone else");
else if (tickets[ticket].used) res.status(500).send("Ticket "+ticket+" has already been used");
else {
@@ -234,6 +243,7 @@ app.post("/updateoffered", requireLogin, (req,res) => {
}
})
+
app.get("/useticket",(req,res) => {
let ticket=req.t;
let hash=req.h;
@@ -323,9 +333,7 @@ app.get('/signup', (req, res) => {
function consumeToken(tok) {
- console.log("ConsumeToken 1");
if (!(tok in tokens)) return false;
- console.log("ConsumeToken 2");
const rval=tokens[tok].expires==0 || tokens[tok].expires>=Date.now();
delete tokensu[tokens[tok].username];
delete tokens[tok];
@@ -379,23 +387,27 @@ app.post('/changepassword', (req, res) => {
});
app.get('/login', (req, res) => {
- res.send(`
- Log In
-
- Sign Up
- `);
-});
+ console.log("In get /login: ",req.session);
+ res.send(`
+ Log In
+
+ Sign Up
+ `);
+ });
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;
- return res.redirect('/transfer');
+ console.log("In post /login",req.session);
+ const redir=req.session.returnTo;
+ delete req.session.returnTo;
+ return res.redirect(redir || "/transfer");
}
res.send('Invalid username or password. Try again');
});
@@ -409,11 +421,9 @@ app.get('/logout', (req, res) => {
app.post('/qrcode',requireLogin,async (req,res) => {
const username=req.session.username;
const ticket=req.body.ticket;
- console.log("Body: ",req.body);
- console.log("Tickets["+ticket+"]",tickets[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({ qrcode: URL });
+ return res.send({ owner:tickets[ticket].owner, qrcode: URL });
})
// Protected routes
diff --git a/public/styles.css b/public/styles.css
index 5047409..85a48ed 100644
--- a/public/styles.css
+++ b/public/styles.css
@@ -1,5 +1,3 @@
-
-
/* Modal container */
.modal {
display: none; /* Hidden by default */
@@ -10,6 +8,8 @@
width: 100%; /* Full width */
height: 100%; /* Full height */
background-color: rgba(0, 0, 0, 0.5); /* Black with opacity */
+ align-items: center; /* Vertically center the modal */
+ justify-content: center; /* Horizontally center the modal */
}
/* Modal content */
@@ -19,6 +19,13 @@
padding: 20px;
border: 1px solid #888;
width: 80%; /* Adjust as needed */
+ height: 80%;
+}
+
+.qrcode-image {
+ object-fit: contain;
+ width: 80%; /* Adjust as needed */
+ height: 80%;
}
/* Close button */
diff --git a/views/issue.ejs b/views/issue.ejs
new file mode 100644
index 0000000..9484f46
--- /dev/null
+++ b/views/issue.ejs
@@ -0,0 +1,32 @@
+
+
+
+ Issue New Tickets
+
+
+
+ Welcome, <%= username %>!
+
+