Refactors configuration management (#4271)
* Adds flow types / Configuration interfaces * Lets call it options * Use a single interface to generate the configurations * Translates options to definitions only if comments are set * improves logic * Moves objects around * Fixes issue affecting logging of circular objects * fixes undefined env * Moves all defaults to defaults * Adds back CLI defaults * Restored defaults in commander.js * Merge provided defaults and platform defaults * Addresses visual nits * Improves Config.js code * Adds ability to pass the default value in trailing comments * Load platform defaults from the definitions file * proper default values on various options * Adds ParseServer.start and server.start(options) as quick startup methods * Moves creating liveQueryServer http into ParseServer.js * removes dead code * Adds tests to guarantee we can start a LQ Server from main module * Fixes incorrect code regading liveQuery init port * Start a http server for LQ if port is specified * ensure we dont fail if config.port is not set * Specify port * ignore other path skipped in tests * Adds test for custom middleware setting * Refactors new Config into Config.get - Hides AppCache from ParseServer.js, use Config.put which validates * Extracts controller creation into Controllers/index.js - This makes the ParseServer init way simpler * Move serverURL inference into ParseServer * review nits
This commit is contained in:
@@ -18,65 +18,34 @@ function removeTrailingSlash(str) {
|
||||
}
|
||||
|
||||
export class Config {
|
||||
constructor(applicationId: string, mount: string) {
|
||||
static get(applicationId: string, mount: string) {
|
||||
const cacheInfo = AppCache.get(applicationId);
|
||||
if (!cacheInfo) {
|
||||
return;
|
||||
}
|
||||
const config = new Config();
|
||||
config.applicationId = applicationId;
|
||||
Object.keys(cacheInfo).forEach((key) => {
|
||||
if (key == 'databaseController') {
|
||||
const schemaCache = new SchemaCache(cacheInfo.cacheController,
|
||||
cacheInfo.schemaCacheTTL,
|
||||
cacheInfo.enableSingleSchemaCache);
|
||||
config.database = new DatabaseController(cacheInfo.databaseController.adapter, schemaCache);
|
||||
} else {
|
||||
config[key] = cacheInfo[key];
|
||||
}
|
||||
});
|
||||
config.mount = removeTrailingSlash(mount);
|
||||
config.generateSessionExpiresAt = config.generateSessionExpiresAt.bind(config);
|
||||
config.generateEmailVerifyTokenExpiresAt = config.generateEmailVerifyTokenExpiresAt.bind(config);
|
||||
return config;
|
||||
}
|
||||
|
||||
this.applicationId = applicationId;
|
||||
this.jsonLogs = cacheInfo.jsonLogs;
|
||||
this.masterKey = cacheInfo.masterKey;
|
||||
this.masterKeyIps = cacheInfo.masterKeyIps;
|
||||
this.clientKey = cacheInfo.clientKey;
|
||||
this.javascriptKey = cacheInfo.javascriptKey;
|
||||
this.dotNetKey = cacheInfo.dotNetKey;
|
||||
this.restAPIKey = cacheInfo.restAPIKey;
|
||||
this.webhookKey = cacheInfo.webhookKey;
|
||||
this.fileKey = cacheInfo.fileKey;
|
||||
this.allowClientClassCreation = cacheInfo.allowClientClassCreation;
|
||||
this.userSensitiveFields = cacheInfo.userSensitiveFields;
|
||||
|
||||
// Create a new DatabaseController per request
|
||||
if (cacheInfo.databaseController) {
|
||||
const schemaCache = new SchemaCache(cacheInfo.cacheController, cacheInfo.schemaCacheTTL, cacheInfo.enableSingleSchemaCache);
|
||||
this.database = new DatabaseController(cacheInfo.databaseController.adapter, schemaCache);
|
||||
}
|
||||
|
||||
this.schemaCacheTTL = cacheInfo.schemaCacheTTL;
|
||||
this.enableSingleSchemaCache = cacheInfo.enableSingleSchemaCache;
|
||||
|
||||
this.serverURL = cacheInfo.serverURL;
|
||||
this.publicServerURL = removeTrailingSlash(cacheInfo.publicServerURL);
|
||||
this.verifyUserEmails = cacheInfo.verifyUserEmails;
|
||||
this.preventLoginWithUnverifiedEmail = cacheInfo.preventLoginWithUnverifiedEmail;
|
||||
this.emailVerifyTokenValidityDuration = cacheInfo.emailVerifyTokenValidityDuration;
|
||||
this.accountLockout = cacheInfo.accountLockout;
|
||||
this.passwordPolicy = cacheInfo.passwordPolicy;
|
||||
this.appName = cacheInfo.appName;
|
||||
|
||||
this.analyticsController = cacheInfo.analyticsController;
|
||||
this.cacheController = cacheInfo.cacheController;
|
||||
this.hooksController = cacheInfo.hooksController;
|
||||
this.filesController = cacheInfo.filesController;
|
||||
this.pushController = cacheInfo.pushController;
|
||||
this.pushControllerQueue = cacheInfo.pushControllerQueue;
|
||||
this.pushWorker = cacheInfo.pushWorker;
|
||||
this.hasPushSupport = cacheInfo.hasPushSupport;
|
||||
this.hasPushScheduledSupport = cacheInfo.hasPushScheduledSupport;
|
||||
this.loggerController = cacheInfo.loggerController;
|
||||
this.userController = cacheInfo.userController;
|
||||
this.authDataManager = cacheInfo.authDataManager;
|
||||
this.customPages = cacheInfo.customPages || {};
|
||||
this.mount = removeTrailingSlash(mount);
|
||||
this.liveQueryController = cacheInfo.liveQueryController;
|
||||
this.sessionLength = cacheInfo.sessionLength;
|
||||
this.maxLimit = cacheInfo.maxLimit;
|
||||
this.expireInactiveSessions = cacheInfo.expireInactiveSessions;
|
||||
this.generateSessionExpiresAt = this.generateSessionExpiresAt.bind(this);
|
||||
this.generateEmailVerifyTokenExpiresAt = this.generateEmailVerifyTokenExpiresAt.bind(this);
|
||||
this.revokeSessionOnPasswordReset = cacheInfo.revokeSessionOnPasswordReset;
|
||||
this.objectIdSize = cacheInfo.objectIdSize;
|
||||
static put(serverConfiguration) {
|
||||
Config.validate(serverConfiguration);
|
||||
AppCache.put(serverConfiguration.appId, serverConfiguration);
|
||||
Config.setupPasswordValidator(serverConfiguration.passwordPolicy);
|
||||
return serverConfiguration;
|
||||
}
|
||||
|
||||
static validate({
|
||||
|
||||
@@ -30,7 +30,7 @@ export class AdaptableController {
|
||||
}
|
||||
|
||||
get config() {
|
||||
return new Config(this.appId);
|
||||
return Config.get(this.appId);
|
||||
}
|
||||
|
||||
expectedAdapterType() {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ParseCloudCodePublisher } from '../LiveQuery/ParseCloudCodePublisher';
|
||||
|
||||
import { LiveQueryOptions } from '../Options';
|
||||
export class LiveQueryController {
|
||||
classNames: any;
|
||||
liveQueryPublisher: any;
|
||||
|
||||
constructor(config: any) {
|
||||
constructor(config: ?LiveQueryOptions) {
|
||||
// If config is empty, we just assume no classs needs to be registered as LiveQuery
|
||||
if (!config || !config.classNames) {
|
||||
this.classNames = new Set();
|
||||
|
||||
231
src/Controllers/index.js
Normal file
231
src/Controllers/index.js
Normal file
@@ -0,0 +1,231 @@
|
||||
import authDataManager from '../Adapters/Auth';
|
||||
import { ParseServerOptions } from '../Options';
|
||||
import { loadAdapter } from '../Adapters/AdapterLoader';
|
||||
import defaults from '../defaults';
|
||||
import url from 'url';
|
||||
// Controllers
|
||||
import { LoggerController } from './LoggerController';
|
||||
import { FilesController } from './FilesController';
|
||||
import { HooksController } from './HooksController';
|
||||
import { UserController } from './UserController';
|
||||
import { CacheController } from './CacheController';
|
||||
import { LiveQueryController } from './LiveQueryController';
|
||||
import { AnalyticsController } from './AnalyticsController';
|
||||
import { PushController } from './PushController';
|
||||
import { PushQueue } from '../Push/PushQueue';
|
||||
import { PushWorker } from '../Push/PushWorker';
|
||||
import DatabaseController from './DatabaseController';
|
||||
import SchemaCache from './SchemaCache';
|
||||
|
||||
// Adapters
|
||||
import { GridStoreAdapter } from '../Adapters/Files/GridStoreAdapter';
|
||||
import { WinstonLoggerAdapter } from '../Adapters/Logger/WinstonLoggerAdapter';
|
||||
import { InMemoryCacheAdapter } from '../Adapters/Cache/InMemoryCacheAdapter';
|
||||
import { AnalyticsAdapter } from '../Adapters/Analytics/AnalyticsAdapter';
|
||||
import MongoStorageAdapter from '../Adapters/Storage/Mongo/MongoStorageAdapter';
|
||||
import PostgresStorageAdapter from '../Adapters/Storage/Postgres/PostgresStorageAdapter';
|
||||
import ParsePushAdapter from 'parse-server-push-adapter';
|
||||
|
||||
export function getControllers(options: ParseServerOptions) {
|
||||
const loggerController = getLoggerController(options);
|
||||
const filesController = getFilesController(options);
|
||||
const userController = getUserController(options);
|
||||
const {
|
||||
pushController,
|
||||
hasPushScheduledSupport,
|
||||
hasPushSupport,
|
||||
pushControllerQueue,
|
||||
pushWorker
|
||||
} = getPushController(options);
|
||||
const cacheController = getCacheController(options);
|
||||
const analyticsController = getAnalyticsController(options);
|
||||
const liveQueryController = getLiveQueryController(options);
|
||||
const databaseController = getDatabaseController(options, cacheController);
|
||||
const hooksController = getHooksController(options, databaseController);
|
||||
const authDataManager = getAuthDataManager(options);
|
||||
return {
|
||||
loggerController,
|
||||
filesController,
|
||||
userController,
|
||||
pushController,
|
||||
hasPushScheduledSupport,
|
||||
hasPushSupport,
|
||||
pushWorker,
|
||||
pushControllerQueue,
|
||||
analyticsController,
|
||||
cacheController,
|
||||
liveQueryController,
|
||||
databaseController,
|
||||
hooksController,
|
||||
authDataManager,
|
||||
};
|
||||
}
|
||||
|
||||
export function getLoggerController(options: ParseServerOptions): LoggerController {
|
||||
const {
|
||||
appId,
|
||||
jsonLogs,
|
||||
logsFolder,
|
||||
verbose,
|
||||
logLevel,
|
||||
silent,
|
||||
loggerAdapter,
|
||||
} = options;
|
||||
const loggerOptions = { jsonLogs, logsFolder, verbose, logLevel, silent };
|
||||
const loggerControllerAdapter = loadAdapter(loggerAdapter, WinstonLoggerAdapter, loggerOptions);
|
||||
return new LoggerController(loggerControllerAdapter, appId, loggerOptions);
|
||||
}
|
||||
|
||||
export function getFilesController(options: ParseServerOptions): FilesController {
|
||||
const {
|
||||
appId,
|
||||
databaseURI,
|
||||
filesAdapter,
|
||||
databaseAdapter,
|
||||
} = options;
|
||||
if (!filesAdapter && databaseAdapter) {
|
||||
throw 'When using an explicit database adapter, you must also use an explicit filesAdapter.';
|
||||
}
|
||||
const filesControllerAdapter = loadAdapter(filesAdapter, () => {
|
||||
return new GridStoreAdapter(databaseURI);
|
||||
});
|
||||
return new FilesController(filesControllerAdapter, appId);
|
||||
}
|
||||
|
||||
export function getUserController(options: ParseServerOptions): UserController {
|
||||
const {
|
||||
appId,
|
||||
emailAdapter,
|
||||
verifyUserEmails,
|
||||
} = options;
|
||||
const emailControllerAdapter = loadAdapter(emailAdapter);
|
||||
return new UserController(emailControllerAdapter, appId, { verifyUserEmails });
|
||||
}
|
||||
|
||||
export function getCacheController(options: ParseServerOptions): CacheController {
|
||||
const {
|
||||
appId,
|
||||
cacheAdapter,
|
||||
cacheTTL,
|
||||
cacheMaxSize,
|
||||
} = options;
|
||||
const cacheControllerAdapter = loadAdapter(cacheAdapter, InMemoryCacheAdapter, {appId: appId, ttl: cacheTTL, maxSize: cacheMaxSize });
|
||||
return new CacheController(cacheControllerAdapter, appId);
|
||||
}
|
||||
|
||||
export function getAnalyticsController(options: ParseServerOptions): AnalyticsController {
|
||||
const {
|
||||
analyticsAdapter,
|
||||
} = options;
|
||||
const analyticsControllerAdapter = loadAdapter(analyticsAdapter, AnalyticsAdapter);
|
||||
return new AnalyticsController(analyticsControllerAdapter);
|
||||
}
|
||||
|
||||
export function getLiveQueryController(options: ParseServerOptions): LiveQueryController {
|
||||
return new LiveQueryController(options.liveQuery);
|
||||
}
|
||||
|
||||
export function getDatabaseController(options: ParseServerOptions, cacheController: CacheController): DatabaseController {
|
||||
const {
|
||||
databaseURI,
|
||||
databaseOptions,
|
||||
collectionPrefix,
|
||||
schemaCacheTTL,
|
||||
enableSingleSchemaCache,
|
||||
} = options;
|
||||
let {
|
||||
databaseAdapter
|
||||
} = options;
|
||||
if ((databaseOptions || (databaseURI && databaseURI !== defaults.databaseURI) || collectionPrefix !== defaults.collectionPrefix) && databaseAdapter) {
|
||||
throw 'You cannot specify both a databaseAdapter and a databaseURI/databaseOptions/collectionPrefix.';
|
||||
} else if (!databaseAdapter) {
|
||||
databaseAdapter = getDatabaseAdapter(databaseURI, collectionPrefix, databaseOptions)
|
||||
} else {
|
||||
databaseAdapter = loadAdapter(databaseAdapter)
|
||||
}
|
||||
return new DatabaseController(databaseAdapter, new SchemaCache(cacheController, schemaCacheTTL, enableSingleSchemaCache));
|
||||
}
|
||||
|
||||
export function getHooksController(options: ParseServerOptions, databaseController: DatabaseController): HooksController {
|
||||
const {
|
||||
appId,
|
||||
webhookKey
|
||||
} = options;
|
||||
return new HooksController(appId, databaseController, webhookKey);
|
||||
}
|
||||
|
||||
interface PushControlling {
|
||||
pushController: PushController,
|
||||
hasPushScheduledSupport: boolean,
|
||||
pushControllerQueue: PushQueue,
|
||||
pushWorker: PushWorker
|
||||
}
|
||||
|
||||
export function getPushController(options: ParseServerOptions): PushControlling {
|
||||
const {
|
||||
scheduledPush,
|
||||
push,
|
||||
} = options;
|
||||
|
||||
const pushOptions = Object.assign({}, push);
|
||||
const pushQueueOptions = pushOptions.queueOptions || {};
|
||||
if (pushOptions.queueOptions) {
|
||||
delete pushOptions.queueOptions;
|
||||
}
|
||||
|
||||
// Pass the push options too as it works with the default
|
||||
const pushAdapter = loadAdapter(pushOptions && pushOptions.adapter, ParsePushAdapter, pushOptions);
|
||||
// We pass the options and the base class for the adatper,
|
||||
// Note that passing an instance would work too
|
||||
const pushController = new PushController();
|
||||
const hasPushSupport = !!(pushAdapter && push);
|
||||
const hasPushScheduledSupport = hasPushSupport && (scheduledPush === true);
|
||||
|
||||
const {
|
||||
disablePushWorker
|
||||
} = pushQueueOptions;
|
||||
|
||||
const pushControllerQueue = new PushQueue(pushQueueOptions);
|
||||
let pushWorker;
|
||||
if (!disablePushWorker) {
|
||||
pushWorker = new PushWorker(pushAdapter, pushQueueOptions);
|
||||
}
|
||||
return {
|
||||
pushController,
|
||||
hasPushSupport,
|
||||
hasPushScheduledSupport,
|
||||
pushControllerQueue,
|
||||
pushWorker
|
||||
}
|
||||
}
|
||||
|
||||
export function getAuthDataManager(options: ParseServerOptions) {
|
||||
const {
|
||||
auth,
|
||||
enableAnonymousUsers
|
||||
} = options;
|
||||
return authDataManager(auth, enableAnonymousUsers)
|
||||
}
|
||||
|
||||
export function getDatabaseAdapter(databaseURI, collectionPrefix, databaseOptions) {
|
||||
let protocol;
|
||||
try {
|
||||
const parsedURI = url.parse(databaseURI);
|
||||
protocol = parsedURI.protocol ? parsedURI.protocol.toLowerCase() : null;
|
||||
} catch(e) { /* */ }
|
||||
switch (protocol) {
|
||||
case 'postgres:':
|
||||
return new PostgresStorageAdapter({
|
||||
uri: databaseURI,
|
||||
collectionPrefix,
|
||||
databaseOptions
|
||||
});
|
||||
default:
|
||||
return new MongoStorageAdapter({
|
||||
uri: databaseURI,
|
||||
collectionPrefix,
|
||||
mongoOptions: databaseOptions,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ class ParseLiveQueryServer {
|
||||
subscriber: Object;
|
||||
|
||||
constructor(server: any, config: any) {
|
||||
this.server = server;
|
||||
this.clients = new Map();
|
||||
this.subscriptions = new Map();
|
||||
|
||||
|
||||
385
src/Options/Definitions.js
Normal file
385
src/Options/Definitions.js
Normal file
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
**** GENERATED CODE ****
|
||||
This code has been generated by resources/buildConfigDefinitions.js
|
||||
Do not edit manually, but update Options/index.js
|
||||
*/"use strict";
|
||||
|
||||
var parsers = require("./parsers");
|
||||
|
||||
module.exports.ParseServerOptions = {
|
||||
"appId": {
|
||||
"env": "PARSE_SERVER_APPLICATION_ID",
|
||||
"help": "Your Parse Application ID",
|
||||
"required": true
|
||||
},
|
||||
"masterKey": {
|
||||
"env": "PARSE_SERVER_MASTER_KEY",
|
||||
"help": "Your Parse Master Key",
|
||||
"required": true
|
||||
},
|
||||
"serverURL": {
|
||||
"env": "PARSE_SERVER_URL",
|
||||
"help": "URL to your parse server with http:// or https://.",
|
||||
"required": true
|
||||
},
|
||||
"masterKeyIps": {
|
||||
"env": "PARSE_SERVER_MASTER_KEY_IPS",
|
||||
"help": "Restrict masterKey to be used by only these ips. defaults to [] (allow all ips)",
|
||||
"action": parsers.arrayParser,
|
||||
"default": []
|
||||
},
|
||||
"appName": {
|
||||
"env": "PARSE_SERVER_APP_NAME",
|
||||
"help": "Sets the app name"
|
||||
},
|
||||
"analyticsAdapter": {
|
||||
"env": "PARSE_SERVER_ANALYTICS_ADAPTER",
|
||||
"help": "Adapter module for the analytics",
|
||||
"action": parsers.moduleOrObjectParser
|
||||
},
|
||||
"filesAdapter": {
|
||||
"env": "PARSE_SERVER_FILES_ADAPTER",
|
||||
"help": "Adapter module for the files sub-system",
|
||||
"action": parsers.moduleOrObjectParser
|
||||
},
|
||||
"push": {
|
||||
"env": "PARSE_SERVER_PUSH",
|
||||
"help": "Configuration for push, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#push-notifications",
|
||||
"action": parsers.objectParser
|
||||
},
|
||||
"scheduledPush": {
|
||||
"env": "PARSE_SERVER_SCHEDULED_PUSH",
|
||||
"help": "Configuration for push scheduling. Defaults to false.",
|
||||
"action": parsers.booleanParser,
|
||||
"default": false
|
||||
},
|
||||
"loggerAdapter": {
|
||||
"env": "PARSE_SERVER_LOGGER_ADAPTER",
|
||||
"help": "Adapter module for the logging sub-system",
|
||||
"action": parsers.moduleOrObjectParser
|
||||
},
|
||||
"jsonLogs": {
|
||||
"env": "JSON_LOGS",
|
||||
"help": "Log as structured JSON objects",
|
||||
"action": parsers.booleanParser
|
||||
},
|
||||
"logsFolder": {
|
||||
"env": "PARSE_SERVER_LOGS_FOLDER",
|
||||
"help": "Folder for the logs (defaults to './logs'); set to null to disable file based logging",
|
||||
"default": "./logs"
|
||||
},
|
||||
"verbose": {
|
||||
"env": "VERBOSE",
|
||||
"help": "Set the logging to verbose",
|
||||
"action": parsers.booleanParser
|
||||
},
|
||||
"logLevel": {
|
||||
"env": "PARSE_SERVER_LOG_LEVEL",
|
||||
"help": "Sets the level for logs"
|
||||
},
|
||||
"silent": {
|
||||
"env": "SILENT",
|
||||
"help": "Disables console output",
|
||||
"action": parsers.booleanParser
|
||||
},
|
||||
"databaseURI": {
|
||||
"env": "PARSE_SERVER_DATABASE_URI",
|
||||
"help": "The full URI to your mongodb database",
|
||||
"required": true,
|
||||
"default": "mongodb://localhost:27017/parse"
|
||||
},
|
||||
"databaseOptions": {
|
||||
"env": "PARSE_SERVER_DATABASE_OPTIONS",
|
||||
"help": "Options to pass to the mongodb client",
|
||||
"action": parsers.objectParser
|
||||
},
|
||||
"databaseAdapter": {
|
||||
"env": "PARSE_SERVER_DATABASE_ADAPTER",
|
||||
"help": "Adapter module for the database",
|
||||
"action": parsers.moduleOrObjectParser
|
||||
},
|
||||
"cloud": {
|
||||
"env": "PARSE_SERVER_CLOUD",
|
||||
"help": "Full path to your cloud code main.js",
|
||||
"action": parsers.objectParser
|
||||
},
|
||||
"collectionPrefix": {
|
||||
"env": "PARSE_SERVER_COLLECTION_PREFIX",
|
||||
"help": "A collection prefix for the classes",
|
||||
"default": ""
|
||||
},
|
||||
"clientKey": {
|
||||
"env": "PARSE_SERVER_CLIENT_KEY",
|
||||
"help": "Key for iOS, MacOS, tvOS clients"
|
||||
},
|
||||
"javascriptKey": {
|
||||
"env": "PARSE_SERVER_JAVASCRIPT_KEY",
|
||||
"help": "Key for the Javascript SDK"
|
||||
},
|
||||
"dotNetKey": {
|
||||
"env": "PARSE_SERVER_DOT_NET_KEY",
|
||||
"help": "Key for Unity and .Net SDK"
|
||||
},
|
||||
"restAPIKey": {
|
||||
"env": "PARSE_SERVER_REST_APIKEY",
|
||||
"help": "Key for REST calls"
|
||||
},
|
||||
"webhookKey": {
|
||||
"env": "PARSE_SERVER_WEBHOOK_KEY",
|
||||
"help": "Key sent with outgoing webhook calls"
|
||||
},
|
||||
"fileKey": {
|
||||
"env": "PARSE_SERVER_FILE_KEY",
|
||||
"help": "Key for your files"
|
||||
},
|
||||
"userSensitiveFields": {
|
||||
"env": "PARSE_SERVER_USER_SENSITIVE_FIELDS",
|
||||
"help": "Personally identifiable information fields in the user table the should be removed for non-authorized users.",
|
||||
"action": parsers.arrayParser,
|
||||
"default": ["email"]
|
||||
},
|
||||
"enableAnonymousUsers": {
|
||||
"env": "PARSE_SERVER_ENABLE_ANON_USERS",
|
||||
"help": "Enable (or disable) anon users, defaults to true",
|
||||
"action": parsers.booleanParser,
|
||||
"default": true
|
||||
},
|
||||
"allowClientClassCreation": {
|
||||
"env": "PARSE_SERVER_ALLOW_CLIENT_CLASS_CREATION",
|
||||
"help": "Enable (or disable) client class creation, defaults to true",
|
||||
"action": parsers.booleanParser,
|
||||
"default": true
|
||||
},
|
||||
"auth": {
|
||||
"env": "PARSE_SERVER_AUTH_PROVIDERS",
|
||||
"help": "Configuration for your authentication providers, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication",
|
||||
"action": parsers.objectParser
|
||||
},
|
||||
"maxUploadSize": {
|
||||
"env": "PARSE_SERVER_MAX_UPLOAD_SIZE",
|
||||
"help": "Max file size for uploads. defaults to 20mb",
|
||||
"default": "20mb"
|
||||
},
|
||||
"verifyUserEmails": {
|
||||
"env": "PARSE_SERVER_VERIFY_USER_EMAILS",
|
||||
"help": "Enable (or disable) user email validation, defaults to false",
|
||||
"action": parsers.booleanParser,
|
||||
"default": false
|
||||
},
|
||||
"preventLoginWithUnverifiedEmail": {
|
||||
"env": "PARSE_SERVER_PREVENT_LOGIN_WITH_UNVERIFIED_EMAIL",
|
||||
"help": "Prevent user from login if email is not verified and PARSE_SERVER_VERIFY_USER_EMAILS is true, defaults to false",
|
||||
"action": parsers.booleanParser,
|
||||
"default": false
|
||||
},
|
||||
"emailVerifyTokenValidityDuration": {
|
||||
"env": "PARSE_SERVER_EMAIL_VERIFY_TOKEN_VALIDITY_DURATION",
|
||||
"help": "Email verification token validity duration",
|
||||
"action": parsers.numberParser("emailVerifyTokenValidityDuration")
|
||||
},
|
||||
"accountLockout": {
|
||||
"env": "PARSE_SERVER_ACCOUNT_LOCKOUT",
|
||||
"help": "account lockout policy for failed login attempts",
|
||||
"action": parsers.objectParser
|
||||
},
|
||||
"passwordPolicy": {
|
||||
"env": "PARSE_SERVER_PASSWORD_POLICY",
|
||||
"help": "Password policy for enforcing password related rules",
|
||||
"action": parsers.objectParser
|
||||
},
|
||||
"cacheAdapter": {
|
||||
"env": "PARSE_SERVER_CACHE_ADAPTER",
|
||||
"help": "Adapter module for the cache",
|
||||
"action": parsers.moduleOrObjectParser
|
||||
},
|
||||
"emailAdapter": {
|
||||
"env": "PARSE_SERVER_EMAIL_ADAPTER",
|
||||
"help": "Adapter module for the email sending",
|
||||
"action": parsers.moduleOrObjectParser
|
||||
},
|
||||
"publicServerURL": {
|
||||
"env": "PARSE_PUBLIC_SERVER_URL",
|
||||
"help": "Public URL to your parse server with http:// or https://."
|
||||
},
|
||||
"customPages": {
|
||||
"env": "PARSE_SERVER_CUSTOM_PAGES",
|
||||
"help": "custom pages for password validation and reset",
|
||||
"action": parsers.objectParser,
|
||||
"default": {}
|
||||
},
|
||||
"liveQuery": {
|
||||
"env": "PARSE_SERVER_LIVE_QUERY",
|
||||
"help": "parse-server's LiveQuery configuration object",
|
||||
"action": parsers.objectParser
|
||||
},
|
||||
"sessionLength": {
|
||||
"env": "PARSE_SERVER_SESSION_LENGTH",
|
||||
"help": "Session duration, in seconds, defaults to 1 year",
|
||||
"action": parsers.numberParser("sessionLength"),
|
||||
"default": 31536000
|
||||
},
|
||||
"maxLimit": {
|
||||
"env": "PARSE_SERVER_MAX_LIMIT",
|
||||
"help": "Max value for limit option on queries, defaults to unlimited",
|
||||
"action": parsers.numberParser("maxLimit")
|
||||
},
|
||||
"expireInactiveSessions": {
|
||||
"env": "PARSE_SERVER_EXPIRE_INACTIVE_SESSIONS",
|
||||
"help": "Sets wether we should expire the inactive sessions, defaults to true",
|
||||
"action": parsers.booleanParser,
|
||||
"default": true
|
||||
},
|
||||
"revokeSessionOnPasswordReset": {
|
||||
"env": "PARSE_SERVER_REVOKE_SESSION_ON_PASSWORD_RESET",
|
||||
"help": "When a user changes their password, either through the reset password email or while logged in, all sessions are revoked if this is true. Set to false if you don't want to revoke sessions.",
|
||||
"action": parsers.booleanParser,
|
||||
"default": true
|
||||
},
|
||||
"schemaCacheTTL": {
|
||||
"env": "PARSE_SERVER_SCHEMA_CACHE_TTL",
|
||||
"help": "The TTL for caching the schema for optimizing read/write operations. You should put a long TTL when your DB is in production. default to 5000; set 0 to disable.",
|
||||
"action": parsers.numberParser("schemaCacheTTL"),
|
||||
"default": 5000
|
||||
},
|
||||
"cacheTTL": {
|
||||
"env": "PARSE_SERVER_CACHE_TTL",
|
||||
"help": "Sets the TTL for the in memory cache (in ms), defaults to 5000 (5 seconds)",
|
||||
"action": parsers.numberParser("cacheTTL"),
|
||||
"default": 5000
|
||||
},
|
||||
"cacheMaxSize": {
|
||||
"env": "PARSE_SERVER_CACHE_MAX_SIZE",
|
||||
"help": "Sets the maximum size for the in memory cache, defaults to 10000",
|
||||
"action": parsers.numberParser("cacheMaxSize"),
|
||||
"default": 10000
|
||||
},
|
||||
"enableSingleSchemaCache": {
|
||||
"env": "PARSE_SERVER_ENABLE_SINGLE_SCHEMA_CACHE",
|
||||
"help": "Use a single schema cache shared across requests. Reduces number of queries made to _SCHEMA. Defaults to false, i.e. unique schema cache per request.",
|
||||
"action": parsers.booleanParser,
|
||||
"default": false
|
||||
},
|
||||
"objectIdSize": {
|
||||
"env": "PARSE_SERVER_OBJECT_ID_SIZE",
|
||||
"help": "Sets the number of characters in generated object id's, default 10",
|
||||
"action": parsers.numberParser("objectIdSize"),
|
||||
"default": 10
|
||||
},
|
||||
"port": {
|
||||
"env": "PARSE_SERVER_PORT",
|
||||
"help": "The port to run the ParseServer. defaults to 1337.",
|
||||
"action": parsers.numberParser("port"),
|
||||
"default": 1337
|
||||
},
|
||||
"host": {
|
||||
"env": "PARSE_SERVER_HOST",
|
||||
"help": "The host to serve ParseServer on. defaults to 0.0.0.0",
|
||||
"default": "0.0.0.0"
|
||||
},
|
||||
"mountPath": {
|
||||
"env": "PARSE_SERVER_MOUNT_PATH",
|
||||
"help": "Mount path for the server, defaults to /parse",
|
||||
"default": "/parse"
|
||||
},
|
||||
"cluster": {
|
||||
"env": "PARSE_SERVER_CLUSTER",
|
||||
"help": "Run with cluster, optionally set the number of processes default to os.cpus().length",
|
||||
"action": parsers.numberOrBooleanParser
|
||||
},
|
||||
"middleware": {
|
||||
"env": "PARSE_SERVER_MIDDLEWARE",
|
||||
"help": "middleware for express server, can be string or function"
|
||||
},
|
||||
"startLiveQueryServer": {
|
||||
"env": "PARSE_SERVER_START_LIVE_QUERY_SERVER",
|
||||
"help": "Starts the liveQuery server",
|
||||
"action": parsers.booleanParser
|
||||
},
|
||||
"liveQueryServerOptions": {
|
||||
"env": "PARSE_SERVER_LIVE_QUERY_SERVER_OPTIONS",
|
||||
"help": "Live query server configuration options (will start the liveQuery server)",
|
||||
"action": parsers.objectParser
|
||||
}
|
||||
};
|
||||
module.exports.CustomPagesOptions = {
|
||||
"invalidLink": {
|
||||
"env": "PARSE_SERVER_CUSTOM_PAGES_INVALID_LINK",
|
||||
"help": "invalid link page path"
|
||||
},
|
||||
"verifyEmailSuccess": {
|
||||
"env": "PARSE_SERVER_CUSTOM_PAGES_VERIFY_EMAIL_SUCCESS",
|
||||
"help": "verify email success page path"
|
||||
},
|
||||
"choosePassword": {
|
||||
"env": "PARSE_SERVER_CUSTOM_PAGES_CHOOSE_PASSWORD",
|
||||
"help": "choose password page path"
|
||||
},
|
||||
"passwordResetSuccess": {
|
||||
"env": "PARSE_SERVER_CUSTOM_PAGES_PASSWORD_RESET_SUCCESS",
|
||||
"help": "password reset success page path"
|
||||
}
|
||||
};
|
||||
module.exports.LiveQueryOptions = {
|
||||
"classNames": {
|
||||
"env": "PARSE_SERVER_LIVEQUERY_CLASSNAMES",
|
||||
"help": "parse-server's LiveQuery classNames",
|
||||
"action": parsers.arrayParser
|
||||
},
|
||||
"redisURL": {
|
||||
"env": "undefinedREDIS_URL",
|
||||
"help": "parse-server's LiveQuery redisURL"
|
||||
},
|
||||
"pubSubAdapter": {
|
||||
"env": "undefinedPUB_SUB_ADAPTER",
|
||||
"help": "LiveQuery pubsub adapter",
|
||||
"action": parsers.moduleOrObjectParser
|
||||
}
|
||||
};
|
||||
module.exports.LiveQueryServerOptions = {
|
||||
"appId": {
|
||||
"env": "PARSE_SERVER_LIVE_QUERY_APP_ID",
|
||||
"help": "This string should match the appId in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same appId."
|
||||
},
|
||||
"masterKey": {
|
||||
"env": "PARSE_SERVER_LIVE_QUERY_MASTER_KEY",
|
||||
"help": "This string should match the masterKey in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same masterKey."
|
||||
},
|
||||
"serverURL": {
|
||||
"env": "PARSE_SERVER_LIVE_QUERY_SERVER_URL",
|
||||
"help": "This string should match the serverURL in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same serverURL."
|
||||
},
|
||||
"keyPairs": {
|
||||
"env": "PARSE_SERVER_LIVE_QUERY_KEY_PAIRS",
|
||||
"help": "A JSON object that serves as a whitelist of keys. It is used for validating clients when they try to connect to the LiveQuery server. Check the following Security section and our protocol specification for details.",
|
||||
"action": parsers.objectParser
|
||||
},
|
||||
"websocketTimeout": {
|
||||
"env": "PARSE_SERVER_LIVE_QUERY_WEBSOCKET_TIMEOUT",
|
||||
"help": "Number of milliseconds between ping/pong frames. The WebSocket server sends ping/pong frames to the clients to keep the WebSocket alive. This value defines the interval of the ping/pong frame from the server to clients. Defaults to 10 * 1000 ms (10 s).",
|
||||
"action": parsers.numberParser("websocketTimeout")
|
||||
},
|
||||
"cacheTimeout": {
|
||||
"env": "PARSE_SERVER_LIVE_QUERY_CACHE_TIMEOUT",
|
||||
"help": "Number in milliseconds. When clients provide the sessionToken to the LiveQuery server, the LiveQuery server will try to fetch its ParseUser's objectId from parse server and store it in the cache. The value defines the duration of the cache. Check the following Security section and our protocol specification for details. Defaults to 30 * 24 * 60 * 60 * 1000 ms (~30 days).",
|
||||
"action": parsers.numberParser("cacheTimeout")
|
||||
},
|
||||
"logLevel": {
|
||||
"env": "PARSE_SERVER_LIVE_QUERY_LOG_LEVEL",
|
||||
"help": "This string defines the log level of the LiveQuery server. We support VERBOSE, INFO, ERROR, NONE. Defaults to INFO."
|
||||
},
|
||||
"port": {
|
||||
"env": "PARSE_SERVER_LIVE_QUERY_PORT",
|
||||
"help": "The port to run the ParseServer. defaults to 1337.",
|
||||
"action": parsers.numberParser("port"),
|
||||
"default": 1337
|
||||
},
|
||||
"redisURL": {
|
||||
"env": "PARSE_SERVER_LIVE_QUERY_REDIS_URL",
|
||||
"help": "parse-server's LiveQuery redisURL"
|
||||
},
|
||||
"pubSubAdapter": {
|
||||
"env": "PARSE_SERVER_LIVE_QUERY_PUB_SUB_ADAPTER",
|
||||
"help": "LiveQuery pubsub adapter",
|
||||
"action": parsers.moduleOrObjectParser
|
||||
}
|
||||
};
|
||||
176
src/Options/index.js
Normal file
176
src/Options/index.js
Normal file
@@ -0,0 +1,176 @@
|
||||
// @flow
|
||||
type Adapter = string|any;
|
||||
type NumberOrBoolean = number|boolean;
|
||||
|
||||
export interface ParseServerOptions {
|
||||
/* Your Parse Application ID
|
||||
:ENV: PARSE_SERVER_APPLICATION_ID */
|
||||
appId: string;
|
||||
/* Your Parse Master Key */
|
||||
masterKey: string;
|
||||
/* URL to your parse server with http:// or https://.
|
||||
:ENV: PARSE_SERVER_URL */
|
||||
serverURL: string;
|
||||
/* Restrict masterKey to be used by only these ips. defaults to [] (allow all ips) */
|
||||
masterKeyIps: ?string[]; // = []
|
||||
/* Sets the app name */
|
||||
appName: ?string;
|
||||
/* Adapter module for the analytics */
|
||||
analyticsAdapter: ?Adapter;
|
||||
/* Adapter module for the files sub-system */
|
||||
filesAdapter: ?Adapter;
|
||||
/* Configuration for push, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#push-notifications */
|
||||
push: ?any;
|
||||
/* Configuration for push scheduling. Defaults to false. */
|
||||
scheduledPush: ?boolean; // = false
|
||||
/* Adapter module for the logging sub-system */
|
||||
loggerAdapter: ?Adapter;
|
||||
/* Log as structured JSON objects
|
||||
:ENV: JSON_LOGS */
|
||||
jsonLogs: ?boolean;
|
||||
/* Folder for the logs (defaults to './logs'); set to null to disable file based logging
|
||||
:ENV: PARSE_SERVER_LOGS_FOLDER */
|
||||
logsFolder: ?string; // = ./logs
|
||||
/* Set the logging to verbose
|
||||
:ENV: VERBOSE */
|
||||
verbose: ?boolean;
|
||||
/* Sets the level for logs */
|
||||
logLevel: ?string;
|
||||
/* Disables console output
|
||||
:ENV: SILENT */
|
||||
silent: ?boolean;
|
||||
/* The full URI to your mongodb database */
|
||||
databaseURI: string; // = mongodb://localhost:27017/parse
|
||||
/* Options to pass to the mongodb client */
|
||||
databaseOptions: ?any;
|
||||
/* Adapter module for the database */
|
||||
databaseAdapter: ?Adapter;
|
||||
/* Full path to your cloud code main.js */
|
||||
cloud: ?any;
|
||||
/* A collection prefix for the classes */
|
||||
collectionPrefix: ?string; // = ''
|
||||
/* Key for iOS, MacOS, tvOS clients */
|
||||
clientKey: ?string;
|
||||
/* Key for the Javascript SDK */
|
||||
javascriptKey: ?string;
|
||||
/* Key for Unity and .Net SDK */
|
||||
dotNetKey: ?string;
|
||||
/* Key for REST calls */
|
||||
restAPIKey: ?string;
|
||||
/* Key sent with outgoing webhook calls */
|
||||
webhookKey: ?string;
|
||||
/* Key for your files */
|
||||
fileKey: ?string;
|
||||
/* Personally identifiable information fields in the user table the should be removed for non-authorized users. */
|
||||
userSensitiveFields: ?string[]; // = ["email"]
|
||||
/* Enable (or disable) anon users, defaults to true
|
||||
:ENV: PARSE_SERVER_ENABLE_ANON_USERS */
|
||||
enableAnonymousUsers: ?boolean; // = true
|
||||
/* Enable (or disable) client class creation, defaults to true
|
||||
:ENV: PARSE_SERVER_ALLOW_CLIENT_CLASS_CREATION */
|
||||
allowClientClassCreation: ?boolean; // = true
|
||||
/* Configuration for your authentication providers, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication
|
||||
:ENV: PARSE_SERVER_AUTH_PROVIDERS */
|
||||
auth: ?any;
|
||||
/* Max file size for uploads. defaults to 20mb */
|
||||
maxUploadSize: ?string; // = 20mb
|
||||
/* Enable (or disable) user email validation, defaults to false */
|
||||
verifyUserEmails: ?boolean; // = false
|
||||
/* Prevent user from login if email is not verified and PARSE_SERVER_VERIFY_USER_EMAILS is true, defaults to false */
|
||||
preventLoginWithUnverifiedEmail: ?boolean; // = false
|
||||
/* Email verification token validity duration */
|
||||
emailVerifyTokenValidityDuration: ?number;
|
||||
/* account lockout policy for failed login attempts */
|
||||
accountLockout: ?any;
|
||||
/* Password policy for enforcing password related rules */
|
||||
passwordPolicy: ?any;
|
||||
/* Adapter module for the cache */
|
||||
cacheAdapter: ?Adapter;
|
||||
/* Adapter module for the email sending */
|
||||
emailAdapter: ?Adapter;
|
||||
/* Public URL to your parse server with http:// or https://.
|
||||
:ENV: PARSE_PUBLIC_SERVER_URL */
|
||||
publicServerURL: ?string;
|
||||
/* custom pages for password validation and reset */
|
||||
customPages: ?CustomPagesOptions; // = {}
|
||||
/* parse-server's LiveQuery configuration object */
|
||||
liveQuery: ?LiveQueryOptions;
|
||||
/* Session duration, in seconds, defaults to 1 year */
|
||||
sessionLength: ?number; // = 31536000
|
||||
/* Max value for limit option on queries, defaults to unlimited */
|
||||
maxLimit: ?number;
|
||||
/* Sets wether we should expire the inactive sessions, defaults to true */
|
||||
expireInactiveSessions: ?boolean; // = true
|
||||
/* When a user changes their password, either through the reset password email or while logged in, all sessions are revoked if this is true. Set to false if you don't want to revoke sessions. */
|
||||
revokeSessionOnPasswordReset: ?boolean; // = true
|
||||
/* The TTL for caching the schema for optimizing read/write operations. You should put a long TTL when your DB is in production. default to 5000; set 0 to disable. */
|
||||
schemaCacheTTL: ?number; // = 5000
|
||||
/* Sets the TTL for the in memory cache (in ms), defaults to 5000 (5 seconds) */
|
||||
cacheTTL: ?number; // = 5000
|
||||
/* Sets the maximum size for the in memory cache, defaults to 10000 */
|
||||
cacheMaxSize : ?number; // = 10000
|
||||
/* Use a single schema cache shared across requests. Reduces number of queries made to _SCHEMA. Defaults to false, i.e. unique schema cache per request. */
|
||||
enableSingleSchemaCache: ?boolean; // = false
|
||||
/* Sets the number of characters in generated object id's, default 10 */
|
||||
objectIdSize: ?number; // = 10
|
||||
/* The port to run the ParseServer. defaults to 1337. */
|
||||
port: ?number; // = 1337
|
||||
/* The host to serve ParseServer on. defaults to 0.0.0.0 */
|
||||
host: ?string; // = 0.0.0.0
|
||||
/* Mount path for the server, defaults to /parse */
|
||||
mountPath: ?string; // = /parse
|
||||
/* Run with cluster, optionally set the number of processes default to os.cpus().length */
|
||||
cluster: ?NumberOrBoolean;
|
||||
/* middleware for express server, can be string or function */
|
||||
middleware: ?((()=>void)|string);
|
||||
/* Starts the liveQuery server */
|
||||
startLiveQueryServer: ?boolean;
|
||||
/* Live query server configuration options (will start the liveQuery server) */
|
||||
liveQueryServerOptions: ?LiveQueryServerOptions;
|
||||
|
||||
__indexBuildCompletionCallbackForTests: ?()=>void;
|
||||
}
|
||||
|
||||
export interface CustomPagesOptions {
|
||||
/* invalid link page path */
|
||||
invalidLink: ?string;
|
||||
/* verify email success page path */
|
||||
verifyEmailSuccess: ?string;
|
||||
/* choose password page path */
|
||||
choosePassword: ?string;
|
||||
/* password reset success page path */
|
||||
passwordResetSuccess: ?string;
|
||||
}
|
||||
|
||||
export interface LiveQueryOptions {
|
||||
/* parse-server's LiveQuery classNames
|
||||
:ENV: PARSE_SERVER_LIVEQUERY_CLASSNAMES */
|
||||
classNames: ?string[],
|
||||
/* parse-server's LiveQuery redisURL */
|
||||
redisURL: ?string,
|
||||
/* LiveQuery pubsub adapter */
|
||||
pubSubAdapter: ?Adapter,
|
||||
}
|
||||
|
||||
export interface LiveQueryServerOptions {
|
||||
/* This string should match the appId in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same appId.*/
|
||||
appId: ?string,
|
||||
/* This string should match the masterKey in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same masterKey.*/
|
||||
masterKey: ?string,
|
||||
/* This string should match the serverURL in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same serverURL.*/
|
||||
serverURL: ?string,
|
||||
/* A JSON object that serves as a whitelist of keys. It is used for validating clients when they try to connect to the LiveQuery server. Check the following Security section and our protocol specification for details.*/
|
||||
keyPairs: ?any,
|
||||
/* Number of milliseconds between ping/pong frames. The WebSocket server sends ping/pong frames to the clients to keep the WebSocket alive. This value defines the interval of the ping/pong frame from the server to clients. Defaults to 10 * 1000 ms (10 s).*/
|
||||
websocketTimeout: ?number,
|
||||
/* Number in milliseconds. When clients provide the sessionToken to the LiveQuery server, the LiveQuery server will try to fetch its ParseUser's objectId from parse server and store it in the cache. The value defines the duration of the cache. Check the following Security section and our protocol specification for details. Defaults to 30 * 24 * 60 * 60 * 1000 ms (~30 days).*/
|
||||
cacheTimeout: ?number,
|
||||
/* This string defines the log level of the LiveQuery server. We support VERBOSE, INFO, ERROR, NONE. Defaults to INFO.*/
|
||||
logLevel: ?string,
|
||||
/* The port to run the ParseServer. defaults to 1337.*/
|
||||
port: ?number, // = 1337
|
||||
/* parse-server's LiveQuery redisURL */
|
||||
redisURL: ?string,
|
||||
/* LiveQuery pubsub adapter */
|
||||
pubSubAdapter: ?Adapter,
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
export function numberParser(key) {
|
||||
function numberParser(key) {
|
||||
return function(opt) {
|
||||
const intOpt = parseInt(opt);
|
||||
if (!Number.isInteger(intOpt)) {
|
||||
@@ -8,7 +8,7 @@ export function numberParser(key) {
|
||||
}
|
||||
}
|
||||
|
||||
export function numberOrBoolParser(key) {
|
||||
function numberOrBoolParser(key) {
|
||||
return function(opt) {
|
||||
if (typeof opt === 'boolean') {
|
||||
return opt;
|
||||
@@ -23,14 +23,14 @@ export function numberOrBoolParser(key) {
|
||||
}
|
||||
}
|
||||
|
||||
export function objectParser(opt) {
|
||||
function objectParser(opt) {
|
||||
if (typeof opt == 'object') {
|
||||
return opt;
|
||||
}
|
||||
return JSON.parse(opt)
|
||||
}
|
||||
|
||||
export function arrayParser(opt) {
|
||||
function arrayParser(opt) {
|
||||
if (Array.isArray(opt)) {
|
||||
return opt;
|
||||
} else if (typeof opt === 'string') {
|
||||
@@ -40,7 +40,7 @@ export function arrayParser(opt) {
|
||||
}
|
||||
}
|
||||
|
||||
export function moduleOrObjectParser(opt) {
|
||||
function moduleOrObjectParser(opt) {
|
||||
if (typeof opt == 'object') {
|
||||
return opt;
|
||||
}
|
||||
@@ -50,16 +50,26 @@ export function moduleOrObjectParser(opt) {
|
||||
return opt;
|
||||
}
|
||||
|
||||
export function booleanParser(opt) {
|
||||
function booleanParser(opt) {
|
||||
if (opt == true || opt == 'true' || opt == '1') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function nullParser(opt) {
|
||||
function nullParser(opt) {
|
||||
if (opt == 'null') {
|
||||
return null;
|
||||
}
|
||||
return opt;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
numberParser,
|
||||
numberOrBoolParser,
|
||||
nullParser,
|
||||
booleanParser,
|
||||
moduleOrObjectParser,
|
||||
arrayParser,
|
||||
objectParser
|
||||
};
|
||||
@@ -5,59 +5,38 @@ var batch = require('./batch'),
|
||||
express = require('express'),
|
||||
middlewares = require('./middlewares'),
|
||||
Parse = require('parse/node').Parse,
|
||||
path = require('path'),
|
||||
url = require('url'),
|
||||
authDataManager = require('./Adapters/Auth');
|
||||
path = require('path');
|
||||
|
||||
import { ParseServerOptions,
|
||||
LiveQueryServerOptions } from './Options';
|
||||
import defaults from './defaults';
|
||||
import * as logging from './logger';
|
||||
import AppCache from './cache';
|
||||
import Config from './Config';
|
||||
import PromiseRouter from './PromiseRouter';
|
||||
import requiredParameter from './requiredParameter';
|
||||
import { AnalyticsRouter } from './Routers/AnalyticsRouter';
|
||||
import { ClassesRouter } from './Routers/ClassesRouter';
|
||||
import { FeaturesRouter } from './Routers/FeaturesRouter';
|
||||
import { InMemoryCacheAdapter } from './Adapters/Cache/InMemoryCacheAdapter';
|
||||
import { AnalyticsController } from './Controllers/AnalyticsController';
|
||||
import { CacheController } from './Controllers/CacheController';
|
||||
import { AnalyticsAdapter } from './Adapters/Analytics/AnalyticsAdapter';
|
||||
import { WinstonLoggerAdapter } from './Adapters/Logger/WinstonLoggerAdapter';
|
||||
import { FilesController } from './Controllers/FilesController';
|
||||
import { FilesRouter } from './Routers/FilesRouter';
|
||||
import { FunctionsRouter } from './Routers/FunctionsRouter';
|
||||
import { GlobalConfigRouter } from './Routers/GlobalConfigRouter';
|
||||
import { GridStoreAdapter } from './Adapters/Files/GridStoreAdapter';
|
||||
import { HooksController } from './Controllers/HooksController';
|
||||
import { HooksRouter } from './Routers/HooksRouter';
|
||||
import { IAPValidationRouter } from './Routers/IAPValidationRouter';
|
||||
import { InstallationsRouter } from './Routers/InstallationsRouter';
|
||||
import { loadAdapter } from './Adapters/AdapterLoader';
|
||||
import { LiveQueryController } from './Controllers/LiveQueryController';
|
||||
import { LoggerController } from './Controllers/LoggerController';
|
||||
import { LogsRouter } from './Routers/LogsRouter';
|
||||
import { ParseLiveQueryServer } from './LiveQuery/ParseLiveQueryServer';
|
||||
import { PublicAPIRouter } from './Routers/PublicAPIRouter';
|
||||
import { PushController } from './Controllers/PushController';
|
||||
import { PushQueue } from './Push/PushQueue';
|
||||
import { PushWorker } from './Push/PushWorker';
|
||||
import { PushRouter } from './Routers/PushRouter';
|
||||
import { CloudCodeRouter } from './Routers/CloudCodeRouter';
|
||||
import { RolesRouter } from './Routers/RolesRouter';
|
||||
import { SchemasRouter } from './Routers/SchemasRouter';
|
||||
import { SessionsRouter } from './Routers/SessionsRouter';
|
||||
import { UserController } from './Controllers/UserController';
|
||||
import { UsersRouter } from './Routers/UsersRouter';
|
||||
import { PurgeRouter } from './Routers/PurgeRouter';
|
||||
import { AudiencesRouter } from './Routers/AudiencesRouter';
|
||||
|
||||
import DatabaseController from './Controllers/DatabaseController';
|
||||
import SchemaCache from './Controllers/SchemaCache';
|
||||
import ParsePushAdapter from 'parse-server-push-adapter';
|
||||
import MongoStorageAdapter from './Adapters/Storage/Mongo/MongoStorageAdapter';
|
||||
import PostgresStorageAdapter from './Adapters/Storage/Postgres/PostgresStorageAdapter';
|
||||
|
||||
import { ParseServerRESTController } from './ParseServerRESTController';
|
||||
import * as controllers from './Controllers';
|
||||
// Mutate the Parse object to add the Cloud Code handlers
|
||||
addParseCloud();
|
||||
|
||||
@@ -90,200 +69,31 @@ addParseCloud();
|
||||
|
||||
class ParseServer {
|
||||
|
||||
constructor({
|
||||
appId = requiredParameter('You must provide an appId!'),
|
||||
masterKey = requiredParameter('You must provide a masterKey!'),
|
||||
masterKeyIps = [],
|
||||
appName,
|
||||
analyticsAdapter,
|
||||
filesAdapter,
|
||||
push,
|
||||
scheduledPush = false,
|
||||
loggerAdapter,
|
||||
jsonLogs = defaults.jsonLogs,
|
||||
logsFolder = defaults.logsFolder,
|
||||
verbose = defaults.verbose,
|
||||
logLevel = defaults.level,
|
||||
silent = defaults.silent,
|
||||
databaseURI = defaults.DefaultMongoURI,
|
||||
databaseOptions,
|
||||
databaseAdapter,
|
||||
cloud,
|
||||
collectionPrefix = '',
|
||||
clientKey,
|
||||
javascriptKey,
|
||||
dotNetKey,
|
||||
restAPIKey,
|
||||
webhookKey,
|
||||
fileKey,
|
||||
userSensitiveFields = [],
|
||||
enableAnonymousUsers = defaults.enableAnonymousUsers,
|
||||
allowClientClassCreation = defaults.allowClientClassCreation,
|
||||
oauth = {},
|
||||
auth = {},
|
||||
serverURL = requiredParameter('You must provide a serverURL!'),
|
||||
maxUploadSize = defaults.maxUploadSize,
|
||||
verifyUserEmails = defaults.verifyUserEmails,
|
||||
preventLoginWithUnverifiedEmail = defaults.preventLoginWithUnverifiedEmail,
|
||||
emailVerifyTokenValidityDuration,
|
||||
accountLockout,
|
||||
passwordPolicy,
|
||||
cacheAdapter,
|
||||
emailAdapter,
|
||||
publicServerURL,
|
||||
customPages = {
|
||||
invalidLink: undefined,
|
||||
verifyEmailSuccess: undefined,
|
||||
choosePassword: undefined,
|
||||
passwordResetSuccess: undefined
|
||||
},
|
||||
liveQuery = {},
|
||||
sessionLength = defaults.sessionLength, // 1 Year in seconds
|
||||
maxLimit,
|
||||
expireInactiveSessions = defaults.expireInactiveSessions,
|
||||
revokeSessionOnPasswordReset = defaults.revokeSessionOnPasswordReset,
|
||||
schemaCacheTTL = defaults.schemaCacheTTL, // cache for 5s
|
||||
cacheTTL = defaults.cacheTTL, // cache for 5s
|
||||
cacheMaxSize = defaults.cacheMaxSize, // 10000
|
||||
enableSingleSchemaCache = false,
|
||||
objectIdSize = defaults.objectIdSize,
|
||||
__indexBuildCompletionCallbackForTests = () => {},
|
||||
}) {
|
||||
|
||||
constructor(options: ParseServerOptions) {
|
||||
injectDefaults(options);
|
||||
const {
|
||||
appId = requiredParameter('You must provide an appId!'),
|
||||
masterKey = requiredParameter('You must provide a masterKey!'),
|
||||
cloud,
|
||||
javascriptKey,
|
||||
serverURL = requiredParameter('You must provide a serverURL!'),
|
||||
__indexBuildCompletionCallbackForTests = () => {},
|
||||
} = options;
|
||||
// Initialize the node client SDK automatically
|
||||
Parse.initialize(appId, javascriptKey || 'unused', masterKey);
|
||||
Parse.serverURL = serverURL;
|
||||
if ((databaseOptions || (databaseURI && databaseURI != defaults.DefaultMongoURI) || collectionPrefix !== '') && databaseAdapter) {
|
||||
throw 'You cannot specify both a databaseAdapter and a databaseURI/databaseOptions/collectionPrefix.';
|
||||
} else if (!databaseAdapter) {
|
||||
databaseAdapter = this.getDatabaseAdapter(databaseURI, collectionPrefix, databaseOptions)
|
||||
} else {
|
||||
databaseAdapter = loadAdapter(databaseAdapter)
|
||||
}
|
||||
|
||||
if (!filesAdapter && !databaseURI) {
|
||||
throw 'When using an explicit database adapter, you must also use an explicit filesAdapter.';
|
||||
}
|
||||
|
||||
userSensitiveFields = Array.from(new Set(userSensitiveFields.concat(
|
||||
defaults.userSensitiveFields,
|
||||
userSensitiveFields
|
||||
)));
|
||||
|
||||
masterKeyIps = Array.from(new Set(masterKeyIps.concat(
|
||||
defaults.masterKeyIps,
|
||||
masterKeyIps
|
||||
)));
|
||||
|
||||
const loggerOptions = { jsonLogs, logsFolder, verbose, logLevel, silent };
|
||||
const loggerControllerAdapter = loadAdapter(loggerAdapter, WinstonLoggerAdapter, loggerOptions);
|
||||
const loggerController = new LoggerController(loggerControllerAdapter, appId, loggerOptions);
|
||||
logging.setLogger(loggerController);
|
||||
|
||||
const filesControllerAdapter = loadAdapter(filesAdapter, () => {
|
||||
return new GridStoreAdapter(databaseURI);
|
||||
});
|
||||
const filesController = new FilesController(filesControllerAdapter, appId);
|
||||
|
||||
const pushOptions = Object.assign({}, push);
|
||||
const pushQueueOptions = pushOptions.queueOptions || {};
|
||||
if (pushOptions.queueOptions) {
|
||||
delete pushOptions.queueOptions;
|
||||
}
|
||||
// Pass the push options too as it works with the default
|
||||
const pushAdapter = loadAdapter(pushOptions && pushOptions.adapter, ParsePushAdapter, pushOptions);
|
||||
// We pass the options and the base class for the adatper,
|
||||
// Note that passing an instance would work too
|
||||
const pushController = new PushController();
|
||||
const hasPushSupport = !!(pushAdapter && push);
|
||||
const hasPushScheduledSupport = hasPushSupport && (scheduledPush === true);
|
||||
const allControllers = controllers.getControllers(options);
|
||||
|
||||
const {
|
||||
disablePushWorker
|
||||
} = pushQueueOptions;
|
||||
|
||||
const pushControllerQueue = new PushQueue(pushQueueOptions);
|
||||
let pushWorker;
|
||||
if (!disablePushWorker) {
|
||||
pushWorker = new PushWorker(pushAdapter, pushQueueOptions);
|
||||
}
|
||||
|
||||
const emailControllerAdapter = loadAdapter(emailAdapter);
|
||||
const userController = new UserController(emailControllerAdapter, appId, { verifyUserEmails });
|
||||
|
||||
const cacheControllerAdapter = loadAdapter(cacheAdapter, InMemoryCacheAdapter, {appId: appId, ttl: cacheTTL, maxSize: cacheMaxSize });
|
||||
const cacheController = new CacheController(cacheControllerAdapter, appId);
|
||||
|
||||
const analyticsControllerAdapter = loadAdapter(analyticsAdapter, AnalyticsAdapter);
|
||||
const analyticsController = new AnalyticsController(analyticsControllerAdapter);
|
||||
|
||||
const liveQueryController = new LiveQueryController(liveQuery);
|
||||
const databaseController = new DatabaseController(databaseAdapter, new SchemaCache(cacheController, schemaCacheTTL, enableSingleSchemaCache));
|
||||
const hooksController = new HooksController(appId, databaseController, webhookKey);
|
||||
|
||||
const dbInitPromise = databaseController.performInitialization();
|
||||
|
||||
if (Object.keys(oauth).length > 0) {
|
||||
/* eslint-disable no-console */
|
||||
console.warn('oauth option is deprecated and will be removed in a future release, please use auth option instead');
|
||||
if (Object.keys(auth).length > 0) {
|
||||
console.warn('You should use only the auth option.');
|
||||
}
|
||||
/* eslint-enable */
|
||||
}
|
||||
|
||||
auth = Object.assign({}, oauth, auth);
|
||||
|
||||
AppCache.put(appId, {
|
||||
appId,
|
||||
masterKey: masterKey,
|
||||
masterKeyIps:masterKeyIps,
|
||||
serverURL: serverURL,
|
||||
collectionPrefix: collectionPrefix,
|
||||
clientKey: clientKey,
|
||||
javascriptKey: javascriptKey,
|
||||
dotNetKey: dotNetKey,
|
||||
restAPIKey: restAPIKey,
|
||||
webhookKey: webhookKey,
|
||||
fileKey: fileKey,
|
||||
analyticsController: analyticsController,
|
||||
cacheController: cacheController,
|
||||
filesController: filesController,
|
||||
pushController: pushController,
|
||||
loggerController: loggerController,
|
||||
hooksController: hooksController,
|
||||
userController: userController,
|
||||
verifyUserEmails: verifyUserEmails,
|
||||
preventLoginWithUnverifiedEmail: preventLoginWithUnverifiedEmail,
|
||||
emailVerifyTokenValidityDuration: emailVerifyTokenValidityDuration,
|
||||
accountLockout: accountLockout,
|
||||
passwordPolicy: passwordPolicy,
|
||||
allowClientClassCreation: allowClientClassCreation,
|
||||
authDataManager: authDataManager(auth, enableAnonymousUsers),
|
||||
appName: appName,
|
||||
publicServerURL: publicServerURL,
|
||||
customPages: customPages,
|
||||
maxUploadSize: maxUploadSize,
|
||||
liveQueryController: liveQueryController,
|
||||
sessionLength: Number(sessionLength),
|
||||
maxLimit: Number(maxLimit),
|
||||
expireInactiveSessions: expireInactiveSessions,
|
||||
jsonLogs,
|
||||
revokeSessionOnPasswordReset,
|
||||
loggerController,
|
||||
databaseController,
|
||||
schemaCacheTTL,
|
||||
enableSingleSchemaCache,
|
||||
userSensitiveFields,
|
||||
pushWorker,
|
||||
pushControllerQueue,
|
||||
hasPushSupport,
|
||||
hasPushScheduledSupport,
|
||||
objectIdSize
|
||||
});
|
||||
hooksController,
|
||||
} = allControllers;
|
||||
this.config = Config.put(Object.assign({}, options, allControllers));
|
||||
|
||||
Config.validate(AppCache.get(appId));
|
||||
this.config = AppCache.get(appId);
|
||||
Config.setupPasswordValidator(this.config.passwordPolicy);
|
||||
logging.setLogger(loggerController);
|
||||
const dbInitPromise = databaseController.performInitialization();
|
||||
hooksController.load();
|
||||
|
||||
// Note: Tests will start to fail if any validation happens after this is called.
|
||||
@@ -303,30 +113,11 @@ class ParseServer {
|
||||
}
|
||||
}
|
||||
|
||||
getDatabaseAdapter(databaseURI, collectionPrefix, databaseOptions) {
|
||||
let protocol;
|
||||
try {
|
||||
const parsedURI = url.parse(databaseURI);
|
||||
protocol = parsedURI.protocol ? parsedURI.protocol.toLowerCase() : null;
|
||||
} catch(e) { /* */ }
|
||||
switch (protocol) {
|
||||
case 'postgres:':
|
||||
return new PostgresStorageAdapter({
|
||||
uri: databaseURI,
|
||||
collectionPrefix,
|
||||
databaseOptions
|
||||
});
|
||||
default:
|
||||
return new MongoStorageAdapter({
|
||||
uri: databaseURI,
|
||||
collectionPrefix,
|
||||
mongoOptions: databaseOptions,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get app() {
|
||||
return ParseServer.app(this.config);
|
||||
if (!this._app) {
|
||||
this._app = ParseServer.app(this.config);
|
||||
}
|
||||
return this._app;
|
||||
}
|
||||
|
||||
handleShutdown() {
|
||||
@@ -363,17 +154,17 @@ class ParseServer {
|
||||
// run the following when not testing
|
||||
if (!process.env.TESTING) {
|
||||
//This causes tests to spew some useless warnings, so disable in test
|
||||
/* istanbul ignore next */
|
||||
process.on('uncaughtException', (err) => {
|
||||
if (err.code === "EADDRINUSE") { // user-friendly message for this common error
|
||||
/* eslint-disable no-console */
|
||||
console.error(`Unable to listen on port ${err.port}. The port is already in use.`);
|
||||
/* eslint-enable no-console */
|
||||
process.stderr.write(`Unable to listen on port ${err.port}. The port is already in use.`);
|
||||
process.exit(0);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
// verify the server url after a 'mount' event is received
|
||||
/* istanbul ignore next */
|
||||
api.on('mount', function() {
|
||||
ParseServer.verifyServerUrl();
|
||||
});
|
||||
@@ -415,7 +206,44 @@ class ParseServer {
|
||||
return appRouter;
|
||||
}
|
||||
|
||||
static createLiveQueryServer(httpServer, config) {
|
||||
start(options: ParseServerOptions, callback: ?()=>void) {
|
||||
const app = express();
|
||||
if (options.middleware) {
|
||||
let middleware;
|
||||
if (typeof options.middleware == 'string') {
|
||||
middleware = require(path.resolve(process.cwd(), options.middleware));
|
||||
} else {
|
||||
middleware = options.middleware; // use as-is let express fail
|
||||
}
|
||||
app.use(middleware);
|
||||
}
|
||||
|
||||
app.use(options.mountPath, this.app);
|
||||
const server = app.listen(options.port, options.host, callback);
|
||||
this.server = server;
|
||||
|
||||
if (options.startLiveQueryServer || options.liveQueryServerOptions) {
|
||||
this.liveQueryServer = ParseServer.createLiveQueryServer(server, options.liveQueryServerOptions);
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
if (!process.env.TESTING) {
|
||||
configureListeners(this);
|
||||
}
|
||||
this.expressApp = app;
|
||||
return this;
|
||||
}
|
||||
|
||||
static start(options: ParseServerOptions, callback: ?()=>void) {
|
||||
const parseServer = new ParseServer(options);
|
||||
return parseServer.start(options, callback);
|
||||
}
|
||||
|
||||
static createLiveQueryServer(httpServer, config: LiveQueryServerOptions) {
|
||||
if (!httpServer || (config && config.port)) {
|
||||
var app = express();
|
||||
httpServer = require('http').createServer(app);
|
||||
httpServer.listen(config.port);
|
||||
}
|
||||
return new ParseLiveQueryServer(httpServer, config);
|
||||
}
|
||||
|
||||
@@ -447,4 +275,59 @@ function addParseCloud() {
|
||||
global.Parse = Parse;
|
||||
}
|
||||
|
||||
function injectDefaults(options: ParseServerOptions) {
|
||||
Object.keys(defaults).forEach((key) => {
|
||||
if (!options.hasOwnProperty(key)) {
|
||||
options[key] = defaults[key];
|
||||
}
|
||||
});
|
||||
|
||||
if (!options.hasOwnProperty('serverURL')) {
|
||||
options.serverURL = `http://localhost:${options.port}${options.mountPath}`;
|
||||
}
|
||||
|
||||
options.userSensitiveFields = Array.from(new Set(options.userSensitiveFields.concat(
|
||||
defaults.userSensitiveFields,
|
||||
options.userSensitiveFields
|
||||
)));
|
||||
|
||||
options.masterKeyIps = Array.from(new Set(options.masterKeyIps.concat(
|
||||
defaults.masterKeyIps,
|
||||
options.masterKeyIps
|
||||
)));
|
||||
}
|
||||
|
||||
// Those can't be tested as it requires a subprocess
|
||||
/* istanbul ignore next */
|
||||
function configureListeners(parseServer) {
|
||||
const server = parseServer.server;
|
||||
const sockets = {};
|
||||
/* Currently, express doesn't shut down immediately after receiving SIGINT/SIGTERM if it has client connections that haven't timed out. (This is a known issue with node - https://github.com/nodejs/node/issues/2642)
|
||||
This function, along with `destroyAliveConnections()`, intend to fix this behavior such that parse server will close all open connections and initiate the shutdown process as soon as it receives a SIGINT/SIGTERM signal. */
|
||||
server.on('connection', (socket) => {
|
||||
const socketId = socket.remoteAddress + ':' + socket.remotePort;
|
||||
sockets[socketId] = socket;
|
||||
socket.on('close', () => {
|
||||
delete sockets[socketId];
|
||||
});
|
||||
});
|
||||
|
||||
const destroyAliveConnections = function() {
|
||||
for (const socketId in sockets) {
|
||||
try {
|
||||
sockets[socketId].destroy();
|
||||
} catch (e) { /* */ }
|
||||
}
|
||||
}
|
||||
|
||||
const handleShutdown = function() {
|
||||
process.stdout.write('Termination signal received. Shutting down.');
|
||||
destroyAliveConnections();
|
||||
server.close();
|
||||
parseServer.handleShutdown();
|
||||
};
|
||||
process.on('SIGTERM', handleShutdown);
|
||||
process.on('SIGINT', handleShutdown);
|
||||
}
|
||||
|
||||
export default ParseServer;
|
||||
|
||||
@@ -35,7 +35,7 @@ function ParseServerRESTController(applicationId, router) {
|
||||
// Store the arguments, for later use if internal fails
|
||||
const args = arguments;
|
||||
|
||||
const config = new Config(applicationId);
|
||||
const config = Config.get(applicationId);
|
||||
const serverURL = URL.parse(config.serverURL);
|
||||
if (path.indexOf(serverURL.path) === 0) {
|
||||
path = path.slice(serverURL.path.length, path.length);
|
||||
|
||||
@@ -48,7 +48,7 @@ export class PushWorker {
|
||||
}
|
||||
|
||||
run({ body, query, pushStatus, applicationId, UTCOffset }: any): Promise<*> {
|
||||
const config = new Config(applicationId);
|
||||
const config = Config.get(applicationId);
|
||||
const auth = master(config);
|
||||
const where = utils.applyDeviceTokenExists(query.where);
|
||||
delete query.where;
|
||||
|
||||
@@ -34,7 +34,7 @@ export class FilesRouter {
|
||||
}
|
||||
|
||||
getHandler(req, res) {
|
||||
const config = new Config(req.params.appId);
|
||||
const config = Config.get(req.params.appId);
|
||||
const filesController = config.filesController;
|
||||
const filename = req.params.filename;
|
||||
const contentType = mime.lookup(filename);
|
||||
|
||||
@@ -13,7 +13,7 @@ export class PublicAPIRouter extends PromiseRouter {
|
||||
verifyEmail(req) {
|
||||
const { token, username } = req.query;
|
||||
const appId = req.params.appId;
|
||||
const config = new Config(appId);
|
||||
const config = Config.get(appId);
|
||||
|
||||
if (!config.publicServerURL) {
|
||||
return this.missingPublicServerURL();
|
||||
@@ -38,7 +38,7 @@ export class PublicAPIRouter extends PromiseRouter {
|
||||
resendVerificationEmail(req) {
|
||||
const username = req.body.username;
|
||||
const appId = req.params.appId;
|
||||
const config = new Config(appId);
|
||||
const config = Config.get(appId);
|
||||
|
||||
if (!config.publicServerURL) {
|
||||
return this.missingPublicServerURL();
|
||||
@@ -65,7 +65,7 @@ export class PublicAPIRouter extends PromiseRouter {
|
||||
|
||||
changePassword(req) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const config = new Config(req.query.id);
|
||||
const config = Config.get(req.query.id);
|
||||
if (!config.publicServerURL) {
|
||||
return resolve({
|
||||
status: 404,
|
||||
@@ -172,7 +172,7 @@ export class PublicAPIRouter extends PromiseRouter {
|
||||
}
|
||||
|
||||
setConfig(req) {
|
||||
req.config = new Config(req.params.appId);
|
||||
req.config = Config.get(req.params.appId);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,42 +1,2 @@
|
||||
import {
|
||||
numberParser
|
||||
} from '../utils/parsers';
|
||||
|
||||
|
||||
export default {
|
||||
"appId": {
|
||||
required: true,
|
||||
help: "Required. This string should match the appId in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same appId."
|
||||
},
|
||||
"masterKey": {
|
||||
required: true,
|
||||
help: "Required. This string should match the masterKey in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same masterKey."
|
||||
},
|
||||
"serverURL": {
|
||||
required: true,
|
||||
help: "Required. This string should match the serverURL in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same serverURL."
|
||||
},
|
||||
"redisURL": {
|
||||
help: "Optional. This string should match the masterKey in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same masterKey."
|
||||
},
|
||||
"keyPairs": {
|
||||
help: "Optional. A JSON object that serves as a whitelist of keys. It is used for validating clients when they try to connect to the LiveQuery server. Check the following Security section and our protocol specification for details."
|
||||
},
|
||||
"websocketTimeout": {
|
||||
help: "Optional. Number of milliseconds between ping/pong frames. The WebSocket server sends ping/pong frames to the clients to keep the WebSocket alive. This value defines the interval of the ping/pong frame from the server to clients. Defaults to 10 * 1000 ms (10 s).",
|
||||
action: numberParser("websocketTimeout")
|
||||
},
|
||||
"cacheTimeout": {
|
||||
help: "Optional. Number in milliseconds. When clients provide the sessionToken to the LiveQuery server, the LiveQuery server will try to fetch its ParseUser's objectId from parse server and store it in the cache. The value defines the duration of the cache. Check the following Security section and our protocol specification for details. Defaults to 30 * 24 * 60 * 60 * 1000 ms (~30 days).",
|
||||
action: numberParser("cacheTimeout")
|
||||
},
|
||||
"logLevel": {
|
||||
help: "Optional. This string defines the log level of the LiveQuery server. We support VERBOSE, INFO, ERROR, NONE. Defaults to INFO.",
|
||||
},
|
||||
"port": {
|
||||
env: "PORT",
|
||||
help: "The port to run the ParseServer. defaults to 1337.",
|
||||
default: 1337,
|
||||
action: numberParser("port")
|
||||
},
|
||||
};
|
||||
const LiveQueryServerOptions = require('../../Options/Definitions').LiveQueryServerOptions;
|
||||
export default LiveQueryServerOptions;
|
||||
|
||||
@@ -1,284 +1,2 @@
|
||||
import {
|
||||
numberParser,
|
||||
numberOrBoolParser,
|
||||
objectParser,
|
||||
arrayParser,
|
||||
moduleOrObjectParser,
|
||||
booleanParser,
|
||||
nullParser
|
||||
} from '../utils/parsers';
|
||||
|
||||
export default {
|
||||
"appId": {
|
||||
env: "PARSE_SERVER_APPLICATION_ID",
|
||||
help: "Your Parse Application ID",
|
||||
required: true
|
||||
},
|
||||
"masterKey": {
|
||||
env: "PARSE_SERVER_MASTER_KEY",
|
||||
help: "Your Parse Master Key",
|
||||
required: true
|
||||
},
|
||||
"masterKeyIps": {
|
||||
env: "PARSE_SERVER_MASTER_KEY_IPS",
|
||||
help: "Restrict masterKey to be used by only these ips. defaults to [] (allow all ips)",
|
||||
default: []
|
||||
},
|
||||
"port": {
|
||||
env: "PORT",
|
||||
help: "The port to run the ParseServer. defaults to 1337.",
|
||||
default: 1337,
|
||||
action: numberParser("port")
|
||||
},
|
||||
"host": {
|
||||
env: "PARSE_SERVER_HOST",
|
||||
help: "The host to serve ParseServer on. defaults to 0.0.0.0",
|
||||
default: '0.0.0.0',
|
||||
},
|
||||
"databaseURI": {
|
||||
env: "PARSE_SERVER_DATABASE_URI",
|
||||
help: "The full URI to your mongodb database"
|
||||
},
|
||||
"databaseOptions": {
|
||||
env: "PARSE_SERVER_DATABASE_OPTIONS",
|
||||
help: "Options to pass to the mongodb client",
|
||||
action: objectParser
|
||||
},
|
||||
"collectionPrefix": {
|
||||
env: "PARSE_SERVER_COLLECTION_PREFIX",
|
||||
help: 'A collection prefix for the classes'
|
||||
},
|
||||
"serverURL": {
|
||||
env: "PARSE_SERVER_URL",
|
||||
help: "URL to your parse server with http:// or https://.",
|
||||
},
|
||||
"publicServerURL": {
|
||||
env: "PARSE_PUBLIC_SERVER_URL",
|
||||
help: "Public URL to your parse server with http:// or https://.",
|
||||
},
|
||||
"clientKey": {
|
||||
env: "PARSE_SERVER_CLIENT_KEY",
|
||||
help: "Key for iOS, MacOS, tvOS clients"
|
||||
},
|
||||
"javascriptKey": {
|
||||
env: "PARSE_SERVER_JAVASCRIPT_KEY",
|
||||
help: "Key for the Javascript SDK"
|
||||
},
|
||||
"restAPIKey": {
|
||||
env: "PARSE_SERVER_REST_API_KEY",
|
||||
help: "Key for REST calls"
|
||||
},
|
||||
"dotNetKey": {
|
||||
env: "PARSE_SERVER_DOT_NET_KEY",
|
||||
help: "Key for Unity and .Net SDK"
|
||||
},
|
||||
"webhookKey": {
|
||||
env: "PARSE_SERVER_WEBHOOK_KEY",
|
||||
help: "Key sent with outgoing webhook calls"
|
||||
},
|
||||
"cloud": {
|
||||
env: "PARSE_SERVER_CLOUD_CODE_MAIN",
|
||||
help: "Full path to your cloud code main.js"
|
||||
},
|
||||
"push": {
|
||||
env: "PARSE_SERVER_PUSH",
|
||||
help: "Configuration for push, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#push-notifications",
|
||||
action: objectParser
|
||||
},
|
||||
"scheduledPush": {
|
||||
env: "PARSE_SERVER_SCHEDULED_PUSH",
|
||||
help: "Configuration for push scheduling. Defaults to false.",
|
||||
action: booleanParser
|
||||
},
|
||||
"oauth": {
|
||||
env: "PARSE_SERVER_OAUTH_PROVIDERS",
|
||||
help: "[DEPRECATED (use auth option)] Configuration for your oAuth providers, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication",
|
||||
action: objectParser
|
||||
},
|
||||
"auth": {
|
||||
env: "PARSE_SERVER_AUTH_PROVIDERS",
|
||||
help: "Configuration for your authentication providers, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication",
|
||||
action: objectParser
|
||||
},
|
||||
"fileKey": {
|
||||
env: "PARSE_SERVER_FILE_KEY",
|
||||
help: "Key for your files",
|
||||
},
|
||||
"facebookAppIds": {
|
||||
env: "PARSE_SERVER_FACEBOOK_APP_IDS",
|
||||
help: "[DEPRECATED (use auth option)]",
|
||||
action: function() {
|
||||
throw 'facebookAppIds is deprecated, please use { auth: \
|
||||
{facebook: \
|
||||
{ appIds: [] } \
|
||||
}\
|
||||
}\
|
||||
}';
|
||||
}
|
||||
},
|
||||
"enableAnonymousUsers": {
|
||||
env: "PARSE_SERVER_ENABLE_ANON_USERS",
|
||||
help: "Enable (or disable) anon users, defaults to true",
|
||||
action: booleanParser
|
||||
},
|
||||
"allowClientClassCreation": {
|
||||
env: "PARSE_SERVER_ALLOW_CLIENT_CLASS_CREATION",
|
||||
help: "Enable (or disable) client class creation, defaults to true",
|
||||
action: booleanParser
|
||||
},
|
||||
"mountPath": {
|
||||
env: "PARSE_SERVER_MOUNT_PATH",
|
||||
help: "Mount path for the server, defaults to /parse",
|
||||
default: "/parse"
|
||||
},
|
||||
"filesAdapter": {
|
||||
env: "PARSE_SERVER_FILES_ADAPTER",
|
||||
help: "Adapter module for the files sub-system",
|
||||
action: moduleOrObjectParser
|
||||
},
|
||||
"emailAdapter": {
|
||||
env: "PARSE_SERVER_EMAIL_ADAPTER",
|
||||
help: "Adapter module for the email sending",
|
||||
action: moduleOrObjectParser
|
||||
},
|
||||
"verifyUserEmails": {
|
||||
env: "PARSE_SERVER_VERIFY_USER_EMAILS",
|
||||
help: "Enable (or disable) user email validation, defaults to false",
|
||||
action: booleanParser
|
||||
},
|
||||
"preventLoginWithUnverifiedEmail": {
|
||||
env: "PARSE_SERVER_PREVENT_LOGIN_WITH_UNVERIFIED_EMAIL",
|
||||
help: "Prevent user from login if email is not verified and PARSE_SERVER_VERIFY_USER_EMAILS is true, defaults to false",
|
||||
action: booleanParser
|
||||
},
|
||||
"emailVerifyTokenValidityDuration": {
|
||||
env: "PARSE_SERVER_EMAIL_VERIFY_TOKEN_VALIDITY_DURATION",
|
||||
help: "Email verification token validity duration",
|
||||
action: numberParser("emailVerifyTokenValidityDuration")
|
||||
},
|
||||
"accountLockout": {
|
||||
env: "PARSE_SERVER_ACCOUNT_LOCKOUT",
|
||||
help: "account lockout policy for failed login attempts",
|
||||
action: objectParser
|
||||
},
|
||||
"passwordPolicy": {
|
||||
env: "PARSE_SERVER_PASSWORD_POLICY",
|
||||
help: "Password policy for enforcing password related rules",
|
||||
action: objectParser
|
||||
},
|
||||
"appName": {
|
||||
env: "PARSE_SERVER_APP_NAME",
|
||||
help: "Sets the app name"
|
||||
},
|
||||
"loggerAdapter": {
|
||||
env: "PARSE_SERVER_LOGGER_ADAPTER",
|
||||
help: "Adapter module for the logging sub-system",
|
||||
action: moduleOrObjectParser
|
||||
},
|
||||
"customPages": {
|
||||
env: "PARSE_SERVER_CUSTOM_PAGES",
|
||||
help: "custom pages for password validation and reset",
|
||||
action: objectParser
|
||||
},
|
||||
"maxUploadSize": {
|
||||
env: "PARSE_SERVER_MAX_UPLOAD_SIZE",
|
||||
help: "Max file size for uploads.",
|
||||
default: "20mb"
|
||||
},
|
||||
"userSensitiveFields": {
|
||||
help: "Personally identifiable information fields in the user table the should be removed for non-authorized users.",
|
||||
default: ["email"]
|
||||
},
|
||||
"sessionLength": {
|
||||
env: "PARSE_SERVER_SESSION_LENGTH",
|
||||
help: "Session duration, defaults to 1 year",
|
||||
action: numberParser("sessionLength")
|
||||
},
|
||||
"maxLimit": {
|
||||
env: "PARSE_SERVER_MAX_LIMIT",
|
||||
help: "Max value for limit option on queries, defaults to unlimited",
|
||||
action: numberParser("maxLimit")
|
||||
},
|
||||
"verbose": {
|
||||
env: "VERBOSE",
|
||||
help: "Set the logging to verbose"
|
||||
},
|
||||
"jsonLogs": {
|
||||
env: "JSON_LOGS",
|
||||
help: "Log as structured JSON objects"
|
||||
},
|
||||
"logLevel": {
|
||||
env: "PARSE_SERVER_LOG_LEVEL",
|
||||
help: "Sets the level for logs"
|
||||
},
|
||||
"logsFolder": {
|
||||
env: "PARSE_SERVER_LOGS_FOLDER",
|
||||
help: "Folder for the logs (defaults to './logs'); set to null to disable file based logging",
|
||||
action: nullParser
|
||||
},
|
||||
"silent": {
|
||||
help: "Disables console output",
|
||||
},
|
||||
"revokeSessionOnPasswordReset": {
|
||||
env: "PARSE_SERVER_REVOKE_SESSION_ON_PASSWORD_RESET",
|
||||
help: "When a user changes their password, either through the reset password email or while logged in, all sessions are revoked if this is true. Set to false if you don't want to revoke sessions.",
|
||||
action: booleanParser
|
||||
},
|
||||
"schemaCacheTTL": {
|
||||
env: "PARSE_SERVER_SCHEMA_CACHE_TTL",
|
||||
help: "The TTL for caching the schema for optimizing read/write operations. You should put a long TTL when your DB is in production. default to 0; disabled.",
|
||||
action: numberParser("schemaCacheTTL"),
|
||||
},
|
||||
"enableSingleSchemaCache": {
|
||||
env: "PARSE_SERVER_ENABLE_SINGLE_SCHEMA_CACHE",
|
||||
help: "Use a single schema cache shared across requests. Reduces number of queries made to _SCHEMA. Defaults to false, i.e. unique schema cache per request.",
|
||||
action: booleanParser
|
||||
},
|
||||
"cacheTTL": {
|
||||
env: "PARSE_SERVER_CACHE_TTL",
|
||||
help: "Sets the TTL for the in memory cache (in ms), defaults to 5000 (5 seconds)",
|
||||
action: numberParser("cacheTTL"),
|
||||
},
|
||||
"cacheMaxSize": {
|
||||
env: "PARSE_SERVER_CACHE_MAX_SIZE",
|
||||
help: "Sets the maximum size for the in memory cache, defaults to 10000",
|
||||
action: numberParser("cacheMaxSize")
|
||||
},
|
||||
"cluster": {
|
||||
env: "PARSE_SERVER_CLUSTER",
|
||||
help: "Run with cluster, optionally set the number of processes default to os.cpus().length",
|
||||
action: numberOrBoolParser("cluster")
|
||||
},
|
||||
"liveQuery": {
|
||||
env: "PARSE_SERVER_LIVE_QUERY_OPTIONS",
|
||||
help: "parse-server's LiveQuery configuration object",
|
||||
action: objectParser
|
||||
},
|
||||
"liveQuery.classNames": {
|
||||
help: "parse-server's LiveQuery classNames",
|
||||
action: arrayParser
|
||||
},
|
||||
"liveQuery.redisURL": {
|
||||
help: "parse-server's LiveQuery redisURL",
|
||||
},
|
||||
"startLiveQueryServer": {
|
||||
help: "Starts the liveQuery server",
|
||||
action: booleanParser
|
||||
},
|
||||
"liveQueryPort": {
|
||||
help: 'Specific port to start the live query server',
|
||||
action: numberParser("liveQueryPort")
|
||||
},
|
||||
"liveQueryServerOptions": {
|
||||
help: "Live query server configuration options (will start the liveQuery server)",
|
||||
action: objectParser
|
||||
},
|
||||
"middleware": {
|
||||
help: "middleware for express server, can be string or function"
|
||||
},
|
||||
"objectIdSize": {
|
||||
env: "PARSE_SERVER_OBJECT_ID_SIZE",
|
||||
help: "Sets the number of characters in generated object id's, default 10",
|
||||
action: numberParser("objectIdSize")
|
||||
}
|
||||
};
|
||||
const ParseServerDefinitions = require('../../Options/Definitions').ParseServerOptions;
|
||||
export default ParseServerDefinitions;
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import definitions from './definitions/parse-live-query-server';
|
||||
import runner from './utils/runner';
|
||||
import { ParseServer } from '../index';
|
||||
import express from 'express';
|
||||
|
||||
runner({
|
||||
definitions,
|
||||
start: function(program, options, logOptions) {
|
||||
logOptions();
|
||||
var app = express();
|
||||
var httpServer = require('http').createServer(app);
|
||||
httpServer.listen(options.port);
|
||||
ParseServer.createLiveQueryServer(httpServer, options);
|
||||
ParseServer.createLiveQueryServer(undefined, options);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
/* eslint-disable no-console */
|
||||
import express from 'express';
|
||||
import ParseServer from '../index';
|
||||
import definitions from './definitions/parse-server';
|
||||
import cluster from 'cluster';
|
||||
import os from 'os';
|
||||
import runner from './utils/runner';
|
||||
const path = require("path");
|
||||
|
||||
const help = function(){
|
||||
console.log(' Get Started guide:');
|
||||
@@ -29,79 +27,12 @@ const help = function(){
|
||||
console.log('');
|
||||
};
|
||||
|
||||
function startServer(options, callback) {
|
||||
const app = express();
|
||||
if (options.middleware) {
|
||||
let middleware;
|
||||
if (typeof options.middleware == 'function') {
|
||||
middleware = options.middleware;
|
||||
} if (typeof options.middleware == 'string') {
|
||||
middleware = require(path.resolve(process.cwd(), options.middleware));
|
||||
} else {
|
||||
throw "middleware should be a string or a function";
|
||||
}
|
||||
app.use(middleware);
|
||||
}
|
||||
|
||||
const parseServer = new ParseServer(options);
|
||||
const sockets = {};
|
||||
app.use(options.mountPath, parseServer.app);
|
||||
|
||||
const server = app.listen(options.port, options.host, callback);
|
||||
server.on('connection', initializeConnections);
|
||||
|
||||
if (options.startLiveQueryServer || options.liveQueryServerOptions) {
|
||||
let liveQueryServer = server;
|
||||
if (options.liveQueryPort) {
|
||||
liveQueryServer = express().listen(options.liveQueryPort, () => {
|
||||
console.log('ParseLiveQuery listening on ' + options.liveQueryPort);
|
||||
});
|
||||
}
|
||||
ParseServer.createLiveQueryServer(liveQueryServer, options.liveQueryServerOptions);
|
||||
}
|
||||
|
||||
function initializeConnections(socket) {
|
||||
/* Currently, express doesn't shut down immediately after receiving SIGINT/SIGTERM if it has client connections that haven't timed out. (This is a known issue with node - https://github.com/nodejs/node/issues/2642)
|
||||
|
||||
This function, along with `destroyAliveConnections()`, intend to fix this behavior such that parse server will close all open connections and initiate the shutdown process as soon as it receives a SIGINT/SIGTERM signal. */
|
||||
|
||||
const socketId = socket.remoteAddress + ':' + socket.remotePort;
|
||||
sockets[socketId] = socket;
|
||||
|
||||
socket.on('close', () => {
|
||||
delete sockets[socketId];
|
||||
});
|
||||
}
|
||||
|
||||
function destroyAliveConnections() {
|
||||
for (const socketId in sockets) {
|
||||
try {
|
||||
sockets[socketId].destroy();
|
||||
} catch (e) { /* */ }
|
||||
}
|
||||
}
|
||||
|
||||
const handleShutdown = function() {
|
||||
console.log('Termination signal received. Shutting down.');
|
||||
destroyAliveConnections();
|
||||
server.close();
|
||||
parseServer.handleShutdown();
|
||||
};
|
||||
process.on('SIGTERM', handleShutdown);
|
||||
process.on('SIGINT', handleShutdown);
|
||||
}
|
||||
|
||||
|
||||
runner({
|
||||
definitions,
|
||||
help,
|
||||
usage: '[options] <path/to/configuration.json>',
|
||||
start: function(program, options, logOptions) {
|
||||
if (!options.serverURL) {
|
||||
options.serverURL = `http://localhost:${options.port}${options.mountPath}`;
|
||||
}
|
||||
|
||||
if (!options.appId || !options.masterKey || !options.serverURL) {
|
||||
if (!options.appId || !options.masterKey) {
|
||||
program.outputHelp();
|
||||
console.error("");
|
||||
console.error('\u001b[31mERROR: appId and masterKey are required\u001b[0m');
|
||||
@@ -132,12 +63,12 @@ runner({
|
||||
cluster.fork();
|
||||
});
|
||||
} else {
|
||||
startServer(options, () => {
|
||||
ParseServer.start(options, () => {
|
||||
console.log('[' + process.pid + '] parse-server running on ' + options.serverURL);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
startServer(options, () => {
|
||||
ParseServer.start(options, () => {
|
||||
logOptions();
|
||||
console.log('');
|
||||
console.log('[' + process.pid + '] parse-server running on ' + options.serverURL);
|
||||
|
||||
@@ -20,13 +20,6 @@ Command.prototype.loadDefinitions = function(definitions) {
|
||||
return program.option(`--${opt} [${opt}]`);
|
||||
}, this);
|
||||
|
||||
_defaults = Object.keys(definitions).reduce((defs, opt) => {
|
||||
if(_definitions[opt].default) {
|
||||
defs[opt] = _definitions[opt].default;
|
||||
}
|
||||
return defs;
|
||||
}, {});
|
||||
|
||||
_reverseDefinitions = Object.keys(definitions).reduce((object, key) => {
|
||||
let value = definitions[key];
|
||||
if (typeof value == "object") {
|
||||
@@ -38,6 +31,13 @@ Command.prototype.loadDefinitions = function(definitions) {
|
||||
return object;
|
||||
}, {});
|
||||
|
||||
_defaults = Object.keys(definitions).reduce((defs, opt) => {
|
||||
if(_definitions[opt].default) {
|
||||
defs[opt] = _definitions[opt].default;
|
||||
}
|
||||
return defs;
|
||||
}, {});
|
||||
|
||||
/* istanbul ignore next */
|
||||
this.on('--help', function(){
|
||||
console.log(' Configure From Environment:');
|
||||
|
||||
@@ -8,7 +8,13 @@ function logStartupOptions(options) {
|
||||
value = "***REDACTED***";
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
value = JSON.stringify(value);
|
||||
try {
|
||||
value = JSON.stringify(value)
|
||||
} catch(e) {
|
||||
if (value && value.constructor && value.constructor.name) {
|
||||
value = value.constructor.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* eslint-disable no-console */
|
||||
console.log(`${key}: ${value}`);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {nullParser} from './cli/utils/parsers';
|
||||
|
||||
import { nullParser } from './Options/parsers';
|
||||
const { ParseServerOptions } = require('./Options/Definitions');
|
||||
const logsFolder = (() => {
|
||||
let folder = './logs/';
|
||||
if (typeof process !== 'undefined' && process.env.TESTING === '1') {
|
||||
@@ -16,25 +16,21 @@ const { verbose, level } = (() => {
|
||||
return { verbose, level: verbose ? 'verbose' : undefined }
|
||||
})();
|
||||
|
||||
export default {
|
||||
DefaultMongoURI: 'mongodb://localhost:27017/parse',
|
||||
|
||||
const DefinitionDefaults = Object.keys(ParseServerOptions).reduce((memo, key) => {
|
||||
const def = ParseServerOptions[key];
|
||||
if (def.hasOwnProperty('default')) {
|
||||
memo[key] = def.default;
|
||||
}
|
||||
return memo;
|
||||
}, {});
|
||||
|
||||
const computedDefaults = {
|
||||
jsonLogs: process.env.JSON_LOGS || false,
|
||||
logsFolder,
|
||||
verbose,
|
||||
level,
|
||||
silent: false,
|
||||
enableAnonymousUsers: true,
|
||||
allowClientClassCreation: true,
|
||||
maxUploadSize: '20mb',
|
||||
verifyUserEmails: false,
|
||||
preventLoginWithUnverifiedEmail: false,
|
||||
sessionLength: 31536000,
|
||||
expireInactiveSessions: true,
|
||||
revokeSessionOnPasswordReset: true,
|
||||
schemaCacheTTL: 5000, // in ms
|
||||
cacheTTL: 5000,
|
||||
cacheMaxSize: 10000,
|
||||
userSensitiveFields: ['email'],
|
||||
objectIdSize: 10,
|
||||
masterKeyIps: []
|
||||
}
|
||||
|
||||
export default Object.assign({}, DefinitionDefaults, computedDefaults);
|
||||
export const DefaultMongoURI = DefinitionDefaults.databaseURI;
|
||||
|
||||
@@ -9,14 +9,16 @@ import * as TestUtils from './TestUtils';
|
||||
import { useExternal } from './deprecated';
|
||||
import { getLogger } from './logger';
|
||||
import { PushWorker } from './Push/PushWorker';
|
||||
import { ParseServerOptions } from './Options';
|
||||
|
||||
// Factory function
|
||||
const _ParseServer = function(options) {
|
||||
const _ParseServer = function(options: ParseServerOptions) {
|
||||
const server = new ParseServer(options);
|
||||
return server.app;
|
||||
}
|
||||
// Mount the create liveQueryServer
|
||||
_ParseServer.createLiveQueryServer = ParseServer.createLiveQueryServer;
|
||||
_ParseServer.start = ParseServer.start;
|
||||
|
||||
const GCSAdapter = useExternal('GCSAdapter', 'parse-server-gcs-adapter');
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ export function handleParseHeaders(req, res, next) {
|
||||
const clientIp = getClientIp(req);
|
||||
|
||||
info.app = AppCache.get(info.appId);
|
||||
req.config = new Config(info.appId, mount);
|
||||
req.config = Config.get(info.appId, mount);
|
||||
req.config.headers = req.headers || {};
|
||||
req.config.ip = clientIp;
|
||||
req.info = info;
|
||||
|
||||
Reference in New Issue
Block a user