@fdm-monster/server
Version:
FDM Monster is a bulk OctoPrint manager to set up, configure and monitor 3D printers. Our aim is to provide extremely optimized websocket performance and reliability.
131 lines (130 loc) • 5.45 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "PrusaLinkHttpClientBuilder", {
enumerable: true,
get: function() {
return PrusaLinkHttpClientBuilder;
}
});
const _defaulthttpclientbuilder = require("../../../shared/default-http-client.builder");
const _octoprintserviceconstants = require("../../octoprint/constants/octoprint-service.constants");
const _digestauthutil = require("./digest-auth.util");
const _nodecrypto = require("node:crypto");
class PrusaLinkHttpClientBuilder extends _defaulthttpclientbuilder.DefaultHttpClientBuilder {
maxRetries = 1;
username;
password;
authHeaderContext;
onAuthError;
onAuthSuccess;
onRequestRetry;
build() {
if (!this.axiosOptions.baseURL) {
throw new Error("Base URL is required");
}
const axiosInstance = super.build();
if (this.username && this.password) {
axiosInstance.interceptors.request.use(async (config)=>{
if (this.authHeaderContext) {
const computedDigestHeader = this.generateDigestHeader(config.method?.toUpperCase() ?? "GET", config.url ?? "/");
config.headers[_octoprintserviceconstants.authorizationHeaderKey] = computedDigestHeader;
}
return config;
});
axiosInstance.interceptors.response.use((response)=>response, async (error)=>{
const originalRequest = error.config;
if (error.response?.status === 401 && this.username?.length && this.password?.length && (!originalRequest._retryCount || originalRequest._retryCount < this.maxRetries)) {
const wwwAuthHeader = error.response.headers[_octoprintserviceconstants.wwwAuthenticationHeaderKey];
if (wwwAuthHeader) {
if (typeof this.onAuthSuccess === "function") {
this.onAuthSuccess(wwwAuthHeader);
}
this.saveParsedAuthHeaderContext(wwwAuthHeader);
originalRequest._retryCount = (originalRequest._retryCount ?? 0) + 1;
if (typeof this.onRequestRetry === "function") {
this.onRequestRetry(error, originalRequest._retryCount);
}
return axiosInstance(originalRequest);
}
}
if (error.response?.status === 401 && this.onAuthError && typeof this.onAuthError === "function") {
this.onAuthError(error);
}
return Promise.reject(error);
});
}
return axiosInstance;
}
withDigestAuth(username, password, onAuthError, onRequestRetry, onAuthSuccess) {
if (!username?.length) {
throw new Error("username may not be an empty string");
}
if (!password?.length) {
throw new Error("password may not be an empty string");
}
if (onAuthError && typeof onAuthError !== "function") {
throw new Error("onAuthError must be a function");
}
if (onAuthSuccess && typeof onAuthSuccess !== "function") {
throw new Error("onAuthSuccess must be a function");
}
if (onRequestRetry && typeof onRequestRetry !== "function") {
throw new Error("onRequestRetry must be a function");
}
this.username = username;
this.password = password;
this.onAuthError = onAuthError;
this.onRequestRetry = onRequestRetry;
this.onAuthSuccess = onAuthSuccess;
return this;
}
withAuthHeader(authHeader) {
if (!authHeader?.length) {
throw new Error("Digest header may not be an empty string");
}
this.saveParsedAuthHeaderContext(authHeader);
return this;
}
saveParsedAuthHeaderContext(authHeader) {
const headerValue = authHeader.startsWith("Digest ") ? authHeader.substring(7) : authHeader;
const authParams = Object.fromEntries(headerValue.split(", ").map((param)=>{
const parts = param.split("=");
if (parts.length === 2) {
return [
parts[0],
parts[1].replace(/"/g, "")
];
}
return [
parts[0],
""
];
}));
this.authHeaderContext = {
realm: authParams.realm,
nonce: authParams.nonce,
qop: authParams.qop,
hasQop: "qop" in authParams
};
}
generateDigestHeader(method, uri) {
if (!this.authHeaderContext || !this.username || !this.password) {
throw new Error("Digest auth not properly configured");
}
const { realm, nonce, qop, hasQop } = this.authHeaderContext;
return (0, _digestauthutil.generateDigestAuthHeader)({
username: this.username,
password: this.password,
method,
uri,
realm,
nonce,
qop: hasQop ? qop : undefined,
nc: hasQop ? "00000001" : undefined,
cnonce: hasQop ? (0, _nodecrypto.randomBytes)(8).toString("hex") : undefined
});
}
}
//# sourceMappingURL=prusa-link-http-client.builder.js.map