Admin UI mostly there; TODO: User management
This commit is contained in:
parent
8c6c0daadd
commit
cea5348d6c
79
index.js
79
index.js
|
@ -29,19 +29,35 @@ db.get("SELECT * FROM users WHERE id = 1", [], (err, row) => {
|
|||
console.error('Error checking for admin user:', err);
|
||||
return;
|
||||
}
|
||||
if (!row) {
|
||||
bcrypt.hash('admin', saltRounds, (err, hash) => {
|
||||
if (!row || process.env.RESET_ADMIN) {
|
||||
// Destroy all sessions
|
||||
sessionStore.clear((err) => {
|
||||
if (err) {
|
||||
console.error('Error clearing sessions:', err);
|
||||
return;
|
||||
}
|
||||
});
|
||||
// delete all users
|
||||
db.run("DELETE FROM users", [], (err) => {
|
||||
if (err) {
|
||||
console.error('Error deleting users:', err);
|
||||
return;
|
||||
}
|
||||
});
|
||||
// Generate 32 char random string
|
||||
const passwd = crypto.randomBytes(32).toString('hex');
|
||||
bcrypt.hash(passwd, saltRounds, (err, hash) => {
|
||||
if (err) {
|
||||
console.error('Error creating hash:', err);
|
||||
return;
|
||||
}
|
||||
db.run("INSERT INTO users (id, username, passwordHash) VALUES (1, 'admin', ?)",
|
||||
[hash],
|
||||
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');
|
||||
console.log(`Created admin user with password: ${passwd}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -59,7 +75,7 @@ app.use(expressSession({
|
|||
secret: process.env.SESSION_SECRET || 'default_secret',
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
cookie: {
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
||||
}
|
||||
|
@ -90,6 +106,14 @@ app.get('/admin', (req, res) => {
|
|||
res.render('admin/index', { user: req.session.user });
|
||||
});
|
||||
|
||||
app.get('/admin/create', (req, res) => {
|
||||
if (!req.session.authenticated) {
|
||||
res.redirect('/admin/login');
|
||||
return;
|
||||
}
|
||||
res.render('admin/create', { user: req.session.user });
|
||||
});
|
||||
|
||||
app.get('/admin/route/:id', (req, res) => {
|
||||
if (!req.session.authenticated) {
|
||||
res.redirect('/admin/login');
|
||||
|
@ -150,7 +174,6 @@ app.get('/api/v1/admin/routes', (req, res) => { // Get all routes
|
|||
res.status(500).json({ error: 'Internal server error' });
|
||||
return;
|
||||
}
|
||||
console.log(rows)
|
||||
res.json(rows);
|
||||
});
|
||||
});
|
||||
|
@ -191,16 +214,29 @@ app.post('/api/v1/admin/route', (req, res) => { // Create a new route
|
|||
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' });
|
||||
});
|
||||
// Check if route already exists (OR conditions on server, and block range)
|
||||
db.get('SELECT * FROM routes WHERE server = ? OR block_start <= ? AND block_start + block_length >= ?', [server, block_start, block_start], (err, row) => {
|
||||
if (err) {
|
||||
console.error('Error checking for existing route:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
return;
|
||||
}
|
||||
if (row) {
|
||||
res.status(409).json({ error: 'Conflict' });
|
||||
return;
|
||||
} else {
|
||||
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
|
||||
|
@ -227,8 +263,8 @@ app.put('/api/v1/admin/route/:id', (req, res) => { // Update a route
|
|||
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],
|
||||
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);
|
||||
|
@ -262,12 +298,10 @@ 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;
|
||||
}
|
||||
|
@ -279,14 +313,11 @@ app.get('/api/v1/route/:apiKey/:ani/:number', (req, res) => {
|
|||
// 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 IAX2/${row.auth}:${row.secret}@${row.server}:${row.port}/${number}`)
|
||||
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`);
|
||||
}
|
||||
});
|
||||
|
|
26
public/assets/js/adminCreate.js
Normal file
26
public/assets/js/adminCreate.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
const createForm = document.getElementById('createForm');
|
||||
createForm.addEventListener('submit', async (e) => {
|
||||
console.log("GUH")
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(createForm);
|
||||
const data = {};
|
||||
|
||||
for (const [key, value] of formData.entries()) {
|
||||
data[key] = value;
|
||||
}
|
||||
|
||||
const response = await fetch('/api/v1/admin/route', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
window.location.href = '/admin';
|
||||
} else {
|
||||
alert('Failed to create entry');
|
||||
}
|
||||
});
|
|
@ -1,28 +1,25 @@
|
|||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const routeId = document.getElementById('route').value;
|
||||
const editForm = document.getElementById('editForm');
|
||||
editForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
async function updateRoute(routeData) {
|
||||
try {
|
||||
const response = await fetch(`/api/v1/admin/route/${routeId}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(routeData)
|
||||
});
|
||||
const formData = new FormData(editForm);
|
||||
const data = {};
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error updating route:', error);
|
||||
throw error;
|
||||
}
|
||||
for (const [key, value] of formData.entries()) {
|
||||
data[key] = value;
|
||||
}
|
||||
|
||||
// Export the function if needed
|
||||
window.updateRoute = updateRoute;
|
||||
const response = await fetch(`/api/v1/admin/route/${route}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
window.location.href = '/admin';
|
||||
} else {
|
||||
alert('Failed to update entry');
|
||||
}
|
||||
});
|
63
views/admin/create.ejs
Normal file
63
views/admin/create.ejs
Normal file
|
@ -0,0 +1,63 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="/assets/css/bootstrap.min.css">
|
||||
<title>AstroCom Admin - Create new server</title>
|
||||
</head>
|
||||
<body class="bg-dark text-white">
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">AstroCom</a>
|
||||
<div class="navbar-nav ms-auto">
|
||||
<span class="navbar-text me-3">
|
||||
Welcome, <%= user %>
|
||||
</span>
|
||||
<a href="/admin/logout" class="btn btn-danger">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container mt-4">
|
||||
<h2>Create new entry</h2>
|
||||
<form id="createForm">
|
||||
<div class="mb-3">
|
||||
<label for="server" class="form-label">Server</label>
|
||||
<input type="text" class="form-control" id="server" name="server" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="port" class="form-label">Port</label>
|
||||
<input type="number" class="form-control" id="port" name="port" value="4569" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="auth" class="form-label">Auth</label>
|
||||
<input type="text" class="form-control" id="auth" name="auth" value="from-astrocom" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="secret" class="form-label">Secret (Optional)</label>
|
||||
<input type="text" class="form-control" id="secret" name="secret">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="block_start" class="form-label">Block Start</label>
|
||||
<input type="number" class="form-control" id="block_start" name="block_start" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="block_length" class="form-label">Block Length</label>
|
||||
<input type="number" class="form-control" id="block_length" name="block_length" value="9999" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="apiKey" class="form-label">API Key (Optional)</label>
|
||||
<input type="text" class="form-control" id="apiKey" name="apiKey">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
<a href="/admin" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
<script src="/assets/js/adminCreate.js"></script>
|
||||
</div>
|
||||
|
||||
<!-- <script src="/assets/js/adminCreate.js"></script> -->
|
||||
<script src="/assets/js/bootstrap.min.js"></script>
|
||||
<script src="/assets/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/assets/js/jquery.min.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -18,9 +18,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container mt-4"></div>
|
||||
<div class="container mt-4">
|
||||
<h2>Edit Entry <%= data.id %></h2>
|
||||
<form id="editForm" onsubmit="return false;">
|
||||
<form id="editForm" onsubmit="return false;" class="mt-4">
|
||||
<% for (const [key, value] of Object.entries(data)) { %>
|
||||
<% if (key !== 'id') { %>
|
||||
<div class="mb-3">
|
||||
|
@ -31,38 +31,13 @@
|
|||
<% } %>
|
||||
<button type="submit" class="btn btn-primary">Update</button>
|
||||
<a href="/admin" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
</form>
|
||||
<script>
|
||||
const route = <%= data.id %>;
|
||||
</script>
|
||||
</div>
|
||||
<script src="/assets/js/adminEdit.js"></script>
|
||||
</div>
|
||||
<script>
|
||||
const route = <%= data.id %>;
|
||||
|
||||
const editForm = document.getElementById('editForm');
|
||||
editForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(editForm);
|
||||
const data = {};
|
||||
|
||||
for (const [key, value] of formData.entries()) {
|
||||
data[key] = value;
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/v1/admin/route/${route}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
window.location.href = '/admin';
|
||||
} else {
|
||||
alert('Failed to update entry');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script src="/assets/js/bootstrap.min.js"></script>
|
||||
<script src="/assets/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/assets/js/jquery.min.js"></script>
|
||||
|
|
|
@ -18,7 +18,10 @@
|
|||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<h2>Admin Dashboard</h2>
|
||||
<div class="d-flex align-items-center gap-3 mb-3">
|
||||
<h2 class="m-0">Admin Dashboard</h2>
|
||||
<a href="/admin/create" class="btn btn-primary">Create New Server</a>
|
||||
</div>
|
||||
<table class="table table-striped table-dark" id="adminTable">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
Loading…
Reference in a new issue