UNPKG

10 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
193 // Trigger when the connection is made.
194 mongoose.connection.on('error', function(err) {
195 util.log(err.message);
196 deferred.reject(err.message);
197 });
198
199 // Called when the connection is made.
200 mongoose.connection.once('open', function() {
201 util.log(' > Mongo connection established.');
202
203 // Load the BaseModel.
204 router.formio.BaseModel = require('./src/models/BaseModel');
205
206 // Load the plugins.
207 router.formio.plugins = require('./src/plugins/plugins');
208
209 router.formio.schemas = {
210 PermissionSchema: require('./src/models/PermissionSchema')(router.formio),
211 AccessSchema: require('./src/models/AccessSchema')(router.formio)
212 };
213
214 // Get the models for our project.
215 const models = require('./src/models/models')(router);
216
217 // Load the Schemas.
218 router.formio.schemas = _.assign(router.formio.schemas, models.schemas);
219
220 // Load the Models.
221 router.formio.models = models.models;
222
223 // Load the Resources.
224 router.formio.resources = require('./src/resources/resources')(router);
225
226 // Load the request cache
227 router.formio.cache = require('./src/cache/cache')(router);
228
229 // Return the form components.
230 router.get('/form/:formId/components', function(req, res, next) {
231 router.formio.resources.form.model.findOne({_id: req.params.formId}, function(err, form) {
232 if (err) {
233 return next(err);
234 }
235
236 if (!form) {
237 return res.status(404).send('Form not found');
238 }
239 // If query params present, filter components that match params
240 const filter = Object.keys(req.query).length !== 0 ? _.omit(req.query, ['limit', 'skip']) : null;
241 res.json(
242 _(util.flattenComponents(form.components))
243 .filter(function(component) {
244 if (!filter) {
245 return true;
246 }
247 return _.reduce(filter, function(prev, value, prop) {
248 if (!value) {
249 return prev && _.has(component, prop);
250 }
251 const actualValue = _.property(prop)(component);
252 // loose equality so number values can match
253 return prev && actualValue == value || // eslint-disable-line eqeqeq
254 value === 'true' && actualValue === true ||
255 value === 'false' && actualValue === false;
256 }, true);
257 })
258 .values()
259 .value()
260 );
261 });
262 });
263
264 // Import the form actions.
265 router.formio.Action = router.formio.models.action;
266 router.formio.actions = require('./src/actions/actions')(router);
267
268 // Add submission data export capabilities.
269 require('./src/export/export')(router);
270
271 // Add the available templates.
272 router.formio.templates = {
273 default: _.cloneDeep(require('./src/templates/default.json')),
274 empty: _.cloneDeep(require('./src/templates/empty.json'))
275 };
276
277 // Add the template functions.
278 router.formio.template = require('./src/templates/index')(router);
279
280 const swagger = require('./src/util/swagger');
281 // Show the swagger for the whole site.
282 router.get('/spec.json', function(req, res, next) {
283 swagger(req, router, function(spec) {
284 res.json(spec);
285 });
286 });
287
288 // Show the swagger for specific forms.
289 router.get('/form/:formId/spec.json', function(req, res, next) {
290 swagger(req, router, function(spec) {
291 res.json(spec);
292 });
293 });
294
295 require('./src/middleware/recaptcha')(router);
296
297 // Say we are done.
298 deferred.resolve(router.formio);
299 });
300 });
301 /* eslint-enable max-statements */
302
303 return deferred.promise;
304 };
305
306 // Return the router.
307 return router;
308};