2024-08-30 08:28:01 -06:00
const express = require ( 'express' ) ;
const mariadb = require ( 'mariadb' ) ;
const cors = require ( 'cors' ) ;
2024-11-29 08:53:26 -07:00
require ( 'dotenv' ) . config ( { debug : true } )
2024-08-30 08:28:01 -06:00
const app = express ( ) ;
app . use ( express . json ( ) ) ;
app . use ( cors ( ) ) ;
const version = "V1.1" ;
2024-11-29 09:28:29 -07:00
const dbip = process . env . DBIP || "192.168.2.55" ;
2024-08-30 08:28:01 -06:00
const dbusername = process . env . DBUSERNAME || "root" ;
const dbpassword = process . env . DBPASSWORD || "U&P7UH1mhRG@JF2K" ;
const db = process . env . DB || "LicenseTracker" ;
// Database details
const pool = mariadb . createPool ( {
2024-11-29 08:46:24 -07:00
dbip ,
dbusername ,
dbpassword ,
db
2024-08-30 08:28:01 -06:00
} ) ;
//Initialize pollrate variable
2024-08-30 10:24:37 -06:00
let pollrate = "5000" ; // 5 Seconds
2024-08-30 08:28:01 -06:00
//Api Key
const token = "&v94gt8ZHFTTTeuT" ;
//Allows the clients to check if the server is alive
app . get ( '/api' , ( req , res ) => {
console . log ( "Welcome to the MPE licenses api" ) ;
res . sendStatus ( 200 ) . json ( { success : "Welcome to the MPE licenses api" } ) ;
} ) ;
//Allows the clients to check if the server is alive
app . get ( '/api/healthcheck' , ( req , res ) => {
res . sendStatus ( 200 ) ;
} ) ;
//This will show all of the records for a specific program. It takes a program name as a parameter in the body and returns all records that have that program name
app . post ( '/api/programRecords' , async ( req , res ) => {
try {
const { programName } = req . body ;
if ( ! programName ) {
return res . status ( 400 ) . json ( { error : "Program name is required" } ) ;
}
// Get the records for the specified program name
const records = await getProgramRecords ( programName ) ;
res . status ( 200 ) . json ( records ) ;
} catch ( error ) {
console . log ( "[ERROR]: There was an error retrieving the program records. More info: " + error ) ;
res . status ( 500 ) . json ( { error : "Internal Server Error" } ) ;
}
} ) ;
//Test to see if a connection to the database is feasible
async function testDatabaseConnection ( ) {
let connection ;
try {
// Create a connection to the database
connection = await pool . getConnection ( ) ;
// Test the connection
console . log ( 'Database connection successful' ) ;
2024-11-29 08:53:26 -07:00
console . log ( dbip + " is our database" ) ;
2024-08-30 08:28:01 -06:00
} catch ( error ) {
console . error ( 'Database connection failed:' , error . message ) ;
2024-11-29 08:53:26 -07:00
console . log ( "Tried database " + dbip ) ;
2024-08-30 08:28:01 -06:00
} finally {
// Ensure the connection is closed
if ( connection ) {
try {
await connection . end ( ) ;
} catch ( endError ) {
console . error ( 'Error closing the connection:' , endError . message ) ;
}
}
}
}
const getProgramRecords = async ( programName ) => {
let n = ( pollRate / 1000 ) + ( ( pollRate / 1000 ) * 0.25 ) ; // Milliseconds to seconds
let records = [ ] ;
//console.log('Retrieving program records');
try {
const conn = await pool . getConnection ( ) ;
try {
// Get the records for the specified program name within the time constraints
const query = `
SELECT * FROM usedLicenses
WHERE program = ? AND date >= DATE _SUB ( NOW ( ) , INTERVAL ? SECOND )
` ;
records = await conn . query ( query , [ programName , n ] ) ;
} catch ( error ) {
console . log ( "[ERROR]: There was an error reading from the database. More info: " + error ) ;
}
finally {
conn . release ( ) ; // Ensure the connection is released
}
} catch ( error ) {
console . log ( "[ERROR]: There was an error reading from the database. More info: " + error ) ;
}
return records ;
} ;
// This gives the client a list of all of the programs that are currently being requested
app . post ( '/api/licenseCount' , async ( req , res ) => {
try {
const { programName } = req . body ;
if ( ! programName ) {
return res . status ( 400 ) . json ( { error : "Program name is required" } ) ;
}
// Call getLicensesCount function for the provided programName
const count = await getLicensesCount ( programName ) ;
//console.log(count.toString());
res . status ( 200 ) . json ( { program : programName , count : count . toString ( ) } ) ;
} catch ( error ) {
console . log ( "[ERROR]: There was an error retrieving the license count. More info: " + error ) ;
res . status ( 500 ) . json ( { error : "Internal Server Error" } ) ;
}
} ) ;
const getLicensesCount = async ( programName ) => {
let n = ( pollRate / 1000 ) + ( ( pollRate / 1000 ) * 0.25 ) ; // Milliseconds to seconds
let count = 0 ;
try {
const conn = await pool . getConnection ( ) ;
try {
// Get the current time minus n seconds
const query = `
SELECT COUNT ( * ) AS count FROM usedLicenses
WHERE program = ? AND date >= DATE _SUB ( NOW ( ) , INTERVAL ? SECOND )
` ;
const result = await conn . query ( query , [ programName , n ] ) ;
count = result [ 0 ] . count ;
} catch ( error ) {
console . log ( "[ERROR]: There was an error reading from the database. More info: " + error ) ;
} finally {
conn . release ( ) ; // Ensure the connection is released
}
} catch ( error ) {
console . log ( "[ERROR]: There was an error reading from the database. More info: " + error ) ;
}
return count ;
} ;
// This gives the client a list of all of the programs that are currently being requested
app . get ( '/api/programs' , async ( req , res ) => {
try {
const programsResult = await getRequestedPrograms ( ) ;
// Extract program names from the result
const programNames = programsResult . map ( record => record . name ) ;
res . status ( 200 ) . json ( programNames ) ;
} catch ( error ) {
console . log ( "[ERROR]: There was an error retrieving the programs. More info: " + error ) ;
res . status ( 500 ) . json ( { error : "Internal Server Error" } ) ;
}
} ) ;
const getRequestedPrograms = async ( ) => {
let result = [ ] ;
try {
const conn = await pool . getConnection ( ) ;
try {
// Query to get all requested programs
const query = ` SELECT name FROM requestedLicenses ` ;
result = await conn . query ( query ) ;
} catch ( error ) {
console . log ( "[ERROR]: There was an error retrieving program names. More info: " + error ) ;
} finally {
conn . release ( ) ; // Ensure the connection is released
}
} catch ( error ) {
console . log ( "[ERROR]: There was an error retrieving program names. More info: " + error ) ;
}
return result ;
} ;
// This tells the client how often to phone home
app . get ( '/api/pollrate' , async ( req , res ) => {
try {
pollrate = await getPollRate ( ) ;
// Used to change the poll rate
res . json ( { pollrate : ` ${ pollrate } ` } ) ; // In milliseconds
} catch ( error ) {
console . log ( "[ERROR]: There was an error retrieving the poll rate. More info: " + error ) ;
res . status ( 500 ) . json ( { error : "Internal Server Error" } ) ;
}
} ) ;
const getPollRate = async ( ) => {
// Debugging! console.log('Retrieving poll rate');
try {
const conn = await pool . getConnection ( ) ;
try {
// Query to get the poll rate where sid = 1
const query = ` SELECT pollrate FROM settings WHERE sid = 1 ` ;
const result = await conn . query ( query ) ;
if ( result . length > 0 ) {
pollRate = result [ 0 ] . pollrate ; // Extract poll rate from the result
} else {
console . log ( "[INFO]: No poll rate found for sid = 1." ) ;
}
} catch ( error ) {
console . log ( "[ERROR]: There was an error retrieving the poll rate. More info: " + error ) ;
} finally {
conn . release ( ) ; // Ensure the connection is released
}
} catch ( error ) {
console . log ( "[ERROR]: There was an error retrieving the poll rate. More info: " + error ) ;
}
return pollRate ;
} ;
// Retrieves currently used licenses from the database
app . get ( '/api/licenses' , async ( req , res ) => {
try {
// Retrieve the currently used licenses from the database
const licenses = await getLicenses ( ) ;
res . json ( licenses ) ;
} catch ( error ) {
console . log ( "[ERROR]: There was an error retrieving licenses. More info: " + error ) ;
res . status ( 500 ) . json ( { error : "Internal Server Error" } ) ;
}
} ) ;
// Retrieves currently used licenses from the database
const getLicenses = async ( ) => {
let n = ( pollRate / 1000 ) + ( ( pollRate / 1000 ) * 0.25 ) ; //Milliseconds to seconds
let records = [ ] ;
//console.log('Retrieving list');
try {
const conn = await pool . getConnection ( ) ;
try {
// Get the current time minus n minutes
const query = `
SELECT * FROM usedLicenses
WHERE date >= DATE _SUB ( NOW ( ) , INTERVAL ? SECOND )
` ;
records = await conn . query ( query , [ n ] ) ;
} catch ( error ) {
console . log ( "[ERROR]: There was an error reading from the database. More info: " + error ) ;
} finally {
conn . release ( ) ; // Ensure the connection is released
}
} catch ( error ) {
console . log ( "[ERROR]: There was an error reading from the database. More info: " + error ) ;
}
return records ;
} ;
app . get ( '/api/count' , async ( req , res ) => {
try {
// Retrieve the currently used licenses count along with the total and available licenses
const programs = await getLicenseCounts ( ) ;
res . json ( { programs } ) ;
} catch ( error ) {
console . log ( "[ERROR]: There was an error retrieving licenses. More info: " + error ) ;
res . status ( 500 ) . json ( { error : "Internal Server Error" } ) ;
}
} ) ;
// Retrieves currently used licenses count, total licenses, and available licenses grouped by program
const getLicenseCounts = async ( ) => {
let n = ( pollRate / 1000 ) + ( ( pollRate / 1000 ) * 0.25 ) ; // Convert pollRate to seconds and add 25%
let records = [ ] ;
try {
const conn = await pool . getConnection ( ) ;
try {
// Get the current time minus n seconds and count the number of licenses per program
const query = `
SELECT ul . program , COUNT ( * ) as activecount , rl . total ,
( rl . total - COUNT ( * ) ) as available
FROM usedLicenses ul
JOIN requestedLicenses rl ON ul . program = rl . name
WHERE ul . date >= DATE _SUB ( NOW ( ) , INTERVAL ? SECOND )
GROUP BY ul . program , rl . total
` ;
const results = await conn . query ( query , [ n ] ) ;
// Convert the results to handle bigint serialization
records = results . map ( row => ( {
program : row . program ,
activecount : String ( row . activecount ) , // Convert bigint to string
total : String ( row . total ) , // Convert bigint to string
available : String ( row . available ) // Convert bigint to string
} ) ) ;
} catch ( error ) {
console . log ( "[ERROR]: There was an error reading from the database. More info: " + error ) ;
} finally {
conn . release ( ) ; // Ensure the connection is released
}
} catch ( error ) {
console . log ( "[ERROR]: There was an error reading from the database. More info: " + error ) ;
}
return records ;
} ;
2024-08-30 09:00:52 -06:00
app . get ( '/api/zabbix' , async ( req , res ) => {
try {
const programs = await getZabbixCounts ( ) ;
res . json ( programs ) ; // Directly return the reshaped JSON
} catch ( error ) {
console . log ( "[ERROR]: There was an error retrieving licenses. More info: " + error ) ;
res . status ( 500 ) . json ( { error : "Internal Server Error" } ) ;
}
} ) ;
const getZabbixCounts = async ( ) => {
let n = ( pollRate / 1000 ) + ( ( pollRate / 1000 ) * 0.25 ) ;
let records = { } ;
try {
const conn = await pool . getConnection ( ) ;
try {
const query = `
SELECT ul . program , COUNT ( * ) as activecount , rl . total ,
( rl . total - COUNT ( * ) ) as available
FROM usedLicenses ul
JOIN requestedLicenses rl ON ul . program = rl . name
WHERE ul . date >= DATE _SUB ( NOW ( ) , INTERVAL ? SECOND )
GROUP BY ul . program , rl . total
` ;
const results = await conn . query ( query , [ n ] ) ;
results . forEach ( row => {
records [ row . program ] = {
activecount : String ( row . activecount ) ,
total : String ( row . total ) ,
available : String ( row . available )
} ;
} ) ;
} catch ( error ) {
console . log ( "[ERROR]: There was an error reading from the database. More info: " + error ) ;
} finally {
conn . release ( ) ;
}
} catch ( error ) {
console . log ( "[ERROR]: There was an error reading from the database. More info: " + error ) ;
}
return records ;
} ;
2024-08-30 08:28:01 -06:00
// Clients use this route to tell the server what programs they have open
app . post ( '/api/currentPrograms' , ( req , res ) => {
//console.log('Post request received:', JSON.stringify(req.body));
const { applicationName , machineName , apiKey } = req . body ;
if ( apiKey == token ) { //Verify the client is one of our own
addProgram ( applicationName , machineName ) ;
}
// Handle the request further if needed
res . sendStatus ( 200 ) ;
} ) ;
// Adds a program and machine name to the database
const addProgram = async ( programName , machineName ) => {
// Get the current date and format it for MariaDB datetime
const date = new Date ( ) . toISOString ( ) . slice ( 0 , 19 ) . replace ( 'T' , ' ' ) ;
try {
const conn = await pool . getConnection ( ) ;
try {
// Check if a record with the same machineName and programName already exists
const existingRecord = await conn . query ( 'SELECT * FROM usedLicenses WHERE machine = ? AND program = ?' , [ machineName , programName ] ) ;
if ( existingRecord . length > 0 ) {
// If record exists, update its timestamp
await conn . query ( 'UPDATE usedLicenses SET date = ? WHERE machine = ? AND program = ?' , [ date , machineName , programName ] ) ;
//console.log("[SUCCESS]: Existing record updated in the database.");
} else {
// If no record exists, insert a new one
await conn . query ( 'INSERT INTO usedLicenses (program, machine, date) VALUES (?, ?, ?)' , [ programName , machineName , date ] ) ;
//console.log("[SUCCESS]: New record added to the database.");
}
} finally {
conn . release ( ) ; // Ensure the connection is released
}
} catch ( error ) {
console . log ( "[ERROR]: There was an error adding/updating the record in the database. More info: " + error ) ;
}
} ;
setTimeout ( ( ) => {
testDatabaseConnection ( ) ;
} , 1000 ) ;
//Start the api and display the port it is running on
const port = process . env . PORT || 3000 ;
app . listen ( port , ( ) => {
console . log ( ` ********************************** ` ) ;
console . log ( ` ** ** ` ) ;
console . log ( ` * MPE License Tracker API * ` ) ;
console . log ( ` ** ** ` ) ;
console . log ( ` * ${ version } * ` ) ;
console . log ( ` ** ** ` ) ;
console . log ( ` ********************************** ` ) ;
console . log ( ` API is running on port ${ port } ` ) ;
// Run the function to test the database connection
//testDatabaseConnection();
//console.log(`Configured to use database IP ${dbip}`);
//console.log(`Using user ${dbusername} for database`);
} ) ;