licensetrackerbackend/app.js
2024-11-29 08:53:26 -07:00

420 lines
15 KiB
JavaScript

const express = require('express');
const mariadb = require('mariadb');
const cors = require('cors');
require('dotenv').config({ debug: true })
const app = express();
app.use(express.json());
app.use(cors());
const version = "V1.1";
const dbip = process.env.DBIP || "192.168.2.54";
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({
dbip,
dbusername,
dbpassword,
db
});
//Initialize pollrate variable
let pollrate = "5000"; // 5 Seconds
//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');
console.log(dbip + " is our database");
} catch (error) {
console.error('Database connection failed:', error.message);
console.log("Tried database " + dbip);
} 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;
};
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;
};
// 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`);
});