Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds support for multiple apps on a single server #263

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,11 @@ node_modules
# WebStorm/IntelliJ
.idea

# visual studio code
.vscode

# Babel.js
lib/

# Cache
.cache
91 changes: 89 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,6 @@ var ParseServer = require('parse-server').ParseServer;

var app = express();

var port = process.env.PORT || 1337;

// Specify the connection string for your mongodb database
// and the location to your Parse cloud code
var api = new ParseServer({
Expand All @@ -138,6 +136,7 @@ app.get('/', function(req, res) {
res.status(200).send('Express is running here.');
});

var port = process.env.PORT || 1337;
app.listen(port, function() {
console.log('parse-server-example running on port ' + port + '.');
});
Expand Down Expand Up @@ -171,6 +170,86 @@ Alernatively, you can use the `PARSE_SERVER_OPTIONS` environment variable set to

To start the server, just run `npm start`.


#### Configuration file

You can pass a configuration JSON file to npm start:

`$ npm start -- --config path/to/config.json`

(note that the first `--` is the required format by npm)

#### Multiple applications

You can host mutiple applications on the same server by specifying as options or use a config JSON;

```
{
"applications": [
{
"appId": "APP1",
"masterKey": "MASTERKEY1",
...
},
{
"appId": "APP2",
"masterKey": "MASTERKEY2",
...
},
// General adapters configuration (optional)
// It's overriden by specific configuration
databaseAdapter: "...",
filesAdatpter: "..."
]
}
```

Use `$ npm start -- --config path/to/config.json` to start the server


:+1: if you use the `PARSE_SERVER_OPTIONS` environment variable, the multiple applications support will be granted too.

:warning: Make sure to use different databases for each app. The behaviour could be unexpected otherwise.

##### Cloud Code for multiple applications

Cloud code will run in a separate node process and use HTTP as a transport to register the hooks.

```
cloud: "path/to/main.js"
```

The cloud code server will start on port 8081 and will be incremented for each app.


You can specify a specific port for each of your cloud code:

```
cloud: {
main: "/path/to/main.js",
port: 12345,
forever: {
... // (Options to pass to forever)[https://github.com/foreverjs/forever-monitor]
}
}
```

If you only have a single app, but pass an object for the cloud option,
this will be run in a separate process too.

The other options available for Cloud Code are:

`hooksCreationStrategy: "always" | "never" | "try"`

* *always* will always use the last cloud code server
* *never* will not register the new hook
* *try* will register the hook if it doesn't exist

##### Standalone Cloud Code Server

please see (here)[https://github.com/ParsePlatform/parse-server/blob/master/src/cloud-code/README.md]


##### Global installation

You can install parse-server globally
Expand All @@ -179,6 +258,14 @@ You can install parse-server globally

Now you can just run `$ parse-server` from your command line.

To pass a configuration file you can use `$ parse-server --config path/to/config.json`


#### Create a new set of keys

run `$ ./bin/gen-keys` to generate a new set of keys for a new app.

You can use the configuration provided with the json configuration.

### Supported

Expand Down
62 changes: 62 additions & 0 deletions bin/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
var path = require("path");
function loadFromCommandLine(args) {
args = args || [];
while (args.length > 0) {
if (args[0] == "--config") {
if (args.length < 2) {
throw "Please specify the configuration file (json)";
}
return require(path.resolve(args[1]));
}
args = args.slice(1, args.length);
}
}

function loadFromEnvironment(env) {
env = env || {};
var options = {};
if (env.PARSE_SERVER_OPTIONS) {

options = JSON.parse(env.PARSE_SERVER_OPTIONS);

} else {

options.databaseURI = env.PARSE_SERVER_DATABASE_URI;
options.cloud = env.PARSE_SERVER_CLOUD_CODE_MAIN;
options.collectionPrefix = env.PARSE_SERVER_COLLECTION_PREFIX;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be app-specific, allowing multiple apps to share the same database... (that was the original intent of this variable.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we'll support multiple apps through ENV, seems painful to provide all APP ID settings. But I agree in principle with multiple levels of configuration for multiple apps.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, and i see the part above for PARSE_SERVER_OPTIONS.


// Keys and App ID
options.appId = env.PARSE_SERVER_APPLICATION_ID;
options.clientKey = env.PARSE_SERVER_CLIENT_KEY;
options.restAPIKey = env.PARSE_SERVER_REST_API_KEY;
options.dotNetKey = env.PARSE_SERVER_DOTNET_KEY;
options.javascriptKey = env.PARSE_SERVER_JAVASCRIPT_KEY;
options.dotNetKey = env.PARSE_SERVER_DOTNET_KEY;
options.masterKey = env.PARSE_SERVER_MASTER_KEY;
options.fileKey = env.PARSE_SERVER_FILE_KEY;
// Comma separated list of facebook app ids
var facebookAppIds = env.PARSE_SERVER_FACEBOOK_APP_IDS;

if (facebookAppIds) {
facebookAppIds = facebookAppIds.split(",");
options.facebookAppIds = facebookAppIds;
}
var oauth = process.env.PARSE_SERVER_OAUTH_PROVIDERS;
if (oauth) {
options.oauth = JSON.parse(oauth);
};
}
return options;
}


module.exports = function() {
var options = loadFromCommandLine(process.argv);
if (typeof options == "undefined") {
options = loadFromEnvironment(process.env);
}
return options;
}

module.exports.loadFromEnvironment = loadFromEnvironment;
module.exports.loadFromCommandLine = loadFromCommandLine;
15 changes: 15 additions & 0 deletions bin/gen-keys
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env node
var rack = require('hat').rack();

var newApp = {
"appId": rack(),
"masterKey": rack(),
"clientKey": rack(),
"javascriptKey": rack(),
"dotNetKey": rack(),
"restAPIKey": rack(),
"collectionPrefix": "",
"databaseURI": ""
};

process.stdout.write(JSON.stringify(newApp, null, 4)+"\n");
36 changes: 3 additions & 33 deletions bin/parse-server
Original file line number Diff line number Diff line change
@@ -1,43 +1,13 @@
#!/usr/bin/env node
var path = require("path");
var express = require('express');
var ParseServer = require("../lib/index").ParseServer;

var options = require("./config")();
var app = express();

var options = {};
if (process.env.PARSE_SERVER_OPTIONS) {

options = JSON.parse(process.env.PARSE_SERVER_OPTIONS);

} else {

options.databaseURI = process.env.PARSE_SERVER_DATABASE_URI;
options.cloud = process.env.PARSE_SERVER_CLOUD_CODE_MAIN;
options.collectionPrefix = process.env.PARSE_SERVER_COLLECTION_PREFIX;

// Keys and App ID
options.appId = process.env.PARSE_SERVER_APPLICATION_ID;
options.clientKey = process.env.PARSE_SERVER_CLIENT_KEY;
options.restAPIKey = process.env.PARSE_SERVER_REST_API_KEY;
options.dotNetKey = process.env.PARSE_SERVER_DOTNET_KEY;
options.javascriptKey = process.env.PARSE_SERVER_JAVASCRIPT_KEY;
options.masterKey = process.env.PARSE_SERVER_MASTER_KEY;
options.fileKey = process.env.PARSE_SERVER_FILE_KEY;
// Comma separated list of facebook app ids
var facebookAppIds = process.env.PARSE_SERVER_FACEBOOK_APP_IDS;

if (facebookAppIds) {
facebookAppIds = facebookAppIds.split(",");
options.facebookAppIds = facebookAppIds;
}

var oauth = process.env.PARSE_SERVER_OAUTH_PROVIDERS;
if (oauth) {
options.oauth = JSON.parse(oauth);
};
}

var mountPath = process.env.PARSE_SERVER_MOUNT_PATH || "/";

var api = new ParseServer(options);
app.use(mountPath, api);

Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
"body-parser": "^1.14.2",
"deepcopy": "^0.6.1",
"express": "^4.13.4",
"forever-monitor": "^1.7.0",
"mime": "^1.3.4",
"mongodb": "~2.1.0",
"multer": "^1.1.0",
"node-gcm": "^0.14.0",
"parse": "^1.7.0",
"parse-cloud-express": "~1.2.0",
"randomstring": "^1.1.3",
"request": "^2.65.0",
"winston": "^2.1.1"
},
Expand Down
88 changes: 88 additions & 0 deletions spec/ConfigurationLoading.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
var configuration = require("./support/parse-server-config.json");
var Parse = require("parse/node");
var apps = configuration.applications;
var configLoader = require("../bin/config");

describe('Configuration loading', () => {

it('should load a JSON from arguments', done => {
var config = configLoader.loadFromCommandLine(["--config", "./spec/support/parse-server-config.json"]);
expect(config).not.toBe(undefined);
expect(config.applications.length).toBe(2);
done();
});

it('should throw when json does not exist', done => {
function load() {
return configLoader.loadFromCommandLine(["--config", "./spec/support/bar.json"]);
}
expect(load).toThrow();
done();
});

it('should throw when json is missing', done => {
function load() {
return configLoader.loadFromCommandLine(["--config"]);
}
expect(load).toThrow("Please specify the configuration file (json)");
done();
});

it('should retun nothing when nothing is specified', done => {
var config = configLoader.loadFromCommandLine();
expect(config).toBe(undefined);
done();
});

it('should support more arguments', done => {
var config = configLoader.loadFromCommandLine(["--some","--config", "./spec/support/parse-server-config.json", "--other"]);
expect(config).not.toBe(undefined);
expect(config.applications.length).toBe(2);
done();
});

it('should load from environment', done => {
var env = {
PARSE_SERVER_DATABASE_URI: "",
PARSE_SERVER_CLOUD_CODE_MAIN: "",
PARSE_SERVER_COLLECTION_PREFIX: "",
PARSE_SERVER_APPLICATION_ID: "",
PARSE_SERVER_CLIENT_KEY: "",
PARSE_SERVER_REST_API_KEY: "",
PARSE_SERVER_DOTNET_KEY: "",
PARSE_SERVER_JAVASCRIPT_KEY: "",
PARSE_SERVER_DOTNET_KEY: "",
PARSE_SERVER_MASTER_KEY: "",
PARSE_SERVER_FILE_KEY: "",
PARSE_SERVER_FACEBOOK_APP_IDS: "hello,world"
}

var config = configLoader.loadFromEnvironment(env);
expect(config).not.toBe(undefined);
expect(Object.keys(config).length).toBe(Object.keys(env).length);
expect(config.facebookAppIds.length).toBe(2);
expect(config.facebookAppIds).toContain("hello");
expect(config.facebookAppIds).toContain("world");
done();
});

it('should load from environment options', done => {
var env = {
PARSE_SERVER_OPTIONS: require("fs").readFileSync("./spec/support/parse-server-config.json")
}

var config = configLoader.loadFromEnvironment(env);
expect(config).not.toBe(undefined);
expect(config.applications.length).toBe(2);
done();
});

it('should load empty configuration options', done => {
var config = configLoader();
expect(config).not.toBe(undefined);
expect(config).not.toBe({});
expect(config.appId).toBe(undefined);
done();
});

});
6 changes: 5 additions & 1 deletion spec/ParseACL.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,11 @@ describe('Parse.ACL', () => {
equal(results.length, 1);
var result = results[0];
ok(result);
equal(result.id, object.id);
if (!result) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prevents a crash when running the tests

fail("should have result");
} else {
equal(result.id, object.id);
}
done();
}
});
Expand Down
Loading