Integrate new account backend

This commit is contained in:
2025-09-28 14:23:31 +02:00
parent 6e93cfdf2c
commit 0315241d76
9 changed files with 4086 additions and 3793 deletions

View File

@@ -3,48 +3,58 @@ import db from './storage/db';
const createUser = ( uid: string, username: string, email: string ): Promise<boolean> => {
return new Promise( ( resolve, reject ) => {
db.writeDataSimple( 'users', 'uid', uid, { 'uid': uid, 'username': username, 'email': email } ).then( () => {
db.writeDataSimple( 'users', 'uid', uid, {
'uid': uid,
'username': username,
'email': email
} ).then( () => {
resolve( true );
} ).catch( err => {
reject( err );
} );
} )
.catch( err => {
reject( err );
} );
} );
}
};
const saveUserData = ( uid: string, data: object ): Promise<boolean> => {
return new Promise( ( resolve, reject ) => {
db.writeDataSimple( 'users', 'uid', uid, { 'data': data } ).then( () => {
db.writeDataSimple( 'users', 'uid', uid, {
'data': data
} ).then( () => {
resolve( true );
} ).catch( err => {
reject( err );
} );
} )
.catch( err => {
reject( err );
} );
} );
}
};
const checkUser = ( uid: string ): Promise<boolean> => {
return new Promise( ( resolve, reject ) => {
db.checkDataAvailability( 'users', 'uid', uid ).then( res => {
resolve( res );
} ).catch( err => {
reject( err );
} )
.catch( err => {
reject( err );
} );
} );
}
};
const getUserData = ( uid: string ): Promise<object> => {
return new Promise( ( resolve, reject ) => {
db.getDataSimple( 'users', 'uid', uid ).then( data => {
resolve( data );
} ).catch( err => {
reject( err );
} );
} )
.catch( err => {
reject( err );
} );
} );
}
};
export default {
createUser,
saveUserData,
checkUser,
getUserData
}
};

View File

@@ -3,8 +3,6 @@ import path from 'path';
import fs from 'fs';
import jwt from 'jsonwebtoken';
import cors from 'cors';
import account from './account';
import sdk from 'oauth-janishutz-client-server';
import {
createServer
} from 'node:http';
@@ -15,26 +13,19 @@ import crypto from 'node:crypto';
import type {
Room, Song
} from './definitions';
import storeSDK from 'store.janishutz.com-sdk';
import bodyParser from 'body-parser';
// ┌ ┐
// │ Handle FOSS vs paid version │
// └ ┘
const isFossVersion = true;
declare let __dirname: string | undefined;
import storeSDK from '@janishutz/store-sdk';
import sdk from '@janishutz/login-sdk-server';
// import storeSDK from './sdk/store-sdk-stub';
// import sdk from '@janishutz/login-sdk-server-stubs';
if ( typeof __dirname === 'undefined' ) {
__dirname = path.resolve( path.dirname( '' ) );
}
// TODO: Change config file, as well as in main.ts, index.html, oauth, if deploying there
// const sdkConfig = JSON.parse( fs.readFileSync( path.join(
// __dirname,
// '/config/sdk.config.testing.json'
// ) ).toString() );
const sdkConfig = JSON.parse( fs.readFileSync( path.join(
__dirname,
'/config/sdk.config.secret.json'
) ).toString() );
const run = () => {
const app = express();
@@ -45,10 +36,6 @@ const run = () => {
} ) );
if ( !isFossVersion ) {
// storeSDK.configure( JSON.parse( fs.readFileSync( path.join(
// __dirname,
// '/config/store-sdk.config.testing.json'
// ) ).toString() ) );
storeSDK.configure( JSON.parse( fs.readFileSync( path.join(
__dirname,
'/config/store-sdk.config.secret.json'
@@ -58,26 +45,49 @@ const run = () => {
const httpServer = createServer( app );
if ( !isFossVersion ) {
const sdkConfig = JSON.parse( fs.readFileSync( path.join(
__dirname,
'/config/sdk.config.secret.json'
) ).toString() );
// Load id.janishutz.com SDK and allow signing in
sdk.routes( app, ( uid: string ) => {
return new Promise( ( resolve, reject ) => {
account.checkUser( uid ).then( stat => {
resolve( stat );
} )
.catch( e => {
reject( e );
} );
} );
}, ( uid: string, email: string, username: string ) => {
return new Promise( ( resolve, reject ) => {
account.createUser( uid, username, email ).then( stat => {
resolve( stat );
} )
.catch( e => {
reject( e );
} );
} );
}, sdkConfig );
sdk.setUp(
{
'prod': false,
'service': {
'serviceID': 'jh-music',
'serviceToken': sdkConfig[ 'token' ]
},
'user-agent': sdkConfig[ 'ua' ],
'sessionType': 'memory',
'frontendURL': 'https://music.janishutz.com',
'corsWhitelist': [ 'https://music.janishutz.com' ],
'recheckTimeout': 300 * 1000,
'advancedVerification': 'sdk'
},
app,
() => {
return new Promise( resolve => {
resolve( true );
} );
},
() => {
return new Promise( resolve => {
resolve( true );
} );
},
() => {
return new Promise( resolve => {
resolve( true );
} );
},
() => {
return new Promise( resolve => {
resolve( true );
} );
},
sdkConfig
);
}
// Websocket for events
@@ -291,7 +301,7 @@ const run = () => {
} ) }\n\n` );
const sid = sdk.getSessionID( request );
if ( sdk.checkAuth( request ) ) {
if ( sdk.getSignedIn( request ) ) {
importantClients[ sid ] = {
'response': response,
'room': String( request.query.room )
@@ -488,38 +498,35 @@ const run = () => {
app.get(
'/createRoomToken',
sdk.loginCheck(),
( request: express.Request, response: express.Response ) => {
if ( sdk.checkAuth( request ) ) {
// eslint-disable-next-line no-constant-binary-expression
const roomName = String( request.query.roomName ) ?? '';
// eslint-disable-next-line no-constant-binary-expression
const roomName = String( request.query.roomName ) ?? '';
if ( !socketData[ roomName ] ) {
const roomToken = crypto.randomUUID();
if ( !socketData[ roomName ] ) {
const roomToken = crypto.randomUUID();
socketData[ roomName ] = {
'playbackStart': 0,
'playbackStatus': false,
'playlist': [],
'playlistIndex': 0,
'roomName': roomName,
'roomToken': roomToken,
'ownerUID': sdk.getUserData( request ).uid,
'useAntiTamper': request.query.useAntiTamper === 'true'
? true : false,
};
response.send( roomToken );
} else {
if (
socketData[ roomName ].ownerUID
=== sdk.getUserData( request ).uid
) {
response.send( socketData[ roomName ].roomToken );
} else {
response.status( 409 ).send( 'ERR_CONFLICT' );
}
}
socketData[ roomName ] = {
'playbackStart': 0,
'playbackStatus': false,
'playlist': [],
'playlistIndex': 0,
'roomName': roomName,
'roomToken': roomToken,
'ownerUID': sdk.getUID( request ),
'useAntiTamper': request.query.useAntiTamper === 'true'
? true : false,
};
response.send( roomToken );
} else {
response.status( 403 ).send( 'ERR_FORBIDDEN' );
if (
socketData[ roomName ].ownerUID
=== sdk.getUID( request )
) {
response.send( socketData[ roomName ].roomToken );
} else {
response.status( 409 ).send( 'ERR_CONFLICT' );
}
}
}
);
@@ -569,46 +576,43 @@ const run = () => {
const checkIfOwned = ( request: express.Request ): Promise<boolean> => {
return new Promise( ( resolve, reject ) => {
if ( sdk.checkAuth( request ) ) {
const userData = sdk.getUserData( request );
const uid = sdk.getUID( request );
if ( ownedCache[ userData.uid ] ) {
resolve( ownedCache[ userData.uid ] );
} else {
storeSDK.getSubscriptions( userData.uid )
.then( stat => {
const now = new Date().getTime();
if ( ownedCache[ uid ] ) {
resolve( ownedCache[ uid ] );
} else {
storeSDK.getSubscriptions( uid )
.then( stat => {
const now = new Date().getTime();
for ( const sub in stat ) {
if ( stat[ sub ].expires - now > 0
for ( const sub in stat ) {
if ( stat[ sub ].expires - now > 0
&& (
stat[ sub ].id
=== 'com.janishutz.MusicPlayer.subscription'
|| stat[ sub ].id
=== 'com.janishutz.MusicPlayer.subscription-month'
)
) {
ownedCache[ userData.uid ] = true;
resolve( true );
}
) {
ownedCache[ uid ] = true;
resolve( true );
}
}
ownedCache[ userData.uid ] = false;
resolve( false );
} )
.catch( e => {
console.error( e );
reject( 'ERR_NOT_OWNED' );
} );
}
} else {
reject( 'ERR_AUTH_REQUIRED' );
ownedCache[ uid ] = false;
resolve( false );
} )
.catch( e => {
console.error( e );
reject( 'ERR_NOT_OWNED' );
} );
}
} );
};
app.get(
'/checkUserStatus',
sdk.loginCheck(),
( request: express.Request, response: express.Response ) => {
checkIfOwned( request )
.then( owned => {

View File

@@ -1,41 +0,0 @@
import express from 'express';
import expressSession from 'express-session';
import crypto from 'node:crypto';
// TODO: Use also express-session to make it work with getSessionID and session referencing
const checkAuth = ( request: express.Request ) => {
return true;
}
export interface AuthSDKConfig {
token: string;
name: string;
client: string;
backendURL: string;
failReturnURL: string;
useSecureCookie?: boolean;
}
declare module 'express-session' {
interface SessionData {
isAuth: boolean;
uid: string;
username: string;
email: string;
additionalData: object;
}
}
const getUserData = ( request: express.Request ) => {
if ( !request.session.uid ) {
request.session.uid = crypto.randomUUID();
request.session.username = 'FOSS-Version';
request.session.email = 'example@example.com';
}
return { 'email': request.session.email, 'username': request.session.username, 'uid': request.session.uid, 'id': request.session.id };
}
export default {
checkAuth,
getUserData
}

View File

@@ -1,11 +1,15 @@
const getSubscriptions = ( uid: string ) => {
return [ {
'id': 'com.janishutz.MusicPlayer.subscription',
'expires': new Date().getTime() + 200000,
'status': true
} ];
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const getSubscriptions = ( _uid: string ) => {
return [
{
'id': 'com.janishutz.MusicPlayer.subscription',
'expires': new Date().getTime() + 200000,
'status': true
}
];
};
export default {
getSubscriptions,
}
};

View File

@@ -11,20 +11,20 @@ import path from 'path';
import fs from 'fs';
import * as sqlDB from './mysqldb.js';
declare let __dirname: string | undefined
if ( typeof( __dirname ) === 'undefined' ) {
declare let __dirname: string | undefined;
if ( typeof __dirname === 'undefined' ) {
__dirname = path.resolve( path.dirname( '' ) );
} else {
__dirname = __dirname + '/../';
}
const dbRef = {
'user': 'music_users',
const dbRef = {
'user': 'music_users',
'users': 'music_users',
};
const dbh = new sqlDB.SQLDB();
let dbh = new sqlDB.SQLDB();
dbh.connect();
/**
@@ -32,7 +32,7 @@ dbh.connect();
* @returns {undefined}
*/
const initDB = (): undefined => {
( async() => {
( async () => {
console.log( '[ DB ] Setting up...' );
dbh.setupDB();
console.log( '[ DB ] Setting up complete!' );
@@ -48,11 +48,16 @@ const initDB = (): undefined => {
*/
const getDataSimple = ( db: string, column: string, searchQuery: string ): Promise<object> => {
return new Promise( ( resolve, reject ) => {
dbh.query( { 'command': 'getFilteredData', 'property': column, 'searchQuery': searchQuery }, dbRef[ db ] ).then( data => {
dbh.query( {
'command': 'getFilteredData',
'property': column,
'searchQuery': searchQuery
}, dbRef[ db ] ).then( data => {
resolve( data );
} ).catch( error => {
reject( error );
} );
} )
.catch( error => {
reject( error );
} );
} );
};
@@ -66,34 +71,36 @@ const getDataSimple = ( db: string, column: string, searchQuery: string ): Promi
* @param {string} nameOfMatchingParam Which properties should be matched to get the data, e.g. order.user_id=users.id
* @returns {Promise<Object | Error>} Returns all records from the db and all matching data specified with the matchingParam from the secondTable.
*/
const getDataWithLeftJoinFunction = ( db: string, column: string, searchQuery: string, secondTable: string, columns: object, nameOfMatchingParam: string ): Promise<Object> => {
/*
LeftJoin (Select values in first table and return all corresponding values of second table):
- operation.property (the column to search for the value),
const getDataWithLeftJoinFunction = ( db: string, column: string, searchQuery: string, secondTable: string, columns: object, nameOfMatchingParam: string ): Promise<object> => {
/*
LeftJoin (Select values in first table and return all corresponding values of second table):
- operation.property (the column to search for the value),
- operation.searchQuery (the value to search for [will be sanitised by method])
- operation.columns (The columns of both tables to be selected, list of objects: { 'db': TABLE NAME, 'column': COLUMN NAME })
- operation.secondTable (The second table to perform Join operation with)
- operation.matchingParam (Which properties should be matched to get the data, e.g. order.user_id=users.id)
*/
return new Promise( ( resolve, reject ) => {
let settings = {
'command': 'LeftJoin',
'property': column,
'searchQuery': searchQuery,
const settings = {
'command': 'LeftJoin',
'property': column,
'searchQuery': searchQuery,
'selection': '',
'secondTable': dbRef[ secondTable ],
'secondTable': dbRef[ secondTable ],
'matchingParam': dbRef[ db ] + '.' + nameOfMatchingParam + '=' + dbRef[ secondTable ] + '.' + nameOfMatchingParam,
}
for ( let el in columns ) {
};
for ( const el in columns ) {
settings.selection += dbRef[ columns[ el ].db ] + '.' + columns[ el ].column + ',';
}
settings.selection = settings.selection.slice( 0, settings.selection.length - 1 );
dbh.query( settings, dbRef[ db ] ).then( data => {
resolve( data );
} ).catch( error => {
reject( error );
} );
} )
.catch( error => {
reject( error );
} );
} );
};
@@ -102,13 +109,16 @@ const getDataWithLeftJoinFunction = ( db: string, column: string, searchQuery: s
* @param {string} db The database of which all data should be retrieved
* @returns {Promise<object>} Returns an object containing all data
*/
const getData = ( db: string ): Promise<Object> => {
const getData = ( db: string ): Promise<object> => {
return new Promise( ( resolve, reject ) => {
dbh.query( { 'command': 'getAllData' }, dbRef[ db ] ).then( data => {
dbh.query( {
'command': 'getAllData'
}, dbRef[ db ] ).then( data => {
resolve( data );
} ).catch( error => {
reject( error );
} );
} )
.catch( error => {
reject( error );
} );
} );
};
@@ -120,25 +130,40 @@ const getData = ( db: string ): Promise<Object> => {
* @param {string} data The data to write. Also include the column & searchQuery parameters, if they also need to be added
* @returns {Promise<object>} Returns a promise that resolves to the interaction module return.
*/
const writeDataSimple = ( db: string, column: string, searchQuery: string, data: any ): Promise<Object> => {
const writeDataSimple = ( db: string, column: string, searchQuery: string, data: any ): Promise<object> => {
return new Promise( ( resolve, reject ) => {
dbh.query( { 'command': 'checkDataAvailability', 'property': column, 'searchQuery': searchQuery }, dbRef[ db ] ).then( res => {
dbh.query( {
'command': 'checkDataAvailability',
'property': column,
'searchQuery': searchQuery
}, dbRef[ db ] ).then( res => {
if ( res.length > 0 ) {
dbh.query( { 'command': 'updateData', 'property': column, 'searchQuery': searchQuery, 'newValues': data }, dbRef[ db ] ).then( dat => {
dbh.query( {
'command': 'updateData',
'property': column,
'searchQuery': searchQuery,
'newValues': data
}, dbRef[ db ] ).then( dat => {
resolve( dat );
} ).catch( error => {
reject( error );
} );
} )
.catch( error => {
reject( error );
} );
} else {
dbh.query( { 'command': 'addData', 'data': data }, dbRef[ db ] ).then( dat => {
dbh.query( {
'command': 'addData',
'data': data
}, dbRef[ db ] ).then( dat => {
resolve( dat );
} ).catch( error => {
reject( error );
} );
} )
.catch( error => {
reject( error );
} );
}
} ).catch( error => {
reject( error );
} );
} )
.catch( error => {
reject( error );
} );
} );
};
@@ -151,11 +176,16 @@ const writeDataSimple = ( db: string, column: string, searchQuery: string, data:
*/
const deleteDataSimple = ( db: string, column: string, searchQuery: string ): Promise<object> => {
return new Promise( ( resolve, reject ) => {
dbh.query( { 'command': 'deleteData', 'property': column, 'searchQuery': searchQuery }, dbRef[ db ] ).then( dat => {
dbh.query( {
'command': 'deleteData',
'property': column,
'searchQuery': searchQuery
}, dbRef[ db ] ).then( dat => {
resolve( dat );
} ).catch( error => {
reject( error );
} );
} )
.catch( error => {
reject( error );
} );
} );
};
@@ -168,15 +198,20 @@ const deleteDataSimple = ( db: string, column: string, searchQuery: string ): Pr
*/
const checkDataAvailability = ( db: string, column: string, searchQuery: string ): Promise<boolean> => {
return new Promise( ( resolve, reject ) => {
dbh.query( { 'command': 'checkDataAvailability', 'property': column, 'searchQuery': searchQuery }, dbRef[ db ] ).then( res => {
dbh.query( {
'command': 'checkDataAvailability',
'property': column,
'searchQuery': searchQuery
}, dbRef[ db ] ).then( res => {
if ( res.length > 0 ) {
resolve( true );
} else {
resolve( false );
}
} ).catch( error => {
reject( error );
} );
} )
.catch( error => {
reject( error );
} );
} );
};
@@ -186,16 +221,18 @@ const checkDataAvailability = ( db: string, column: string, searchQuery: string
* @returns {Promise<object>} Returns the data from all files
*/
const getJSONDataBatch = async ( files: Array<string> ): Promise<object> => {
let allFiles = {};
for ( let file in files ) {
const allFiles = {};
for ( const file in files ) {
try {
allFiles[ files[ file ] ] = await getJSONData( files[ file ] );
} catch( err ) {
} catch ( err ) {
allFiles[ files[ file ] ] = 'ERROR: ' + err;
}
}
return allFiles;
}
};
/**
* Load all data from a JSON file
@@ -245,7 +282,7 @@ const getJSONDataSimple = ( file: string, identifier: string ): Promise<object>
* @param {string} file The file to be loaded (path relative to root)
* @returns {object} Returns the JSON file
*/
const getJSONDataSync = ( file: string ): Object => {
const getJSONDataSync = ( file: string ): object => {
return JSON.parse( fs.readFileSync( path.join( __dirname + '/' + file ) ).toString() );
};
@@ -263,14 +300,17 @@ const writeJSONDataSimple = ( db: string, identifier: string, values: any ) => {
reject( 'Error occurred: Error trace: ' + error );
} else {
let dat = {};
if ( data.byteLength > 0 ) {
dat = JSON.parse( data.toString() ) ?? {};
}
dat[ identifier ] = values;
fs.writeFile( path.join( __dirname + '/../../data/' + db + '.json' ), JSON.stringify( dat ), ( error ) => {
fs.writeFile( path.join( __dirname + '/../../data/' + db + '.json' ), JSON.stringify( dat ), error => {
if ( error ) {
reject( 'Error occurred: Error trace: ' + error );
}
resolve( true );
} );
}
@@ -286,7 +326,7 @@ const writeJSONDataSimple = ( db: string, identifier: string, values: any ) => {
*/
const writeJSONData = ( db: string, data: object ): Promise<boolean> => {
return new Promise( ( resolve, reject ) => {
fs.writeFile( path.join( __dirname + '/../../data/' + db + '.json' ), JSON.stringify( data ), ( error ) => {
fs.writeFile( path.join( __dirname + '/../../data/' + db + '.json' ), JSON.stringify( data ), error => {
if ( error ) {
reject( 'Error occurred: Error trace: ' + error );
} else {
@@ -309,14 +349,17 @@ const deleteJSONDataSimple = ( db: string, identifier: string ): Promise<boolean
reject( 'Error occurred: Error trace: ' + error );
} else {
let dat = {};
if ( data.byteLength > 0 ) {
dat = JSON.parse( data.toString() ) ?? {};
}
delete dat[ identifier ];
fs.writeFile( path.join( __dirname + '/../../data/' + db + '.json' ), JSON.stringify( dat ), ( error ) => {
fs.writeFile( path.join( __dirname + '/../../data/' + db + '.json' ), JSON.stringify( dat ), error => {
if ( error ) {
reject( 'Error occurred: Error trace: ' + error );
}
resolve( true );
} );
}
@@ -324,7 +367,19 @@ const deleteJSONDataSimple = ( db: string, identifier: string ): Promise<boolean
} );
};
export default { initDB, checkDataAvailability, deleteDataSimple, deleteJSONDataSimple, getData,
getDataSimple, getDataWithLeftJoinFunction, getJSONData, getJSONDataBatch, getJSONDataSimple,
getJSONDataSync, writeDataSimple, writeJSONData, writeJSONDataSimple
export default {
initDB,
checkDataAvailability,
deleteDataSimple,
deleteJSONDataSimple,
getData,
getDataSimple,
getDataWithLeftJoinFunction,
getJSONData,
getJSONDataBatch,
getJSONDataSimple,
getJSONDataSync,
writeDataSimple,
writeJSONData,
writeJSONDataSimple
};

View File

@@ -11,8 +11,9 @@ import mysql from 'mysql';
import fs from 'fs';
import path from 'path';
declare let __dirname: string | undefined
if ( typeof( __dirname ) === 'undefined' ) {
declare let __dirname: string | undefined;
if ( typeof __dirname === 'undefined' ) {
__dirname = path.resolve( path.dirname( '' ) );
} else {
__dirname = __dirname + '/../';
@@ -22,21 +23,35 @@ if ( typeof( __dirname ) === 'undefined' ) {
// to the whitelist of the database
class SQLConfig {
command: string;
property?: string;
searchQuery?: string;
selection?: string;
query?: string;
newValues?: object;
secondTable?: string;
matchingParam?: string;
data?: object;
}
class SQLDB {
sqlConnection: mysql.Connection;
isRecovering: boolean;
config: object;
constructor () {
this.config = JSON.parse( '' + fs.readFileSync( path.join( __dirname + '/config/db.config.secret.json' ) ) );
this.sqlConnection = mysql.createConnection( this.config );
@@ -46,19 +61,23 @@ class SQLDB {
connect () {
return new Promise( ( resolve, reject ) => {
const self = this;
if ( this.isRecovering ) {
console.log( '[ SQL ] Attempting to recover from critical error' );
this.sqlConnection = mysql.createConnection( this.config );
this.isRecovering = false;
}
this.sqlConnection.connect( ( err ) => {
this.sqlConnection.connect( err => {
if ( err ) {
console.error( '[ SQL ]: An error ocurred whilst connecting: ' + err.stack );
reject( err );
return;
}
console.log( '[ SQL ] Connected to database successfully' );
self.sqlConnection.on( 'error', ( err ) => {
self.sqlConnection.on( 'error', err => {
if ( err.code === 'ECONNRESET' ) {
self.isRecovering = true;
setTimeout( () => {
@@ -81,22 +100,24 @@ class SQLDB {
async setupDB () {
this.sqlConnection.query( 'SELECT @@default_storage_engine;', ( error, results ) => {
if ( error ) throw error;
if ( results[ 0 ][ '@@default_storage_engine' ] !== 'InnoDB' ) throw 'DB HAS TO USE InnoDB!';
} );
this.sqlConnection.query( 'CREATE TABLE music_users ( account_id INT ( 10 ) NOT NULL AUTO_INCREMENT, email TINYTEXT NOT NULL, uid TINYTEXT, lang TINYTEXT, username TINYTEXT, settings VARCHAR( 5000 ), PRIMARY KEY ( account_id ) ) ENGINE=INNODB;', ( error ) => {
this.sqlConnection.query( 'CREATE TABLE music_users ( account_id INT ( 10 ) NOT NULL AUTO_INCREMENT, email TINYTEXT NOT NULL, uid TINYTEXT, lang TINYTEXT, username TINYTEXT, settings VARCHAR( 5000 ), PRIMARY KEY ( account_id ) ) ENGINE=INNODB;', error => {
if ( error ) if ( error.code !== 'ER_TABLE_EXISTS_ERROR' ) throw error;
return 'DONE';
} );
}
query ( operation: SQLConfig, table: string ): Promise<Array<Object>> {
query ( operation: SQLConfig, table: string ): Promise<Array<object>> {
return new Promise( ( resolve, reject ) => {
/*
/*
Possible operation.command values (all need the table argument of the method call):
- getAllData: no additional instructions needed
- getFilteredData:
- operation.property (the column to search for the value),
- getFilteredData:
- operation.property (the column to search for the value),
- operation.searchQuery (the value to search for [will be sanitised by method])
- InnerJoin (Select values that match in both tables):
@@ -106,15 +127,15 @@ class SQLDB {
- operation.secondTable (The second table to perform Join operation with)
- operation.matchingParam (Which properties should be matched to get the data, e.g. order.user_id=users.id)
- LeftJoin (Select values in first table and return all corresponding values of second table):
- operation.property (the column to search for the value),
- LeftJoin (Select values in first table and return all corresponding values of second table):
- operation.property (the column to search for the value),
- operation.searchQuery (the value to search for [will be sanitised by method])
- operation.selection (The columns of both tables to be selected, e.g. users.name, orders.id)
- operation.secondTable (The second table to perform Join operation with)
- operation.matchingParam (Which properties should be matched to get the data, e.g. order.user_id=users.id)
- RightJoin (Select values in second table and return all corresponding values of first table):
- operation.property (the column to search for the value),
- RightJoin (Select values in second table and return all corresponding values of first table):
- operation.property (the column to search for the value),
- operation.searchQuery (the value to search for [will be sanitised by method])
- operation.selection (The columns of both tables to be selected, e.g. users.name, orders.id)
- operation.secondTable (The second table to perform Join operation with)
@@ -122,25 +143,26 @@ 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)
- operation.property (the column to search for the value),
- operation.property (the column to search for the value),
- operation.searchQuery (the value to search for [will be sanitised by method])
- checkDataAvailability:
- operation.property (the column to search for the value),
- operation.property (the column to search for the value),
- operation.searchQuery (the value to search for [will be sanitised by method])
- fullCustomCommand:
- operation.query (the SQL instruction to be executed) --> NOTE: This command will not be sanitised, so use only with proper sanitisation!
*/
let command = '';
if ( operation.command === 'getAllData' ) {
command = 'SELECT * FROM ' + table;
} else if ( operation.command === 'getFilteredData' || operation.command === 'checkDataAvailability' ) {
@@ -150,19 +172,23 @@ class SQLDB {
} else if ( operation.command === 'addData' ) {
let keys = '';
let values = '';
for ( let key in operation.data ) {
for ( const key in operation.data ) {
keys += String( key ) + ', ';
values += this.sqlConnection.escape( String( operation.data[ key ] ) ) + ', ' ;
values += this.sqlConnection.escape( String( operation.data[ key ] ) ) + ', ';
}
command = 'INSERT INTO ' + table + ' (' + keys.slice( 0, keys.length - 2 ) + ') VALUES (' + values.slice( 0, values.length - 2 ) + ');';
command = 'INSERT INTO ' + table + ' (' + keys.slice( 0, keys.length - 2 ) + ') VALUES (' + values.slice( 0, values.length - 2 ) + ');';
} else if ( operation.command === 'updateData' ) {
if ( !operation.property || !operation.searchQuery ) reject( 'Refusing to run destructive command: Missing Constraints' );
else {
command = 'UPDATE ' + table + ' SET ';
let updatedValues = '';
for ( let value in operation.newValues ) {
for ( const value in operation.newValues ) {
updatedValues += value + ' = ' + this.sqlConnection.escape( String( operation.newValues[ value ] ) ) + ', ';
}
command += updatedValues.slice( 0, updatedValues.length - 2 );
command += ' WHERE ' + operation.property + ' = ' + this.sqlConnection.escape( operation.searchQuery );
}
@@ -178,12 +204,17 @@ class SQLDB {
} else if ( operation.command === 'RightJoin' ) {
command = 'SELECT ' + operation.selection + ' FROM ' + table + ' RIGHT JOIN ' + operation.secondTable + ' ON ' + operation.matchingParam + ' WHERE ' + operation.property + ' = ' + this.sqlConnection.escape( operation.searchQuery );
}
this.sqlConnection.query( command, ( error, results ) => {
if ( error ) reject( error );
resolve( results );
} );
} );
}
}
export { SQLConfig, SQLDB };
export {
SQLConfig, SQLDB
};