diff --git a/controllers/playlists.js b/controllers/playlists.js index c1fbf1b..8cd1917 100644 --- a/controllers/playlists.js +++ b/controllers/playlists.js @@ -89,6 +89,7 @@ const getUserPlaylist = async (req, res) => { ); // TODO: this whole section needs to be DRYer + // look into serializr playlist.uri = response.data.uri playlist.name = response.data.name playlist.description = response.data.description diff --git a/index.js b/index.js index ea12003..a0211a6 100644 --- a/index.js +++ b/index.js @@ -1,22 +1,27 @@ require('dotenv-flow').config(); + const express = require('express'); const session = require("express-session"); + const cors = require('cors'); const cookieParser = require('cookie-parser'); const helmet = require("helmet"); + const redis = require('redis'); const RedisStore = require("connect-redis").default; +const neo4j = require('neo4j-driver'); + const logger = require("./utils/logger")(module); const app = express(); // Enable this if you run behind a proxy (e.g. nginx) -app.set('trust proxy', 1); +app.set('trust proxy', process.env.TRUST_PROXY); -// Configure Redis client +// Configure Redis client and connect const redisClient = redis.createClient({ - host: process.env.NODE_ENV === 'development' ? 'localhost' : process.env.LIVE_URL, - port: 6379, + host: process.env.REDIS_HOST, + port: process.env.REDIS_PORT, }); redisClient.connect() @@ -25,10 +30,28 @@ redisClient.connect() }) .catch((error) => { logger.error("Redis connection error", { error }); + cleanupFunc(); }); const redisStore = new RedisStore({ client: redisClient }); +// Configure Neo4j driver and connect +const neo4jDriver = neo4j.driver( + process.env.NEO4J_URL, + neo4j.auth.basic(process.env.NEO4J_USER, process.env.NEO4J_PASSWD) +); +const neo4jSession = neo4jDriver.session(); + +(async () => { + try { + await neo4jSession.run('MATCH () RETURN 1 LIMIT 1'); + logger.info("Connected to Neo4j graph DB"); + } catch (error) { + logger.error("Neo4j connection error", { error }); + cleanupFunc(); + } +})(); + // Configure session middleware app.use(session({ store: redisStore, @@ -43,7 +66,7 @@ app.use(session({ // Configure CORS options const corsOptions = { - origin: process.env.NODE_ENV === 'development' ? 'localhost:' + (process.env.PORT || 3000) : process.env.LIVE_URL, + origin: [process.env.CORS_ORIGIN], } app.use(cors(corsOptions)); @@ -72,6 +95,20 @@ app.use((_req, res) => { const port = process.env.PORT || 3000; -app.listen(port, () => { +const server = app.listen(port, () => { logger.info(`App Listening on port ${port}`); }); + +const cleanupFunc = async () => { + logger.debug('SIGTERM signal received: closing server'); + if (neo4jSession) await neo4jSession.close(); + if (neo4jDriver) await neo4jDriver.close(); + logger.debug('Neo4j connection closed'); + if (redisClient) await redisClient.disconnect(); + logger.debug('Redis client disconnected'); + server.close(() => { + logger.debug('HTTP server closed'); + }); +} + +process.on('SIGTERM', cleanupFunc); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d90120c..6b6c7ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "express-session": "^1.17.3", "express-validator": "^7.0.1", "helmet": "^7.0.0", + "neo4j-driver": "^5.15.0", "redis": "^4.6.10", "winston": "^3.10.0" }, @@ -244,9 +245,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", - "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.3.tgz", + "integrity": "sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -259,6 +260,25 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -313,6 +333,29 @@ "node": ">=8" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -910,6 +953,25 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -1109,6 +1171,31 @@ "node": ">= 0.6" } }, + "node_modules/neo4j-driver": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/neo4j-driver/-/neo4j-driver-5.15.0.tgz", + "integrity": "sha512-VavA63sld5qWRiQNkyJ1a9nU+gyrNg5AQ/Mop4Y/LkIMfhfuxsy7VF5T3Ba+UID7zfiSrarCV3FjRy0LDXfnDg==", + "dependencies": { + "neo4j-driver-bolt-connection": "5.15.0", + "neo4j-driver-core": "5.15.0", + "rxjs": "^7.8.1" + } + }, + "node_modules/neo4j-driver-bolt-connection": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/neo4j-driver-bolt-connection/-/neo4j-driver-bolt-connection-5.15.0.tgz", + "integrity": "sha512-/uNkyl1NbDKzufZhQKcD4ATgCF648loNjtOYth/Hv5mfltLJdqISD9aqGbCY5nKLsM0xGGqr3zBpimH/3CtVlA==", + "dependencies": { + "buffer": "^6.0.3", + "neo4j-driver-core": "5.15.0", + "string_decoder": "^1.3.0" + } + }, + "node_modules/neo4j-driver-core": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/neo4j-driver-core/-/neo4j-driver-core-5.15.0.tgz", + "integrity": "sha512-nsnuz9oZPVVavbllcwNORFQFhJ2yMk6hEp7RBlkRIBasPKip+PiU0pfMIcfoGMgjWIhz/beajtESWoAr7USPBw==" + }, "node_modules/nodemon": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", @@ -1358,6 +1445,14 @@ "@redis/time-series": "1.0.5" } }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1587,6 +1682,11 @@ "node": ">= 14.0.0" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1904,9 +2004,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "axios": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", - "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.3.tgz", + "integrity": "sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -1919,6 +2019,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1963,6 +2068,15 @@ "fill-range": "^7.0.1" } }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -2400,6 +2514,11 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, "ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -2553,6 +2672,31 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, + "neo4j-driver": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/neo4j-driver/-/neo4j-driver-5.15.0.tgz", + "integrity": "sha512-VavA63sld5qWRiQNkyJ1a9nU+gyrNg5AQ/Mop4Y/LkIMfhfuxsy7VF5T3Ba+UID7zfiSrarCV3FjRy0LDXfnDg==", + "requires": { + "neo4j-driver-bolt-connection": "5.15.0", + "neo4j-driver-core": "5.15.0", + "rxjs": "^7.8.1" + } + }, + "neo4j-driver-bolt-connection": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/neo4j-driver-bolt-connection/-/neo4j-driver-bolt-connection-5.15.0.tgz", + "integrity": "sha512-/uNkyl1NbDKzufZhQKcD4ATgCF648loNjtOYth/Hv5mfltLJdqISD9aqGbCY5nKLsM0xGGqr3zBpimH/3CtVlA==", + "requires": { + "buffer": "^6.0.3", + "neo4j-driver-core": "5.15.0", + "string_decoder": "^1.3.0" + } + }, + "neo4j-driver-core": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/neo4j-driver-core/-/neo4j-driver-core-5.15.0.tgz", + "integrity": "sha512-nsnuz9oZPVVavbllcwNORFQFhJ2yMk6hEp7RBlkRIBasPKip+PiU0pfMIcfoGMgjWIhz/beajtESWoAr7USPBw==" + }, "nodemon": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", @@ -2737,6 +2881,14 @@ "@redis/time-series": "1.0.5" } }, + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "requires": { + "tslib": "^2.1.0" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2906,6 +3058,11 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==" }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", diff --git a/package.json b/package.json index 0345dfd..a130bf8 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "express-session": "^1.17.3", "express-validator": "^7.0.1", "helmet": "^7.0.0", + "neo4j-driver": "^5.15.0", "redis": "^4.6.10", "winston": "^3.10.0" }, diff --git a/utils/logger.js b/utils/logger.js index 6a56ba3..1c7ec30 100644 --- a/utils/logger.js +++ b/utils/logger.js @@ -44,7 +44,7 @@ const logger = (callingModule) => { levels: config.npm.levels, format: combine( label({ label: getLabel(callingModule) }), - timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), + timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }), logFormat, ), transports: [