UNPKG

10.1 kBJavaScriptView Raw
1'use strict';
2
3// Setup the Form.IO server.
4const express = require('express');
5const cors = require('cors');
6const router = express.Router();
7const mongoose = require('mongoose');
8mongoose.Promise = global.Promise;
9const bodyParser = require('body-parser');
10const methodOverride = require('method-override');
11const _ = require('lodash');
12const events = require('events');
13const Q = require('q');
14const nunjucks = require('nunjucks');
15const util = require('./src/util/util');
16const log = require('debug')('formio:log');
17// Keep track of the formio interface.
18router.formio = {};
19
20// Allow libraries to use a single instance of mongoose.
21router.formio.mongoose = mongoose;
22
23// Use custom template delimiters.
24_.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
25
26// Allow custom configurations passed to the Form.IO server.
27module.exports = function(config) {
28 // Give app a reference to our config.
29 router.formio.config = config;
30
31 // Add the middleware.
32 router.formio.middleware = require('./src/middleware/middleware')(router);
33
34 // Configure nunjucks to not watch any files
35 nunjucks.configure([], {
36 watch: false
37 });
38
39 // Allow events to be triggered.
40 router.formio.events = new events.EventEmitter();
41 router.formio.config.schema = require('./package.json').schema;
42
43 router.formio.log = (event, req, ...info) => {
44 const result = router.formio.hook.alter('log', event, req, ...info);
45
46 if (result) {
47 log(event, ...info);
48 }
49 };
50
51 /**
52 * Initialize the formio server.
53 */
54 router.init = function(hooks) {
55 const deferred = Q.defer();
56
57 // Hooks system during boot.
58 router.formio.hooks = hooks;
59
60 // Add the utils to the formio object.
61 router.formio.util = util;
62
63 // Get the hook system.
64 router.formio.hook = require('./src/util/hook')(router.formio);
65
66 // Get the encryption system.
67 router.formio.encrypt = require('./src/util/encrypt');
68
69 // Load the updates and attach them to the router.
70 router.formio.update = require('./src/db/index')(router.formio);
71
72 // Run the healthCheck sanity check on /health
73 /* eslint-disable max-statements */
74 router.formio.update.initialize(function(err, db) {
75 // If an error occurred, then reject the initialization.
76 if (err) {
77 return deferred.reject(err);
78 }
79
80 util.log('Initializing API Server.');
81
82 // Add the database connection to the router.
83 router.formio.db = db;
84
85 // Establish our url alias middleware.
86 if (!router.formio.hook.invoke('init', 'alias', router.formio)) {
87 router.use(router.formio.middleware.alias);
88 }
89
90 // Establish the parameters.
91 if (!router.formio.hook.invoke('init', 'params', router.formio)) {
92 router.use(router.formio.middleware.params);
93 }
94
95 // Add the db schema sanity check to each request.
96 router.use(router.formio.update.sanityCheck);
97
98 // Add Middleware necessary for REST API's
99 router.use(bodyParser.urlencoded({extended: true}));
100 router.use(bodyParser.json({
101 limit: '16mb'
102 }));
103 router.use(methodOverride('X-HTTP-Method-Override'));
104
105 // Error handler for malformed JSON
106 router.use(function(err, req, res, next) {
107 if (err instanceof SyntaxError) {
108 res.status(400).send(err.message);
109 }
110
111 next();
112 });
113
114 // CORS Support
115 const corsRoute = cors(router.formio.hook.alter('cors'));
116 router.use(function(req, res, next) {
117 if (req.url === '/') {
118 return next();
119 }
120
121 if (res.headersSent) {
122 return next();
123 }
124
125 corsRoute(req, res, next);
126 });
127
128 // Import our authentication models.
129 router.formio.auth = require('./src/authentication/index')(router);
130
131 // Perform token mutation before all requests.
132 if (!router.formio.hook.invoke('init', 'token', router.formio)) {
133 router.use(router.formio.middleware.tokenHandler);
134 }
135
136 // The get token handler
137 if (!router.formio.hook.invoke('init', 'getTempToken', router.formio)) {
138 router.get('/token', router.formio.auth.tempToken);
139 }
140
141 // The current user handler.
142 if (!router.formio.hook.invoke('init', 'logout', router.formio)) {
143 router.get('/logout', router.formio.auth.logout);
144 }
145
146 // The current user handler.
147 if (!router.formio.hook.invoke('init', 'current', router.formio)) {
148 router.get('/current', router.formio.hook.alter('currentUser', [router.formio.auth.currentUser]));
149 }
150
151 // The access handler.
152 if (!router.formio.hook.invoke('init', 'access', router.formio)) {
153 router.get('/access', router.formio.middleware.accessHandler);
154 }
155
156 // Authorize all urls based on roles and permissions.
157 if (!router.formio.hook.invoke('init', 'perms', router.formio)) {
158 router.use(router.formio.middleware.permissionHandler);
159 }
160
161 let mongoUrl = config.mongo;
162 const mongoConfig = config.mongoConfig ? JSON.parse(config.mongoConfig) : {};
163 if (!mongoConfig.hasOwnProperty('connectTimeoutMS')) {
164 mongoConfig.connectTimeoutMS = 300000;
165 }
166 if (!mongoConfig.hasOwnProperty('socketTimeoutMS')) {
167 mongoConfig.socketTimeoutMS = 300000;
168 }
169 if (!mongoConfig.hasOwnProperty('useNewUrlParser')) {
170 mongoConfig.useNewUrlParser = true;
171 }
172 if (!mongoConfig.hasOwnProperty('keepAlive')) {
173 mongoConfig.keepAlive = 120;
174 }
175 if (process.env.MONGO_HIGH_AVAILABILITY) {
176 mongoConfig.mongos = true;
177 }
178 if (_.isArray(config.mongo)) {
179 mongoUrl = config.mongo.join(',');
180 mongoConfig.mongos = true;
181 }
182 if (config.mongoSA) {
183 mongoConfig.sslValidate = true;
184 mongoConfig.sslCA = config.mongoSA;
185 }
186
187 mongoConfig.useUnifiedTopology = true;
188 mongoConfig.useCreateIndex = true;
189
190 // Connect to MongoDB.
191 mongoose.connect(mongoUrl, mongoConfig);
192 mongoose.set('useFindAndModify', false);
193 mongoose.set('useCreateIndex', true);
194
195 // Trigger when the connection is made.
196 mongoose.connection.on('error', function(err) {
197 util.log(err.message);
198 deferred.reject(err.message);
199 });
200
201 // Called when the connection is made.
202 mongoose.connection.once('open', function() {
203 util.log(' > Mongo connection established.');
204
205 // Load the BaseModel.
206 router.formio.BaseModel = require('./src/models/BaseModel');
207
208 // Load the plugins.
209 router.formio.plugins = require('./src/plugins/plugins');
210
211 router.formio.schemas = {
212 PermissionSchema: require('./src/models/PermissionSchema')(router.formio),
213 AccessSchema: require('./src/models/AccessSchema')(router.formio)
214 };
215
216 // Get the models for our project.
217 const models = require('./src/models/models')(router);
218
219 // Load the Schemas.
220 router.formio.schemas = _.assign(router.formio.schemas, models.schemas);
221
222 // Load the Models.
223 router.formio.models = models.models;
224
225 // Load the Resources.
226 router.formio.resources = require('./src/resources/resources')(router);
227
228 // Load the request cache
229 router.formio.cache = require('./src/cache/cache')(router);
230
231 // Return the form components.
232 router.get('/form/:formId/components', function(req, res, next) {
233 router.formio.resources.form.model.findOne({_id: req.params.formId}, function(err, form) {
234 if (err) {
235 return next(err);
236 }
237
238 if (!form) {
239 return res.status(404).send('Form not found');
240 }
241 // If query params present, filter components that match params
242 const filter = Object.keys(req.query).length !== 0 ? _.omit(req.query, ['limit', 'skip']) : null;
243 res.json(
244 _(util.flattenComponents(form.components))
245 .filter(function(component) {
246 if (!filter) {
247 return true;
248 }
249 return _.reduce(filter, function(prev, value, prop) {
250 if (!value) {
251 return prev && _.has(component, prop);
252 }
253 const actualValue = _.property(prop)(component);
254 // loose equality so number values can match
255 return prev && actualValue == value || // eslint-disable-line eqeqeq
256 value === 'true' && actualValue === true ||
257 value === 'false' && actualValue === false;
258 }, true);
259 })
260 .values()
261 .value()
262 );
263 });
264 });
265
266 // Import the form actions.
267 router.formio.Action = router.formio.models.action;
268 router.formio.actions = require('./src/actions/actions')(router);
269
270 // Add submission data export capabilities.
271 require('./src/export/export')(router);
272
273 // Add the available templates.
274 router.formio.templates = {
275 default: _.cloneDeep(require('./src/templates/default.json')),
276 empty: _.cloneDeep(require('./src/templates/empty.json'))
277 };
278
279 // Add the template functions.
280 router.formio.template = require('./src/templates/index')(router);
281
282 const swagger = require('./src/util/swagger');
283 // Show the swagger for the whole site.
284 router.get('/spec.json', function(req, res, next) {
285 swagger(req, router, function(spec) {
286 res.json(spec);
287 });
288 });
289
290 // Show the swagger for specific forms.
291 router.get('/form/:formId/spec.json', function(req, res, next) {
292 swagger(req, router, function(spec) {
293 res.json(spec);
294 });
295 });
296
297 require('./src/middleware/recaptcha')(router);
298
299 // Say we are done.
300 deferred.resolve(router.formio);
301 });
302 });
303 /* eslint-enable max-statements */
304
305 return deferred.promise;
306 };
307
308 // Return the router.
309 return router;
310};