newStaffLDB-API (#48)

Merge newStaffLDB-API into main.

Ready to deploy

Reviewed-on: #48
This commit is contained in:
Fred Boniface 2023-10-03 21:35:00 +01:00
parent 5db9d8e52a
commit 0a25ae85f5
61 changed files with 2498 additions and 520705 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +0,0 @@
{
"GetStationBoardResult": {
"generatedAt": "2023-01-14T11:23:12.6558466+00:00",
"locationName": "Pilning",
"crs": "PIL",
"nrccMessages": {
"message": "\nPoor weather affecting services in Wales due to flooding on the railway More details can be found in <a href=\"https://t.co/uBU966PUmX\">Latest Travel News</a>."
},
"platformAvailable": "true"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,52 +0,0 @@
{
"service": [
{
"sta": "16:07",
"eta": "On time",
"operator": "South Western Railway",
"operatorCode": "SW",
"serviceType": "ferry",
"serviceID": "37782PHBR____",
"origin": {
"location": { "locationName": "Ryde Pier Head", "crs": "RYP" }
},
"destination": {
"location": { "locationName": "Portsmouth Harbour", "crs": "PMH" }
},
"previousCallingPoints": {
"callingPointList": {
"callingPoint": {
"locationName": "Ryde Pier Head",
"crs": "RYP",
"st": "15:45",
"et": "On time"
}
}
}
},
{
"std": "16:15",
"etd": "On time",
"operator": "South Western Railway",
"operatorCode": "SW",
"serviceType": "ferry",
"serviceID": "37746PHBR____",
"origin": {
"location": { "locationName": "Portsmouth Harbour", "crs": "PMH" }
},
"destination": {
"location": { "locationName": "Ryde Pier Head", "crs": "RYP" }
},
"subsequentCallingPoints": {
"callingPointList": {
"callingPoint": {
"locationName": "Ryde Pier Head",
"crs": "RYP",
"st": "16:37",
"et": "On time"
}
}
}
}
]
}

View File

@ -5,6 +5,7 @@ COPY ./.npmrc ./
RUN npm install RUN npm install
COPY . . COPY . .
# Ideally the tests should be run separately in a CI/CD workflow rather than during the build # Ideally the tests should be run separately in a CI/CD workflow rather than during the build
# Currently, it does prevent a container being published with failing tests
RUN npm run test RUN npm run test
RUN npm run build RUN npm run build

51
app.js
View File

@ -2,9 +2,12 @@
// Please see the included LICENSE file // Please see the included LICENSE file
console.log("Initialising OwlBoard");
const mode = process.env.NODE_ENV || "development"; const mode = process.env.NODE_ENV || "development";
// Logging
const logger = require("./src/utils/logger.utils");
logger.logger.info("Logger Initialised");
// External Requires // External Requires
const express = require("express"); const express = require("express");
const app = express(); const app = express();
@ -16,8 +19,8 @@ const cors = require("cors");
const authenticate = require("./src/middlewares/auth.middlewares"); const authenticate = require("./src/middlewares/auth.middlewares");
// Internal Requires // Internal Requires
const log = require("./src/utils/logs.utils"); const version = require("./src/configs/version.configs"); /*
const version = require("./src/configs/version.configs"); {}{}{} REMOVE THESE FILES - THEY ARE NO LONGER REQUIRED - {}{}{}
const listRtr = require("./src/routes/list.routes"); const listRtr = require("./src/routes/list.routes");
const ldbRtr = require("./src/routes/ldb.routes"); const ldbRtr = require("./src/routes/ldb.routes");
const ldbsRtr = require("./src/routes/ldbs.routes"); const ldbsRtr = require("./src/routes/ldbs.routes");
@ -26,7 +29,7 @@ const findRtr = require("./src/routes/find.routes");
const issueRtr = require("./src/routes/issue.routes"); const issueRtr = require("./src/routes/issue.routes");
const statRtr = require("./src/routes/stats.routes"); const statRtr = require("./src/routes/stats.routes");
const regRtr = require("./src/routes/registration.routes"); const regRtr = require("./src/routes/registration.routes");
const pisRtr = require("./src/routes/pis.routes"); const pisRtr = require("./src/routes/pis.routes");*/
const trainRtr = require("./src/routes/train.routes"); const trainRtr = require("./src/routes/train.routes");
const pis2Rtr = require("./src/routes/pis2.routes"); // API Version 2 Routes const pis2Rtr = require("./src/routes/pis2.routes"); // API Version 2 Routes
const ref2Rtr = require("./src/routes/ref2.routes"); // API Version 2 Routes const ref2Rtr = require("./src/routes/ref2.routes"); // API Version 2 Routes
@ -43,21 +46,19 @@ const limiter = rateLimit({
windowMs: 15 * (60 * 1000), // 15 minutes windowMs: 15 * (60 * 1000), // 15 minutes
max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes) max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: true, // Disable the `X-RateLimit-*` headers legacyHeaders: false, // Disable the `X-RateLimit-*` headers
}); });
// Print version number: // Print version number:
log.out(`app: Starting OwlBoard in ${mode} mode`, "init"); logger.logger.info(`Starting version ${version.app} in ${mode} mode`);
log.out(
`app: Starting OwlBoard - Backend Version: ${version.app} - ` + // Remove X-Powered-By header:
`API versions: ${version.api}`, app.disable("x-powered-by");
"init"
);
// Express Error Handling: // Express Error Handling:
app.use((err, req, res, next) => { app.use((err, req, res, next) => {
const statusCode = err.statuscode || 500; const statusCode = err.statuscode || 500;
console.error(err.message, err.stack); logger.logger.error(err, "Express Error");
res.status(statusCode).json({ message: err.message }); res.status(statusCode).json({ message: err.message });
return; return;
}); });
@ -82,17 +83,17 @@ app.use("/api/v2/user", user2Rtr); // API Version 2
app.use("/misc", miscRtr); // Non public-api endpoints (Stats, Issue, etc.) app.use("/misc", miscRtr); // Non public-api endpoints (Stats, Issue, etc.)
// Unauthenticated Routes // Unauthenticated Routes
app.use("/api/v1/list", listRtr); //app.use("/api/v1/list", listRtr); - Remove v1 List API - list.routes
app.use("/api/v1/ldb", ldbRtr); //app.use("/api/v1/ldb", ldbRtr); - Remove v1 LDB API
app.use("/api/v1/kube", kubeRtr); //app.use("/api/v1/kube", kubeRtr); - Remove v1 Kube API
app.use("/api/v1/find", findRtr); //app.use("/api/v1/find", findRtr); - Remove v1 Find API
app.use("/api/v1/issue", issueRtr); //app.use("/api/v1/issue", issueRtr); - Remove v1 Issue API
app.use("/api/v1/stats", statRtr); //app.use("/api/v1/stats", statRtr); - Remove v1 Stats API
app.use("/api/v1/register", regRtr); //app.use("/api/v1/register", regRtr); - Remove v1 Register API
// Authented Routes // Authented Routes
app.use("/api/v1/ldbs", authenticate, ldbsRtr); //app.use("/api/v1/ldbs", authenticate, ldbsRtr); - Remove v1 LDBS API
app.use("/api/v1/pis", authenticate, pisRtr); //app.use("/api/v1/pis", authenticate, pisRtr); - Remove v1 PIS API
app.use("/api/v1/auth/test", authenticate, (req, res) => app.use("/api/v1/auth/test", authenticate, (req, res) =>
res.status(200).json({ res.status(200).json({
status: "ok", status: "ok",
@ -110,12 +111,8 @@ mode === "development"
// Start Express // Start Express
app.listen(srvPort, srvListen, (error) => { app.listen(srvPort, srvListen, (error) => {
if (!error) { if (!error) {
log.out(`app.listen: Listening on http://${srvListen}:${srvPort}`, "init"); logger.logger.info(`Listening on http://${srvListen}:${srvPort}`);
log.out("app.listen: State - alive", "init");
} else { } else {
log.out( logger.logger.error(error, `Error starting server`);
`app.listen: Error occurred, server can't start ${error.message}`,
"err"
);
} }
}); });

View File

@ -4,19 +4,26 @@
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<style> <style>
html { html {
background-color: #404c55;
background-image: radial-gradient(#2b343c, #404c55);
text-align: center; text-align: center;
width: 100%; width: 100%;
} }
body {
margin: 0;
padding: 0;
}
a { a {
color: azure; color: azure;
} }
table { table {
width: 100%; width: 100%;
height: 100%;
margin: 0;
padding: 0;
color: azure; color: azure;
font-family: sans-serif; font-family: sans-serif;
text-align: center; text-align: center;
background-color: #404c55;
background-image: radial-gradient(#2b343c, #404c55);
} }
#title { #title {
height: 100px; height: 100px;
@ -53,14 +60,25 @@
<td> <td>
<h1>Register for OwlBoard</h1> <h1>Register for OwlBoard</h1>
<br /> <br />
<p>Use the link below to register for OwlBoard (Staff Version)</p> <p>
Tap the button to register this device, or scan the barcode with
another device.
</p>
<br /> <br />
<a <a
href="https://owlboard.info/more/reg/submit?key=>>ACCESSCODE<<" href="https://owlboard.info/more/reg/submit?key=>>ACCESSCODE<<"
id="button" id="button"
>Register</a >Register this device</a
> >
<br /><br /><br /> <br /><br /><br />
<p>Or scan with the device you want to register</p>
<br />
<img
src="https://barcodes.fjla.uk/generate?type=qr&text=https%3A%2F%2Fowlboard.info%2Fmore%2Freg%2Fsubmit%3Fkey%3D>>ACCESSCODE<<"
alt="Scan barcode to register on another device"
title="Scan to register on another device"
/>
<br /><br /><br />
<p> <p>
Alternatively copy and paste the link:<br />https://owlboard.info/more/reg/submit?key=>>ACCESSCODE<< Alternatively copy and paste the link:<br />https://owlboard.info/more/reg/submit?key=>>ACCESSCODE<<
</p> </p>

View File

@ -2,7 +2,9 @@ Complete your OwlBoard (Staff) Registration using the link below.
https://owlboard.info/more/reg/submit?key=>>ACCESSCODE<< https://owlboard.info/more/reg/submit?key=>>ACCESSCODE<<
Alternatively you can copy and paste the above link. Alternatively you can copy and paste the above link into your browser.
You can also view a QR code to register on another device: https://barcodes.fjla.uk/generate?type=qr&text=https%3A%2F%2Fowlboard.info%2Fmore%2Freg%2Fsubmit%3Fkey%3D>>ACCESSCODE<<
If you did not request to register to OwlBoard then you can safely ignore this email. If you did not request to register to OwlBoard then you can safely ignore this email.
Your email address has not been stored by us and will not be required unless you wish to register again. Your email address has not been stored by us and will not be required unless you wish to register again.

3089
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,15 +8,15 @@
}, },
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"author": "Fred Boniface", "author": "Fred Boniface",
"main": "express.js", "main": "app.js",
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"run": "tsc && node dist/app.js", "run": "tsc && node dist/app.js",
"start": "node app.js", "start": "node app.js",
"test": "jest" "test": "jest",
"format": "npx prettier -w ."
}, },
"dependencies": { "dependencies": {
"axios": "^1.2.1",
"compression": "^1.7.4", "compression": "^1.7.4",
"cors": "^2.8.5", "cors": "^2.8.5",
"express": "^4.18.2", "express": "^4.18.2",
@ -27,11 +27,12 @@
"moment-timezone": "^0.5.43", "moment-timezone": "^0.5.43",
"mongodb": "^4.13.0", "mongodb": "^4.13.0",
"nodemailer": "^6.9.1", "nodemailer": "^6.9.1",
"pino": "^8.15.1",
"redis": "^4.6.7", "redis": "^4.6.7",
"zlib": "^1.0.5" "zlib": "^1.0.5"
}, },
"devDependencies": { "devDependencies": {
"@owlboard/ts-types": "^0.0.8", "@owlboard/ts-types": "^0.0.9",
"@types/jest": "^29.5.3", "@types/jest": "^29.5.3",
"eslint": "^8.39.0", "eslint": "^8.39.0",
"jest": "^29.6.2", "jest": "^29.6.2",

View File

@ -1,6 +1,6 @@
// statusCodes should be a map, not an object // statusCodes should be a map, not an object
const statusCodes = { export const statusCodes = {
400: "data not found", 400: "data not found",
700: "no authentication attempt", 700: "no authentication attempt",
701: "invalid credentials", 701: "invalid credentials",
@ -14,5 +14,18 @@ const statusCodes = {
951: "unknown server error", 951: "unknown server error",
}; };
export const msgCodes = new Map<string, string>([
[
"LOC_NOT_FOUND",
"Location not found. If you are sure that the location exists, there may be a fault with the data provider.",
],
[
"USR_NOT_FOUND",
"User is not registered, consider regeristering for access to this resource",
],
["AUTH_ERR", "Authentication Error"],
["OK", "OK"],
]);
module.exports = statusCodes; module.exports = statusCodes;
export { statusCodes }; //export { statusCodes };

View File

@ -5,7 +5,7 @@ interface versions {
const version: versions = { const version: versions = {
api: ["/api/v1/", "/api/v2"], api: ["/api/v1/", "/api/v2"],
app: "2023.8.2", app: "2023.10.1",
}; };
module.exports = version; module.exports = version;

View File

@ -52,7 +52,7 @@ async function getStation(req, res, next) {
if (!req.isAuthed) { if (!req.isAuthed) {
const err = new Error("Unauthorized"); const err = new Error("Unauthorized");
err.status = 401; err.status = 401;
throw err; return next(err);
} }
res.json(await ldb.get(id, true)); res.json(await ldb.get(id, true));
} else { } else {

View File

@ -1,12 +1,13 @@
const utils = require("../utils/auth.utils"); const utils = require("../utils/auth.utils");
const log = require("../utils/logs.utils"); const logger = require("../utils/logger.utils");
module.exports = async function authCheck(req, res, next) { module.exports = async function authCheck(req, res, next) {
log.out("authMiddlewares: Checking authentication", "dbug"); //log.out("authMiddlewares: Checking authentication", "dbug");
logger.logger.debug("Auth check starting");
try { try {
var uuid = req.headers.uuid; var uuid = req.headers.uuid;
} catch (err) { } catch (err) {
log.out("authMiddlewares: User !isAuthed", "dbug"); logger.logger.warn("Unable to read UUID header - Not authenticated");
req.isAuthed = false; req.isAuthed = false;
return next(); return next();
} }
@ -14,14 +15,21 @@ module.exports = async function authCheck(req, res, next) {
var result = (await utils.isAuthed(uuid)) || false; var result = (await utils.isAuthed(uuid)) || false;
if (!result) { if (!result) {
req.isAuthed = false; req.isAuthed = false;
log.out("authMiddlewares: User !isAuthed", "dbug"); //log.out("authMiddlewares: User !isAuthed", "dbug");
logger.logger.debug("Auth denied");
} else { } else {
req.isAuthed = true; req.isAuthed = true;
log.out("authMiddlewares: User isAuthed", "dbug"); //log.out("authMiddlewares: User isAuthed", "dbug");
logger.logger.debug("Auth successful");
} }
return next(); return next();
} catch (err) { } catch (err) {
/*log.out(
"authMiddlewares: Unable to check auth, default to !isAuthed",
"warn"
);*/
logger.logger.error(err, `Auth check failed`);
req.isAuthed = false; req.isAuthed = false;
return next(); return next(err);
} }
}; };

View File

@ -1,8 +0,0 @@
const express = require("express");
const router = express.Router();
// Controller Imports
// Routes
module.exports = router;

View File

@ -1,28 +0,0 @@
const express = require("express");
const router = express.Router();
/* Controller Imports */
// Live
// Timetable
// Ref
// User
/* Routes */
// Live
router.get("/live/station/:id/:type");
router.get("/live/train/:searchType/:id");
// Timetable
router.get("/timetable/train/:date/:searchType/:id");
// User
router.post("/user");
router.get("/user/:uuid");
router.delete("/user/:uuid");
module.exports = router;

View File

@ -1,5 +1,4 @@
/* global process */ import { logger } from "../utils/logger.utils";
const log = require("../utils/logs.utils"); // Log Helper
const dbUser = process.env.OWL_DB_USER || "owl"; const dbUser = process.env.OWL_DB_USER || "owl";
const dbPass = process.env.OWL_DB_PASS || "twittwoo"; const dbPass = process.env.OWL_DB_PASS || "twittwoo";
@ -15,27 +14,27 @@ const db = client.db(dbName);
async function query(collection, query, returnId = false) { async function query(collection, query, returnId = false) {
await client.connect(); await client.connect();
log.out(`dbAccess.query: Connecting to collection: '${collection}'`, "info"); logger.trace(`dbAccess.query: Connecting to collection: '${collection}'`);
var qcoll = db.collection(collection); var qcoll = db.collection(collection);
var qcursor = qcoll.find(query); var qcursor = qcoll.find(query);
if (!returnId) { if (!returnId) {
qcursor.project({ _id: 0 }); qcursor.project({ _id: 0 });
} }
log.out(`dbAccess.query: Running Query: ${JSON.stringify(query)}`, "info"); logger.trace(query, "dbAccess.query: Runnung Query");
increment(collection); increment(collection);
return await qcursor.toArray(); let result = await qcursor.toArray();
logger.trace(result, "dbAccess.query: Response");
return result;
} }
async function queryProject(collection, query, projection) { async function queryProject(collection, query, projection) {
await client.connect(); await client.connect();
log.out(`dbAccess.queryProject: Connecting to col: '${collection}'`, "info"); logger.debug(`dbAccess.queryProject: Connecting to col: '${collection}'`);
const qcoll = db.collection(collection); const qcoll = db.collection(collection);
const qcursor = qcoll.find(query).project(projection); const qcursor = qcoll.find(query).project(projection);
log.out( logger.debug(
`dbAccess.query: Running Query: ${JSON.stringify( projection,
query `dbAccess.query: Running Query: ${JSON.stringify(query)}`
)}, Projection: ${JSON.stringify(projection)}`,
"dbug"
); );
increment(collection); increment(collection);
return await qcursor.toArray(); return await qcursor.toArray();
@ -43,17 +42,14 @@ async function queryProject(collection, query, projection) {
async function queryAggregate(collection, pipeline) { async function queryAggregate(collection, pipeline) {
await client.connect(); await client.connect();
log.out(`dbAccess.queryProject: Connecting to col: '${collection}'`, "info"); logger.debug(`dbAccess.queryProject: Connecting to col: '${collection}'`);
log.out( logger.trace(pipeline, "dbAccess.query: Running Aggregation");
`dbAccess.query: Running Aggregation: ${JSON.stringify(pipeline)}`,
"dbug"
);
increment(collection); increment(collection);
return await db.collection(collection).aggregate(pipeline).toArray(); return await db.collection(collection).aggregate(pipeline).toArray();
} }
async function increment(target) { async function increment(target) {
log.out(`dbAccess.increment: Incrementing counter for: ${target}`, "info"); logger.debug(`dbAccess.increment: Incrementing counter for: ${target}`);
await client.connect(); await client.connect();
let col = db.collection("meta"); let col = db.collection("meta");
let update = {}; let update = {};
@ -63,7 +59,7 @@ async function increment(target) {
async function addUser(uuid, domain) { async function addUser(uuid, domain) {
// Needs testing // Needs testing
log.out("dbAccess.addUser: Adding user to database"); logger.debug("dbAccess.addUser: Adding user to database");
let doc = { uuid: uuid, domain: domain, atime: new Date() }; let doc = { uuid: uuid, domain: domain, atime: new Date() };
await client.connect(); await client.connect();
let col = db.collection("users"); let col = db.collection("users");
@ -76,7 +72,7 @@ async function addUser(uuid, domain) {
async function addRegReq(uuid, domain) { async function addRegReq(uuid, domain) {
// Needs testing // Needs testing
log.out("dbAccess.addRegReq: Adding registration request"); logger.debug("dbAccess.addRegReq: Adding registration request");
let doc = { uuid: uuid, time: new Date(), domain: domain }; let doc = { uuid: uuid, time: new Date(), domain: domain };
await client.connect(); await client.connect();
let col = db.collection("registrations"); let col = db.collection("registrations");
@ -86,7 +82,7 @@ async function addRegReq(uuid, domain) {
async function userAtime(uuid) { async function userAtime(uuid) {
// Needs testing // Needs testing
log.out("dbAccess.userAtime: Updating access time for user"); logger.debug("dbAccess.userAtime: Updating access time for user");
let q = { uuid: uuid }; let q = { uuid: uuid };
let n = { $set: { uuid: uuid, atime: new Date() } }; let n = { $set: { uuid: uuid, atime: new Date() } };
await client.connect(); await client.connect();
@ -97,7 +93,7 @@ async function userAtime(uuid) {
// Deletes one single registration request entry from the DB // Deletes one single registration request entry from the DB
async function delRegReq(uuid) { async function delRegReq(uuid) {
log.out("dbAccess.delRegReq: Deleting a Registration Request"); logger.debug("dbAccess.delRegReq: Deleting a Registration Request");
let collection = "registrations"; let collection = "registrations";
await client.connect(); await client.connect();
let col = db.collection(collection); let col = db.collection(collection);
@ -105,11 +101,13 @@ async function delRegReq(uuid) {
} }
async function colCount(collection) { async function colCount(collection) {
log.out(`dbAccess.colCount: Counting entries in collection: ${collection}`); logger.debug(
`dbAccess.colCount: Counting entries in collection: ${collection}`
);
await client.connect(); await client.connect();
let col = db.collection(collection); let col = db.collection(collection);
let count = col.countDocuments(); let count = col.countDocuments();
log.out( logger.debug(
`dbAccess.colCount: Collection: ${collection} contains ${count}` + `dbAccess.colCount: Collection: ${collection} contains ${count}` +
" documents" " documents"
); );
@ -127,3 +125,15 @@ module.exports = {
delRegReq, delRegReq,
colCount, colCount,
}; };
export {
query,
queryProject,
queryAggregate,
increment,
addUser,
userAtime,
addRegReq,
delRegReq,
colCount,
};

View File

@ -1,59 +0,0 @@
// Parse and return a find request
const log = require("../utils/logs.utils"); // Log Helper
const db = require("../services/dbAccess.services");
const san = require("../utils/sanitizer.utils");
// DB Query: query(collection, query)
// Define collection as all queries are for the "corpus" collection.
const col = "corpus";
async function name(id) {
log.out(`findServices.name: Finding station name: ${id}`, "info");
var name = san.cleanApiEndpointTxt(id.toUpperCase());
let query = { NLCDESC: name };
//var data = await db.query(col,query)
return await db.query(col, query);
}
async function crs(id) {
log.out(`findServices.crs: Finding crs: ${id}`, "info");
var crs = san.cleanApiEndpointTxt(id.toUpperCase());
let query = { "3ALPHA": crs };
//var data = await db.query(col,query)
return await db.query(col, query);
}
async function nlc(id) {
log.out(`findServices.nlc: Finding nlc: ${id}`, "info");
var nlc = san.cleanApiEndpointNum(id);
let query = { NLC: parseInt(nlc) };
log.out(`findServices.nlc: NLC Converted to int: ${query}`, "info");
//var data = await db.query(col,query)
return await db.query(col, query);
}
async function tiploc(id) {
log.out(`findServices.tiploc: Finding tiploc: ${id}`, "info");
var tiploc = san.cleanApiEndpointTxt(id.toUpperCase());
let query = { TIPLOC: tiploc };
//var data = await db.query(col,query)
return await db.query(col, query);
}
async function stanox(id) {
log.out(`findServices.stanox: Finding stanox: ${id}`, "info");
var stanox = san.cleanApiEndpointNum(id);
let query = { STANOX: String(stanox) };
//var data = await db.query(col,query)
return await db.query(col, query);
}
module.exports = {
name,
crs,
nlc,
tiploc,
stanox,
};

View File

@ -0,0 +1,55 @@
// Parse and return a find request
import { query } from "../services/dbAccess.services";
import {
cleanApiEndpointTxt,
cleanApiEndpointNum,
} from "../utils/sanitizer.utils";
import { logger } from "../utils/logger.utils";
// Define collection as all queries are for the "corpus" collection.
const col: string = "corpus";
async function name(id: string) {
logger.debug(`findServices.name: Finding station name: ${id}`);
var name = cleanApiEndpointTxt(id.toUpperCase());
let queryObj = { NLCDESC: name };
return await query(col, queryObj);
}
async function crs(id: string) {
logger.debug(`findServices.crs: Finding crs: ${id}`);
var crs = cleanApiEndpointTxt(id.toUpperCase());
let queryObj = { "3ALPHA": crs };
return await query(col, queryObj);
}
async function nlc(id: string) {
logger.debug(`findServices.nlc: Finding nlc: ${id}`);
var nlc = cleanApiEndpointNum(id);
let queryObj = { NLC: parseInt(nlc) };
logger.trace(`findServices.nlc: NLC Converted to int: ${query}`);
return await query(col, queryObj);
}
async function tiploc(id: string) {
logger.debug(`findServices.tiploc: Finding tiploc: ${id}`);
var tiploc = cleanApiEndpointTxt(id.toUpperCase());
let queryObj = { TIPLOC: tiploc };
return await query(col, queryObj);
}
async function stanox(id: string) {
logger.debug(`findServices.stanox: Finding stanox: ${id}`);
var stanox = cleanApiEndpointNum(id);
let queryObj = { STANOX: String(stanox) };
return await query(col, queryObj);
}
module.exports = {
name,
crs,
nlc,
tiploc,
stanox,
};

View File

@ -1,6 +1,4 @@
/* eslint-disable no-useless-escape */ import { logger } from "../utils/logger.utils";
const axios = require("axios");
const log = require("../utils/logs.utils");
const issueLabels = { const issueLabels = {
bug: 120, bug: 120,
@ -11,8 +9,7 @@ const issueLabels = {
}; };
async function processor(data) { async function processor(data) {
log.out("issueService.processor: Issue received", "info"); logger.debug("issueService.processor: Issue received");
console.log(data); // TEMPORARY MEASURE
let out = {}; let out = {};
out.labels = [issueLabels[data?.label] || 0, issueLabels["web-user"]]; out.labels = [issueLabels[data?.label] || 0, issueLabels["web-user"]];
out.title = data?.subject.replace(/<[^>]+>|[\*\$]/g, ""); out.title = data?.subject.replace(/<[^>]+>|[\*\$]/g, "");
@ -21,22 +18,32 @@ async function processor(data) {
} }
async function sendToGitea(body) { async function sendToGitea(body) {
let key = process.env.OWL_GIT_ISSUEBOT; try {
let url = process.env.OWL_GIT_APIENDPOINT; const key = process.env.OWL_GIT_ISSUEBOT;
let opts = { const url = process.env.OWL_GIT_APIENDPOINT;
headers: { const opts = {
Authorization: key, method: "POST",
}, headers: {
}; Authorization: key,
var res = await axios.post(url, body, opts); "Content-Type": "application/json",
/* Need to read the output from the POST and pass the result upwards to the },
client.*/ body: JSON.stringify(body),
if (res.status == 201) { };
log.out("issueService.sendToGitea: Issue sent to Gitea", "info");
return { status: res.status, message: "issue created" }; const res = await fetch(url, opts);
} else {
log.out(`issueService.sendToGitea: Fail to send issue: ${res.body}`, "err"); if (res.status === 201) {
return { status: res.status, message: "issue not created" }; logger.debug("issueService.sendToGitea: Issue created");
return { status: res.status, message: "issue created" };
} else {
logger.error(
`issueService.sendtoGitea: Error creating issue RETURN: ${res.status}`
);
return { status: res.status, message: "issue not created" };
}
} catch (err) {
logger.error(err, `issueService.sendToGitea`);
return { status: 500, message: "Internal Server Error" };
} }
} }

View File

@ -1,28 +0,0 @@
const testing = require("../services/mail.services");
const log = require("../utils/logs.utils");
async function getAlive() {
log.out("kubeServices.getAlive: alive hook checked", "info");
return { code: 200, state: { state: "alive", noise: "twit-twoo" } };
}
async function getReady() {
log.out("kubeServices.getReady: ready hook checked", "info");
testing.send({
to: "fred@fjla.uk",
subject: "OwlBoard Test",
txt: "This is a test message from OwlBoard (testing)",
});
return "not_implemented";
}
async function getTime() {
var now = new Date();
return { responseGenerated: now };
}
module.exports = {
getAlive,
getReady,
getTime,
};

View File

@ -0,0 +1,22 @@
import { logger } from "../utils/logger.utils";
async function getAlive() {
logger.trace("kubeServices.getAlive: alive hook checked");
return { code: 200, state: { state: "alive", noise: "twit-twoo" } };
}
async function getReady() {
logger.trace("kubeServices.getReady: ready hook checked");
return "not_implemented";
}
async function getTime() {
var now: Date = new Date();
return { responseGenerated: now };
}
module.exports = {
getAlive,
getReady,
getTime,
};

View File

@ -1,12 +1,14 @@
// Parse and return an LDB Request // Parse and return an LDB Request
const log = require("../utils/logs.utils"); // Log Helper
const ldb = require("ldbs-json"); const ldb = require("ldbs-json");
const util = require("../utils/ldb.utils"); const util = require("../utils/ldb.utils");
const san = require("../utils/sanitizer.utils"); const san = require("../utils/sanitizer.utils");
const db = require("../services/dbAccess.services"); const db = require("../services/dbAccess.services");
import { logger } from "../utils/logger.utils";
import { transform as staffStationTransform } from "../utils/translators/ldb/staffStation"; import { transform as staffStationTransform } from "../utils/translators/ldb/staffStation";
import { msgCodes } from "../configs/errorCodes.configs";
const ldbKey = process.env.OWL_LDB_KEY; const ldbKey = process.env.OWL_LDB_KEY;
const ldbsvKey = process.env.OWL_LDB_SVKEY; const ldbsvKey = process.env.OWL_LDB_SVKEY;
@ -16,7 +18,7 @@ async function get(id, staff = false) {
const obj = await util.checkCrs(cleanId); const obj = await util.checkCrs(cleanId);
try { try {
const crs = obj[0]["3ALPHA"]; const crs = obj[0]["3ALPHA"];
log.out(`ldbService.get: Determined CRS for lookup to be: ${crs}`, "info"); logger.debug(`ldbService.get: Determined CRS for lookup to be: ${crs}`);
if (staff) { if (staff) {
const data = arrDepBoardStaff(crs); const data = arrDepBoardStaff(crs);
db.increment("ldbsvws"); db.increment("ldbsvws");
@ -27,16 +29,16 @@ async function get(id, staff = false) {
return await data; return await data;
} }
} catch (err) { } catch (err) {
log.out(`ldbService.get: Error, Unable to find CRS: ${err}`, "info"); logger.error(err, "ldbService.get: Error, Unable to find CRS");
return { return {
ERROR: "NOT_FOUND", obStatus: "LOC_NOT_FOUND",
description: "The entered station was not found.", obMsg: "UNABLE TO FIND MESSAGE",
}; };
} }
} }
async function arrDepBoard(CRS) { async function arrDepBoard(CRS) {
log.out(`ldbService.arrDepBoard: Trying to fetch board for ${CRS}`, "info"); logger.trace(`ldbService.arrDepBoard: Trying to fetch board for ${CRS}`);
try { try {
const options = { const options = {
numRows: 10, numRows: 10,
@ -46,7 +48,7 @@ async function arrDepBoard(CRS) {
let d = await api.call("GetArrDepBoardWithDetails", options, false, false); let d = await api.call("GetArrDepBoardWithDetails", options, false, false);
return await util.cleanData(d); return await util.cleanData(d);
} catch (err) { } catch (err) {
log.out(`ldbService.arrDepBoard: Lookup Failed for: ${CRS}`, "warn"); logger.error(err, "ldbService.arrDepBoard: Lookup Failed");
return { return {
GetStationBoardResult: "not available", GetStationBoardResult: "not available",
Reason: `The CRS code ${CRS} is not valid`, Reason: `The CRS code ${CRS} is not valid`,
@ -55,7 +57,7 @@ async function arrDepBoard(CRS) {
} }
async function arrDepBoardStaff(CRS) { async function arrDepBoardStaff(CRS) {
log.out(`ldbService.arrDepBoardStaff: Try to fetch board for ${CRS}`, "dbug"); logger.debug(`ldbService.arrDepBoardStaff: Try to fetch board for ${CRS}`);
try { try {
const options = { const options = {
numRows: 40, numRows: 40,
@ -73,18 +75,24 @@ async function arrDepBoardStaff(CRS) {
false, false,
false false
); );
console.log("\n\n\nORIGINAL DATA");
console.log("\n" + JSON.stringify(result) + "\n\n\n");
console.timeEnd(`Fetch Staff LDB for ${CRS.toUpperCase()}`); console.timeEnd(`Fetch Staff LDB for ${CRS.toUpperCase()}`);
try { try {
const _staffLdb = staffStationTransform(result); const _staffLdb = staffStationTransform(result);
console.log("Transformation Test Successful"); logger.debug("StaffLDB Transformed");
console.log(JSON.stringify(_staffLdb)); logger.trace(_staffLdb, "StaffLDB Transformed");
return {
obStatus: "OK",
obMsg: "OK",
data: _staffLdb,
};
} catch (err) { } catch (err) {
console.log("Transformation Test Failed: " + err); logger.error(err, "Transformation Error");
} }
return result; return result;
} catch (err) { } catch (err) {
log.out(`ldbService.arrDepBoardStaff: Lookup Failed for: ${CRS}`, "warn"); logger.error(err, "ldbService.arrDepBoardStaff error");
log.out(`ldbService.arrDepBoardStaff: ${err}`);
return { return {
GetStationBoardResult: "not available", GetStationBoardResult: "not available",
Reason: `The CRS code ${CRS} is not valid`, Reason: `The CRS code ${CRS} is not valid`,
@ -93,7 +101,7 @@ async function arrDepBoardStaff(CRS) {
} }
async function getServiceByRID(rid) { async function getServiceByRID(rid) {
log.out(`ldbService.getServiceByRID: Finding RID: ${rid}`, "dbug"); logger.debug(`ldbService.getServiceByRID: Finding RID: ${rid}`);
try { try {
const options = { const options = {
rid: String(rid), rid: String(rid),
@ -101,12 +109,12 @@ async function getServiceByRID(rid) {
const api = new ldb(ldbsvKey, true); const api = new ldb(ldbsvKey, true);
return await api.call("GetServiceDetailsByRID", options, false, false); return await api.call("GetServiceDetailsByRID", options, false, false);
} catch (err) { } catch (err) {
log.out(`ldbService.queryService: ${err}`, "EROR"); logger.error(err, `ldbService.queryService`);
} }
} }
async function getServicesByOther(id) { async function getServicesByOther(id) {
log.out(`ldbService.getServiceByOther: Finding services: ${id}`, "dbug"); logger.debug(`ldbService.getServiceByOther: Finding services: ${id}`);
try { try {
const options = { const options = {
serviceID: id, serviceID: id,
@ -115,30 +123,30 @@ async function getServicesByOther(id) {
const api = new ldb(ldbsvKey, true); const api = new ldb(ldbsvKey, true);
return await api.call("QueryServices", options, false, false); return await api.call("QueryServices", options, false, false);
} catch (err) { } catch (err) {
log.out(`ldbService.getServiceByOther: Error: ${err}`, "EROR"); logger.error(err, "ldbService.getServiceByOther");
return false; return false;
} }
} }
async function getReasonCodeList() { async function getReasonCodeList() {
log.out("ldbService.getReasonCodeList: Fetching reason code list", "eror"); logger.debug("ldbService.getReasonCodeList: Fetching reason code list");
try { try {
const dbFilter = {}; const dbFilter = {};
return await db.query("reasonCodes", dbFilter, false); return await db.query("reasonCodes", dbFilter, false);
} catch (err) { } catch (err) {
log.out(`ldbService.getReasonCodeList: ${err}`, "eror"); logger.error(err, "ldbService.getReasonCodeList");
} }
} }
async function getReasonCode(code) { async function getReasonCode(code) {
log.out(`ldbService.getReasonCode: Fetching reason code ${code}`, "dbug"); logger.debug(`ldbService.getReasonCode: Fetching reason code ${code}`);
try { try {
const dbFilter = { const dbFilter = {
code: code, code: code,
}; };
return await db.query("reasonCodes", dbFilter, false); return await db.query("reasonCodes", dbFilter, false);
} catch (err) { } catch (err) {
log.out(`ldbService.getReasonCode: ${err}`, "eror"); logger.error(err, "ldbService.getReasonCode");
} }
} }

View File

@ -1,19 +0,0 @@
const log = require("../utils/logs.utils"); // Log Helper
const db = require("../services/dbAccess.services");
async function getStations() {
var out = db.query("stations");
log.out("listServices.getStations: Fetching stations list", "info");
return await out;
}
async function getCorpus() {
var out = db.query("corpus");
log.out("listServices.getCorpus: Fetching CORPUS list", "info");
return await out;
}
module.exports = {
getStations,
getCorpus,
};

View File

@ -0,0 +1,19 @@
import { query } from "./dbAccess.services";
import { logger } from "../utils/logger.utils";
async function getStations() {
var out = query("stations");
logger.debug("listServices.getStations: Fetching stations list");
return await out;
}
async function getCorpus() {
var out = query("corpus");
logger.debug("listServices.getCorpus: Fetching CORPUS list");
return await out;
}
module.exports = {
getStations,
getCorpus,
};

View File

@ -1,37 +0,0 @@
const log = require("../utils/logs.utils");
const mail = require("nodemailer"); //>> Probs wrong
const fromAddr = process.env.OWL_EML_FROM;
const smtpUser = process.env.OWL_EML_USER;
const smtpPass = process.env.OWL_EML_PASS;
const smtpHost = process.env.OWL_EML_HOST;
const smtpPort = process.env.OWL_EML_PORT;
let transporter = mail.createTransport({
host: smtpHost,
port: smtpPort,
secure: false, // Must be false for STARTTLS on port 587
auth: {
user: smtpUser,
pass: smtpPass,
},
});
/* 'message' is an object containing string values for:
*to, cc, bcc, *subject, *txt, html. * denotes required */
async function send(message) {
log.out("mailServices.send: Message send request received", "info");
message.from = fromAddr;
try {
var res = await transporter.sendMail(message);
} catch (err) {
log.out(`mailServices.send: Message send failed: ${err}`, "err");
return false;
}
log.out(`mailServices.send: SMTP Response: ${res.response}`);
return true;
}
module.exports = {
send,
};

View File

@ -0,0 +1,45 @@
const mail = require("nodemailer");
import { logger } from "../utils/logger.utils";
export interface msgDescriptor {
to: string;
cc?: string;
bcc?: string;
subject: string;
txt: string;
html?: string;
from: string;
}
const fromAddr = process.env.OWL_EML_FROM;
const smtpUser = process.env.OWL_EML_USER;
const smtpPass = process.env.OWL_EML_PASS;
const smtpHost = process.env.OWL_EML_HOST;
const smtpPort = process.env.OWL_EML_PORT;
const transporter = mail.createTransport({
host: smtpHost,
port: smtpPort,
secure: false, // Must be false for STARTTLS on port 587 which is always secure
auth: {
user: smtpUser,
pass: smtpPass,
},
});
async function send(message: msgDescriptor): Promise<boolean> {
logger.debug("mailServices.send: Message send request received");
message.from = fromAddr || "no-reply@owlboard.info";
try {
var res = await transporter.sendMail(message);
} catch (err) {
logger.error(err, "mailServices.send: Message send failed");
return false;
}
logger.debug(res.response, "mailServices.send: SMTP Response");
return true;
}
module.exports = {
send,
};

View File

@ -1,16 +1,15 @@
// Finds PIS Codes using DB Lookups // Finds PIS Codes using DB Lookups
const db = require("../services/dbAccess.services"); const db = require("../services/dbAccess.services");
const log = require("../utils/logs.utils");
const clean = require("../utils/sanitizer.utils"); const clean = require("../utils/sanitizer.utils");
import { logger } from "../utils/logger.utils";
const supported = ["GW", "UK"]; const supported = ["GW", "UK"];
async function findPisByOrigDest(start, end) { async function findPisByOrigDest(start, end) {
log.out( logger.debug(
"pisServices.findPisByOrigDest: Searching for PIS for Orig: " + `pisServices.findPisByOrigDest: Searching for Orig: ${start}, Dest: ${end}`
`${start}, Dest: ${end}`,
"dbug"
); );
const firstCrs = clean.cleanApiEndpointTxt(start.toLowerCase()); const firstCrs = clean.cleanApiEndpointTxt(start.toLowerCase());
const lastCrs = clean.cleanApiEndpointTxt(end.toLowerCase()); const lastCrs = clean.cleanApiEndpointTxt(end.toLowerCase());
@ -33,7 +32,7 @@ async function findPisByOrigDest(start, end) {
} }
async function findPisByCode(code) { async function findPisByCode(code) {
log.out(`pisServices.findPisByCode: Searching for PIS code: ${code}`, "dbug"); logger.debug(`pisServices.findPisByCode: Searching for PIS code: ${code}`);
const cleanCode = clean.removeNonNumeric(code); const cleanCode = clean.removeNonNumeric(code);
const query = { const query = {
code: parseInt(cleanCode), code: parseInt(cleanCode),

View File

@ -1,4 +1,3 @@
const log = require("../utils/logs.utils");
const auth = require("../utils/auth.utils"); const auth = require("../utils/auth.utils");
const db = require("./dbAccess.services"); const db = require("./dbAccess.services");
const mail = require("./mail.services"); const mail = require("./mail.services");
@ -6,23 +5,24 @@ const clean = require("../utils/sanitizer.utils");
const domains = require("../configs/domains.configs"); const domains = require("../configs/domains.configs");
const errors = require("../configs/errorCodes.configs"); const errors = require("../configs/errorCodes.configs");
import { logger } from "../utils/logger.utils";
async function createRegKey(body) { async function createRegKey(body) {
log.out("registerServices.createRegKey: Incoming request", "INFO"); logger.debug("registerServices.createRegKey: Incoming request");
if (body.email) { if (body.email) {
const domain = await clean.getDomainFromEmail(body.email); const domain = await clean.getDomainFromEmail(body.email);
log.out(`registerServices: Registration request from: ${domain}`, "info"); logger.info(`registerServices: Registration request from: ${domain}`);
if (domains.includes(domain)) { if (domains.includes(domain)) {
log.out( logger.debug(`registerServices.createRegKey: Key from valid: ${domain}`);
`registerServices.createRegKey: Key from valid: ${domain}`,
"info"
);
const uuid = await auth.generateKey(); const uuid = await auth.generateKey();
db.addRegReq(uuid, domain); db.addRegReq(uuid, domain);
const message = await auth.generateConfirmationEmail(body.email, uuid); const message = await auth.generateConfirmationEmail(body.email, uuid);
if (!message) { if (!message) {
const err = new Error("Message generation error"); const err = new Error("Message generation error");
log.out("registerServices.createRegKey: Error generating email", "err"); logger.error(
log.out(err, "err"); err,
"registerServices.createRegKey: Error generating email"
);
return 500; return 500;
} }
if ((await mail.send(message)) == true) { if ((await mail.send(message)) == true) {
@ -38,10 +38,9 @@ async function createRegKey(body) {
async function regUser(req) { async function regUser(req) {
// Add input validation // Add input validation
log.out(`Read UUID: ${req.uuid}`, "dbug"); logger.trace(`Read UUID: ${req.uuid}`);
log.out(`registrationServices.regUser: Request from: ${req.uuid}`, "info");
const res = await auth.checkRequest(req.uuid); const res = await auth.checkRequest(req.uuid);
log.out(`registrationServices.regUser: ${JSON.stringify(res)}`, "info"); logger.debug(res, "registrationServices");
if (res.result) { if (res.result) {
const uuid = await auth.generateKey(); const uuid = await auth.generateKey();
const apiKey = await db.addUser(uuid, res.domain); const apiKey = await db.addUser(uuid, res.domain);
@ -54,7 +53,6 @@ async function regUser(req) {
} }
async function getUser(uuid) { async function getUser(uuid) {
log.out("registrationServices: Finding user for given UUID", "dbug");
try { try {
const filter = { const filter = {
uuid: uuid, uuid: uuid,

View File

@ -1,8 +1,9 @@
const log = require("../utils/logs.utils"); // Log Helper
const db = require("../services/dbAccess.services"); const db = require("../services/dbAccess.services");
const os = require("os"); const os = require("os");
const vers = require("../configs/version.configs"); const vers = require("../configs/version.configs");
import { logger } from "../utils/logger.utils";
async function buildJson() { async function buildJson() {
let json = {}; let json = {};
json.count = {}; json.count = {};
@ -34,14 +35,13 @@ async function buildJson() {
} }
async function hits() { async function hits() {
log.out("statsServices.hits: Statistics Requested", "info"); logger.debug("statsServices.hits: Statistics Requested");
const out = await buildJson(); const out = await buildJson();
log.out(`statsServices.hits: Sending Data: ${JSON.stringify(out)}`, "info");
return out; return out;
} }
async function getVersions() { async function getVersions() {
log.out("statsServices.getVersions: Fetching versions", "info"); logger.debug("statsServices.getVersions: Fetching versions");
const dbMan = await db.query("versions", { target: "dbmanager" }); const dbMan = await db.query("versions", { target: "dbmanager" });
const mqClt = await db.query("versions", { target: "mq-client" }); const mqClt = await db.query("versions", { target: "mq-client" });
const data = { const data = {
@ -53,7 +53,7 @@ async function getVersions() {
} }
async function statistics() { async function statistics() {
log.out("statsServices.statistics: Fetching statistics", "info"); logger.debug("statsServices.statistics: Fetching statistics");
const countersPromise = db.query("meta", { target: "counters" }); const countersPromise = db.query("meta", { target: "counters" });
const timetablePromise = db.query("meta", { target: "timetable" }); const timetablePromise = db.query("meta", { target: "timetable" });

View File

@ -1,15 +1,13 @@
const log = require("../utils/logs.utils");
const db = require("./dbAccess.services"); const db = require("./dbAccess.services");
const clean = require("../utils/sanitizer.utils"); const clean = require("../utils/sanitizer.utils");
const pis = require("../services/pis.services"); const pis = require("../services/pis.services");
const { filter } = require("compression");
import { logger } from "../utils/logger.utils";
async function findByHeadcodeToday(headcode) { async function findByHeadcodeToday(headcode) {
const sanitizedHeadcode = clean.removeNonAlphanumeric(headcode).toUpperCase(); const sanitizedHeadcode = clean.removeNonAlphanumeric(headcode).toUpperCase();
log.out( logger.debug(
"trainServiceServices.findByHeadcode: Searching for headcode " + `trainServiceServices.findByHeadcode: Searching for headcode ${sanitizedHeadcode}`
sanitizedHeadcode,
"dbug"
); );
const now = new Date(); const now = new Date();
const dayMap = ["su", "m", "t", "w", "th", "f", "s"]; const dayMap = ["su", "m", "t", "w", "th", "f", "s"];
@ -43,10 +41,8 @@ async function findByHeadcodeToday(headcode) {
async function findByHeadcode(date, headcode) { async function findByHeadcode(date, headcode) {
const sanitizedHeadcode = clean.removeNonAlphanumeric(headcode).toUpperCase(); const sanitizedHeadcode = clean.removeNonAlphanumeric(headcode).toUpperCase();
log.out( logger.debug(
"trainServiceServices.findByHeadcode: Searching for headcode " + `trainServiceServices.findByHeadcode: Searching for headcode ${sanitizedHeadcode}`
sanitizedHeadcode,
"dbug"
); );
let searchDate; let searchDate;
if (date === "now") { if (date === "now") {
@ -82,7 +78,6 @@ async function findByHeadcode(date, headcode) {
}, },
]; ];
const queryData = await db.queryAggregate("timetable", pipeline); const queryData = await db.queryAggregate("timetable", pipeline);
console.log(JSON.stringify(queryData));
let filteredData = filterServices(queryData); let filteredData = filterServices(queryData);
return await filteredData; return await filteredData;
} }

View File

@ -1,19 +1,18 @@
const logs = require("../utils/logs.utils");
const crypt = require("crypto"); const crypt = require("crypto");
const db = require("../services/dbAccess.services"); const db = require("../services/dbAccess.services");
const fs = require("fs/promises"); const fs = require("fs/promises");
import { minifyMail } from "./minify.utils"; import { minifyMail } from "./minify.utils";
import { logger } from "./logger.utils";
// Checks users registration key against issued keys // Checks users registration key against issued keys
async function isAuthed(uuid: string) { async function isAuthed(uuid: string): Promise<boolean> {
// Needs testing // Needs testing
const q = { uuid: uuid }; const q = {
uuid: uuid,
};
const res = await db.query("users", q); const res = await db.query("users", q);
logs.out( logger.debug(res, "checkUser: DB Query Result");
"authUtils.checkUser: DB Query answer: " + JSON.stringify(res[0]),
"dbug"
);
const authorized = res && res[0] && res[0].domain; const authorized = res && res[0] && res[0].domain;
if (authorized) db.userAtime(uuid); if (authorized) db.userAtime(uuid);
return authorized; return authorized;
@ -21,13 +20,11 @@ async function isAuthed(uuid: string) {
// Checks whether a registration request key is valid // Checks whether a registration request key is valid
async function checkRequest(key: string) { async function checkRequest(key: string) {
// For some reason db.query seems to return correctly, but the second logs.out statement prints []??? so registration fails!!
const collection = "registrations"; const collection = "registrations";
const query = { uuid: key }; const query = { uuid: key };
const res = await db.query(collection, query); const res = await db.query(collection, query);
logs.out( logger.debug(res, "checkRequest: DB Lookup result");
"authUtils.checkRequest: DB Query result: " + JSON.stringify(res),
"dbug"
);
const result = const result =
res.length > 0 && res[0].time res.length > 0 && res[0].time
? { result: true, domain: res[0].domain } ? { result: true, domain: res[0].domain }
@ -37,7 +34,7 @@ async function checkRequest(key: string) {
// Creates an API key for a user // Creates an API key for a user
async function generateKey() { async function generateKey() {
// Needs testing & moving to 'register.utils' // Needs testing & moving to 'register.utils' ??? Why does it need moving?
return crypt.randomUUID(); return crypt.randomUUID();
} }
@ -54,9 +51,9 @@ async function generateConfirmationEmail(eml: string, uuid: string) {
html: htmlMin, html: htmlMin,
}; };
} catch (err) { } catch (err) {
logs.out( logger.error(
"mailServices.generateConfirmationEmail: Error reading template, " + err, err,
"err" "generateConfirmationEmail: Error rendering email templates"
); );
return false; return false;
} }

View File

@ -18,6 +18,7 @@ async function checkCrs(input = "") {
} }
// Needs to be moved to the frontend `ensureArray() func` // Needs to be moved to the frontend `ensureArray() func`
// Usage of this function should be migrated to the `translator` utilities.
async function cleanMessages(input) { async function cleanMessages(input) {
log.out("ldbUtils.cleanMessages: Deprecated function has been called", "err"); log.out("ldbUtils.cleanMessages: Deprecated function has been called", "err");
var out = []; var out = [];

19
src/utils/logger.utils.ts Normal file
View File

@ -0,0 +1,19 @@
import pino from "pino";
const runtime = process.env.NODE_ENV;
let level: string;
if (runtime === "production") {
level = "info";
} else {
level = "debug";
}
export const logger = pino({
level: level,
formatters: {
level: (label) => {
return { level: label.toUpperCase() };
},
},
timestamp: pino.stdTimeFunctions.isoTime,
});

View File

@ -1,7 +1,11 @@
import { logger } from "./logger.utils";
const htmlShrink = require("html-minifier").minify; const htmlShrink = require("html-minifier").minify;
const juice = require("juice"); const juice = require("juice");
// Inlines styles and minifies the inlined HTML
async function minifyMail(input: string): Promise<string> { async function minifyMail(input: string): Promise<string> {
logger.trace("minifyMail: Minifying mail output");
const inlined: string = juice(input); const inlined: string = juice(input);
return htmlShrink(inlined, { return htmlShrink(inlined, {
removeComments: true, removeComments: true,

View File

@ -1,4 +1,7 @@
import { logger } from "./logger.utils";
export function removeNewlineAndPTag(input: string): string { export function removeNewlineAndPTag(input: string): string {
logger.debug("removeNewlineAndPTag: Cleaning string");
const regex = /[\n\r]|<\/?p[^>]*>/g; const regex = /[\n\r]|<\/?p[^>]*>/g;
return input.replace(regex, function (match) { return input.replace(regex, function (match) {
if (match === "\n" || match === "\r") { if (match === "\n" || match === "\r") {

View File

@ -1,14 +1,19 @@
//const log = require('../utils/log.utils'); //const log = require('../utils/log.utils');
import { logger } from "./logger.utils";
function removeNonAlphanumeric(inputString: string) { function removeNonAlphanumeric(inputString: string) {
logger.debug("removeNonAlphanumeric: Sanitizing string");
return inputString.replace(/[^a-zA-Z0-9]/g, ""); return inputString.replace(/[^a-zA-Z0-9]/g, "");
} }
function removeNonAlpha(inputString: string) { function removeNonAlpha(inputString: string) {
logger.debug("removeNonAlpha: Sanitizing string");
return inputString.replace(/[^a-zA-Z]/g, ""); return inputString.replace(/[^a-zA-Z]/g, "");
} }
function removeNonNumeric(inputString: string) { function removeNonNumeric(inputString: string) {
logger.debug("removeNonNumeric: Sanitizing string");
return inputString.replace(/[^0-9]/g, ""); return inputString.replace(/[^0-9]/g, "");
} }
@ -16,15 +21,17 @@ const cleanApiEndpointTxt = removeNonAlpha;
const cleanApiEndpointNum = removeNonAlphanumeric; const cleanApiEndpointNum = removeNonAlphanumeric;
function cleanNrcc(input: string) { function cleanNrcc(input: string) {
logger.error("DEPRECATED FUNCTION", "cleanNrcc: Converting NRCC Data");
// Remove newlines and then <p> tags from input // Remove newlines and then <p> tags from input
const cleanInput = input.replace(/[\n\r]/g, "").replace(/<\/?p[^>]*>/g, ""); const cleanInput = input.replace(/[\n\r]/g, "").replace(/<\/?p[^>]*>/g, "");
return cleanInput; return cleanInput;
} }
function getDomainFromEmail(mail: string) { function getDomainFromEmail(mail: string) {
logger.debug("getDomainFromEmail: Obtaining domain from email address");
// Needs testing // Needs testing
let split = mail.split("@"); let split = mail.split("@");
return split[1]; return split[1].toLowerCase();
} }
module.exports = { module.exports = {

View File

@ -1,14 +1,19 @@
function unixLocal(unix: number) { import { logger } from "./logger.utils";
function unixLocal(unix: number): string {
logger.trace(`unixLocal: Converting time: ${unix}`);
var jsTime = unix * 1000; var jsTime = unix * 1000;
var dt = new Date(jsTime); var dt = new Date(jsTime);
return dt.toLocaleString(); return dt.toLocaleString();
} }
function jsUnix(js: number) { function jsUnix(js: number): number {
var preRound = js / 1000; logger.trace(`jsUnix: Converting time: ${js}`);
return Math.round(preRound); return Math.floor(js / 1000);
} }
export { jsUnix, unixLocal };
module.exports = { module.exports = {
unixLocal, unixLocal,
jsUnix, jsUnix,

View File

@ -8,6 +8,8 @@ import type {
import { tz } from "moment-timezone"; import { tz } from "moment-timezone";
import { removeNewlineAndPTag } from "../../newSanitizer"; import { removeNewlineAndPTag } from "../../newSanitizer";
import { logger } from "../../logger.utils";
/// I do not yet have a type defined for any of the input object /// I do not yet have a type defined for any of the input object
export function transform(input: any): StaffLdb | null { export function transform(input: any): StaffLdb | null {
console.time("StaffLdb Transformation"); console.time("StaffLdb Transformation");
@ -24,20 +26,26 @@ export function transform(input: any): StaffLdb | null {
ferryServices: transformTrainServices(data?.ferryServices) || undefined, ferryServices: transformTrainServices(data?.ferryServices) || undefined,
}; };
console.timeEnd("StaffLdb Transformation"); console.timeEnd("StaffLdb Transformation");
return output; if (output.locationName !== "Not Found") {
return output;
}
} catch (err) { } catch (err) {
console.log("utils/translators/ldb/staffLdb.transform: Caught Error"); logger.error(err, "utils/translators/ldb/staffLdb.transform");
console.log("Unable to parse data, assuming no data: " + err);
} }
console.timeEnd("StaffLdb Transformation"); console.timeEnd("StaffLdb Transformation");
return null; return null;
} }
function transformDateTime(input: string): Date { function transformDateTime(input: string): Date {
logger.trace("utils/translators/ldb/staffLdb.transformDateTime: Running");
return new Date(input); return new Date(input);
} }
function transformNrcc(input: any): NrccMessage[] | undefined { function transformNrcc(input: any): NrccMessage[] | undefined {
logger.trace("utils/translators/ldb/staffLdb.transformNrcc: Running");
if (input === undefined) {
return input;
}
let output: NrccMessage[] = []; let output: NrccMessage[] = [];
let messages = input; let messages = input;
if (!Array.isArray(input?.message)) { if (!Array.isArray(input?.message)) {
@ -57,6 +65,9 @@ function transformNrcc(input: any): NrccMessage[] | undefined {
} }
function transformTrainServices(input: any): TrainServices[] { function transformTrainServices(input: any): TrainServices[] {
logger.trace(
"utils/translators/ldb/staffLdb.transformTrainServices: Running"
);
let services: any = input?.service; let services: any = input?.service;
let output: TrainServices[] = []; let output: TrainServices[] = [];
if (services === undefined) { if (services === undefined) {
@ -66,6 +77,7 @@ function transformTrainServices(input: any): TrainServices[] {
services = [input.service]; services = [input.service];
} }
for (const service of services) { for (const service of services) {
const times = parseTimes(service);
const trainService: TrainServices = { const trainService: TrainServices = {
rid: service?.rid, rid: service?.rid,
uid: service?.uid, uid: service?.uid,
@ -73,7 +85,7 @@ function transformTrainServices(input: any): TrainServices[] {
operatorCode: service?.operatorCode || "UK", operatorCode: service?.operatorCode || "UK",
platform: service?.platform || "-", platform: service?.platform || "-",
platformIsHidden: service?.platformIsHidden, platformIsHidden: service?.platformIsHidden,
serviceIsSupressed: service?.serviceIsSupressed, serviceIsSupressed: checkIsSupressed(service),
origin: transformLocation(service?.origin), origin: transformLocation(service?.origin),
destination: transformLocation(service?.destination), destination: transformLocation(service?.destination),
length: calculateLength(service), length: calculateLength(service),
@ -82,12 +94,12 @@ function transformTrainServices(input: any): TrainServices[] {
delayReason: service?.delayReason, delayReason: service?.delayReason,
arrivalType: service?.arrivalType, arrivalType: service?.arrivalType,
departureType: service?.departureType, departureType: service?.departureType,
sta: transformUnspecifiedDateTime(service?.sta), sta: times.sta,
eta: transformUnspecifiedDateTime(service?.eta), eta: times.eta,
ata: transformUnspecifiedDateTime(service?.ata), ata: times.ata,
std: transformUnspecifiedDateTime(service?.std), std: times.std,
etd: transformUnspecifiedDateTime(service?.etd), etd: times.etd,
atd: transformUnspecifiedDateTime(service?.atd), atd: times.atd,
}; };
Object.keys(trainService).forEach( Object.keys(trainService).forEach(
(key) => trainService[key] === undefined && delete trainService[key] (key) => trainService[key] === undefined && delete trainService[key]
@ -97,7 +109,17 @@ function transformTrainServices(input: any): TrainServices[] {
return output; return output;
} }
function checkIsSupressed(service: TrainServices): string | undefined {
logger.trace("utils/translators/ldb/staffStation.checkIsSupressed: Running");
if (service.serviceIsSupressed === "true" || service.isPassengerService === "false") {
return "true";
} else {
return undefined;
}
}
function transformLocation(input: any): ServiceLocation[] { function transformLocation(input: any): ServiceLocation[] {
logger.trace("utils/translators/ldb/staffStation.transformLocation: Running");
let output: ServiceLocation[] = []; let output: ServiceLocation[] = [];
let locations: any[] = input.location; let locations: any[] = input.location;
if (!Array.isArray(input.location)) { if (!Array.isArray(input.location)) {
@ -116,6 +138,7 @@ function transformLocation(input: any): ServiceLocation[] {
} }
export function calculateLength(input: any): number | undefined { export function calculateLength(input: any): number | undefined {
logger.trace("utils/translators/ldb/staffStation.calculateLength: Running");
let length: number; let length: number;
if (input?.length) { if (input?.length) {
length = input.length; length = input.length;
@ -129,9 +152,66 @@ export function calculateLength(input: any): number | undefined {
} }
function transformUnspecifiedDateTime(input: string): Date | undefined { function transformUnspecifiedDateTime(input: string): Date | undefined {
logger.trace(
"utils/translators/ldb/staffStation.transformUnspecifiedDateTime: Running"
);
if (!input) { if (!input) {
return undefined; return undefined;
} }
const date = tz(input, "Europe/London"); const date = tz(input, "Europe/London"); // Want to be creating a moment object using moment.tz(...)
return date.toDate(); return date.toDate();
} }
function parseTimes(service: TrainServices) {
logger.trace("utils/translators/ldb/staffStation.parseTimes: Running");
let { sta, eta, ata, std, etd, atd } = Object.fromEntries(
Object.entries(service).map(([key, value]) => [
key,
transformUnspecifiedDateTime(value),
])
);
let etaResult: Date | undefined | string = eta;
let ataResult: Date | undefined | string = ata;
let etdResult: Date | undefined | string = etd;
let atdResult: Date | undefined | string = atd;
if (sta) {
if (
eta !== undefined &&
Math.abs(eta.getTime() - sta.getTime()) / 60000 <= 1.5
) {
etaResult = "RT";
}
if (
ata !== undefined &&
Math.abs(ata.getTime() - sta.getTime()) / 60000 <= 1.5
) {
ataResult = "RT";
}
}
if (std) {
if (
etd !== undefined &&
Math.abs(etd.getTime() - std.getTime()) / 60000 <= 1.5
) {
etdResult = "RT";
}
if (
atd !== undefined &&
Math.abs(atd.getTime() - std.getTime()) / 60000 <= 1.5
) {
atdResult = "RT";
}
}
return {
sta: sta,
eta: etaResult,
ata: ataResult,
std: std,
etd: etdResult,
atd: atdResult,
};
}

View File

@ -1,5 +1,7 @@
import { getDomainFromEmail } from "../../src/utils/sanitizer.utils"; import { getDomainFromEmail } from "../../src/utils/sanitizer.utils";
import { removeNonNumeric } from "../../src/utils/sanitizer.utils"; import { removeNonNumeric } from "../../src/utils/sanitizer.utils";
import { removeNonAlpha } from "../../src/utils/sanitizer.utils";
import { removeNonAlphanumeric } from "../../src/utils/sanitizer.utils";
describe("Sanitize Email", () => { describe("Sanitize Email", () => {
const inputs = [ const inputs = [
@ -9,6 +11,7 @@ describe("Sanitize Email", () => {
"I%Have{Special}%Characters@example.com", "I%Have{Special}%Characters@example.com",
"Basic.address@example.com", "Basic.address@example.com",
`"very.(),:;<>[]\".VERY.\"very\ \"very\".unusual"@example.com`, `"very.(),:;<>[]\".VERY.\"very\ \"very\".unusual"@example.com`,
"THIS_EMAIL_CONTAINS_CAPITALISED_DOMAIN@EXAMPLE.COM",
]; ];
const expectedOutput = "example.com"; const expectedOutput = "example.com";
for (const addr of inputs) { for (const addr of inputs) {
@ -29,3 +32,31 @@ describe("Remove non-numeric", () => {
}); });
} }
}); });
describe("Remove non-Alpha", () => {
const inputs = ["DROP/*comment*/sampletable", "10; DROP TABLE members /*"];
const outputs = ["DROPcommentsampletable", "DROPTABLEmembers"];
for (const key in inputs) {
const input = inputs[key];
const desired = outputs[key];
test(`Should return with only letters: ${key}`, () => {
expect(removeNonAlpha(input)).toEqual(desired);
});
}
});
describe("Remove non-alphanumeric", () => {
const inputs = [
"DROP/*comment*/sampletable",
"10; DROP TABLE members /*",
"1F44",
];
const outputs = ["DROPcommentsampletable", "10DROPTABLEmembers", "1F44"];
for (const key in inputs) {
const input = inputs[key];
const desired = outputs[key];
test(`Should return with only alphanumeric: ${key}`, () => {
expect(removeNonAlphanumeric(input)).toEqual(desired);
});
}
});

View File

@ -0,0 +1,17 @@
import { jsUnix, unixLocal } from "../../src/utils/timeConvert.utils";
describe("Time Conversion", () => {
test("Should return unix time (seconds)", () => {
const now = new Date();
const nowJs = now.getTime();
const nowUnix = Math.floor(now.getTime() / 1000);
expect(jsUnix(nowJs)).toEqual(nowUnix);
});
test("Should return locale date string", () => {
const now = new Date();
const nowUnix = Math.floor(now.getTime() / 1000);
const result = now.toLocaleString();
expect(unixLocal(nowUnix)).toEqual(result);
});
});

View File

@ -53,4 +53,51 @@ export const inputs: any[] = [
}, },
}, },
}, },
{
GetBoardResult: {
generatedAt: "2023-08-01T20:37:05.559123+01:00",
locationName: "Railway Station",
crs: "RLY",
stationManager: "Network Rail",
stationManagerCode: "RT",
isTruncated: "true",
trainServices: {
service: [
{
rid: "202308017159276",
uid: "G59276",
trainid: "1M83",
sdd: "2023-08-01",
operator: "CrossCountry",
operatorCode: "XC",
sta: "2023-08-01T20:24:00",
ata: "2023-08-01T20:27:22",
arrivalType: "Actual",
std: "2023-08-01T20:35:00",
etd: "2023-08-01T20:35:00",
departureType: "Estimated",
departureSource: "Darwin",
platform: "5",
length: "10",
origin: {
location: {
locationName: "Plymouth",
crs: "PLY",
tiploc: "PLYMTH",
},
},
destination: {
location: {
locationName: "Birmingham New Street",
crs: "BHM",
tiploc: "BHAMNWS",
},
},
category: "XX",
activities: "T",
},
],
},
},
},
]; ];

View File

@ -26,7 +26,40 @@ export const outputs: StaffLdb[] = [
ata: expect.any(Date), ata: expect.any(Date),
arrivalType: "Actual", arrivalType: "Actual",
std: expect.any(Date), std: expect.any(Date),
etd: expect.any(Date), etd: "RT",
departureType: "Estimated",
platform: "5",
length: 10,
origin: [
{
tiploc: "PLYMTH",
},
],
destination: [
{
tiploc: "BHAMNWS",
},
],
},
],
busServices: [],
ferryServices: [],
},
{
generatedAt: expect.any(Date),
locationName: "Railway Station",
stationManagerCode: "RT",
trainServices: [
{
rid: "202308017159276",
uid: "G59276",
trainid: "1M83",
operatorCode: "XC",
sta: expect.any(Date),
ata: expect.any(Date),
arrivalType: "Actual",
std: expect.any(Date),
etd: "RT",
departureType: "Estimated", departureType: "Estimated",
platform: "5", platform: "5",
length: 10, length: 10,

View File

@ -25,7 +25,7 @@
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */ /* Modules */
"module": "commonjs" /* Specify what module code is generated. */, "module": "Node16" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */ // "rootDir": "./", /* Specify the root folder within your source files. */
"moduleResolution": "node16" /* Specify how TypeScript looks up a file from a given module specifier. */, "moduleResolution": "node16" /* Specify how TypeScript looks up a file from a given module specifier. */,
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
@ -52,11 +52,11 @@
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */ "sourceMap": true /* Create source map files for emitted JavaScript files. */,
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./dist" /* Specify an output folder for all emitted files. */, "outDir": "./dist" /* Specify an output folder for all emitted files. */,
// "removeComments": true, /* Disable emitting comments. */ "removeComments": true /* Disable emitting comments. */,
// "noEmit": true, /* Disable emitting files from a compilation. */ // "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */