Compare commits
2 commits
Author | SHA1 | Date | |
---|---|---|---|
|
013d6d4c62 | ||
|
2f849f63e7 |
60
analytics.js
Normal file
60
analytics.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
const pool = global.db_pool;
|
||||
if (!pool) {
|
||||
throw new Error('Database pool is not defined');
|
||||
}
|
||||
|
||||
const addAnalytic = (tag) => {
|
||||
pool.query("SELECT * FROM analytics WHERE tag = ?", [tag]).then((rows) => {
|
||||
if (rows.length === 0) {
|
||||
conn.query("INSERT INTO analytics (tag, count) VALUES (?, 1)", [tag]).catch(err => {
|
||||
console.error('Error creating analytics:', err);
|
||||
});
|
||||
} else {
|
||||
conn.query("UPDATE analytics SET count = count + 1 WHERE tag = ?", [tag]).catch(err => {
|
||||
console.error('Error updating analytics:', err);
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Error checking analytics:', err);
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
}
|
||||
|
||||
const dailyAnalytic = (tag) => { // This is a bit more complex, but it's just a daily count
|
||||
// check if the tag, and tag_date exists. If the date is not today reset count to 0 and date to today
|
||||
// If the date is today, increment count
|
||||
const date = new Date();
|
||||
const today = `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
|
||||
|
||||
pool.query("SELECT * FROM dailyAnalytics WHERE tag = ? AND tag_date = ?", [tag, today]).then((rows) => {
|
||||
if (rows.length === 0) {
|
||||
conn.query("INSERT INTO dailyAnalytics (tag, tag_date, count) VALUES (?, ?, 1)", [tag, today]).catch(err => {
|
||||
console.error('Error creating daily analytics:', err);
|
||||
});
|
||||
} else {
|
||||
conn.query("UPDATE dailyAnalytics SET count = count + 1 WHERE tag = ? AND tag_date = ?", [tag, today]).catch(err => {
|
||||
console.error('Error updating daily analytics:', err);
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Error checking daily analytics:', err);
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
}
|
||||
|
||||
const logCall = (caller, callee) => {
|
||||
pool.query('INSERT INTO callLogs (caller, callee, timestamp) VALUES (?, ?, ?)',
|
||||
[caller, callee, Math.floor(Date.now())]).catch(err => {
|
||||
console.error('Error logging call:', err);
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addAnalytic,
|
||||
dailyAnalytic,
|
||||
logCall
|
||||
}
|
719
index.js
719
index.js
|
@ -21,6 +21,8 @@ const mariadb = require('mariadb');
|
|||
const bcrypt = require("bcrypt")
|
||||
const crypto = require("crypto")
|
||||
const app = express();
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const port = process.env.SERVER_PORT || 3000;
|
||||
|
||||
const pool = mariadb.createPool({
|
||||
|
@ -33,6 +35,11 @@ const pool = mariadb.createPool({
|
|||
});
|
||||
const saltRounds = 10;
|
||||
|
||||
global.db_pool = pool; // Make the pool global so we can use it in other files
|
||||
|
||||
const analytics = require("./analytics");
|
||||
|
||||
// Run migrations and reset admin user if desired
|
||||
pool.getConnection().then((conn) => {
|
||||
require("./migrations")(pool).then(() => {
|
||||
conn.query("SELECT * FROM users WHERE id = 1").then((row) => {
|
||||
|
@ -80,537 +87,14 @@ app.set('views', __dirname + '/views');
|
|||
// Static files
|
||||
app.use(express.static('public'));
|
||||
|
||||
const addAnalytic = (tag) => {
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query("SELECT * FROM analytics WHERE tag = ?", [tag]).then((rows) => {
|
||||
if (rows.length === 0) {
|
||||
conn.query("INSERT INTO analytics (tag, count) VALUES (?, 1)", [tag]).catch(err => {
|
||||
console.error('Error creating analytics:', err);
|
||||
});
|
||||
} else {
|
||||
conn.query("UPDATE analytics SET count = count + 1 WHERE tag = ?", [tag]).catch(err => {
|
||||
console.error('Error updating analytics:', err);
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Error checking analytics:', err);
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('Error getting connection:', err);
|
||||
});
|
||||
}
|
||||
|
||||
const dailyAnalytic = (tag) => { // This is a bit more complex, but it's just a daily count
|
||||
// check if the tag, and tag_date exists. If the date is not today reset count to 0 and date to today
|
||||
// If the date is today, increment count
|
||||
const date = new Date();
|
||||
const today = `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
|
||||
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query("SELECT * FROM dailyAnalytics WHERE tag = ? AND tag_date = ?", [tag, today]).then((rows) => {
|
||||
if (rows.length === 0) {
|
||||
conn.query("INSERT INTO dailyAnalytics (tag, tag_date, count) VALUES (?, ?, 1)", [tag, today]).catch(err => {
|
||||
console.error('Error creating daily analytics:', err);
|
||||
});
|
||||
} else {
|
||||
conn.query("UPDATE dailyAnalytics SET count = count + 1 WHERE tag = ? AND tag_date = ?", [tag, today]).catch(err => {
|
||||
console.error('Error updating daily analytics:', err);
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Error checking daily analytics:', err);
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('Error getting connection:', err);
|
||||
});
|
||||
}
|
||||
|
||||
app.use((req, res, next) => {
|
||||
if (req.path.startsWith("/api/v1")) {
|
||||
addAnalytic("apiCalls");
|
||||
analytics.addAnalytic("apiCalls");
|
||||
};
|
||||
next();
|
||||
})
|
||||
|
||||
// Admin routes
|
||||
|
||||
// admin/logout
|
||||
app.get('/admin/logout', (req, res) => {
|
||||
req.session.destroy();
|
||||
res.redirect('/admin/login');
|
||||
});
|
||||
|
||||
app.get('/admin/login', (req, res) => {
|
||||
res.render('admin/login');
|
||||
});
|
||||
|
||||
app.get('/admin', (req, res) => {
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.redirect('/admin/login');
|
||||
return;
|
||||
}
|
||||
res.render('admin/index', { user: req.session.user });
|
||||
});
|
||||
|
||||
app.get('/admin/create', (req, res) => {
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.redirect('/admin/login');
|
||||
return;
|
||||
}
|
||||
res.render('admin/create', { user: req.session.user });
|
||||
});
|
||||
|
||||
app.get('/admin/route/:id', (req, res) => {
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.redirect('/admin/login');
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM routes WHERE id = ?', [req.params.id]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (!row) {
|
||||
res.status(404).send('Not Found');
|
||||
return;
|
||||
}
|
||||
res.render('admin/edit', { user: req.session.user, data: row });
|
||||
}).catch(err => {
|
||||
console.error('Error getting route:', err);
|
||||
res.status(500).send('Internal server error');
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/admin/login', (req, res) => {
|
||||
const username = req.body.username;
|
||||
const password = req.body.password;
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query("SELECT * FROM users WHERE username = ?", [String(username)]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (!row) {
|
||||
res.status(401).send('Unauthorized (Not Found)');
|
||||
return;
|
||||
}
|
||||
bcrypt.compare(password, row.passwordHash, (err, result) => {
|
||||
if (err) {
|
||||
console.error('Error comparing password:', err);
|
||||
res.status(500).send('Internal server error');
|
||||
return;
|
||||
}
|
||||
if (result) {
|
||||
req.session.adminAuthenticated = true;
|
||||
req.session.user = row.username;
|
||||
res.redirect('/admin');
|
||||
} else {
|
||||
res.status(401).send('Unauthorized');
|
||||
}
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('Error getting user:', err);
|
||||
res.status(500).send('Internal server error');
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
app.get('/api/v1/admin/routes', (req, res) => { // Get all routes
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM routes').then((rows) => {
|
||||
res.json(rows);
|
||||
}).catch(err => {
|
||||
console.error('Error getting routes:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/v1/admin/route/:id', (req, res) => { // Get route
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM routes WHERE id = ?', [req.params.id]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (!row) {
|
||||
res.status(404).json({ error: 'Not Found' });
|
||||
return;
|
||||
}
|
||||
res.json(row);
|
||||
}).catch(err => {
|
||||
console.error('Error getting route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/api/v1/admin/route', (req, res) => { // Create a new route
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
const server = req.body.server;
|
||||
const port = req.body.port;
|
||||
const auth = req.body.auth || "from-astrocom";
|
||||
const secret = req.body.secret || crypto.randomBytes(15).toString('hex');
|
||||
const block_start = req.body.block_start;
|
||||
const block_length = req.body.block_length || 9999;
|
||||
const apiKey = crypto.randomBytes(32).toString('hex');
|
||||
const contact = req.body.contact || "Unknown";
|
||||
// Validate all inputs exist
|
||||
if (!server || !port || !block_start) {
|
||||
res.status(400).json({ error: 'Bad Request' });
|
||||
return;
|
||||
}
|
||||
// Check if route already exists (OR conditions on server, and block range)
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM routes WHERE block_start <= ? AND block_start + block_length >= ?', [block_start, block_start]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (row) {
|
||||
res.status(409).json({ error: 'Conflict' });
|
||||
return;
|
||||
} else {
|
||||
conn.query('INSERT INTO routes (server, port, auth, secret, block_start, block_length, apiKey, contact) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[server, port, auth, secret, block_start, block_length, apiKey, contact]).then(() => {
|
||||
res.status(201).json({ message: 'Created' });
|
||||
}).catch(err => {
|
||||
console.error('Error creating route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Error checking for existing route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.put('/api/v1/admin/route/:id', (req, res) => { // Update a route
|
||||
// Check if authenticated
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
// Check if route exists
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM routes WHERE id = ?', [req.params.id]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (!row) {
|
||||
res.status(404).json({ error: 'Not Found' });
|
||||
return;
|
||||
}
|
||||
// Update route
|
||||
const server = req.body.server || row.server;
|
||||
const port = req.body.port || row.port;
|
||||
const auth = req.body.auth || row.auth;
|
||||
const secret = req.body.secret || row.secret;
|
||||
const block_start = req.body.block_start || row.block_start;
|
||||
const block_length = req.body.block_length || row.block_length;
|
||||
const contact = req.body.contact || row.contact;
|
||||
console.log(`Updating ${req.params.id} to ${server}:${port} with ${auth}:${secret} for ${block_start} - ${block_start + block_length}. Contact: ${contact}`);
|
||||
conn.query('UPDATE routes SET server = ?, port = ?, auth = ?, secret = ?, block_start = ?, block_length = ?, contact = ? WHERE id = ?',
|
||||
[server, port, auth, secret, block_start, block_length, contact, req.params.id]).then(() => {
|
||||
res.json({ message: 'Updated' });
|
||||
}).catch(err => {
|
||||
console.error('Error updating route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('Error getting route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.delete('/api/v1/admin/route/:id', (req, res) => { // Delete a route
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(async conn => {
|
||||
await conn.query('DELETE FROM directory WHERE route = ?', [req.params.id])
|
||||
conn.query('DELETE FROM routes WHERE id = ?', [req.params.id]).then(() => {
|
||||
res.json({ message: 'Deleted' });
|
||||
}).catch(err => {
|
||||
console.error('Error deleting route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.delete('/api/v1/admin/directory/:number', (req, res) => { // Delete a directory entry
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
const number = Number(req.params.number);
|
||||
if (!number) {
|
||||
res.status(400).json({ error: 'Bad Request' });
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('DELETE FROM directory WHERE number = ?', [number]).then(() => {
|
||||
res.status(200).json({ message: 'Deleted' });
|
||||
}).catch(err => {
|
||||
console.error('Error deleting directory entry:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/api/v1/admin/callLogs", (req, res) => {
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
// if ?page is set, return 100 results from that page, if no page assume page 1
|
||||
const page = Number(req.query.page) || 1;
|
||||
const offset = (page - 1) * 100;
|
||||
// Get full count of call logs to calculate total pages
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query("SELECT COUNT(*) as count FROM callLogs").then((rows) => {
|
||||
const totalPages = Math.ceil(rows[0].count / 100);
|
||||
conn.query("SELECT * FROM callLogs ORDER BY timestamp DESC LIMIT 100 OFFSET ?", [offset]).then((rows) => {
|
||||
res.json({ totalPages, page, data: rows });
|
||||
}).catch(err => {
|
||||
console.error('Error getting call logs:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('Error getting call log count:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// == END ADMIN ROUTES ==
|
||||
|
||||
// == User routes == // allows someone to log in with their API key and add entries to the Directory (as long as the number is within their block range)
|
||||
app.get('/user', (req, res) => {
|
||||
if (!req.session.userAuthenticated) {
|
||||
res.redirect('/user/login');
|
||||
return;
|
||||
}
|
||||
res.render('user/index', { user: req.session.user });
|
||||
});
|
||||
|
||||
app.get('/user/login', (req, res) => {
|
||||
res.render('user/login');
|
||||
});
|
||||
|
||||
app.post('/user/login', (req, res) => {
|
||||
const apiKey = req.body.apiKey;
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query("SELECT * FROM routes WHERE apiKey = ?", [apiKey]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (!row) {
|
||||
res.status(401).send('Unauthorized');
|
||||
return;
|
||||
}
|
||||
req.session.userAuthenticated = true;
|
||||
req.session.userData = row;
|
||||
res.redirect('/user');
|
||||
}).catch(err => {
|
||||
console.error('Error getting route:', err);
|
||||
res.status(500).send('Internal server error');
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/user/logout', (req, res) => {
|
||||
req.session.destroy();
|
||||
res.redirect('/user/login');
|
||||
});
|
||||
|
||||
app.get("/user/edit", (req, res) => {
|
||||
if (!req.session.userAuthenticated) {
|
||||
res.redirect('/user/login');
|
||||
return;
|
||||
}
|
||||
// Remove block_start, block_length, and apiKey from the response
|
||||
responseData = {
|
||||
server: req.session.userData.server,
|
||||
port: req.session.userData.port,
|
||||
auth: req.session.userData.auth,
|
||||
secret: req.session.userData.secret
|
||||
}
|
||||
res.render('user/edit', { data: responseData });
|
||||
});
|
||||
|
||||
app.get('/api/v1/user/route', (req, res) => { // Get route
|
||||
if (!req.session.userAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
res.json(req.session.userData);
|
||||
});
|
||||
|
||||
app.put('/api/v1/user/route', (req, res) => { // Update route
|
||||
if (!req.session.userAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
if (!req.session.userData.apiKey) {
|
||||
req.session.destroy(); // Something weird happened, destroy session
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
// Does not allow for ID to be specified, always update current users route
|
||||
const server = req.body.server || req.session.userData.server;
|
||||
const port = req.body.port || req.session.userData.port;
|
||||
const auth = req.body.auth || req.session.userData.auth;
|
||||
const secret = req.body.secret || req.session.userData.secret;
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('UPDATE routes SET server = ?, port = ?, auth = ?, secret = ? WHERE apiKey = ?',
|
||||
[server, port, auth, secret, req.session.userData.apiKey]).then(() => {
|
||||
req.session.userData.server = server;
|
||||
req.session.userData.port = port;
|
||||
req.session.userData.auth = auth;
|
||||
req.session.userData.secret = secret;
|
||||
res.json({ message: 'Updated' });
|
||||
}).catch(err => {
|
||||
console.error('Error updating route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/v1/user/directory', (req, res) => { // Get directory entries created by user
|
||||
if (!req.session.userAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM directory WHERE route = ?', [req.session.userData.id]).then((rows) => {
|
||||
res.json(rows);
|
||||
}).catch(err => {
|
||||
console.error('Error getting routes:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/api/v1/user/directory', (req, res) => { // Create a new directory entry
|
||||
// Check if authenticated
|
||||
if (!req.session.userAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
// Check that the number is within the block range for the current user
|
||||
var number = Number(req.body.number);
|
||||
var name = String(req.body.name);
|
||||
if (!number || !name) {
|
||||
res.status(400).json({ error: 'Bad Request' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (number < req.session.userData.block_start || number > req.session.userData.block_start + req.session.userData.block_length) {
|
||||
res.status(403).json({ error: 'Forbidden' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove html
|
||||
name = require("escape-html")(name);
|
||||
|
||||
const route = req.session.userData.id;
|
||||
// If number already exists, update, otherwise insert
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM directory WHERE number = ? AND route = ?', [number, route]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (row) {
|
||||
conn.query('UPDATE directory SET name = ? WHERE number = ? AND route = ?',
|
||||
[name, number, route]).then(() => {
|
||||
res.json({ message: 'Updated' });
|
||||
}).catch(err => {
|
||||
console.error('Error updating directory entry:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
});
|
||||
} else {
|
||||
conn.query('INSERT INTO directory (number, name, route) VALUES (?, ?, ?)',
|
||||
[number, name, route]).then(() => {
|
||||
res.status(201).json({ message: 'Created' });
|
||||
}).catch(err => {
|
||||
console.error('Error creating directory entry:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Error checking for existing directory entry:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.delete('/api/v1/user/directory/:number', (req, res) => { // Delete a directory entry
|
||||
if (!req.session.userAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
const number = Number(req.params.number);
|
||||
if (!number) {
|
||||
res.status(400).json({ error: 'Bad Request' });
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('DELETE FROM directory WHERE number = ? AND route = ?', [number, req.session.userData.id]).then(() => {
|
||||
res.status(200).json({ message: 'Deleted' });
|
||||
}).catch(err => {
|
||||
console.error('Error deleting directory entry:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// == END USER ROUTES ==
|
||||
|
||||
// == Directory routes == (unauthenticated)
|
||||
|
||||
app.get("/api/v1/directory", (req, res) => {
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query("SELECT * FROM directory").then((rows) => {
|
||||
res.json(rows);
|
||||
}).catch(err => {
|
||||
console.error('Error getting directory:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Other public endpoints that need special handling
|
||||
discordInviteCache = { time: 0, url: "" };
|
||||
|
@ -634,187 +118,24 @@ app.get("/discord", (req, res) => {
|
|||
});
|
||||
});
|
||||
|
||||
app.get("/api/analytics", (req, res) => {
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query("SELECT * FROM analytics").then((total) => {
|
||||
conn.query("SELECT * FROM dailyAnalytics").then((daily) => {
|
||||
// Find the latest date and add "current:true" to it
|
||||
var latest = { tag_date: "1970-01-01", count: 0 };
|
||||
daily.forEach((entry) => {
|
||||
if (entry.tag_date > latest.tag_date) {
|
||||
latest = entry;
|
||||
}
|
||||
});
|
||||
latest.current = true;
|
||||
res.json({ total, daily });
|
||||
}).catch(err => {
|
||||
console.error('Error getting daily analytics:', err);
|
||||
res.status(500).send('Internal server error');
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('Error getting analytics:', err);
|
||||
res.status(500).send('Internal server error');
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/footer", (req, res) => {
|
||||
res.render("footer", { version });
|
||||
});
|
||||
|
||||
app.get("/api/v1/checkAvailability/:number", (req, res) => {
|
||||
// Check if the number is 7 digits
|
||||
const number = Number(req.params.number);
|
||||
if (number < 2000000 || number > 9999999) {
|
||||
res.status(400).json({ error: `Number is outside valid range` });
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM routes WHERE block_start <= ? AND block_start + block_length >= ?', [number, number]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (row) {
|
||||
res.json({ available: false, block: row.block_start });
|
||||
} else {
|
||||
res.json({ available: true });
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Error getting route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
const startup = () => {
|
||||
// Load all route modules from the 'routes' folder
|
||||
const routesPath = path.join(__dirname, 'routes');
|
||||
fs.readdirSync(routesPath).forEach((file) => {
|
||||
const route = require(path.join(routesPath, file));
|
||||
const routeName = `/${file.replace('.js', '')}`; // Use filename as route base
|
||||
app.use(routeName, route);
|
||||
console.log(`Using ${routeName}`)
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/api/healthcheck", (req, res) => {
|
||||
// Check ability to connect to database with select * from routes
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM routes').then(() => {
|
||||
res.status(200).send('OK');
|
||||
}).catch(err => {
|
||||
console.error('Error checking health:', err);
|
||||
res.status(500).send('Internal server error');
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// logCall function (caller, callee)
|
||||
const logCall = (caller, callee) => {
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('INSERT INTO callLogs (caller, callee, timestamp) VALUES (?, ?, ?)',
|
||||
[caller, callee, Math.floor(Date.now())]).catch(err => {
|
||||
console.error('Error logging call:', err);
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
// Start server
|
||||
app.listen(port, () => {
|
||||
console.log(`Listening on port ${port}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Query to get a route
|
||||
app.get('/api/v1/route/:apiKey/:ani/:number', (req, res) => {
|
||||
const apiKey = req.params.apiKey;
|
||||
const number = Number(req.params.number);
|
||||
const ani = Number(req.params.ani);
|
||||
pool.getConnection().then(conn => {
|
||||
//conn.query("SELECT * FROM routes WHERE apiKey = ? AND block_start <= ? AND block_start + block_length >= ?", [apiKey, ani, ani]).then((rows) => {
|
||||
conn.query("SELECT * FROM routes WHERE apiKey = ?", [apiKey]).then((rows) => { // We'll try this Nick, if it doesn't work we'll go back to the original
|
||||
const row = rows[0];
|
||||
// If no row or error, return 401
|
||||
if (!row) {
|
||||
res.status(401).send(`${process.env.MSG_ROUTE_ADDRESS}/401`)
|
||||
return;
|
||||
}
|
||||
conn.query('SELECT * FROM routes WHERE block_start <= ? AND block_start + block_length >= ?', [number, number]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (row) {
|
||||
// Check if the ANI is within the block range
|
||||
// If it is, return `local`
|
||||
console.log(`New Call: ${ani} -> ${number}`);
|
||||
logCall(ani, number);
|
||||
// incriment estCallsMade analytics
|
||||
addAnalytic("estCallsMade");
|
||||
dailyAnalytic("dailyCallsMade");
|
||||
if (ani >= row.block_start && ani <= row.block_start + row.block_length) {
|
||||
res.status(200).send('local');
|
||||
} else {
|
||||
res.status(200).send(`IAX2/${row.auth}:${row.secret}@${row.server}:${row.port}/${number}`);
|
||||
}
|
||||
} else {
|
||||
res.status(404).send(`${process.env.MSG_ROUTE_ADDRESS}/404`);
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Error getting route:', err);
|
||||
res.status(500).send(`${process.env.MSG_ROUTE_ADDRESS}/500`)
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
res.status(401).send(`${process.env.MSG_ROUTE_ADDRESS}/401`)
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/v1', (req, res) => { // Backwards compatibility with TandmX cause why not, it's easy
|
||||
const apiKey = req.query.auth;
|
||||
const number = Number(req.query.number);
|
||||
const ani = Number(req.query.ani);
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query("SELECT * FROM routes WHERE apiKey = ? AND block_start <= ? AND block_start + block_length >= ?", [apiKey, ani, ani]).then((rows) => {
|
||||
const row = rows[0];
|
||||
// If no row or error, return 401
|
||||
if (!row) {
|
||||
res.status(401).send(`${process.env.MSG_ROUTE_ADDRESS}/401`)
|
||||
return;
|
||||
}
|
||||
conn.query('SELECT * FROM routes WHERE block_start <= ? AND block_start + block_length >= ?', [number, number]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (row) {
|
||||
// Check if the ANI is within the block range
|
||||
// If it is, return `local`
|
||||
console.log(`New Call: ${ani} -> ${number}`);
|
||||
logCall(ani, number);
|
||||
addAnalytic("estCallsMade");
|
||||
dailyAnalytic("dailyCallsMade");
|
||||
if (ani >= row.block_start && ani <= row.block_start + row.block_length) {
|
||||
res.status(200).send('local');
|
||||
} else {
|
||||
res.status(200).send(`IAX2/${row.auth}:${row.secret}@${row.server}:${row.port}/${number}`);
|
||||
}
|
||||
} else {
|
||||
res.status(404).send(`${process.env.MSG_ROUTE_ADDRESS}/404`);
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Error getting route:', err);
|
||||
res.status(500).send(`${process.env.MSG_ROUTE_ADDRESS}/500`)
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
res.status(401).send(`${process.env.MSG_ROUTE_ADDRESS}/401`)
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Management Routes (Like restarting the server)
|
||||
app.post("/api/v1/manage/restart", (req, res) => {
|
||||
// Check Authorization header against process.env.MANAGEMENT_AUTH
|
||||
if (req.headers.authorization !== (process.env.MANAGEMENT_AUTH || crypto.randomBytes(32).toString('hex'))) { // Default to random in case we forget to configure it
|
||||
res.status(401).send("Unauthorized");
|
||||
return;
|
||||
}
|
||||
res.status(200).send("Restarting server...");
|
||||
setTimeout(() => {
|
||||
process.exit(0);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// Start server
|
||||
app.listen(port, () => {
|
||||
console.log(`Listening on port ${port}`);
|
||||
});
|
||||
startup();
|
526
package-lock.json
generated
526
package-lock.json
generated
|
@ -10,14 +10,12 @@
|
|||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.1.1",
|
||||
"connect-sqlite": "^0.0.1",
|
||||
"dotenv": "^16.4.7",
|
||||
"ejs": "^3.1.10",
|
||||
"escape-html": "^1.0.3",
|
||||
"express": "^4.21.2",
|
||||
"express-session": "^1.18.1",
|
||||
"mariadb": "^3.4.0",
|
||||
"session-file-store": "^1.5.0",
|
||||
"sqlite3": "^5.1.7"
|
||||
}
|
||||
},
|
||||
|
@ -48,54 +46,6 @@
|
|||
"node-pre-gyp": "bin/node-pre-gyp"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/node-pre-gyp/node_modules/are-we-there-yet": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
|
||||
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
|
||||
"deprecated": "This package is no longer supported.",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"delegates": "^1.0.0",
|
||||
"readable-stream": "^3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/node-pre-gyp/node_modules/gauge": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
|
||||
"integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
|
||||
"deprecated": "This package is no longer supported.",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"aproba": "^1.0.3 || ^2.0.0",
|
||||
"color-support": "^1.1.2",
|
||||
"console-control-strings": "^1.0.0",
|
||||
"has-unicode": "^2.0.1",
|
||||
"object-assign": "^4.1.1",
|
||||
"signal-exit": "^3.0.0",
|
||||
"string-width": "^4.2.3",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wide-align": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/node-pre-gyp/node_modules/npmlog": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
|
||||
"integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
|
||||
"deprecated": "This package is no longer supported.",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"are-we-there-yet": "^2.0.0",
|
||||
"console-control-strings": "^1.1.0",
|
||||
"gauge": "^3.0.0",
|
||||
"set-blocking": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@npmcli/fs": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz",
|
||||
|
@ -133,15 +83,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/geojson": {
|
||||
"version": "7946.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.15.tgz",
|
||||
"integrity": "sha512-9oSxFzDCT2Rj6DfcHF8G++jxBKS7mBqXl5xrRW+Kbvjry6Uduya2iiwqHPhVXpasAVMBYKkEPGgKhd3+/HZ6xA==",
|
||||
"version": "7946.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
|
||||
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz",
|
||||
"integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==",
|
||||
"version": "22.13.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz",
|
||||
"integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
|
@ -202,9 +152,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/agentkeepalive": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
|
||||
"integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==",
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz",
|
||||
"integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
|
@ -259,18 +209,17 @@
|
|||
"license": "ISC"
|
||||
},
|
||||
"node_modules/are-we-there-yet": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
|
||||
"integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
|
||||
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
|
||||
"deprecated": "This package is no longer supported.",
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"delegates": "^1.0.0",
|
||||
"readable-stream": "^3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/array-flatten": {
|
||||
|
@ -279,30 +228,12 @@
|
|||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/asn1.js": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
|
||||
"integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bn.js": "^4.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"safer-buffer": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bagpipe": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/bagpipe/-/bagpipe-0.3.5.tgz",
|
||||
"integrity": "sha512-42sAlmPDKes1nLm/aly+0VdaopSU9br+jkRELedhQxI5uXHgtk47I83Mpmf4zoNTRMASdLFtUkimlu/Z9zQ8+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
|
@ -343,12 +274,6 @@
|
|||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bcrypt/node_modules/node-addon-api": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bindings": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
|
@ -369,12 +294,6 @@
|
|||
"readable-stream": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bn.js": {
|
||||
"version": "4.12.1",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz",
|
||||
"integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.3",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||
|
@ -472,28 +391,23 @@
|
|||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
|
||||
"integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
|
||||
"license": "MIT",
|
||||
"node_modules/cacache/node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.0",
|
||||
"es-define-property": "^1.0.0",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"set-function-length": "^1.2.2"
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
|
||||
"integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
|
@ -504,13 +418,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/call-bound": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.2.tgz",
|
||||
"integrity": "sha512-0lk0PHFe/uz0vl527fG9CgdE9WdafjDbCXvBbs+LUv000TVt2Jjhqbs4Jwm8gz070w8xXyEAxrPOMullsxXeGg==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
|
||||
"integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.8",
|
||||
"get-intrinsic": "^1.2.5"
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"get-intrinsic": "^1.2.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
|
@ -587,17 +501,6 @@
|
|||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/connect-sqlite": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/connect-sqlite/-/connect-sqlite-0.0.1.tgz",
|
||||
"integrity": "sha512-75BbN7FUSs8MWxlYoqqhyx0TSKErEzRqKx2o4SIcAR/8H81acKm2Oy1ym3uMz1BCoH7bNmyc2ETVV4hrAK26Pw==",
|
||||
"dependencies": {
|
||||
"sqlite": ">= 1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.1.98"
|
||||
}
|
||||
},
|
||||
"node_modules/console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||
|
@ -673,23 +576,6 @@
|
|||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/define-data-property": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/delegates": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||
|
@ -746,12 +632,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz",
|
||||
"integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.0",
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
|
@ -863,9 +749,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
|
||||
"integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
|
@ -1056,20 +942,6 @@
|
|||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^4.0.0",
|
||||
"universalify": "^0.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6 <7 || >=8"
|
||||
}
|
||||
},
|
||||
"node_modules/fs-minipass": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||
|
@ -1098,42 +970,42 @@
|
|||
}
|
||||
},
|
||||
"node_modules/gauge": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz",
|
||||
"integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
|
||||
"integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
|
||||
"deprecated": "This package is no longer supported.",
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"aproba": "^1.0.3 || ^2.0.0",
|
||||
"color-support": "^1.1.3",
|
||||
"console-control-strings": "^1.1.0",
|
||||
"color-support": "^1.1.2",
|
||||
"console-control-strings": "^1.0.0",
|
||||
"has-unicode": "^2.0.1",
|
||||
"signal-exit": "^3.0.7",
|
||||
"object-assign": "^4.1.1",
|
||||
"signal-exit": "^3.0.0",
|
||||
"string-width": "^4.2.3",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wide-align": "^1.1.5"
|
||||
"wide-align": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz",
|
||||
"integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==",
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
|
||||
"integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"dunder-proto": "^1.0.0",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.0.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.0",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.0.0"
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
|
@ -1142,6 +1014,19 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/github-from-package": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
||||
|
@ -1185,7 +1070,8 @@
|
|||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||
"license": "ISC"
|
||||
"license": "ISC",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
|
@ -1196,18 +1082,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/has-property-descriptors": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
|
@ -1384,6 +1258,7 @@
|
|||
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
||||
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.8.19"
|
||||
}
|
||||
|
@ -1467,12 +1342,6 @@
|
|||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
|
@ -1505,39 +1374,11 @@
|
|||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/jsonfile": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
|
||||
"license": "MIT",
|
||||
"optionalDependencies": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/kruptein": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/kruptein/-/kruptein-2.2.3.tgz",
|
||||
"integrity": "sha512-BTwprBPTzkFT9oTugxKd3WnWrX630MqUDsnmBuoa98eQs12oD4n4TeI0GbpdGcYn/73Xueg2rfnw+oK4dovnJg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asn1.js": "^5.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">6"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
"version": "10.4.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/make-dir": {
|
||||
"version": "3.1.0",
|
||||
|
@ -1591,6 +1432,19 @@
|
|||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/make-fetch-happen/node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/mariadb": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/mariadb/-/mariadb-3.4.0.tgz",
|
||||
|
@ -1619,16 +1473,10 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mariadb/node_modules/lru-cache": {
|
||||
"version": "10.4.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.0.0.tgz",
|
||||
"integrity": "sha512-4MqMiKP90ybymYvsut0CH2g4XWbfLtmlCkXmtmdcDCxNB+mQcu1w/1+L/VD7vi/PSv7X2JYV7SCcR+jiPXnQtA==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
|
@ -1706,12 +1554,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/minimalistic-assert": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
|
@ -1853,9 +1695,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/napi-build-utils": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
|
||||
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
|
||||
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
|
@ -1868,9 +1710,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/node-abi": {
|
||||
"version": "3.71.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz",
|
||||
"integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==",
|
||||
"version": "3.74.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.74.0.tgz",
|
||||
"integrity": "sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"semver": "^7.3.5"
|
||||
|
@ -1880,9 +1722,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
|
||||
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
|
@ -1930,22 +1772,43 @@
|
|||
"node": ">= 10.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nopt": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
||||
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
|
||||
"node_modules/node-gyp/node_modules/are-we-there-yet": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
|
||||
"integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==",
|
||||
"deprecated": "This package is no longer supported.",
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"abbrev": "1"
|
||||
},
|
||||
"bin": {
|
||||
"nopt": "bin/nopt.js"
|
||||
"delegates": "^1.0.0",
|
||||
"readable-stream": "^3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/npmlog": {
|
||||
"node_modules/node-gyp/node_modules/gauge": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz",
|
||||
"integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
|
||||
"deprecated": "This package is no longer supported.",
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"aproba": "^1.0.3 || ^2.0.0",
|
||||
"color-support": "^1.1.3",
|
||||
"console-control-strings": "^1.1.0",
|
||||
"has-unicode": "^2.0.1",
|
||||
"signal-exit": "^3.0.7",
|
||||
"string-width": "^4.2.3",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wide-align": "^1.1.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp/node_modules/npmlog": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
|
||||
"integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==",
|
||||
|
@ -1962,6 +1825,34 @@
|
|||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nopt": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
||||
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"abbrev": "1"
|
||||
},
|
||||
"bin": {
|
||||
"nopt": "bin/nopt.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/npmlog": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
|
||||
"integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
|
||||
"deprecated": "This package is no longer supported.",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"are-we-there-yet": "^2.0.0",
|
||||
"console-control-strings": "^1.1.0",
|
||||
"gauge": "^3.0.0",
|
||||
"set-blocking": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
|
@ -1972,9 +1863,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
|
||||
"integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==",
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
|
@ -2054,9 +1945,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/prebuild-install": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz",
|
||||
"integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==",
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
|
||||
"integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.0",
|
||||
|
@ -2064,7 +1955,7 @@
|
|||
"github-from-package": "0.0.0",
|
||||
"minimist": "^1.2.3",
|
||||
"mkdirp-classic": "^0.5.3",
|
||||
"napi-build-utils": "^1.0.1",
|
||||
"napi-build-utils": "^2.0.0",
|
||||
"node-abi": "^3.3.0",
|
||||
"pump": "^3.0.0",
|
||||
"rc": "^1.2.7",
|
||||
|
@ -2205,6 +2096,7 @@
|
|||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
|
||||
"integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
|
@ -2252,9 +2144,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
|
@ -2317,46 +2209,12 @@
|
|||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/session-file-store": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/session-file-store/-/session-file-store-1.5.0.tgz",
|
||||
"integrity": "sha512-60IZaJNzyu2tIeHutkYE8RiXVx3KRvacOxfLr2Mj92SIsRIroDsH0IlUUR6fJAjoTW4RQISbaOApa2IZpIwFdQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"bagpipe": "^0.3.5",
|
||||
"fs-extra": "^8.0.1",
|
||||
"kruptein": "^2.0.4",
|
||||
"object-assign": "^4.1.1",
|
||||
"retry": "^0.12.0",
|
||||
"write-file-atomic": "3.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/set-function-length": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.1.4",
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"gopd": "^1.0.1",
|
||||
"has-property-descriptors": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
|
@ -2498,9 +2356,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/socks": {
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz",
|
||||
"integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==",
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz",
|
||||
"integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
|
@ -2559,12 +2417,6 @@
|
|||
"license": "BSD-3-Clause",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/sqlite": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/sqlite/-/sqlite-5.1.1.tgz",
|
||||
"integrity": "sha512-oBkezXa2hnkfuJwUo44Hl9hS3er+YFtueifoajrgidvqsJRQFpc5fKoAkAor1O5ZnLoa28GBScfHXs8j0K358Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sqlite3": {
|
||||
"version": "5.1.7",
|
||||
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz",
|
||||
|
@ -2589,6 +2441,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/sqlite3/node_modules/node-addon-api": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
|
||||
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ssri": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
|
||||
|
@ -2685,9 +2543,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/tar-fs": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
|
||||
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz",
|
||||
"integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chownr": "^1.1.1",
|
||||
|
@ -2767,15 +2625,6 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typedarray-to-buffer": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
|
||||
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uid-safe": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
|
||||
|
@ -2814,15 +2663,6 @@
|
|||
"imurmurhash": "^0.1.4"
|
||||
}
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
|
@ -2903,18 +2743,6 @@
|
|||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/write-file-atomic": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
|
||||
"integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"imurmurhash": "^0.1.4",
|
||||
"is-typedarray": "^1.0.0",
|
||||
"signal-exit": "^3.0.2",
|
||||
"typedarray-to-buffer": "^3.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
|
|
|
@ -5,20 +5,22 @@
|
|||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.chrischro.me/AstroCom/AstroCom-API"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.1.1",
|
||||
"connect-sqlite": "^0.0.1",
|
||||
"dotenv": "^16.4.7",
|
||||
"ejs": "^3.1.10",
|
||||
"escape-html": "^1.0.3",
|
||||
"express": "^4.21.2",
|
||||
"express-session": "^1.18.1",
|
||||
"mariadb": "^3.4.0",
|
||||
"session-file-store": "^1.5.0",
|
||||
"sqlite3": "^5.1.7"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,22 @@
|
|||
<script type="application/ld+json">
|
||||
{"name":"AstroCom","description":"Simplifying communication.","@type":"WebSite","url":"https://astrocom.tel/","headline":"AstroCom","@context":"http://schema.org"}
|
||||
</script>
|
||||
<script>
|
||||
(function (d, t) {
|
||||
var BASE_URL = "https://support.chrischro.me";
|
||||
var g = d.createElement(t), s = d.getElementsByTagName(t)[0];
|
||||
g.src = BASE_URL + "/packs/js/sdk.js";
|
||||
g.defer = true;
|
||||
g.async = true;
|
||||
s.parentNode.insertBefore(g, s);
|
||||
g.onload = function () {
|
||||
window.chatwootSDK.run({
|
||||
websiteToken: '1Epwwnhnmieqzu2dm3jYH3Qp',
|
||||
baseUrl: BASE_URL
|
||||
})
|
||||
}
|
||||
})(document, "script");
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
89
routes/admin.js
Normal file
89
routes/admin.js
Normal file
|
@ -0,0 +1,89 @@
|
|||
const pool = global.db_pool;
|
||||
if (!pool) {
|
||||
throw new Error('Database pool is not defined');
|
||||
}
|
||||
|
||||
const express = require('express');
|
||||
const app = new express.Router();
|
||||
|
||||
app.get('/logout', (req, res) => {
|
||||
req.session.destroy();
|
||||
res.redirect('/admin/login');
|
||||
});
|
||||
|
||||
app.get('/login', (req, res) => {
|
||||
res.render('admin/login');
|
||||
});
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.redirect('/admin/login');
|
||||
return;
|
||||
}
|
||||
res.render('admin/index', { user: req.session.user });
|
||||
});
|
||||
|
||||
app.get('/create', (req, res) => {
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.redirect('/admin/login');
|
||||
return;
|
||||
}
|
||||
res.render('admin/create', { user: req.session.user });
|
||||
});
|
||||
|
||||
app.get('/route/:id', (req, res) => {
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.redirect('/admin/login');
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM routes WHERE id = ?', [req.params.id]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (!row) {
|
||||
res.status(404).send('Not Found');
|
||||
return;
|
||||
}
|
||||
res.render('admin/edit', { user: req.session.user, data: row });
|
||||
}).catch(err => {
|
||||
console.error('Error getting route:', err);
|
||||
res.status(500).send('Internal server error');
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/login', (req, res) => {
|
||||
const username = req.body.username;
|
||||
const password = req.body.password;
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query("SELECT * FROM users WHERE username = ?", [String(username)]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (!row) {
|
||||
res.status(401).send('Unauthorized (Not Found)');
|
||||
return;
|
||||
}
|
||||
bcrypt.compare(password, row.passwordHash, (err, result) => {
|
||||
if (err) {
|
||||
console.error('Error comparing password:', err);
|
||||
res.status(500).send('Internal server error');
|
||||
return;
|
||||
}
|
||||
if (result) {
|
||||
req.session.adminAuthenticated = true;
|
||||
req.session.user = row.username;
|
||||
res.redirect('/admin');
|
||||
} else {
|
||||
res.status(401).send('Unauthorized');
|
||||
}
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('Error getting user:', err);
|
||||
res.status(500).send('Internal server error');
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = app;
|
516
routes/api.js
Normal file
516
routes/api.js
Normal file
|
@ -0,0 +1,516 @@
|
|||
const pool = global.db_pool;
|
||||
if (!pool) {
|
||||
throw new Error('Database pool is not defined');
|
||||
}
|
||||
|
||||
const express = require('express');
|
||||
const app = new express.Router();
|
||||
|
||||
const analytics = require('../analytics');
|
||||
|
||||
// Admin routes (Authenticated)
|
||||
|
||||
app.get('/v1/admin/routes', (req, res) => { // Get all routes
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM routes').then((rows) => {
|
||||
res.json(rows);
|
||||
}).catch(err => {
|
||||
console.error('Error getting routes:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/v1/admin/route/:id', (req, res) => { // Get route
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM routes WHERE id = ?', [req.params.id]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (!row) {
|
||||
res.status(404).json({ error: 'Not Found' });
|
||||
return;
|
||||
}
|
||||
res.json(row);
|
||||
}).catch(err => {
|
||||
console.error('Error getting route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/v1/admin/route', (req, res) => { // Create a new route
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
const server = req.body.server;
|
||||
const port = req.body.port;
|
||||
const auth = req.body.auth || "from-astrocom";
|
||||
const secret = req.body.secret || crypto.randomBytes(15).toString('hex');
|
||||
const block_start = req.body.block_start;
|
||||
const block_length = req.body.block_length || 9999;
|
||||
const apiKey = crypto.randomBytes(32).toString('hex');
|
||||
const contact = req.body.contact || "Unknown";
|
||||
// Validate all inputs exist
|
||||
if (!server || !port || !block_start) {
|
||||
res.status(400).json({ error: 'Bad Request' });
|
||||
return;
|
||||
}
|
||||
// Check if route already exists (OR conditions on server, and block range)
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM routes WHERE block_start <= ? AND block_start + block_length >= ?', [block_start, block_start]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (row) {
|
||||
res.status(409).json({ error: 'Conflict' });
|
||||
return;
|
||||
} else {
|
||||
conn.query('INSERT INTO routes (server, port, auth, secret, block_start, block_length, apiKey, contact) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[server, port, auth, secret, block_start, block_length, apiKey, contact]).then(() => {
|
||||
res.status(201).json({ message: 'Created' });
|
||||
}).catch(err => {
|
||||
console.error('Error creating route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Error checking for existing route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.put('/v1/admin/route/:id', (req, res) => { // Update a route
|
||||
// Check if authenticated
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
// Check if route exists
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM routes WHERE id = ?', [req.params.id]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (!row) {
|
||||
res.status(404).json({ error: 'Not Found' });
|
||||
return;
|
||||
}
|
||||
// Update route
|
||||
const server = req.body.server || row.server;
|
||||
const port = req.body.port || row.port;
|
||||
const auth = req.body.auth || row.auth;
|
||||
const secret = req.body.secret || row.secret;
|
||||
const block_start = req.body.block_start || row.block_start;
|
||||
const block_length = req.body.block_length || row.block_length;
|
||||
const contact = req.body.contact || row.contact;
|
||||
console.log(`Updating ${req.params.id} to ${server}:${port} with ${auth}:${secret} for ${block_start} - ${block_start + block_length}. Contact: ${contact}`);
|
||||
conn.query('UPDATE routes SET server = ?, port = ?, auth = ?, secret = ?, block_start = ?, block_length = ?, contact = ? WHERE id = ?',
|
||||
[server, port, auth, secret, block_start, block_length, contact, req.params.id]).then(() => {
|
||||
res.json({ message: 'Updated' });
|
||||
}).catch(err => {
|
||||
console.error('Error updating route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('Error getting route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.delete('/v1/admin/route/:id', (req, res) => { // Delete a route
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(async conn => {
|
||||
await conn.query('DELETE FROM directory WHERE route = ?', [req.params.id])
|
||||
conn.query('DELETE FROM routes WHERE id = ?', [req.params.id]).then(() => {
|
||||
res.json({ message: 'Deleted' });
|
||||
}).catch(err => {
|
||||
console.error('Error deleting route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.delete('/v1/admin/directory/:number', (req, res) => { // Delete a directory entry
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
const number = Number(req.params.number);
|
||||
if (!number) {
|
||||
res.status(400).json({ error: 'Bad Request' });
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('DELETE FROM directory WHERE number = ?', [number]).then(() => {
|
||||
res.status(200).json({ message: 'Deleted' });
|
||||
}).catch(err => {
|
||||
console.error('Error deleting directory entry:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/v1/admin/callLogs", (req, res) => {
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
// if ?page is set, return 100 results from that page, if no page assume page 1
|
||||
const page = Number(req.query.page) || 1;
|
||||
const offset = (page - 1) * 100;
|
||||
// Get full count of call logs to calculate total pages
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query("SELECT COUNT(*) as count FROM callLogs").then((rows) => {
|
||||
const totalPages = Math.ceil(rows[0].count / 100);
|
||||
conn.query("SELECT * FROM callLogs ORDER BY timestamp DESC LIMIT 100 OFFSET ?", [offset]).then((rows) => {
|
||||
res.json({ totalPages, page, data: rows });
|
||||
}).catch(err => {
|
||||
console.error('Error getting call logs:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('Error getting call log count:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// User routes (Authenticated)
|
||||
|
||||
app.get('/v1/user/route', (req, res) => { // Get route
|
||||
if (!req.session.userAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
res.json(req.session.userData);
|
||||
});
|
||||
|
||||
app.put('/v1/user/route', (req, res) => { // Update route
|
||||
if (!req.session.userAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
if (!req.session.userData.apiKey) {
|
||||
req.session.destroy(); // Something weird happened, destroy session
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
// Does not allow for ID to be specified, always update current users route
|
||||
const server = req.body.server || req.session.userData.server;
|
||||
const port = req.body.port || req.session.userData.port;
|
||||
const auth = req.body.auth || req.session.userData.auth;
|
||||
const secret = req.body.secret || req.session.userData.secret;
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('UPDATE routes SET server = ?, port = ?, auth = ?, secret = ? WHERE apiKey = ?',
|
||||
[server, port, auth, secret, req.session.userData.apiKey]).then(() => {
|
||||
req.session.userData.server = server;
|
||||
req.session.userData.port = port;
|
||||
req.session.userData.auth = auth;
|
||||
req.session.userData.secret = secret;
|
||||
res.json({ message: 'Updated' });
|
||||
}).catch(err => {
|
||||
console.error('Error updating route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/v1/user/directory', (req, res) => { // Get directory entries created by user
|
||||
if (!req.session.userAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM directory WHERE route = ?', [req.session.userData.id]).then((rows) => {
|
||||
res.json(rows);
|
||||
}).catch(err => {
|
||||
console.error('Error getting routes:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/v1/user/directory', (req, res) => { // Create a new directory entry
|
||||
// Check if authenticated
|
||||
if (!req.session.userAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
// Check that the number is within the block range for the current user
|
||||
var number = Number(req.body.number);
|
||||
var name = String(req.body.name);
|
||||
if (!number || !name) {
|
||||
res.status(400).json({ error: 'Bad Request' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (number < req.session.userData.block_start || number > req.session.userData.block_start + req.session.userData.block_length) {
|
||||
res.status(403).json({ error: 'Forbidden' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove html
|
||||
name = require("escape-html")(name);
|
||||
|
||||
const route = req.session.userData.id;
|
||||
// If number already exists, update, otherwise insert
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM directory WHERE number = ? AND route = ?', [number, route]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (row) {
|
||||
conn.query('UPDATE directory SET name = ? WHERE number = ? AND route = ?',
|
||||
[name, number, route]).then(() => {
|
||||
res.json({ message: 'Updated' });
|
||||
}).catch(err => {
|
||||
console.error('Error updating directory entry:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
});
|
||||
} else {
|
||||
conn.query('INSERT INTO directory (number, name, route) VALUES (?, ?, ?)',
|
||||
[number, name, route]).then(() => {
|
||||
res.status(201).json({ message: 'Created' });
|
||||
}).catch(err => {
|
||||
console.error('Error creating directory entry:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Error checking for existing directory entry:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.delete('/v1/user/directory/:number', (req, res) => { // Delete a directory entry
|
||||
if (!req.session.userAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
const number = Number(req.params.number);
|
||||
if (!number) {
|
||||
res.status(400).json({ error: 'Bad Request' });
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('DELETE FROM directory WHERE number = ? AND route = ?', [number, req.session.userData.id]).then(() => {
|
||||
res.status(200).json({ message: 'Deleted' });
|
||||
}).catch(err => {
|
||||
console.error('Error deleting directory entry:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Public routes
|
||||
|
||||
app.get("/v1/directory", (req, res) => {
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query("SELECT * FROM directory").then((rows) => {
|
||||
res.json(rows);
|
||||
}).catch(err => {
|
||||
console.error('Error getting directory:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/analytics", (req, res) => {
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query("SELECT * FROM analytics").then((total) => {
|
||||
conn.query("SELECT * FROM dailyAnalytics").then((daily) => {
|
||||
// Find the latest date and add "current:true" to it
|
||||
var latest = { tag_date: "1970-01-01", count: 0 };
|
||||
daily.forEach((entry) => {
|
||||
if (entry.tag_date > latest.tag_date) {
|
||||
latest = entry;
|
||||
}
|
||||
});
|
||||
latest.current = true;
|
||||
res.json({ total, daily });
|
||||
}).catch(err => {
|
||||
console.error('Error getting daily analytics:', err);
|
||||
res.status(500).send('Internal server error');
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('Error getting analytics:', err);
|
||||
res.status(500).send('Internal server error');
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/v1/checkAvailability/:number", (req, res) => {
|
||||
// Check if the number is 7 digits
|
||||
const number = Number(req.params.number);
|
||||
if (number < 2000000 || number > 9999999) {
|
||||
res.status(400).json({ error: `Number is outside valid range` });
|
||||
return;
|
||||
}
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM routes WHERE block_start <= ? AND block_start + block_length >= ?', [number, number]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (row) {
|
||||
res.json({ available: false, block: row.block_start });
|
||||
} else {
|
||||
res.json({ available: true });
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Error getting route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/healthcheck", (req, res) => {
|
||||
// Check ability to connect to database with select * from routes
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query('SELECT * FROM routes').then(() => {
|
||||
res.status(200).send('OK');
|
||||
}).catch(err => {
|
||||
console.error('Error checking health:', err);
|
||||
res.status(500).send('Internal server error');
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// AstroCom Asterisk routes (Authenticated)
|
||||
|
||||
app.get('/v1/route/:apiKey/:ani/:number', (req, res) => {
|
||||
const apiKey = req.params.apiKey;
|
||||
const number = Number(req.params.number);
|
||||
const ani = Number(req.params.ani);
|
||||
pool.getConnection().then(conn => {
|
||||
//conn.query("SELECT * FROM routes WHERE apiKey = ? AND block_start <= ? AND block_start + block_length >= ?", [apiKey, ani, ani]).then((rows) => {
|
||||
conn.query("SELECT * FROM routes WHERE apiKey = ?", [apiKey]).then((rows) => { // We'll try this Nick, if it doesn't work we'll go back to the original
|
||||
const row = rows[0];
|
||||
// If no row or error, return 401
|
||||
if (!row) {
|
||||
res.status(401).send(`${process.env.MSG_ROUTE_ADDRESS}/401`)
|
||||
return;
|
||||
}
|
||||
conn.query('SELECT * FROM routes WHERE block_start <= ? AND block_start + block_length >= ?', [number, number]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (row) {
|
||||
// Check if the ANI is within the block range
|
||||
// If it is, return `local`
|
||||
console.log(`New Call: ${ani} -> ${number}`);
|
||||
analytics.logCall(ani, number);
|
||||
// incriment estCallsMade analytics
|
||||
analytics.addAnalytic("estCallsMade");
|
||||
analytics.dailyAnalytic("dailyCallsMade");
|
||||
if (ani >= row.block_start && ani <= row.block_start + row.block_length) {
|
||||
res.status(200).send('local');
|
||||
} else {
|
||||
res.status(200).send(`IAX2/${row.auth}:${row.secret}@${row.server}:${row.port}/${number}`);
|
||||
}
|
||||
} else {
|
||||
res.status(404).send(`${process.env.MSG_ROUTE_ADDRESS}/404`);
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Error getting route:', err);
|
||||
res.status(500).send(`${process.env.MSG_ROUTE_ADDRESS}/500`)
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
res.status(401).send(`${process.env.MSG_ROUTE_ADDRESS}/401`)
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/v1', (req, res) => { // Backwards compatibility with TandmX cause why not, it's easy
|
||||
const apiKey = req.query.auth;
|
||||
const number = Number(req.query.number);
|
||||
const ani = Number(req.query.ani);
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query("SELECT * FROM routes WHERE apiKey = ? AND block_start <= ? AND block_start + block_length >= ?", [apiKey, ani, ani]).then((rows) => {
|
||||
const row = rows[0];
|
||||
// If no row or error, return 401
|
||||
if (!row) {
|
||||
res.status(401).send(`${process.env.MSG_ROUTE_ADDRESS}/401`)
|
||||
return;
|
||||
}
|
||||
conn.query('SELECT * FROM routes WHERE block_start <= ? AND block_start + block_length >= ?', [number, number]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (row) {
|
||||
// Check if the ANI is within the block range
|
||||
// If it is, return `local`
|
||||
console.log(`New Call: ${ani} -> ${number}`);
|
||||
analytics.logCall(ani, number);
|
||||
analytics.addAnalytic("estCallsMade");
|
||||
analytics.dailyAnalytic("dailyCallsMade");
|
||||
if (ani >= row.block_start && ani <= row.block_start + row.block_length) {
|
||||
res.status(200).send('local');
|
||||
} else {
|
||||
res.status(200).send(`IAX2/${row.auth}:${row.secret}@${row.server}:${row.port}/${number}`);
|
||||
}
|
||||
} else {
|
||||
res.status(404).send(`${process.env.MSG_ROUTE_ADDRESS}/404`);
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Error getting route:', err);
|
||||
res.status(500).send(`${process.env.MSG_ROUTE_ADDRESS}/500`)
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
res.status(401).send(`${process.env.MSG_ROUTE_ADDRESS}/401`)
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Management Routes (Like restarting the server)
|
||||
|
||||
app.post("/api/v1/manage/restart", (req, res) => {
|
||||
// Check Authorization header against process.env.MANAGEMENT_AUTH
|
||||
if (req.headers.authorization !== (process.env.MANAGEMENT_AUTH || crypto.randomBytes(32).toString('hex'))) { // Default to random in case we forget to configure it
|
||||
res.status(401).send("Unauthorized");
|
||||
return;
|
||||
}
|
||||
res.status(200).send("Restarting server...");
|
||||
setTimeout(() => {
|
||||
process.exit(0);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
module.exports = app;
|
58
routes/user.js
Normal file
58
routes/user.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
const pool = global
|
||||
const express = require('express');
|
||||
const app = new express.Router();
|
||||
|
||||
app.get('/user', (req, res) => {
|
||||
if (!req.session.userAuthenticated) {
|
||||
res.redirect('/user/login');
|
||||
return;
|
||||
}
|
||||
res.render('user/index', { user: req.session.user });
|
||||
});
|
||||
|
||||
app.get('/user/login', (req, res) => {
|
||||
res.render('user/login');
|
||||
});
|
||||
|
||||
app.post('/user/login', (req, res) => {
|
||||
const apiKey = req.body.apiKey;
|
||||
pool.getConnection().then(conn => {
|
||||
conn.query("SELECT * FROM routes WHERE apiKey = ?", [apiKey]).then((rows) => {
|
||||
const row = rows[0];
|
||||
if (!row) {
|
||||
res.status(401).send('Unauthorized');
|
||||
return;
|
||||
}
|
||||
req.session.userAuthenticated = true;
|
||||
req.session.userData = row;
|
||||
res.redirect('/user');
|
||||
}).catch(err => {
|
||||
console.error('Error getting route:', err);
|
||||
res.status(500).send('Internal server error');
|
||||
}).finally(() => {
|
||||
conn.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/user/logout', (req, res) => {
|
||||
req.session.destroy();
|
||||
res.redirect('/user/login');
|
||||
});
|
||||
|
||||
app.get("/user/edit", (req, res) => {
|
||||
if (!req.session.userAuthenticated) {
|
||||
res.redirect('/user/login');
|
||||
return;
|
||||
}
|
||||
// Remove block_start, block_length, and apiKey from the response
|
||||
responseData = {
|
||||
server: req.session.userData.server,
|
||||
port: req.session.userData.port,
|
||||
auth: req.session.userData.auth,
|
||||
secret: req.session.userData.secret
|
||||
}
|
||||
res.render('user/edit', { data: responseData });
|
||||
});
|
||||
|
||||
module.exports = app;
|
Loading…
Reference in a new issue