2024-12-05 13:05:35 -05:00
const express = require ( 'express' ) ;
const bodyParser = require ( 'body-parser' ) ;
const session = require ( 'express-session' ) ;
const QRCode = require ( 'qrcode' ) ;
const crypto = require ( 'crypto' ) ;
2024-12-09 15:11:22 -05:00
const path = require ( 'path' ) ;
2024-12-05 13:05:35 -05:00
const app = express ( ) ;
app . set ( 'view engine' , 'ejs' ) ;
app . use ( express . json ( ) ) ;
2024-12-19 15:57:33 -05:00
app . use ( express . static ( 'public' ) ) ;
2024-12-05 13:05:35 -05:00
const PORT = 3000 ;
2024-12-28 19:31:45 -05:00
const MainURL = "http://localhost:3000" ;
2024-12-28 00:07:12 -05:00
const PWSalt = "!SaltyMagic7283715374" ;
const EmailSalt = "!SaltyMagic3562196239" ;
const QRSalt = "!SaltyMagic5392370662" ;
2024-12-05 13:05:35 -05:00
//
// ToDo, Actions:
// + Login with Password
// + Login with Token
// + Email Link
2024-12-27 08:49:34 -05:00
// + Display Many Tickets (User)
// + Display One Ticket (User)
// + Camp Editor
2024-12-05 13:05:35 -05:00
// + Use Ticket
2024-12-27 08:49:34 -05:00
// + Claim Any Tickets
2024-12-28 00:07:12 -05:00
// + Purge Revoked Function (Admin)
// + Issue Tickets from Camp Admin Page
2024-12-28 19:31:45 -05:00
// Make one Update button on manytickets (User)
// Make one Update button on editcamp (Admin)
2024-12-28 00:07:12 -05:00
// Send email when new tickets are issued/offered
2024-12-28 19:31:45 -05:00
// Magic-link Login System
// Deactivate individual magic links (User)
// Deactivate individual magic links (Admin)
// See how much each camp/ticket has paid (Admin)
2024-12-05 13:05:35 -05:00
//
// ToDo, Later
2024-12-19 15:57:33 -05:00
// + Use a templating engine
// + Store password hashed and salted
2024-12-05 13:05:35 -05:00
// Make all HTML look nice
// Logging and Replay system
2024-12-28 00:07:12 -05:00
// Stripe Integration
2024-12-05 13:05:35 -05:00
//
2024-12-28 00:07:12 -05:00
//
// Login workflow:
// There is always an implicit magic link that allows anyone to log in with their email address and hash(GlobalSalt+PerUserSalty+Email), except if the user sets a password for himself.
// We store a per-user salt, initially the empty string, in case we need to invalidate particular users' magic links.
//
//
// Commands
// ISSUE campname email
// USE ticket
// STATUS ticket i/u/r
// CLAIM ticket email
//
function hashEmail ( email ) {
const hash0 = crypto . createHash ( 'sha256' ) ;
2024-12-28 19:31:45 -05:00
const usersalt = email in users ? ( users [ email ] . linksalt ? users [ email ] . linksalt : "" ) : "" ;
2024-12-28 00:07:12 -05:00
const hash1 = hash0 . update ( email + EmailSalt + usersalt ) ;
const hash = hash1 . digest ( "base64" ) ;
return ( hash ) ;
}
2024-12-05 13:05:35 -05:00
function hashPW ( pw ) {
const hash0 = crypto . createHash ( 'sha256' ) ;
const hash1 = hash0 . update ( pw + PWSalt ) ;
const hash = hash1 . digest ( "base64" ) ;
return ( hash ) ;
}
2024-12-14 23:26:38 -05:00
function hashQR ( t , ownername ) {
2024-12-05 13:05:35 -05:00
const hash0 = crypto . createHash ( 'sha256' ) ;
2024-12-14 23:26:38 -05:00
const hash1 = hash0 . update ( t + QRSalt + ownername ) ;
2024-12-05 13:05:35 -05:00
const hash = hash1 . digest ( "base64" ) . slice ( 0 , 6 ) ;
return ( hash ) ;
}
2024-12-28 19:31:45 -05:00
function GetMagicLink ( email ) {
return MainURL + "/login?u=" + email + "&h=" + hashEmail ( email ) ;
}
function MagicLinkValid ( email , hash ) {
if ( HasPW ( email ) ) return false ;
return hashEmail ( email ) == hash ;
}
2024-12-05 13:05:35 -05:00
//
// In-memory data structures
//
// There are two ways to log in: with a username/password or with a token.
//
2024-12-28 19:31:45 -05:00
//let users = { "teppy@egenesis.com" : { password: hashPW("x6321872X.1"), superuser:true, linksalt:"" },
// "fallsonfire@gmail.com" : { password: hashPW("indiantreeonfire"), superuser:false, linksalt:"boobsarefun" }
// };
//
2024-12-26 00:33:23 -05:00
// Status can be "i"=issued, "u"=used, "r"=revoked"
2024-12-28 19:31:45 -05:00
// 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 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 ( ) ;
2024-12-09 15:11:22 -05:00
2024-12-05 13:05:35 -05:00
// Middleware setup
app . use ( bodyParser . urlencoded ( { extended : true } ) ) ;
app . use ( session ( {
secret : 'supersecretkey' ,
resave : false ,
saveUninitialized : false ,
} ) ) ;
// Middleware to protect routes
function requireLogin ( req , res , next ) {
2024-12-19 15:57:33 -05:00
if ( req . session . username ) return next ( ) ;
2024-12-26 00:33:23 -05:00
req . session . returnTo = req . originalUrl ;
2024-12-19 15:57:33 -05:00
return res . redirect ( '/login' ) ;
2024-12-09 15:11:22 -05:00
}
2024-12-05 13:05:35 -05:00
2024-12-09 15:11:22 -05:00
function requireSuperUser ( req , res , next ) {
2024-12-19 15:57:33 -05:00
if ( req . session . superuser ) return next ( ) ;
req . session . returnTo = req . originalUrl ;
return res . redirect ( '/login' ) ;
2024-12-09 15:11:22 -05:00
}
2024-12-05 13:05:35 -05:00
2024-12-09 15:11:22 -05:00
app . get ( '/styles.css' , ( req , res ) => {
res . setHeader ( 'Content-Type' , 'text/css' ) ;
res . sendFile ( path . join ( _ _dirname , 'public' , 'styles.css' ) ) ;
} ) ;
2024-12-05 13:05:35 -05:00
function generateSecureToken ( length = 8 ) {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' ;
let token = '' ;
const array = new Uint8Array ( length ) ;
crypto . getRandomValues ( array ) ;
for ( let i = 0 ; i < length ; i ++ ) {
token += characters . charAt ( array [ i ] % characters . length ) ;
}
return token ;
}
2024-12-28 00:07:12 -05:00
2024-12-05 13:05:35 -05:00
const postmark = require ( 'postmark' ) ;
const client = new postmark . ServerClient ( 'f45bcb9f-6556-4420-9e21-05d16739b5e8' ) ;
app . get ( '/emaillink' , ( req , res ) => {
res . send ( `
< h1 > Email me a login link < / h 1 >
< form method = "POST" action = "/emaillink" >
< label > Email Address : < input name = "email" > < / l a b e l >
< button type = "submit" > Submit < / b u t t o n > `
)
2024-12-19 15:57:33 -05:00
} ) ;
2024-12-05 13:05:35 -05:00
2024-12-21 00:29:45 -05:00
app . get ( '/camps' , requireSuperUser , ( req , res ) => {
const camplist = { } ;
for ( const c in camps ) {
camplist [ c ] = { leader : camps [ c ] . leader , issued : 0 , claimed : 0 , used : 0 } ;
}
for ( const t in tickets ) {
const parts = t . split ( "-" ) ;
2024-12-26 00:33:23 -05:00
const campname = parts [ 0 ] ;
2024-12-21 00:29:45 -05:00
const ticketnum = Number ( parts [ 1 ] ) ;
2024-12-26 00:33:23 -05:00
if ( tickets [ t ] . status != "r" ) {
camplist [ campname ] . issued += 1 ;
if ( tickets [ t ] . owner != "" ) camplist [ campname ] . claimed += 1 ;
if ( tickets [ t ] . status == "u" ) camplist [ campname ] . used += 1 ;
}
2024-12-21 00:29:45 -05:00
}
2024-12-28 00:07:12 -05:00
return res . render ( "camps" , { username : req . session . username , superuser : req . session . superuser , camps : camplist } ) ;
2024-12-21 00:29:45 -05:00
} )
2024-12-26 00:33:23 -05:00
app . post ( "/camps" , requireSuperUser , ( req , res ) => {
const campname = req . body . campname ;
const leader = req . body . leader ;
const qty = Number ( req . body . qty ) ;
camps [ campname ] ? ? = { leader : leader , lastid : 0 } ;
for ( let i = 0 ; i < qty ; i ++ ) {
2024-12-28 00:07:12 -05:00
camps [ campname ] . lastid += 1 ; // LOG
tickets [ campname + '-' + camps [ campname ] . lastid ] = { owner : "" , offered : leader , status : "i" } ; // LOG
2024-12-26 00:33:23 -05:00
}
2024-12-28 19:31:45 -05:00
EmailTickets ( leader ) ;
2024-12-26 00:33:23 -05:00
return res . redirect ( "/camps" ) ;
} )
2024-12-21 00:29:45 -05:00
2024-12-26 00:33:23 -05:00
app . get ( '/editcamp' , requireSuperUser , ( req , res ) => {
let campname = req . query . campname ;
2024-12-28 00:07:12 -05:00
const edit = { username : req . session . username , superuser : req . session . superuser , campname : campname , leader : camps [ campname ] . leader , tickets : { } } ;
2024-12-26 00:33:23 -05:00
for ( const t in tickets ) {
const parts = t . split ( "-" ) ;
const cname = parts [ 0 ] ;
const tnum = Number ( parts [ 1 ] ) ;
if ( cname == campname ) {
edit . tickets [ t ] = { } ;
edit . tickets [ t ] . owner = tickets [ t ] . owner ;
edit . tickets [ t ] . offered = tickets [ t ] . offered ;
edit . tickets [ t ] . status = tickets [ t ] . status ;
}
}
return res . render ( "editcamp" , edit ) ;
2024-12-19 15:57:33 -05:00
} )
2024-12-26 00:33:23 -05:00
2024-12-28 00:07:12 -05:00
app . post ( '/moretickets' , requireSuperUser , ( req , res ) => {
const qty = Number ( req . body . qty ) ;
const campname = req . body . campname ;
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" } ;
}
const referer = req . get ( 'Referer' ) ;
if ( referer ) return res . redirect ( referer ) ;
else return res . redirect ( '/' ) ;
} ) ;
2024-12-26 21:46:13 -05:00
app . get ( '/manytickets' , requireLogin , ( req , res ) => {
let username = req . session . username ;
2024-12-28 00:07:12 -05:00
const edit = { username : req . session . username , superuser : req . session . superuser , tickets : { } } ;
2024-12-26 21:46:13 -05:00
for ( const t in tickets ) if ( tickets [ t ] . owner == username && tickets [ t ] . status == "i" ) {
edit . tickets [ t ] = { } ;
edit . tickets [ t ] . offered = tickets [ t ] . offered ;
}
return res . render ( "manytickets" , edit ) ;
} )
2024-12-26 00:33:23 -05:00
2024-12-26 21:46:13 -05:00
app . get ( '/mytickets' , requireLogin , async ( req , res ) => {
2024-12-26 00:33:23 -05:00
let username = req . session . username ;
let claimed = 0 ;
let owned = 0 ;
let theticket = "" ;
2024-12-28 00:07:12 -05:00
const edit = { username : req . session . username , superuser : req . session . superuser , tickets : { } } ;
2024-12-26 00:33:23 -05:00
for ( const t in tickets ) {
2024-12-28 00:07:12 -05:00
if ( tickets [ t ] . status == "i" && tickets [ t ] . offered == username ) { claimed ++ ; tickets [ t ] . owner = username ; tickets [ t ] . offered = "" ; } // LOG
2024-12-26 00:33:23 -05:00
if ( tickets [ t ] . status == "i" && tickets [ t ] . owner == username ) {
owned ++ ;
if ( owned == 1 ) theticket = t ; else theticket = "" ;
edit . tickets [ t ] = { } ;
edit . tickets [ t ] . owner = tickets [ t ] . owner ;
edit . tickets [ t ] . offered = tickets [ t ] . offered ;
}
2024-12-28 19:31:45 -05:00
}
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 } ) ;
2024-12-26 21:46:13 -05:00
else if ( owned == 1 ) {
const hash0 = crypto . createHash ( 'sha256' ) ;
const hash1 = hash0 . update ( theticket + QRSalt ) ;
const hash = hash1 . digest ( "base64" ) . slice ( 0 , 6 ) ;
2024-12-28 19:31:45 -05:00
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 } ) ;
2024-12-26 21:46:13 -05:00
}
2024-12-26 00:33:23 -05:00
else return res . render ( "manytickets" , edit ) ;
} ) ;
2024-12-19 15:57:33 -05:00
2024-12-28 19:31:45 -05:00
app . post ( '/oneticket' , requireLogin , async ( req , res ) => {
2024-12-26 21:46:13 -05:00
let username = req . session . username ;
let theticket = req . body . ticket ;
let offered = req . body . offered ;
2024-12-28 19:31:45 -05:00
let message = "" ;
2024-12-26 21:46:13 -05:00
if ( tickets [ theticket ] . owner == username && tickets [ theticket ] . status == "i" ) {
2024-12-28 00:07:12 -05:00
tickets [ theticket ] . offered = offered ; // LOG
2024-12-26 21:46:13 -05:00
}
const hash0 = crypto . createHash ( 'sha256' ) ;
const hash1 = hash0 . update ( theticket + QRSalt ) ;
const hash = hash1 . digest ( "base64" ) . slice ( 0 , 6 ) ;
2024-12-28 19:31:45 -05:00
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 } ) ;
2024-12-26 21:46:13 -05:00
} ) ;
2024-12-05 13:05:35 -05:00
2024-12-26 00:33:23 -05:00
app . post ( "/changestatus" , requireSuperUser , ( req , res ) => {
const ticket = req . body . ticket ;
2024-12-28 00:07:12 -05:00
tickets [ ticket ] . status = req . body . status ; // LOG
2024-12-26 00:33:23 -05:00
res . json ( { message : 'Status received' , status : req . body . status } ) ;
} )
2024-12-09 15:11:22 -05:00
app . post ( "/updateoffered" , requireLogin , ( req , res ) => {
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" ) ;
2024-12-26 21:46:13 -05:00
else if ( tickets [ ticket ] . status == "u" ) res . status ( 500 ) . send ( "Ticket " + ticket + " has already been used" ) ;
else if ( tickets [ ticket ] . status == "r" ) res . status ( 500 ) . send ( "Ticket " + ticket + " was revoked" ) ;
2024-12-09 15:11:22 -05:00
else {
2024-12-28 00:07:12 -05:00
tickets [ ticket ] . offered = offered ; // LOG
2024-12-09 15:11:22 -05:00
res . json ( { message : 'Updated owner of ' + ticket + ' to ' + offered } ) ;
}
} )
2024-12-05 13:05:35 -05:00
2024-12-28 19:31:45 -05:00
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" ) ;
} )
2024-12-19 15:57:33 -05:00
2024-12-26 00:33:23 -05:00
app . post ( "/updateticketsu" , requireSuperUser , ( req , res ) => {
const ticket = req . body . ticket ;
const owner = req . body . owner ;
const offered = req . body . offered ;
2024-12-28 00:07:12 -05:00
tickets [ ticket ] . owner = req . body . owner ; // LOG
tickets [ ticket ] . offered = req . body . offered ; // LOG
2024-12-26 00:33:23 -05:00
res . json ( { message : 'Updated ' + ticket } ) ;
} )
2024-12-05 13:05:35 -05:00
app . get ( "/useticket" , ( req , res ) => {
2024-12-28 19:31:45 -05:00
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>" ) ;
2024-12-05 13:05:35 -05:00
} )
2024-12-28 19:31:45 -05:00
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
} ) ;
}
2024-12-05 13:05:35 -05:00
app . get ( '/testemail' ,
( req , res ) => {
client . sendEmail ( { From : 'tickets@fallsonfire.net' ,
To : 'teppy@egenesis.com' ,
Subject : 'Email from Ticketing System' ,
TextBody : 'This is a test email.' ,
HtmlBody : '<p>This is a test email.</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>" ) ;
} ) } ) ;
// Routes
app . get ( '/' , ( req , res ) => {
2024-12-28 00:07:12 -05:00
if ( req . session . username ) return res . redirect ( "/mytickets" ) ;
return res . render ( "login" , { superuser : false } ) ;
} ) ;
2024-12-28 19:31:45 -05:00
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 ;
}
2024-12-28 00:07:12 -05:00
app . get ( '/login' , ( req , res ) => {
2024-12-28 19:31:45 -05:00
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." } ) ;
2024-12-05 13:05:35 -05:00
} ) ;
2024-12-28 19:31:45 -05:00
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 ( '/' ) ;
} ) ;
} ) ;
2024-12-05 13:05:35 -05:00
app . get ( '/signup' , ( req , res ) => {
res . send ( `
< h1 > Sign Up < / h 1 >
< form method = "POST" action = "/signup" >
< label > Username : < input type = "text" name = "username" required > < / l a b e l > < b r >
< label > Password : < input type = "password" name = "password" required > < / l a b e l > < b r >
< button type = "submit" > Sign Up < / b u t t o n >
< / f o r m >
< a href = "/login" > Log In < / a >
` );
} ) ;
app . post ( '/signup' , ( req , res ) => {
const { username , password } = req . body ;
if ( users [ username ] ) {
return res . send ( 'User already exists. <a href="/signup">Try again</a>' ) ;
}
2024-12-09 15:11:22 -05:00
users [ username ] = { password : hashPW ( password ) } ;
console . log ( "Created new account:" , username ) ;
2024-12-05 13:05:35 -05:00
res . redirect ( '/login' ) ;
} ) ;
app . get ( '/changepassword' , ( req , res ) => {
res . send ( `
< h1 > Change Password < / h 1 >
< form method = "POST" action = "/changepassword" >
< label > Password : < input type = "password" name = "password1" required > < / l a b e l > < b r >
< label > Again : < input type = "password" name = "password2" required > < / l a b e l > < b r >
< button type = "submit" > Sign Up < / b u t t o n >
< / f o r m >
< a href = "/" > Home < / a >
` );
} ) ;
app . post ( '/changepassword' , ( req , res ) => {
const { password1 , password2 } = req . body ;
if ( ! req . session . username ) {
return res . send ( 'You are not logged in<a href="/">Back</a>' ) ;
}
if ( password1 != password2 ) {
return res . send ( 'Passwords do not match<a href="/">Back</a>' ) ;
}
users [ req . session . username ] . password = hashPW ( password1 ) ;
2024-12-28 00:07:12 -05:00
res . redirect ( '/' ) ;
2024-12-05 13:05:35 -05:00
} ) ;
2024-12-14 23:26:38 -05:00
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" ) ;
2024-12-28 19:31:45 -05:00
const URL = await QRCode . toDataURL ( '/useticket?t=' + ticket + '&h=' + hashQR ( ticket , username ) ) ;
return res . send ( { owner : username , qrcode : URL , magiclink : GetMagicLink ( username ) } ) ;
2024-12-14 23:26:38 -05:00
} )
2024-12-26 00:33:23 -05:00
app . post ( '/qrcodesu' , requireSuperUser , async ( req , res ) => {
const ticket = req . body . ticket ;
2024-12-28 19:31:45 -05:00
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 ) } ) ;
2024-12-26 00:33:23 -05:00
} )
2024-12-28 19:31:45 -05:00
2024-12-28 00:07:12 -05:00
app . get ( '/settings' , requireSuperUser , ( req , res ) => {
res . render ( 'settings' , { username : req . session . username , superuser : req . session . superuser , message : "" } )
} ) ;
2024-12-05 13:05:35 -05:00
2024-12-28 00:07:12 -05:00
app . post ( '/wipedb' , requireSuperUser , ( req , res ) => {
2024-12-28 19:31:45 -05:00
InitDatabase ( ) ;
2024-12-28 00:07:12 -05:00
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 ] ; }
2024-12-28 19:31:45 -05:00
res . render ( 'settings' , { username : req . session . username , superuser : req . session . superuser , message : "Purged " + count + " revoked tickets" } )
2024-12-28 00:07:12 -05:00
} ) ;
2024-12-05 13:05:35 -05:00
// Start the server
app . listen ( PORT , ( ) => {
console . log ( ` Server is running at http://localhost: ${ PORT } ` ) ;
} ) ;