User panel is almost done!
This commit is contained in:
parent
cea5348d6c
commit
3c8dfe3a36
208
index.js
208
index.js
|
@ -16,12 +16,10 @@ const db = new sqlite3.Database('astrocom.db', (err) => {
|
||||||
console.log('Connected to SQLite database');
|
console.log('Connected to SQLite database');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// Run migrations
|
||||||
|
require("./migrations")(db)
|
||||||
|
|
||||||
// Create 'routes' table
|
// Check if user 1 exists, if not, create it
|
||||||
// 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 \'from-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;
|
const saltRounds = 10;
|
||||||
|
|
||||||
db.get("SELECT * FROM users WHERE id = 1", [], (err, row) => {
|
db.get("SELECT * FROM users WHERE id = 1", [], (err, row) => {
|
||||||
|
@ -37,7 +35,7 @@ db.get("SELECT * FROM users WHERE id = 1", [], (err, row) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// delete all users
|
// delete all users (The big scary one lol)
|
||||||
db.run("DELETE FROM users", [], (err) => {
|
db.run("DELETE FROM users", [], (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error('Error deleting users:', err);
|
console.error('Error deleting users:', err);
|
||||||
|
@ -99,7 +97,7 @@ app.get('/admin/login', (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/admin', (req, res) => {
|
app.get('/admin', (req, res) => {
|
||||||
if (!req.session.authenticated) {
|
if (!req.session.adminAuthenticated) {
|
||||||
res.redirect('/admin/login');
|
res.redirect('/admin/login');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -107,7 +105,7 @@ app.get('/admin', (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/admin/create', (req, res) => {
|
app.get('/admin/create', (req, res) => {
|
||||||
if (!req.session.authenticated) {
|
if (!req.session.adminAuthenticated) {
|
||||||
res.redirect('/admin/login');
|
res.redirect('/admin/login');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -115,7 +113,7 @@ app.get('/admin/create', (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/admin/route/:id', (req, res) => {
|
app.get('/admin/route/:id', (req, res) => {
|
||||||
if (!req.session.authenticated) {
|
if (!req.session.adminAuthenticated) {
|
||||||
res.redirect('/admin/login');
|
res.redirect('/admin/login');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -153,7 +151,7 @@ app.post('/admin/login', (req, res) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (result) {
|
if (result) {
|
||||||
req.session.authenticated = true;
|
req.session.adminAuthenticated = true;
|
||||||
req.session.user = row.username;
|
req.session.user = row.username;
|
||||||
res.redirect('/admin');
|
res.redirect('/admin');
|
||||||
} else {
|
} else {
|
||||||
|
@ -164,7 +162,7 @@ app.post('/admin/login', (req, res) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get('/api/v1/admin/routes', (req, res) => { // Get all routes
|
app.get('/api/v1/admin/routes', (req, res) => { // Get all routes
|
||||||
if (!req.session.authenticated) {
|
if (!req.session.adminAuthenticated) {
|
||||||
res.status(401).json({ error: 'Unauthorized' });
|
res.status(401).json({ error: 'Unauthorized' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -179,7 +177,7 @@ app.get('/api/v1/admin/routes', (req, res) => { // Get all routes
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/api/v1/admin/route/:id', (req, res) => { // Get route
|
app.get('/api/v1/admin/route/:id', (req, res) => { // Get route
|
||||||
if (!req.session.authenticated) {
|
if (!req.session.adminAuthenticated) {
|
||||||
res.status(401).json({ error: 'Unauthorized' });
|
res.status(401).json({ error: 'Unauthorized' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -198,7 +196,7 @@ app.get('/api/v1/admin/route/:id', (req, res) => { // Get route
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/api/v1/admin/route', (req, res) => { // Create a new route
|
app.post('/api/v1/admin/route', (req, res) => { // Create a new route
|
||||||
if (!req.session.authenticated) {
|
if (!req.session.adminAuthenticated) {
|
||||||
res.status(401).json({ error: 'Unauthorized' });
|
res.status(401).json({ error: 'Unauthorized' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -241,7 +239,7 @@ app.post('/api/v1/admin/route', (req, res) => { // Create a new route
|
||||||
|
|
||||||
app.put('/api/v1/admin/route/:id', (req, res) => { // Update a route
|
app.put('/api/v1/admin/route/:id', (req, res) => { // Update a route
|
||||||
// Check if authenticated
|
// Check if authenticated
|
||||||
if (!req.session.authenticated) {
|
if (!req.session.adminAuthenticated) {
|
||||||
res.status(401).json({ error: 'Unauthorized' });
|
res.status(401).json({ error: 'Unauthorized' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -277,7 +275,7 @@ app.put('/api/v1/admin/route/:id', (req, res) => { // Update a route
|
||||||
});
|
});
|
||||||
|
|
||||||
app.delete('/api/v1/admin/route/:id', (req, res) => { // Delete a route
|
app.delete('/api/v1/admin/route/:id', (req, res) => { // Delete a route
|
||||||
if (!req.session.authenticated) {
|
if (!req.session.adminAuthenticated) {
|
||||||
res.status(401).json({ error: 'Unauthorized' });
|
res.status(401).json({ error: 'Unauthorized' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -293,6 +291,186 @@ app.delete('/api/v1/admin/route/:id', (req, res) => { // Delete a route
|
||||||
|
|
||||||
// == END ADMIN ROUTES ==
|
// == 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;
|
||||||
|
db.get("SELECT * FROM routes WHERE apiKey = ?", [apiKey], (err, row) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Error getting route:', err);
|
||||||
|
res.status(500).send('Internal server error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!row) {
|
||||||
|
res.status(401).send('Unauthorized');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
req.session.userAuthenticated = true;
|
||||||
|
req.session.userData = row;
|
||||||
|
res.redirect('/user');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/user/logout', (req, res) => {
|
||||||
|
req.session.destroy();
|
||||||
|
res.redirect('/user/login');
|
||||||
|
});
|
||||||
|
|
||||||
|
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;
|
||||||
|
// We don't allow block changes, admins only.
|
||||||
|
const block_start = req.session.userData.block_start;
|
||||||
|
const block_length = req.session.userData.block_length;
|
||||||
|
const apiKey = req.session.userData.apiKey;
|
||||||
|
db.run('UPDATE routes SET server = ?, port = ?, auth = ?, secret = ?, block_start = ?, block_length = ? WHERE apiKey = ?',
|
||||||
|
[server, port, auth, secret, block_start, block_length, apiKey],
|
||||||
|
(err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Error updating route:', err);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json({ message: 'Updated' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
db.all('SELECT * FROM directory WHERE route = ?', [req.session.userData.id], (err, rows) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Error getting routes:', err);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json(rows);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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 = name.replace(/<[^>]*>?/gm, '');
|
||||||
|
const route = req.session.userData.id;
|
||||||
|
// If number already exists, update, otherwise insert
|
||||||
|
db.get('SELECT * FROM directory WHERE number = ? AND route = ?', [number, route], (err, row) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Error checking for existing directory entry:', err);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (row) {
|
||||||
|
db.run('UPDATE directory SET name = ? WHERE number = ? AND route = ?',
|
||||||
|
[name, number, route],
|
||||||
|
(err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Error updating directory entry:', err);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json({ message: 'Updated' });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
db.run('INSERT INTO directory (number, name, route) VALUES (?, ?, ?)',
|
||||||
|
[number, name, route],
|
||||||
|
(err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Error creating directory entry:', err);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.status(201).json({ message: 'Created' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
db.run('DELETE FROM directory WHERE number = ? AND route = ?', [number, req.session.userData.id], (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Error deleting directory entry:', err);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.status(200).json({ message: 'Deleted' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// == END USER ROUTES ==
|
||||||
|
|
||||||
|
// == Directory routes == (unauthenticated)
|
||||||
|
|
||||||
|
app.get("/api/v1/directory", (req, res) => {
|
||||||
|
db.all("SELECT * FROM directory", (err, rows) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Error getting directory:', err);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json(rows);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Query to get a route
|
// Query to get a route
|
||||||
app.get('/api/v1/route/:apiKey/:ani/:number', (req, res) => {
|
app.get('/api/v1/route/:apiKey/:ani/:number', (req, res) => {
|
||||||
const apiKey = req.params.apiKey;
|
const apiKey = req.params.apiKey;
|
||||||
|
|
65
migrations.js
Normal file
65
migrations.js
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
const sqlite3 = require('sqlite3').verbose();
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const util = require("util");
|
||||||
|
|
||||||
|
function runMigrations(db) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const migrationDir = path.join(__dirname, 'migrations');
|
||||||
|
|
||||||
|
const runQuery = util.promisify(db.run.bind(db));
|
||||||
|
const getQuery = util.promisify(db.get.bind(db));
|
||||||
|
|
||||||
|
// Ensure a migrations table exists to track applied migrations
|
||||||
|
runQuery(`CREATE TABLE IF NOT EXISTS migrations (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);`)
|
||||||
|
.then(() => {
|
||||||
|
// Read all migration files
|
||||||
|
const files = fs.readdirSync(migrationDir).sort(); // Sort to apply in order
|
||||||
|
|
||||||
|
return files.reduce((promise, file) => {
|
||||||
|
return promise.then(() => {
|
||||||
|
const migrationName = path.basename(file);
|
||||||
|
|
||||||
|
// Check if the migration has already been applied
|
||||||
|
return getQuery(
|
||||||
|
'SELECT 1 FROM migrations WHERE name = ? LIMIT 1',
|
||||||
|
[migrationName]
|
||||||
|
).then((row) => {
|
||||||
|
if (row) {
|
||||||
|
// console.log(`Skipping already applied migration: ${migrationName}`);
|
||||||
|
return; // Skip this migration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read and execute the migration SQL
|
||||||
|
const migrationPath = path.join(migrationDir, file);
|
||||||
|
const sql = fs.readFileSync(migrationPath, 'utf8');
|
||||||
|
|
||||||
|
return runQuery(sql).then(() => {
|
||||||
|
// Record the applied migration
|
||||||
|
return runQuery(
|
||||||
|
'INSERT INTO migrations (name) VALUES (?)',
|
||||||
|
[migrationName]
|
||||||
|
).then(() => {
|
||||||
|
console.log(`Applied migration: ${migrationName}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, Promise.resolve());
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
console.log('All migrations applied successfully!');
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error('Error running migrations:', err);
|
||||||
|
reject(err);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = runMigrations;
|
10
migrations/001_gen_routes_table.sql
Normal file
10
migrations/001_gen_routes_table.sql
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
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 'from-astrocom',
|
||||||
|
secret TEXT NOT NULL,
|
||||||
|
block_start INTEGER UNIQUE NOT NULL,
|
||||||
|
block_length INTEGER NOT NULL DEFAULT 9999,
|
||||||
|
apiKey TEXT NOT NULL
|
||||||
|
)
|
5
migrations/002_gen_users_table.sql
Normal file
5
migrations/002_gen_users_table.sql
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
username TEXT NOT NULL,
|
||||||
|
passwordHash TEXT NOT NULL
|
||||||
|
)
|
6
migrations/003_gen_directory_table.sql
Normal file
6
migrations/003_gen_directory_table.sql
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS directory (
|
||||||
|
number INTEGER PRIMARY KEY NOT NULL, -- This is the directory phone number
|
||||||
|
name TEXT NOT NULL, -- This is the text of the entry, set by the user.
|
||||||
|
route INTEGER NOT NULL, -- This is the ID of the route that owns this entry. Foreign key to routes.id
|
||||||
|
FOREIGN KEY(route) REFERENCES routes(id)
|
||||||
|
)
|
17
public/assets/js/directory.js
Normal file
17
public/assets/js/directory.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
function getDirectoryEntries() {
|
||||||
|
fetch('/api/v1/directory')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
const table = document.getElementById('directoryList');
|
||||||
|
data.forEach(entry => {
|
||||||
|
const row = document.createElement('tr');
|
||||||
|
row.innerHTML = `<td>${entry.number}</td><td>${entry.name}</td>`;
|
||||||
|
table.appendChild(row);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error fetching directory:', error));
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
getDirectoryEntries();
|
||||||
|
});
|
78
public/assets/js/userMain.js
Normal file
78
public/assets/js/userMain.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
getInfo();
|
||||||
|
getDirectoryEntries();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function getInfo() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/v1/user/route');
|
||||||
|
const route = await response.json();
|
||||||
|
document.getElementById('routeId').textContent = route.id || '';
|
||||||
|
document.getElementById('routeHost').textContent = `${route.server}:${route.port}` || '';
|
||||||
|
document.getElementById('routeAuth').textContent = route.auth || '';
|
||||||
|
document.getElementById('routeSecret').textContent = route.secret || '';
|
||||||
|
document.getElementById('routeBlock').textContent = `${route.block_start} - ${route.block_start + route.block_length}` || '';
|
||||||
|
document.getElementById('routeApiKey').textContent = route.apiKey || '';
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching route info:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get directory entries
|
||||||
|
function getDirectoryEntries() {
|
||||||
|
fetch('/api/v1/user/directory')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
const table = document.getElementById('directoryList');
|
||||||
|
// Keep the first row (update form) and remove the rest
|
||||||
|
while (table.children.length > 1) {
|
||||||
|
table.removeChild(table.lastChild);
|
||||||
|
}
|
||||||
|
data.forEach(entry => {
|
||||||
|
const row = document.createElement('tr');
|
||||||
|
row.innerHTML = `<td>${entry.number}</td><td>${entry.name}</td><td><button onclick="deleteEntry(${entry.number})" class="btn btn-danger btn-sm">Delete</button></td>`;
|
||||||
|
table.appendChild(row);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error fetching directory:', error));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Handle update form for directory entries
|
||||||
|
document.getElementById('dirForm').addEventListener('submit', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const number = document.getElementById('dirNumber').value;
|
||||||
|
const name = document.getElementById('dirName').value;
|
||||||
|
const submitButton = document.getElementById('dirSubmit');
|
||||||
|
|
||||||
|
submitButton.disabled = true;
|
||||||
|
fetch('/api/v1/user/directory', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ number, name })
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
getDirectoryEntries();
|
||||||
|
submitButton.disabled = false;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error updating directory:', error);
|
||||||
|
submitButton.disabled = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle delete entry
|
||||||
|
function deleteEntry(number) {
|
||||||
|
fetch(`/api/v1/user/directory/${number}`, {
|
||||||
|
method: 'DELETE'
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.status === 200) {
|
||||||
|
getDirectoryEntries();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error deleting entry:', error));
|
||||||
|
}
|
41
public/directory/index.html
Normal file
41
public/directory/index.html
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<!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 Directory</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>
|
||||||
|
<div class="ms-auto d-flex text-nowrap">
|
||||||
|
<a href="/user" class="btn btn-outline-light me-2">User Login</a>
|
||||||
|
<a href="/admin" class="btn btn-outline-light">Admin Login</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="d-flex align-items-center gap-3 mb-3">
|
||||||
|
<h2 class="m-0 text-center w-100">AtroCom Directory</h2>
|
||||||
|
</div>
|
||||||
|
<div class="container mt-4" style="max-width: 400px;">
|
||||||
|
<table class="table table-dark table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Number</th>
|
||||||
|
<th>Name</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="directoryList"></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<script src="/assets/js/directory.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>
|
|
@ -54,8 +54,6 @@
|
||||||
</form>
|
</form>
|
||||||
<script src="/assets/js/adminCreate.js"></script>
|
<script src="/assets/js/adminCreate.js"></script>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <script src="/assets/js/adminCreate.js"></script> -->
|
|
||||||
<script src="/assets/js/bootstrap.min.js"></script>
|
<script src="/assets/js/bootstrap.min.js"></script>
|
||||||
<script src="/assets/js/bootstrap.bundle.min.js"></script>
|
<script src="/assets/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="/assets/js/jquery.min.js"></script>
|
<script src="/assets/js/jquery.min.js"></script>
|
||||||
|
|
0
views/directory.ejs
Normal file
0
views/directory.ejs
Normal file
77
views/user/index.ejs
Normal file
77
views/user/index.ejs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
<!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 User Panel</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!
|
||||||
|
</span>
|
||||||
|
<a href="/user/logout" class="btn btn-danger">Logout</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="d-flex align-items-center gap-3 mb-3">
|
||||||
|
<h2 class="m-0 text-center w-100">User Dashboard</h2>
|
||||||
|
</div>
|
||||||
|
<div class="container mt-4">
|
||||||
|
<div class="card bg-secondary mb-4 text-white">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="position-absolute top-0 end-0 m-3">
|
||||||
|
<a href="/user/edit" class="btn btn-primary disabled" id="editInfoBtn">Edit Information (Coming Soon)</a>
|
||||||
|
</div>
|
||||||
|
<h5 class="card-title">Your Route Information</h5>
|
||||||
|
<p class="card-text">
|
||||||
|
<strong>Route ID:</strong> <span id="routeId"></span><br>
|
||||||
|
<strong>Host:</strong> <span id="routeHost"></span><br>
|
||||||
|
<strong>IAX2 Username/context:</strong> <span id="routeAuth"></span><br>
|
||||||
|
<strong>IAX2 Password:</strong> <span id="routeSecret"></span><br>
|
||||||
|
<strong>Block:</strong> <span id="routeBlock"></span><br>
|
||||||
|
<strong>API Key:</strong> <span id="routeApiKey"></span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container mt-4">
|
||||||
|
<h5 class="mb-3">Directory Entries</h5>
|
||||||
|
<table class="table table-dark table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Number</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="directoryList">
|
||||||
|
<tr>
|
||||||
|
<form id="dirForm">
|
||||||
|
<td>
|
||||||
|
<input type="text" class="form-control" id="dirNumber" placeholder="Phone Number" required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" class="form-control" id="dirName" placeholder="Name" required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button type="submit" class="btn btn-primary" id="dirSubmit">Submit</button>
|
||||||
|
</td>
|
||||||
|
</form>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<script src="/assets/js/userMain.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>
|
33
views/user/login.ejs
Normal file
33
views/user/login.ejs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<!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 User Login</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="bg-dark">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center mt-5">
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
|
<div class="card bg-dark text-light shadow">
|
||||||
|
<div class="card-body p-4">
|
||||||
|
<h2 class="text-center mb-4">User Login</h2>
|
||||||
|
<form action="/user/login" method="POST">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="apiKey" class="form-label">API Key:</label>
|
||||||
|
<input type="text" class="form-control" id="apiKey" name="apiKey" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary w-100">Login</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<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>
|
Loading…
Reference in a new issue