Skip to content

Commit ea211e6

Browse files
committed
moves ParseServer to it's own file
1 parent 16b6ec4 commit ea211e6

File tree

2 files changed

+289
-284
lines changed

2 files changed

+289
-284
lines changed

src/ParseServer.js

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
// ParseServer - open-source compatible API Server for Parse apps
2+
3+
import 'babel-polyfill';
4+
5+
var batch = require('./batch'),
6+
bodyParser = require('body-parser'),
7+
DatabaseAdapter = require('./DatabaseAdapter'),
8+
express = require('express'),
9+
middlewares = require('./middlewares'),
10+
multer = require('multer'),
11+
Parse = require('parse/node').Parse,
12+
authDataManager = require('./authDataManager');
13+
14+
//import passwordReset from './passwordReset';
15+
import cache from './cache';
16+
import Config from './Config';
17+
import parseServerPackage from '../package.json';
18+
import ParsePushAdapter from './Adapters/Push/ParsePushAdapter';
19+
import PromiseRouter from './PromiseRouter';
20+
import requiredParameter from './requiredParameter';
21+
import { AnalyticsRouter } from './Routers/AnalyticsRouter';
22+
import { ClassesRouter } from './Routers/ClassesRouter';
23+
import { FeaturesRouter } from './Routers/FeaturesRouter';
24+
import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter';
25+
import { FilesController } from './Controllers/FilesController';
26+
import { FilesRouter } from './Routers/FilesRouter';
27+
import { FunctionsRouter } from './Routers/FunctionsRouter';
28+
import { GCSAdapter } from './Adapters/Files/GCSAdapter';
29+
import { GlobalConfigRouter } from './Routers/GlobalConfigRouter';
30+
import { GridStoreAdapter } from './Adapters/Files/GridStoreAdapter';
31+
import { HooksController } from './Controllers/HooksController';
32+
import { HooksRouter } from './Routers/HooksRouter';
33+
import { IAPValidationRouter } from './Routers/IAPValidationRouter';
34+
import { InstallationsRouter } from './Routers/InstallationsRouter';
35+
import { loadAdapter } from './Adapters/AdapterLoader';
36+
import { LiveQueryController } from './Controllers/LiveQueryController';
37+
import { LoggerController } from './Controllers/LoggerController';
38+
import { LogsRouter } from './Routers/LogsRouter';
39+
import { ParseLiveQueryServer } from './LiveQuery/ParseLiveQueryServer';
40+
import { PublicAPIRouter } from './Routers/PublicAPIRouter';
41+
import { PushController } from './Controllers/PushController';
42+
import { PushRouter } from './Routers/PushRouter';
43+
import { randomString } from './cryptoUtils';
44+
import { RolesRouter } from './Routers/RolesRouter';
45+
import { S3Adapter } from './Adapters/Files/S3Adapter';
46+
import { SchemasRouter } from './Routers/SchemasRouter';
47+
import { SessionsRouter } from './Routers/SessionsRouter';
48+
import { setFeature } from './features';
49+
import { UserController } from './Controllers/UserController';
50+
import { UsersRouter } from './Routers/UsersRouter';
51+
import { FileSystemAdapter } from './Adapters/Files/FileSystemAdapter';
52+
53+
// Mutate the Parse object to add the Cloud Code handlers
54+
addParseCloud();
55+
56+
// ParseServer works like a constructor of an express app.
57+
// The args that we understand are:
58+
// "databaseAdapter": a class like DatabaseController providing create, find,
59+
// update, and delete
60+
// "filesAdapter": a class like GridStoreAdapter providing create, get,
61+
// and delete
62+
// "loggerAdapter": a class like FileLoggerAdapter providing info, error,
63+
// and query
64+
// "databaseURI": a uri like mongodb://localhost:27017/dbname to tell us
65+
// what database this Parse API connects to.
66+
// "cloud": relative location to cloud code to require, or a function
67+
// that is given an instance of Parse as a parameter. Use this instance of Parse
68+
// to register your cloud code hooks and functions.
69+
// "appId": the application id to host
70+
// "masterKey": the master key for requests to this app
71+
// "facebookAppIds": an array of valid Facebook Application IDs, required
72+
// if using Facebook login
73+
// "collectionPrefix": optional prefix for database collection names
74+
// "fileKey": optional key from Parse dashboard for supporting older files
75+
// hosted by Parse
76+
// "clientKey": optional key from Parse dashboard
77+
// "dotNetKey": optional key from Parse dashboard
78+
// "restAPIKey": optional key from Parse dashboard
79+
// "javascriptKey": optional key from Parse dashboard
80+
// "push": optional key from configure push
81+
82+
class ParseServer {
83+
84+
constructor({
85+
appId = requiredParameter('You must provide an appId!'),
86+
masterKey = requiredParameter('You must provide a masterKey!'),
87+
appName,
88+
databaseAdapter,
89+
filesAdapter,
90+
push,
91+
loggerAdapter,
92+
databaseURI = DatabaseAdapter.defaultDatabaseURI,
93+
databaseOptions,
94+
cloud,
95+
collectionPrefix = '',
96+
clientKey,
97+
javascriptKey,
98+
dotNetKey,
99+
restAPIKey,
100+
fileKey = 'invalid-file-key',
101+
facebookAppIds = [],
102+
enableAnonymousUsers = true,
103+
allowClientClassCreation = true,
104+
oauth = {},
105+
serverURL = requiredParameter('You must provide a serverURL!'),
106+
maxUploadSize = '20mb',
107+
verifyUserEmails = false,
108+
emailAdapter,
109+
publicServerURL,
110+
customPages = {
111+
invalidLink: undefined,
112+
verifyEmailSuccess: undefined,
113+
choosePassword: undefined,
114+
passwordResetSuccess: undefined
115+
},
116+
liveQuery = {}
117+
}) {
118+
setFeature('serverVersion', parseServerPackage.version);
119+
// Initialize the node client SDK automatically
120+
Parse.initialize(appId, javascriptKey || 'unused', masterKey);
121+
Parse.serverURL = serverURL;
122+
123+
if (databaseAdapter) {
124+
DatabaseAdapter.setAdapter(databaseAdapter);
125+
}
126+
127+
if (databaseOptions) {
128+
DatabaseAdapter.setAppDatabaseOptions(appId, databaseOptions);
129+
}
130+
131+
if (databaseURI) {
132+
DatabaseAdapter.setAppDatabaseURI(appId, databaseURI);
133+
}
134+
135+
if (cloud) {
136+
addParseCloud();
137+
if (typeof cloud === 'function') {
138+
cloud(Parse)
139+
} else if (typeof cloud === 'string') {
140+
require(cloud);
141+
} else {
142+
throw "argument 'cloud' must either be a string or a function";
143+
}
144+
}
145+
146+
147+
const filesControllerAdapter = loadAdapter(filesAdapter, () => {
148+
return new GridStoreAdapter(databaseURI);
149+
});
150+
// Pass the push options too as it works with the default
151+
const pushControllerAdapter = loadAdapter(push && push.adapter, ParsePushAdapter, push);
152+
const loggerControllerAdapter = loadAdapter(loggerAdapter, FileLoggerAdapter);
153+
const emailControllerAdapter = loadAdapter(emailAdapter);
154+
// We pass the options and the base class for the adatper,
155+
// Note that passing an instance would work too
156+
const filesController = new FilesController(filesControllerAdapter, appId);
157+
const pushController = new PushController(pushControllerAdapter, appId);
158+
const loggerController = new LoggerController(loggerControllerAdapter, appId);
159+
const hooksController = new HooksController(appId, collectionPrefix);
160+
const userController = new UserController(emailControllerAdapter, appId, { verifyUserEmails });
161+
const liveQueryController = new LiveQueryController(liveQuery);
162+
163+
cache.apps.set(appId, {
164+
masterKey: masterKey,
165+
serverURL: serverURL,
166+
collectionPrefix: collectionPrefix,
167+
clientKey: clientKey,
168+
javascriptKey: javascriptKey,
169+
dotNetKey: dotNetKey,
170+
restAPIKey: restAPIKey,
171+
fileKey: fileKey,
172+
facebookAppIds: facebookAppIds,
173+
filesController: filesController,
174+
pushController: pushController,
175+
loggerController: loggerController,
176+
hooksController: hooksController,
177+
userController: userController,
178+
verifyUserEmails: verifyUserEmails,
179+
allowClientClassCreation: allowClientClassCreation,
180+
authDataManager: authDataManager(oauth, enableAnonymousUsers),
181+
appName: appName,
182+
publicServerURL: publicServerURL,
183+
customPages: customPages,
184+
maxUploadSize: maxUploadSize,
185+
liveQueryController: liveQueryController
186+
});
187+
188+
// To maintain compatibility. TODO: Remove in some version that breaks backwards compatability
189+
if (process.env.FACEBOOK_APP_ID) {
190+
cache.apps.get(appId)['facebookAppIds'].push(process.env.FACEBOOK_APP_ID);
191+
}
192+
193+
Config.validate(cache.apps.get(appId));
194+
this.config = cache.apps.get(appId);
195+
hooksController.load();
196+
}
197+
198+
get app() {
199+
return ParseServer.app(this.config);
200+
}
201+
202+
static app({maxUploadSize = '20mb'}) {
203+
// This app serves the Parse API directly.
204+
// It's the equivalent of https://api.parse.com/1 in the hosted Parse API.
205+
var api = express();
206+
//api.use("/apps", express.static(__dirname + "/public"));
207+
// File handling needs to be before default middlewares are applied
208+
api.use('/', middlewares.allowCrossDomain, new FilesRouter().getExpressRouter({
209+
maxUploadSize: maxUploadSize
210+
}));
211+
212+
api.use('/', bodyParser.urlencoded({extended: false}), new PublicAPIRouter().expressApp());
213+
214+
// TODO: separate this from the regular ParseServer object
215+
if (process.env.TESTING == 1) {
216+
api.use('/', require('./testing-routes').router);
217+
}
218+
219+
api.use(bodyParser.json({ 'type': '*/*' , limit: maxUploadSize }));
220+
api.use(middlewares.allowCrossDomain);
221+
api.use(middlewares.allowMethodOverride);
222+
api.use(middlewares.handleParseHeaders);
223+
224+
let routers = [
225+
new ClassesRouter(),
226+
new UsersRouter(),
227+
new SessionsRouter(),
228+
new RolesRouter(),
229+
new AnalyticsRouter(),
230+
new InstallationsRouter(),
231+
new FunctionsRouter(),
232+
new SchemasRouter(),
233+
new PushRouter(),
234+
new LogsRouter(),
235+
new IAPValidationRouter(),
236+
new FeaturesRouter(),
237+
];
238+
239+
if (process.env.PARSE_EXPERIMENTAL_CONFIG_ENABLED || process.env.TESTING) {
240+
routers.push(new GlobalConfigRouter());
241+
}
242+
243+
if (process.env.PARSE_EXPERIMENTAL_HOOKS_ENABLED || process.env.TESTING) {
244+
routers.push(new HooksRouter());
245+
}
246+
247+
let routes = routers.reduce((memo, router) => {
248+
return memo.concat(router.routes);
249+
}, []);
250+
251+
let appRouter = new PromiseRouter(routes);
252+
253+
batch.mountOnto(appRouter);
254+
255+
api.use(appRouter.expressApp());
256+
257+
api.use(middlewares.handleParseErrors);
258+
259+
//This causes tests to spew some useless warnings, so disable in test
260+
if (!process.env.TESTING) {
261+
process.on('uncaughtException', (err) => {
262+
if ( err.code === "EADDRINUSE" ) { // user-friendly message for this common error
263+
console.log(`Unable to listen on port ${err.port}. The port is already in use.`);
264+
process.exit(0);
265+
} else {
266+
throw err;
267+
}
268+
});
269+
}
270+
return api;
271+
}
272+
273+
static createLiveQueryServer(httpServer, config) {
274+
return new ParseLiveQueryServer(httpServer, config);
275+
}
276+
}
277+
278+
function addParseCloud() {
279+
const ParseCloud = require("./cloud-code/Parse.Cloud");
280+
Object.assign(Parse.Cloud, ParseCloud);
281+
global.Parse = Parse;
282+
}
283+
284+
export default ParseServer;

0 commit comments

Comments
 (0)