diff --git a/README.md b/README.md index 84e1e48..0a21975 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,10 @@ The base for creating a self-hosted pad. 2. Download the [latest stable version](https://github.com/musiqpad/mqp-server/releases/latest) 3. Unzip it in the location you want to install 4. Open a terminal and `npm install --production` it -5. Copy the `serverconfig.example.js` to create the file `serverconfig.js` -6. Start the server by running `npm start` -7. If everything went well, there should be no error messages. +5. Start the server by running `npm start` +6. If everything went well, there should be no error messages! + +To change the settings, edit the config.hjson file! If you want to start musiqpad using an application manager like forever, start the app.js file. To see server logs, run `npm run log` You can also download the latest pre-release [here](https://github.com/musiqpad/mqp-server/releases) (rc = release candidate, exp = experimental) @@ -57,7 +58,8 @@ Params: debug: false, stream: false } - } + }, + config: fs.readFileSync('config.hjson'), // example config: config.example.hjson } ``` diff --git a/config.example.hjson b/config.example.hjson index d35b084..52fac31 100644 --- a/config.example.hjson +++ b/config.example.hjson @@ -60,6 +60,18 @@

Pad Description

Here you can put anything you want in HTML! ''' + tags: { // Tags for Google & co + keywords: "musiqpad" + description: "" + image: "https://cdn.musiqpad.com/img/icon-256.png" // Image on twitter/facebook/slack/... + twitter: "@musiqpad" + description: // A one to two sentence description for search engines & co + ''' + + ''' + themeColor: "" // a hex color for the theme on chrome for android + favicon: "/pads/lib/img/icon.png" + } } apis: { YT: { @@ -154,7 +166,6 @@ "bot" ] - /* @@ -175,14 +186,14 @@ */ // Defines roles and permissions - - owner: { // REQUIRED ROLE - title: "Owner" - showtitle: true - style: { - color: "#F46B40" - } - permissions: [ + roles: { + owner: { // REQUIRED ROLE + title: "Owner" + showtitle: true + style: { + color: "#F46B40" + } + permissions: [ "djqueue.join" "djqueue.joinlocked" "djqueue.leave" @@ -214,23 +225,23 @@ "room.whois" "room.whois.iphistory" "server.checkForUpdates" - ] - canGrantRoles: [ + ] + canGrantRoles: [ "dev" "coowner" "supervisor" "bot" "regular" "default" - ] - } - dev: { // OPTIONAL ROLE FOR MUSIQPAD DEVS - title: "Dev" - showtitle: true - style: { - color: "#A77DC2" + ] } - permissions: [ + dev: { // OPTIONAL ROLE FOR MUSIQPAD DEVS + title: "Dev" + showtitle: true + style: { + color: "#A77DC2" + } + permissions: [ "djqueue.join" "djqueue.joinlocked" "djqueue.leave" @@ -260,24 +271,24 @@ "room.restrict.mute_silent" "room.ratelimit.bypass" "room.whois" - ] - canGrantRoles: [ + ] + canGrantRoles: [ "dev" "coowner" "supervisor" "bot" "regular" "default" - ] - mention: "devs" - } - coowner: { - title: "Co-owner" - showtitle: true - style: { - color: "#89BE6C" + ] + mention: "devs" } - permissions: [ + coowner: { + title: "Co-owner" + showtitle: true + style: { + color: "#89BE6C" + } + permissions: [ "djqueue.join" "djqueue.joinlocked" "djqueue.leave" @@ -308,21 +319,21 @@ "room.ratelimit.bypass" "room.whois" "room.whois.iphistory" - ] - canGrantRoles: [ + ] + canGrantRoles: [ "supervisor" "bot" "regular" "default" - ] - } - supervisor: { - title: "Supervisor" - showtitle: true - style: { - color: "#009CDD" + ] } - permissions: [ + supervisor: { + title: "Supervisor" + showtitle: true + style: { + color: "#009CDD" + } + permissions: [ "djqueue.join" "djqueue.joinlocked" "djqueue.leave" @@ -350,20 +361,20 @@ "room.restrict.mute_silent" "room.ratelimit.bypass" "room.whois" - ] - canGrantRoles: [ + ] + canGrantRoles: [ "regular" "default" - ] - } - bot: { - title: "Bot" - showtitle: true - badge: "android" - style: { - color: "#964B74" + ] } - permissions: [ + bot: { + title: "Bot" + showtitle: true + badge: "android" + style: { + color: "#964B74" + } + permissions: [ "djqueue.skip.other" "djqueue.lock" "djqueue.cycle" @@ -375,17 +386,17 @@ "room.restrict.mute" "room.restrict.mute_silent" "room.ratelimit.bypass" - ] - canGrantRoles: [ - ] - } - regular: { - title: "Regular" - showtitle: false - style: { - color: "#925AFF" + ] + canGrantRoles: [ + ] } - permissions: [ + regular: { + title: "Regular" + showtitle: false + style: { + color: "#925AFF" + } + permissions: [ "djqueue.join" "djqueue.joinlocked" "djqueue.leave" @@ -396,17 +407,17 @@ "playlist.delete" "playlist.rename" "playlist.import" - ] - canGrantRoles: [ - ] - } - default: { // REQUIRED ROLE - title: "Default" - showtitle: false - style: { - color: "#ffffff" + ] + canGrantRoles: [ + ] } - permissions: [ + default: { // REQUIRED ROLE + title: "Default" + showtitle: false + style: { + color: "#ffffff" + } + permissions: [ "djqueue.join" "djqueue.leave" "chat.send" @@ -416,8 +427,9 @@ "playlist.delete" "playlist.rename" "playlist.import" - ] - canGrantRoles: [ - ] + ] + canGrantRoles: [ + ] + } } } \ No newline at end of file diff --git a/package.json b/package.json index f6197d6..20b30e3 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "deasync": "^0.1.4", "download-git-repo": "^0.1.2", "durationjs": "^1.1.1", + "ejs": "^2.4.2", "express": "^4.13.3", "extend": "^3.0.0", "file-tail": "^0.3.0", diff --git a/server-package.js b/server-package.js index 5fb5553..bb5937f 100644 --- a/server-package.js +++ b/server-package.js @@ -5,6 +5,7 @@ var log = new(require('basic-logger'))({ showTimestamp: true, prefix: "ServerContainer" }); +var fs = require('fs'); var extend = require('extend'); @@ -23,8 +24,11 @@ var server = function (params) { } } extend(true, this.settings, params); - + this.start = function() { + if(this.settings.config) { + fs.writeFileSync('./config.hjson', this.settings.config, 'utf8'); + } if (this.settings.forever.enabled) { forever.load(this.settings.forever.options); that.pid = forever.start('./start.js'); @@ -35,11 +39,11 @@ var server = function (params) { }); } }; - + this.stop = function() { stopServer(); }; - + function stopServer() { if (that.settings.forever.enabled) { forever.stop(); diff --git a/serverconfig.example.js b/serverconfig.example.js deleted file mode 100644 index df0d5e5..0000000 --- a/serverconfig.example.js +++ /dev/null @@ -1,418 +0,0 @@ -var fs = require('fs'); -var config = {}; - -// IMPORTANT: In order to be able to launch the musiqpad server, set this to true -config.setup = false; - -/* - 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. -*/ -config.hostWebserver = false; - -config.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 -}; - -config.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). -}; - -config.useSSL = true; - -config.certificate = { -// key: fs.readFileSync('../cert.key'), -// cert: fs.readFileSync('../cert.crt') -}; - -config.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: null, // Background image file path. Accepts external images. If this is undefined the default background will be used. - maxCon: 0, - ownerEmail: 'pad.owner@self-hosted.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!\ - ', -}; - -config.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: true, - }, -}; - -// The amount of time users stay logged in for before having to login again in days. -// 0 = login every time; -config.loginExpire = 7; - -// Database config -config.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) -config.roleOrder = ['dev', 'owner', 'coowner', 'supervisor', 'bot', 'regular', 'default']; - - -// Defines which roles are 'staff' members -// PROPERTY names. NOT title. (case-sensitive) -config.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 -config.roles = { - 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 DEVELOPERS - 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: [], - } -}; - -module.exports = config; diff --git a/webserver/app.js b/webserver/app.js index 9cf49fd..0cbe913 100644 --- a/webserver/app.js +++ b/webserver/app.js @@ -1,21 +1,27 @@ -var express = require('express'); -var compression = require('compression'); -var path = require('path'); -var http = require('http'); -var https = require('https'); -var fs = require('fs'); +// eslint-disable-next-line +'use strict'; +const express = require('express'); +const compression = require('compression'); +const path = require('path'); +const http = require('http'); +const https = require('https'); +const fs = require('fs'); const nconf = require('nconf'); +const ejs = require('ejs'); -var app = express(); -var server = null; -var server2 = null; -var socketServer = null; +const app = express(); +let server2 = null; +let server = null; +let socketServer = null; + +/* SSL */ 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); @@ -25,48 +31,64 @@ else { server = http.createServer(app); } +app.set('view engine', 'html'); +app.engine('html', ejs.renderFile); +app.set('views', __dirname + '/public'); app.use(compression()); -if(nconf.get('webServer:redirectHTTP')) - app.use(function(req, res, next) { - if(!req.secure) { - return res.redirect(['https://', req.hostname, ":", nconf.get('webServer:port') || process.env.PORT, req.url].join('')); +if (nconf.get('webServer:redirectHTTP')) { + app.use((req, res, next) => { + if (!req.secure) { + return res.redirect(['https://', req.hostname, ':', nconf.get('webServer:port') || process.env.PORT, req.url].join('')); } next(); }); +} +app.get(['/', '/index.html'], (req, res) => { + res.render('index', { + tags: nconf.get('room:tags'), + room: nconf.get('room'), + }); +}); app.use(express.static(path.resolve(__dirname, 'public'))); app.use('/pads', express.static(path.resolve(__dirname, 'public'))); -app.get('/config', function(req, res) { - res.setHeader("Content-Type", "application/javascript"); +app.get('/config', (req, res) => { + res.setHeader('Content-Type', 'application/javascript'); res.send(fs.readFileSync(__dirname + '/public/lib/js/webconfig.js')); }); -app.get('/api/room', function(req, res) { - var roomInfo = { - "slug": nconf.get('room:slug'), - "name": nconf.get('room:name'), - "people": null, - "queue": null, - "media": null, + +app.get('/api/room', (req, res) => { + const roomInfo = { + slug: nconf.get('room:slug'), + name: nconf.get('room:name'), + people: null, + queue: null, + media: null, }; res.send(roomInfo); }); 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); + const addr = server.address(); + console.log('Webserver listening at', addr.address + ':' + addr.port); }); -if(server2 != null){ +if (server2 != null) { 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); + const addr2 = server2.address(); + console.log('HTTP Webserver listening at', addr2.address + ':' + addr2.port); }); } -var setSocketServer = function(ss){ +const setSocketServer = function (ss) { socketServer = ss; }; -module.exports = {app: app, server: server, server2: server2, setSocketServer: setSocketServer}; +module.exports = { + app, + server, + server2, + setSocketServer, +}; diff --git a/webserver/public/index.html b/webserver/public/index.html index ef92eef..ce04284 100644 --- a/webserver/public/index.html +++ b/webserver/public/index.html @@ -2,9 +2,21 @@ - - - musiqpad - join us! + + + <% if (tags) { %> + + + + + + + + + <% } else { %> + <% } %> + + <%- room.name -%>