summaryrefslogblamecommitdiffstats
path: root/server/api/configloader.js
blob: bec480dae194b846ef636204386b442d76cce3a6 (plain) (tree)
1
2
3
4
5
6
7
8
9
                     
                          

                                                         

                                                   

                                                               
 
                                                                                           
                                                     


                                             






                                                                                                
                                                                                                              







                            
                        

                                                    














                                                                                                                   










































                                                                                                            

              


                                                

              

















                                                          
       











                                                          
       















                                                        
     
                                          
                                                                           

                                           
                                                                                
   
  
 









                                                        

                                                             



                          
                    







                                                                                                  
     
                                                       
                                                                   
                                                                                                
                                                                                       







                                                                                                          
                                                                                        








                                                                                        
                                                                  




                                                                                          
                                                                     


                                                 
                                  


                                         
                                
                                                                                                                                    
                                                         












                                                          
                                                                                               











                                                                                                  
                                                 





                        
                                                                       
                               



                                                                    
                         

                              
                                                                   




                                              
 


                                                
                                                          
                                                                                                         
      


                                                      
                        
 


                 
 








                                           
                                          
/* global __appdir */
var path = require('path')
var db = require(path.join(__appdir, 'lib', 'sequelize'))
var express = require('express')
const { decorateApp } = require('@awaitjs/express')
var noAuthRouter = decorateApp(express.Router())
const config = require(path.join(__appdir, 'config', 'config'))
const url = config.https.host + ':' + config.https.port

// if client in db -> load script (default if none is found), else load registration script
noAuthRouter.getAsync('/:uuid', async (req, res) => {
  const uuid = req.params.uuid
  res.setHeader('content-type', 'text/plain')

  var client = await db.client.findOne({ where: { uuid: uuid }, include: ['groups', 'events'] })
  if (client !== null) {
    // 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 } })
      if (hook.type === 'IPXE') res.send(hook.script)
      else if (hook.type === 'BASH') await sendFilePromise(res, path.join(__appdir, 'ipxe', 'minilinux.ipxe'))
      return
    }

    var events = []
    var blacklist = []
    var importantEvents = []
    var groupIds = []
    var result
    var now = new Date()

    for (let i = 0; i < client.events.length; i++) {
      let intime = true
      let times = client.events[i].times

      if (times.repetitive) {
        let intervalBool = true
        if (times.monthMap[now.getMonth()] && times.dayMap[((now.getDay() + 6) % 7)] && intervalBool) intime = true
      } else {
        if (times.start <= now.getTime() <= times.end) intime = true
      }

      if (intime) {
        if (client.events[i].client_x_event.blacklist) blacklist.push(client.events[i].id)
        else if (client.events[i].important) importantEvents.push(client.events[i])
        else events.push(client.events[i])
      } else continue
    }

    importantEvents = importantEvents.filter(e => !blacklist.includes(e.id))
    importantEvents = importantEvents.map(e => e.config)
    importantEvents = importantEvents.filter(c => c !== null)
    importantEvents = importantEvents.filter(function (elem, pos, arr) { return arr.indexOf(elem) === pos })
    if (importantEvents.length === 1) {
      result = await createIpxeScript(importantEvents[0])
      res.send(result)
      return
    }
    if (importantEvents.length > 1) {
      result = await createDynamicMenu(importantEvents)
      res.send(result)
      return
    }

    events = events.filter(e => !blacklist.includes(e.id))
    events = events.map(e => e.config)
    events = events.filter(c => c !== null)
    events = events.filter(function (elem, pos, arr) { return arr.indexOf(elem) === pos })

    groupIds = client.groups.map(x => x.id)

    var response = await fetchParentConfigs(groupIds, blacklist)
    if (response.ids.length > 0) {
      // Check if there is an important event
      if (response.type === 'important') {
        if (response.ids.length === 1) {
          result = await createIpxeScript(response.ids[0])
          res.send(result)
          return
        }
        if (response.ids.length > 1) {
          result = await createDynamicMenu(response.ids)
          res.send(result)
          return
        }
      }
      // No important event, use client event if existent
      if (events.length === 1) {
        result = await createIpxeScript(events[0])
        res.send(result)
        return
      }
      if (events.length > 1) {
        result = await createDynamicMenu(events)
        res.send(result)
        return
      }
      // No client event, use events of lowest parents
      if (response.type === 'event') {
        if (response.ids.length === 1) {
          result = await createIpxeScript(response.ids[0])
          res.send(result)
          return
        }
        if (response.ids.length > 1) {
          result = await createDynamicMenu(response.ids)
          res.send(result)
          return
        }
      }
      // No events, use client config
      if (client.configId !== null) {
        result = await createIpxeScript(client.configId)
        res.send(result)
        return
      }
      // No client config, use configs of lowest parents
      if (response.type === 'config') {
        if (response.ids.length === 1) {
          result = await createIpxeScript(response.ids[0])
          res.send(result)
          return
        }
        if (response.ids.length > 1) {
          result = await createDynamicMenu(response.ids)
          res.send(result)
          return
        }
      }
    } else {
      if (events.length === 1) {
        result = await createIpxeScript(events[0])
        res.send(result)
        return
      }
      if (events.length > 1) {
        result = await createDynamicMenu(events)
        res.send(result)
        return
      }
      if (client.configId !== null) {
        result = await createIpxeScript(client.configId)
        res.send(result)
        return
      }
    }
    // No config found, use default config
    await sendFilePromise(res, path.join(__appdir, 'ipxe', 'default.ipxe'))
  } else {
    // client not known, start registration
    await sendFilePromise(res, path.join(__appdir, 'ipxe', 'registration.ipxe'))
  }
})

// load config by given id
noAuthRouter.get('/getconfig/:configId', (req, res) => {
  const configId = req.params.configId
  res.setHeader('content-type', 'text/plain')

  createIpxeScript(configId).then(response => {
    res.send(response)
  })
})

async function fetchParentConfigs (groupIds, blacklist) {
  if (groupIds.length === 0) return { 'ids': [], 'type': '' }

  var importantEvents = []
  var events = []
  var configs = []
  var parentIds = []
  var newBlacklist = blacklist.slice(0)

  var groups = await db.group.findAll({ where: { id: groupIds }, include: ['parents', 'events'] })
  for (let i = 0; i < groups.length; i++) {
    configs.push(groups[i].configId)
    // groups[i].map(g => g.parents.map(p => p.id))
    for (let j = 0; j < groups[i].parents.length; j++) {
      parentIds.push(groups[i].parents[j].id)
    }
    for (let j = 0; j < groups[i].events.length; j++) {
      // Execute these 3 Lines only if event is happening right now
      if (groups[i].events[j].group_x_event.blacklist) newBlacklist.push(groups[i].events[j].id)
      else if (groups[i].events[j].important) importantEvents.push(groups[i].events[j])
      else events.push(groups[i].events[j])
    }
  }

  importantEvents = importantEvents.filter(e => !newBlacklist.includes(e.id))
  importantEvents = importantEvents.map(e => e.config)
  importantEvents = importantEvents.filter(c => c !== null)
  importantEvents = importantEvents.filter(function (elem, pos, arr) { return arr.indexOf(elem) === pos })
  if (importantEvents.length > 0) return { 'ids': importantEvents, 'type': 'important' }

  var response = await fetchParentConfigs(parentIds, newBlacklist)

  if (response.type === 'important') return response

  events = events.filter(e => !newBlacklist.includes(e.id))
  events = events.map(e => e.config)
  events = events.filter(c => c !== null)
  events = events.filter(function (elem, pos, arr) { return arr.indexOf(elem) === pos })
  if (events.length > 0) return { 'ids': events, 'type': 'event' }

  if (response.type === 'event') return response

  configs = configs.filter(function (elem, pos, arr) { return arr.indexOf(elem) === pos })
  configs = configs.filter(c => c !== null)
  if (configs.length > 0) return { 'ids': configs, 'type': 'config' }

  if (response.type === 'config') return response

  return { 'ids': [], 'type': '' }
}

// create the config script from database
function createIpxeScript (id) {
  return db.config.findOne({ where: { id: id }, include: ['entries'], order: [[['entries'], 'sortValue', 'ASC']] }).then(config => {
    if (config.script !== null && config.script !== '') {
      return config.script
    }
    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

    return script
  })
}

// create dynamic menu to load the different given configs for a client
// TODO: Hardcoded Chain Port?!
function createDynamicMenu (ids) {
  return db.config.findAll({ where: { id: ids } }).then(configs => {
    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

    return script
  })
}

function sendFilePromise (res, file) {
  return new Promise((resolve, reject) => {
    res.sendFile(file, {}, err => {
      if (err) console.log(err)
      resolve()
    })
  })
}

module.exports.noAuthRouter = noAuthRouter