mirror of
https://github.com/filebrowser/filebrowser.git
synced 2025-09-01 20:10:29 +00:00
123 lines
3.4 KiB
TypeScript
123 lines
3.4 KiB
TypeScript
import * as tus from "tus-js-client";
|
|
import { baseURL, tusEndpoint, tusSettings, origin } from "@/utils/constants";
|
|
import { useAuthStore } from "@/stores/auth";
|
|
import { removePrefix } from "@/api/utils";
|
|
|
|
const RETRY_BASE_DELAY = 1000;
|
|
const RETRY_MAX_DELAY = 20000;
|
|
const CURRENT_UPLOAD_LIST: { [key: string]: tus.Upload } = {};
|
|
|
|
export async function upload(
|
|
filePath: string,
|
|
content: ApiContent = "",
|
|
overwrite = false,
|
|
onupload: any
|
|
) {
|
|
if (!tusSettings) {
|
|
// Shouldn't happen as we check for tus support before calling this function
|
|
throw new Error("Tus.io settings are not defined");
|
|
}
|
|
|
|
filePath = removePrefix(filePath);
|
|
const resourcePath = `${tusEndpoint}${filePath}?override=${overwrite}`;
|
|
|
|
const authStore = useAuthStore();
|
|
|
|
// Exit early because of typescript, tus content can't be a string
|
|
if (content === "") {
|
|
return false;
|
|
}
|
|
return new Promise<void | string>((resolve, reject) => {
|
|
const upload = new tus.Upload(content, {
|
|
endpoint: `${origin}${baseURL}${resourcePath}`,
|
|
chunkSize: tusSettings.chunkSize,
|
|
retryDelays: computeRetryDelays(tusSettings),
|
|
parallelUploads: 1,
|
|
storeFingerprintForResuming: false,
|
|
headers: {
|
|
"X-Auth": authStore.jwt,
|
|
},
|
|
onShouldRetry: function (err) {
|
|
const status = err.originalResponse
|
|
? err.originalResponse.getStatus()
|
|
: 0;
|
|
|
|
// Do not retry for file conflict.
|
|
if (status === 409) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
onError: function (error: Error | tus.DetailedError) {
|
|
delete CURRENT_UPLOAD_LIST[filePath];
|
|
|
|
if (error.message === "Upload aborted") {
|
|
return reject(error);
|
|
}
|
|
|
|
const message =
|
|
error instanceof tus.DetailedError
|
|
? error.originalResponse === null
|
|
? "000 No connection"
|
|
: error.originalResponse.getBody()
|
|
: "Upload failed";
|
|
|
|
console.error(error);
|
|
|
|
reject(new Error(message));
|
|
},
|
|
onProgress: function (bytesUploaded) {
|
|
if (typeof onupload === "function") {
|
|
onupload({ loaded: bytesUploaded });
|
|
}
|
|
},
|
|
onSuccess: function () {
|
|
delete CURRENT_UPLOAD_LIST[filePath];
|
|
resolve();
|
|
},
|
|
});
|
|
CURRENT_UPLOAD_LIST[filePath] = upload;
|
|
upload.start();
|
|
});
|
|
}
|
|
|
|
function computeRetryDelays(tusSettings: TusSettings): number[] | undefined {
|
|
if (!tusSettings.retryCount || tusSettings.retryCount < 1) {
|
|
// Disable retries altogether
|
|
return undefined;
|
|
}
|
|
// The tus client expects our retries as an array with computed backoffs
|
|
// E.g.: [0, 3000, 5000, 10000, 20000]
|
|
const retryDelays = [];
|
|
let delay = 0;
|
|
|
|
for (let i = 0; i < tusSettings.retryCount; i++) {
|
|
retryDelays.push(Math.min(delay, RETRY_MAX_DELAY));
|
|
delay =
|
|
delay === 0 ? RETRY_BASE_DELAY : Math.min(delay * 2, RETRY_MAX_DELAY);
|
|
}
|
|
|
|
return retryDelays;
|
|
}
|
|
|
|
export async function useTus(content: ApiContent) {
|
|
return isTusSupported() && content instanceof Blob;
|
|
}
|
|
|
|
function isTusSupported() {
|
|
return tus.isSupported === true;
|
|
}
|
|
|
|
export function abortAllUploads() {
|
|
for (const filePath in CURRENT_UPLOAD_LIST) {
|
|
if (CURRENT_UPLOAD_LIST[filePath]) {
|
|
CURRENT_UPLOAD_LIST[filePath].abort(true);
|
|
CURRENT_UPLOAD_LIST[filePath].options!.onError!(
|
|
new Error("Upload aborted")
|
|
);
|
|
}
|
|
delete CURRENT_UPLOAD_LIST[filePath];
|
|
}
|
|
}
|