feat: Add RFC error messages
Signed-off-by: Florian BOUILLON <f.bouillon@aptatio.com>
This commit is contained in:
parent
e39e3726ac
commit
19e3892099
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "3dprint-time-estimator",
|
"name": "fi3d-slicer-as-a-service",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
|
54
src/HTTPError.ts
Normal file
54
src/HTTPError.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { Response } from 'express'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An error as of the RFC7807
|
||||||
|
*
|
||||||
|
* you MUST send the content-type `application/problem+json`
|
||||||
|
*/
|
||||||
|
export default interface HTTPError extends Record<string, any> {
|
||||||
|
/**
|
||||||
|
* A URI reference [RFC3986] that identifies the
|
||||||
|
* problem type. This specification encourages that, when
|
||||||
|
* dereferenced, it provide human-readable documentation for the
|
||||||
|
* problem type (e.g., using HTML [W3C.REC-html5-20141028]). When
|
||||||
|
* this member is not present, its value is assumed to be
|
||||||
|
* "about:blank".
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
/**
|
||||||
|
* A short, human-readable summary of the problem
|
||||||
|
* type. It SHOULD NOT change from occurrence to occurrence of the
|
||||||
|
* problem, except for purposes of localization (e.g., using
|
||||||
|
* proactive content negotiation; see [RFC7231], Section 3.4).
|
||||||
|
*/
|
||||||
|
type?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The HTTP status code ([RFC7231], Section 6)
|
||||||
|
* generated by the origin server for this occurrence of the problem.
|
||||||
|
*/
|
||||||
|
status?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A human-readable explanation specific to this
|
||||||
|
* occurrence of the problem.
|
||||||
|
*/
|
||||||
|
detail?: string
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A URI reference that identifies the specific
|
||||||
|
* occurrence of the problem. It may or may not yield further
|
||||||
|
* information if dereferenced.
|
||||||
|
*/
|
||||||
|
instance?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createErrorResponse(res: Response, error: HTTPError) {
|
||||||
|
res.status(error.status ?? 500)
|
||||||
|
if (!error.type) {
|
||||||
|
error.type = 'about:blank'
|
||||||
|
}
|
||||||
|
res.setHeader('Content-Type', 'application/problem+json')
|
||||||
|
res.json(error)
|
||||||
|
}
|
64
src/main.ts
64
src/main.ts
@ -1,10 +1,11 @@
|
|||||||
|
import { objectMap, objectOmit } from '@dzeio/object-util'
|
||||||
|
import { exec as execAsync } from 'child_process'
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import { exec as execAsync } from 'child_process'
|
|
||||||
import { objectMap, objectOmit } from '@dzeio/object-util'
|
|
||||||
import { getParams } from './gcodeUtils'
|
|
||||||
import util from 'util'
|
|
||||||
import { evaluate } from 'mathjs'
|
import { evaluate } from 'mathjs'
|
||||||
|
import util from 'util'
|
||||||
|
import { createErrorResponse } from './HTTPError'
|
||||||
|
import { getParams } from './gcodeUtils'
|
||||||
|
|
||||||
const exec = util.promisify(execAsync)
|
const exec = util.promisify(execAsync)
|
||||||
const server = express()
|
const server = express()
|
||||||
@ -48,8 +49,12 @@ server.post('/', async (req, res) => {
|
|||||||
let config = `${path}/configs/` + (req.query?.config ?? 'config') + '.ini'
|
let config = `${path}/configs/` + (req.query?.config ?? 'config') + '.ini'
|
||||||
if (!exists(config)) {
|
if (!exists(config)) {
|
||||||
console.log('request finished in error :(', file)
|
console.log('request finished in error :(', file)
|
||||||
res.status(403).send('Configuration file not existing')
|
return createErrorResponse(res, {
|
||||||
return
|
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 stlPath = `${path}/files/${file}.stl`
|
||||||
const gcodePath = `${path}/files/${file}.gcode`
|
const gcodePath = `${path}/files/${file}.gcode`
|
||||||
@ -74,23 +79,50 @@ server.post('/', async (req, res) => {
|
|||||||
console.error(e)
|
console.error(e)
|
||||||
if (line.includes('Objects could not fit on the bed')) {
|
if (line.includes('Objects could not fit on the bed')) {
|
||||||
await fs.rm(stlPath)
|
await fs.rm(stlPath)
|
||||||
res.status(403).send('Object is too large')
|
return createErrorResponse(res, {
|
||||||
return
|
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')) {
|
} else if (line.includes('No such file')) {
|
||||||
await fs.rm(stlPath)
|
await fs.rm(stlPath)
|
||||||
res.status(403).send('Configuration file not existing')
|
return createErrorResponse(res, {
|
||||||
return
|
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')) {
|
} else if (line.includes('Unknown option')) {
|
||||||
await fs.rm(stlPath)
|
await fs.rm(stlPath)
|
||||||
res.status(403).send('an override does not exists, please contact an administrator or refer to the documentation')
|
return createErrorResponse(res, {
|
||||||
return
|
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')) {
|
} else if (line.includes('ETIMEDOUT') || line.includes('Command failed')) {
|
||||||
await fs.rm(stlPath)
|
await fs.rm(stlPath)
|
||||||
res.status(403).send('the file is taking too long to process :(')
|
return createErrorResponse(res, {
|
||||||
return
|
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
|
||||||
|
})
|
||||||
}
|
}
|
||||||
res.status(500).send(`Error while making the file:<br />${e.toString().replace(cmd, '<i>software</i>').replace(stlPath, `<i>file ${file}</i>`).replace(`${path}/configs/`, '').replace('.ini', '')}`)
|
return createErrorResponse(res, {
|
||||||
return
|
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')
|
const gcode = await fs.readFile(gcodePath, 'utf-8')
|
||||||
await fs.rm(stlPath)
|
await fs.rm(stlPath)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user