@fdm-monster/server
Version:
FDM Monster is a bulk OctoPrint, Klipper, PrusaLink and BambuLab manager to set up, configure and monitor 3D printers. Our aim is to provide neat overview over your farm.
186 lines (185 loc) • 6.8 kB
JavaScript
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
import { AppConstants } from "../server.constants.js";
import { MulterService } from "../services/core/multer.service.js";
import { FileStorageService } from "../services/file-storage.service.js";
import { FileAnalysisService } from "../services/file-analysis.service.js";
import { slicerApiKeyAuth } from "../middleware/slicer-api-key.middleware.js";
import { GET, POST, before, route } from "awilix-express";
//#region src/controllers/slicer-compat.controller.ts
var slicer_compat_controller_exports = /* @__PURE__ */ __exportAll({ SlicerCompatController: () => SlicerCompatController });
var _ref, _ref2, _ref3, _SlicerCompatController;
let SlicerCompatController = _SlicerCompatController = class SlicerCompatController {
logger;
constructor(loggerFactory, fileStorageService, fileAnalysisService, multerService) {
this.fileStorageService = fileStorageService;
this.fileAnalysisService = fileAnalysisService;
this.multerService = multerService;
this.logger = loggerFactory(_SlicerCompatController.name);
}
/**
* OctoPrint version endpoint
* GET /api/version
*/
async getVersion(req, res) {
res.send({
api: "0.1",
server: "1.9.0",
text: "OctoPrint 1.9.3"
});
}
/**
* OctoPrint files endpoint - Upload file
* POST /api/files/local
*
* This endpoint mimics OctoPrint's file upload API for slicer compatibility
* PrusaSlicer uses: POST /api/files/local with multipart form data
*/
async uploadFile(req, res) {
let files;
try {
const acceptedExtensions = [...AppConstants.defaultAcceptedGcodeExtensions, ...AppConstants.defaultAcceptedBambuExtensions];
files = await this.multerService.multerLoadFileAsync(req, res, acceptedExtensions, true);
if (!files?.length) {
res.status(400).send({ error: "No file uploaded" });
return;
}
const file = files[0];
await this.fileStorageService.validateUniqueFilename(file.originalname);
const fileHash = await this.fileStorageService.calculateFileHash(file.path);
const fileStorageId = await this.fileStorageService.saveFile(file, fileHash);
const filePath = this.fileStorageService.getFilePath(fileStorageId);
let metadata = {};
let thumbnails = [];
try {
const analysisResult = await this.fileAnalysisService.analyzeFile(filePath);
metadata = analysisResult.metadata;
thumbnails = analysisResult.thumbnails || [];
const thumbnailMetadata = thumbnails.length > 0 ? await this.fileStorageService.saveThumbnails(fileStorageId, thumbnails) : [];
await this.fileStorageService.saveMetadata(fileStorageId, metadata, fileHash, file.originalname, thumbnailMetadata);
} catch (analysisError) {
this.logger.error(`Failed to analyze uploaded file: ${analysisError}`);
}
try {
this.multerService.clearUploadedFile(file);
} catch (e) {
this.logger.error(`Could not remove uploaded file from temporary storage`);
}
res.status(201).send({
files: { local: {
name: file.originalname,
origin: "local",
refs: {
resource: `/api/files/local/${fileStorageId}`,
download: `/api/files/local/${fileStorageId}`
}
} },
done: true,
_fdmMonster: {
fileStorageId,
fileHash,
analyzed: Object.keys(metadata).length > 0,
thumbnailCount: thumbnails.length,
printTime: metadata.gcodePrintTimeSeconds,
filament: metadata.filamentUsedGrams
}
});
this.logger.log(`File uploaded to printer: ${file.originalname} -> ${fileStorageId}`);
} catch (error) {
if (files?.[0]?.path) try {
this.multerService.clearUploadedFile(files[0]);
} catch (e) {
this.logger.error(`Could not remove uploaded file from temporary storage`);
}
throw error;
}
}
/**
* Files list endpoint
* GET /api/files
*/
async listFiles(req, res) {
try {
const knownFiles = (await this.fileStorageService.listAllFiles()).map((file) => ({
name: file.metadata?._originalFileName || file.fileName,
path: file.fileStorageId,
type: "machinecode",
typePath: ["machinecode", file.fileFormat],
origin: "local",
refs: {
resource: `/api/files/local/${file.fileStorageId}`,
download: `/api/files/local/${file.fileStorageId}`
},
gcodeAnalysis: file.metadata ? {
estimatedPrintTime: file.metadata.gcodePrintTimeSeconds,
filament: { tool0: {
length: file.metadata.filamentUsedMm,
volume: file.metadata.filamentUsedCm3
} }
} : void 0,
date: Math.floor(file.createdAt.getTime() / 1e3),
size: file.fileSize
}));
res.send({
files: knownFiles,
free: 0,
total: 0
});
} catch (error) {
this.logger.error(`Failed to list files via printer API: ${error}`);
throw error;
}
}
/**
* Server endpoint (for compatibility checks)
* GET /api/server
*/
async getServer(req, res) {
res.send({
version: "1.9.0",
safemode: null
});
}
};
__decorate([
GET(),
route("/version"),
__decorateMetadata("design:type", Function),
__decorateMetadata("design:paramtypes", [Object, Object]),
__decorateMetadata("design:returntype", Promise)
], SlicerCompatController.prototype, "getVersion", null);
__decorate([
POST(),
route("/files/local"),
__decorateMetadata("design:type", Function),
__decorateMetadata("design:paramtypes", [Object, Object]),
__decorateMetadata("design:returntype", Promise)
], SlicerCompatController.prototype, "uploadFile", null);
__decorate([
GET(),
route("/files"),
__decorateMetadata("design:type", Function),
__decorateMetadata("design:paramtypes", [Object, Object]),
__decorateMetadata("design:returntype", Promise)
], SlicerCompatController.prototype, "listFiles", null);
__decorate([
GET(),
route("/server"),
__decorateMetadata("design:type", Function),
__decorateMetadata("design:paramtypes", [Object, Object]),
__decorateMetadata("design:returntype", Promise)
], SlicerCompatController.prototype, "getServer", null);
SlicerCompatController = _SlicerCompatController = __decorate([
route("/api"),
before([slicerApiKeyAuth()]),
__decorateMetadata("design:paramtypes", [
Object,
typeof (_ref = typeof FileStorageService !== "undefined" && FileStorageService) === "function" ? _ref : Object,
typeof (_ref2 = typeof FileAnalysisService !== "undefined" && FileAnalysisService) === "function" ? _ref2 : Object,
typeof (_ref3 = typeof MulterService !== "undefined" && MulterService) === "function" ? _ref3 : Object
])
], SlicerCompatController);
//#endregion
export { SlicerCompatController, slicer_compat_controller_exports };
//# sourceMappingURL=slicer-compat.controller.js.map