Source: index.js


'use strict'

// Fichier principal de l'application, qui exporte une fonction à appeler pour booter l'appli.
// En cli on appelle directement cli.js sans passer par ce fichier.

// La fonction exportée fera
// - chargement lassi
// - déclaration d'un composant pour l'application avec nos autres composants en prérequis
// - ajout d'éventuels composants en prérequi|postrequis définis dans la conf
// - ajout de middleware sur le rail (CORS, Expire & co)
// - boot de l'appli

const anLog = require('an-log')
const sjt = require('sesajstools')
const { merge } = require('sesajstools/utils/object')
const log = require('sesajstools/utils/log')

const { addBodyParsers, addCorsAndLog } = require('./addMiddlewares')
const beforeTransport = require('./beforeTransport')
const boot = require('./boot')
const config = require('./config')
const { checkLocalOnRemote } = require('./checkConfig')

const getResolvedPromise = () => Promise.resolve()

let lassiInstance

/**
 * Ajoute nos middlewares et listeners, après déclaration des composants mais avant bootstrap
 * @param {Lassi} lassi L’instance de lassi de cette application
 * @param {Lassi#Component} mainComponent Le composant principal sur lequel sera lancé bootstrap() (après la fin de l'éxécution de cette fonction)
 * @param {Lassi#Component[]} allComponents La liste des composants en dépendances
 */
function beforeBootsrap (lassi, mainComponent, allComponents) {
  anLog.config(config.lassiLogger)

  // une fois les composants chargés on ajoutera nos listeners lassi
  mainComponent.config(function ($accessControl, $routes, $settings) {
    // on désactive toujours la compression dust (pas seulement en dev), car ça crée trop de
    // pbs dans le code js des templates dust
    lassi.transports.html.engine.disableWhiteSpaceCompression()

    // on ajoute nos filtres perso pour dust
    try {
      // un js dump
      lassi.transports.html.engine.addFilter('jsd', function (value) {
        return sjt.stringify(value, 2)
      })
      lassi.transports.html.engine.addFilter('nl2br', function (value) {
        return value.replace('\n', '<br />\n')
      })
    } catch (error) {
      log.error("impossible d'ajouter nos filtres à dust", error)
    }

    /**
     * On ajoutera nos middleware après session
     * (sauf content-type après compression, pour qu'il soit avant body-parser)
     * @param {Object} rail le rail express
     * @param {string} name Le nom du middleware qui vient d'être mis sur le rail
     */
    lassi.on('afterRailUse', function (rail, name) {
      // on peut ajouter les arguments , settings, middleware puis log(middleware) pour voir le code de chaque middleware
      if (name === 'cookie') addBodyParsers(rail)
      else if (name === 'session') addCorsAndLog(rail)
    })

    // le listener beforeTransport
    lassi.on('beforeTransport', beforeTransport)

    // si $sesalabSsoClient existe, faut l'ajouter en client d'authentification
    // on lui passe les infos dont il a besoin
    if (allComponents.indexOf('sesalab-sso') !== -1) {
      const authName = (config.sesalabs && config.sesalabs[0] && config.sesalabs[0].name) || 'Sesalab'
      const authBaseId = config.sesalabs && config.sesalabs[0] && config.sesalabs[0].baseId
      if (!authBaseId) throw new Error('sesalab sans baseId en configuration')
      require('./auth/authClientSesalabSso')(authName, authBaseId)
    }

    log(`FIN config de l'application ${config.application.name} en mode ${config.application.staging}`)
  })
}

/**
 * Démarre l'application Sesathèque
 * @param {object} [options]
 * @param {object} [options.settings] Éventuelles surcharges des settings
 * @param {boolean} [options.noCheckLocalOnRemote=false] Passer true pour ne pas vérifier la cohérence de notre configuration sur les sésathèques distantes (déclarées dans notre configuration)
 * @param {simpleCallback} [afterBootCallback]
 * @return {Promise<Lassi>}
 */
function app (options, afterBootCallback) {
  // after boot est facultatif, et on veut un message dans le log
  function afterBootCallbackWrapper () {
    if (afterBootCallback) afterBootCallback()
    log(`${config.application.name} started with staging ${config.application.staging}`)
  }

  if (lassiInstance) return Promise.resolve(lassiInstance)

  if (typeof options === 'function') {
    afterBootCallback = options
    options = {}
  } else if (!options) {
    options = {}
  }
  if (options.settings) merge(config, options.settings)
  anLog.config(config.lassiLogger)
  log(`Starting ${config.application.name}`)
  const bootOptions = {}
  if (options.settings) bootOptions.settings = options.settings
  // cette option noGlobalLassi n'est pas encore gérée correctement dans lassi
  if (options.noGlobalLassi) bootOptions.noGlobalLassi = options.noGlobalLassi

  // Rq : les callbacks beforeBoot et afterBoot sont sync, notre vérif async

  // le check sur les sesathèques distantes est plus simple avant de lancer le boot
  // mais si on en lance 2 en même temps chacune attend l'autre jusqu'au timeout
  // on le passe donc après, quitte à arrêter l'appli en cas de pb
  lassiInstance = boot(beforeBootsrap, bootOptions, afterBootCallbackWrapper)

  const check = options.noCheckLocalOnRemote ? getResolvedPromise : checkLocalOnRemote

  return check().then((results) => {
    // si c'est résolu avec des erreurs, on les affiche sans bloquer la suite
    if (Array.isArray(results) && results.length) results.forEach(r => log.warn(r))
    // et on résoud avec l'instance lassi
    return Promise.resolve(lassiInstance)
  }).catch((error) => {
    log.error(error)
    log.error(`${config.application.name} ABORTING`)
    lassiInstance.shutdown()
    // on ne retourne rien le shutdown fera un process.exit
  })
}

module.exports = app