@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.
222 lines (221 loc) • 8.04 kB
JavaScript
import { ExternalServiceError } from "../../exceptions/runtime.exceptions.js";
import { uploadDoneEvent, uploadFailedEvent, uploadProgressEvent } from "../../constants/event.constants.js";
import { OctoprintRoutes } from "./octoprint-api.routes.js";
import { flattenOctoPrintFiles } from "./utils/file.utils.js";
import { OctoprintHttpClientBuilder } from "./utils/octoprint-http-client.builder.js";
import FormData from "form-data";
//#region src/services/octoprint/octoprint.client.ts
/**
* OctoPrint REST API
* https://docs.octoprint.org/en/master/api/index.html
*/
var OctoprintClient = class OctoprintClient extends OctoprintRoutes {
logger;
constructor(loggerFactory, httpClientFactory, eventEmitter2, settingsStore) {
super();
this.httpClientFactory = httpClientFactory;
this.eventEmitter2 = eventEmitter2;
this.settingsStore = settingsStore;
this.logger = loggerFactory(OctoprintClient.name);
}
async getApiVersion(login) {
return await this.createClient(login).get(this.apiVersion);
}
async getServer(login) {
return await this.createClient(login).get(this.apiServer);
}
async login(login) {
return await this.createClient(login).post(this.apiLogin, { passive: true });
}
async sendConnectionCommand(login, commandData) {
await this.createClient(login).post(this.apiConnection, commandData);
}
async sendPrintHeadJogCommand(login, amounts) {
const commandData = {
command: "jog",
x: amounts.x ?? 0,
y: amounts.y ?? 0,
z: amounts.z ?? 0
};
return this.sendPrintHeadCommand(login, commandData);
}
async sendPrintHeadHomeCommand(login, axes) {
const commandData = {
command: "home",
axes: [
...axes.x ? ["x"] : [],
...axes.y ? ["y"] : [],
...axes.z ? ["z"] : []
]
};
return this.sendPrintHeadCommand(login, commandData);
}
/**
* Ability to jog, home or set feed rate
*/
async sendPrintHeadCommand(login, commandData) {
await this.createClient(login).post(this.apiPrinterHead, commandData);
}
async sendCustomGCodeCommand(login, commandString) {
await this.createClient(login).post(this.apiPrinterCustomCommand, { command: commandString });
}
async getJob(login) {
return await this.createClient(login).get(this.apiJob);
}
/**
* Ability to start, cancel, restart, or pause a job
*/
async sendJobCommand(login, commandData) {
return await this.createClient(login).post(this.apiJob, commandData);
}
async sendBedTempCommand(login, targetTemp) {
const data = this.getBedTargetCommand(targetTemp);
return await this.createClient(login).post(this.apiPrinterBed, data);
}
async getSettings(login) {
return await this.createClient(login).get(this.apiSettingsPart);
}
async updatePrinterNameSetting(login, printerName) {
const settingPatch = this.printerNameSetting(printerName);
return await this.createClient(login).post(this.apiSettingsPart, settingPatch);
}
async getCurrentUser(login) {
return await this.createClient(login).get(this.apiCurrentUser);
}
async getAdminUserOrDefault(login) {
return (await this.getCurrentUser(login))?.data?.name;
}
async getUsers(login) {
return await this.createClient(login).get(this.apiUsers);
}
async getLocalFiles(login, recursive = false, startDir = "") {
const url = this.apiGetFiles(recursive, startDir);
const response = await this.createClient(login).get(url);
if (!response?.data) return [];
const data = response.data;
const items = data.files || (data.children ? data.children : []);
if (!items.length) return [];
if (recursive) return flattenOctoPrintFiles(items, startDir);
else return items.map((item) => ({
path: item.path || item.name,
size: item.size || 0,
date: item.date || null,
dir: item.type === "folder"
}));
}
async getFile(login, path) {
const urlPath = this.apiFile(path);
const file = (await this.createClient(login).get(urlPath))?.data;
return {
path: file.path,
size: file.size,
date: file.date,
dir: file.type === "folder"
};
}
async downloadFile(login, path) {
const urlPath = this.downloadFileLocal(path);
return await this.createClient(login).get(urlPath, { responseType: "stream" });
}
async getFileChunk(login, filePath, startBytes, endBytes) {
const pathUrl = this.downloadFileLocal(filePath);
return await this.createClient(login, (o) => o.withHeaders({ Range: `bytes=${startBytes}-${endBytes}` })).get(pathUrl);
}
async createFolder(login, path, foldername) {
const formData = new FormData();
formData.append("path", path);
formData.append("foldername", foldername);
const headers = {
...formData.getHeaders(),
"Content-Length": formData.getLengthSync().toString()
};
return await this.createClient(login, (o) => o.withHeaders(headers)).post(this.apiFilesLocal, formData);
}
async moveFileOrFolder(login, path, destination) {
const command = this.moveFileCommand(destination);
return await this.createClient(login).post(this.apiFile(path), command);
}
async postSelectPrintFile(login, path, print) {
const command = this.selectCommand(print);
const url = this.apiFile(path);
await this.createClient(login).post(url, command);
}
async uploadFileAsMultiPart(login, stream, fileName, contentLength, startPrint, progressToken) {
const urlPath = this.apiFilesLocal;
const formData = new FormData();
if (startPrint) formData.append("print", "true");
formData.append("file", stream, {
filename: fileName,
knownLength: contentLength
});
const result = await new Promise((resolve, reject) => {
return formData.getLength((err, length) => {
if (err) reject(/* @__PURE__ */ new Error("Could not retrieve formData length"));
resolve(length);
});
});
try {
const response = await this.createClient(login, (builder) => builder.withMultiPartFormData().withTimeout(this.settingsStore.getTimeoutSettings().apiUploadTimeout).withHeaders({
...formData.getHeaders(),
"Content-Length": result.toString()
}).withOnUploadProgress((p) => {
if (progressToken) this.eventEmitter2.emit(`${uploadProgressEvent(progressToken)}`, progressToken, p);
})).post(urlPath, formData);
if (progressToken) this.eventEmitter2.emit(`${uploadDoneEvent(progressToken)}`, progressToken);
return response.data;
} catch (e) {
if (progressToken) this.eventEmitter2.emit(`${uploadFailedEvent(progressToken)}`, progressToken, e?.message);
let data;
try {
data = JSON.parse(e.response?.body);
} catch {
data = e.response?.body;
}
throw new ExternalServiceError({
error: e.message,
statusCode: e.response?.statusCode,
data,
success: false,
stack: e.stack
}, "OctoPrint");
}
}
async deleteFileOrFolder(login, path) {
await this.createClient(login).delete(this.apiFile(path));
}
async getPrinterCurrent(login, history, limit, exclude) {
const pathUrl = this.apiPrinterCurrent(history, limit, exclude);
return await this.createClient(login).get(pathUrl);
}
async getConnection(login) {
return await this.createClient(login).get(this.apiConnection);
}
async getPrinterProfiles(login) {
return await this.createClient(login).get(this.apiPrinterProfiles);
}
async getSystemInfo(login) {
return await this.createClient(login).get(this.apiSystemInfo);
}
async getSystemCommands(login) {
return await this.createClient(login).get(this.apiSystemCommands);
}
async postServerRestartCommand(login) {
await this.createClient(login).post(this.apiServerRestartCommand);
}
createClient(login, buildFluentOptions) {
const baseAddress = login.printerURL;
return this.createAnonymousClient(baseAddress, (o) => {
if (buildFluentOptions) buildFluentOptions(o);
o.withXApiKeyHeader(login.apiKey);
});
}
createAnonymousClient(baseAddress, buildFluentOptions) {
const builder = new OctoprintHttpClientBuilder();
return this.httpClientFactory.createClientWithBaseUrl(builder, baseAddress, (b) => {
if (buildFluentOptions) buildFluentOptions(b);
});
}
};
//#endregion
export { OctoprintClient };
//# sourceMappingURL=octoprint.client.js.map