/* ______ __ ______ / \ / | / \ /$$$$$$ | _______ _$$ |_ ______ ______ /$$$$$$ | ______ _____ ____ $$ |__$$ | / |/ $$ | / \ / \ $$ | $$/ / \ / \/ \ $$ $$ |/$$$$$$$/ $$$$$$/ /$$$$$$ |/$$$$$$ |$$ | /$$$$$$ |$$$$$$ $$$$ | $$$$$$$$ |$$ \ $$ | __ $$ | $$/ $$ | $$ |$$ | __ $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$$$$$ | $$ |/ |$$ | $$ \__$$ |$$ \__/ |$$ \__$$ |$$ | $$ | $$ | $$ | $$ |/ $$/ $$ $$/ $$ | $$ $$/ $$ $$/ $$ $$/ $$ | $$ | $$ | $$/ $$/ $$$$$$$/ $$$$/ $$/ $$$$$$/ $$$$$$/ $$$$$$/ $$/ $$/ $$/ Made with <3 by Chris Chrome and the rest of the AstroCom team */ require("dotenv").config(); const { execSync } = require('child_process'); const express = require('express'); const expressSession = require('express-session'); const ejs = require("ejs") const mariadb = require('mariadb'); const bcrypt = require("bcrypt") const crypto = require("crypto") const app = express(); const port = process.env.SERVER_PORT || 3000; const invalidBlocks = [ // Emergency number prefixes (112, 911, 999, 110, 117, 119, 113, 191, 111) 1120000, 9110000, 9990000, 1100000, 1170000, 1190000, 1130000, 1910000, 1110000 ] const pool = mariadb.createPool({ host: process.env.DB_HOST || '127.0.0.1', port: process.env.DB_PORT || 3306, user: process.env.DB_USER || 'root', password: process.env.DB_PASSWORD || '', database: process.env.DB_NAME || 'astrocom', connectionLimit: 10 }); const saltRounds = 10; pool.getConnection().then((conn) => { require("./migrations")(pool).then(() => { conn.query("SELECT * FROM users WHERE id = 1").then((row) => { if (!row || process.env.RESET_ADMIN == "true") { // delete all users (The big scary one lol) conn.query("DELETE FROM users").then(() => { // Generate 32 char random string const passwd = crypto.randomBytes(32).toString('hex'); bcrypt.hash(passwd, 10).then((hash) => { conn.query("INSERT INTO users (id, username, passwordHash) VALUES (1, 'admin', ?)", [hash]).then(() => { console.log(`Created admin user with password: ${passwd}`); }); }); }); } }); }).finally(() => { conn.release(); }); }); const gitCommitHashShort = execSync('git rev-parse --short HEAD').toString().trim(); const branch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim(); const version = `${gitCommitHashShort}-${branch}`; console.log(`Version: ${version}`); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(expressSession({ store: expressSession.MemoryStore(), secret: process.env.SESSION_SECRET || 'default_secret', resave: false, saveUninitialized: false, cookie: { secure: process.env.NODE_ENV === 'production', maxAge: 24 * 60 * 60 * 1000 // 24 hours } })); app.set('view engine', 'ejs'); 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"); }; 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(); }); }); }); // Function to find open number blocks app.get("/api/v1/directory/openBlocks", (req, res) => { pool.query("SELECT block_start, block_length FROM routes").then((rows) => { console.log(JSON.stringify(rows)); // for testing const takenBlocks = rows.map(row => { return { start: row.block_start, end: row.block_start + row.block_length }; }); const openBlocks = []; for (let i = 1000000; i <= 9999999; i += 10000) { const blockStart = i; const blockEnd = i + 9999; // Check if block is invalid if (invalidBlocks.includes(blockStart)) { continue; } // Check if block overlaps with any taken blocks const overlap = takenBlocks.some(taken => { return (blockStart <= taken.end && blockEnd >= taken.start); }); if (!overlap) { openBlocks.push(blockStart); } } res.json(openBlocks); }).catch(err => { console.error('Error getting open blocks:', err); res.status(500).json({ error: 'Internal server error' }); }); }); // Other public endpoints that need special handling discordInviteCache = { time: 0, url: "" }; app.get("/discord", (req, res) => { // fetch from process.env.WIDGET_URL, get json body, redirect to body.instant_invite. Cache url for 5 minutes if (Date.now() - discordInviteCache.time < 300000) { res.redirect(discordInviteCache.url); return; } fetch(process.env.WIDGET_URL) .then(response => response.json()) .then(data => { discordInviteCache.time = Date.now(); discordInviteCache.url = data.instant_invite; res.redirect(data.instant_invite); }) .catch(error => { console.error('Error fetching discord invite:', error); res.status(500).send('Internal server error'); }); }); 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); // Round to nearest 10000 so it's always NXX0000 number = Math.floor(number / 10000) * 10000; if (!number || number < 1000000 || number > 9999999 || invalidBlocks.includes(number)) { res.status(400).json({ error: `Number is outside valid range or is an invalid block` }); 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("/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(); }); }); } // 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}`); });