AstroCom-API/index.js
2024-12-14 12:30:08 -07:00

200 lines
6.3 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 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
app.get('/admin', (req, res) => {
if (!req.session.authenticated) {
res.redirect('/admin/login');
return;
}
db.all('SELECT * FROM routes', (err, rows) => {
if (err) {
console.error('Error getting routes:', err);
res.status(500).send('Internal server error');
return;
}
console.log(rows)
res.render('admin', { routes: rows });
});
});
// 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');
}
});
});
})
// post /api/v1/admin (We take in a JSON array of actions to perform. If Authorization header isn't set, rely on session)
app.post('/api/v1/admin', (req, res) => {
if (!req.session.authenticated) {
res.status(401).send('Unauthorized');
return;
}
const actions = req.body;
actions.forEach((action) => {
if (action.action === 'add') {
db.run('INSERT INTO routes (server, port, auth, secret, block_start, block_length, apiKey) VALUES (?, ?, ?, ?, ?, ?, ?)',
[action.server, action.port, action.auth, action.secret, action.block_start, action.block_length, action.apiKey],
(err) => {
if (err) {
console.error('Error adding route:', err);
}
});
} else if (action.action === 'delete') {
db.run('DELETE FROM routes WHERE id = ?', [action.id], (err) => {
if (err) {
console.error('Error deleting route:', err);
}
});
} else if (action.action === 'update') {
db.run('UPDATE routes SET server = ?, port = ?, auth = ?, secret = ?, block_start = ?, block_length = ?, apiKey = ? WHERE id = ?', [action.server, action.port, action.auth, action.secret, action.block_start, action.block_length, action.apiKey, action.id], (err) => {
if (err) {
console.error('Error updating route:', err);
}
}
);
}
});
res.status(200).send('OK');
});
// 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(`Server is running on port ${port}`);
});