Reviewed-on: #12
This commit is contained in:
Fred Boniface
2023-05-06 21:54:49 +01:00
parent fe9eeae7db
commit 7da713526f
46 changed files with 3166 additions and 2622 deletions

View File

@@ -1,10 +1,36 @@
module.exports = valid
const valid = [
"owlboard.info",
"fjla.uk",
"gwr.com",
"swrailway.com",
"firstrail.com",
"networkrail.co.uk"
]
'owlboard.info',
'avantiwestcoast.co.uk',
'btp.police.uk',
'c2crail.net',
'chilternrailways.co.uk',
'crosscountrytrains.co.uk',
'eastmidlandsrailway.co.uk',
'abellio.co.uk',
'tfl.gov.uk',
'mtrel.co.uk',
'eurostar.com',
'eurotunnel.com',
'ffwhr.com',
'gwr.com',
'hitachirail-eu.com',
'greateranglia.co.uk',
'heathrow.com',
'swrailway.com',
'lsltoc.co.uk',
'lner.co.uk',
'arrivarl.co.uk',
'tube.tfl.gov.uk',
'lumo.co.uk',
'merseyrail.org',
'nrcommcentre.com',
'networkrail.co.uk',
'northernrailway.co.uk',
'scotrail.co.uk',
'southeasternrailway.co.uk',
'tpeexpress.co.uk',
'tfwrail.wales',
'wmtrains.co.uk',
];
module.exports = valid;

View File

@@ -0,0 +1,12 @@
const statusCodes = {
700: 'no authentication attempt',
701: 'invalid credentials',
702: 'domain not whitelisted',
703: 'registration request not found, maybe expired',
800: 'location code not found',
801: 'unable to fetch location data',
900: 'invalid request format',
950: 'upstream server error',
};
module.exports = statusCodes;

View File

@@ -1,6 +1,6 @@
const version = {
api: ["/api/v1/",],
app: "1.1.5"
api: ['/api/v1/',],
app: '2.0.0'
};
module.exports = version;

View File

@@ -1,58 +1,58 @@
const find = require('../services/find.services');
async function findName(req, res, next){
try {
var id = req.params.id
res.json(await find.name(id))
} catch (err) {
console.error(`Unknown Error`, err.message);
next(err);
}
try {
var id = req.params.id;
res.json(await find.name(id));
} catch (err) {
console.error('Unknown Error', err.message);
next(err);
}
}
async function findCrs(req, res, next){
try {
var id = req.params.id
res.json(await find.crs(id))
} catch (err) {
console.error(`Unknown Error`, err.message);
next(err);
}
try {
var id = req.params.id;
res.json(await find.crs(id));
} catch (err) {
console.error('Unknown Error', err.message);
next(err);
}
}
async function findNlc(req, res, next){
try {
var id = req.params.id
res.json(await find.nlc(id))
} catch (err) {
console.error(`Unknown Error`, err.message);
next(err);
}
try {
var id = req.params.id;
res.json(await find.nlc(id));
} catch (err) {
console.error('Unknown Error', err.message);
next(err);
}
}
async function findTiploc(req, res, next){
try {
var id = req.params.id
res.json(await find.tiploc(id))
} catch (err) {
console.error(`Unknown Error`, err.message);
next(err);
}
try {
var id = req.params.id;
res.json(await find.tiploc(id));
} catch (err) {
console.error('Unknown Error', err.message);
next(err);
}
}
async function findStanox(req, res, next){
try {
var id = req.params.id
res.json(await find.stanox(id))
} catch (err) {
console.error(`Unknown Error`, err.message);
next(err);
}
try {
var id = req.params.id;
res.json(await find.stanox(id));
} catch (err) {
console.error('Unknown Error', err.message);
next(err);
}
}
module.exports = {
findName,
findCrs,
findNlc,
findTiploc,
findStanox
}
findName,
findCrs,
findNlc,
findTiploc,
findStanox
};

View File

@@ -1,14 +1,14 @@
const issue = require('../services/issue.services');
async function post(req, res, next){
try {
res.json(await issue.processor(req.body))
} catch (err) {
console.error(`Controller Error`, err.message);
next(err);
}
try {
res.json(await issue.processor(req.body));
} catch (err) {
console.error('Controller Error', err.message);
next(err);
}
}
module.exports = {
post
}
post
};

View File

@@ -1,34 +1,35 @@
const kube = require('../services/kube.services');
async function getAlive(req, res, next){
try {
var state = kube.getAlive()
res.status((await state).code).send((await state).state)
} catch (err) {
res.status("503").send({state: "error"})
}
try {
var state = kube.getAlive();
res.status((await state).code).send((await state).state);
} catch (err) {
res.status('503').send({state: 'error'});
}
}
async function getReady(req, res, next){
try {
res.json(await kube.getReady(req.body))
} catch (err) {
console.error(`Unknown Error`, err.message);
next(err);
}
try {
res.json(await kube.getReady(req.body));
} catch (err) {
console.error('Unknown Error', err.message);
next(err);
}
}
async function getTime(req, res, next){
try {
res.json(await kube.getTime(req.body))
} catch (err) {
console.error(`Unknown Error`, err.message);
next(err);
}
try {
res.json(await kube.getTime(req.body));
} catch (err) {
console.error('Unknown Error', err.message);
err.status = 503;
next(err);
}
}
module.exports = {
getAlive,
getReady,
getTime
}
getAlive,
getReady,
getTime
};

View File

@@ -1,15 +1,16 @@
const ldb = require('../services/ldb.services');
async function get(req, res, next){
try {
var id = req.params.id
res.json(await ldb.get(req.body, id))
} catch (err) {
console.error(`Unknown Error`, err.message);
next(err);
}
try {
var id = req.params.id;
res.json(await ldb.get(id));
} catch (err) {
console.error('Unknown Error', err.message);
err.status = 500;
next(err);
}
}
module.exports = {
get
}
get
};

View File

@@ -0,0 +1,16 @@
const ldb = require('../services/ldb.services');
async function get(req, res, next){
try {
var id = req.params.id;
res.json(await ldb.get(id, true));
} catch (err) {
console.error('Unknown Error', err.message);
err.status = 500;
next(err);
}
}
module.exports = {
get
};

View File

@@ -1,34 +1,37 @@
const list = require('../services/list.services');
async function getStations(req, res, next){
try {
res.json(await list.getStations(req.body))
} catch (err) {
console.error(`Controller Error`, err.message);
next(err);
}
try {
res.json(await list.getStations(req.body));
} catch (err) {
console.error('Controller Error', err.message);
err.status = 500;
next(err);
}
}
async function getCorpus(req, res, next){
try {
res.json(await list.getCorpus(req.body))
} catch (err) {
console.error(`Controller Error`, err.message);
next(err);
}
try {
res.json(await list.getCorpus(req.body));
} catch (err) {
console.error('Controller Error', err.message);
err.status = 500;
next(err);
}
}
async function hits(req, res, next) {
try {
res.json(await list.hits())
} catch (err) {
console.error(`Controller Error`, err);
next(err);
}
try {
res.json(await list.hits());
} catch (err) {
console.error('Controller Error', err);
err.status = 500;
next(err);
}
}
module.exports = {
getStations,
getCorpus,
hits
}
getStations,
getCorpus,
hits
};

View File

@@ -0,0 +1,17 @@
const pis = require('../services/pis.services');
async function byOrigDest(req, res, next){
try {
let start = req.params.start;
let end = req.params.end;
res.json(await pis.findPisByOrigDest(start,end));
} catch (err) {
console.error('Unknown Error', err.message);
next(err);
}
}
module.exports = {
byOrigDest
};

View File

@@ -0,0 +1,26 @@
const reg = require('../services/registration.services');
async function register(req, res, next) {
try {
let response = await reg.regUser(req.body);
res.status(response.status).json(response);
} catch (err) {
console.error('Controller Error', err.message);
next(err);
}
}
async function request(req, res, next) {
try {
let response = await reg.createRegKey(req.body);
res.status(response.status).json(response);
} catch (err) {
console.error(err);
next(err);
}
}
module.exports = {
register,
request
};

View File

@@ -1,13 +1,15 @@
const stat = require('../services/stats.services');
async function get(req, res, next) {
try {
res.json(await stat.hits())
} catch (err) {
console.error(`Controller Error`, err);
next(err);
}
try {
res.json(await stat.hits());
} catch (err) {
console.error('Controller Error', err);
err.status = 500;
next(err);
}
}
module.exports = {
get}
get
};

View File

@@ -1,17 +0,0 @@
<html lang="en" style="background-color: grey; width: 100%;">
<head>
<title>OwlBoard - Register</title>
</head>
<body>
<table width="100%">
<tr>
<td>
<img src="https://owlboard.info/images/logo/wide_logo.svg" style="height: 100px">
</td>
<td>
<h1>Register for OwlBoard</h1>
</td>
</tr>
</table>
</body>
</html>

View File

@@ -0,0 +1,27 @@
const utils = require('../utils/auth.utils');
const log = require('../utils/log.utils');
module.exports = async function authCheck(req, res, next) {
log.out('authMiddlewares: Checking authentication', 'dbug');
try {
var uuid = req.headers.uuid;
} catch(err) {
log.out('authMiddlewares: No authentication attempted', 'dbug');
err.status = 401;
return next(err);
}
try {
var result = await utils.isAuthed(uuid) || false;
if (!result) {
const err = new Error('Unauthorised');
err.status = 401;
log.out('authMiddlewares: Authentication attempted with incorrect key', 'warn');
return next(err);
} else {
log.out('authMiddlewares: User authenticated', 'dbug');
return next();
}
} catch(err) {
return next(err);
}
};

View File

@@ -0,0 +1,12 @@
const log = require('../utils/log.utils');
module.exports = async function requireJson(req, res, next) {
if (req.headers['content-type'] !== 'application/json') {
log.out('requireJson.middlewares: Bad Request: Not in JSON format');
res.status(400).send({status: 400, message: 'Server requires JSON'});
} else {
next();
}
};
// Possibly want to check the req type?

View File

@@ -6,4 +6,4 @@ router.get('/alive', kubeController.getAlive);
router.get('/ready', kubeController.getReady);
router.get('/time', kubeController.getTime);
module.exports = router
module.exports = router;

19
src/routes/ldbs.routes.js Normal file
View File

@@ -0,0 +1,19 @@
const express = require('express');
const router = express.Router();
const ldbsController = require('../controllers/ldbs.controllers');
/* GET programming languages. */
//router.get('/', programmingLanguagesController.get);
/* POST programming language */
//router.post('/', programmingLanguagesController.create);
/* PUT programming language */
//router.put('/:id', programmingLanguagesController.update);
/* DELETE programming language */
//router.delete('/:id', programmingLanguagesController.remove);
router.get('/:id', ldbsController.get);
module.exports = router;

7
src/routes/pis.routes.js Normal file
View File

@@ -0,0 +1,7 @@
const express = require('express');
const router = express.Router();
const pisController = require('../controllers/pis.controllers');
router.get('/origdest/:start/:end', pisController.byOrigDest);
module.exports = router;

View File

@@ -0,0 +1,8 @@
const express = require('express');
const router = express.Router();
const regController = require('../controllers/registration.controllers');
router.post('/request', regController.request);
router.post('/register', regController.register);
module.exports = router;

View File

@@ -1,10 +1,11 @@
const log = require('../utils/log.utils'); // Log Helper
/* global process */
const log = require('../utils/log.utils'); // Log Helper
const dbUser = process.env.OWL_DB_USER || "owl"
const dbPass = process.env.OWL_DB_PASS || "twittwoo"
const dbName = process.env.OWL_DB_NAME || "owlboard"
const dbPort = process.env.OWL_DB_PORT || 27017
const dbHost = process.env.OWL_DB_HOST || "localhost"
const dbUser = process.env.OWL_DB_USER || 'owl';
const dbPass = process.env.OWL_DB_PASS || 'twittwoo';
const dbName = process.env.OWL_DB_NAME || 'owlboard';
const dbPort = process.env.OWL_DB_PORT || 27017;
const dbHost = process.env.OWL_DB_HOST || 'localhost';
const uri = `mongodb://${dbUser}:${dbPass}@${dbHost}:${dbPort}`;
const { MongoClient } = require('mongodb');
@@ -13,27 +14,70 @@ const client = new MongoClient(uri);
const db = client.db(dbName);
async function query(collection, query){
await client.connect();
log.out(`dbAccess.query: Connecting to collection: '${collection}'`, "info")
var qcoll = db.collection(collection);
var qcursor = qcoll.find(query)
qcursor.project({_id: 0})
log.out(`dbAccess.query: Running Query: ${JSON.stringify(query)}`, "info")
increment(collection)
return (await qcursor.toArray());
await client.connect();
log.out(`dbAccess.query: Connecting to collection: '${collection}'`, 'info');
var qcoll = db.collection(collection);
var qcursor = qcoll.find(query);
qcursor.project({_id: 0});
log.out(`dbAccess.query: Running Query: ${JSON.stringify(query)}`, 'info');
increment(collection);
return (await qcursor.toArray());
}
async function increment(target) {
log.out(`dbAccess.increment: Incrementing counter for: ${target}`, "info")
await client.connect();
let col = db.collection("meta");
let update = {}
update[target] = 1
col.updateOne({target: "counters"}, {$inc:update})
return;
log.out(`dbAccess.increment: Incrementing counter for: ${target}`, 'info');
await client.connect();
let col = db.collection('meta');
let update = {};
update[target] = 1;
col.updateOne({target: 'counters'}, {$inc:update});
}
async function addUser(uuid, domain) { // Needs testing
log.out('dbAccess.addUser: Adding user to database');
let doc = {uuid: uuid, domain: domain, atime: new Date};
await client.connect();
let col = db.collection('users');
let res = await col.insertOne(doc);
if (res.insertedId) {
return true;
}
return false;
}
async function addRegReq(uuid, domain) { // Needs testing
log.out('dbAccess.addRegReq: Adding registration request');
let doc = {uuid: uuid, time: new Date, domain: domain};
await client.connect();
let col = db.collection('registrations');
let res = col.insertOne(doc);
return res;
}
async function userAtime(uuid) { // Needs testing
log.out('dbAccess.userAtime: Updating access time for user');
let q = {uuid: uuid};
let n = {$set: {uuid: uuid, atime: new Date}};
await client.connect();
let col = db.collection('users');
let res = col.updateOne(q, n, {upsert: true});
return res;
}
// Deletes one single registration request entry from the DB
async function delRegReq(uuid) {
log.out('dbAccess.delRegReq: Deleting a Registration Request');
let collection = 'registrations';
await client.connect();
let col = db.collection(collection);
col.deleteOne({uuid: uuid});
}
module.exports = {
query,
increment
}
query,
increment, // Probqbly doesn't need exporting? - It does, ldbServices needs to increment when the API is hit!
addUser,
userAtime,
addRegReq,
delRegReq
};

View File

@@ -2,58 +2,58 @@
const log = require('../utils/log.utils'); // Log Helper
const db = require('../services/dbAccess.services');
const san = require('../utils/sanitizer.utils')
const san = require('../utils/sanitizer.utils');
// DB Query: query(collection, query)
// Define collection as all queries are for the "corpus" collection.
const col = "corpus"
const col = 'corpus';
async function name(id){
log.out(`findServices.name: Finding station name: ${id}`, "info")
var name = san.cleanApiEndpointTxt(id.toUpperCase())
query = {NLCDESC: name}
//var data = await db.query(col,query)
return await db.query(col,query)
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())
query = {'3ALPHA': crs}
//var data = await db.query(col,query)
return await db.query(col,query)
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)
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)
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())
query = {TIPLOC: tiploc}
//var data = await db.query(col,query)
return await db.query(col,query)
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)
query = {STANOX: String(stanox)}
//var data = await db.query(col,query)
return await db.query(col,query)
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
}
name,
crs,
nlc,
tiploc,
stanox
};

View File

@@ -1,33 +1,35 @@
const axios = require('axios')
const log = require('../utils/log.utils')
/* eslint-disable no-useless-escape */
/* global process */
const axios = require('axios');
const log = require('../utils/log.utils');
async function processor(data) {
log.out(`issueService.processor: Issue received`, "info")
let out = {}
out.title = data.subject.replace(/<[^>]+>|[\*\$]/g, '');
out.body = data.msg.replace(/<[^>]+>|[\*\$]/g, '')
return await sendToGitea(out);
log.out('issueService.processor: Issue received', 'info');
let out = {};
out.title = data.subject.replace(/<[^>]+>|[\*\$]/g, '');
out.body = data.msg.replace(/<[^>]+>|[\*\$]/g, '');
return await sendToGitea(out);
}
async function sendToGitea(body) {
let key = process.env.OWL_GIT_ISSUEBOT
let url = process.env.OWL_GIT_APIENDPOINT
let opts = {
headers: {
Authorization: key
}
}
var res = await axios.post(url, body, opts)
// Need to read the output from the POST and pass the result upwards to the client.
if (res.status == 201) {
log.out("issueService.sendToGitea: Issue sent to Gitea", "info")
return {status: res.status,message:"issue created"}
} else {
log.out(`issueService.sendToGitea: Failed to send issue to Gitea: ${res.body}`, "err")
return {status: res.status,message:"issue not created"}
let key = process.env.OWL_GIT_ISSUEBOT;
let url = process.env.OWL_GIT_APIENDPOINT;
let opts = {
headers: {
Authorization: key
}
};
var res = await axios.post(url, body, opts);
// Need to read the output from the POST and pass the result upwards to the client.
if (res.status == 201) {
log.out('issueService.sendToGitea: Issue sent to Gitea', 'info');
return {status: res.status,message:'issue created'};
} else {
log.out(`issueService.sendToGitea: Failed to send issue to Gitea: ${res.body}`, 'err');
return {status: res.status,message:'issue not created'};
}
}
module.exports = {
processor
}
processor
};

View File

@@ -1,20 +1,28 @@
const testing = require('../services/mail.services');
const log = require('../utils/log.utils');
async function getAlive(){
log.out(`kubeServices.getAlive: alive hook checked`, "info")
return {code: 200, state: {state: "alive",noise: "twit-twoo"}}
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")
return "not_implemented";
};
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}
var now = new Date();
return {responseGenerated: now};
}
module.exports = {
getAlive,
getReady,
getTime
}
getAlive,
getReady,
getTime
};

View File

@@ -1,46 +1,67 @@
/* global process */
// Parse and return an LDB Request
const log = require('../utils/log.utils'); // Log Helper
const ldb = require('ldbs-json')
const util = require('../utils/ldb.utils')
const san = require('../utils/sanitizer.utils')
const db = require('../services/dbAccess.services')
const ldb = require('ldbs-json');
const util = require('../utils/ldb.utils');
const san = require('../utils/sanitizer.utils');
const db = require('../services/dbAccess.services');
const ldbKey = process.env.OWL_LDB_KEY
const ldbsvKey = process.env.OWL_LDB_SVKEY
const ldbKey = process.env.OWL_LDB_KEY;
const ldbsvKey = process.env.OWL_LDB_SVKEY;
async function get(body, id){
var cleanId = san.cleanApiEndpointTxt(id);
var obj = await util.checkCrs(cleanId);
try {
var crs = obj[0]['3ALPHA'];
log.out(`ldbService.get: Determined CRS for lookup to be: ${crs}`, "info");
var data = arrDepBoard(crs);
db.increment("ldbws");
await data;
} catch (err) {
log.out(`ldbService.get: Error, Unable to find CRS: ${err}`, "info")
var data = {ERROR:'NOT_FOUND',description:'The entered station was not found. Please check and try again.'};
async function get(id, staff=false){
const cleanId = san.cleanApiEndpointTxt(id);
const obj = await util.checkCrs(cleanId);
try {
const crs = obj[0]['3ALPHA'];
log.out(`ldbService.get: Determined CRS for lookup to be: ${crs}`, 'info');
if (staff) {
const data = arrDepBoardStaff(crs);
db.increment('ldbsvws');
return await data;
} else {
const data = arrDepBoard(crs);
db.increment('ldbws');
return await data;
}
return data;
} catch (err) {
log.out(`ldbService.get: Error, Unable to find CRS: ${err}`, 'info');
return {ERROR:'NOT_FOUND',description:'The entered station was not found. Please check and try again.'};
}
}
async function arrDepBoard(CRS){
log.out(`ldbService.arrDepBoard: Trying to fetch ArrDep Board for ${CRS}`, "info")
try {
var options = {
numRows: 10,
crs: CRS.toUpperCase()
}
var api = new ldb(ldbKey,false)
var reply = api.call("GetArrDepBoardWithDetails", options, false, false)
return await reply
} catch (err) {
log.out(`ldbService.arrDepBoard: Lookup Failed for: ${CRS}`, "warn")
return {GetStationBoardResult: "not available", Reason: `The CRS code ${CRS} is not valid`, Why: `Sometimes a station will have more than one CRS - for example Filton Abbey Wood has FIT and FAW however schedules are only available when looking up with FIT - this is how the National Rail Enquiries systems work.`};
}
};
log.out(`ldbService.arrDepBoard: Trying to fetch ArrDep Board for ${CRS}`, 'info');
try {
const options = {
numRows: 10,
crs: CRS.toUpperCase()
};
const api = new ldb(ldbKey,false);
return await api.call('GetArrDepBoardWithDetails', options, false, false);
} catch (err) {
log.out(`ldbService.arrDepBoard: Lookup Failed for: ${CRS}`, 'warn');
return {GetStationBoardResult: 'not available', Reason: `The CRS code ${CRS} is not valid`, Why: 'Sometimes a station will have more than one CRS - for example Filton Abbey Wood has FIT and FAW however schedules are only available when looking up with FIT - this is how the National Rail Enquiries systems work.'};
}
}
async function arrDepBoardStaff(CRS) {
log.out(`ldbService.arrDepBoardStaff: Trying to fetch ArrDep Board for ${CRS}`, 'dbug');
try {
const options = {
numRows: 25,
crs: CRS.toUpperCase(),
getNonPassengerServices: true
};
const api = new ldb(ldbsvKey,true);
return await api.call('GetArrDepBoardWithDetails', options, false, false);
} catch (err) {
log.out(`ldbService.arrDepBoardStaff: Lookup Failed for: ${CRS}, "warn`);
return {GetStationBoardResult: 'not available', Reason: `The CRS code ${CRS} is not valid`, Why: 'Sometimes a station will have more than one CRS - for example Filton Abbey Wood has FIT and FAW however schedules are only available when looking up with FIT - this is how the National Rail Enquiries systems work.'};
}
}
module.exports = {
get
}
get
};

View File

@@ -1,20 +1,19 @@
const log = require('../utils/log.utils'); // Log Helper
const db = require('../services/dbAccess.services')
const os = require('os')
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;
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;
var out = db.query('corpus');
log.out('listServices.getCorpus: Fetching CORPUS list', 'info');
return await out;
}
module.exports = {
getStations,
getCorpus
}
getStations,
getCorpus
};

View File

@@ -1,7 +1,36 @@
const log = require('../utils/log.utils')
const mail = require('some-mail-module')
/* global process */
const log = require('../utils/log.utils');
const mail = require('nodemailer'); //>> Probs wrong
async function sendTest() {
// Send test mail message
return;
}
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
}
});
async function send(message){ // message is an object containing strings for: *to, cc, bcc, *subject, *txt, html (* denotes required)
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,34 @@
// Finds PIS Codes using DB Lookups
const db = require('../services/dbAccess.services');
const log = require('../utils/log.utils');
const clean = require('../utils/sanitizer.utils');
async function findPisByOrigDest(start,end) {
log.out(`pisServices.findPisByOrigDest: Searching for PIS for Orig: ${start}, Dest: ${end}`, 'dbug');
const firstCrs = clean.cleanApiEndpointTxt(start.toLowerCase());
const lastCrs = clean.cleanApiEndpointTxt(end.toLowerCase());
const query = {
stops: {
$all: [
{ $elemMatch: { $eq: firstCrs } },
{ $elemMatch: { $eq: lastCrs } }
]
},
$expr: {
$and: [
{ $eq: [{ $arrayElemAt: [ '$stops', -1 ] }, lastCrs] },
{ $eq: [{ $arrayElemAt: [ '$stops', 0 ] }, firstCrs] }
]
}
};
//const oldQuery = {$and:[{$expr:{$eq:[{$first:"$stops"},firstCrs]}},{$expr:{$eq:[{$last:"$stops"},lastCrs]}}]}
const search = db.query('pis', query);
return search;
}
// Hopefully at some point, I will also be able to implement a find PIS code by headcode option.
module.exports = {
findPisByOrigDest
};

View File

@@ -0,0 +1,51 @@
const log = require('../utils/log.utils');
const auth = require('../utils/auth.utils');
const db = require('./dbAccess.services');
const mail = require('./mail.services');
const clean = require('../utils/sanitizer.utils');
const domains = require('../configs/domains.configs');
const errors = require('../configs/errorCodes.configs');
async function createRegKey(body) {
log.out('registerServices.createRegKey: Incoming request', 'INFO');
const domain = await clean.getDomainFromEmail(body.email); // The function should validate the email
log.out(`registerServices: New registration request from domain: ${domain}`, 'info');
if (domains.includes(domain)) {
log.out(`registerServices.createRegKey: Key from valid domain: ${domain}`);
const uuid = await auth.generateKey();
db.addRegReq(uuid, domain);
const message = await auth.generateConfirmationEmail(body.email, uuid);
if (!message) {
const err = new Error('Message generation error');
log.out('registerServices.createRegKey: Error generating registration email', 'err');
log.out(err, 'err');
return 500;
}
if (await mail.send(message) == true) {
return {status: 201, message: 'email sent'};
}
return {status: 500, errorCode: 950, errorMsg: errors[950]};
}
return {status: 403, errorCode: 702, errorMsg: errors[702]};
}
async function regUser(req) { // Add input validation
log.out(`Read UUID: ${req.uuid}`, 'dbug');
log.out(`registrationServices.regUser: Checking validity of : ${req.uuid}`, 'info');
const res = await auth.checkRequest(req.uuid);
log.out(`registrationServices.regUser: checkRequest returned: ${JSON.stringify(res)}`, 'info');
if (res.result) {
const uuid = await auth.generateKey();
const apiKey = await db.addUser(uuid, res.domain);
if (apiKey) {
db.delRegReq(req.uuid);
return {status: 201, message: 'User added', api_key: uuid};
}
}
return {status: 401, errorCode: 703, errorMsg: errors[703]};
}
module.exports = {
regUser,
createRegKey
};

View File

@@ -1,24 +1,25 @@
const log = require('../utils/log.utils'); // Log Helper
const db = require('../services/dbAccess.services')
const os = require('os')
const vers = require('../configs/version.configs')
const db = require('../services/dbAccess.services');
const os = require('os');
const vers = require('../configs/version.configs');
async function hits(){
log.out("statsServices.hits: Statistics Requested", "info")
var dat = db.query("meta", {target: "counters"});
var ver = db.query("meta", {target: "versions"});
log.out(`statsServices.hits: fetched server meta`, "info")
let out = {};
out.host = os.hostname();
out.mode = process.env.NODE_ENV;
out.verBkend = vers.app;
out.verApi = vers.api;
out.dat = await dat;
out.ver = await ver;
log.out(`statsServices.hits: Sending Data: ${JSON.stringify(out)}`, "info")
return out;
log.out('statsServices.hits: Statistics Requested', 'info');
var dat = db.query('meta', {target: 'counters'});
var ver = db.query('meta', {target: 'versions'});
log.out('statsServices.hits: fetched server meta', 'info');
let out = {};
out.host = os.hostname();
// eslint-disable-next-line no-undef
out.mode = process.env.NODE_ENV;
out.verBkend = vers.app;
out.verApi = vers.api;
out.dat = await dat;
out.ver = await ver;
log.out(`statsServices.hits: Sending Data: ${JSON.stringify(out)}`, 'info');
return out;
}
module.exports = {
hits
}
hits
};

57
src/utils/auth.utils.js Normal file
View File

@@ -0,0 +1,57 @@
const log = require('../utils/log.utils');
const crypto = require('crypto');
const db = require('../services/dbAccess.services');
const fs = require('fs/promises');
const minify = require('../utils/minify.utils');
// Checks users registration key against issued keys
async function isAuthed(uuid) { // Needs testing
const q = {uuid: uuid};
const res = await db.query('users', q);
log.out(`authUtils.checkUser: DB Query answer: ${JSON.stringify(res[0])}`, 'dbug');
const authorized = res && res[0] && res[0].domain;
if (authorized) db.userAtime(uuid);
return authorized;
}
// Checks whether a registration request key is valid
async function checkRequest(key) {
const collection = 'registrations';
const query = {uuid: key};
const res = await db.query(collection, query);
log.out(`authUtils.checkRequest: DB Query result: ${JSON.stringify(res)}`, 'dbug');
const result = res.length > 0 && res[0].time
? { result: true, domain: res[0].domain }
: { result: false };
return result;
}
// Creates an API key for a user
async function generateKey() { // Needs testing & moving to 'register.utils'
return crypto.randomUUID();
}
async function generateConfirmationEmail(eml, uuid) {
try {
const htmlTpl = await fs.readFile('mail-templates/register.html', 'utf-8');
const htmlStr = htmlTpl.replace(/>>ACCESSCODE<</g, uuid);
const htmlMin = await minify(htmlStr);
const txtTpl = fs.readFile('mail-templates/register.txt', 'utf-8');
return {
to: eml,
subject: 'OwlBoard Registration',
text: (await txtTpl).replace(/>>ACCESSCODE<</g, uuid),
html: htmlMin
};
} catch(err) {
log.out('mailServices.generateConfirmationEmail: Error reading templates, $(err)', 'err');
return false;
}
}
module.exports = {
isAuthed,
generateKey,
generateConfirmationEmail,
checkRequest
};

View File

@@ -1,43 +1,49 @@
const log = require('../utils/log.utils'); // Log Helper
const db = require('../services/dbAccess.services') // DB Access
const san = require('../utils/sanitizer.utils') // Sanitiser
const db = require('../services/dbAccess.services'); // DB Access
const san = require('../utils/sanitizer.utils'); // Sanitiser
async function checkCrs(input){
var INPUT = input.toUpperCase()
log.out(`ldbUtils.checkCrs: Building database query to find: '${INPUT}'`, "info")
var query = {'$or':[{'3ALPHA':INPUT},{'TIPLOC':INPUT},{'STANOX':INPUT}]};
var result = await db.query("stations", query)
log.out(`ldbUtils.checkCrs: Query results: ${JSON.stringify(result)}`, "info")
return result
var INPUT = input.toUpperCase();
log.out(`ldbUtils.checkCrs: Building database query to find: '${INPUT}'`, 'info');
var query = {
'$or':[
{'3ALPHA':INPUT},
{'TIPLOC':INPUT},
{'STANOX':INPUT}
]
};
var result = await db.query('stations', query);
log.out(`ldbUtils.checkCrs: Query results: ${JSON.stringify(result)}`, 'dbug');
return result;
}
async function cleanMessages(input){ // Needs to be moved to the frontend `ensureArray() func`
var out = []
if (typeof input.message == "string") {
out.push(await san.cleanNrcc(input.message))
} else if (typeof input.message == "object") {
for(var i = 0; i < input.message.length; i++) {
out.push(await san.cleanNrcc(input.message[i]))
}
var out = [];
if (typeof input.message == 'string') {
out.push(await san.cleanNrcc(input.message));
} else if (typeof input.message == 'object') {
for(var i = 0; i < input.message.length; i++) {
out.push(await san.cleanNrcc(input.message[i]));
}
return out;
}
return out;
}
// Accepts an object but not an Array and returns it wrapped in an array.
async function cleanServices(input){ // Need to triple check but I don't think this is used anymore.
var out = []
if (!Array.isArray(input)) {
log.out(`ldbUtils.cleanServices: Transforming input: ${input}`, "depr")
out.push(input)
log.out(`ldbUtils.cleanServices: Returning output: ${out}`, "depr")
return out;
} else {
return input;
}
var out = [];
if (!Array.isArray(input)) {
log.out(`ldbUtils.cleanServices: Transforming input: ${input}`, 'depr');
out.push(input);
log.out(`ldbUtils.cleanServices: Returning output: ${out}`, 'depr');
return out;
} else {
return input;
}
}
module.exports = {
checkCrs,
cleanMessages,
cleanServices
}
checkCrs,
cleanMessages,
cleanServices
};

View File

@@ -1,14 +1,17 @@
/* global process */
const environment = process.env.NODE_ENV;
const hideInProduction = ['info', 'dbug'];
async function out(msg, level = 'othr') {
if (environment === "production" && level === "info") {
return;
} else {
const time = new Date().toISOString();
console.log(`${time} - ${level.toUpperCase()} - ${msg}`);
}
if (environment === 'production' && hideInProduction.includes(level.toLowerCase())) {
return;
} else {
const time = new Date().toISOString();
console.log(`${time} - ${level.toUpperCase()} - ${msg}`);
}
}
module.exports = {
out
}
out
};

10
src/utils/minify.utils.js Normal file
View File

@@ -0,0 +1,10 @@
const htmlShrink = require('html-minifier').minify;
const juice = require('juice');
module.exports = async function minifyMail(input) {
const inlined = juice(input);
return htmlShrink(inlined, {
removeComments: true,
collapseWhitespace: true
});
};

View File

@@ -1,45 +1,31 @@
const clean = require('string-sanitizer-fix');
const log = require('../utils/log.utils');
//const log = require('../utils/log.utils');
/*
string.sanitize("a.bc@d efg#h"); // abcdefgh
string.sanitize.keepSpace("a.bc@d efg#h"); // abcd efgh
string.sanitize.keepUnicode("a.bc@d efg#hক"); // abcd efghক
string.sanitize.addFullstop("a.bc@d efg#h"); // abcd.efgh
string.sanitize.addUnderscore("a.bc@d efg#h"); // abcd_efgh
string.sanitize.addDash("a.bc@d efg#h"); // abcd-efgh
string.sanitize.removeNumber("@abcd efgh123"); // abcdefgh
string.sanitize.keepNumber("@abcd efgh123"); // abcdefgh123
string.addFullstop("abcd efgh"); // abcd.efgh
string.addUnderscore("@abcd efgh"); // @abcd_efgh
string.addDash("@abcd efgh"); // @abcd-efgh
string.removeSpace("@abcd efgh"); // @abcdefgh
*/
function cleanApiEndpointTxt(input) {
var output = clean.sanitize.keepSpace(input)
if (output != input){
log.out(`sanitizerUtils.cleanApiEndpoint: WARN: Sanitizing changed string. Input = ${input}`, "warn");
}
return output
function removeNonAlphanumeric(inputString) { // Should be able to replace sanitizer module
return inputString.replace(/[^a-zA-Z0-9]/g, '');
}
function cleanApiEndpointNum(input) {
var output = clean.sanitize.keepNumber(input)
if (output != input){
log.out(`sanitizerUtils.cleanApiEndpointNum: WARN: Sanitizing changed string. Input = ${input}`, "warn");
}
return output
function removeNonAlpha(inputString) { // Should be able to replace sanitizer module
return inputString.replace(/[^a-zA-Z]/g, '');
}
function cleanNrcc(input) {
var rmNewline = input.replace(/[\n\r]/g, ""); // Remove newlines
var rmPara = rmNewline.replace(/<\/?p[^>]*>/g, ""); // Remove <p> & </p>
return rmPara;
const cleanApiEndpointTxt = removeNonAlpha;
const cleanApiEndpointNum = removeNonAlphanumeric;
function cleanNrcc(input) { // Remove newlines and then <p> tags from input
const cleanInput = input.replace(/[\n\r]/g, '').replace(/<\/?p[^>]*>/g, '');
return cleanInput;
}
async function getDomainFromEmail(mail) { // Needs testing
let split = mail.split('@');
return split[1];
}
module.exports = {
cleanApiEndpointTxt,
cleanApiEndpointNum,
cleanNrcc
}
cleanApiEndpointTxt,
cleanApiEndpointNum,
removeNonAlpha,
removeNonAlphanumeric,
cleanNrcc,
getDomainFromEmail,
};

View File

@@ -1,15 +1,15 @@
function unixLocal(unix) {
var jsTime = unix*1000
var dt = new Date(jsTime)
return dt.toLocaleString()
var jsTime = unix*1000;
var dt = new Date(jsTime);
return dt.toLocaleString();
}
function jsUnix(js) {
var preRound = js / 1000
return Math.round(preRound)
var preRound = js / 1000;
return Math.round(preRound);
}
module.exports = {
unixLocal,
jsUnix,
}
unixLocal,
jsUnix,
};

View File

@@ -1,27 +1,28 @@
/* global process */
// Checks that all required environment variables are present.
// Returns True or False and offers an object detailing what is missing.
async function varTest(){
var required = {
OWL_LDB_KEY: process.env.OWL_LDB_KEY,
OWL_LDB_CORPUSUSER: process.env.OWL_LDB_CORPUSUSER,
OWL_LDB_CORPUSPASS: process.env.OWL_LDB_CORPUSPASS,
OWL_NOT_USED: process.env.OWL_NOT_USED
}
var desired = {
OWL_DB_PASS: process.env.OWL_DB_PASS
}
// DO NOT LOG CREDENTIALS!!!
var required = {
OWL_LDB_KEY: process.env.OWL_LDB_KEY,
OWL_LDB_CORPUSUSER: process.env.OWL_LDB_CORPUSUSER,
OWL_LDB_CORPUSPASS: process.env.OWL_LDB_CORPUSPASS,
OWL_NOT_USED: process.env.OWL_NOT_USED
};
var desired = {
OWL_DB_PASS: process.env.OWL_DB_PASS
};
// DO NOT LOG CREDENTIALS!!!
// Test that each of required is NOT undefined.
// var pass = true if all okay, false if not.
// Append any missing values to missing_required = []
// Test that each of desired is NOT undefined.
// Append any missing values to missing_desired = []
// Test that each of required is NOT undefined.
// var pass = true if all okay, false if not.
// Append any missing values to missing_required = []
// Test that each of desired is NOT undefined.
// Append any missing values to missing_desired = []
// Return : {pass: $pass, missong_required = $missing_required, missing_desired = $missing_desired}
// Return : {pass: $pass, missong_required = $missing_required, missing_desired = $missing_desired}
}
module.exports = {
varTest
}
varTest
};