feat: Move to Astro
This commit is contained in:
146
src/pages/api/run.ts
Normal file
146
src/pages/api/run.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import { objectMap, objectOmit } from '@dzeio/object-util'
|
||||
import URLManager from '@dzeio/url-manager'
|
||||
import type { APIRoute } from 'astro'
|
||||
import { exec as execSync } from 'child_process'
|
||||
import fs from 'fs/promises'
|
||||
import { evaluate } from 'mathjs'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path/posix'
|
||||
import { promisify } from 'node:util'
|
||||
import FilesUtils from '../../libs/FilesUtils'
|
||||
import { buildRFC7807Error } from '../../libs/HTTPError'
|
||||
import { getParams } from '../../libs/gcodeUtils'
|
||||
|
||||
const exec = promisify(execSync)
|
||||
|
||||
let tmpDir: string
|
||||
|
||||
export const post: APIRoute = async ({ request }) => {
|
||||
if (!tmpDir) {
|
||||
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'paas-'))
|
||||
}
|
||||
|
||||
const query = new URLManager(request.url).query()
|
||||
const file = (Math.random() * 1000000).toFixed(0)
|
||||
console.log('started processing new request', file)
|
||||
await fs.mkdir(`${tmpDir}/files`, { recursive: true })
|
||||
|
||||
const overrides = objectOmit(query, 'algo', 'config')
|
||||
const configName = query?.config ?? 'config'
|
||||
let config = `${import.meta.env.CONFIGS_PATH}/` + configName + '.ini'
|
||||
if (!await FilesUtils.exists(config)) {
|
||||
console.log('request finished in error :(', file)
|
||||
return buildRFC7807Error({
|
||||
type: '/missing-config-file',
|
||||
status: 404,
|
||||
title: 'Configuration file is missing',
|
||||
details: `the configuration file "${configName}" is not available on the remote server`
|
||||
})
|
||||
}
|
||||
const stlPath = `${tmpDir}/files/${file}.stl`
|
||||
const gcodePath = `${tmpDir}/files/${file}.gcode`
|
||||
|
||||
// write file
|
||||
await fs.writeFile(stlPath, new Uint8Array(Buffer.from(await request.arrayBuffer())), {
|
||||
encoding: null
|
||||
})
|
||||
// console.log(fs.statSync(stlPath).size, req.body.length)
|
||||
let additionnalParams = objectMap(overrides, (value, key) => `--${(key as string).replace(/_/g, '-')} ${value}`).join(' ')
|
||||
const slicer = import.meta.env.PRUSASLICER_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 buildRFC7807Error({
|
||||
type: '/object-too-large',
|
||||
status: 413,
|
||||
title: 'Object is too large'
|
||||
})
|
||||
} else if (line.includes('No such file')) {
|
||||
await fs.rm(stlPath)
|
||||
return buildRFC7807Error({
|
||||
type: '/missing-config-file',
|
||||
status: 404,
|
||||
title: 'Configuration file is missing',
|
||||
details: `the configuration file "${configName}" is not available on the remote server`
|
||||
})
|
||||
} else if (line.includes('Unknown option')) {
|
||||
await fs.rm(stlPath)
|
||||
return buildRFC7807Error({
|
||||
type: '/slicer-option-unknown',
|
||||
status: 400,
|
||||
title: ' config override doew not exists',
|
||||
details: 'an override does not exists, please contact an administrator or refer to the documentation'
|
||||
})
|
||||
} else if (
|
||||
line.includes('is not recognized as an internal or external command') ||
|
||||
line.includes('.dll was not loaded')
|
||||
) {
|
||||
await fs.rm(stlPath)
|
||||
return buildRFC7807Error({
|
||||
type: '/slicer-not-found',
|
||||
status: 408,
|
||||
title: 'the slicer used to process this file has not been found',
|
||||
details: 'the server has a misconfiguration meaning that we can\'t process the request, please contact an administrator',
|
||||
additionnalInfo: line.includes('dll') ? 'Missing DLL' : 'Slicer not found '
|
||||
})
|
||||
} else if (line.includes('ETIMEDOUT')) {
|
||||
await fs.rm(stlPath)
|
||||
return buildRFC7807Error({
|
||||
type: '/timed-out-slicing',
|
||||
status: 408,
|
||||
title: 'Timed out slicing file',
|
||||
detail: `The file you are trying to process takes too long to be processed`,
|
||||
processingTimeoutMillis: 60000
|
||||
})
|
||||
}
|
||||
return buildRFC7807Error({
|
||||
type: '/general-input-output-error',
|
||||
status: 500,
|
||||
title: 'General I/O error',
|
||||
details: 'A server error make it impossible to slice the file, please contact an administrator with the json error',
|
||||
fileId: file,
|
||||
config: configName,
|
||||
// fileSize: req.body.length,
|
||||
overrides: overrides,
|
||||
serverMessage:
|
||||
e.toString().replace(cmd, '***SLICER***').replace(stlPath, configName ?? `***FILE***`).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 (query?.algo) {
|
||||
let algo = 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)
|
||||
}
|
||||
console.log('request successfull :)', file)
|
||||
return {
|
||||
status: 200,
|
||||
body: JSON.stringify({
|
||||
price: parseFloat(price.toFixed(2)),
|
||||
...getParams(gcode),
|
||||
gcode
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user