Florian Bouillon 19e3892099
feat: Add RFC error messages
Signed-off-by: Florian BOUILLON <f.bouillon@aptatio.com>
2023-04-05 14:46:59 +02:00

156 lines
4.6 KiB
TypeScript

import { objectMap, objectOmit } from '@dzeio/object-util'
import { exec as execAsync } from 'child_process'
import express from 'express'
import fs from 'fs/promises'
import { evaluate } from 'mathjs'
import util from 'util'
import { createErrorResponse } from './HTTPError'
import { getParams } from './gcodeUtils'
const exec = util.promisify(execAsync)
const server = express()
async function exists(file: string) {
try {
await fs.stat(file)
return true
} catch {
return false
}
}
// make the files dir
server.use((req, _, next) => {
let data: Array<any> = []
// req.setEncoding()
req.on('data', function(chunk) {
data.push(Buffer.from(chunk))
});
req.on('end', function() {
req.body = Buffer.concat(data);
next();
});
});
const path = process.cwd()
server.get('/', (_, res) => {
res.send('please read the README.md file...')
})
server.post('/', async (req, res) => {
const file = (Math.random() * 1000000).toFixed(0)
console.log('started processing new request', file)
await fs.mkdir('files', { recursive: true })
const overrides = objectOmit(req.query, 'algo', 'config')
let config = `${path}/configs/` + (req.query?.config ?? 'config') + '.ini'
if (!exists(config)) {
console.log('request finished in error :(', file)
return createErrorResponse(res, {
title: 'Configuration file does not exist',
status: 404,
detail: `The configuration file you want to use does not exist`,
config: req.query?.config ?? 'config'
})
}
const stlPath = `${path}/files/${file}.stl`
const gcodePath = `${path}/files/${file}.gcode`
// console.log(stlPath, req.body)
await fs.writeFile(stlPath, req.body, {
encoding: null
})
// console.log(fs.statSync(stlPath).size, req.body.length)
let additionnalParams = objectMap(overrides, (v, k) => `--${(k as string).replace(/_/g, '-')} ${v}`).join(' ')
const slicer = process.env.SLICER_PATH ?? 'prusa-slicer'
if (slicer.includes('prusa')) {
additionnalParams += ' --export-gcode'
}
const cmd = `${slicer} ${stlPath} --load ${config} --output ${gcodePath} ${additionnalParams}`
try {
await exec(cmd, {
timeout: 60000
})
} catch (e: any) {
console.log('request finished in error :(', file)
const line = e.toString()
console.error(e)
if (line.includes('Objects could not fit on the bed')) {
await fs.rm(stlPath)
return createErrorResponse(res, {
title: 'Object is too large to fit on the bed',
status: 413,
detail: `The object you are trying to slice is too large to fit on the current bed`
})
} else if (line.includes('No such file')) {
await fs.rm(stlPath)
return createErrorResponse(res, {
title: 'Configuration file does not exist',
status: 404,
detail: `The configuration file you want to use does not exist`,
config: req.query?.config ?? 'config'
})
} else if (line.includes('Unknown option')) {
await fs.rm(stlPath)
return createErrorResponse(res, {
title: 'An override does exist',
status: 404,
detail: `One or more of your overrides are not available in the current slicer`,
overrides: overrides
})
} else if (line.includes('ETIMEDOUT') || line.includes('Command failed')) {
await fs.rm(stlPath)
return createErrorResponse(res, {
title: 'File is taking too long to process',
status: 400,
detail: `The file you are trying to process is too large (${req.body.length}o) to be processed in less than 60 secs`,
fileId: file,
config: req.query?.config ?? 'config',
fileSize: req.body.length,
overrides: overrides
})
}
return createErrorResponse(res, {
title: 'General I/O error',
status: 500,
detail: `A server error make it impossible to slice the file`,
fileId: file,
config: req.query?.config ?? 'config',
fileSize: req.body.length,
overrides: overrides,
serverMessage: e.toString().replace(cmd, '<i>software</i>').replace(stlPath, `<i>file ${file}</i>`).replace(`${path}/configs/`, '').replace('.ini', '')
})
}
const gcode = await fs.readFile(gcodePath, 'utf-8')
await fs.rm(stlPath)
await fs.rm(gcodePath)
const params = getParams(gcode)
let price = -1
if (req.query?.algo) {
let algo = req.query.algo as string
// objectLoop(params, (value, key) => {
// if (typeof value !== 'number') {
// return
// }
// while (algo.includes(key)) {
// algo = algo.replace(key, value.toString())
// }
// })
price = evaluate(algo, params)
}
res.json({
price: parseFloat(price.toFixed(2)),
...getParams(gcode),
gcode
})
console.log('request successfull :)', file)
// res.sendFile(gcodePath)
})
server.listen(3000, () => {
console.log(`🚀 Server ready at localhost:3000`);
})