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

                          
                        
                                                        
                                

                                                   
                                   
                                                      


                                                                   
 




                                             
                                                                                                                                   






















                                                                                                                                               
                
                                     
                                             
                                                            

  
                                          



                                                           
                                      
                                             
                                                        

  
                                      
                                             
                                                        

  
                                  
                                                
                                             
                                                             



                                                          



                                 


    




























                                                                                                                      
                     

                   

                                         
                                                           
                              
                                                                                      
                                                                      
                                         

                                                          
                              
          




                                                                   





                                                                                                   














                                                                      

    



                             
                                               





                                                             

                                                                              
                                                                                             
                 

  
                                                




                                                            

                                                     

  
                                               




                                                         

                                    

  
                                     
                                                                         

  






                                                                                                
              
       
                                                                     
                   




                                                    
   
                              















































                                                                                                     
                              
 

                                                
                                                                                             



                      
 

                                                      
                                                                
  
 
                                          
/* global __appdir */
var path = require('path')
const fs = require('fs')
var shell = require(path.join(__appdir, 'lib', 'shell'))
var express = require('express')
const { decorateApp } = require('@awaitjs/express')
var router = decorateApp(express.Router())
var noAuthRouter = express.Router()
const log = require(path.join(__appdir, 'lib', 'log'))
const buildLinkPath = path.join(__appdir, 'ipxe', 'current_builds')
const buildsPath = path.join(__appdir, 'ipxe', 'builds')
const sanitize = require('sanitize-filename')

// Permission check middleware
router.all('/:x', async (req, res, next) => {
  switch (req.method) {
    case 'GET':
      switch (req.params.x) {
        case 'script': case 'certificate': case 'general': case 'console': case 'log': case 'config': case 'status': case 'builds':
          if (!await req.user.hasPermission('ipxe.view')) return res.status(403).send({ error: 'Missing permission', permission: 'ipxe.view' })
          break

        case 'build': case 'cancel': case 'clean':
          if (!await req.user.hasPermission('ipxe.edit')) return res.status(403).send({ error: 'Missing permission', permission: 'ipxe.edit' })
          break

        default:
          return res.status(400).send()
      }
      break

    case 'PUT': case 'POST':
      if (!await req.user.hasPermission('ipxe.edit')) return res.status(403).send({ error: 'Missing permission', permission: 'ipxe.edit' })
      break

    default:
      return res.status(400).send()
  }

  next()
})

// GET requests.
router.get('/script', (req, res) => {
  res.setHeader('content-type', 'text/plain')
  res.sendFile(path.join(__appdir, 'ipxe', 'embedded.ipxe'))
})

router.get('/certificate', (req, res) => {
  res.setHeader('content-type', 'text/plain')
  res.sendFile(path.join(__appdir, 'bin', 'fullchain.pem'))
})

router.get('/general', (req, res) => {
  res.setHeader('content-type', 'text/plain')
  res.sendFile(path.join(__appdir, 'ipxe', 'general.h'))
})

router.get('/console', (req, res) => {
  res.setHeader('content-type', 'text/plain')
  res.sendFile(path.join(__appdir, 'ipxe', 'console.h'))
})

router.get('/log', (req, res) => {
  const max = req.query.max ? req.query.max : -1
  res.setHeader('content-type', 'text/plain')
  const filepath = path.join(__appdir, 'ipxe', 'ipxelog.txt')

  fs.readFile(filepath, 'utf-8', function (err, content) {
    if (err) res.end()
    if (max !== -1 && content) {
      let c = content.split('\n')
      c = c.splice(-max)
      res.send(c.join('\n'))
    } else res.send(content)
  })
})

router.get('/config', (req, res) => {
  delete require.cache[require.resolve(path.join(__appdir, 'config', 'ipxe'))]
  var config = require(path.join(__appdir, 'config', 'ipxe'))
  res.send(config)
})

router.post('/config', (req, res) => {
  let buildTargets = req.body.buildTargets
  const targetList = req.body.targetList
  const branch = req.body.branch
  const repository = req.body.repository
  var config = require(path.join(__appdir, 'config', 'ipxe'))

  if (repository) config.repository = repository
  if (branch) config.branch = branch
  if (targetList && config.targets.custom) config.targets.list = targetList

  if (buildTargets) {
    if (!config.targets.custom) buildTargets = buildTargets.filter(target => config.targets.list.indexOf(target) >= 0)
    config.targets.build = buildTargets
  }

  fs.writeFile(path.join(__appdir, 'config', 'ipxe.json'), JSON.stringify(config, null, 4), {}, (err) => {
    if (err) res.status(500).end()
    else res.send()
  })
})

router.put('/:filename', (req, res) => {
  var filepath = null
  let filename = ''

  // Set the file path
  if (req.params.filename === 'script') {
    filepath = path.join(__appdir, 'ipxe', 'embedded.ipxe')
    filename = 'embedded.ipxe'
  } else if (req.params.filename === 'console' || req.params.filename === 'general') {
    filepath = path.join(__appdir, 'ipxe', req.params.filename + '.h')
    filename = req.params.filename + '.h'
  } else if (req.params.filename === 'certificate') {
    filepath = path.join(__appdir, 'bin', 'fullchain.pem')
    filename = 'fullchain.pem'
  } else {
    log({
      category: 'ERROR_IPXEBUILDER_SAVE',
      description: filename + ' cannot be saved. Filename unknown',
      userId: req.user.id
    })
    res.status(500).send({ status: 'UNKNOWN_FILENAME', error: 'The provided filename is unknown' })
    return
  }

  // Write File
  fs.writeFile(filepath, req.body.data, err => {
    if (err) {
      log({
        category: 'ERROR_IPXEBUILDER_SAVE',
        description: 'Error while saving ' + filename + '. ' + err,
        userId: req.user.id
      })
      res.status(500).send({ status: 'WRITE_FILE_ERROR', error: err })
    } else {
      log({
        category: 'IPXEBUILDER_SAVE',
        description: 'User has saved ' + filename,
        userId: req.user.id
      })
      res.status(200).send({ status: 'SUCCESS' })
    }
  })
})

/*
 * @return: Rebuild the ipxe.
 */
router.getAsync('/build', async (req, res) => {
  log({
    category: 'IPXEBUILDER_BUILD',
    description: 'User initiated the ipxe building process.',
    userId: req.user.id
  })

  delete require.cache[require.resolve(path.join(__appdir, 'config', 'ipxe'))]
  const config = require(path.join(__appdir, 'config', 'ipxe'))
  const build = await shell.buildIpxe(config.targets.build, config.repository, config.branch)
  res.send(build)
})

router.getAsync('/cancel', async (req, res) => {
  log({
    category: 'IPXEBUILDER_CANCEL',
    description: 'User canceled the ipxe building process.',
    userId: req.user.id
  })
  const result = await shell.cancelBuilding(req, res)
  res.send(result)
})

router.getAsync('/clean', async (req, res) => {
  log({
    category: 'IPXEBUILDER_CLEAN',
    description: 'User initiated ipxe cleaning process.',
    userId: req.user.id
  })
  const result = await shell.clean()
  res.send(result)
})

router.get('/status', (req, res) => {
  res.send({ status: 'SUCCESS', data: shell.status(req.params.version) })
})

/*
 * Return a json of all available builds in /ipxe/builds
 * { 'YYYY-M-DD_h-m-s': { type: '<directory/file>', children: { ... }, selected: <true/false> }}
 */
router.get('/builds', (req, res) => {
  // Reads directory of the builded ipxe targets /ipxe/builds
  let recursiveDirectory = shell.readdirRecursive(buildsPath)
  let linkname
  try {
    linkname = fs.readlinkSync(buildLinkPath).split('/').slice(-1)[0]
  } catch (error) {
    linkname = ''
  }

  for (let buildDir of recursiveDirectory) {
    buildDir.selected = (buildDir.name === linkname)
  }
  res.send(recursiveDirectory)
})

/*
 * Recreates the symlinks to the selected target.
 */
router.post('/builds', (req, res) => {
  let buildname = sanitize(req.body.build)
  const buildDir = fs.readdirSync(buildsPath)

  if (buildDir.includes(buildname)) {
    // Recreate the symlink
    return res.send(shell.forceSymlink(path.join(buildsPath, buildname), buildLinkPath))
  } else return res.send({ status: 'ERROR', error: 'BUILD_NOT_FOUND' })
})

/*
 * Renames the directory of the builds
 */
router.post('/builds/:buildname', (req, res) => {
  const buildname = sanitize(req.params.buildname)
  const newname = sanitize(req.body.name)

  const buildDir = fs.readdirSync(buildsPath)

  // Check if the build exists and the destination don't
  if (!buildDir.includes(buildname)) return res.send({ status: 'ERROR', error: 'BUILD_NOT_FOUND' })
  if (buildDir.includes(newname)) return res.send({ status: 'ERROR', error: 'BUILD_ALREADY_EXISTS' })

  // Rename the build
  fs.renameSync(path.join(buildsPath, buildname), path.join(buildsPath, newname))

  // Check if the link has to be changed (if the current default build was renamed)
  let linkname = fs.readlinkSync(buildLinkPath).split('/').slice(-1)[0]
  if (linkname === buildname) shell.forceSymlink(path.join(buildsPath, newname), buildLinkPath)

  return res.send({ status: 'SUCCESS' })
})

router.delete('/builds/:buildname', (req, res) => {
  let buildname = sanitize(req.params.buildname)

  const buildDir = fs.readdirSync(buildsPath)
  if (buildDir.includes(buildname)) {
    shell.forceDeleteBuild(path.join(__appdir, 'ipxe', 'builds', buildname))
    return res.send()
  } else return res.send({ status: 'ERROR', error: 'BUILDS_NOT_FOUND' })
})

module.exports.router = router

noAuthRouter.get('/load/script', (req, res) => {
  res.setHeader('content-type', 'text/plain')
  fs.readFile(path.join(__appdir, 'ipxe', 'default.ipxe'), 'utf-8', function (err, content) {
    if (err) res.end()
    res.send(content)
  })
})

noAuthRouter.get('/load/registration', (req, res) => {
  res.setHeader('content-type', 'text/plain')
  res.sendFile(path.join(__appdir, 'ipxe', 'registration.ipxe'))
})

module.exports.noAuthRouter = noAuthRouter