mirror of
https://github.com/filebrowser/filebrowser.git
synced 2025-07-19 14:30:27 +00:00

macOS saves the download URL in the metadata of the downloaded file. This means that the downloaded file contains a metadata item with the JWT token of the user. If the user were to share this file with someone else, they would have access to their account using the JWT in the metadata during the validity of the JWT. The JWT has been removed from the URLs. Since the user is logged in, there is an authentication cookie set. A JWT in the URL is not necessary.
215 lines
5.0 KiB
TypeScript
215 lines
5.0 KiB
TypeScript
import { useAuthStore } from "@/stores/auth";
|
|
import { useLayoutStore } from "@/stores/layout";
|
|
import { baseURL } from "@/utils/constants";
|
|
import { upload as postTus, useTus } from "./tus";
|
|
import { createURL, fetchURL, removePrefix } from "./utils";
|
|
|
|
export async function fetch(url: string) {
|
|
url = removePrefix(url);
|
|
|
|
const res = await fetchURL(`/api/resources${url}`, {});
|
|
|
|
const data = (await res.json()) as Resource;
|
|
data.url = `/files${url}`;
|
|
|
|
if (data.isDir) {
|
|
if (!data.url.endsWith("/")) data.url += "/";
|
|
// Perhaps change the any
|
|
data.items = data.items.map((item: any, index: any) => {
|
|
item.index = index;
|
|
item.url = `${data.url}${encodeURIComponent(item.name)}`;
|
|
|
|
if (item.isDir) {
|
|
item.url += "/";
|
|
}
|
|
|
|
return item;
|
|
});
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
async function resourceAction(url: string, method: ApiMethod, content?: any) {
|
|
url = removePrefix(url);
|
|
|
|
const opts: ApiOpts = {
|
|
method,
|
|
};
|
|
|
|
if (content) {
|
|
opts.body = content;
|
|
}
|
|
|
|
const res = await fetchURL(`/api/resources${url}`, opts);
|
|
|
|
return res;
|
|
}
|
|
|
|
export async function remove(url: string) {
|
|
return resourceAction(url, "DELETE");
|
|
}
|
|
|
|
export async function put(url: string, content = "") {
|
|
return resourceAction(url, "PUT", content);
|
|
}
|
|
|
|
export function download(format: any, ...files: string[]) {
|
|
let url = `${baseURL}/api/raw`;
|
|
|
|
if (files.length === 1) {
|
|
url += removePrefix(files[0]) + "?";
|
|
} else {
|
|
let arg = "";
|
|
|
|
for (const file of files) {
|
|
arg += removePrefix(file) + ",";
|
|
}
|
|
|
|
arg = arg.substring(0, arg.length - 1);
|
|
arg = encodeURIComponent(arg);
|
|
url += `/?files=${arg}&`;
|
|
}
|
|
|
|
if (format) {
|
|
url += `algo=${format}&`;
|
|
}
|
|
|
|
window.open(url);
|
|
}
|
|
|
|
export async function post(
|
|
url: string,
|
|
content: ApiContent = "",
|
|
overwrite = false,
|
|
onupload: any = () => {}
|
|
) {
|
|
// Use the pre-existing API if:
|
|
const useResourcesApi =
|
|
// a folder is being created
|
|
url.endsWith("/") ||
|
|
// We're not using http(s)
|
|
(content instanceof Blob &&
|
|
!["http:", "https:"].includes(window.location.protocol)) ||
|
|
// Tus is disabled / not applicable
|
|
!(await useTus(content));
|
|
return useResourcesApi
|
|
? postResources(url, content, overwrite, onupload)
|
|
: postTus(url, content, overwrite, onupload);
|
|
}
|
|
|
|
async function postResources(
|
|
url: string,
|
|
content: ApiContent = "",
|
|
overwrite = false,
|
|
onupload: any
|
|
) {
|
|
url = removePrefix(url);
|
|
|
|
let bufferContent: ArrayBuffer;
|
|
if (
|
|
content instanceof Blob &&
|
|
!["http:", "https:"].includes(window.location.protocol)
|
|
) {
|
|
bufferContent = await new Response(content).arrayBuffer();
|
|
}
|
|
|
|
const authStore = useAuthStore();
|
|
return new Promise((resolve, reject) => {
|
|
const request = new XMLHttpRequest();
|
|
request.open(
|
|
"POST",
|
|
`${baseURL}/api/resources${url}?override=${overwrite}`,
|
|
true
|
|
);
|
|
request.setRequestHeader("X-Auth", authStore.jwt);
|
|
|
|
if (typeof onupload === "function") {
|
|
request.upload.onprogress = onupload;
|
|
}
|
|
|
|
request.onload = () => {
|
|
if (request.status === 200) {
|
|
resolve(request.responseText);
|
|
} else if (request.status === 409) {
|
|
reject(request.status);
|
|
} else {
|
|
reject(request.responseText);
|
|
}
|
|
};
|
|
|
|
request.onerror = () => {
|
|
reject(new Error("001 Connection aborted"));
|
|
};
|
|
|
|
request.send(bufferContent || content);
|
|
});
|
|
}
|
|
|
|
function moveCopy(
|
|
items: any[],
|
|
copy = false,
|
|
overwrite = false,
|
|
rename = false
|
|
) {
|
|
const layoutStore = useLayoutStore();
|
|
const promises = [];
|
|
|
|
for (const item of items) {
|
|
const from = item.from;
|
|
const to = encodeURIComponent(removePrefix(item.to ?? ""));
|
|
const url = `${from}?action=${
|
|
copy ? "copy" : "rename"
|
|
}&destination=${to}&override=${overwrite}&rename=${rename}`;
|
|
promises.push(resourceAction(url, "PATCH"));
|
|
}
|
|
layoutStore.closeHovers();
|
|
return Promise.all(promises);
|
|
}
|
|
|
|
export function move(items: any[], overwrite = false, rename = false) {
|
|
return moveCopy(items, false, overwrite, rename);
|
|
}
|
|
|
|
export function copy(items: any[], overwrite = false, rename = false) {
|
|
return moveCopy(items, true, overwrite, rename);
|
|
}
|
|
|
|
export async function checksum(url: string, algo: ChecksumAlg) {
|
|
const data = await resourceAction(`${url}?checksum=${algo}`, "GET");
|
|
return (await data.json()).checksums[algo];
|
|
}
|
|
|
|
export function getDownloadURL(file: ResourceItem, inline: any) {
|
|
const params = {
|
|
...(inline && { inline: "true" }),
|
|
};
|
|
|
|
return createURL("api/raw" + file.path, params);
|
|
}
|
|
|
|
export function getPreviewURL(file: ResourceItem, size: string) {
|
|
const params = {
|
|
inline: "true",
|
|
key: Date.parse(file.modified),
|
|
};
|
|
|
|
return createURL("api/preview/" + size + file.path, params);
|
|
}
|
|
|
|
export function getSubtitlesURL(file: ResourceItem) {
|
|
const params = {
|
|
inline: "true",
|
|
};
|
|
|
|
return file.subtitles?.map((d) => createURL("api/subtitle" + d, params));
|
|
}
|
|
|
|
export async function usage(url: string) {
|
|
url = removePrefix(url);
|
|
|
|
const res = await fetchURL(`/api/usage${url}`, {});
|
|
|
|
return await res.json();
|
|
}
|