Add directory API endpoints

This commit is contained in:
Christopher Cookman 2025-10-27 08:04:46 -06:00
parent 11592d8bd1
commit af1eaa3a57
3 changed files with 93 additions and 28 deletions

View file

@ -708,6 +708,94 @@ app.delete('/api/v1/user/directory/:number', (req, res) => { // Delete a directo
}); });
}); });
// User directory management via API key, for automated scripts
app.post('/api/v1/user/dir/newEntry', async (req, res) => {
const apiKey = req.headers['authorization'] ? req.headers['authorization'].replace('Bearer ', '') : null;
if (!apiKey) {
res.status(401).json({ error: 'API Key is required!' });
return;
}
const routeData = await pool.query("SELECT * FROM routes WHERE apiKey = ?", [apiKey]);
if (!routeData || routeData.length === 0) {
res.status(401).json({ error: 'Unauthorized' });
return;
}
const route = routeData[0];
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 < route.block_start || number > route.block_start + route.block_length) {
res.status(403).json({ error: 'Forbidden' });
return;
}
// Remove html
name = require("escape-html")(name);
// If number already exists, update, otherwise insert
pool.query('SELECT * FROM directory WHERE number = ? AND route = ?', [number, route.id]).then((rows) => {
const row = rows[0];
if (row) {
pool.query('UPDATE directory SET name = ? WHERE number = ? AND route = ?',
[name, number, route.id]).then(() => {
res.json({ message: 'Updated' });
}
).catch(err => {
console.error('Error updating directory entry:', err);
res.status(500).json({ error: 'Internal server error' });
});
} else {
pool.query('INSERT INTO directory (number, name, route) VALUES (?, ?, ?)',
[number, name, route.id]).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' });
});
});
app.delete('/api/v1/user/dir/deleteEntry/:number', async (req, res) => {
const apiKey = req.headers['authorization'] ? req.headers['authorization'].replace('Bearer ', '') : null;
if (!apiKey) {
res.status(401).json({ error: 'API Key is required!' });
return;
}
const routeData = await pool.query("SELECT * FROM routes WHERE apiKey = ?", [apiKey]);
if (!routeData || routeData.length === 0) {
res.status(401).json({ error: 'Unauthorized' });
return;
}
const route = routeData[0];
const number = Number(req.params.number);
if (!number) {
res.status(400).json({ error: 'Bad Request' });
return;
}
// Check that the number is within the block range for the current user
if (number < route.block_start || number > route.block_start + route.block_length) {
res.status(403).json({ error: 'Forbidden' });
return;
}
pool.query('DELETE FROM directory WHERE number = ? AND route = ?', [number, route.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' });
});
});
// == END USER ROUTES == // == END USER ROUTES ==
// == Directory routes == (unauthenticated) // == Directory routes == (unauthenticated)

31
package-lock.json generated
View file

@ -11,7 +11,7 @@
"dependencies": { "dependencies": {
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"connect-sqlite": "^0.0.1", "connect-sqlite": "^0.0.1",
"dotenv": "^16.4.7", "dotenv": "^16.6.1",
"ejs": "^3.1.10", "ejs": "^3.1.10",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"express": "^4.21.2", "express": "^4.21.2",
@ -734,9 +734,9 @@
} }
}, },
"node_modules/dotenv": { "node_modules/dotenv": {
"version": "16.4.7", "version": "16.6.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
@ -795,29 +795,6 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/encoding": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
"license": "MIT",
"optional": true,
"dependencies": {
"iconv-lite": "^0.6.2"
}
},
"node_modules/encoding/node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT",
"optional": true,
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/end-of-stream": { "node_modules/end-of-stream": {
"version": "1.4.4", "version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",

View file

@ -12,7 +12,7 @@
"dependencies": { "dependencies": {
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"connect-sqlite": "^0.0.1", "connect-sqlite": "^0.0.1",
"dotenv": "^16.4.7", "dotenv": "^16.6.1",
"ejs": "^3.1.10", "ejs": "^3.1.10",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"express": "^4.21.2", "express": "^4.21.2",