diff --git a/.eslintrc.json b/.eslintrc.json index 1d77459..abb10b1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -7,7 +7,8 @@ "no-param-reassign": 0, "no-shadow": 0, "consistent-return": 0, - "func-names": 0 + "func-names": 0, + "indent": 0 }, "env": { "browser": true, diff --git a/.gitignore b/.gitignore index 61fd71f..63d331a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ webconfig.js webserver/public/lib/js/webconfig.js pidfile log.txt +config.hjson # IDE files .idea diff --git a/config.example.hjson b/config.example.hjson new file mode 100644 index 0000000..d35b084 --- /dev/null +++ b/config.example.hjson @@ -0,0 +1,423 @@ +{ + /* + Set this flag to false to disable web server hosting or true to enable web server hosting. + This is useful if you want to host static files in another web server such as nginx. + + If you are only hosting the socket and want musiqpad to host the frontend set this to false. + */ + hostWebserver: true + socketServer: { + host: "" // Host name or IP that the socket server is located at. Leave blank to bind to process IP address + port: 8082// Leave blank to bind to process PORT + } + webServer: { + address: "" // Leave blank to bind to process IP address. + port: 8080// Leave blank to bind to process PORT. + + redirectHTTP: false// Set to true if you want HTTP redirect to HTTPS. + redirectPort: 80// Required if setting above is true. Set to the port you want to redirect HTTP to HTTPS from (Default: 80). + } + useSSL: false// If you want your pad to be accesible over HTTPS set SSL to true and add the path of your certificates + certificate: { + key: "path-to-key" + cert: "path-to-cert" + } + room: { + name: "Pad Name" // This is your pad name. It is shown as a user friendly description on the lounge and tab name. + slug: "this-is-your-slug" // Slugs are used to identify your pad when connecting to musiqpad! This slug must be unique and all in lowecase. + greet: "Welcome to musiqpad!" + bg: "" // Background image file path. Accepts external images. If this is undefined the default background will be used. + maxCon: 0 + ownerEmail: "mail@explodingcamera.com" // This needs to be set then the server restarted to take effect. + guestCanSeeChat: true + bannedCanSeeChat: false + lastmsglimit: 6// How many messages a user can see after joining. + signupcd: 0// How many miliseconds the user cannot do certain things after they sign up. + allowemojis: true + allowrecovery: false + recaptcha: false + queue: { + cycle: true + lock: false + limit: 50 + } + history: { + limit_save: 0 + limit_send: 50 + } + email: { + confirmation: false// Whether to force user to confirm his email address before he is able to do anything + sender: "your@email.tld" + /* + description: Email server setup please refer to https://github.com/nodemailer/nodemailer documention on what the options are supports xOAuth 2.0 + default: {} + */ + options: { + } + } + description: + ''' +

Pad Description

+ Here you can put anything you want in HTML! + ''' + } + apis: { + YT: { + key: "" // Required api key in order for YouTube search to work. + restrictSearchToMusic: false + } + SC: { + key: "" + } + reCaptcha: { + key: "" + secret: "" + } + musiqpad: { + key: "" // This is required in order for your socket to update the musiqpad lounge. Request an API Key here: https://musiqpad.com/lounge + sendLobbyStats: false + } + } + + // The amount of time users stay logged in for before having to login again in days. + // 0 = login every time + loginExpire: 7 + db: { + dbType: "level" // Values "level" for LevelDB, "mysql" for MySQL and "mongo" for MongoDB + dbDir: "./socketserver/db" // Only used for LevelDB. Directory to save databases. Default is ./socketserver/db + mysqlUser: "" // Only used for MySQL. Database username + mysqlPassword: "" // Only used for MySQL. Database password + mysqlHost: "" // Only used for MySQL. Host address + mysqlDatabase: "" // Only used for MySQL. Database being used + mongoUser: "" // Only used for MongoDB. Database username + mongoPassword: "" // Only used for MongoDB. Database password + mongoHost: "" // Only used for MongoDB. Host address + mongoDatabase: "" // Only used for MongoDB. Database being used + } + + /* + "djqueue.join": Ability to join queue + "djqueue.joinlocked": Ability to join locked queue + "djqueue.leave": Ability to leave queue + "djqueue.skip.self": Ability to skip self + "djqueue.skip.other": Ability to skip others + "djqueue.lock": Ability to lock/unlock queue + "djqueue.limit": Ability to change waitlist limit + "djqueue.cycle": Ability to enable/disable queue cycle + "djqueue.move": Ability to move, swap, add and remove people in the queue + "djqueue.playLiveVideos": Ability to play live videos with undefined duration + "djqueue.lock.bypass": Bypass locked queue + "djqueue.limit.bypass": Bypass queue limit + "chat.send": Abilty to send chat messages + "chat.delete": Ability to delete others" chat messages + "chat.specialMention": Ability to use @everyone, @guest and @djs as mention + "chat.broadcast": Ability to send a highlighted broadcast message + "chat.private": Ability to send PMs + "chat.staff": Ability to send and receive special staff chat + "playlist.create": Ability to create playlists + "playlist.delete": Ability to delete playlists + "playlist.rename": Ability to rename playlists + "playlist.import": Ability to import playlists + "playlist.shuffle": Ability to shuffle playlists + "room.grantroles": Ability to change user roles (requires canGrantPerms property) + "room.restrict.ban": Ability to ban and unban users + "room.restrict.mute": Ability to mute and unmute users + "room.restrict.mute_silent": Ability to shadow mute and unmute users + "room.ratelimit.bypass": Will bypass ratelimit + "room.whois": Possibility to request additional information about a user + "room.whois.iphistory": Possibility to request all IP addresses that the user logged from since account creation + + NOTE: Changing the PROPERTY NAME will break role assignments. Title can be changed + without breaking things, but property name must stay the same. + */ + + + // Defines the order that roles will appear on the user list + // PROPERTY names. NOT title. (case-sensitive) + roleOrder: [ + "dev" + "owner" + "coowner" + "supervisor" + "bot" + "regular" + "default" + ] + + // Defines which roles are "staff" members + // PROPERTY names. NOT title. (case-sensitive) + staffRoles: [ + "dev" + "owner" + "coowner" + "supervisor" + "bot" + ] + + + + /* + + Role Options: + + rolename:{ + title: "", // This is the title that gets displayed on the frontend. + showtitle: true/false, // This is whether or not to display the title on the frontend. + badge: "", // This can be any icon from the mdi package. A list of the icons is available here: https://materialdesignicons.com + style: {}, // This can be used to set specific styles to the Username of a user with this role. + permissions: [], // A list of permissions a user with this role is allowed to use. + canGrantRoles: [], // A list of the roles that a user with this role can grant. I.e. an owner should be able to grant manager. + mention: "" // A custom mention. I.e. "owner" would mention this group when someone typed @owner. + } + + Below are a list of roles we suggest using. + + */ + + // Defines roles and permissions + + owner: { // REQUIRED ROLE + title: "Owner" + showtitle: true + style: { + color: "#F46B40" + } + permissions: [ + "djqueue.join" + "djqueue.joinlocked" + "djqueue.leave" + "djqueue.skip.self" + "djqueue.skip.other" + "djqueue.lock" + "djqueue.cycle" + "djqueue.limit" + "djqueue.move" + "djqueue.playLiveVideos" + "djqueue.limit.bypass" + "djqueue.lock.bypass" + "chat.send" + "chat.private" + "chat.broadcast" + "chat.delete" + "chat.specialMention" + "chat.staff" + "playlist.create" + "playlist.delete" + "playlist.rename" + "playlist.import" + "playlist.shuffle" + "room.grantroles" + "room.restrict.ban" + "room.restrict.mute" + "room.restrict.mute_silent" + "room.ratelimit.bypass" + "room.whois" + "room.whois.iphistory" + "server.checkForUpdates" + ] + canGrantRoles: [ + "dev" + "coowner" + "supervisor" + "bot" + "regular" + "default" + ] + } + dev: { // OPTIONAL ROLE FOR MUSIQPAD DEVS + title: "Dev" + showtitle: true + style: { + color: "#A77DC2" + } + permissions: [ + "djqueue.join" + "djqueue.joinlocked" + "djqueue.leave" + "djqueue.skip.self" + "djqueue.skip.other" + "djqueue.lock" + "djqueue.cycle" + "djqueue.limit" + "djqueue.move" + "djqueue.playLiveVideos" + "djqueue.limit.bypass" + "djqueue.lock.bypass" + "chat.send" + "chat.private" + "chat.broadcast" + "chat.delete" + "chat.specialMention" + "chat.staff" + "playlist.create" + "playlist.delete" + "playlist.rename" + "playlist.import" + "playlist.shuffle" + "room.grantroles" + "room.restrict.ban" + "room.restrict.mute" + "room.restrict.mute_silent" + "room.ratelimit.bypass" + "room.whois" + ] + canGrantRoles: [ + "dev" + "coowner" + "supervisor" + "bot" + "regular" + "default" + ] + mention: "devs" + } + coowner: { + title: "Co-owner" + showtitle: true + style: { + color: "#89BE6C" + } + permissions: [ + "djqueue.join" + "djqueue.joinlocked" + "djqueue.leave" + "djqueue.skip.self" + "djqueue.skip.other" + "djqueue.lock" + "djqueue.cycle" + "djqueue.limit" + "djqueue.move" + "djqueue.playLiveVideos" + "djqueue.limit.bypass" + "djqueue.lock.bypass" + "chat.send" + "chat.private" + "chat.delete" + "chat.specialMention" + "chat.broadcast" + "chat.staff" + "playlist.create" + "playlist.delete" + "playlist.rename" + "playlist.import" + "playlist.shuffle" + "room.grantroles" + "room.restrict.ban" + "room.restrict.mute" + "room.restrict.mute_silent" + "room.ratelimit.bypass" + "room.whois" + "room.whois.iphistory" + ] + canGrantRoles: [ + "supervisor" + "bot" + "regular" + "default" + ] + } + supervisor: { + title: "Supervisor" + showtitle: true + style: { + color: "#009CDD" + } + permissions: [ + "djqueue.join" + "djqueue.joinlocked" + "djqueue.leave" + "djqueue.skip.self" + "djqueue.skip.other" + "djqueue.lock" + "djqueue.cycle" + "djqueue.move" + "djqueue.playLiveVideos" + "djqueue.limit.bypass" + "djqueue.lock.bypass" + "chat.send" + "chat.private" + "chat.delete" + "chat.specialMention" + "chat.staff" + "playlist.create" + "playlist.delete" + "playlist.rename" + "playlist.import" + "playlist.shuffle" + "room.grantroles" + "room.restrict.ban" + "room.restrict.mute" + "room.restrict.mute_silent" + "room.ratelimit.bypass" + "room.whois" + ] + canGrantRoles: [ + "regular" + "default" + ] + } + bot: { + title: "Bot" + showtitle: true + badge: "android" + style: { + color: "#964B74" + } + permissions: [ + "djqueue.skip.other" + "djqueue.lock" + "djqueue.cycle" + "djqueue.move" + "chat.send" + "chat.delete" + "chat.specialMention" + "room.restrict.ban" + "room.restrict.mute" + "room.restrict.mute_silent" + "room.ratelimit.bypass" + ] + canGrantRoles: [ + ] + } + regular: { + title: "Regular" + showtitle: false + style: { + color: "#925AFF" + } + permissions: [ + "djqueue.join" + "djqueue.joinlocked" + "djqueue.leave" + "chat.send" + "chat.private" + "djqueue.skip.self" + "playlist.create" + "playlist.delete" + "playlist.rename" + "playlist.import" + ] + canGrantRoles: [ + ] + } + default: { // REQUIRED ROLE + title: "Default" + showtitle: false + style: { + color: "#ffffff" + } + permissions: [ + "djqueue.join" + "djqueue.leave" + "chat.send" + "chat.private" + "djqueue.skip.self" + "playlist.create" + "playlist.delete" + "playlist.rename" + "playlist.import" + ] + canGrantRoles: [ + ] + } +} \ No newline at end of file diff --git a/mqp.js b/mqp.js index 38ca176..714934a 100644 --- a/mqp.js +++ b/mqp.js @@ -1,5 +1,4 @@ const chalk = require('chalk'); -const cproc = require('child_process'); const fs = require('fs'); const daemon = require('daemon'); const path = require('path'); @@ -13,14 +12,13 @@ const notifier = updateNotifier({ updateCheckInterval: 0, }); if (notifier.update) { - console.log('Update available ' + chalk.dim(notifier.update.current) + chalk.reset(' → ') + chalk.green(notifier.update.latest)); -} else { + console.log(`Update available ${chalk.dim(notifier.update.current)}${chalk.reset(' → ')}${chalk.green(notifier.update.latest)}`); } function getRunningPid(callback) { - fs.readFile(__dirname + '/pidfile', { + fs.readFile(`${__dirname}/pidfile`, { encoding: 'utf-8', - }, function (err, pid) { + }, (err, pid) => { if (err) { return callback(err); } @@ -36,17 +34,17 @@ function getRunningPid(callback) { switch (process.argv[2]) { case 'start': - getRunningPid(function (err, pid) { + getRunningPid((err, pid) => { if (!err) { console.log('Musiqpad is already running!'); } else { console.log('\nStarting musiqpad'); - console.log(' "' + chalk.yellow.bold('npm stop') + '" to stop the musiqpad server'); - console.log(' "' + chalk.yellow.bold('npm run log') + '" to view server output'); - console.log(' "' + chalk.yellow.bold('npm restart') + '" to restart musiqpad'); + console.log(` "${chalk.yellow.bold('npm stop')}" to stop the musiqpad server`); + console.log(` "${chalk.yellow.bold('npm run log')}" to view server output`); + console.log(` "${chalk.yellow.bold('npm restart')}" to restart musiqpad`); // Spawn a new musiqpad daemon process, might need some more settings but I'm waiting for the new config storage for that. - daemon.daemon(__dirname + '/start.js', '--daemon', { + daemon.daemon(`${__dirname}/start.js`, '--daemon', { stdout: fs.openSync(path.join(process.cwd(), 'log.txt'), 'a'), }); } @@ -54,7 +52,7 @@ switch (process.argv[2]) { break; case 'stop': - getRunningPid(function (err, pid) { + getRunningPid((err, pid) => { if (!err) { process.kill(pid, 'SIGTERM'); console.log('Stopping musiqpad!'); @@ -65,11 +63,11 @@ switch (process.argv[2]) { break; case 'restart': - getRunningPid(function (err, pid) { + getRunningPid((err, pid) => { if (!err) { process.kill(pid, 'SIGTERM'); console.log('\nRestarting musiqpad'); - daemon.daemon(__dirname + '/start.js', '--daemon', { + daemon.daemon(`${__dirname}/start.js`, '--daemon', { stdout: fs.openSync(path.join(process.cwd(), 'log.txt'), 'a'), }); @@ -79,18 +77,16 @@ switch (process.argv[2]) { }); break; - case 'log': - console.log('Type ' + 'Ctrl-C ' + 'to exit'); - - ft = tail.startTailing('./log.txt'); - ft.on('line', function (line) { + case 'log': { + console.log('Type Ctrl-C to exit'); + const ft = tail.startTailing('./log.txt'); + ft.on('line', line => { console.log(line); }); - break; - + } case 'update': - getRunningPid(function (err, pid) { + getRunningPid((err, pid) => { if (!err) { process.kill(pid, 'SIGTERM'); console.log('Stopping musiqpad!'); diff --git a/package.json b/package.json index aac3fc9..f6197d6 100644 --- a/package.json +++ b/package.json @@ -1,63 +1,66 @@ -{ - "name": "mqp-server", - "version": "0.7.1", - "description": "musiqpad self-hosted server", - "main": "server-package.js", - "author": "musiqpad Team ", - "private": false, - "repository": { - "type": "git", - "url": "git+https://github.com/musiqpad/mqp-server.git" - }, - "license": "MIT", - "bugs": { - "url": "https://github.com/musiqpad/mqp-server/issues" - }, - "homepage": "https://github.com/musiqpad/mqp-server#readme", - "scripts": { - "start": "node ./mqp.js start", - "stop": "node ./mqp.js stop", - "restart": "node ./mqp.js restart", - "log": "node ./mqp.js log", - "update": "node ./mqp.js update", - "test": "ava --verbose" - }, - "dependencies": { - "basic-logger": "^0.4.4", - "chalk": "^1.0.0", - "clean-css": "^3.4.9", - "compression": "^1.6.2", - "daemon": "^1.1.0", - "deasync": "^0.1.4", - "download-git-repo": "^0.1.2", - "durationjs": "^1.1.1", - "express": "^4.13.3", - "extend": "^3.0.0", - "file-tail": "^0.3.0", - "forever": "^0.15.1", - "leveldown": "1.4.4", - "levelup": "^1.3.1", - "mongodb": "^2.1.16", - "mysql": "^2.10.2", - "nodemailer": "^2.1.0", - "path": "^0.12.7", - "ps-tree": "^1.0.1", - "request": "^2.67.0", - "update-notifier": "^0.7.0", - "ws": "^1.0.1", - "xoauth2": "^1.1.0", - "yesno": "0.0.1" - }, - "devDependencies": { - "ava": "^0.15.2", - "eslint": "^2.13.1", - "eslint-config-airbnb": "^9.0.1", - "eslint-plugin-import": "^1.9.2", - "eslint-plugin-jsx-a11y": "^1.5.3", - "eslint-plugin-react": "^5.2.2" - }, - "ava": { - "concurrency": 5, - "failFast": true - } -} +{ + "name": "mqp-server", + "version": "0.7.1", + "description": "musiqpad self-hosted server", + "main": "server-package.js", + "author": "musiqpad Team ", + "private": false, + "repository": { + "type": "git", + "url": "git+https://github.com/musiqpad/mqp-server.git" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/musiqpad/mqp-server/issues" + }, + "homepage": "https://github.com/musiqpad/mqp-server#readme", + "scripts": { + "start": "node ./mqp.js start", + "stop": "node ./mqp.js stop", + "restart": "node ./mqp.js restart", + "log": "node ./mqp.js log", + "update": "node ./mqp.js update", + "test": "ava --verbose" + }, + "dependencies": { + "basic-logger": "^0.4.4", + "chalk": "^1.0.0", + "clean-css": "^3.4.9", + "compression": "^1.6.2", + "daemon": "^1.1.0", + "deasync": "^0.1.4", + "download-git-repo": "^0.1.2", + "durationjs": "^1.1.1", + "express": "^4.13.3", + "extend": "^3.0.0", + "file-tail": "^0.3.0", + "forever": "^0.15.1", + "fs-extra": "^0.30.0", + "hjson": "^1.8.4", + "leveldown": "1.4.4", + "levelup": "^1.3.1", + "mongodb": "^2.1.16", + "mysql": "^2.10.2", + "nconf": "^0.8.4", + "nodemailer": "^2.1.0", + "path": "^0.12.7", + "ps-tree": "^1.0.1", + "request": "^2.67.0", + "update-notifier": "^0.7.0", + "ws": "^1.0.1", + "xoauth2": "^1.1.0", + "yesno": "0.0.1" + }, + "devDependencies": { + "ava": "^0.15.2", + "eslint": "^2.13.1", + "eslint-config-airbnb": "^9.0.1", + "eslint-plugin-import": "^1.9.2", + "eslint-plugin-jsx-a11y": "^1.5.3", + "eslint-plugin-react": "^5.2.2" + }, + "ava": { + "concurrency": 5, + "failFast": true + } +} diff --git a/socketserver/SC.js b/socketserver/SC.js index 3dce6ea..ee1bd96 100644 --- a/socketserver/SC.js +++ b/socketserver/SC.js @@ -1,11 +1,11 @@ // API reference: https://developers.soundcloud.com/docs/api/reference#track -var https = require('https'); -var util = require('util'); -var log = new (require('basic-logger'))({showTimestamp: true, prefix: "SC"}); -var querystring = require('querystring'); -var config = require('../serverconfig'); -var key = config.apis.SC.key; +const https = require('https'); +const util = require('util'); +const log = new (require('basic-logger'))({showTimestamp: true, prefix: "SC"}); +const querystring = require('querystring'); +const nconf = require('nconf'); +const key = nconf.get('apis:SC:key'); var SC = function(){ }; diff --git a/socketserver/YT.js b/socketserver/YT.js index 89cfb0e..e6d5812 100644 --- a/socketserver/YT.js +++ b/socketserver/YT.js @@ -3,8 +3,8 @@ var util = require('util'); var log = new (require('basic-logger'))({showTimestamp: true, prefix: "YT"}); var querystring = require('querystring'); var Duration = require("durationjs"); -var config = require('../serverconfig'); -var key = key = config.apis.YT.key; +const nconf = require('nconf'); +const key = nconf.get('apis:YT:key'); https.globalAgent.keepAlive = true; https.globalAgent.keepAliveMsecs = 60e3; @@ -146,7 +146,7 @@ YT.prototype.search = function(query, callback){ key: key }; - if (config.apis.YT.restrictSearchToMusic) + if (nconf.get('apis:YT:restrictSearchToMusic')) inObj.videoCategoryId = 10; // This is restricting the search to things categorized as music var url = "https://www.googleapis.com/youtube/v3/search?" + querystring.stringify(inObj); diff --git a/socketserver/database.js b/socketserver/database.js index 9f2cfc0..5f73840 100644 --- a/socketserver/database.js +++ b/socketserver/database.js @@ -1,16 +1,19 @@ -var config = require('../serverconfig'); +const nconf = require('nconf'); -function Database(){ - config.db.dbType = config.db.dbType.toLowerCase() || 'level'; - - switch(config.db.dbType){ +function Database() { + nconf.defaults({ + 'db:dbType': 'level' + }); + switch (nconf.get('db:dbType')) { case 'level': return require('./db_level'); case 'mysql': return require('./db_mysql'); case 'mongo': return require('./db_mongo'); + default: + return require('./db_level'); } } -module.exports = new Database(); \ No newline at end of file +module.exports = new Database(); diff --git a/socketserver/database_util.js b/socketserver/database_util.js index 04ad32d..95b2376 100644 --- a/socketserver/database_util.js +++ b/socketserver/database_util.js @@ -1,16 +1,16 @@ -var Hash = require('./hash'); +const Hash = require('./hash'); -function DBUtils(){} +function DBUtils() {} -DBUtils.prototype.makePass = function(inPass, salt) { +DBUtils.prototype.makePass = function (inPass, salt) { return Hash.md5(('' + inPass) + (salt || '')).toString(); }; -DBUtils.prototype.validateEmail = function(email) { +DBUtils.prototype.validateEmail = function (email) { return /^.+@.+\..+$/.test(email); }; -DBUtils.prototype.validateUsername = function(un) { +DBUtils.prototype.validateUsername = function (un) { return /^[a-z0-9_-]{3,20}$/i.test(un); }; diff --git a/socketserver/db_level.js b/socketserver/db_level.js index cb8bc1a..ebc28cd 100644 --- a/socketserver/db_level.js +++ b/socketserver/db_level.js @@ -1,101 +1,123 @@ -//Modules -var levelup = require('levelup'); -var path = require('path'); -var util = require('util'); -var fs = require('fs'); -var log = new(require('basic-logger'))({ +// eslint-disable-next-line +'use strict'; +// Modules +const levelup = require('levelup'); +const path = require('path'); +const util = require('util'); +const fs = require('fs'); +const log = new(require('basic-logger'))({ showTimestamp: true, - prefix: "LevelDB" + prefix: 'LevelDB' }); +const nconf = require('nconf'); + +// Files +const Mailer = require('./mailer'); +const DBUtils = require('./database_util'); + +// Variables +let currentPID = 0; +let currentUID = 0; +let currentCID = 0; +const expires = 1000 * 60 * 60 * 24 * nconf.get('loginExpire'); +let usernames = []; + +function setupDB(dir, setup, callback) { + setup = setup || function () {}; + callback = callback || function () {}; + + return levelup(dir, null, (err, newdb) => { + if (err) { + log.error('Could not open db'); + callback(err); + return; + } -//Files -var config = require('../serverconfig.js'); -var Mailer = require('./mailer'); -var DBUtils = require('./database_util'); - -//Variables -var currentPID = 0; -var currentUID = 0; -var currentCID = 0; -var expires = 1000 * 60 * 60 * 24 * config.loginExpire; -var usernames = []; + newdb.get('setup', (err) => { + if (err && err.notFound) { + newdb.put('setup', 1); + setup(newdb); + callback(null, newdb); + } else { + callback(null, newdb); + } + }); + }); +} function LevelDB(callback) { - var dbdir = path.resolve(config.db.dbDir || './socketserver/db'); + const dbdir = path.resolve(nconf.get('db:dbDir') || './socketserver/db'); try { fs.statSync(dbdir); - } catch(e) { + } catch (e) { fs.mkdirSync(dbdir); } - //PlaylistDB - if(!this.PlaylistDB) - this.PlaylistDB = setupDB(dbdir + '/playlists', - - //If new DB is created - function(newdb) { - currentPID = 1; - log.debug('PIDCOUNTER set to 1'); - newdb.put('PIDCOUNTER', 1); - }, - - //Callback - function(err, db) { - if (err) log.error('Could not open PlaylistDB: ' + err); - - if (currentPID != 0) return; - - db.get('PIDCOUNTER', function(err, val) { - if (err) { - throw new Error('Cannot get PIDCOUNTER from UserDB. Might be corrupt'); - } - currentPID = parseInt(val); - }); - }); - - //RoomDB - if(!this.RoomDB) - this.RoomDB = setupDB(dbdir + '/room', - - //If new DB is created - function(newdb) {}, - - //Callback - function(err, db) { - if (err) throw new Error('Could not open RoomDB: ' + err); - if (callback) callback(null, db); - }); + // PlaylistDB + if (!this.PlaylistDB) { + this.PlaylistDB = setupDB(`${dbdir}/playlists`, + // If new DB is created + (newdb) => { + currentPID = 1; + log.debug('PIDCOUNTER set to 1'); + newdb.put('PIDCOUNTER', 1); + }, + + // Callback + (err, db) => { + if (err) log.error(`Could not open PlaylistDB: ${err}`); + if (currentPID !== 0) return; + db.get('PIDCOUNTER', (err, val) => { + if (err) { + throw new Error('Cannot get PIDCOUNTER from UserDB. Might be corrupt'); + } + currentPID = parseInt(val, 10); + }); + }); + } - //TokenDB - if(!this.TokenDB) - this.TokenDB = setupDB(dbdir + '/tokens', + // RoomDB + if (!this.RoomDB) { + this.RoomDB = setupDB(`${dbdir}/room`, + // If new DB is created + () => {}, + + // Callback + (err, db) => { + if (err) throw new Error(`Could not open RoomDB: ${err}`); + if (callback) callback(null, db); + }); + } + // TokenDB + if (!this.TokenDB) + this.TokenDB = setupDB(`${dbdir}/tokens`, - //If new DB is created - function(newdb) {}, + // If new DB is created + function (newdb) {}, - //Callback - function(err, db) { + // Callback + function (err, db) { if (err) log.error('Could not open TokenDB: ' + err); }); - //UserDB - if(!this.UserDB) - this.UserDB = setupDB(dbdir + '/users', + // UserDB + if (!this.UserDB) + this.UserDB = setupDB(`${dbdir}/users`, - //If new DB is created - function(newdb) { + // If new DB is created + function (newdb) { currentUID = 1; log.debug('UIDCOUNTER set to 1'); newdb.put('UIDCOUNTER', 1); }, - //Callback - function(err, newdb) { + // Callback + function (err, newdb) { if (err) { throw new Error('Could not open UserDB: ' + err); } if (currentUID != 0) return; - newdb.get('UIDCOUNTER', function(err, val) { + newdb.get('UIDCOUNTER', function (err, val) { if (err) { throw new Error('Cannot get UIDCOUNTER from UserDB. Might be corrupt'); } @@ -103,7 +125,7 @@ function LevelDB(callback) { }); newdb.createReadStream() - .on('data', function(data) { + .on('data', function (data) { if (data.key.indexOf('@') == -1) return; try { var user = JSON.parse(data.value); @@ -115,88 +137,65 @@ function LevelDB(callback) { user.lastdj = false; newdb.put(data.key, JSON.stringify(user)); }) - .on('end', function() { + .on('end', function () { return false; }); }); - - //ChatDB - if(!this.ChatDB) + + // ChatDB + if (!this.ChatDB) this.ChatDB = setupDB(dbdir + '/chat', - //If new DB is created - function(newdb) { + // If new DB is created + function (newdb) { currentCID = 1; log.debug('CIDCOUNTER set to 1'); newdb.put('CIDCOUNTER', 1); }, - //Callback - function(err, newdb) { + // Callback + function (err, newdb) { if (err) { throw new Error('Could not open ChatDB: ' + err); } if (currentCID != 0) return; - newdb.get('CIDCOUNTER', function(err, val) { + newdb.get('CIDCOUNTER', function (err, val) { if (err) { throw new Error('Cannot get CIDCOUNTER from PmDB. Might be corrupt'); } currentCID = parseInt(val); }); }); - - //PmDB - if(!this.PmDB) + // PmDB + if (!this.PmDB) this.PmDB = setupDB(dbdir + '/pm', - //If new DB is created - function(newdb) {}, + // If new DB is created + function (newdb) {}, - //Callback - function(err, newdb) { + // Callback + function (err, newdb) { if (err) { throw new Error('Could not open PmDB: ' + err); } }); - - //IpDB - if(!this.IpDB) + + // IpDB + if (!this.IpDB) this.IpDB = setupDB(dbdir + '/ip', - //If new DB is created - function(newdb) {}, + // If new DB is created + function (newdb) {}, - //Callback - function(err, newdb) { + // Callback + function (err, newdb) { if (err) { throw new Error('Could not open IpDB: ' + err); } }); } -function setupDB(dir, setup, callback){ - setup = setup || function(){}; - callback = callback || function(){}; - - return levelup(dir, null, function(err, newdb){ - if (err){ - log.error('Could not open db'); - callback(err); - return; - } - - newdb.get("setup", function( err, val ){ - if (err && err.notFound){ - newdb.put('setup', 1); - setup(newdb); - callback(null, newdb); - }else{ - callback(null, newdb); - } - }); - }); -} /** * getJSON() gives the callback function a parsed JSON object @@ -206,10 +205,10 @@ function setupDB(dir, setup, callback){ * @param {Function} callback * @return {Object} this */ -LevelDB.prototype.getJSON = function(db, key, callback) { - callback = callback || function() {}; +LevelDB.prototype.getJSON = function (db, key, callback) { + callback = callback || function () {}; - db.get(key, function(err, val) { + db.get(key, function (err, val) { if (val) { try { val = JSON.parse(val); @@ -231,17 +230,17 @@ LevelDB.prototype.getJSON = function(db, key, callback) { * @param {Function} callback * @return {Object} this */ -LevelDB.prototype.putJSON = function(db, key, val, callback) { - callback = callback || function() {}; +LevelDB.prototype.putJSON = function (db, key, val, callback) { + callback = callback || function () {}; db.put(key, JSON.stringify(val), callback); return this; }; -//PlaylistDB -LevelDB.prototype.getPlaylist = function(pid, callback) { +// PlaylistDB +LevelDB.prototype.getPlaylist = function (pid, callback) { var Playlist = require('./playlist'); - this.getJSON(this.PlaylistDB, pid, function(err, data) { + this.getJSON(this.PlaylistDB, pid, function (err, data) { if (err) { callback('PlaylistNotFound'); return; @@ -257,7 +256,7 @@ LevelDB.prototype.getPlaylist = function(pid, callback) { return this; }; -LevelDB.prototype.createPlaylist = function(owner, name, callback) { +LevelDB.prototype.createPlaylist = function (owner, name, callback) { var Playlist = require('./playlist'); var pl = new Playlist(); @@ -271,51 +270,51 @@ LevelDB.prototype.createPlaylist = function(owner, name, callback) { callback(null, pl); }; -LevelDB.prototype.deletePlaylist = function(pid, callback) { +LevelDB.prototype.deletePlaylist = function (pid, callback) { this.PlaylistDB.del(pid.toString(), callback); }; -LevelDB.prototype.putPlaylist = function(pid, data, callback) { +LevelDB.prototype.putPlaylist = function (pid, data, callback) { this.putJSON(this.PlaylistDB, pid, data, callback); }; -//RoomDB -LevelDB.prototype.getRoom = function(slug, callback) { +// RoomDB +LevelDB.prototype.getRoom = function (slug, callback) { this.getJSON(this.RoomDB, slug, callback); return this; }; -LevelDB.prototype.setRoom = function(slug, val, callback) { +LevelDB.prototype.setRoom = function (slug, val, callback) { this.putJSON(this.RoomDB, slug, val, callback); return this; }; -//TokenDB -LevelDB.prototype.deleteToken = function(tok) { +// TokenDB +LevelDB.prototype.deleteToken = function (tok) { this.TokenDB.del(tok); }; -LevelDB.prototype.createToken = function(email) { +LevelDB.prototype.createToken = function (email) { var tok = DBUtils.makePass(email, Date.now()); this.putJSON(this.TokenDB, tok, { - email: email, + email, time: Date.now(), }); return tok; }; -LevelDB.prototype.isTokenValid = function(tok, callback) { +LevelDB.prototype.isTokenValid = function (tok, callback) { var that = this; - this.getJSON(this.TokenDB, tok, function(err, data) { + this.getJSON(this.TokenDB, tok, function (err, data) { if (err || data == null) { callback('InvalidToken'); return; } - if (config.loginExpire && (Date.now() - data.time) < expires) { + if (nconf.get('loginExpire') && (Date.now() - data.time) < expires) { callback(null, data.email); } else { that.deleteToken(data.token); @@ -324,7 +323,7 @@ LevelDB.prototype.isTokenValid = function(tok, callback) { }); }; -//UserDB +// UserDB function addUsername(un) { usernames.push(un.toLowerCase()); } @@ -341,7 +340,7 @@ function usernameExists(un) { return ((ind = usernames.indexOf(un)) != -1 ? ind : false); } -LevelDB.prototype.createUser = function(obj, callback) { +LevelDB.prototype.createUser = function (obj, callback) { var User = require('./user'); var that = this; @@ -355,7 +354,7 @@ LevelDB.prototype.createUser = function(obj, callback) { var inData = defaultCreateObj; inData.email = inData.email.toLowerCase(); - //Validation + // Validation if (!inData.email || !DBUtils.validateEmail(inData.email)) { callback('InvalidEmail'); return; @@ -373,43 +372,43 @@ LevelDB.prototype.createUser = function(obj, callback) { return; } - //Check for existing account - this.userEmailExists(inData.email, function(err, res) { + // Check for existing account + this.userEmailExists(inData.email, function (err, res) { if (!err) { if (callback) callback('AccountExists'); return; } var user = new User(); - + user.data.uid = currentUID++; that.UserDB.put('UIDCOUNTER', currentUID); user.data.un = inData.un; user.data.salt = DBUtils.makePass(Date.now()).slice(0, 10); user.data.pw = DBUtils.makePass(inData.pw, user.data.salt); user.data.created = Date.now(); - if (config.room.email.confirmation) user.data.confirmation = DBUtils.makePass(Date.now()); + if (nconf.get('room:email:confirmation')) user.data.confirmation = DBUtils.makePass(Date.now()); var updatedUserObj = user.makeDbObj(); var tok = that.createToken(inData.email); - that.putJSON(that.UserDB, inData.email, updatedUserObj, function(err) { + that.putJSON(that.UserDB, inData.email, updatedUserObj, function (err) { if (err) { callback(err); return; } - //Send confirmation email - if (config.room.email.confirmation) { + // Send confirmation email + if (nconf.get('room:email:confirmation')) { Mailer.sendEmail('signup', { code: user.data.confirmation, user: inData.un, - }, inData.email, function(data) { + }, inData.email, function (data) { console.log(data); }); } - //Do other ~messy~ stuff + // Do other ~messy~ stuff addUsername(inData.un); user.login(inData.email); callback(null, user, tok); @@ -417,7 +416,7 @@ LevelDB.prototype.createUser = function(obj, callback) { }); }; -LevelDB.prototype.loginUser = function(obj, callback) { +LevelDB.prototype.loginUser = function (obj, callback) { var User = require('./user'); var that = this; @@ -433,7 +432,7 @@ LevelDB.prototype.loginUser = function(obj, callback) { if (inData.email && inData.pw) { inData.email = inData.email.toLowerCase(); - this.getJSON(this.UserDB, inData.email, function(err, data) { + this.getJSON(this.UserDB, inData.email, function (err, data) { if ((err && err.notFound) || data == null) { callback('UserNotFound'); return; @@ -452,19 +451,18 @@ LevelDB.prototype.loginUser = function(obj, callback) { var tok = that.createToken(inData.email); var user = new User(); - user.login(inData.email, data, function() { - + user.login(inData.email, data, function () { callback(null, user, tok); }); }); } else if (inData.token) { - that.isTokenValid(inData.token, function(err, email) { + that.isTokenValid(inData.token, function (err, email) { if (err) { callback(err); return; } - that.getJSON(that.UserDB, email, function(err, data) { + that.getJSON(that.UserDB, email, function (err, data) { if ((err && err.notFound) || data == null) { callback('UserNotFound'); return; @@ -476,8 +474,7 @@ LevelDB.prototype.loginUser = function(obj, callback) { } var user = new User(); - user.login(email, data, function() { - + user.login(email, data, function () { callback(null, user); }); }); @@ -487,39 +484,38 @@ LevelDB.prototype.loginUser = function(obj, callback) { } }; -LevelDB.prototype.putUser = function(email, data, callback) { +LevelDB.prototype.putUser = function (email, data, callback) { this.putJSON(this.UserDB, email, data, callback); }; -LevelDB.prototype.getUser = function(email, callback){ +LevelDB.prototype.getUser = function (email, callback) { var User = require('./user'); - this.getJSON(this.UserDB, email, function(err, data){ - if ((err && err.notFound) || data == null) {callback('UserNotFound'); return; } - - if (err) {callback(err); return; } + this.getJSON(this.UserDB, email, function (err, data) { + if ((err && err.notFound) || data == null) { callback('UserNotFound'); return; } + + if (err) { callback(err); return; } var user = new User(); - - user.login(email, data, function(){ + user.login(email, data, function () { callback(null, user); }); }); }; -LevelDB.prototype.deleteUser = function(email, callback){ +LevelDB.prototype.deleteUser = function (email, callback) { var that = this; - - this.getUser(email, function(err, user){ - if (err){ if (callback) callback(err); return; } - + + this.getUser(email, function (err, user) { + if (err) { if (callback) callback(err); return; } + that.UserDB.del(email); - + callback(null, true); }); }; -LevelDB.prototype.getUserByUid = function(uid, opts, callback) { +LevelDB.prototype.getUserByUid = function (uid, opts, callback) { var User = require('./user'); var done = false; @@ -542,7 +538,7 @@ LevelDB.prototype.getUserByUid = function(uid, opts, callback) { var len = 0; var stream = this.UserDB.createReadStream() - .on('data', function(data) { + .on('data', function (data) { var obj = {}; try { @@ -555,7 +551,7 @@ LevelDB.prototype.getUserByUid = function(uid, opts, callback) { if (uid.indexOf(obj.uid) > -1) { var user = new User(); - user.login(data.key, obj, opts, function() { + user.login(data.key, obj, opts, function () { out[obj.uid] = user; len++; @@ -572,13 +568,13 @@ LevelDB.prototype.getUserByUid = function(uid, opts, callback) { stream.destroy(); var user = new User(); - user.login(data.key, obj, opts, function() { + user.login(data.key, obj, opts, function () { callback(null, user); }); } } }) - .on('end', function() { + .on('end', function () { if (!done) { if (typeof uid === 'number') { callback('UserNotFound'); @@ -590,7 +586,7 @@ LevelDB.prototype.getUserByUid = function(uid, opts, callback) { }); }; -LevelDB.prototype.getUserByName = function(name, opts, callback) { +LevelDB.prototype.getUserByName = function (name, opts, callback) { var User = require('./user'); var done = false; @@ -600,7 +596,7 @@ LevelDB.prototype.getUserByName = function(name, opts, callback) { } var stream = this.UserDB.createReadStream() - .on('data', function(data) { + .on('data', function (data) { var obj = {}; try { @@ -615,20 +611,19 @@ LevelDB.prototype.getUserByName = function(name, opts, callback) { stream.destroy(); var user = new User(); - user.login(data.key, obj, opts, function() { + user.login(data.key, obj, opts, function () { if (callback) callback(null, user); }); } }) - .on('end', function() { + .on('end', function () { if (!done && callback) callback('UserNotFound'); }); }; -LevelDB.prototype.userEmailExists = function(key, callback) { - this.getJSON(this.UserDB, key, function(err, data) { - +LevelDB.prototype.userEmailExists = function (key, callback) { + this.getJSON(this.UserDB, key, function (err, data) { if (err && err.notFound) { if (callback) callback(err, false); return; @@ -638,38 +633,38 @@ LevelDB.prototype.userEmailExists = function(key, callback) { }); }; -//ChatDB -LevelDB.prototype.logChat = function(uid, msg, special, callback) { - this.putJSON(this.ChatDB, currentCID, { uid: uid, msg: msg, special: special }); +// ChatDB +LevelDB.prototype.logChat = function (uid, msg, special, callback) { + this.putJSON(this.ChatDB, currentCID, { uid, msg, special }); callback(null, currentCID++); }; -//PmDB -LevelDB.prototype.logPM = function(from, to, msg, callback) { +// PmDB +LevelDB.prototype.logPM = function (from, to, msg, callback) { var that = this; - var key = Math.min(from, to) + ":" + Math.max(from, to); - - this.getJSON(this.PmDB, key, function(err, res){ + var key = Math.min(from, to) + ':' + Math.max(from, to); + + this.getJSON(this.PmDB, key, function (err, res) { var out = []; - - if(!err) out = res; - + + if (!err) out = res; + out.push({ message: msg, time: new Date(), - from: from, + from, unread: true, }); - + that.putJSON(that.PmDB, key, out); }); }; -LevelDB.prototype.getConversation = function(from, to, callback) { - var key = Math.min(from, to) + ":" + Math.max(from, to); - - this.getJSON(this.PmDB, key, function(err, res){ - if(err){ +LevelDB.prototype.getConversation = function (from, to, callback) { + var key = Math.min(from, to) + ':' + Math.max(from, to); + + this.getJSON(this.PmDB, key, function (err, res) { + if (err) { callback(null, []); } else { callback(null, res); @@ -677,15 +672,15 @@ LevelDB.prototype.getConversation = function(from, to, callback) { }); }; -LevelDB.prototype.getConversations = function(uid, callback) { +LevelDB.prototype.getConversations = function (uid, callback) { var that = this; - + var out = {}; var uids; uid = uid.toString(); - + this.PmDB.createReadStream() - .on('data', function(data) { + .on('data', function (data) { if (data.key.indexOf(':') == -1 || (uids = data.key.split(':')).indexOf(uid) == -1) return; try { @@ -693,10 +688,10 @@ LevelDB.prototype.getConversations = function(uid, callback) { } catch (e) { return; } - + var unread = 0; - convo.map(function(e){ - if(e.unread && e.from != uid) unread++; + convo.map(function (e) { + if (e.unread && e.from != uid) unread++; return { messages: e.messages, time: e.time, @@ -706,15 +701,15 @@ LevelDB.prototype.getConversations = function(uid, callback) { out[uids[(uids.indexOf(uid) + 1) % 2]] = { user: null, - messages: [ convo.pop() ], - unread: unread, + messages: [convo.pop()], + unread, }; }) - .on('end', function() { - var uids = Object.keys(out).map(function(e){ return parseInt(e); }); - + .on('end', function () { + var uids = Object.keys(out).map(function (e) { return parseInt(e); }); + if (uids.length > 0) { - that.getUserByUid(uids, function(err, result){ + that.getUserByUid(uids, function (err, result) { if (err) { callback(err); } else { @@ -731,45 +726,47 @@ LevelDB.prototype.getConversations = function(uid, callback) { }); }; -LevelDB.prototype.markConversationRead = function(uid, uid2, time) { +LevelDB.prototype.markConversationRead = function (uid, uid2, time) { var that = this; - var key = Math.min(uid, uid2) + ":" + Math.max(uid, uid2); - - this.getJSON(this.PmDB, key, function(err, res) { - if(err) return; - - res.map(function(e){ - if(e.from == uid2 && new Date(e.time) < new Date(time)) e.unread = false; + var key = Math.min(uid, uid2) + ':' + Math.max(uid, uid2); + + this.getJSON(this.PmDB, key, function (err, res) { + if (err) return; + + res.map(function (e) { + if (e.from == uid2 && new Date(e.time) < new Date(time)) e.unread = false; return e; }); - + that.putJSON(that.PmDB, key, res); }); }; -//IpDB -LevelDB.prototype.logIp = function(address, uid) { - var that = this; +// IpDB +LevelDB.prototype.logIp = function (address, uid) { + let that = this; + + this.getJSON(this.IpDB, uid, function (err, res) { + let out = res || []; - this.getJSON(this.IpDB, uid, function(err, res){ - var out = res || []; - out.push({ - address: address, + address, time: new Date(), }); - + that.putJSON(that.IpDB, uid, out); }); }; -LevelDB.prototype.getIpHistory = function(uid, callback) { - this.getJSON(this.IpDB, uid, function(err, data) { - if(err) - callback(err) - else - callback(null, data.sort(function(a, b){ return a.address > b.address; }).reverse().filter(function(e, i, a){ return i == 0 || a[i - 1].address != e.address; }).sort(function(a, b){ return a.time < b.time; })); +LevelDB.prototype.getIpHistory = function (uid, callback) { + this.getJSON(this.IpDB, uid, (err, data) => { + if (err) { + callback(err); + } + else { + callback(null, data.sort(function (a, b) { return a.address > b.address; }).reverse().filter(function (e, i, a) { return i == 0 || a[i - 1].address != e.address; }).sort(function (a, b) { return a.time < b.time; })); + } }); }; -module.exports = new LevelDB(); \ No newline at end of file +module.exports = new LevelDB(); diff --git a/socketserver/db_mongo.js b/socketserver/db_mongo.js index edac477..7312da9 100644 --- a/socketserver/db_mongo.js +++ b/socketserver/db_mongo.js @@ -1,40 +1,42 @@ -//Modules -var mongodb = require('mongodb').MongoClient; -var util = require('util'); -var log = new(require('basic-logger'))({ +// eslint-disable-next-line +'use strict'; +// Modules +const mongodb = require('mongodb').MongoClient; +const util = require('util'); +const log = new(require('basic-logger'))({ showTimestamp: true, - prefix: "MongoDB" + prefix: 'MongoDB' }); -//Files -var config = require('../serverconfig.js'); -var Mailer = require('./mailer'); -var DBUtils = require('./database_util'); - -//Variables -var expires = 1000 * 60 * 60 * 24 * config.loginExpire; -var usernames = []; -var db = null; -var poolqueue = []; -var ready = false; - -var playlistscol = null; -var roomcol = null; -var tokenscol = null; -var userscol = null; -var chatcol = null; -var pmscol = null; -var ipcol = null; +// Files +const nconf = require('nconf'); +const Mailer = require('./mailer'); +const DBUtils = require('./database_util'); + +// Variables +const expires = 1000 * 60 * 60 * 24 * nconf.get('loginExpire'); +let usernames = []; +let db = null; +let poolqueue = []; +let ready = false; + +let playlistscol = null; +let roomcol = null; +let tokenscol = null; +let userscol = null; +let chatcol = null; +let pmscol = null; +let ipcol = null; function dbQueue(callback) { if (callback === true) { while (poolqueue.length > 0) (poolqueue.shift())(); - + ready = true; return; } - + if (!ready) { return poolqueue.push(callback); } @@ -45,110 +47,120 @@ function dbQueue(callback) { function createCollectionsIfNoExist(callback) { var step = 0; var total = 7; - - db.collection('playlists', {strict:true}, function(err, col) { + + db.collection('playlists', { + strict: true + }, function (err, col) { if (err) { - db.createCollection('playlists', function(errc, result) { - if (errc) - throw new Error('Failed to create the playlists collection'); - - playlistscol = result; - if (++step == total) callback(); - }); + db.createCollection('playlists', function (errc, result) { + if (errc) + throw new Error('Failed to create the playlists collection'); + + playlistscol = result; + if (++step == total) callback(); + }); } else { playlistscol = col; if (++step == total) callback(); } - }); - - db.collection('room', {strict:true}, function(err, col) { + + db.collection('room', { + strict: true + }, function (err, col) { if (err) { - db.createCollection('room', function(errc, result) { - if (errc) - throw new Error('Failed to create the room collection'); - - roomcol = result; - if (++step == total) callback(); - }); + db.createCollection('room', function (errc, result) { + if (errc) + throw new Error('Failed to create the room collection'); + + roomcol = result; + if (++step == total) callback(); + }); } else { roomcol = col; if (++step == total) callback(); } - }); - - db.collection('tokens', {strict:true}, function(err, col) { + + db.collection('tokens', { + strict: true + }, function (err, col) { if (err) { - db.createCollection('tokens', function(errc, result) { - if (errc) - throw new Error('Failed to create the tokens collection'); - - tokenscol = result; - if (++step == total) callback(); - }); + db.createCollection('tokens', function (errc, result) { + if (errc) + throw new Error('Failed to create the tokens collection'); + + tokenscol = result; + if (++step == total) callback(); + }); } else { tokenscol = col; if (++step == total) callback(); } - }); - - db.collection('users', {strict:true}, function(err, col) { + + db.collection('users', { + strict: true + }, function (err, col) { if (err) { - db.createCollection('users', function(errc, result) { - if (errc) - throw new Error('Failed to create the users collection'); - - userscol = result; - if (++step == total) callback(); - }); + db.createCollection('users', function (errc, result) { + if (errc) + throw new Error('Failed to create the users collection'); + + userscol = result; + if (++step == total) callback(); + }); } else { userscol = col; if (++step == total) callback(); } - }); - - db.collection('chat', {strict:true}, function(err, col) { + + db.collection('chat', { + strict: true + }, function (err, col) { if (err) { - db.createCollection('chat', function(errc, result) { - if (errc) - throw new Error('Failed to create the chat collection'); - - chatcol = result; - if (++step == total) callback(); - }); + db.createCollection('chat', function (errc, result) { + if (errc) + throw new Error('Failed to create the chat collection'); + + chatcol = result; + if (++step == total) callback(); + }); } else { chatcol = col; if (++step == total) callback(); } }); - - db.collection('pms', {strict:true}, function(err, col) { + + db.collection('pms', { + strict: true + }, function (err, col) { if (err) { - db.createCollection('pms', function(errc, result) { - if (errc) - throw new Error('Failed to create the pms collection'); - - pmscol = result; - if (++step == total) callback(); - }); + db.createCollection('pms', function (errc, result) { + if (errc) + throw new Error('Failed to create the pms collection'); + + pmscol = result; + if (++step == total) callback(); + }); } else { pmscol = col; if (++step == total) callback(); } }); - - db.collection('ip', {strict:true}, function(err, col) { + + db.collection('ip', { + strict: true + }, function (err, col) { if (err) { - db.createCollection('ip', function(errc, result) { - if (errc) - throw new Error('Failed to create the ip collection'); - - ipcol = result; - if (++step == total) callback(); - }); + db.createCollection('ip', function (errc, result) { + if (errc) + throw new Error('Failed to create the ip collection'); + + ipcol = result; + if (++step == total) callback(); + }); } else { ipcol = col; if (++step == total) callback(); @@ -159,232 +171,282 @@ function createCollectionsIfNoExist(callback) { function initCollections(callback) { var step = 0; var total = 4; - - //Playlists - playlistscol.findOne({_id: 'PIDCOUNTER'}, function(err, pidobj) { - if (err) { - throw new Error('Cannot get PIDCOUNTER from playlists'); - } - - if (!pidobj) { - playlistscol.insert({_id: "PIDCOUNTER", seq: 1}, function(error, data) { - if (error) { - throw new Error('Cannot set PIDCOUNTER to playlists'); - } - if (++step == total) callback(); - }); - } else { - if (++step == total) callback(); - } - }); - - //Users - userscol.findOne({_id: 'UIDCOUNTER'}, function(err, pidobj) { - if (err) { - throw new Error('Cannot get UIDCOUNTER from users'); - } - - if (!pidobj) { - userscol.insert({_id: "UIDCOUNTER", seq: 1}, function(error, data) { - if (error) { - throw new Error('Cannot set UIDCOUNTER to users'); - } - if (++step == total) callback(); - }); - } else { - if (++step == total) callback(); - } - }); - - //Chat - chatcol.findOne({_id: 'CIDCOUNTER'}, function(err, pidobj) { - if (err) { - throw new Error('Cannot get CIDCOUNTER from chat'); - } - if (!pidobj) { - chatcol.insert({_id: "CIDCOUNTER", seq: 1}, function(error, data) { - if (error) { - throw new Error('Cannot set CIDCOUNTER to chat'); - } - if (++step == total) callback(); - }); - } else { - if (++step == total) callback(); - } - }); - - //PMs - pmscol.findOne({_id: 'PMIDCOUNTER'}, function(err, pidobj) { - if (err) { - throw new Error('Cannot get PMIDCOUNTER from pms'); - } - if (!pidobj) { - pmscol.insert({_id: "PMIDCOUNTER", seq: 1}, function(error, data) { - if (error) { - throw new Error('Cannot set PMIDCOUNTER to pms'); - } - if (++step == total) callback(); - }); - } else { - if (++step == total) callback(); - } - }); + + // Playlists + playlistscol.findOne({ + _id: 'PIDCOUNTER' + }, function (err, pidobj) { + if (err) { + throw new Error('Cannot get PIDCOUNTER from playlists'); + } + + if (!pidobj) { + playlistscol.insert({ + _id: 'PIDCOUNTER', + seq: 1 + }, function (error, data) { + if (error) { + throw new Error('Cannot set PIDCOUNTER to playlists'); + } + if (++step == total) callback(); + }); + } else { + if (++step == total) callback(); + } + }); + + // Users + userscol.findOne({ + _id: 'UIDCOUNTER' + }, function (err, pidobj) { + if (err) { + throw new Error('Cannot get UIDCOUNTER from users'); + } + + if (!pidobj) { + userscol.insert({ + _id: 'UIDCOUNTER', + seq: 1 + }, function (error, data) { + if (error) { + throw new Error('Cannot set UIDCOUNTER to users'); + } + if (++step == total) callback(); + }); + } else { + if (++step == total) callback(); + } + }); + + // Chat + chatcol.findOne({ + _id: 'CIDCOUNTER' + }, function (err, pidobj) { + if (err) { + throw new Error('Cannot get CIDCOUNTER from chat'); + } + if (!pidobj) { + chatcol.insert({ + _id: 'CIDCOUNTER', + seq: 1 + }, function (error, data) { + if (error) { + throw new Error('Cannot set CIDCOUNTER to chat'); + } + if (++step == total) callback(); + }); + } else { + if (++step == total) callback(); + } + }); + + // PMs + pmscol.findOne({ + _id: 'PMIDCOUNTER' + }, function (err, pidobj) { + if (err) { + throw new Error('Cannot get PMIDCOUNTER from pms'); + } + if (!pidobj) { + pmscol.insert({ + _id: 'PMIDCOUNTER', + seq: 1 + }, function (error, data) { + if (error) { + throw new Error('Cannot set PMIDCOUNTER to pms'); + } + if (++step == total) callback(); + }); + } else { + if (++step == total) callback(); + } + }); } function MongoDB(cb) { - var dburl = 'mongodb://' + config.db.mongoUser + ':' + config.db.mongoPassword + '@' + config.db.mongoHost + ':27017/' + config.db.mongoDatabase; - - mongodb.connect(dburl, function(err, database) { - if (err) { - throw new Error('Could not connect to database: ' + err); - } - - db = database; - - createCollectionsIfNoExist(function() { - initCollections(function() { - dbQueue(true); - }); - }); - }); -} + const dburl = `mongodb://${nconf.get('db:mongoUser')}:${nconf.get('db:mongoPassword')}@${nconf.get('db:mongoHost')}:27017/${nconf.get('db:mongoDatabase')}`; -function getNextSequence(collection, id, callback) { - dbQueue(function(){ - db.collection(collection).findOneAndUpdate({_id: id}, { $inc: { seq: 1 } }, function(err, r) { - if (err) throw new Error('Cannot update index counter'); - callback(r.value.seq); + mongodb.connect(dburl, function (err, database) { + if (err) { + throw new Error(`Could not connect to database: ${err}`); + } + + db = database; + + createCollectionsIfNoExist(() => { + initCollections(() => { + dbQueue(true); + }); }); }); } -//PlaylistDB -MongoDB.prototype.getPlaylist = function(pid, callback) { +function getNextSequence(collection, id, callback) { + dbQueue(() => { + db.collection(collection).findOneAndUpdate({ + _id: id + }, { + $inc: { + seq: 1 + } + }, (err, r) => { + if (err) throw new Error('Cannot update index counter'); + callback(r.value.seq); + }); + }); +} + +// PlaylistDB +MongoDB.prototype.getPlaylist = function (pid, callback) { var Playlist = require('./playlist'); - - dbQueue(function(){ - playlistscol.findOne({_id: pid}, {_id: 0}, function(err, data) { + + dbQueue(function () { + playlistscol.findOne({ + _id: pid + }, { + _id: 0 + }, function (err, data) { if (err || !data) { - callback('PlaylistNotFound'); - return; + callback('PlaylistNotFound'); + return; } - + var pl = new Playlist(); pl.id = pid; util._extend(pl.data, data); - + callback(err, pl); - }); + }); }); - + return this; }; -MongoDB.prototype.createPlaylist = function(owner, name, callback) { +MongoDB.prototype.createPlaylist = function (owner, name, callback) { var Playlist = require('./playlist'); - dbQueue(function(){ - getNextSequence('playlists', 'PIDCOUNTER', function(currentPID) { - var pl = new Playlist(); - - pl.id = currentPID; - pl.data.created = Date.now(); - pl.data.owner = owner; - pl.data.name = name.substr(0, 100); - - var updatedPlObj = pl.makeDbObj(); - updatedPlObj._id = currentPID; - - playlistscol.insert(updatedPlObj, function(error, data) { - callback(error, pl); - }); + dbQueue(function () { + getNextSequence('playlists', 'PIDCOUNTER', function (currentPID) { + var pl = new Playlist(); + + pl.id = currentPID; + pl.data.created = Date.now(); + pl.data.owner = owner; + pl.data.name = name.substr(0, 100); + + var updatedPlObj = pl.makeDbObj(); + updatedPlObj._id = currentPID; + + playlistscol.insert(updatedPlObj, function (error, data) { + callback(error, pl); + }); }); }); }; -MongoDB.prototype.deletePlaylist = function(pid, callback) { - dbQueue(function(){ - playlistscol.remove({_id: pid}, callback); - }); +MongoDB.prototype.deletePlaylist = function (pid, callback) { + dbQueue(function () { + playlistscol.remove({ + _id: pid + }, callback); + }); }; -MongoDB.prototype.putPlaylist = function(pid, data, callback) { +MongoDB.prototype.putPlaylist = function (pid, data, callback) { var newData = {}; util._extend(newData, data); - + newData._id = pid; - - dbQueue(function(){ - playlistscol.updateOne({_id: pid}, newData, {upsert:true, w: 1}, function(error, res) { - callback(data); + + dbQueue(function () { + playlistscol.updateOne({ + _id: pid + }, newData, { + upsert: true, + w: 1 + }, function (error, res) { + callback(data); }); }); }; -//RoomDB -MongoDB.prototype.getRoom = function(slug, callback) { - dbQueue(function(){ - roomcol.findOne({slug: slug}, {_id: 0}, callback); +// RoomDB +MongoDB.prototype.getRoom = function (slug, callback) { + dbQueue(function () { + roomcol.findOne({ + slug + }, { + _id: 0 + }, callback); }); return this; }; -MongoDB.prototype.setRoom = function(slug, val, callback) { - dbQueue(function(){ +MongoDB.prototype.setRoom = function (slug, val, callback) { + dbQueue(function () { var newData = {}; util._extend(newData, val); - + newData.slug = slug; - roomcol.updateOne({slug: slug}, newData, {upsert:true, w: 1}, function(error, data) { - if (callback) callback(error, data); + roomcol.updateOne({ + slug + }, newData, { + upsert: true, + w: 1 + }, function (error, data) { + if (callback) callback(error, data); }); }); return this; }; -//TokenDB -MongoDB.prototype.deleteToken = function(tok) { - dbQueue(function(){ - tokenscol.remove({tok: tok}, function(){}); - }); +// TokenDB +MongoDB.prototype.deleteToken = function (tok) { + dbQueue(function () { + tokenscol.remove({ + tok + }, function () {}); + }); }; -MongoDB.prototype.createToken = function(email) { +MongoDB.prototype.createToken = function (email) { var tok = DBUtils.makePass(email, Date.now()); - dbQueue(function(){ - tokenscol.insert({ - tok: tok, - email: email, + dbQueue(function () { + tokenscol.insert({ + tok, + email, time: Date.now(), - }, function() {}); - }); + }, function () {}); + }); return tok; }; -MongoDB.prototype.isTokenValid = function(tok, callback) { +MongoDB.prototype.isTokenValid = function (tok, callback) { var that = this; - dbQueue(function(){ - tokenscol.findOne({tok: tok}, function(err, data) { - if (err || data == null) { + dbQueue(function () { + tokenscol.findOne({ + tok + }, function (err, data) { + if (err || data == null) { callback('InvalidToken'); return; } - - if (config.loginExpire && (Date.now() - data.time) < expires) { + + if (nconf.get('loginExpire') && (Date.now() - data.time) < expires) { callback(null, data.email); } else { that.deleteToken(data.token); callback('InvalidToken'); } - }); + }); }); }; -//UserDB +// UserDB function addUsername(un) { usernames.push(un.toLowerCase()); } @@ -396,7 +458,7 @@ function usernameExists(un) { return ((ind = usernames.indexOf(un)) != -1 ? ind : false); } -MongoDB.prototype.createUser = function(obj, callback) { +MongoDB.prototype.createUser = function (obj, callback) { var User = require('./user'); var that = this; @@ -410,7 +472,7 @@ MongoDB.prototype.createUser = function(obj, callback) { var inData = defaultCreateObj; inData.email = inData.email.toLowerCase(); - //Validation + // Validation if (!inData.email || !DBUtils.validateEmail(inData.email)) { callback('InvalidEmail'); return; @@ -428,57 +490,58 @@ MongoDB.prototype.createUser = function(obj, callback) { return; } - dbQueue(function(){ - //Check for existing account - that.userEmailExists(inData.email, function(err, res) { + dbQueue(function () { + // Check for existing account + that.userEmailExists(inData.email, function (err, res) { if (err) { if (callback) callback('AccountExists'); return; } - - getNextSequence('users', 'UIDCOUNTER', function(currentUID) { + + getNextSequence('users', 'UIDCOUNTER', function (currentUID) { var user = new User(); - + user.data.uid = currentUID; user.data.un = inData.un; user.data.salt = DBUtils.makePass(Date.now()).slice(0, 10); user.data.pw = DBUtils.makePass(inData.pw, user.data.salt); user.data.created = Date.now(); - if (config.room.email.confirmation) user.data.confirmation = DBUtils.makePass(Date.now()); + if (nconf.get('room:email:confirmation')) { + user.data.confirmation = DBUtils.makePass(Date.now()); + } var updatedUserObj = user.makeDbObj(); updatedUserObj._id = currentUID; updatedUserObj.email = inData.email; - + var tok = that.createToken(inData.email); - - userscol.insert(updatedUserObj, function(error, data) { - if (error) { + + userscol.insert(updatedUserObj, function (error, data) { + if (error) { callback(error); return; } - - //Send confirmation email - if (config.room.email.confirmation) { + + // Send confirmation email + if (nconf.get('room:email:confirmation')) { Mailer.sendEmail('signup', { code: user.data.confirmation, user: inData.un, - }, inData.email, function(data) { + }, inData.email, function (data) { console.log(data); }); } - - //Do other ~messy~ stuff + + // Do other ~messy~ stuff addUsername(inData.un); user.login(inData.email); callback(null, user, tok); - }); + }); }); - }); }); }; -MongoDB.prototype.loginUser = function(obj, callback) { +MongoDB.prototype.loginUser = function (obj, callback) { var User = require('./user'); var that = this; @@ -491,54 +554,61 @@ MongoDB.prototype.loginUser = function(obj, callback) { var inData = defaultLoginObj; - dbQueue(function(){ + dbQueue(function () { if (inData.email && inData.pw) { inData.email = inData.email.toLowerCase(); - - userscol.findOne({email: inData.email}, {_id: 0}, function(err, data) { + + userscol.findOne({ + email: inData.email + }, { + _id: 0 + }, function (err, data) { if (err) { callback(err); return; } - + if (!data) { callback('UserNotFound'); return; } - + if (DBUtils.makePass(inData.pw, data.salt) != data.pw) { callback('IncorrectPassword'); return; } - + var tok = that.createToken(inData.email); var user = new User(); - - user.login(inData.email, data, function() { + + user.login(inData.email, data, function () { callback(null, user, tok); }); }); } else if (inData.token) { - that.isTokenValid(inData.token, function(err, email) { + that.isTokenValid(inData.token, function (err, email) { if (err) { callback(err); return; } - - userscol.findOne({email: email}, {_id: 0}, function(err, data) { + + userscol.findOne({ + email + }, { + _id: 0 + }, function (err, data) { if (err) { callback(err); return; } - + if (!data) { callback('UserNotFound'); return; } - + var user = new User(); - user.login(email, data, function() { - + user.login(email, data, function () { callback(null, user); }); }); @@ -549,58 +619,71 @@ MongoDB.prototype.loginUser = function(obj, callback) { }); }; -MongoDB.prototype.putUser = function(email, data, callback) { +MongoDB.prototype.putUser = function (email, data, callback) { var newData = {}; util._extend(newData, data); - + newData._id = data.uid; newData.email = email; - - dbQueue(function(){ - userscol.updateOne({email: email}, newData, {upsert: true, w: 1}, callback); + + dbQueue(function () { + userscol.updateOne({ + email + }, newData, { + upsert: true, + w: 1 + }, callback); }); }; -MongoDB.prototype.getUser = function(email, callback){ - var User = require('./user'); +MongoDB.prototype.getUser = function (email, callback) { + var User = require('./user'); - dbQueue(function(){ - userscol.findOne({email: email}, {_id: 0}, function(err, data) { - if (err) { + dbQueue(function () { + userscol.findOne({ + email + }, { + _id: 0 + }, function (err, data) { + if (err) { callback(err); return; } - + if (!data) { callback('UserNotFound'); return; } - - var user = new User(); - - user.login(email, data, function(){ - - callback(null, user); - }); - }); + + var user = new User(); + + user.login(email, data, function () { + callback(null, user); + }); + }); }); }; -MongoDB.prototype.deleteUser = function(email, callback) { +MongoDB.prototype.deleteUser = function (email, callback) { var that = this; - - dbQueue(function(){ - that.getUser(email, function(err, user){ - if (err){ if (callback) callback(err); return; } - - userscol.remove({email: email}, function(error, data){ - callback(error || null, error ? false : true); - }); - }); + + dbQueue(function () { + that.getUser(email, function (err, user) { + if (err) { + if (callback) callback(err); + return; + } + + userscol.remove({ + email + }, function (error, data) { + callback(error || null, error ? false : true); + }); + }); }); }; -MongoDB.prototype.getUserByUid = function(uid, opts, callback) { +MongoDB.prototype.getUserByUid = function (uid, opts, callback) { var User = require('./user'); if (typeof opts === 'function') { @@ -618,32 +701,38 @@ MongoDB.prototype.getUserByUid = function(uid, opts, callback) { } var isArray = Array.isArray(uid); - + if (!Array.isArray(uid)) uid = [uid]; - + var out = {}; var len = 0; - dbQueue(function(){ - userscol.find({_id: { $in: uid}}, {_id: 0}).toArray(function(err, data) { - if(err || !data || data.length == 0){ + dbQueue(function () { + userscol.find({ + _id: { + $in: uid + } + }, { + _id: 0 + }).toArray(function (err, data) { + if (err || !data || data.length == 0) { callback('SomeUsersNotFound', out); return; } - - data.forEach(function(userobj) { + + data.forEach(function (userobj) { var user = new User(); - - user.login(userobj.email, userobj, opts, function(){ + + user.login(userobj.email, userobj, opts, function () { if (isArray) out[userobj.uid] = user; else out = user; - - console.log("Initialized user " + user.email); - if(++len == data.length){ - if(uid.length == data.length) callback(null, out); + + console.log('Initialized user ' + user.email); + if (++len == data.length) { + if (uid.length == data.length) callback(null, out); else callback('SomeUsersNotFound', out); } }); @@ -652,70 +741,104 @@ MongoDB.prototype.getUserByUid = function(uid, opts, callback) { }); }; -MongoDB.prototype.getUserByName = function(name, opts, callback) { +MongoDB.prototype.getUserByName = function (name, opts, callback) { var User = require('./user'); if (typeof opts === 'function') { callback = opts; opts = {}; } - - dbQueue(function(){ - userscol.findOne({un: name}, {_id: 0}, function(err, userobj) { - if(err || !userobj){ + + dbQueue(function () { + userscol.findOne({ + un: name + }, { + _id: 0 + }, function (err, userobj) { + if (err || !userobj) { if (callback) callback('UserNotFound'); return; } - + var user = new User(); - - user.login(userobj.email, userobj, opts, function() { + + user.login(userobj.email, userobj, opts, function () { if (callback) callback(null, user); }); }); }); }; -MongoDB.prototype.userEmailExists = function(key, callback) { - dbQueue(function(){ - userscol.findOne({email: key}, {_id: 0}, function(err, data) { +MongoDB.prototype.userEmailExists = function (key, callback) { + dbQueue(function () { + userscol.findOne({ + email: key + }, { + _id: 0 + }, function (err, data) { if (callback) callback(err, data ? true : false); }); }); }; -//ChatDB -MongoDB.prototype.logChat = function(uid, msg, special, callback) { - dbQueue(function(){ - getNextSequence('chat', 'CIDCOUNTER', function(currentCID) { - chatcol.insert({_id: currentCID, uid: uid, msg: msg, special: special}, function(error, data) { - if (callback) callback(error, currentCID); - }); +// ChatDB +MongoDB.prototype.logChat = function (uid, msg, special, callback) { + dbQueue(function () { + getNextSequence('chat', 'CIDCOUNTER', function (currentCID) { + chatcol.insert({ + _id: currentCID, + uid, + msg, + special + }, function (error, data) { + if (callback) callback(error, currentCID); + }); }); }); }; -//PmDB -MongoDB.prototype.logPM = function(from, to, msg, callback) { - dbQueue(function(){ - getNextSequence('pms', 'PMIDCOUNTER', function(currentCID) { - pmscol.insert({_id: currentCID, msg: msg, from: from, to: to, time: new Date(), unread: true }, function(error, data) { - if (error) log.error("Error logging chat message"); - if (callback) callback(error, currentCID); - }); +// PmDB +MongoDB.prototype.logPM = function (from, to, msg, callback) { + dbQueue(function () { + getNextSequence('pms', 'PMIDCOUNTER', function (currentCID) { + pmscol.insert({ + _id: currentCID, + msg, + from, + to, + time: new Date(), + unread: true + }, function (error, data) { + if (error) log.error('Error logging chat message'); + if (callback) callback(error, currentCID); + }); }); }); }; -MongoDB.prototype.getConversation = function(from, to, callback) { - dbQueue(function(){ - pmscol.find({ $or: [ {from: from, to: to}, {from: to, to: from}] }, {_id: 0}).toArray(function(err, data) { - if(err){ +MongoDB.prototype.getConversation = function (from, to, callback) { + dbQueue(function () { + pmscol.find({ + $or: [{ + from, + to + }, { + from: to, + to: from + }] + }, { + _id: 0 + }).toArray(function (err, data) { + if (err) { callback(err); } else { var out = []; - for(var key in data){ - out.push({message:data[key].msg,time:data[key].time,from:data[key].from}); + for (var key in data) { + out.push({ + message: data[key].msg, + time: data[key].time, + from: data[key].from + }); } callback(null, out); } @@ -723,19 +846,27 @@ MongoDB.prototype.getConversation = function(from, to, callback) { }); }; -MongoDB.prototype.getConversations = function(uid, callback) { +MongoDB.prototype.getConversations = function (uid, callback) { var that = this; - - dbQueue(function(){ - pmscol.find({ $or: [ {from: uid}, {to: uid}] }, {_id: 0}).toArray(function(err, data) { - if(err){ + + dbQueue(function () { + pmscol.find({ + $or: [{ + from: uid + }, { + to: uid + }] + }, { + _id: 0 + }).toArray(function (err, data) { + if (err) { callback(err); } else { var out = {}; var uids = []; for (var key in data) { var otherUid = data[key].to == uid ? data[key].from : data[key].to; - + if (out[otherUid] === undefined) { uids.push(otherUid); out[otherUid] = { @@ -744,14 +875,18 @@ MongoDB.prototype.getConversations = function(uid, callback) { unread: 0 }; } - out[otherUid].messages.push({ message: data[key].msg, time: data[key].time, from: data[key].from }); - + out[otherUid].messages.push({ + message: data[key].msg, + time: data[key].time, + from: data[key].from + }); + if (data[key].unread && data[key].from != uid) out[otherUid].unread++; } - + if (uids.length > 0) { - that.getUserByUid(uids, function(err, result){ + that.getUserByUid(uids, function (err, result) { if (err) { callback(err); } else { @@ -763,8 +898,7 @@ MongoDB.prototype.getConversations = function(uid, callback) { callback(null, out); } }); - } - else { + } else { callback(null, out); } } @@ -772,30 +906,51 @@ MongoDB.prototype.getConversations = function(uid, callback) { }); }; -MongoDB.prototype.markConversationRead = function(uid, uid2, time) { - dbQueue(function(){ - pmscol.updateMany({to: uid, from: uid2, time: {$lt: new Date(time)}}, {$set: {unread: false}}, function(){}); +MongoDB.prototype.markConversationRead = function (uid, uid2, time) { + dbQueue(function () { + pmscol.updateMany({ + to: uid, + from: uid2, + time: { + $lt: new Date(time) + } + }, { + $set: { + unread: false + } + }, function () {}); }); }; -//IpDB -MongoDB.prototype.logIp = function(address, uid) { - dbQueue(function(){ +// IpDB +MongoDB.prototype.logIp = function (address, uid) { + dbQueue(function () { ipcol.insert({ - uid: uid, - address: address, + uid, + address, time: new Date() }); }); }; -MongoDB.prototype.getIpHistory = function(uid, callback) { - dbQueue(function(){ - ipcol.find({uid: uid}, {_id: 0, uid: 0}).toArray(function(err, data) { - if(err) +MongoDB.prototype.getIpHistory = function (uid, callback) { + dbQueue(function () { + ipcol.find({ + uid + }, { + _id: 0, + uid: 0 + }).toArray(function (err, data) { + if (err) callback(err); else - callback(null, data.sort(function(a, b){ return a.address > b.address; }).reverse().filter(function(e, i, a){ return i == 0 || a[i - 1].address != e.address; }).sort(function(a, b){ return a.time < b.time; })); + callback(null, data.sort(function (a, b) { + return a.address > b.address; + }).reverse().filter(function (e, i, a) { + return i == 0 || a[i - 1].address != e.address; + }).sort(function (a, b) { + return a.time < b.time; + })); }); }); }; diff --git a/socketserver/db_mysql.js b/socketserver/db_mysql.js index 244d095..d1727fe 100644 --- a/socketserver/db_mysql.js +++ b/socketserver/db_mysql.js @@ -1,14 +1,14 @@ //Modules -var mysql = require('mysql'); -var util = require('util'); -var _ = require('underscore'); -var log = new(require('basic-logger'))({ +const mysql = require('mysql'); +const util = require('util'); +const _ = require('underscore'); +const log = new(require('basic-logger'))({ showTimestamp: true, prefix: "MysqlDB" }); +const nconf = require('nconf'); //Files -var config = require('../serverconfig.js'); var Hash = require('./hash'); var Mailer = require('./mailer'); var DBUtils = require('./database_util'); @@ -21,10 +21,10 @@ var MysqlDB = function(){ var that = this; var mysqlConfig = { - host: config.db.mysqlHost, - user: config.db.mysqlUser, - password: config.db.mysqlPassword, - database: config.db.mysqlDatabase, + host: nconf.get('db:mysqlHost'), + user: nconf.get('db:mysqlUser'), + password: nconf.get('db:mysqlPassword'), + database: nconf.get('db:mysqlDatabase'), charset: "UTF8_GENERAL_CI", multipleStatements: true, connectionLimit: 1, @@ -443,7 +443,7 @@ MysqlDB.prototype.createToken = function(email) { }; MysqlDB.prototype.isTokenValid = function(tok, callback) { - this.execute("SELECT `token`, `email` FROM `tokens` WHERE ? AND DATEDIFF(NOW(), `created`) < ?;", [{ token: tok, }, config.loginExpire || 365], function(err, res) { + this.execute("SELECT `token`, `email` FROM `tokens` WHERE ? AND DATEDIFF(NOW(), `created`) < ?;", [{ token: tok, }, nconf.get('loginExpire') || 365], function(err, res) { if (err || res.length == 0) { callback('InvalidToken'); return; @@ -550,7 +550,7 @@ MysqlDB.prototype.createUser = function(obj, callback) { user.data.salt = DBUtils.makePass(Date.now()).slice(0, 10); user.data.pw = DBUtils.makePass(inData.pw, user.data.salt); user.data.created = Date.now(); - if (config.room.email.confirmation) user.data.confirmation = DBUtils.makePass(Date.now()); + if (nconf.get('room:email:confirmation')) user.data.confirmation = DBUtils.makePass(Date.now()); var updatedUserObj = user.makeDbObj(); var tok = that.createToken(inData.email); @@ -564,7 +564,7 @@ MysqlDB.prototype.createUser = function(obj, callback) { } //Send confirmation email - if (config.room.email.confirmation) { + if (nconf.get('room:email:confirmation')) { Mailer.sendEmail('signup', { code: user.data.confirmation, user: inData.un, diff --git a/socketserver/djqueue.js b/socketserver/djqueue.js index 97182f2..3c18973 100644 --- a/socketserver/djqueue.js +++ b/socketserver/djqueue.js @@ -1,5 +1,5 @@ var Roles = require('./role'); -var config = require('../serverconfig'); +const nconf = require('nconf'); var defaultVoteObj = function(){ return { @@ -30,9 +30,9 @@ function djqueue(room){ this.currentsong = null; this.songstart = null; this.lasttimer = null; - this.limit = config.room.queue.limit; - this.cycle = config.room.queue.cycle; - this.lock = config.room.queue.lock; + this.limit = nconf.get('room:queue:limit'); + this.cycle = nconf.get('room:queue:cycle'); + this.lock = nconf.get('room:queue:lock'); this.votes = new defaultVoteObj; } diff --git a/socketserver/mailer.js b/socketserver/mailer.js index 914488d..c5e3e1f 100644 --- a/socketserver/mailer.js +++ b/socketserver/mailer.js @@ -1,13 +1,13 @@ var NM = require('nodemailer'); var util = require('util'); -var config = require('../serverconfig'); var xoauth2 = require('xoauth2'); var fs = require('fs'); +const nconf = require('nconf'); function Mailer(){ - //Check if we need to authorize against email server - if(config.room.allowrecovery || config.room.email.confirmation){ - var opts = config.room.email.options; + // Check if we need to authorize against email server + if (nconf.get('room:allowrecovery') || nconf.get('room:email:confirmation')) { + const opts = nconf.get('room:email:options'); this.trans = NM.createTransport(((opts || {}).auth || {}).xoauth2 ? util._extend(opts, { auth: { xoauth2: xoauth2.createXOAuth2Generator(opts.auth.xoauth2), @@ -32,10 +32,10 @@ Mailer.prototype.makeEmailObj = function(type, receiver, opts){ //Return email options return { - from: config.room.email.sender, - to: receiver, - subject: type.subject, - html: type.body, + from: nconf.get('room:email:sender'), + to: receiver, + subject: type.subject, + html: type.body, }; }; diff --git a/socketserver/role.js b/socketserver/role.js index 47207af..e7805ac 100644 --- a/socketserver/role.js +++ b/socketserver/role.js @@ -1,5 +1,7 @@ -var config = require('../serverconfig.js'); -var roles = config.roles; +// eslint-disable-next-line +'use strict'; +const nconf = require('nconf'); +var roles = nconf.get('roles'); // Caching the value so we don't have to loop through something every login var roleOrder = null; @@ -53,20 +55,21 @@ Role.prototype.checkCanGrant = function(inRole, inPerm){ return false; }; -Role.prototype.makeClientObj = function(){ +Role.prototype.makeClientObj = function () { return roles; }; -Role.prototype.getOrder = function(){ +Role.prototype.getOrder = function () { if (roleOrder) return roleOrder; - - if (config.roleOrder && Array.isArray(config.roleOrder)){ + + let roleOrderTemp = nconf.get('roleOrder'); + if (roleOrderTemp && Array.isArray(roleOrderTemp)){ for (var i in roles){ - if (config.roleOrder.indexOf(i) == -1) config.roleOrder.push(i); + if (roleOrderTemp.indexOf(i) == -1) roleOrderTemp.push(i); } - - roleOrder = config.roleOrder; - return config.roleOrder; + + roleOrder = roleOrderTemp; + return roleOrderTemp; } var temp = []; @@ -82,8 +85,8 @@ Role.prototype.getOrder = function(){ Role.prototype.getStaffRoles = function(){ if(staffRoles) return staffRoles; - if (config.staffRoles && Array.isArray(config.staffRoles)) { - staffRoles = config.staffRoles; + if (nconf.get('staffRoles') && Array.isArray(nconf.get('staffRoles'))) { + staffRoles = nconf.get('staffRoles'); return staffRoles; } return []; @@ -91,4 +94,4 @@ Role.prototype.getStaffRoles = function(){ -module.exports = new Role(); \ No newline at end of file +module.exports = new Role(); diff --git a/socketserver/room.js b/socketserver/room.js index 07b28ae..0c80083 100644 --- a/socketserver/room.js +++ b/socketserver/room.js @@ -5,8 +5,8 @@ var http = require('http'); var log = new (require('basic-logger'))({showTimestamp: true, prefix: "Room"}); var DJQueue = require('./djqueue.js'); var Roles = require('./role'); -var config = require('../serverconfig'); var DB = require('./database'); +const nconf = require('nconf'); var defaultDBObj = function(){ return { @@ -74,7 +74,7 @@ Room.prototype.getRoomMeta = function(){ }; Room.prototype.makeOwner = function(){ - if (!config.room.ownerEmail) return; + if (!nconf.get('room:ownerEmail')) return; var that = this; @@ -523,7 +523,7 @@ Room.prototype.sendMessage = function( sock, message, ext, specdata, callback ){ time: Date.now(), cid: cid, }); - if(that.lastChat.length > config.room.lastmsglimit) that.lastChat.shift(); + if(that.lastChat.length > nconf.get('room:lastmsglimit')) that.lastChat.shift(); } callback(cid); @@ -608,13 +608,13 @@ Room.prototype.getUsersObj = function(){ }; Room.prototype.getHistoryObj = function() { - return this.data.history.slice(-config.room.history.limit_send).reverse(); + return this.data.history.slice(-nconf.get('room:history:limit_send')).reverse(); }; Room.prototype.addToHistory = function(historyObj) { //Limit history - if(config.room.history.limit_save !== 0) - while(this.data.history.length >= config.room.history.limit_save) { + if(nconf.get('room:history:limit_save') !== 0) + while (this.data.history.length >= nconf.get('room:history:limit_save')) { this.data.history.shift(); } @@ -624,12 +624,12 @@ Room.prototype.addToHistory = function(historyObj) { }; Room.prototype.updateLobbyServer = function(song, dj, callback) { - if (!config.apis.musiqpad.sendLobbyStats) { + if (!nconf.get('apis:musiqpad:sendLobbyStats')) { if (callback) callback(); return; } - else if (!config.apis.musiqpad.key || config.apis.musiqpad.key == "") { - throw "A musiqpad key must be defined in the config for updating the lobby server."; + else if (!nconf.get('apis:musiqpad:key') || nconf.get('apis:musiqpad:key') == "") { + console.log("A musiqpad key must be defined in the config for updating the lobby server."); return; } var postData = { @@ -645,7 +645,7 @@ Room.prototype.updateLobbyServer = function(song, dj, callback) { method: 'POST', headers: { 'Content-Type': 'application/json', - 'apikey': config.apis.musiqpad.key + 'apikey': nconf.get('apis:musiqpad:key') } }; try { diff --git a/socketserver/socketserver.js b/socketserver/socketserver.js index d737e0c..ff1fcd5 100644 --- a/socketserver/socketserver.js +++ b/socketserver/socketserver.js @@ -1,3 +1,5 @@ +// eslint-disable-next-line +'use strict'; //Modules var ws = require('ws'); var http = require('http'); @@ -8,9 +10,10 @@ var util = require('util'); var extend = require('extend'); var updateNotifier = require('update-notifier'); var _ = require('underscore'); +const fs = require('fs-extra'); +const nconf = require('nconf'); //Files -var config = require('../serverconfig'); var DB = require("./database"); var Room = require('./room'); var User = require('./user'); @@ -195,23 +198,27 @@ var SocketServer = function(server){ if (server){ settings.server = server; }else{ - var port = config.socketServer.port || undefined; - var ip = config.socketServer.host || undefined; + var port = nconf.get('socketServer:port') || undefined; + var ip = nconf.get('socketServer:host') || undefined; - if (config.certificate && config.certificate.key && config.certificate.cert){ - settings.server = https.createServer(config.certificate).listen(port,ip); - }else{ + if (nconf.get('useSSL') && nconf.get('certificate') && nconf.get('certificate:key') && nconf.get('certificate:cert')) { + let certificates = { + key: fs.readFileSync(nconf.get('certificate:key')), + cert: fs.readFileSync(nconf.get('certificate:cert')), + } + settings.server = https.createServer(certificates).listen(port, ip); + } else { settings.server = http.createServer().listen(port,ip); } } this.wss = new WebSocketServer(settings); - log.info('Socket server listening on port ' + (config.socketServer.port || config.webServer.port)); + log.info('Socket server listening on port ' + (nconf.get('socketServer:port') || nconf.get('webServer:port'))); // this.wss = new WebSocketServer({ port: config.socketServer.port }); // log.info('Socket server listening on port ' + config.socketServer.port); - this.room = new Room(this, config.room); + this.room = new Room(this, nconf.get('room')); // Keepalive packets. This.... is messy. setInterval( function(){ @@ -361,7 +368,7 @@ var SocketServer = function(server){ } else if(socket.room && that.room.isUserRestricted(socket.user.uid, 'BAN')){ returnObj.data = { error: 'UserBanned' }; - } else if((Date.now() - socket.user.created) <= config.room.signupcd){ + } else if((Date.now() - socket.user.created) <= nconf.get('room:signupcd')){ returnObj.data = { error: 'UserOnCooldown' }; } else if(socket.user.confirmation){ @@ -401,7 +408,7 @@ var SocketServer = function(server){ } */ //Check if recovery is enabled - if (!(config.room.allowrecovery)){ + if (!(nconf.get('room:allowrecovery'))){ returnObj.data = { error: 'RecoveryDisabled' }; @@ -664,16 +671,16 @@ var SocketServer = function(server){ votes: that.room.queue.makeVoteObj(), vote: that.room.queue.getUserVote( socket ), }, - historylimit: config.room.history.limit_send, + historylimit: nconf.get('room:history:limit_send'), roles: Roles.makeClientObj(), roleOrder: Roles.getOrder(), staffRoles: Roles.getStaffRoles(), - lastChat: ((!socket.user && !config.room.guestCanSeeChat) || (that.room.isUserRestricted((socket.user || {}).uid, 'BAN') && !config.room.bannedCanSeeChat)) ? [] : that.room.makePrevChatObj(), + lastChat: ((!socket.user && !nconf.get('room:guestCanSeeChat')) || (that.room.isUserRestricted((socket.user || {}).uid, 'BAN') && !nconf.get('room:bannedCanSeeChat'))) ? [] : that.room.makePrevChatObj(), time: new Date().getTime(), - captchakey: config.apis.reCaptcha.key, - allowemojis: config.room.allowemojis, - description: config.room.description, - recaptcha: config.room.recaptcha, + captchakey: nconf.get('apis:reCaptcha:key'), + allowemojis: nconf.get('room:allowemojis'), + description: nconf.get('room:description'), + recaptcha: nconf.get('room:recaptcha'), }; socket.sendJSON(returnObj); @@ -1380,12 +1387,12 @@ var SocketServer = function(server){ if(data.type == 'login'){ DB.loginUser(data.data, callback); } else { - if(config.room.recaptcha){ + if (nconf.get('room:recaptcha')) { request.post( 'https://www.google.com/recaptcha/api/siteverify', { form: { - secret: config.apis.reCaptcha.secret, + secret: nconf.get('apis:reCaptcha:secret'), response: data.data.captcha, remoteip: socket.upgradeReq.connection.remoteAddress, } diff --git a/start.js b/start.js index df8d1b8..3b7357a 100644 --- a/start.js +++ b/start.js @@ -1,74 +1,79 @@ -var config = require('./serverconfig'); -var fs = require('fs'); -var SocketServer = require("./socketserver/socketserver"); -var log = new(require('basic-logger'))({ - showTimestamp: true, - prefix: "SocketServer" -}); -var path = require('path'); +// eslint-disable-next-line +'use strict'; +// NCONF +const nconf = require('nconf'); +const fs = require('fs-extra'); +const hjson = require('hjson'); -if(!config.setup){ - log.error("Please, setup your server by editing the 'serverconfig.js' file"); - return; +const hjsonWrapper = { + parse: (text) => hjson.parse(text, { keepWsc: true, }), + stringify: (text) => hjson.stringify(text, { keepWsc: true, quotes: 'always', bracesSameLine: true }), +}; +if (!fileExistsSync('config.hjson')) { + fs.copySync('config.example.hjson', 'config.hjson'); } +nconf.argv().env().file({ file: 'config.hjson', format: hjsonWrapper }); -var server = null; +// Modules +const SocketServer = require('./socketserver/socketserver'); +const path = require('path'); +const webserver = require('./webserver/app'); +const log = new(require('basic-logger'))({ + showTimestamp: true, + prefix: 'SocketServer', +}); +let server; -var webConfig = '// THIS IS AN AUTOMATICALLY GENERATED FILE\n\nvar config=JSON.parse(\'' + JSON.stringify( - { - useSSL: config.useSSL, - serverPort: config.socketServer.port, - selfHosted: true, - serverHost: config.socketServer.host - } - ) + '\')'; +const webConfig = `// THIS IS AN AUTOMATICALLY GENERATED FILE\n\nvar config=JSON.parse('${JSON.stringify({ + useSSL: nconf.get('useSSL'), + serverPort: nconf.get('socketServer:port'), + selfHosted: !0, + serverHost: nconf.get('socketServer:host') +})}')`; -if (config.hostWebserver){ - fs.writeFileSync(path.join(__dirname, '/webserver/public/lib/js', 'webconfig.js'), webConfig); - var webserver = require('./webserver/app'); - server = (config.socketServer.port == config.webServer.port || config.socketServer.port == '') ? webserver.server : null; +if (nconf.get('hostWebserver')) { + fs.writeFileSync(path.join(__dirname, '/webserver/public/lib/js', 'webconfig.js'), webConfig); + server = (nconf.get('socketServer:port') === nconf.get('webServer:port') || nconf.get('socketServer:port') === '') ? webserver.server : null; } -if (config.apis.musiqpad.sendLobbyStats && (!config.apis.musiqpad.key || config.apis.musiqpad.key == '')) { - throw 'In order to send stats to the lobby you must generate an key here: https://musiqpad.com/lounge'; +if (nconf.get('apis:musiqpad:sendLobbyStats') && (!nconf.get('apis:musiqpad:key') || nconf.get('apis:musiqpad:key') === '')) { + log.error('In order to send stats to the lobby you must generate an key here: https://musiqpad.com/lounge'); + process.exit(); } fs.writeFileSync(path.join(__dirname, '', 'webconfig.js'), webConfig); -var socketServer = new SocketServer(server); +const socketServer = new SocketServer(server); -process.on('uncaughtException', function(err) { +process.on('uncaughtException', (err) => { console.log(err); console.log(err.stack); socketServer.gracefulExit(); }); process.on('exit', socketServer.gracefulExit); - -//catches ctrl+c event process.on('SIGINT', socketServer.gracefulExit); -function fileExistsSync() { - var exists = false; - try { - exists = fs.statSync(path); - } catch(err) { - exists = false; - } - - return !!exists; -} - -if(process.argv[2] === "--daemon") { - if (fileExistsSync(__dirname + '/pidfile')) { +if (process.argv[2] === '--daemon') { + if (fileExistsSync(`${__dirname}/pidfile`)) { try { - var pid = fs.readFileSync(__dirname + '/pidfile', { encoding: 'utf-8' }); + const pid = fs.readFileSync(`${__dirname}/pidfile`, { encoding: 'utf-8' }); process.kill(pid, 0); process.exit(); } catch (e) { - fs.unlinkSync(__dirname + '/pidfile'); + fs.unlinkSync(`${__dirname}/pidfile`); } } - fs.writeFile(__dirname + '/pidfile', process.pid); -} \ No newline at end of file + fs.writeFile(`${__dirname}/pidfile`, process.pid); +} + +function fileExistsSync(path) { + let exists = false; + try { + exists = fs.statSync(path); + } catch (err) { + exists = false; + } + return !!exists; +} diff --git a/webserver/app.js b/webserver/app.js index 3fcfb52..9cf49fd 100644 --- a/webserver/app.js +++ b/webserver/app.js @@ -4,17 +4,22 @@ var path = require('path'); var http = require('http'); var https = require('https'); var fs = require('fs'); -var config = require('../serverconfig.js'); +const nconf = require('nconf'); var app = express(); var server = null; var server2 = null; var socketServer = null; -if (config.certificate && config.certificate.key && config.certificate.cert){ - server = https.createServer(config.certificate, app); - if(config.webServer.redirectHTTP && config.webServer.redirectPort != '') +if (nconf.get('useSSL') && nconf.get('certificate') && nconf.get('certificate:key') && nconf.get('certificate:cert')) { + const certificate = { + key: fs.readFileSync(nconf.get('certificate:key')), + cert: fs.readFileSync(nconf.get('certificate:cert')), + }; + server = https.createServer(certificate, app); + if (nconf.get('webServer:redirectHTTP') && nconf.get('webServer:redirectPort') !== '') { server2 = http.createServer(app); + } } else { server = http.createServer(app); @@ -22,10 +27,10 @@ else { app.use(compression()); -if(config.webServer.redirectHTTP) +if(nconf.get('webServer:redirectHTTP')) app.use(function(req, res, next) { if(!req.secure) { - return res.redirect(['https://', req.hostname, ":", config.webServer.port || process.env.PORT, req.url].join('')); + return res.redirect(['https://', req.hostname, ":", nconf.get('webServer:port') || process.env.PORT, req.url].join('')); } next(); }); @@ -38,8 +43,8 @@ app.get('/config', function(req, res) { }); app.get('/api/room', function(req, res) { var roomInfo = { - "slug": config.room.slug, - "name": config.room.name, + "slug": nconf.get('room:slug'), + "name": nconf.get('room:name'), "people": null, "queue": null, "media": null, @@ -47,13 +52,13 @@ app.get('/api/room', function(req, res) { res.send(roomInfo); }); -server.listen(config.webServer.port || process.env.PORT, config.webServer.address || process.env.IP, function(){ +server.listen(nconf.get('webServer:port') || process.env.PORT, nconf.get('webServer:address') || process.env.IP, function(){ var addr = server.address(); console.log("Webserver listening at", addr.address + ":" + addr.port); }); if(server2 != null){ - server2.listen(config.webServer.redirectPort || 80, config.webServer.address || process.env.IP, function(){ + server2.listen(nconf.get('webServer:redirectPort') || 80, nconf.get('webServer:address') || process.env.IP, function(){ var addr2 = server2.address(); console.log("HTTP Webserver listening at", addr2.address + ":" + addr2.port); });