diff --git a/Parse-Dashboard/cli/index.js b/Parse-Dashboard/cli/index.js new file mode 100644 index 0000000000..2ae7073eea --- /dev/null +++ b/Parse-Dashboard/cli/index.js @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016-present, Parse, LLC + * All rights reserved. + * + * This source code is licensed under the license found in the LICENSE file in + * the root directory of this source tree. + */ +"use strict"; +const packageJson = require('package-json'); +const basicAuth = require('basic-auth'); +const path = require('path'); +const jsonFile = require('json-file-plus'); + +var ParseDashboard = require('../index.js'); +// Command line tool for npm start + +const program = require('commander'); +program.option('--appId [appId]', 'the app Id of the app you would like to manage.'); +program.option('--masterKey [masterKey]', 'the master key of the app you would like to manage.'); +program.option('--serverURL [serverURL]', 'the server url of the app you would like to manage.'); +program.option('--appName [appName]', 'the name of the app you would like to manage. Optional.'); +program.option('--config [config]', 'the path to the configuration file'); +program.option('--port [port]', 'the port to run parse-dashboard'); +program.option('--allowInsecureHTTP [allowInsecureHTTP]', 'set this flag when you are running the dashboard behind an HTTPS load balancer or proxy with early SSL termination.'); + +program.parse(process.argv); + + +const port = program.port || process.env.PORT || 4040; +const allowInsecureHTTP = program.allowInsecureHTTP || process.env.PARSE_DASHBOARD_ALLOW_INSECURE_HTTP; + +let explicitConfigFileProvided = !!program.config; +let configFile = null; +let configFromCLI = null; +let configServerURL = program.serverURL || process.env.PARSE_DASHBOARD_SERVER_URL; +let configMasterKey = program.masterKey || process.env.PARSE_DASHBOARD_MASTER_KEY; +let configAppId = program.appId || process.env.PARSE_DASHBOARD_APP_ID; +let configAppName = program.appName || process.env.PARSE_DASHBOARD_APP_NAME; +let configUserId = program.userId || process.env.PARSE_DASHBOARD_USER_ID; +let configUserPassword = program.userPassword || process.env.PARSE_DASHBOARD_USER_PASSWORD; +if (!program.config && !process.env.PARSE_DASHBOARD_CONFIG) { + if (configServerURL && configMasterKey && configAppId) { + configFromCLI = { + data: { + apps: [ + { + appId: configAppId, + serverURL: configServerURL, + masterKey: configMasterKey, + appName: configAppName, + }, + ] + } + }; + if (configUserId && configUserPassword) { + configFromCLI.data.users = [ + { + user: configUserId, + pass: configUserPassword, + } + ]; + } + } else if (!configServerURL && !configMasterKey && !configAppName) { + configFile = path.join(__dirname, '/../parse-dashboard-config.json'); + } +} else if (!program.config && process.env.PARSE_DASHBOARD_CONFIG) { + configFromCLI = { + data: JSON.parse(process.env.PARSE_DASHBOARD_CONFIG) + }; +} else { + configFile = program.config; + if (program.appId || program.serverURL || program.masterKey || program.appName) { + console.log('You must provide either a config file or required CLI options (app ID, Master Key, and server URL); not both.'); + process.exit(3); + } +} + + + +let p = null; +if (configFile) { + p = jsonFile(configFile); +} else if (configFromCLI) { + p = Promise.resolve(configFromCLI); +} else { + //Failed to load default config file. + console.log('You must provide either a config file or an app ID, Master Key, and server URL. See parse-dashboard --help for details.'); + process.exit(4); +} + +p.then(config => { + config.data.port = port; + ParseDashboard.runServer(config.data); +}, error => { + if (error instanceof SyntaxError) { + console.log('Your config file contains invalid JSON. Exiting.'); + process.exit(1); + } else if (error.code === 'ENOENT') { + if (explicitConfigFileProvided) { + console.log('Your config file is missing. Exiting.'); + process.exit(2); + } else { + console.log('You must provide either a config file or required CLI options (app ID, Master Key, and server URL); not both.'); + process.exit(3); + } + } else { + console.log('There was a problem with your config. Exiting.'); + process.exit(-1); + } +}) +.catch(error => { + console.log('There was a problem loading the dashboard. Exiting.'); + process.exit(-1); +}); \ No newline at end of file diff --git a/Parse-Dashboard/index.js b/Parse-Dashboard/index.js index 3b2ed4f524..1b93931b61 100644 --- a/Parse-Dashboard/index.js +++ b/Parse-Dashboard/index.js @@ -5,114 +5,48 @@ * This source code is licensed under the license found in the LICENSE file in * the root directory of this source tree. */ -// Command line tool for npm start -"use strict" +"use strict"; const packageJson = require('package-json'); const basicAuth = require('basic-auth'); const path = require('path'); const jsonFile = require('json-file-plus'); const express = require('express'); -const program = require('commander'); -program.option('--appId [appId]', 'the app Id of the app you would like to manage.'); -program.option('--masterKey [masterKey]', 'the master key of the app you would like to manage.'); -program.option('--serverURL [serverURL]', 'the server url of the app you would like to manage.'); -program.option('--appName [appName]', 'the name of the app you would like to manage. Optional.'); -program.option('--config [config]', 'the path to the configuration file'); -program.option('--port [port]', 'the port to run parse-dashboard'); -program.option('--allowInsecureHTTP [allowInsecureHTTP]', 'set this flag when you are running the dashboard behind an HTTPS load balancer or proxy with early SSL termination.'); +class ParseDashboard { -program.parse(process.argv); - -const currentVersionFeatures = require('../package.json').parseDashboardFeatures; - -var newFeaturesInLatestVersion = []; -packageJson('parse-dashboard', 'latest').then(latestPackage => { - if (latestPackage.parseDashboardFeatures instanceof Array) { - newFeaturesInLatestVersion = latestPackage.parseDashboardFeatures.filter(feature => { - return currentVersionFeatures.indexOf(feature) === -1; - }); - } -}); - -const port = program.port || process.env.PORT || 4040; -const allowInsecureHTTP = program.allowInsecureHTTP || process.env.PARSE_DASHBOARD_ALLOW_INSECURE_HTTP; - -let explicitConfigFileProvided = !!program.config; -let configFile = null; -let configFromCLI = null; -let configServerURL = program.serverURL || process.env.PARSE_DASHBOARD_SERVER_URL; -let configMasterKey = program.masterKey || process.env.PARSE_DASHBOARD_MASTER_KEY; -let configAppId = program.appId || process.env.PARSE_DASHBOARD_APP_ID; -let configAppName = program.appName || process.env.PARSE_DASHBOARD_APP_NAME; -let configUserId = program.userId || process.env.PARSE_DASHBOARD_USER_ID; -let configUserPassword = program.userPassword || process.env.PARSE_DASHBOARD_USER_PASSWORD; -if (!program.config && !process.env.PARSE_DASHBOARD_CONFIG) { - if (configServerURL && configMasterKey && configAppId) { - configFromCLI = { - data: { - apps: [ - { - appId: configAppId, - serverURL: configServerURL, - masterKey: configMasterKey, - appName: configAppName, - }, - ] - } - }; - if (configUserId && configUserPassword) { - configFromCLI.data.users = [ - { - user: configUserId, - pass: configUserPassword, - } - ]; + constructor(dashboardConfig) { + if(typeof dashboardConfig === 'undefined') + { + dashboardConfig = {}; } - } else if (!configServerURL && !configMasterKey && !configAppName) { - configFile = path.join(__dirname, 'parse-dashboard-config.json'); - } -} else if (!program.config && process.env.PARSE_DASHBOARD_CONFIG) { - configFromCLI = { - data: JSON.parse(process.env.PARSE_DASHBOARD_CONFIG) - }; -} else { - configFile = program.config; - if (program.appId || program.serverURL || program.masterKey || program.appName) { - console.log('You must provide either a config file or required CLI options (app ID, Master Key, and server URL); not both.'); - process.exit(3); - } -} - -let p = null; -if (configFile) { - p = jsonFile(configFile); -} else if (configFromCLI) { - p = Promise.resolve(configFromCLI); -} else { - //Failed to load default config file. - console.log('You must provide either a config file or an app ID, Master Key, and server URL. See parse-dashboard --help for details.'); - process.exit(4); -} -p.then(config => { - config.data.apps.forEach(app => { - if (!app.appName) { - app.appName = app.appId; + if(!dashboardConfig.apps) + { + dashboardConfig.apps = []; } - }); - - const app = express(); + ParseDashboard.checkConfig(dashboardConfig); + this.config = { + apps : dashboardConfig.apps, + users: dashboardConfig.users, + }; + } - // Serve public files. - app.use(express.static(path.join(__dirname,'public'))); - // Serve the configuration. - app.get('/parse-dashboard-config.json', function(req, res) { + //Server the provided config retrieved from request to the provided response object + static serveConfig(config,req,res){ + var currentVersionFeatures = require('../package.json').parseDashboardFeatures; + var newFeaturesInLatestVersion = []; + packageJson('parse-dashboard', 'latest').then(latestPackage => { + if (latestPackage.parseDashboardFeatures instanceof Array) { + newFeaturesInLatestVersion = latestPackage.parseDashboardFeatures.filter(feature => { + return currentVersionFeatures.indexOf(feature) === -1; + }); + } + }); const response = { - apps: config.data.apps, + apps: config.apps, newFeaturesInLatestVersion: newFeaturesInLatestVersion, }; - const users = config.data.users; + const users = config.users; let auth = null; //If they provide auth when their config has no users, ignore the auth @@ -165,35 +99,62 @@ p.then(config => { } //We shouldn't get here. Fail closed. res.send({ success: false, error: 'Something went wrong.' }); - }); - - // For every other request, go to index.html. Let client-side handle the rest. - app.get('/*', function(req, res) { - res.sendFile(__dirname + '/index.html'); - }); - - // Start the server. - app.listen(port); - - console.log(`The dashboard is now available at http://localhost:${port}/`); -}, error => { - if (error instanceof SyntaxError) { - console.log('Your config file contains invalid JSON. Exiting.'); - process.exit(1); - } else if (error.code === 'ENOENT') { - if (explicitConfigFileProvided) { - console.log('Your config file is missing. Exiting.'); - process.exit(2); - } else { - console.log('You must provide either a config file or required CLI options (app ID, Master Key, and server URL); not both.'); - process.exit(3); - } - } else { - console.log('There was a problem with your config. Exiting.'); - process.exit(-1); } -}) -.catch(error => { - console.log('There was a problem loading the dashboard. Exiting.'); - process.exit(-1); -}); + + serveConfigWrapper(req,res) + { + ParseDashboard.serveConfig(this.config,req,res); + } + + app() { + var api = express(); + // Serve public files. + api.use("/",express.static(path.join(__dirname,'public'))); + + // Serve the configuration. + api.get('/parse-dashboard-config.json',this.serveConfigWrapper.bind(this)); + + // For every other request, go to index.html. Let client-side handle the rest. + api.get('/*', function(req, res) { + res.sendFile(__dirname + '/index.html'); + }); + return api; + } + + static runServer(config) + { + ParseDashboard.checkConfig(config); + var api = express(); + + // Serve public files. + api.use("/",express.static(path.join(__dirname,'public'))); + + // Serve the configuration. + api.get('/parse-dashboard-config.json',function(req,res) + { + ParseDashboard.serveConfig(config,req,res); + }); + + // For every other request, go to index.html. Let client-side handle the rest. + api.get('/*', function(req, res) { + res.sendFile(__dirname + '/index.html'); + }); + + // Start the server. + api.listen(config.port); + + + console.log('The dashboard is now available at http://localhost:'+config.port); + } + + static checkConfig(config) + { + config.apps.forEach(app => { + if (!app.appName) { + app.appName = app.appId; + } + }); + } +} + +module.exports = ParseDashboard; \ No newline at end of file diff --git a/README.md b/README.md index e861c9c1fc..46f6bea1d9 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,35 @@ You can also manage apps that on Parse.com from the same dashboard. In your conf You can set `appNameForURL` in the config file for each app to control the url of your app within the dashboard. This can make it easier to use bookmarks or share links on your dashboard. +## Use as Express middleware + +Parse Dashboard can also be mounted as middleware in an express application. + +A short example on how to accomplish this is shown below: + +``` +var express = require('express'); +var app = express(); +var ParseDashboard = require('parse-dashboard'); + +var dashboard = new ParseDashboard({ + apps: [ + { + serverURL: "https://example.com:17030/parse", + appId: "YOUR_APP_ID", + masterKey: "YOUR_MASTER_KEY", + appName: "MyApp" + } + ] +}); + +app.use("/",dashboard.app()); + +app.listen(4040, function () { + console.log('Example Parse Dashboard express app listening on port 4040!'); +}); +``` + ## Deploying the dashboard Make sure the server URLs for your apps can be accessed by your browser. If you are deploying the dashboard, then `localhost` urls will not work. diff --git a/bin/parse-dashboard b/bin/parse-dashboard index d83bb2f1a7..ec9f969536 100755 --- a/bin/parse-dashboard +++ b/bin/parse-dashboard @@ -1,2 +1,2 @@ #!/usr/bin/env node -require('../Parse-Dashboard'); +require('../Parse-Dashboard/cli'); diff --git a/package.json b/package.json index 61f8ab138d..d8e3b5cb05 100644 --- a/package.json +++ b/package.json @@ -9,17 +9,20 @@ "Class Level Permissions Editor", "Send Push Notifications" ], + "main": "./Parse-Dashboard/index.js", "description": "The Parse Dashboard", "keywords": [ "parse", "dashboard" ], "homepage": "https://github.com/ParsePlatform/parse-dashboard", - "bugs": "https://github.com/ParsePlatform/parse-dashboard/issues", + "bugs": { + "url": "https://github.com/ParsePlatform/parse-dashboard/issues" + }, "version": "1.0.6", "repository": { "type": "git", - "url": "https://github.com/ParsePlatform/parse-dashboard" + "url": "git+https://github.com/ParsePlatform/parse-dashboard.git" }, "license": "SEE LICENSE IN LICENSE", "files": [ @@ -29,6 +32,7 @@ "LICENSE" ], "dependencies": { + "babel-polyfill": "^6.7.2", "basic-auth": "^1.0.3", "commander": "^2.9.0", "express": "^4.13.4", @@ -73,7 +77,7 @@ "test": "NODE_PATH=./node_modules jest", "generate": "node scripts/generate.js", "prepublish": "webpack --config webpack/publish.config.js --progress", - "start": "node ./Parse-Dashboard/index.js" + "start": "node ./bin/parse-dashboard" }, "bin": { "parse-dashboard": "./bin/parse-dashboard" @@ -96,5 +100,12 @@ "react-addons-test-utils", "fbjs" ] - } + }, + "readme": "# Parse Dashboard\r\n\r\nParse Dashboard is a standalone dashboard for managing your Parse apps. You can use it to manage your [Parse Server](https://github.com/ParsePlatform/parse-server) apps and your apps that are running on [Parse.com](https://Parse.com).\r\n\r\n# Getting Started\r\n\r\n[Node.js](https://nodejs.org) version >= 4.3 is required to run the dashboard. You also need to be using Parse Server version 2.1.4 or higher. Install the dashboard from `npm`.\r\n\r\n```\r\nnpm install -g parse-dashboard\r\n```\r\n\r\nYou can launch the dashboard for an app with a single command by supplying an app ID, master key, URL, and name like this:\r\n\r\n```\r\nparse-dashboard --appId yourAppId --masterKey yourMasterKey --serverURL \"https://example.com/parse\" --appName optionalName\r\n```\r\n\r\nYou can then visit the dashboard in your browser at http://localhost:4040. If you want to use a different port you can supply the --port option to parse-dashboard. You can use anything you want as the app name, or leave it out in which case the app ID will be used.\r\n\r\nIf you want to manage multiple apps from the same dashboard, you can start the dashboard with a config file. For example, you could put your info into a file called `parse-dashboard-config.json` and then start the dashboard using `parse-dashboard --config parse-dashboard-config.json`. The file should match the following format:\r\n\r\n```\r\n{\r\n \"apps\": [\r\n {\r\n \"serverURL\": \"http://localhost:1337/parse\",\r\n \"appId\": \"myAppId\",\r\n \"masterKey\": \"myMasterKey\",\r\n \"appName\": \"MyApp\"\r\n }\r\n ]\r\n}\r\n```\r\n\r\nYou can also manage apps that on Parse.com from the same dashboard. In your config file, you will need to add the `restKey` and `javascriptKey` as well as the other paramaters, which you can find on `dashboard.parse.com`. Set the serverURL to `http://api.parse.com/1`:\r\n\r\n```\r\n{\r\n \"apps\": [\r\n {\r\n \"serverURL\": \"https://api.parse.com/1\",\r\n \"appId\": \"myAppId\",\r\n \"masterKey\": \"myMasterKey\",\r\n \"javascriptKey\": \"myJavascriptKey\",\r\n \"restKey\": \"myRestKey\",\r\n \"appName\": \"My Parse.Com App\"\r\n },\r\n {\r\n \"serverURL\": \"http://localhost:1337/parse\",\r\n \"appId\": \"myAppId\",\r\n \"masterKey\": \"myMasterKey\",\r\n \"appName\": \"My Parse Server App\"\r\n }\r\n ]\r\n}\r\n```\r\n\r\n![Parse Dashboard](.github/dash-shot.png)\r\n\r\n# Advanced Usage\r\n\r\n## Other options\r\n\r\nYou can set `appNameForURL` in the config file for each app to control the url of your app within the dashboard. This can make it easier to use bookmarks or share links on your dashboard.\r\n\r\n## Deploying the dashboard\r\n\r\nMake sure the server URLs for your apps can be accessed by your browser. If you are deploying the dashboard, then `localhost` urls will not work.\r\n\r\nIn order to securely deploy the dashboard without leaking your apps master key, you will need to use HTTPS and Basic Auth. You can do this by adding usernames and passwords for HTTP Basic Auth to your configuration file.\r\n```\r\n{\r\n \"apps\": [...],\r\n \"users\": [\r\n {\r\n \"user\":\"user1\",\r\n \"pass\":\"pass\"\r\n },\r\n {\r\n \"user\":\"user2\",\r\n \"pass\":\"pass\"\r\n }\r\n ]\r\n}\r\n```\r\n\r\nThe deployed dashboard detects if you are using a secure connection. If you are deploying the dashboard behind a load balancer or proxy that does early SSL termination, then the app won't be able to detect that the connection is secure. In this case, you can start the dashboard with the `--allowInsecureHTTP=1` option. You will then be responsible for ensureing that your proxy or load balancer only allows HTTPS.\r\n\r\n## Run with Docker\r\n\r\nIt is easy to use it with Docker. First build the image:\r\n\r\n```\r\ndocker build -t parse-dashboard .\r\n```\r\n\r\nRun the image with your ``config.json`` mounted as a volume\r\n\r\n```\r\ndocker run -d -p 8080:4040 -v host/path/to/config.json:/src/Parse-Dashboard/parse-dashboard-config.json parse-dashboard\r\n```\r\n\r\nBy default, the container will start the app at port 4040 inside the container. However, you can run custom command as well (see ``Deploying in production`` for custom setup).\r\n\r\nIn this example, we want to run the application in production mode at port 80 of the host machine.\r\n\r\n```\r\ndocker run -d -p 80:8080 -v host/path/to/config.json:/src/Parse-Dashboard/parse-dashboard-config.json parse-dashboard --port 8080\r\n```\r\n\r\nIf you are not familiar with Docker, ``--port 8080`` with be passed in as argument to the entrypoint to form the full command ``npm start -- --port 8080``. The application will start at port 8080 inside the container and port ``8080`` will be mounted to port ``80`` on your host machine.\r\n\r\n## Contributing\r\n\r\nWe really want Parse to be yours, to see it grow and thrive in the open source community. Please see the [Contributing to Parse Dashboard guide](CONTRIBUTING.md).\r\n", + "readmeFilename": "README.md", + "gitHead": "57ca0e003364e16116a92bb4b6ee57a25fecf457", + "_id": "parse-dashboard@1.0.6", + "_shasum": "3ea82990367a0acebcc0523d8da02c58f7f04e45", + "_from": "..\\parse-dashboard", + "_resolved": "file:..\\parse-dashboard" }