267 lines
8.2 KiB
JavaScript
267 lines
8.2 KiB
JavaScript
require("dotenv").config();
|
|
const express = require('express');
|
|
const expressSession = require('express-session');
|
|
const FileStore = require('session-file-store')(expressSession);
|
|
const ejs = require("ejs")
|
|
const sqlite3 = require('sqlite3').verbose();
|
|
const bcrypt = require("bcrypt")
|
|
const crypto = require("crypto")
|
|
const app = express();
|
|
const port = process.env.SERVER_PORT || 3000;
|
|
|
|
const db = new sqlite3.Database('astrocom.db', (err) => {
|
|
if (err) {
|
|
console.error('Error connecting to database:', err);
|
|
} else {
|
|
console.log('Connected to SQLite database');
|
|
}
|
|
});
|
|
|
|
// Create 'routes' table
|
|
// We need to store server address, port, secret, block_start and block_length. Then make a query that takes in an arbitrary number, and returns a row if that number between block start and block start + block length.
|
|
db.run('CREATE TABLE IF NOT EXISTS routes (id INTEGER PRIMARY KEY AUTOINCREMENT, server TEXT NOT NULL, port INTEGER NOT NULL DEFAULT 4569, auth TEST NOT NULL DEFAULT astrocom, secret TEXT NOT NULL, block_start INTEGER UNIQUE NOT NULL, block_length INTEGER NOT NULL DEFAULT 9999, apiKey TEXT NOT NULL)');
|
|
db.run('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, passwordHash TEXT NOT NULL)');
|
|
// Check if user 1 exists, if not, create it (admin:admin)
|
|
const saltRounds = 10;
|
|
|
|
db.get("SELECT * FROM users WHERE id = 1", [], (err, row) => {
|
|
if (err) {
|
|
console.error('Error checking for admin user:', err);
|
|
return;
|
|
}
|
|
if (!row) {
|
|
bcrypt.hash('admin', saltRounds, (err, hash) => {
|
|
if (err) {
|
|
console.error('Error creating hash:', err);
|
|
return;
|
|
}
|
|
db.run("INSERT INTO users (id, username, passwordHash) VALUES (1, 'admin', ?)",
|
|
[hash],
|
|
(err) => {
|
|
if (err) {
|
|
console.error('Error creating admin user:', err);
|
|
} else {
|
|
console.log('Admin user created');
|
|
}
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
app.use(express.json());
|
|
app.use(express.urlencoded({ extended: true }));
|
|
|
|
const fileStoreOptions = {};
|
|
const sessionStore = new FileStore(fileStoreOptions);
|
|
|
|
app.use(expressSession({
|
|
store: sessionStore,
|
|
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');
|
|
|
|
// 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('login');
|
|
});
|
|
|
|
app.post('/admin/login', (req, res) => {
|
|
const username = req.body.username;
|
|
const password = req.body.password;
|
|
db.get("SELECT * FROM users WHERE username = ?", [String(username)], (err, row) => {
|
|
if (err) {
|
|
console.error('Error getting user:', err);
|
|
res.status(500).send('Internal server error');
|
|
return;
|
|
}
|
|
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.authenticated = true;
|
|
res.redirect('/admin');
|
|
} else {
|
|
res.status(401).send('Unauthorized');
|
|
}
|
|
});
|
|
});
|
|
})
|
|
|
|
app.get('/api/v1/admin/routes', (req, res) => { // Get all routes
|
|
if (!req.session.authenticated) {
|
|
res.status(401).json({ error: 'Unauthorized' });
|
|
return;
|
|
}
|
|
db.all('SELECT * FROM routes', (err, rows) => {
|
|
if (err) {
|
|
console.error('Error getting routes:', err);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
return;
|
|
}
|
|
console.log(rows)
|
|
res.json(rows);
|
|
});
|
|
});
|
|
|
|
app.get('/api/v1/admin/route/:id', (req, res) => { // Get route
|
|
if (!req.session.authenticated) {
|
|
res.status(401).json({ error: 'Unauthorized' });
|
|
return;
|
|
}
|
|
db.get('SELECT * FROM routes WHERE id = ?', [req.params.id], (err, row) => {
|
|
if (err) {
|
|
console.error('Error getting route:', err);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
return;
|
|
}
|
|
if (!row) {
|
|
res.status(404).json({ error: 'Not Found' });
|
|
return;
|
|
}
|
|
res.json(row);
|
|
});
|
|
});
|
|
|
|
app.post('/api/v1/admin/route', (req, res) => { // Create a new route
|
|
if (!req.session.authenticated) {
|
|
res.status(401).json({ error: 'Unauthorized' });
|
|
return;
|
|
}
|
|
const server = req.body.server;
|
|
const port = req.body.port;
|
|
const auth = req.body.auth || "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');
|
|
// Validate all inputs exist
|
|
if (!server || !port || !block_start) {
|
|
res.status(400).json({ error: 'Bad Request' });
|
|
return;
|
|
}
|
|
db.run('INSERT INTO routes (server, port, auth, secret, block_start, block_length, apiKey) VALUES (?, ?, ?, ?, ?, ?, ?)',
|
|
[server, port, auth, secret, block_start, block_length, apiKey],
|
|
(err) => {
|
|
if (err) {
|
|
console.error('Error creating route:', err);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
return;
|
|
}
|
|
res.status(201).json({ message: 'Created' });
|
|
});
|
|
});
|
|
|
|
app.put('/api/v1/admin/route/:id', (req, res) => { // Update a route
|
|
// Check if authenticated
|
|
if (!req.session.authenticated) {
|
|
res.status(401).json({ error: 'Unauthorized' });
|
|
return;
|
|
}
|
|
// Check if route exists
|
|
db.get('SELECT * FROM routes WHERE id = ?', [req.params.id], (err, row) => {
|
|
if (err) {
|
|
console.error('Error getting route:', err);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
return;
|
|
}
|
|
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;
|
|
db.run('UPDATE routes SET server = ?, port = ?, auth = ?, secret = ?, block_start = ?, block_length = ? WHERE id = ?',
|
|
[server, port, auth, secret, block_start, block_length, req.params.id],
|
|
(err) => {
|
|
if (err) {
|
|
console.error('Error updating route:', err);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
return;
|
|
}
|
|
res.json({ message: 'Updated' });
|
|
});
|
|
});
|
|
});
|
|
|
|
app.delete('/api/v1/admin/route/:id', (req, res) => { // Delete a route
|
|
if (!req.session.authenticated) {
|
|
res.status(401).json({ error: 'Unauthorized' });
|
|
return;
|
|
}
|
|
db.run('DELETE FROM routes WHERE id = ?', [req.params.id], (err) => {
|
|
if (err) {
|
|
console.error('Error deleting route:', err);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
return;
|
|
}
|
|
res.json({ message: 'Deleted' });
|
|
});
|
|
});
|
|
|
|
// 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);
|
|
console.log(`${apiKey}:${req.params.ani}:${number}`);
|
|
db.get("SELECT * FROM routes WHERE apiKey = ? AND block_start <= ? AND block_start + block_length >= ?", [apiKey, ani, ani], (err, row) => {
|
|
// If no row or error, return 401
|
|
if (err || !row) {
|
|
console.error(err);
|
|
console.log(row)
|
|
res.status(401).send(`${process.env.MSG_ROUTE_ADDRESS}/401`)
|
|
return;
|
|
}
|
|
db.get('SELECT * FROM routes WHERE block_start <= ? AND block_start + block_length >= ?', [number, number], (err, row) => {
|
|
if (err) {
|
|
console.error('Error getting route:', err);
|
|
res.status(500).send(`${process.env.MSG_ROUTE_ADDRESS}/500`)
|
|
} else if (row) {
|
|
// Check if the ANI is within the block range
|
|
// If it is, return `local`
|
|
if (req.params.ani >= row.block_start && req.params.ani <= row.block_start + row.block_length) {
|
|
console.log("sent local")
|
|
res.status(200).send('local');
|
|
} else {
|
|
console.log("sent remote")
|
|
res.status(200).send(`IAX2/${row.auth}:${row.secret}@${row.server}:${row.port}/${number}`);
|
|
}
|
|
} else {
|
|
console.log("boowomp")
|
|
res.status(404).send(`${process.env.MSG_ROUTE_ADDRESS}/404`);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
// Start server
|
|
app.listen(port, () => {
|
|
console.log(`Listening on port ${port}`);
|
|
}); |