diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index c5a96b80..2cfcbd94 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -50,7 +50,8 @@
"typescript": "^5.2.2",
"vite": "^4.4.9",
"vite-plugin-compression2": "^0.10.4",
- "vite-plugin-rewrite-all": "^1.0.1"
+ "vite-plugin-rewrite-all": "^1.0.1",
+ "vue-tsc": "^1.8.10"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -2541,6 +2542,33 @@
"vue": "^3.2.25"
}
},
+ "node_modules/@volar/language-core": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.1.tgz",
+ "integrity": "sha512-JnsM1mIPdfGPxmoOcK1c7HYAsL6YOv0TCJ4aW3AXPZN/Jb4R77epDyMZIVudSGjWMbvv/JfUa+rQ+dGKTmgwBA==",
+ "dev": true,
+ "dependencies": {
+ "@volar/source-map": "1.10.1"
+ }
+ },
+ "node_modules/@volar/source-map": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.1.tgz",
+ "integrity": "sha512-3/S6KQbqa7pGC8CxPrg69qHLpOvkiPHGJtWPkI/1AXCsktkJ6gIk/5z4hyuMp8Anvs6eS/Kvp/GZa3ut3votKA==",
+ "dev": true,
+ "dependencies": {
+ "muggle-string": "^0.3.1"
+ }
+ },
+ "node_modules/@volar/typescript": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.1.tgz",
+ "integrity": "sha512-+iiO9yUSRHIYjlteT+QcdRq8b44qH19/eiUZtjNtuh6D9ailYM7DVR0zO2sEgJlvCaunw/CF9Ov2KooQBpR4VQ==",
+ "dev": true,
+ "dependencies": {
+ "@volar/language-core": "1.10.1"
+ }
+ },
"node_modules/@vue/compat": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compat/-/compat-3.3.4.tgz",
@@ -2619,6 +2647,54 @@
"prettier": ">= 3.0.0"
}
},
+ "node_modules/@vue/language-core": {
+ "version": "1.8.10",
+ "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.10.tgz",
+ "integrity": "sha512-db8PtM4ZZr7SYNH30XpKxUYnUBYaTvcuJ4c2whKK04fuAjbtjAIZ2al5GzGEfUlesmvkpgdbiSviRXUxgD9Omw==",
+ "dev": true,
+ "dependencies": {
+ "@volar/language-core": "~1.10.0",
+ "@volar/source-map": "~1.10.0",
+ "@vue/compiler-dom": "^3.3.0",
+ "@vue/reactivity": "^3.3.0",
+ "@vue/shared": "^3.3.0",
+ "minimatch": "^9.0.0",
+ "muggle-string": "^0.3.1",
+ "vue-template-compiler": "^2.7.14"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vue/language-core/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@vue/language-core/node_modules/minimatch": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+ "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/@vue/reactivity": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz",
@@ -2675,6 +2751,16 @@
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ=="
},
+ "node_modules/@vue/typescript": {
+ "version": "1.8.10",
+ "resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.10.tgz",
+ "integrity": "sha512-vPSpTXMk4chYwvyTGjM891cKgnx2r6vtbdANOp2mRU31f4HYGyLrZBlGgiua7SaO2cLjUg8y91OipJe0t8OFhA==",
+ "dev": true,
+ "dependencies": {
+ "@volar/typescript": "~1.10.0",
+ "@vue/language-core": "1.8.10"
+ }
+ },
"node_modules/@vueuse/core": {
"version": "10.4.1",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.4.1.tgz",
@@ -3301,6 +3387,12 @@
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz",
"integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA=="
},
+ "node_modules/de-indent": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
+ "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
+ "dev": true
+ },
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -4194,6 +4286,15 @@
"node": ">=4"
}
},
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true,
+ "bin": {
+ "he": "bin/he"
+ }
+ },
"node_modules/html-encoding-sniffer": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
@@ -4858,6 +4959,12 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
+ "node_modules/muggle-string": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz",
+ "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==",
+ "dev": true
+ },
"node_modules/nanoid": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
@@ -6321,6 +6428,66 @@
"resolved": "https://registry.npmjs.org/vue-simple-progress/-/vue-simple-progress-1.1.1.tgz",
"integrity": "sha512-ltLWYBA5eVQHWyt1NwZeGeK0VQC69JVh1oqUdro0po7r8Hc8SEMEyEfuwyCO4s27h5I3jbD99BKKkyPSQZgoZA=="
},
+ "node_modules/vue-template-compiler": {
+ "version": "2.7.14",
+ "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
+ "integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
+ "dev": true,
+ "dependencies": {
+ "de-indent": "^1.0.2",
+ "he": "^1.2.0"
+ }
+ },
+ "node_modules/vue-tsc": {
+ "version": "1.8.10",
+ "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.10.tgz",
+ "integrity": "sha512-ptpTFFDoHQgkWJF7i5iERxooiQzOGtG1uKTfmAUuS3qPuSQGq+Ky/S8BFHhnFGwoOxq/PjmGN2QSZEfg1rtzQA==",
+ "dev": true,
+ "dependencies": {
+ "@vue/language-core": "1.8.10",
+ "@vue/typescript": "1.8.10",
+ "semver": "^7.3.8"
+ },
+ "bin": {
+ "vue-tsc": "bin/vue-tsc.js"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ }
+ },
+ "node_modules/vue-tsc/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/vue-tsc/node_modules/semver": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/vue-tsc/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
"node_modules/w3c-xmlserializer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 2ff04e29..262e3550 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -6,7 +6,7 @@
"scripts": {
"dev": "vite dev",
"serve": "vite serve",
- "build": "vite build",
+ "build": "vue-tsc -p ./tsconfig.json --noEmit && vite build --emptyOutDir",
"watch": "vite build --watch",
"clean": "find ./dist -maxdepth 1 -mindepth 1 ! -name '.gitkeep' -exec rm -r {} +",
"lint": "eslint --ext .vue,.js src/",
@@ -56,7 +56,8 @@
"typescript": "^5.2.2",
"vite": "^4.4.9",
"vite-plugin-compression2": "^0.10.4",
- "vite-plugin-rewrite-all": "^1.0.1"
+ "vite-plugin-rewrite-all": "^1.0.1",
+ "vue-tsc": "^1.8.10"
},
"browserslist": [
"> 1%",
diff --git a/frontend/public/index.html b/frontend/public/index.html
index 39d926d8..04135401 100644
--- a/frontend/public/index.html
+++ b/frontend/public/index.html
@@ -179,7 +179,7 @@
-
+
[{[ if .Theme -]}]
conn.send(command);
- conn.onmessage = onmessage;
- conn.onclose = onclose;
-}
diff --git a/frontend/src/api/files.js b/frontend/src/api/files.js
deleted file mode 100644
index 65a61e6a..00000000
--- a/frontend/src/api/files.js
+++ /dev/null
@@ -1,204 +0,0 @@
-import { createURL, fetchURL, removePrefix } from "./utils";
-import { baseURL } from "@/utils/constants";
-import { useAuthStore } from "@/stores/auth";
-import { upload as postTus, useTus } from "./tus";
-
-export async function fetch(url) {
- url = removePrefix(url);
-
- const res = await fetchURL(`/api/resources${url}`, {});
-
- let data = await res.json();
- data.url = `/files${url}`;
-
- if (data.isDir) {
- if (!data.url.endsWith("/")) data.url += "/";
- data.items = data.items.map((item, index) => {
- item.index = index;
- item.url = `${data.url}${encodeURIComponent(item.name)}`;
-
- if (item.isDir) {
- item.url += "/";
- }
-
- return item;
- });
- }
-
- return data;
-}
-
-async function resourceAction(url, method, content) {
- url = removePrefix(url);
-
- let opts = { method };
-
- if (content) {
- opts.body = content;
- }
-
- const res = await fetchURL(`/api/resources${url}`, opts);
-
- return res;
-}
-
-export async function remove(url) {
- return resourceAction(url, "DELETE");
-}
-
-export async function put(url, content = "") {
- return resourceAction(url, "PUT", content);
-}
-
-export function download(format, ...files) {
- let url = `${baseURL}/api/raw`;
-
- if (files.length === 1) {
- url += removePrefix(files[0]) + "?";
- } else {
- let arg = "";
-
- for (let file of files) {
- arg += removePrefix(file) + ",";
- }
-
- arg = arg.substring(0, arg.length - 1);
- arg = encodeURIComponent(arg);
- url += `/?files=${arg}&`;
- }
-
- if (format) {
- url += `algo=${format}&`;
- }
-
- const authStore = useAuthStore();
- if (authStore.jwt) {
- url += `auth=${authStore.jwt}&`;
- }
-
- window.open(url);
-}
-
-export async function post(url, content = "", overwrite = false, onupload) {
- // 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, content = "", overwrite = false, onupload) {
- url = removePrefix(url);
-
- let bufferContent;
- if (
- content instanceof Blob &&
- !["http:", "https:"].includes(window.location.protocol)
- ) {
- bufferContent = await new Response(content).arrayBuffer();
- }
-
- const authStore = useAuthStore();
- return new Promise((resolve, reject) => {
- let 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, copy = false, overwrite = false, rename = false) {
- let promises = [];
-
- for (let 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"));
- }
-
- return Promise.all(promises);
-}
-
-export function move(items, overwrite = false, rename = false) {
- return moveCopy(items, false, overwrite, rename);
-}
-
-export function copy(items, overwrite = false, rename = false) {
- return moveCopy(items, true, overwrite, rename);
-}
-
-export async function checksum(url, algo) {
- const data = await resourceAction(`${url}?checksum=${algo}`, "GET");
- return (await data.json()).checksums[algo];
-}
-
-export function getDownloadURL(file, inline) {
- const params = {
- ...(inline && { inline: "true" }),
- };
-
- return createURL("api/raw" + file.path, params);
-}
-
-export function getPreviewURL(file, size) {
- const params = {
- inline: "true",
- key: Date.parse(file.modified),
- };
-
- return createURL("api/preview/" + size + file.path, params);
-}
-
-export function getSubtitlesURL(file) {
- const params = {
- inline: "true",
- };
-
- const subtitles = [];
- for (const sub of file.subtitles) {
- subtitles.push(createURL("api/raw" + sub, params));
- }
-
- return subtitles;
-}
-
-export async function usage(url) {
- url = removePrefix(url);
-
- const res = await fetchURL(`/api/usage${url}`, {});
-
- return await res.json();
-}
diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js
deleted file mode 100644
index abc189dc..00000000
--- a/frontend/src/api/index.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import * as files from "./files";
-import * as share from "./share";
-import * as users from "./users";
-import * as settings from "./settings";
-import * as pub from "./pub";
-import search from "./search";
-import commands from "./commands";
-
-export { files, share, users, settings, pub, commands, search };
diff --git a/frontend/src/api/pub.js b/frontend/src/api/pub.js
deleted file mode 100644
index 1511143d..00000000
--- a/frontend/src/api/pub.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import { fetchURL, removePrefix, createURL } from "./utils";
-import { baseURL } from "@/utils/constants";
-
-export async function fetch(url, password = "") {
- url = removePrefix(url);
-
- const res = await fetchURL(
- `/api/public/share${url}`,
- {
- headers: { "X-SHARE-PASSWORD": encodeURIComponent(password) },
- },
- false
- );
-
- let data = await res.json();
- data.url = `/share${url}`;
-
- if (data.isDir) {
- if (!data.url.endsWith("/")) data.url += "/";
- data.items = data.items.map((item, index) => {
- item.index = index;
- item.url = `${data.url}${encodeURIComponent(item.name)}`;
-
- if (item.isDir) {
- item.url += "/";
- }
-
- return item;
- });
- }
-
- return data;
-}
-
-export function download(format, hash, token, ...files) {
- let url = `${baseURL}/api/public/dl/${hash}`;
-
- if (files.length === 1) {
- url += encodeURIComponent(files[0]) + "?";
- } else {
- let arg = "";
-
- for (let file of files) {
- arg += encodeURIComponent(file) + ",";
- }
-
- arg = arg.substring(0, arg.length - 1);
- arg = encodeURIComponent(arg);
- url += `/?files=${arg}&`;
- }
-
- if (format) {
- url += `algo=${format}&`;
- }
-
- if (token) {
- url += `token=${token}&`;
- }
-
- window.open(url);
-}
-
-export function getDownloadURL(share, inline = false) {
- const params = {
- ...(inline && { inline: "true" }),
- ...(share.token && { token: share.token }),
- };
-
- return createURL("api/public/dl/" + share.hash + share.path, params, false);
-}
diff --git a/frontend/src/api/search.js b/frontend/src/api/search.js
deleted file mode 100644
index 42846880..00000000
--- a/frontend/src/api/search.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { fetchURL, removePrefix } from "./utils";
-import url from "../utils/url";
-
-export default async function search(base, query) {
- base = removePrefix(base);
- query = encodeURIComponent(query);
-
- if (!base.endsWith("/")) {
- base += "/";
- }
-
- let res = await fetchURL(`/api/search${base}?query=${query}`, {});
-
- let data = await res.json();
-
- data = data.map((item) => {
- item.url = `/files${base}` + url.encodePath(item.path);
-
- if (item.dir) {
- item.url += "/";
- }
-
- return item;
- });
-
- return data;
-}
diff --git a/frontend/src/api/settings.js b/frontend/src/api/settings.js
deleted file mode 100644
index e03b0db1..00000000
--- a/frontend/src/api/settings.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { fetchURL, fetchJSON } from "./utils";
-
-export function get() {
- return fetchJSON(`/api/settings`, {});
-}
-
-export async function update(settings) {
- await fetchURL(`/api/settings`, {
- method: "PUT",
- body: JSON.stringify(settings),
- });
-}
diff --git a/frontend/src/api/share.js b/frontend/src/api/share.js
deleted file mode 100644
index 28d550ea..00000000
--- a/frontend/src/api/share.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import { fetchURL, fetchJSON, removePrefix, createURL } from "./utils";
-
-export async function list() {
- return fetchJSON("/api/shares");
-}
-
-export async function get(url) {
- url = removePrefix(url);
- return fetchJSON(`/api/share${url}`);
-}
-
-export async function remove(hash) {
- await fetchURL(`/api/share/${hash}`, {
- method: "DELETE",
- });
-}
-
-export async function create(url, password = "", expires = "", unit = "hours") {
- url = removePrefix(url);
- url = `/api/share${url}`;
- if (expires !== "") {
- url += `?expires=${expires}&unit=${unit}`;
- }
- let body = "{}";
- if (password != "" || expires !== "" || unit !== "hours") {
- body = JSON.stringify({
- password: password,
- expires: expires.toString(), // backend expects string not number
- unit: unit,
- });
- }
- return fetchJSON(url, {
- method: "POST",
- body: body,
- });
-}
-
-export function getShareURL(share) {
- return createURL("share/" + share.hash, {}, false);
-}
diff --git a/frontend/src/api/tus.js b/frontend/src/api/tus.js
deleted file mode 100644
index 83cb607a..00000000
--- a/frontend/src/api/tus.js
+++ /dev/null
@@ -1,91 +0,0 @@
-import * as tus from "tus-js-client";
-import { baseURL, tusEndpoint, tusSettings } from "@/utils/constants";
-import { useAuthStore } from "@/stores/auth";
-import { removePrefix } from "@/api/utils";
-import { fetchURL } from "./utils";
-
-const RETRY_BASE_DELAY = 1000;
-const RETRY_MAX_DELAY = 20000;
-
-export async function upload(
- filePath,
- content = "",
- overwrite = false,
- onupload
-) {
- 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);
- let resourcePath = `${tusEndpoint}${filePath}?override=${overwrite}`;
-
- await createUpload(resourcePath);
-
- const authStore = useAuthStore();
- return new Promise((resolve, reject) => {
- let upload = new tus.Upload(content, {
- uploadUrl: `${baseURL}${resourcePath}`,
- chunkSize: tusSettings.chunkSize,
- retryDelays: computeRetryDelays(tusSettings),
- parallelUploads: 1,
- storeFingerprintForResuming: false,
- headers: {
- "X-Auth": authStore.jwt,
- },
- onError: function (error) {
- reject("Upload failed: " + error);
- },
- onProgress: function (bytesUploaded) {
- // Emulate ProgressEvent.loaded which is used by calling functions
- // loaded is specified in bytes (https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent/loaded)
- if (typeof onupload === "function") {
- onupload({ loaded: bytesUploaded });
- }
- },
- onSuccess: function () {
- resolve();
- },
- });
- upload.start();
- });
-}
-
-async function createUpload(resourcePath) {
- let headResp = await fetchURL(resourcePath, {
- method: "POST",
- });
- if (headResp.status !== 201) {
- throw new Error(
- `Failed to create an upload: ${headResp.status} ${headResp.statusText}`
- );
- }
-}
-
-function computeRetryDelays(tusSettings) {
- if (!tusSettings.retryCount || tusSettings.retryCount < 1) {
- // Disable retries altogether
- return null;
- }
- // 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) {
- return isTusSupported() && content instanceof Blob;
-}
-
-function isTusSupported() {
- return tus.isSupported === true;
-}
diff --git a/frontend/src/api/users.js b/frontend/src/api/users.js
deleted file mode 100644
index 105d6cc0..00000000
--- a/frontend/src/api/users.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { fetchURL, fetchJSON } from "./utils";
-
-export async function getAll() {
- return fetchJSON(`/api/users`, {});
-}
-
-export async function get(id) {
- return fetchJSON(`/api/users/${id}`, {});
-}
-
-export async function create(user) {
- const res = await fetchURL(`/api/users`, {
- method: "POST",
- body: JSON.stringify({
- what: "user",
- which: [],
- data: user,
- }),
- });
-
- if (res.status === 201) {
- return res.headers.get("Location");
- }
-}
-
-export async function update(user, which = ["all"]) {
- await fetchURL(`/api/users/${user.id}`, {
- method: "PUT",
- body: JSON.stringify({
- what: "user",
- which: which,
- data: user,
- }),
- });
-}
-
-export async function remove(id) {
- await fetchURL(`/api/users/${id}`, {
- method: "DELETE",
- });
-}
diff --git a/frontend/src/api/utils.js b/frontend/src/api/utils.js
deleted file mode 100644
index 26b62d4f..00000000
--- a/frontend/src/api/utils.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import { useAuthStore } from "@/stores/auth";
-import { renew, logout } from "@/utils/auth";
-import { baseURL } from "@/utils/constants";
-import { encodePath } from "@/utils/url";
-
-export async function fetchURL(url, opts, auth = true) {
- const authStore = useAuthStore();
-
- opts = opts || {};
- opts.headers = opts.headers || {};
-
- let { headers, ...rest } = opts;
- let res;
- try {
- res = await fetch(`${baseURL}${url}`, {
- headers: {
- "X-Auth": authStore.jwt,
- ...headers,
- },
- ...rest,
- });
- } catch {
- const error = new Error("000 No connection");
- error.status = 0;
-
- throw error;
- }
-
- if (auth && res.headers.get("X-Renew-Token") === "true") {
- await renew(authStore.jwt);
- }
-
- if (res.status < 200 || res.status > 299) {
- const error = new Error(await res.text());
- error.status = res.status;
-
- if (auth && res.status == 401) {
- logout();
- }
-
- throw error;
- }
-
- return res;
-}
-
-export async function fetchJSON(url, opts) {
- const res = await fetchURL(url, opts);
-
- if (res.status === 200) {
- return res.json();
- } else {
- throw new Error(res.status);
- }
-}
-
-export function removePrefix(url) {
- url = url.split("/").splice(2).join("/");
-
- if (url === "") url = "/";
- if (url[0] !== "/") url = "/" + url;
- return url;
-}
-
-export function createURL(endpoint, params = {}, auth = true) {
- const authStore = useAuthStore();
-
- let prefix = baseURL;
- if (!prefix.endsWith("/")) {
- prefix = prefix + "/";
- }
- const url = new URL(prefix + encodePath(endpoint), origin);
-
- const searchParams = {
- ...(auth && { auth: authStore.jwt }),
- ...params,
- };
-
- for (const key in searchParams) {
- url.searchParams.set(key, searchParams[key]);
- }
-
- return url.toString();
-}
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts
index 31035b49..098b1be6 100644
--- a/frontend/src/router/index.ts
+++ b/frontend/src/router/index.ts
@@ -1,4 +1,4 @@
-import { createRouter, createWebHistory } from "vue-router";
+import { RouteLocation, createRouter, createWebHistory } from "vue-router";
import Login from "@/views/Login.vue";
import Layout from "@/views/Layout.vue";
import Files from "@/views/Files.vue";
@@ -144,7 +144,7 @@ const routes = [
},
{
path: "/:catchAll(.*)*",
- redirect: (to) => `/files/${[...to.params.catchAll].join("/")}`,
+ redirect: (to: RouteLocation) => `/files/${[...to.params.catchAll].join("/")}`,
},
];
@@ -156,7 +156,7 @@ async function initAuth() {
}
if (recaptcha) {
- await new Promise((resolve) => {
+ await new Promise((resolve) => {
const check = () => {
if (typeof window.grecaptcha === "undefined") {
setTimeout(check, 100);
@@ -175,10 +175,11 @@ const router = createRouter({
routes,
});
-router.beforeResolve(async (to, from, next) => {
+router.beforeResolve(async (to: RouteLocation, from, next) => {
let title;
try {
// this should not fail after we finished the migration
+ // @ts-ignore
title = i18n.global.t(titles[to.name]);
} catch (error) {
console.error(error);
@@ -187,14 +188,14 @@ router.beforeResolve(async (to, from, next) => {
document.title = title + " - " + name;
/*** RTL related settings per route ****/
- const rtlSet = document.querySelector("body").classList.contains("rtl");
+ const rtlSet = document.querySelector("body")?.classList.contains("rtl");
const shouldSetRtl = rtlLanguages.includes(i18n.global.locale);
switch (true) {
case shouldSetRtl && !rtlSet:
- document.querySelector("body").classList.add("rtl");
+ document.querySelector("body")?.classList.add("rtl");
break;
case !shouldSetRtl && rtlSet:
- document.querySelector("body").classList.remove("rtl");
+ document.querySelector("body")?.classList.remove("rtl");
break;
}
@@ -225,7 +226,7 @@ router.beforeResolve(async (to, from, next) => {
}
if (to.matched.some((record) => record.meta.requiresAdmin)) {
- if (!authStore.user.perm.admin) {
+ if (authStore.user === null || !authStore.user.perm.admin) {
next({ path: "/403" });
return;
}
diff --git a/frontend/src/stores/auth.ts b/frontend/src/stores/auth.ts
index 7b57ef27..985010b3 100644
--- a/frontend/src/stores/auth.ts
+++ b/frontend/src/stores/auth.ts
@@ -5,7 +5,10 @@ import { cloneDeep } from "lodash-es";
export const useAuthStore = defineStore("auth", {
// convert to a function
- state: () => ({
+ state: (): {
+ user: user | null,
+ jwt: string
+ } => ({
user: null,
jwt: "",
}),
@@ -15,7 +18,7 @@ export const useAuthStore = defineStore("auth", {
},
actions: {
// no context as first argument, use `this` instead
- setUser(value) {
+ setUser(value: user) {
if (value === null) {
this.user = null;
return;
@@ -23,19 +26,23 @@ export const useAuthStore = defineStore("auth", {
const locale = value.locale || detectLocale();
dayjs.locale(locale);
+ // @ts-ignore Don't know how to fix this yet
i18n.global.locale.value = locale;
this.user = value;
},
- updateUser(value) {
+ updateUser(value: user) {
if (typeof value !== "object") return;
- for (let field in value) {
+ let field: userKey
+ for (field in value) {
if (field === "locale") {
const locale = value[field];
dayjs.locale(locale);
+ // @ts-ignore Don't know how to fix this yet
i18n.global.locale.value = locale;
}
+ // @ts-ignore to fix
this.user[field] = cloneDeep(value[field]);
}
},
diff --git a/frontend/src/stores/file.ts b/frontend/src/stores/file.ts
index 893f6948..045f6e2c 100644
--- a/frontend/src/stores/file.ts
+++ b/frontend/src/stores/file.ts
@@ -2,7 +2,14 @@ import { defineStore } from "pinia";
export const useFileStore = defineStore("file", {
// convert to a function
- state: () => ({
+ state: (): {
+ req: req,
+ oldReq: req,
+ reload: boolean,
+ selected: any[],
+ multiple: boolean,
+ isFiles: boolean
+ } => ({
req: {},
oldReq: {},
reload: false,
@@ -29,11 +36,11 @@ export const useFileStore = defineStore("file", {
toggleMultiple() {
this.multiple = !this.multiple;
},
- updateRequest(value) {
+ updateRequest(value: req) {
this.oldReq = this.req;
this.req = value;
},
- removeSelected(value) {
+ removeSelected(value: any) {
let i = this.selected.indexOf(value);
if (i === -1) return;
this.selected.splice(i, 1);
diff --git a/frontend/src/stores/index.ts b/frontend/src/stores/index.ts
index 3ab005bc..7f52aa28 100644
--- a/frontend/src/stores/index.ts
+++ b/frontend/src/stores/index.ts
@@ -1,7 +1,8 @@
import { createPinia as _createPinia } from "pinia";
import { markRaw } from "vue";
+import { Router } from "vue-router";
-export default function createPinia(router) {
+export default function createPinia(router: Router) {
const pinia = _createPinia();
pinia.use(({ store }) => {
store.router = markRaw(router);
diff --git a/frontend/src/stores/layout.ts b/frontend/src/stores/layout.ts
index 267a27e9..1ca27243 100644
--- a/frontend/src/stores/layout.ts
+++ b/frontend/src/stores/layout.ts
@@ -4,7 +4,13 @@ import { defineStore } from "pinia";
export const useLayoutStore = defineStore("layout", {
// convert to a function
- state: () => ({
+ state: (): {
+ loading: boolean,
+ show: string | null | boolean,
+ showConfirm: boolean | null,
+ showAction: boolean | null,
+ showShell: boolean | null
+ } => ({
loading: false,
show: null,
showConfirm: null,
@@ -19,7 +25,7 @@ export const useLayoutStore = defineStore("layout", {
toggleShell() {
this.showShell = !this.showShell;
},
- showHover(value) {
+ showHover(value: LayoutValue) {
if (typeof value !== "object") {
this.show = value;
return;
diff --git a/frontend/src/stores/upload.ts b/frontend/src/stores/upload.ts
index 95eb4dbc..fe3d8d7c 100644
--- a/frontend/src/stores/upload.ts
+++ b/frontend/src/stores/upload.ts
@@ -6,14 +6,21 @@ import buttons from "@/utils/buttons";
const UPLOADS_LIMIT = 5;
-const beforeUnload = (event) => {
+const beforeUnload = (event: Event) => {
event.preventDefault();
- event.returnValue = "";
+ // To remove >> is deprecated
+ // event.returnValue = "";
};
export const useUploadStore = defineStore("upload", {
// convert to a function
- state: () => ({
+ state: (): {
+ id: number,
+ sizes: any[],
+ progress: any[],
+ queue: any[],
+ uploads: uploads
+ } => ({
id: 0,
sizes: [],
progress: [],
@@ -29,7 +36,8 @@ export const useUploadStore = defineStore("upload", {
const totalSize = state.sizes.reduce((a, b) => a + b, 0);
- const sum = state.progress.reduce((acc, val) => acc + val);
+ // @ts-ignore
+ const sum: number = state.progress.reduce((acc, val) => acc + val);
return Math.ceil((sum / totalSize) * 100);
},
filesInUploadCount: (state) => {
@@ -64,8 +72,9 @@ export const useUploadStore = defineStore("upload", {
},
actions: {
// no context as first argument, use `this` instead
- setProgress({ id, loaded }) {
+ setProgress(obj: { id: number, loaded: boolean }) {
// Vue.set(this.progress, id, loaded);
+ const { id, loaded } = obj
this.progress[id] = loaded;
},
reset() {
@@ -73,7 +82,7 @@ export const useUploadStore = defineStore("upload", {
this.sizes = [];
this.progress = [];
},
- addJob(item) {
+ addJob(item: item) {
this.queue.push(item);
this.sizes[this.id] = item.file.size;
this.id++;
@@ -84,11 +93,11 @@ export const useUploadStore = defineStore("upload", {
// Vue.set(this.uploads, item.id, item);
this.uploads[item.id] = item;
},
- removeJob(id) {
+ removeJob(id: number) {
// Vue.delete(this.uploads, id);
delete this.uploads[id];
},
- upload(item) {
+ upload(item: item) {
let uploadsCount = Object.keys(this.uploads).length;
let isQueueEmpty = this.queue.length == 0;
@@ -102,8 +111,8 @@ export const useUploadStore = defineStore("upload", {
this.addJob(item);
this.processUploads();
},
- finishUpload(item) {
- this.setProgress({ id: item.id, loaded: item.file.size });
+ finishUpload(item: item) {
+ this.setProgress({ id: item.id, loaded: (item.file.size > 0) });
this.removeJob(item.id);
this.processUploads();
},
diff --git a/frontend/src/types/global.d.ts b/frontend/src/types/global.d.ts
index 8766bd75..8f8d3240 100644
--- a/frontend/src/types/global.d.ts
+++ b/frontend/src/types/global.d.ts
@@ -3,5 +3,6 @@ export {};
declare global {
interface Window {
FileBrowser: any;
+ grecaptcha: any
}
}
\ No newline at end of file
diff --git a/frontend/src/utils/auth.ts b/frontend/src/utils/auth.ts
index 8e5fbcba..3715bfd1 100644
--- a/frontend/src/utils/auth.ts
+++ b/frontend/src/utils/auth.ts
@@ -3,9 +3,9 @@ import router from "@/router";
import jwt_decode from "jwt-decode";
import { baseURL } from "./constants";
-export function parseToken(token) {
+export function parseToken(token: string) {
// falsy or malformed jwt will throw InvalidTokenError
- const data = jwt_decode(token);
+ const data = jwt_decode<{[key:string]: any, user: user}>(token);
document.cookie = `auth=${token}; Path=/; SameSite=Strict;`;
@@ -19,7 +19,7 @@ export function parseToken(token) {
export async function validateLogin() {
try {
if (localStorage.getItem("jwt")) {
- await renew(localStorage.getItem("jwt"));
+ await renew(localStorage.getItem("jwt"));
}
} catch (error) {
console.warn("Invalid JWT token in storage"); // eslint-disable-line
@@ -27,7 +27,7 @@ export async function validateLogin() {
}
}
-export async function login(username, password, recaptcha) {
+export async function login(username: string, password: string, recaptcha: string) {
const data = { username, password, recaptcha };
const res = await fetch(`${baseURL}/api/login`, {
@@ -47,7 +47,7 @@ export async function login(username, password, recaptcha) {
}
}
-export async function renew(jwt) {
+export async function renew(jwt: string) {
const res = await fetch(`${baseURL}/api/renew`, {
method: "POST",
headers: {
@@ -64,7 +64,7 @@ export async function renew(jwt) {
}
}
-export async function signup(username, password) {
+export async function signup(username: string, password: string) {
const data = { username, password };
const res = await fetch(`${baseURL}/api/signup`, {
@@ -76,6 +76,7 @@ export async function signup(username, password) {
});
if (res.status !== 200) {
+ // @ts-ignore still need to fix these errors
throw new Error(res.status);
}
}
@@ -86,6 +87,6 @@ export function logout() {
const authStore = useAuthStore();
authStore.clearUser();
- localStorage.setItem("jwt", null);
+ localStorage.setItem("jwt", '');
router.push({ path: "/login" });
}
diff --git a/frontend/src/utils/buttons.ts b/frontend/src/utils/buttons.ts
index 1c6bdeee..e115c8c1 100644
--- a/frontend/src/utils/buttons.ts
+++ b/frontend/src/utils/buttons.ts
@@ -1,5 +1,5 @@
-function loading(button) {
- let el = document.querySelector(`#${button}-button > i`);
+function loading(button: string) {
+ let el: HTMLButtonElement | null = document.querySelector(`#${button}-button > i`);
if (el === undefined || el === null) {
console.log("Error getting button " + button); // eslint-disable-line
@@ -11,53 +11,62 @@ function loading(button) {
}
el.dataset.icon = el.innerHTML;
- el.style.opacity = 0;
+ el.style.opacity = "0";
setTimeout(() => {
- el.classList.add("spin");
- el.innerHTML = "autorenew";
- el.style.opacity = 1;
+ if(el) {
+ el.classList.add("spin");
+ el.innerHTML = "autorenew";
+ el.style.opacity = "1";
+ }
}, 100);
}
-function done(button) {
- let el = document.querySelector(`#${button}-button > i`);
+function done(button: string) {
+ let el: HTMLButtonElement | null = document.querySelector(`#${button}-button > i`);
if (el === undefined || el === null) {
console.log("Error getting button " + button); // eslint-disable-line
return;
}
- el.style.opacity = 0;
+ el.style.opacity = "0";
setTimeout(() => {
- el.classList.remove("spin");
- el.innerHTML = el.dataset.icon;
- el.style.opacity = 1;
+ if(el !== null) {
+ el.classList.remove("spin");
+ el.innerHTML = el?.dataset?.icon || "";
+ el.style.opacity = "1";
+ }
+
+
}, 100);
}
-function success(button) {
- let el = document.querySelector(`#${button}-button > i`);
+function success(button: string) {
+ let el: HTMLButtonElement | null = document.querySelector(`#${button}-button > i`);
if (el === undefined || el === null) {
console.log("Error getting button " + button); // eslint-disable-line
return;
}
- el.style.opacity = 0;
+ el.style.opacity = "0";
setTimeout(() => {
- el.classList.remove("spin");
- el.innerHTML = "done";
- el.style.opacity = 1;
-
+ if(el !== null) {
+ el.classList.remove("spin");
+ el.innerHTML = "done";
+ el.style.opacity = "1";
+ }
setTimeout(() => {
- el.style.opacity = 0;
+ if(el) el.style.opacity = "0";
setTimeout(() => {
- el.innerHTML = el.dataset.icon;
- el.style.opacity = 1;
+ if(el !== null) {
+ el.innerHTML = el?.dataset?.icon || "";
+ el.style.opacity = "1";
+ }
}, 100);
}, 500);
}, 100);
diff --git a/frontend/src/utils/cookie.ts b/frontend/src/utils/cookie.ts
index 72d59be4..7feb4265 100644
--- a/frontend/src/utils/cookie.ts
+++ b/frontend/src/utils/cookie.ts
@@ -1,4 +1,4 @@
-export default function (name) {
+export default function (name: string) {
let re = new RegExp(
"(?:(?:^|.*;\\s*)" + name + "\\s*\\=\\s*([^;]*).*$)|^.*$"
);
diff --git a/frontend/src/utils/css.ts b/frontend/src/utils/css.ts
index 405c0dd2..7877f57d 100644
--- a/frontend/src/utils/css.ts
+++ b/frontend/src/utils/css.ts
@@ -1,4 +1,4 @@
-export default function getRule(rules) {
+export default function getRule(rules: any) {
for (let i = 0; i < rules.length; i++) {
rules[i] = rules[i].toLowerCase();
}
diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts
index 0b6cd571..5646c03d 100644
--- a/frontend/src/utils/index.ts
+++ b/frontend/src/utils/index.ts
@@ -1 +1 @@
-export * from "./funcs";
+// export * from "./funcs";
diff --git a/frontend/src/utils/upload.ts b/frontend/src/utils/upload.ts
index 88a08b7b..a4d216db 100644
--- a/frontend/src/utils/upload.ts
+++ b/frontend/src/utils/upload.ts
@@ -1,7 +1,7 @@
import { useUploadStore } from "@/stores/upload";
import url from "@/utils/url";
-export function checkConflict(files, items) {
+export function checkConflict(files: file[], items: item[]) {
if (typeof items === "undefined" || items === null) {
items = [];
}
@@ -21,6 +21,7 @@ export function checkConflict(files, items) {
}
let res = items.findIndex(function hasConflict(element) {
+ // @ts-ignore Don't know what this does
return element.name === this;
}, name);
@@ -33,10 +34,10 @@ export function checkConflict(files, items) {
return conflict;
}
-export function scanFiles(dt) {
+export function scanFiles(dt: {[key: string]: any, item: item}) {
return new Promise((resolve) => {
let reading = 0;
- const contents = [];
+ const contents: any[] = [];
if (dt.items !== undefined) {
for (let item of dt.items) {
@@ -52,10 +53,10 @@ export function scanFiles(dt) {
resolve(dt.files);
}
- function readEntry(entry, directory = "") {
+ function readEntry(entry: any, directory = "") {
if (entry.isFile) {
reading++;
- entry.file((file) => {
+ entry.file((file: file) => {
reading--;
file.fullPath = `${directory}${file.name}`;
@@ -79,10 +80,10 @@ export function scanFiles(dt) {
}
}
- function readReaderContent(reader, directory) {
+ function readReaderContent(reader: any, directory: string) {
reading++;
- reader.readEntries(function (entries) {
+ reader.readEntries(function (entries: any[]) {
reading--;
if (entries.length > 0) {
for (const entry of entries) {
@@ -100,7 +101,7 @@ export function scanFiles(dt) {
});
}
-function detectType(mimetype) {
+function detectType(mimetype: string): uploadType {
if (mimetype.startsWith("video")) return "video";
if (mimetype.startsWith("audio")) return "audio";
if (mimetype.startsWith("image")) return "image";
@@ -109,7 +110,7 @@ function detectType(mimetype) {
return "blob";
}
-export function handleFiles(files, base, overwrite = false) {
+export function handleFiles(files: file[], base: string, overwrite = false) {
const uploadStore = useUploadStore();
for (let i = 0; i < files.length; i++) {
diff --git a/frontend/src/utils/url.ts b/frontend/src/utils/url.ts
index bf30a17c..bcd8609f 100644
--- a/frontend/src/utils/url.ts
+++ b/frontend/src/utils/url.ts
@@ -1,4 +1,4 @@
-export function removeLastDir(url) {
+export function removeLastDir(url: string) {
var arr = url.split("/");
if (arr.pop() === "") {
arr.pop();
@@ -9,7 +9,7 @@ export function removeLastDir(url) {
// this function is taken from mozilla
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#Examples
-export function encodeRFC5987ValueChars(str) {
+export function encodeRFC5987ValueChars(str: string) {
return (
encodeURIComponent(str)
// The following creates the sequences %27 %28 %29 %2A (Note that
@@ -28,7 +28,7 @@ export function encodeRFC5987ValueChars(str) {
);
}
-export function encodePath(str) {
+export function encodePath(str: string) {
return str
.split("/")
.map((v) => encodeURIComponent(v))