UNPKG

rutilus-analytics-node-js

Version:

Provides a GUI web app that allows users to examine their data in detail. Includes CSV export functionality.

238 lines (200 loc) 8.45 kB
/** * Rutilus * * @homepage https://gmrutilus.github.io * @license Apache-2.0 */ /** @module */ /** * @param {RawConfig} config * @param {Function} cb callback * @constructor */ function AnalyticsApi(config, cb) { // Node modules const fs = require('fs'); const http = require('http'); const https = require('https'); // Classes const Env = require('./lib/env'); const ResultStreamer = require('./lib/resultStreamer'); const Emailer = require('./lib/emailer'); const RateLimit = require('./lib/rateLimit'); const Logger = require('./lib/logger'); const ErrorHandler = require('./lib/errorHandler'); const RecalculationLock = require('./lib/recalculationLock'); const JsonToMongo = require('./lib/jsonToMongo'); const Parsing = require('./lib/parsing'); const Validation = require('./lib/validation'); const QuerySchedule = require('./lib/querySchedule'); const Random = require('./lib/random'); // Settings /** @type {ServerSettings} */ const serverSettings = require('./lib/serverSettings')(config); // API version] /** @type {String} */ const version = require('./package.json').version; /** * @type {ResultStreamer} */ const resultStreamer = new ResultStreamer(serverSettings.resultFilePath); // Environment variables const env = new Env(); const inProduction = /** @type {Boolean} */ env.check('node_env', 'production'); // Koa stuff const app = /** @type {Application} */ new (require('koa'))(); const serve = require('koa-static'); const router = /** @type {{ routes: Function }} */ new (require('koa-router'))(); const bodyParser = require('koa-bodyparser'); const helmet = require('koa-helmet'); const session = require('koa-session'); // Passport const passport = /** @type {Object<String, Function>} */ require('koa-passport'); // Presets and queries const presets = require('./lib/presets/index'); const createDefaultQueries = require('./lib/createDefaultQueries'); const defaultIndexes = require('./lib/joins/defaultIndexes')(serverSettings.extraIndexes); /** @type {Array<Object>} */ const defaultQueries = require('./lib/presets/defaultQueries'); // Models const mongoose = require('mongoose'); mongoose.Promise = require('bluebird'); // Static files app.use(serve(__dirname + '/public')); // Security const rateLimitConfig = serverSettings.rateLimit; if (!rateLimitConfig.handler) { // Default handler for rate limit rateLimitConfig.handler = function (/** @type {{ _matchedRoute: String, originalUrl: String}} */ self) { logger.error({ errorCode: 429, error: '429 Too many requests', route: self._matchedRoute, url: self.originalUrl }); self.body = '429 Too many requests'; self.status = 429; } } const rateLimit = new RateLimit(serverSettings.rateLimit); app.use(helmet()); // Building the library of joins const joinsLib = require('./lib/joins')(serverSettings.extraInformation); // Other includes const logger = new Logger(serverSettings.loggerConfig); const errorHandler = new ErrorHandler(logger); const checkConflictingExtraInformation = require('./lib/checkConflictingExtraInformation'); const emailer = new Emailer(serverSettings.transporter, serverSettings.addresses.analytics, logger); const recalculationLock = new RecalculationLock(); const jsonToMongo = new JsonToMongo(joinsLib); const querySchedule = new QuerySchedule(); // Checking for conflicting extra information if (!checkConflictingExtraInformation(logger, serverSettings.extraInformation)) { process.exit(ErrorHandler.SYS.CONFLICTING_EXTRA_INFORMATION.code); } // Packing dashboard before starting the API if (config.skipPackingDashboard) { start(); } else { require('./lib/packDashboard')({ inProduction, rootDirectory: __dirname, analyticsLocation: serverSettings.addresses.analytics, logger, extraInformation: serverSettings.extraInformation, joinsLib, }, start); } /** * Starts the server * @param {String} [dashboardSourceCode] */ function start(dashboardSourceCode = '') { // Creating the schema for Mongoose and stores for queries //------------------------------------------------------------------ const schemas = /** @type {Object<String, Object>} */ { Queries: require('./lib/schema/queries')(defaultIndexes.queries || []), Visits: require('./lib/schema/visits')(serverSettings.extraInformation, defaultIndexes.visits || [], defaultIndexes.actions || []), Accounts: require('./lib/schema/accounts')(defaultIndexes.accounts || []), UatCache: require('./lib/schema/uatCache')(serverSettings.extraInformation, defaultIndexes.uatCache || []), }; // Configuring passport //------------------------------------------------------------------ require('./lib/passport')(passport, schemas.Accounts); app.keys = [Math.random().toString(36).substr(2)]; app.use(session(app)); app.use(passport.initialize()); app.use(passport.session()); const auth = require('./lib/auth').Auth(schemas.Accounts, errorHandler.dbErrorCatcher, env); // Creating the server //------------------------------------------------------------------ if (!inProduction) { logger.warn(`MONGODB will connect to ${serverSettings.addresses.database}`); } else { logger.warn(`MONGODB will connect`); } mongoose.connect(serverSettings.addresses.database); const db = mongoose.connection; // Opening Mongo connection and then, when open, loading rest of app // ----------------------------------------------------------------- db.once('open', () => { if (!inProduction) { logger.info(`MONGODB connected to ${serverSettings.addresses.database}`); } else { logger.info(`MONGODB connected`); } // Checking if we have the default queries in the DB. If not, // we create them //------------------------------------------------------------------ createDefaultQueries(schemas.Queries, errorHandler, logger, presets, defaultQueries); // Setting up router // ----------------- require('./lib/setupRouter')({ router, presets, schemas, logger, resultStreamer, extraInformation: serverSettings.extraInformation, bodyParser, auth, rateLimit, errorHandler, emailer, affinityTool: serverSettings.affinityTool, algorithms: serverSettings.algorithms, dashboardSourceCode, passport, recalculationLock, jsonToMongo, querySchedule, serverSettings, db, }); // Binding router routes to app // ---------------------------- app.use(router.routes()); app.use(router.allowedMethods()); // Creating the server // ------------------- const envLabel = (env.check('node_env') + '').toUpperCase(); const protocol = serverSettings.useHttps ? 'HTTPS' : 'HTTP'; const server = serverSettings.useHttps ? https.createServer(serverSettings.httpsKeys, app.callback()) : http.createServer(app.callback()); server.listen(serverSettings.port, () => { logger.info(`${envLabel} ANALYTICS ${version} | ${protocol} on port ${serverSettings.port}`); if (cb) { cb(); } }); }); db.on('error', () => { logger.error(`Mongoose connection refused`); }); } } module.exports = AnalyticsApi;