summaryrefslogblamecommitdiffstats
path: root/server/lib/confighelper.js
blob: 0e152a8833e84166733448ea5e6244b4d6fe71ce (plain) (tree)
1
2
3
4
5
6
7
8
9
10









                                                                      
                                                                   



                                         
                                                                    


































                                                                                               




























                                                                                    




































































































































                                                                                                                                                                                

                          








                                                                 
                                               































                                                                                                       
                                             
/* global __appdir */
const path = require('path')
const fs = require('fs')
const db = require(path.join(__appdir, 'lib', 'sequelize'))
const eventHelper = require(path.join(__appdir, 'lib', 'eventhelper'))
const config = require(path.join(__appdir, 'config', 'config'))
const url = config.https.host + ':' + config.https.port


async function getConfig (uuid, list) {
  const client = await db.client.findOne({ where: { uuid: uuid } })
  let configPath = []

  // client not known, start registration
  if (client === null) {
    let config = { id: 'REGISTRATION', name: 'Client Registration' }
    if (!list) {
      config.script = fs.readFileSync(path.join(__appdir, 'ipxe', 'registration.ipxe'), 'utf8')
      return config
    }
    configPath.push(config)
    return configPath
  }

  // Client is in db, check for registration hooks.
  if (client.registrationState !== null) {
    // client is in registration state, load scripts
    var hook = await db.registrationhook.findOne({ where: { id: client.registrationState } })
    let config = { id: 'HOOK', name: 'hook.name', hookId: hook.id, hookType: hook.type }
    if (!list) {
      if (hook.type === 'IPXE') {
        config.script = hook.script
      } else if (hook.type === 'BASH') {
        config.script = fs.readFileSync(path.join(__appdir, 'ipxe', 'minilinux.ipxe'), 'utf8')
      }
      return config
    }
    configPath.push(config)
  }

  // Check for event or direct configs
  let importantList = []
  let eventList = []
  let configList = []
  await _checkParentConfigs(client.id, importantList, eventList, configList, !list)

  if (!list) {
    if (importantList.length) return importantList[0]
    else if (eventList.length) return eventList[0]
    else if (configList.length) return configList[0]
  } else {
    configPath.push(...importantList, ...eventList, ...configList)
  }

  // No config found, use default config
  let config = await _prepareConfig({ id: 'DEFAULT', source: { type: 'DEFAULT' } })
  if (!list) return config

  configPath.push(config)
  return configPath
}


async function getGroupConfig (groupId, list) {
  const group = await db.group.findOne({ where: { id: groupId } })
  if (!group) return
  let configPath = []

  // Check for event or direct configs
  let importantList = []
  let eventList = []
  let configList = []
  await _checkParentConfigs([group.id], importantList, eventList, configList, !list)

  if (!list) {
    if (importantList.length) return importantList[0]
    else if (eventList.length) return eventList[0]
    else if (configList.length) return configList[0]
  } else {
    configPath.push(...importantList, ...eventList, ...configList)
  }

  // No config found, use default config
  let config = await _prepareConfig({ id: 'DEFAULT', source: { type: 'DEFAULT' } })
  if (!list) return config

  configPath.push(config)
  return configPath
}


async function _checkParentConfigs (ids, importantList, eventList, configList, breakOnImportant, blackList = []) {
  let events = []
  let configs = []

  if (Array.isArray(ids)) {
    events = await db.event.findAll({ where: { '$groups.id$': ids }, include: ['groups', 'config'] })
    let groups = await db.group.findAll({ where: { id: ids }, include: ['config'] })
    groups.forEach(group => {
      if (group.configId) configs.push({ id: group.configId, source: { type: 'GROUP', id: group.id, name: group.name } })
    })
  } else {
    events = await db.event.findAll({ where: { '$clients.id$': ids }, include: ['clients', 'config'] })
    let client = await db.client.findOne({ where: { id: ids }, include: ['config'] })
    if (client && client.configId) configs = [{ id: client.configId, source: { type: 'CLIENT', id: client.id, name: client.name } }]
  }

  let importants = []
  let nonImportants = []

  events.forEach(event => {
    let eventTimes = JSON.parse(event.times)
    if (!eventHelper.isActive(eventTimes)) return

    if ((event.groups && event.groups.some(group => group.group_x_event.blacklist))
     || (event.clients && event.clients.length && event.clients[0].client_x_event.blacklist)) {
      return blackList.push(event.id)
    }

    if (blackList.includes(event.id) || !event.configId) return

    let config = {
      id: event.configId,
      source: {
        type: event.important ? 'IMPORTANT_EVENT' : 'EVENT',
        id: event.id,
        name: event.name
      }
    }

    if (event.groups) config.source.groups = event.groups.map(x => ({ id: x.id, name: x.name }))

    if (event.important) importants.push(config)
    else nonImportants.push(config)
  })

  if (importants.length > 1) importantList.push(await _createDynamicMenu(importants))
  else if (importants.length === 1) importantList.push(await _prepareConfig(importants[0]))

  if (breakOnImportant && importantList.length) return

  if (nonImportants.length > 1) eventList.push(await _createDynamicMenu(nonImportants))
  else if (nonImportants.length === 1) eventList.push(await _prepareConfig(nonImportants[0]))

  if (configs.length > 1) configList.push(await _createDynamicMenu(configs))
  else if (configs.length === 1) configList.push(await _prepareConfig(configs[0]))

  let parents = []
  if (Array.isArray(ids)) {
    parents = await db.group.findAll({ where: { '$subgroups.id$': ids }, include: ['subgroups'] })
  } else {
    parents = await db.group.findAll({ where: { '$clients.id$': ids }, include: ['clients'] })
  }
  if (parents.length) await _checkParentConfigs(parents.map(x => x.id), importantList, eventList, configList, breakOnImportant, blackList)
}


// create the config script from database
async function _prepareConfig (configInfo, noScript) {
  let id = configInfo.id

  // If no id is given, find the default config
  const where = {}
  if (id === 'DEFAULT') where.isDefault = true
  else where.id = id

  const config = await db.config.findOne({ where, include: ['entries'], order: [[db.config.associations.entries, db.config.associations.entries.through, 'sortValue', 'ASC']] })
  if (!config) return configInfo

  const result = { id: config.id, name: config.name, source: configInfo.source }

  if (noScript) return result
  if (config.script !== null && config.script !== '') {
    result.script = config.script
    return result
  }
  var script = ''
  var menuscript = ''
  script += '#!ipxe\r\n\r\n'
  script += ':start\r\n'
  script += 'menu ' + config.name + '\r\n'
  config.entries.forEach(entry => {
    script += 'item'
    if (entry.config_x_entry.keyBind !== null) {
      script += ' --key ' + entry.config_x_entry.keyBind
    }
    script += ' menuentry' + entry.id + ' '
    if (entry.config_x_entry.customName !== null && entry.config_x_entry.customName !== '') {
      script += entry.config_x_entry.customName
    } else {
      script += entry.name
    }
    script += '\r\n'
    menuscript += ':' + 'menuentry' + entry.id + '\r\n'
    menuscript += entry.script + '\r\n\r\n'
  })
  script += 'choose '
  if (config.defaultEntry !== null && config.timeout !== null) {
    script += '--default menuentry' + config.defaultEntry + ' --timeout ' + config.timeout + ' '
  }
  script += `target && goto \${target}\r\n\r\n`
  script += menuscript

  result.script = script
  return result
}


// create dynamic menu to load the different given configs for a client
async function _createDynamicMenu (configInfos, noScript) {
  const ids = []
  const idSourceMap = {}

  console.log(configInfos)

  configInfos.forEach(configInfo => {
    ids.push(configInfo.id)
    idSourceMap[configInfo.id] = configInfo.source
  })

  const configs = await db.config.findAll({ where: { id: ids } })
  const result = {
    merged: true,
    id: JSON.stringify(configs.map(x => x.id)),
    name: configs.map(x => x.name).join(' + '),
    configs: configs.map(x => ({ id: x.id, name: x.name, source: idSourceMap[x.id] }))
  }
  if (noScript) return result

  var script = ''
  var menuscript = ''
  var defaultentry = ''
  script += '#!ipxe\r\n\r\n'
  script += ':start\r\n'
  script += 'menu ' + 'Choose one configuration to boot' + '\r\n'
  configs.forEach(config => {
    script += 'item '
    script += ' menuentry' + config.id + ' '
    script += config.name
    script += '\r\n'

    // Last script processed is default script
    defaultentry = 'menuentry' + config.id

    menuscript += ':' + 'menuentry' + config.id + '\r\n'
    menuscript += 'chain ' + 'https://' + url + '/api/configloader/getconfig/' + config.id + '\r\n\r\n'
  })
  script += 'choose --default ' + defaultentry + ' '
  script += '--timeout 15 '
  script += `target && goto \${target}\r\n\r\n`
  script += menuscript

  result.script = script
  return result
}


module.exports = { getConfig, getGroupConfig }