ticket gen working + various changes

This commit is contained in:
2023-08-07 12:24:52 +02:00
parent 5cbf624284
commit ed38edd880
24 changed files with 211 additions and 98 deletions

View File

@@ -28,10 +28,9 @@ module.exports = ( app, settings ) => {
if ( request.body.mail && request.body.password ) {
pwdmanager.checkpassword( request.body.mail, request.body.password ).then( data => {
request.session.username = request.body.mail;
if ( data ) {
if ( data.status ) {
request.session.username = request.body.mail;
// TODO: Check if user has 2fa enabled
if ( settings.twoFA === 'standard' ) {
if ( data.twoFA === 'simple' ) {
( async () => {
let tok = twoFA.registerStandardAuthentication()[ 'token' ];
let ipRetrieved = request.headers[ 'x-forwarded-for' ];
@@ -40,7 +39,7 @@ module.exports = ( app, settings ) => {
request.session.token = tok;
response.send( { 'status': '2fa' } );
} )();
} else if ( settings.twoFA === 'enhanced' ) {
} else if ( data.twoFA === 'enhanced' ) {
( async () => {
let res = twoFA.registerEnhancedAuthentication();
let ipRetrieved = request.headers[ 'x-forwarded-for' ];
@@ -50,7 +49,7 @@ module.exports = ( app, settings ) => {
response.send( { 'status': '2fa+', 'code': res.code } );
} )();
} else {
request.session.loggedInUser = true;
request.session.loggedInAdmin = true;
response.send( { 'status': 'ok' } );
}
} else {
@@ -63,7 +62,6 @@ module.exports = ( app, settings ) => {
} );
app.get( '/admin/2fa', ( request, response ) => {
// TODO: Add multi language
let tokType = twoFA.verifySimple( request.query.token );
if ( tokType === 'standard' ) {
request.session.loggedInAdmin = true;

View File

@@ -42,7 +42,13 @@ class GETHandler {
} );
} else if ( call === 'getLocations' ) {
db.getJSONData( 'locations' ).then( data => {
resolve( data );
resolve( data ?? {} );
} ).catch( error => {
reject( { 'code': 500, 'error': error } );
} );
} else if ( call === 'getTicketTemplate' ) {
db.getJSONDataSimple( 'tickets', query.ticket ).then( data => {
resolve( data ?? {} );
} ).catch( error => {
reject( { 'code': 500, 'error': error } );
} );

View File

@@ -24,7 +24,7 @@ module.exports.checkpassword = ( username, password ) => {
if ( data ) {
if ( data[ 0 ] ) {
bcrypt.compare( password, data[ 0 ].pass ).then( res => {
resolve( res );
resolve( { 'status': res, 'twoFA': data[ 0 ].two_fa } );
} );
} else {
resolve( false );

View File

@@ -101,6 +101,6 @@ app.use( ( request, response ) => {
console.log( '\n\n[ Server ] loading complete!\n\n' );
const PORT = process.env.PORT || 8081;
const PORT = process.env.PORT || 8080;
console.log( '[ Server ] listening on port ' + PORT );
http.createServer( app ).listen( PORT );

View File

@@ -30,7 +30,7 @@ class GETHandler {
if ( query.event ) {
db.getJSONDataSimple( 'booked', query.event ).then( data => {
db.getDataSimple( 'temp', 'user_id', session.id ).then( dat => {
resolve( { 'booked': data ? data.booked : {}, 'user': dat[ 0 ] ? JSON.parse( dat[ 0 ].data )[ query.event ] ?? {} : {} } );
resolve( { 'booked': data ?? {}, 'user': dat[ 0 ] ? JSON.parse( dat[ 0 ].data )[ query.event ] ?? {} : {} } );
} );
} ).catch( error => {
reject( { 'code': 500, 'message': error } );

View File

@@ -39,7 +39,9 @@ class POSTHandler {
return;
}
transmit[ data.eventID ][ data.id ] = data;
// TODO: Prevent seat selection if already taken (also if in booked!)
// TODO: Respect max ticket count per user
// TODO: maybe move to per event setting
let totalUserTickets = 0;
for ( let event in transmit ) {
for ( let ticket in transmit[ event ] ) {

File diff suppressed because one or more lines are too long

View File

@@ -58,6 +58,16 @@ module.exports.writeDataSimple = ( db, column, searchQuery, data ) => {
} );
};
module.exports.deleteDataSimple = ( db, column, searchQuery ) => {
return new Promise( ( resolve, reject ) => {
dbh.query( { 'command': 'deleteData', 'property': column, 'searchQuery': searchQuery }, dbRef[ db ] ).then( dat => {
resolve( dat );
} ).catch( error => {
reject( error );
} );
} );
};
module.exports.checkDataAvailability = ( db, column, searchQuery ) => {
return new Promise( ( resolve, reject ) => {
dbh.query( { 'command': 'checkDataAvailability', 'property': column, 'searchQuery': searchQuery }, dbRef[ db ] ).then( res => {

View File

@@ -55,7 +55,7 @@ class SQLDB {
if ( error ) throw error;
if ( results[ 0 ][ '@@default_storage_engine' ] !== 'InnoDB' ) return 'DB HAS TO USE InnoDB!';
} );
this.sqlConnection.query( 'CREATE TABLE libreevent_users ( account_id INT ( 10 ) NOT NULL AUTO_INCREMENT, email TINYTEXT NOT NULL, pass TEXT, name TEXT, first_name TEXT, two_fa TINYTEXT, user_data VARCHAR( 60000 ), mail_confirmed TINYTEXT, marketing_ok TINYTEXT, PRIMARY KEY ( account_id ) ) ENGINE=INNODB;', ( error ) => {
this.sqlConnection.query( 'CREATE TABLE libreevent_users ( account_id INT ( 10 ) NOT NULL AUTO_INCREMENT, email TINYTEXT NOT NULL, pass TEXT, name TEXT, first_name TEXT, two_fa TINYTEXT, user_data VARCHAR( 60000 ), mail_confirmed TINYTEXT, marketing TINYTEXT, PRIMARY KEY ( account_id ) ) ENGINE=INNODB;', ( error ) => {
if ( error ) if ( error.code !== 'ER_TABLE_EXISTS_ERROR' ) throw error;
this.sqlConnection.query( 'CREATE TABLE libreevent_orders ( order_id INT ( 10 ) NOT NULL AUTO_INCREMENT, order_name TINYTEXT, account_id INT ( 10 ) NOT NULL, tickets VARCHAR( 60000 ), processed TINYTEXT, PRIMARY KEY ( order_id ), FOREIGN KEY ( account_id ) REFERENCES libreevent_users( account_id ) ) ENGINE=INNODB;', ( error ) => {
if ( error ) if ( error.code !== 'ER_TABLE_EXISTS_ERROR' ) throw error;
@@ -103,7 +103,11 @@ class SQLDB {
- addData:
- operation.data (key-value pair with all data as values and column to insert into as key)
- deleteData:
- operation.property (the column to search for the value)
- operation.searchQuery (the value to search for [will be sanitised by method])
- updateData:
- operation.newValues (a object with keys being the column and value being the value to be inserted into that column, values are being
sanitised by the function)

View File

@@ -40,6 +40,7 @@ module.exports = ( app, settings ) => {
if ( dat[ 0 ] ) {
db.getJSONData( 'events' ).then( events => {
let data = JSON.parse( dat[ 0 ].data );
console.log( data );
( async () => {
for ( let event in data ) {
for ( let item in data[ event ] ) {
@@ -116,7 +117,7 @@ module.exports = ( app, settings ) => {
}
} );
app.post( '/payments/webhook', bodyParser.raw( { type: 'application/json' } ), ( req, res ) => {
app.post( '/payments/webhook', bodyParser.raw( { type: 'application/json' } ), async ( req, res ) => {
const payload = req.body;
const sig = req.headers[ 'stripe-signature' ];
@@ -139,9 +140,26 @@ module.exports = ( app, settings ) => {
db.getDataSimple( 'users', 'email', sessionReference[ event.data.object.id ][ 'email' ] ).then( user => {
if ( user[ 0 ] ) {
console.log( sessionReference[ event.data.object.id ][ 'tok' ] );
const tickets = JSON.parse( dat[ 0 ].data );
db.writeDataSimple( 'orders', 'account_id', user[ 0 ].account_id, { 'account_id': user[ 0 ].account_id, 'tickets': dat[ 0 ].data, 'order_name': sessionReference[ event.data.object.id ][ 'tok' ] } ).then( () => {
TicketGenerator.generateTickets( sessionReference[ event.data.object.id ] );
} );
db.getJSONData( 'booked' ).then( ret => {
let booked = ret ?? {};
for ( let event in tickets ) {
if ( !booked[ String( event ) ] ) {
booked[ String( event ) ] = {};
}
for ( let tik in tickets[ event ] ) {
booked[ event ][ tik ] = tickets[ event ][ tik ];
}
}
db.writeJSONData( 'booked', booked );
} );
db.deleteDataSimple( 'temp', 'user_id', sessionReference[ event.data.object.id ][ 'tok' ] ).catch( error => {
console.error( '[ STRIPE ] ERROR whilst deleting data from DB: ' + error );
} );
} else {
console.log( sessionReference[ event.data.object.id ][ 'email' ] );
console.error( 'user not found' );

View File

@@ -35,7 +35,7 @@ class TicketGenerator {
this.runningTickets = {};
}
// TODO: Save to disk in case of crash of server / reboot / whatever
// TODO: Save to disk / DB in case of crash of server / reboot / whatever
// and continue processing once back online
generateTickets ( order ) {
this.ticketQueue[ this.jobId ] = { 'order': order };
@@ -114,15 +114,18 @@ class TicketGenerator {
for ( let event in order ) {
const template = this.tickets[ event ];
for ( let ticket in order[ event ] ) {
const data = [ {
'locationAndTime': this.events[ event ][ 'date' ],
'ticketName': order[ event ][ ticket ][ 'name' ],
'ticketQRCode': ord[ 0 ].order_name + '_' + order[ event ][ ticket ][ 'id' ],
} ];
const page = await pdfLib.PDFDocument.load( await pdfme.generate( { 'template': template, 'inputs': data } ) );
const p = await doc.copyPages( page, page.getPageIndices() );
pages.push( p );
p.forEach( ( page ) => doc.addPage( page ) );
for ( let tik = 0; tik < ( order[ event ][ ticket ].count ?? 1 ); tik++ ) {
const data = [ {
'eventName': this.events[ event ][ 'name' ],
'locationAndTime': new Date( this.events[ event ][ 'date' ] ).toLocaleString(),
'ticketName': order[ event ][ ticket ][ 'name' ],
'ticketQRCode': ord[ 0 ].order_name + '_' + order[ event ][ ticket ][ 'id' ],
} ];
const page = await pdfLib.PDFDocument.load( await pdfme.generate( { 'template': template, 'inputs': data } ) );
const p = await doc.copyPages( page, page.getPageIndices() );
pages.push( p );
p.forEach( ( page ) => doc.addPage( page ) );
}
}
}
const f = path.join( __dirname + '/store/' + ord[ 0 ].order_name + '.pdf' );

View File

@@ -49,8 +49,8 @@ module.exports = ( app, settings ) => {
app.post( '/user/login', bodyParser.json(), ( request, response ) => {
if ( request.body.mail && request.body.password ) {
pwdmanager.checkpassword( request.body.mail, request.body.password ).then( data => {
request.session.username = request.body.mail;
if ( data.status ) {
request.session.username = request.body.mail;
if ( data.twoFA === 'simple' ) {
( async () => {
let tok = twoFA.registerStandardAuthentication()[ 'token' ];
@@ -83,7 +83,6 @@ module.exports = ( app, settings ) => {
} );
app.get( '/user/2fa', ( request, response ) => {
// TODO: Add multi language
let tokType = twoFA.verifySimple( request.query.token );
if ( tokType === 'standard' ) {
request.session.loggedInUser = true;
@@ -154,7 +153,7 @@ module.exports = ( app, settings ) => {
mailManager.sendMail( request.body.mail, await twoFA.generateSignupEmail( tok, settings.yourDomain, settings.name ), 'Confirm your email', settings.mailSender );
} )();
pwdmanager.hashPassword( request.body.password ).then( hash => {
db.writeDataSimple( 'users', 'email', request.body.mail, { 'email': request.body.mail, 'pass': hash, 'first_name': request.body.firstName, 'name': request.body.name, 'two_fa': 'disabled', 'user_data': JSON.stringify( { 'country': request.body.country } ) } ).then( () => {
db.writeDataSimple( 'users', 'email', request.body.mail, { 'email': request.body.mail, 'pass': hash, 'first_name': request.body.firstName, 'name': request.body.name, 'two_fa': 'disabled', 'user_data': JSON.stringify( { 'country': request.body.country } ), 'marketing': request.body.newsletter ? generator.generateToken( 60 ) : null } ).then( () => {
request.session.loggedInUser = true;
request.session.username = request.body.mail;
response.send( 'ok' );

View File

@@ -5,6 +5,6 @@
"db": "mysql",
"payments": "stripe",
"name": "libreevent",
"yourDomain": "http://localhost:8081",
"yourDomain": "http://localhost:8080",
"mailSender": "libreevent <info@libreevent.janishutz.com>"
}

View File

@@ -3,11 +3,13 @@
- make pricing groups changeable in UI (event categories)
- create function that parses DB every 15 minutes and clears out junk
- create function that parses DB every 15 minutes and clears out junk --> Also update data in db when user goes to purchase to prevent clearing during purchase
- Require user to confirm email before purchasing
- Guest purchase in the future (remove from matura shit)
- Guest purchase in the future (maybe remove from matura)
- Create password changing endpoint (to reset forgotten pwd)
- Add Admin profile (page to change account settings per person like changing pwd)
@@ -19,7 +21,9 @@
- Implement Permission system
- Seat numbering
- Seat numbering!!
- Add localization for date
- add webpack (or any other minifying tool) to project website to decrease file size (OPTIONAL)

View File

@@ -169,7 +169,7 @@
} );
if ( !sessionStorage.getItem( 'seatplan-history' ) ) {
sessionStorage.setItem( 'seatplaTODO:n-history', JSON.stringify( { '1': this.scaleDown( this.draggables ) } ) );
sessionStorage.setItem( 'seatplan-history', JSON.stringify( { '1': this.scaleDown( this.draggables ) } ) );
}
let history = sessionStorage.getItem( 'seatplan-history' ) ? JSON.parse( sessionStorage.getItem( 'seatplan-history' ) ) : {};

View File

@@ -9,7 +9,6 @@
<template>
<div id="window">
<h2>Seat plan: {{ event.name }}</h2>
<div class="parent" id="parent" @wheel="( e ) => { handleScroll( e ); }" @mousemove="( e ) => { handleDrag( e ); }" @mousedown="( e ) => { setOffset( e ); }">
<div class="content-parent">
<Vue3DraggableResizable v-for="draggable in draggables" :initW="draggable.w" :initH="draggable.h" :x="draggable.x" :y="draggable.y" :w="draggable.w" :h="draggable.h"
@@ -42,7 +41,7 @@
<button title="Reset zoom [=]" @click="zoom( 1 );"><span class="material-symbols-outlined">center_focus_strong</span></button>
<button title="Zoom out [-]" @click="zoom( -0.2 )"><span class="material-symbols-outlined">zoom_out</span></button>
</div>
<sideCartView :cart="cart" ref="cart"></sideCartView>
<sideCartView :cart="cart" :name="event.name" ref="cart"></sideCartView>
<notifications ref="notification" location="topright"></notifications>
<popups ref="popups" size="normal" @data="data => { reserveTicket( data ) }"
@ticket="data => { standingTicketHandling( data ) }"></popups>
@@ -156,7 +155,6 @@
}
this.seatChecks();
// TODO: Optimise for odd screen sizes and aspect ratios and fucking webkit
// TODO: Trim scroll box to about 200px more than seatplan size
sessionStorage.setItem( 'seatplan', JSON.stringify( this.scaleDown( this.draggables ) ) );
window.addEventListener( 'visibilitychange', ( e ) => {
@@ -171,15 +169,6 @@
if ( res.status === 200 ) {
let unavailableSeats = {};
res.json().then( data => {
for ( let seat in data.booked ) {
if ( data.booked[ seat ] ) {
if ( !unavailableSeats[ data.booked[ seat ].component ] ) {
unavailableSeats[ data.booked[ seat ].component ];
}
unavailableSeats[ data.booked[ seat ].component ][ data.booked[ seat ].id ] = 'nav';
}
}
for ( let seat in data.reserved ) {
if ( data.reserved[ seat ] ) {
if ( !unavailableSeats[ data.reserved[ seat ].component ] ) {
@@ -198,6 +187,15 @@
}
}
for ( let seat in data.booked ) {
if ( data.booked[ seat ] ) {
if ( !unavailableSeats[ data.booked[ seat ].component ] ) {
unavailableSeats[ data.booked[ seat ].component ];
}
unavailableSeats[ data.booked[ seat ].component ][ data.booked[ seat ].id ] = 'nav';
}
}
let tickets = {};
if ( this.cart[ this.event.eventID ] ) {
tickets = this.cart[ this.event.eventID ][ 'tickets' ];
@@ -449,8 +447,7 @@
if ( data.data[ group ] > 0 ) {
const options = {
method: 'post',
// TODO: Add correct name here as well once it is working at all
body: JSON.stringify( { 'id': 'ticket' + data.component + '_' + group, 'component': data.component, 'ticketOption': '', 'eventID': this.event.eventID, 'count': data.data[ group ], 'category': this.draggables[ data.component ].category, 'name': 'Ticket ' } ),
body: JSON.stringify( { 'id': 'ticket' + data.component + '_' + group, 'component': data.component, 'ticketOption': group, 'eventID': this.event.eventID, 'count': data.data[ group ], 'category': this.draggables[ data.component ].category, 'name': 'Ticket ' + data.component + ' (' + this.event.ageGroups[ group ].name + ')' } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
@@ -510,7 +507,7 @@
.parent {
height: 80vh;
width: 70vw;
top: 17vh;
top: 90px;
left: 5vw;
position: absolute;
border: black 1px solid;
@@ -548,7 +545,7 @@
.toolbar {
display: flex;
position: fixed;
top: 17vh;
top: 90px;
left: 5.5vw;
}
.toolbar button {

View File

@@ -9,6 +9,7 @@
<template>
<div id="sideCartView">
<h1>Seat plan: {{ name }}</h1>
<h2>Cart</h2>
<div v-if="Object.keys( cart ).length > 0" style="height: 100%; width: 100%;">
<div class="scroll-wrapper">
@@ -64,6 +65,10 @@ export default {
'height': {
type: Number,
default: 17
},
'name': {
type: String,
default: ''
}
},
data() {
@@ -92,7 +97,7 @@ export default {
position: fixed;
right: 0;
height: 100vh;
top: 17vh;
top: 90px;
width: 25vw;
background-color: var( --accent-background );
color: var( --secondary-color );

View File

@@ -21,7 +21,7 @@
</td>
</tr>
</table>
<p>Detailed explanation of payment gateways can be found <a href="https://libreevent.janishutz.com/docs/payments" target="_blank">here</a>. You may install more payment gateway integrations in the plugins section.</p>
<p>Detailed explanation of payment gateways can be found <a href="https://libreevent.janishutz.com/docs/payments" target="_blank">here</a>. You may install more payment gateway integrations in the plugins section. Only one may be used at any given time.</p>
<div class="admin-settings">
<h2>Admin Accounts</h2>
@@ -103,8 +103,8 @@
'value': 'stripe'
},
'adyen': {
'displayName':'Adyen',
'value': 'adyen'
'displayName':'Payrexx',
'value': 'payrexx'
},
}
},

File diff suppressed because one or more lines are too long

View File

@@ -43,6 +43,12 @@
<input type="password" v-model="formData[ 'password2' ]" name="password2" id="password2" required><br><br>
</td>
</tr>
<tr>
<td>
<label for="news">Do you want to potentially get newsletter?</label><br>
<input type="checkbox" v-model="formData[ 'newsletter' ]" name="news" id="news"><br><br>
</td>
</tr>
</table>
<!-- TODO: Ask for permission to send emails (Make question sound really optional) -->
</form>