Files
kami-parse-server/src/index.js

269 lines
9.4 KiB
JavaScript
Raw Normal View History

2016-01-28 10:58:12 -08:00
// ParseServer - open-source compatible API Server for Parse apps
2016-02-20 23:45:54 -08:00
import 'babel-polyfill';
2016-01-28 10:58:12 -08:00
var batch = require('./batch'),
bodyParser = require('body-parser'),
DatabaseAdapter = require('./DatabaseAdapter'),
express = require('express'),
middlewares = require('./middlewares'),
multer = require('multer'),
Parse = require('parse/node').Parse;
2016-02-23 12:54:20 -08:00
2016-02-24 15:55:11 -05:00
import cache from './cache';
2016-02-08 22:51:58 -08:00
import ParsePushAdapter from './Adapters/Push/ParsePushAdapter';
//import passwordReset from './passwordReset';
import PromiseRouter from './PromiseRouter';
2016-02-19 23:47:44 -05:00
import { AnalyticsRouter } from './Routers/AnalyticsRouter';
import { ClassesRouter } from './Routers/ClassesRouter';
import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter';
import { FilesController } from './Controllers/FilesController';
import { FilesRouter } from './Routers/FilesRouter';
2016-02-19 23:47:44 -05:00
import { FunctionsRouter } from './Routers/FunctionsRouter';
import { GridStoreAdapter } from './Adapters/Files/GridStoreAdapter';
2016-02-19 23:47:44 -05:00
import { IAPValidationRouter } from './Routers/IAPValidationRouter';
2016-02-23 12:54:20 -08:00
import { LogsRouter } from './Routers/LogsRouter';
import { HooksRouter } from './Routers/HooksRouter';
import { PublicAPIRouter } from './Routers/PublicAPIRouter';
import { GlobalConfigRouter } from './Routers/GlobalConfigRouter';
import { HooksController } from './Controllers/HooksController';
import { UserController } from './Controllers/UserController';
import { InstallationsRouter } from './Routers/InstallationsRouter';
import { loadAdapter } from './Adapters/AdapterLoader';
import { LoggerController } from './Controllers/LoggerController';
import { PushController } from './Controllers/PushController';
import { PushRouter } from './Routers/PushRouter';
import { RolesRouter } from './Routers/RolesRouter';
import { S3Adapter } from './Adapters/Files/S3Adapter';
import { SchemasRouter } from './Routers/SchemasRouter';
import { SessionsRouter } from './Routers/SessionsRouter';
import { UsersRouter } from './Routers/UsersRouter';
2016-02-23 12:54:20 -08:00
import requiredParameter from './requiredParameter';
import { randomString } from './cryptoUtils';
2016-01-28 10:58:12 -08:00
// Mutate the Parse object to add the Cloud Code handlers
addParseCloud();
// ParseServer works like a constructor of an express app.
// The args that we understand are:
// "databaseAdapter": a class like DatabaseController providing create, find,
2016-01-28 10:58:12 -08:00
// update, and delete
// "filesAdapter": a class like GridStoreAdapter providing create, get,
// and delete
2016-02-24 11:15:26 -08:00
// "loggerAdapter": a class like FileLoggerAdapter providing info, error,
// and query
2016-01-28 10:58:12 -08:00
// "databaseURI": a uri like mongodb://localhost:27017/dbname to tell us
// what database this Parse API connects to.
// "cloud": relative location to cloud code to require, or a function
// that is given an instance of Parse as a parameter. Use this instance of Parse
// to register your cloud code hooks and functions.
2016-01-28 10:58:12 -08:00
// "appId": the application id to host
// "masterKey": the master key for requests to this app
2016-01-30 15:20:44 -08:00
// "facebookAppIds": an array of valid Facebook Application IDs, required
// if using Facebook login
2016-01-28 10:58:12 -08:00
// "collectionPrefix": optional prefix for database collection names
// "fileKey": optional key from Parse dashboard for supporting older files
// hosted by Parse
// "clientKey": optional key from Parse dashboard
// "dotNetKey": optional key from Parse dashboard
// "restAPIKey": optional key from Parse dashboard
// "javascriptKey": optional key from Parse dashboard
// "push": optional key from configure push
let validateEmailConfiguration = (verifyUserEmails, appName, emailAdapter) => {
if (verifyUserEmails) {
if (typeof appName !== 'string') {
throw 'An app name is required when using email verification.';
}
if (!emailAdapter) {
throw 'User email verification was enabled, but no email adapter was provided';
}
}
}
function ParseServer({
2016-02-23 12:54:20 -08:00
appId = requiredParameter('You must provide an appId!'),
masterKey = requiredParameter('You must provide a masterKey!'),
appName,
databaseAdapter,
filesAdapter,
push,
loggerAdapter,
databaseURI,
cloud,
collectionPrefix = '',
2016-02-26 06:00:41 -08:00
clientKey,
javascriptKey,
dotNetKey,
restAPIKey,
fileKey = 'invalid-file-key',
facebookAppIds = [],
enableAnonymousUsers = true,
2016-02-26 22:55:39 +08:00
allowClientClassCreation = true,
oauth = {},
2016-02-23 12:54:20 -08:00
serverURL = requiredParameter('You must provide a serverURL!'),
maxUploadSize = '20mb',
verifyUserEmails = false,
emailAdapter,
publicServerURL,
customPages = {
invalidLink: undefined,
verifyEmailSuccess: undefined,
choosePassword: undefined,
passwordResetSuccess: undefined
},
}) {
// Initialize the node client SDK automatically
2016-02-26 06:00:41 -08:00
Parse.initialize(appId, javascriptKey || 'unused', masterKey);
2016-02-25 12:59:19 -05:00
Parse.serverURL = serverURL;
if (databaseAdapter) {
DatabaseAdapter.setAdapter(databaseAdapter);
2016-01-28 10:58:12 -08:00
}
if (databaseURI) {
DatabaseAdapter.setAppDatabaseURI(appId, databaseURI);
2016-01-28 10:58:12 -08:00
}
if (verifyUserEmails && !publicServerURL && !process.env.TESTING) {
console.warn("");
console.warn("You should set publicServerURL to serve the public pages");
console.warn("");
}
if (cloud) {
2016-01-28 10:58:12 -08:00
addParseCloud();
if (typeof cloud === 'function') {
cloud(Parse)
} else if (typeof cloud === 'string') {
require(cloud);
2016-02-01 15:41:22 -05:00
} else {
throw "argument 'cloud' must either be a string or a function";
}
2016-01-28 10:58:12 -08:00
}
2016-02-23 12:54:20 -08:00
const filesControllerAdapter = loadAdapter(filesAdapter, GridStoreAdapter);
const pushControllerAdapter = loadAdapter(push, ParsePushAdapter);
const loggerControllerAdapter = loadAdapter(loggerAdapter, FileLoggerAdapter);
const emailControllerAdapter = loadAdapter(emailAdapter);
// We pass the options and the base class for the adatper,
// Note that passing an instance would work too
const filesController = new FilesController(filesControllerAdapter, appId);
const pushController = new PushController(pushControllerAdapter, appId);
const loggerController = new LoggerController(loggerControllerAdapter, appId);
const hooksController = new HooksController(appId, collectionPrefix);
const userController = new UserController(emailControllerAdapter, appId, { verifyUserEmails });
cache.apps.set(appId, {
masterKey: masterKey,
serverURL: serverURL,
collectionPrefix: collectionPrefix,
clientKey: clientKey,
javascriptKey: javascriptKey,
dotNetKey: dotNetKey,
restAPIKey: restAPIKey,
fileKey: fileKey,
facebookAppIds: facebookAppIds,
filesController: filesController,
pushController: pushController,
loggerController: loggerController,
hooksController: hooksController,
userController: userController,
verifyUserEmails: verifyUserEmails,
enableAnonymousUsers: enableAnonymousUsers,
2016-02-26 22:55:39 +08:00
allowClientClassCreation: allowClientClassCreation,
oauth: oauth,
appName: appName,
publicServerURL: publicServerURL,
customPages: customPages,
});
2016-01-28 10:58:12 -08:00
// To maintain compatibility. TODO: Remove in some version that breaks backwards compatability
2016-01-30 15:20:44 -08:00
if (process.env.FACEBOOK_APP_ID) {
cache.apps.get(appId)['facebookAppIds'].push(process.env.FACEBOOK_APP_ID);
2016-01-30 15:20:44 -08:00
}
2016-01-28 10:58:12 -08:00
// This app serves the Parse API directly.
// It's the equivalent of https://api.parse.com/1 in the hosted Parse API.
var api = express();
//api.use("/apps", express.static(__dirname + "/public"));
2016-01-28 10:58:12 -08:00
// File handling needs to be before default middlewares are applied
2016-02-23 11:49:21 -05:00
api.use('/', new FilesRouter().getExpressRouter({
maxUploadSize: maxUploadSize
}));
2016-01-28 10:58:12 -08:00
api.use('/', bodyParser.urlencoded({extended: false}), new PublicAPIRouter().expressApp());
2016-01-28 10:58:12 -08:00
// TODO: separate this from the regular ParseServer object
if (process.env.TESTING == 1) {
api.use('/', require('./testing-routes').router);
}
2016-02-23 11:49:21 -05:00
api.use(bodyParser.json({ 'type': '*/*' , limit: maxUploadSize }));
2016-01-28 10:58:12 -08:00
api.use(middlewares.allowCrossDomain);
api.use(middlewares.allowMethodOverride);
api.use(middlewares.handleParseHeaders);
2016-02-09 20:45:02 -08:00
let routers = [
2016-02-19 23:47:44 -05:00
new ClassesRouter(),
new UsersRouter(),
new SessionsRouter(),
new RolesRouter(),
new AnalyticsRouter(),
new InstallationsRouter(),
new FunctionsRouter(),
new SchemasRouter(),
new PushRouter(),
new LogsRouter(),
2016-02-19 23:47:44 -05:00
new IAPValidationRouter()
2016-02-09 20:45:02 -08:00
];
2016-02-23 12:54:20 -08:00
if (process.env.PARSE_EXPERIMENTAL_CONFIG_ENABLED || process.env.TESTING) {
routers.push(new GlobalConfigRouter());
}
if (process.env.PARSE_EXPERIMENTAL_HOOKS_ENABLED || process.env.TESTING) {
routers.push(new HooksRouter());
}
let routes = routers.reduce((memo, router) => {
return memo.concat(router.routes);
}, []);
2016-01-28 10:58:12 -08:00
let appRouter = new PromiseRouter(routes);
2016-02-09 20:45:02 -08:00
batch.mountOnto(appRouter);
2016-01-28 10:58:12 -08:00
api.use(appRouter.expressApp());
2016-01-28 10:58:12 -08:00
api.use(middlewares.handleParseErrors);
process.on('uncaughtException', (err) => {
if( err.code === "EADDRINUSE" ) { // user-friendly message for this common error
console.log(`Unable to listen on port ${err.port}. The port is already in use.`);
process.exit(0);
}
else {
throw err;
}
});
hooksController.load();
2016-01-28 10:58:12 -08:00
return api;
}
function addParseCloud() {
const ParseCloud = require("./cloud-code/Parse.Cloud");
Object.assign(Parse.Cloud, ParseCloud);
2016-01-28 10:58:12 -08:00
global.Parse = Parse;
}
module.exports = {
ParseServer: ParseServer,
S3Adapter: S3Adapter,
};