Run lint
This commit is contained in:
parent
bf0a2ac9f0
commit
01b1373130
@ -9,8 +9,8 @@
|
|||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"watch": "vite build --watch",
|
"watch": "vite build --watch",
|
||||||
"clean": "find ./dist -maxdepth 1 -mindepth 1 ! -name '.gitkeep' -exec rm -r {} +",
|
"clean": "find ./dist -maxdepth 1 -mindepth 1 ! -name '.gitkeep' -exec rm -r {} +",
|
||||||
"lint": "eslint --ext .vue,.js src/",
|
"lint": "eslint --ext .vue,.ts src/",
|
||||||
"lint:fix": "eslint --ext .vue,.js --fix src/",
|
"lint:fix": "eslint --ext .vue,.ts --fix src/",
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@ -5,19 +5,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted } from 'vue';
|
import { onMounted } from "vue";
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const loading = document.getElementById("loading");
|
const loading = document.getElementById("loading");
|
||||||
if(loading !== null) {
|
if (loading !== null) {
|
||||||
loading.classList.add("done");
|
loading.classList.add("done");
|
||||||
|
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
if(loading.parentNode !== null) {
|
if (loading.parentNode !== null) {
|
||||||
loading.parentNode.removeChild(loading);
|
loading.parentNode.removeChild(loading);
|
||||||
}
|
}
|
||||||
}, 200);
|
}, 200);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@ -5,13 +5,18 @@ import { useAuthStore } from "@/stores/auth";
|
|||||||
const ssl = window.location.protocol === "https:";
|
const ssl = window.location.protocol === "https:";
|
||||||
const protocol = ssl ? "wss:" : "ws:";
|
const protocol = ssl ? "wss:" : "ws:";
|
||||||
|
|
||||||
export default function command(url: string, command: string, onmessage: WebSocket["onmessage"], onclose: WebSocket["onclose"]) {
|
export default function command(
|
||||||
|
url: string,
|
||||||
|
command: string,
|
||||||
|
onmessage: WebSocket["onmessage"],
|
||||||
|
onclose: WebSocket["onclose"]
|
||||||
|
) {
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
url = removePrefix(url);
|
url = removePrefix(url);
|
||||||
url = `${protocol}//${window.location.host}${baseURL}/api/command${url}?auth=${authStore.jwt}`;
|
url = `${protocol}//${window.location.host}${baseURL}/api/command${url}?auth=${authStore.jwt}`;
|
||||||
|
|
||||||
let conn = new window.WebSocket(url);
|
const conn = new window.WebSocket(url);
|
||||||
conn.onopen = () => conn.send(command);
|
conn.onopen = () => conn.send(command);
|
||||||
conn.onmessage = onmessage;
|
conn.onmessage = onmessage;
|
||||||
conn.onclose = onclose;
|
conn.onclose = onclose;
|
||||||
|
|||||||
@ -8,7 +8,7 @@ export async function fetch(url: apiUrl) {
|
|||||||
|
|
||||||
const res = await fetchURL(`/api/resources${url}`, {});
|
const res = await fetchURL(`/api/resources${url}`, {});
|
||||||
|
|
||||||
let data = await res.json();
|
const data = await res.json();
|
||||||
data.url = `/files${url}`;
|
data.url = `/files${url}`;
|
||||||
|
|
||||||
if (data.isDir) {
|
if (data.isDir) {
|
||||||
@ -33,8 +33,8 @@ async function resourceAction(url: apiUrl, method: apiMethod, content?: any) {
|
|||||||
debugger;
|
debugger;
|
||||||
url = removePrefix(url);
|
url = removePrefix(url);
|
||||||
|
|
||||||
let opts: apiOpts = {
|
const opts: apiOpts = {
|
||||||
method
|
method,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (content) {
|
if (content) {
|
||||||
@ -62,7 +62,7 @@ export function download(format: any, ...files: string[]) {
|
|||||||
} else {
|
} else {
|
||||||
let arg = "";
|
let arg = "";
|
||||||
|
|
||||||
for (let file of files) {
|
for (const file of files) {
|
||||||
arg += removePrefix(file) + ",";
|
arg += removePrefix(file) + ",";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +83,12 @@ export function download(format: any, ...files: string[]) {
|
|||||||
window.open(url);
|
window.open(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function post(url: apiUrl, content: apiContent = "", overwrite = false, onupload: Function = () => {}) {
|
export async function post(
|
||||||
|
url: apiUrl,
|
||||||
|
content: apiContent = "",
|
||||||
|
overwrite = false,
|
||||||
|
onupload: Function = () => {}
|
||||||
|
) {
|
||||||
// Use the pre-existing API if:
|
// Use the pre-existing API if:
|
||||||
const useResourcesApi =
|
const useResourcesApi =
|
||||||
// a folder is being created
|
// a folder is being created
|
||||||
@ -98,7 +103,12 @@ export async function post(url: apiUrl, content: apiContent = "", overwrite = fa
|
|||||||
: postTus(url, content, overwrite, onupload);
|
: postTus(url, content, overwrite, onupload);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function postResources(url: apiUrl, content: apiContent = "", overwrite = false, onupload: any) {
|
async function postResources(
|
||||||
|
url: apiUrl,
|
||||||
|
content: apiContent = "",
|
||||||
|
overwrite = false,
|
||||||
|
onupload: any
|
||||||
|
) {
|
||||||
url = removePrefix(url);
|
url = removePrefix(url);
|
||||||
|
|
||||||
let bufferContent: ArrayBuffer;
|
let bufferContent: ArrayBuffer;
|
||||||
@ -111,7 +121,7 @@ async function postResources(url: apiUrl, content: apiContent = "", overwrite =
|
|||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let request = new XMLHttpRequest();
|
const request = new XMLHttpRequest();
|
||||||
request.open(
|
request.open(
|
||||||
"POST",
|
"POST",
|
||||||
`${baseURL}/api/resources${url}?override=${overwrite}`,
|
`${baseURL}/api/resources${url}?override=${overwrite}`,
|
||||||
@ -141,10 +151,15 @@ async function postResources(url: apiUrl, content: apiContent = "", overwrite =
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveCopy(items: item[], copy = false, overwrite = false, rename = false) {
|
function moveCopy(
|
||||||
let promises = [];
|
items: item[],
|
||||||
|
copy = false,
|
||||||
|
overwrite = false,
|
||||||
|
rename = false
|
||||||
|
) {
|
||||||
|
const promises = [];
|
||||||
|
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
const from = item.from;
|
const from = item.from;
|
||||||
const to = encodeURIComponent(removePrefix(item.to ?? ""));
|
const to = encodeURIComponent(removePrefix(item.to ?? ""));
|
||||||
const url = `${from}?action=${
|
const url = `${from}?action=${
|
||||||
|
|||||||
@ -12,7 +12,7 @@ export async function fetch(url: ApiUrl, password: string = "") {
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
let data = await res.json();
|
const data = await res.json();
|
||||||
data.url = `/share${url}`;
|
data.url = `/share${url}`;
|
||||||
|
|
||||||
if (data.isDir) {
|
if (data.isDir) {
|
||||||
@ -33,7 +33,12 @@ export async function fetch(url: ApiUrl, password: string = "") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Is this redundant code?
|
// Is this redundant code?
|
||||||
export function download(format: any, hash: string, token: string, ...files: any) {
|
export function download(
|
||||||
|
format: any,
|
||||||
|
hash: string,
|
||||||
|
token: string,
|
||||||
|
...files: any
|
||||||
|
) {
|
||||||
let url = `${baseURL}/api/public/dl/${hash}`;
|
let url = `${baseURL}/api/public/dl/${hash}`;
|
||||||
|
|
||||||
if (files.length === 1) {
|
if (files.length === 1) {
|
||||||
@ -41,7 +46,7 @@ export function download(format: any, hash: string, token: string, ...files: any
|
|||||||
} else {
|
} else {
|
||||||
let arg = "";
|
let arg = "";
|
||||||
|
|
||||||
for (let file of files) {
|
for (const file of files) {
|
||||||
arg += encodeURIComponent(file) + ",";
|
arg += encodeURIComponent(file) + ",";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ export default async function search(base: apiUrl, query: string) {
|
|||||||
base += "/";
|
base += "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = await fetchURL(`/api/search${base}?query=${query}`, {});
|
const res = await fetchURL(`/api/search${base}?query=${query}`, {});
|
||||||
|
|
||||||
let data = await res.json();
|
let data = await res.json();
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,12 @@ export async function remove(hash: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create(url: apiUrl, password = "", expires = "", unit = "hours") {
|
export async function create(
|
||||||
|
url: apiUrl,
|
||||||
|
password = "",
|
||||||
|
expires = "",
|
||||||
|
unit = "hours"
|
||||||
|
) {
|
||||||
url = removePrefix(url);
|
url = removePrefix(url);
|
||||||
url = `/api/share${url}`;
|
url = `/api/share${url}`;
|
||||||
if (expires !== "") {
|
if (expires !== "") {
|
||||||
|
|||||||
@ -19,18 +19,18 @@ export async function upload(
|
|||||||
}
|
}
|
||||||
|
|
||||||
filePath = removePrefix(filePath);
|
filePath = removePrefix(filePath);
|
||||||
let resourcePath = `${tusEndpoint}${filePath}?override=${overwrite}`;
|
const resourcePath = `${tusEndpoint}${filePath}?override=${overwrite}`;
|
||||||
|
|
||||||
await createUpload(resourcePath);
|
await createUpload(resourcePath);
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
// Exit early because of typescript, tus content can't be a string
|
// Exit early because of typescript, tus content can't be a string
|
||||||
if(content === "") {
|
if (content === "") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return new Promise<void | string>((resolve, reject) => {
|
return new Promise<void | string>((resolve, reject) => {
|
||||||
let upload = new tus.Upload(content, {
|
const upload = new tus.Upload(content, {
|
||||||
uploadUrl: `${baseURL}${resourcePath}`,
|
uploadUrl: `${baseURL}${resourcePath}`,
|
||||||
chunkSize: tusSettings.chunkSize,
|
chunkSize: tusSettings.chunkSize,
|
||||||
retryDelays: computeRetryDelays(tusSettings),
|
retryDelays: computeRetryDelays(tusSettings),
|
||||||
@ -58,7 +58,7 @@ export async function upload(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function createUpload(resourcePath: resourcePath) {
|
async function createUpload(resourcePath: resourcePath) {
|
||||||
let headResp = await fetchURL(resourcePath, {
|
const headResp = await fetchURL(resourcePath, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
});
|
});
|
||||||
if (headResp.status !== 201) {
|
if (headResp.status !== 201) {
|
||||||
@ -68,7 +68,7 @@ async function createUpload(resourcePath: resourcePath) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function computeRetryDelays(tusSettings: tusSettings): number[] | undefined{
|
function computeRetryDelays(tusSettings: tusSettings): number[] | undefined {
|
||||||
if (!tusSettings.retryCount || tusSettings.retryCount < 1) {
|
if (!tusSettings.retryCount || tusSettings.retryCount < 1) {
|
||||||
// Disable retries altogether
|
// Disable retries altogether
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ export async function fetchURL(url: ApiUrl, opts: ApiOpts, auth = true) {
|
|||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
opts.headers = opts.headers || {};
|
opts.headers = opts.headers || {};
|
||||||
|
|
||||||
let { headers, ...rest } = opts;
|
const { headers, ...rest } = opts;
|
||||||
let res;
|
let res;
|
||||||
try {
|
try {
|
||||||
res = await fetch(`${baseURL}${url}`, {
|
res = await fetch(`${baseURL}${url}`, {
|
||||||
|
|||||||
2
frontend/src/index.d.ts
vendored
2
frontend/src/index.d.ts
vendored
@ -1 +1 @@
|
|||||||
declare module '*.vue';
|
declare module "*.vue";
|
||||||
|
|||||||
@ -144,7 +144,8 @@ const routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/:catchAll(.*)*",
|
path: "/:catchAll(.*)*",
|
||||||
redirect: (to: RouteLocation) => `/files/${[...to.params.catchAll].join("/")}`,
|
redirect: (to: RouteLocation) =>
|
||||||
|
`/files/${[...to.params.catchAll].join("/")}`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -7,8 +7,8 @@ import { cloneDeep } from "lodash-es";
|
|||||||
export const useAuthStore = defineStore("auth", {
|
export const useAuthStore = defineStore("auth", {
|
||||||
// convert to a function
|
// convert to a function
|
||||||
state: (): {
|
state: (): {
|
||||||
user: user | null,
|
user: user | null;
|
||||||
jwt: string
|
jwt: string;
|
||||||
} => ({
|
} => ({
|
||||||
user: null,
|
user: null,
|
||||||
jwt: "",
|
jwt: "",
|
||||||
@ -34,7 +34,7 @@ export const useAuthStore = defineStore("auth", {
|
|||||||
updateUser(value: user) {
|
updateUser(value: user) {
|
||||||
if (typeof value !== "object") return;
|
if (typeof value !== "object") return;
|
||||||
|
|
||||||
let field: userKey
|
let field: userKey;
|
||||||
for (field in value) {
|
for (field in value) {
|
||||||
if (field === "locale") {
|
if (field === "locale") {
|
||||||
const locale = value[field];
|
const locale = value[field];
|
||||||
|
|||||||
@ -3,12 +3,12 @@ import { defineStore } from "pinia";
|
|||||||
export const useFileStore = defineStore("file", {
|
export const useFileStore = defineStore("file", {
|
||||||
// convert to a function
|
// convert to a function
|
||||||
state: (): {
|
state: (): {
|
||||||
req: IFile | null,
|
req: IFile | null;
|
||||||
oldReq: IFile | null,
|
oldReq: IFile | null;
|
||||||
reload: boolean,
|
reload: boolean;
|
||||||
selected: any[],
|
selected: any[];
|
||||||
multiple: boolean,
|
multiple: boolean;
|
||||||
isFiles: boolean
|
isFiles: boolean;
|
||||||
} => ({
|
} => ({
|
||||||
req: null,
|
req: null,
|
||||||
oldReq: null,
|
oldReq: null,
|
||||||
|
|||||||
@ -5,11 +5,11 @@ import { defineStore } from "pinia";
|
|||||||
export const useLayoutStore = defineStore("layout", {
|
export const useLayoutStore = defineStore("layout", {
|
||||||
// convert to a function
|
// convert to a function
|
||||||
state: (): {
|
state: (): {
|
||||||
loading: boolean,
|
loading: boolean;
|
||||||
show: string | null | boolean,
|
show: string | null | boolean;
|
||||||
showConfirm: any,
|
showConfirm: any;
|
||||||
showAction: boolean | null,
|
showAction: boolean | null;
|
||||||
showShell: boolean | null
|
showShell: boolean | null;
|
||||||
} => ({
|
} => ({
|
||||||
loading: false,
|
loading: false,
|
||||||
show: null,
|
show: null,
|
||||||
|
|||||||
@ -16,12 +16,12 @@ const beforeUnload = (event: Event) => {
|
|||||||
export const useUploadStore = defineStore("upload", {
|
export const useUploadStore = defineStore("upload", {
|
||||||
// convert to a function
|
// convert to a function
|
||||||
state: (): {
|
state: (): {
|
||||||
id: number,
|
id: number;
|
||||||
sizes: any[],
|
sizes: any[];
|
||||||
progress: any[],
|
progress: any[];
|
||||||
queue: any[],
|
queue: any[];
|
||||||
uploads: uploads,
|
uploads: uploads;
|
||||||
error: any
|
error: any;
|
||||||
} => ({
|
} => ({
|
||||||
id: 0,
|
id: 0,
|
||||||
sizes: [],
|
sizes: [],
|
||||||
@ -75,9 +75,9 @@ export const useUploadStore = defineStore("upload", {
|
|||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
// no context as first argument, use `this` instead
|
// no context as first argument, use `this` instead
|
||||||
setProgress(obj: { id: number, loaded: boolean }) {
|
setProgress(obj: { id: number; loaded: boolean }) {
|
||||||
// Vue.set(this.progress, id, loaded);
|
// Vue.set(this.progress, id, loaded);
|
||||||
const { id, loaded } = obj
|
const { id, loaded } = obj;
|
||||||
this.progress[id] = loaded;
|
this.progress[id] = loaded;
|
||||||
},
|
},
|
||||||
setError(error) {
|
setError(error) {
|
||||||
@ -104,10 +104,10 @@ export const useUploadStore = defineStore("upload", {
|
|||||||
delete this.uploads[id];
|
delete this.uploads[id];
|
||||||
},
|
},
|
||||||
upload(item: item) {
|
upload(item: item) {
|
||||||
let uploadsCount = Object.keys(this.uploads).length;
|
const uploadsCount = Object.keys(this.uploads).length;
|
||||||
|
|
||||||
let isQueueEmpty = this.queue.length == 0;
|
const isQueueEmpty = this.queue.length == 0;
|
||||||
let isUploadsEmpty = uploadsCount == 0;
|
const isUploadsEmpty = uploadsCount == 0;
|
||||||
|
|
||||||
if (isQueueEmpty && isUploadsEmpty) {
|
if (isQueueEmpty && isUploadsEmpty) {
|
||||||
window.addEventListener("beforeunload", beforeUnload);
|
window.addEventListener("beforeunload", beforeUnload);
|
||||||
@ -118,7 +118,7 @@ export const useUploadStore = defineStore("upload", {
|
|||||||
this.processUploads();
|
this.processUploads();
|
||||||
},
|
},
|
||||||
finishUpload(item: item) {
|
finishUpload(item: item) {
|
||||||
this.setProgress({ id: item.id, loaded: (item.file.size > 0) });
|
this.setProgress({ id: item.id, loaded: item.file.size > 0 });
|
||||||
this.removeJob(item.id);
|
this.removeJob(item.id);
|
||||||
this.processUploads();
|
this.processUploads();
|
||||||
},
|
},
|
||||||
@ -147,7 +147,7 @@ export const useUploadStore = defineStore("upload", {
|
|||||||
if (item.file.isDir) {
|
if (item.file.isDir) {
|
||||||
await api.post(item.path).catch(this.setError);
|
await api.post(item.path).catch(this.setError);
|
||||||
} else {
|
} else {
|
||||||
let onUpload = throttle(
|
const onUpload = throttle(
|
||||||
(event) =>
|
(event) =>
|
||||||
this.setProgress({
|
this.setProgress({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
|
|||||||
39
frontend/src/types/api.d.ts
vendored
39
frontend/src/types/api.d.ts
vendored
@ -1,36 +1,39 @@
|
|||||||
type ApiUrl = string // Can also be set as a path eg: "path1" | "path2"
|
type ApiUrl = string; // Can also be set as a path eg: "path1" | "path2"
|
||||||
|
|
||||||
type resourcePath = string
|
type resourcePath = string;
|
||||||
|
|
||||||
type ApiMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH"
|
type ApiMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
||||||
|
|
||||||
type ApiContent = Blob | File | Pick<ReadableStreamDefaultReader<any>, "read"> | ""
|
type ApiContent =
|
||||||
|
| Blob
|
||||||
|
| File
|
||||||
|
| Pick<ReadableStreamDefaultReader<any>, "read">
|
||||||
|
| "";
|
||||||
|
|
||||||
interface ApiOpts {
|
interface ApiOpts {
|
||||||
method?: ApiMethod,
|
method?: ApiMethod;
|
||||||
headers?: object,
|
headers?: object;
|
||||||
body?: any
|
body?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface tusSettings {
|
interface tusSettings {
|
||||||
retryCount: number
|
retryCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
type algo = any
|
type algo = any;
|
||||||
|
|
||||||
type inline = any
|
type inline = any;
|
||||||
|
|
||||||
interface share {
|
interface share {
|
||||||
expire: any,
|
expire: any;
|
||||||
hash: string,
|
hash: string;
|
||||||
path: string,
|
path: string;
|
||||||
userID: number,
|
userID: number;
|
||||||
token: string
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface settings {
|
interface settings {
|
||||||
any
|
any;
|
||||||
}
|
}
|
||||||
|
|
||||||
type searchParams = any
|
type searchParams = any;
|
||||||
|
|
||||||
|
|||||||
71
frontend/src/types/file.d.ts
vendored
71
frontend/src/types/file.d.ts
vendored
@ -1,45 +1,48 @@
|
|||||||
interface IFile {
|
interface IFile {
|
||||||
index?: number
|
index?: number;
|
||||||
name: string,
|
name: string;
|
||||||
modified: string,
|
modified: string;
|
||||||
path: string,
|
path: string;
|
||||||
subtitles: any[],
|
subtitles: any[];
|
||||||
isDir: boolean,
|
isDir: boolean;
|
||||||
size: number,
|
size: number;
|
||||||
fullPath: string,
|
fullPath: string;
|
||||||
type: uploadType,
|
type: uploadType;
|
||||||
items: IFile[]
|
items: IFile[];
|
||||||
token?: string,
|
token?: string;
|
||||||
hash: string,
|
hash: string;
|
||||||
url?: string
|
url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type uploadType =
|
||||||
|
| "video"
|
||||||
type uploadType = "video" | "audio" | "image" | "pdf" | "text" | "blob" | "textImmutable"
|
| "audio"
|
||||||
|
| "image"
|
||||||
|
| "pdf"
|
||||||
|
| "text"
|
||||||
|
| "blob"
|
||||||
|
| "textImmutable";
|
||||||
|
|
||||||
type req = {
|
type req = {
|
||||||
path: string
|
path: string;
|
||||||
name: string
|
name: string;
|
||||||
size: number
|
size: number;
|
||||||
extension: string
|
extension: string;
|
||||||
modified: string
|
modified: string;
|
||||||
mode: number
|
mode: number;
|
||||||
isDir: boolean
|
isDir: boolean;
|
||||||
isSymlink: boolean
|
isSymlink: boolean;
|
||||||
type: string
|
type: string;
|
||||||
url: string
|
url: string;
|
||||||
hash: string
|
hash: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface uploads {
|
interface uploads {
|
||||||
[key: string]: upload
|
[key: string]: upload;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface upload {
|
interface upload {
|
||||||
id: number,
|
id: number;
|
||||||
file: file,
|
file: file;
|
||||||
type: string
|
type: string;
|
||||||
}
|
}
|
||||||
2
frontend/src/types/global.d.ts
vendored
2
frontend/src/types/global.d.ts
vendored
@ -3,6 +3,6 @@ export {};
|
|||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
FileBrowser: any;
|
FileBrowser: any;
|
||||||
grecaptcha: any
|
grecaptcha: any;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
6
frontend/src/types/layout.d.ts
vendored
6
frontend/src/types/layout.d.ts
vendored
@ -1,5 +1,5 @@
|
|||||||
interface LayoutValue {
|
interface LayoutValue {
|
||||||
prompt: string,
|
prompt: string;
|
||||||
confirm: Function,
|
confirm: any;
|
||||||
action?: boolean,
|
action?: boolean;
|
||||||
}
|
}
|
||||||
8
frontend/src/types/user.d.ts
vendored
8
frontend/src/types/user.d.ts
vendored
@ -1,7 +1,7 @@
|
|||||||
interface user {
|
interface user {
|
||||||
id: number,
|
id: number;
|
||||||
locale: string,
|
locale: string;
|
||||||
perm: any
|
perm: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
type userKey = keyof user
|
type userKey = keyof user;
|
||||||
|
|||||||
2
frontend/src/types/utils.d.ts
vendored
2
frontend/src/types/utils.d.ts
vendored
@ -1 +1 @@
|
|||||||
type settings = any
|
type settings = any;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { baseURL } from "./constants";
|
|||||||
|
|
||||||
export function parseToken(token: string) {
|
export function parseToken(token: string) {
|
||||||
// falsy or malformed jwt will throw InvalidTokenError
|
// falsy or malformed jwt will throw InvalidTokenError
|
||||||
const data = jwt_decode<{[key:string]: any, user: user}>(token);
|
const data = jwt_decode<{ [key: string]: any; user: user }>(token);
|
||||||
|
|
||||||
document.cookie = `auth=${token}; Path=/; SameSite=Strict;`;
|
document.cookie = `auth=${token}; Path=/; SameSite=Strict;`;
|
||||||
|
|
||||||
@ -27,7 +27,11 @@ export async function validateLogin() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function login(username: string, password: string, recaptcha: string) {
|
export async function login(
|
||||||
|
username: string,
|
||||||
|
password: string,
|
||||||
|
recaptcha: string
|
||||||
|
) {
|
||||||
const data = { username, password, recaptcha };
|
const data = { username, password, recaptcha };
|
||||||
|
|
||||||
const res = await fetch(`${baseURL}/api/login`, {
|
const res = await fetch(`${baseURL}/api/login`, {
|
||||||
@ -87,6 +91,6 @@ export function logout() {
|
|||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
authStore.clearUser();
|
authStore.clearUser();
|
||||||
|
|
||||||
localStorage.setItem("jwt", '');
|
localStorage.setItem("jwt", "");
|
||||||
router.push({ path: "/login" });
|
router.push({ path: "/login" });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
function loading(button: string) {
|
function loading(button: string) {
|
||||||
let el: HTMLButtonElement | null = document.querySelector(`#${button}-button > i`);
|
const el: HTMLButtonElement | null = document.querySelector(
|
||||||
|
`#${button}-button > i`
|
||||||
|
);
|
||||||
|
|
||||||
if (el === undefined || el === null) {
|
if (el === undefined || el === null) {
|
||||||
console.log("Error getting button " + button); // eslint-disable-line
|
console.log("Error getting button " + button); // eslint-disable-line
|
||||||
@ -14,7 +16,7 @@ function loading(button: string) {
|
|||||||
el.style.opacity = "0";
|
el.style.opacity = "0";
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if(el) {
|
if (el) {
|
||||||
el.classList.add("spin");
|
el.classList.add("spin");
|
||||||
el.innerHTML = "autorenew";
|
el.innerHTML = "autorenew";
|
||||||
el.style.opacity = "1";
|
el.style.opacity = "1";
|
||||||
@ -23,7 +25,9 @@ function loading(button: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function done(button: string) {
|
function done(button: string) {
|
||||||
let el: HTMLButtonElement | null = document.querySelector(`#${button}-button > i`);
|
const el: HTMLButtonElement | null = document.querySelector(
|
||||||
|
`#${button}-button > i`
|
||||||
|
);
|
||||||
|
|
||||||
if (el === undefined || el === null) {
|
if (el === undefined || el === null) {
|
||||||
console.log("Error getting button " + button); // eslint-disable-line
|
console.log("Error getting button " + button); // eslint-disable-line
|
||||||
@ -33,18 +37,18 @@ function done(button: string) {
|
|||||||
el.style.opacity = "0";
|
el.style.opacity = "0";
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if(el !== null) {
|
if (el !== null) {
|
||||||
el.classList.remove("spin");
|
el.classList.remove("spin");
|
||||||
el.innerHTML = el?.dataset?.icon || "";
|
el.innerHTML = el?.dataset?.icon || "";
|
||||||
el.style.opacity = "1";
|
el.style.opacity = "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
function success(button: string) {
|
function success(button: string) {
|
||||||
let el: HTMLButtonElement | null = document.querySelector(`#${button}-button > i`);
|
const el: HTMLButtonElement | null = document.querySelector(
|
||||||
|
`#${button}-button > i`
|
||||||
|
);
|
||||||
|
|
||||||
if (el === undefined || el === null) {
|
if (el === undefined || el === null) {
|
||||||
console.log("Error getting button " + button); // eslint-disable-line
|
console.log("Error getting button " + button); // eslint-disable-line
|
||||||
@ -54,16 +58,16 @@ function success(button: string) {
|
|||||||
el.style.opacity = "0";
|
el.style.opacity = "0";
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if(el !== null) {
|
if (el !== null) {
|
||||||
el.classList.remove("spin");
|
el.classList.remove("spin");
|
||||||
el.innerHTML = "done";
|
el.innerHTML = "done";
|
||||||
el.style.opacity = "1";
|
el.style.opacity = "1";
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if(el) el.style.opacity = "0";
|
if (el) el.style.opacity = "0";
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if(el !== null) {
|
if (el !== null) {
|
||||||
el.innerHTML = el?.dataset?.icon || "";
|
el.innerHTML = el?.dataset?.icon || "";
|
||||||
el.style.opacity = "1";
|
el.style.opacity = "1";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
export default function (name: string) {
|
export default function (name: string) {
|
||||||
let re = new RegExp(
|
const re = new RegExp(
|
||||||
"(?:(?:^|.*;\\s*)" + name + "\\s*\\=\\s*([^;]*).*$)|^.*$"
|
"(?:(?:^|.*;\\s*)" + name + "\\s*\\=\\s*([^;]*).*$)|^.*$"
|
||||||
);
|
);
|
||||||
return document.cookie.replace(re, "$1");
|
return document.cookie.replace(re, "$1");
|
||||||
|
|||||||
@ -4,7 +4,7 @@ export default function getRule(rules: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let result = null;
|
let result = null;
|
||||||
let find = Array.prototype.find;
|
const find = Array.prototype.find;
|
||||||
|
|
||||||
find.call(document.styleSheets, (styleSheet) => {
|
find.call(document.styleSheets, (styleSheet) => {
|
||||||
result = find.call(styleSheet.cssRules, (cssRule) => {
|
result = find.call(styleSheet.cssRules, (cssRule) => {
|
||||||
|
|||||||
@ -6,21 +6,21 @@ export function checkConflict(files: file[], items: item[]) {
|
|||||||
items = [];
|
items = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
let folder_upload = files[0].fullPath !== undefined;
|
const folder_upload = files[0].fullPath !== undefined;
|
||||||
|
|
||||||
let conflict = false;
|
let conflict = false;
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
let file = files[i];
|
const file = files[i];
|
||||||
let name = file.name;
|
let name = file.name;
|
||||||
|
|
||||||
if (folder_upload) {
|
if (folder_upload) {
|
||||||
let dirs = file.fullPath.split("/");
|
const dirs = file.fullPath.split("/");
|
||||||
if (dirs.length > 1) {
|
if (dirs.length > 1) {
|
||||||
name = dirs[0];
|
name = dirs[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = items.findIndex(function hasConflict(element) {
|
const res = items.findIndex(function hasConflict(element) {
|
||||||
// @ts-ignore Don't know what this does
|
// @ts-ignore Don't know what this does
|
||||||
return element.name === this;
|
return element.name === this;
|
||||||
}, name);
|
}, name);
|
||||||
@ -34,13 +34,13 @@ export function checkConflict(files: file[], items: item[]) {
|
|||||||
return conflict;
|
return conflict;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function scanFiles(dt: {[key: string]: any, item: item}) {
|
export function scanFiles(dt: { [key: string]: any; item: item }) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
let reading = 0;
|
let reading = 0;
|
||||||
const contents: any[] = [];
|
const contents: any[] = [];
|
||||||
|
|
||||||
if (dt.items !== undefined) {
|
if (dt.items !== undefined) {
|
||||||
for (let item of dt.items) {
|
for (const item of dt.items) {
|
||||||
if (
|
if (
|
||||||
item.kind === "file" &&
|
item.kind === "file" &&
|
||||||
typeof item.webkitGetAsEntry === "function"
|
typeof item.webkitGetAsEntry === "function"
|
||||||
@ -114,9 +114,9 @@ export function handleFiles(files: file[], base: string, overwrite = false) {
|
|||||||
const uploadStore = useUploadStore();
|
const uploadStore = useUploadStore();
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
let id = uploadStore.id;
|
const id = uploadStore.id;
|
||||||
let path = base;
|
let path = base;
|
||||||
let file = files[i];
|
const file = files[i];
|
||||||
|
|
||||||
if (file.fullPath !== undefined) {
|
if (file.fullPath !== undefined) {
|
||||||
path += url.encodePath(file.fullPath);
|
path += url.encodePath(file.fullPath);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
export function removeLastDir(url: string) {
|
export function removeLastDir(url: string) {
|
||||||
var arr = url.split("/");
|
const arr = url.split("/");
|
||||||
if (arr.pop() === "") {
|
if (arr.pop() === "") {
|
||||||
arr.pop();
|
arr.pop();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,13 +14,13 @@ import HeaderBar from "@/components/header/HeaderBar.vue";
|
|||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
const { t } = useI18n({})
|
const { t } = useI18n({});
|
||||||
|
|
||||||
const errors: {
|
const errors: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
icon: string,
|
icon: string;
|
||||||
message: string
|
message: string;
|
||||||
}
|
};
|
||||||
} = {
|
} = {
|
||||||
0: {
|
0: {
|
||||||
icon: "cloud_off",
|
icon: "cloud_off",
|
||||||
@ -40,11 +40,9 @@ const errors: {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const props = defineProps(["errorCode", "showHeader"])
|
const props = defineProps(["errorCode", "showHeader"]);
|
||||||
|
|
||||||
|
|
||||||
const info = computed(() => {
|
const info = computed(() => {
|
||||||
return errors[props.errorCode] ? errors [props.errorCode] : errors[500]
|
return errors[props.errorCode] ? errors[props.errorCode] : errors[500];
|
||||||
})
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<header-bar v-if="error || fileStore.req?.type === null" showMenu showLogo />
|
<header-bar
|
||||||
|
v-if="error || fileStore.req?.type === null"
|
||||||
|
showMenu
|
||||||
|
showLogo
|
||||||
|
/>
|
||||||
|
|
||||||
<breadcrumbs base="/files" />
|
<breadcrumbs base="/files" />
|
||||||
|
|
||||||
@ -20,7 +24,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, defineAsyncComponent, onBeforeUnmount, onMounted, onUnmounted, ref, watch } from "vue";
|
import {
|
||||||
|
computed,
|
||||||
|
defineAsyncComponent,
|
||||||
|
onBeforeUnmount,
|
||||||
|
onMounted,
|
||||||
|
onUnmounted,
|
||||||
|
ref,
|
||||||
|
watch,
|
||||||
|
} from "vue";
|
||||||
import { files as api } from "@/api";
|
import { files as api } from "@/api";
|
||||||
import { useFileStore } from "@/stores/file";
|
import { useFileStore } from "@/stores/file";
|
||||||
import { useLayoutStore } from "@/stores/layout";
|
import { useLayoutStore } from "@/stores/layout";
|
||||||
@ -32,23 +44,22 @@ import Errors from "@/views/Errors.vue";
|
|||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
|
||||||
const layoutStore = useLayoutStore()
|
const layoutStore = useLayoutStore();
|
||||||
const fileStore = useFileStore()
|
const fileStore = useFileStore();
|
||||||
const uploadStore = useUploadStore()
|
const uploadStore = useUploadStore();
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const { t } = useI18n({});
|
const { t } = useI18n({});
|
||||||
|
|
||||||
const clean = (path: string) => {
|
const clean = (path: string) => {
|
||||||
return path.endsWith("/") ? path.slice(0, -1) : path;
|
return path.endsWith("/") ? path.slice(0, -1) : path;
|
||||||
}
|
};
|
||||||
|
|
||||||
const Editor = defineAsyncComponent(() => import("@/views/files/Editor.vue"));
|
const Editor = defineAsyncComponent(() => import("@/views/files/Editor.vue"));
|
||||||
const error = ref<any | null>(null)
|
const error = ref<any | null>(null);
|
||||||
const width = computed(() => window.innerWidth)
|
const width = computed(() => window.innerWidth);
|
||||||
|
|
||||||
const currentView = computed(() => {
|
const currentView = computed(() => {
|
||||||
if (fileStore.req?.type == undefined) {
|
if (fileStore.req?.type == undefined) {
|
||||||
@ -65,18 +76,18 @@ const currentView = computed(() => {
|
|||||||
} else {
|
} else {
|
||||||
return "preview";
|
return "preview";
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
// Define hooks
|
// Define hooks
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
fileStore.isFiles = true;
|
fileStore.isFiles = true;
|
||||||
window.addEventListener("keydown", keyEvent);
|
window.addEventListener("keydown", keyEvent);
|
||||||
})
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener("keydown", keyEvent);
|
window.removeEventListener("keydown", keyEvent);
|
||||||
})
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
fileStore.isFiles = false;
|
fileStore.isFiles = false;
|
||||||
@ -84,20 +95,18 @@ onUnmounted(() => {
|
|||||||
layoutStore.toggleShell();
|
layoutStore.toggleShell();
|
||||||
}
|
}
|
||||||
fileStore.updateRequest(null);
|
fileStore.updateRequest(null);
|
||||||
})
|
});
|
||||||
|
|
||||||
watch(route, () => fetchData())
|
watch(route, () => fetchData());
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
watch(fileStore.reload, (val) => {
|
watch(fileStore.reload, (val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
fetchData();
|
fetchData();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
)
|
|
||||||
watch(uploadStore.error, (newValue, oldValue) => {
|
watch(uploadStore.error, (newValue, oldValue) => {
|
||||||
newValue && newValue !== oldValue && layoutStore.showError();
|
newValue && newValue !== oldValue && layoutStore.showError();
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
// Define functions
|
// Define functions
|
||||||
|
|
||||||
@ -118,10 +127,7 @@ const fetchData = async () => {
|
|||||||
try {
|
try {
|
||||||
const res = await api.fetch(url);
|
const res = await api.fetch(url);
|
||||||
|
|
||||||
if (
|
if (clean(res.path) !== clean(`/${[...route.params.path].join("/")}`)) {
|
||||||
clean(res.path) !==
|
|
||||||
clean(`/${[...route.params.path].join("/")}`)
|
|
||||||
) {
|
|
||||||
throw new Error("Data Mismatch!");
|
throw new Error("Data Mismatch!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,11 +138,11 @@ const fetchData = async () => {
|
|||||||
} finally {
|
} finally {
|
||||||
layoutStore.loading = false;
|
layoutStore.loading = false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
const keyEvent = (event: KeyboardEvent) => {
|
const keyEvent = (event: KeyboardEvent) => {
|
||||||
if (event.key === "F1") {
|
if (event.key === "F1") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
layoutStore.showHover("help");
|
layoutStore.showHover("help");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -6,7 +6,11 @@
|
|||||||
<sidebar></sidebar>
|
<sidebar></sidebar>
|
||||||
<main>
|
<main>
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
<shell v-if="enableExec && authStore.isLoggedIn && authStore.user?.perm.execute" />
|
<shell
|
||||||
|
v-if="
|
||||||
|
enableExec && authStore.isLoggedIn && authStore.user?.perm.execute
|
||||||
|
"
|
||||||
|
/>
|
||||||
</main>
|
</main>
|
||||||
<prompts></prompts>
|
<prompts></prompts>
|
||||||
<upload-files></upload-files>
|
<upload-files></upload-files>
|
||||||
@ -37,6 +41,5 @@ watch(route, (newval, oldval) => {
|
|||||||
if (layoutStore.show !== "success") {
|
if (layoutStore.show !== "success") {
|
||||||
layoutStore.closeHovers();
|
layoutStore.closeHovers();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -35,9 +35,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<p @click="toggleMode" v-if="signup">
|
<p @click="toggleMode" v-if="signup">
|
||||||
{{
|
{{ createMode ? t("login.loginInstead") : t("login.createAnAccount") }}
|
||||||
createMode ? t("login.loginInstead") : t("login.createAnAccount")
|
|
||||||
}}
|
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -57,18 +55,17 @@ import { useI18n } from "vue-i18n";
|
|||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
|
||||||
// Define refs
|
// Define refs
|
||||||
const createMode = ref<boolean>(false)
|
const createMode = ref<boolean>(false);
|
||||||
const error = ref<string>("")
|
const error = ref<string>("");
|
||||||
const username = ref<string>("")
|
const username = ref<string>("");
|
||||||
const password = ref<string>("")
|
const password = ref<string>("");
|
||||||
const passwordConfirm = ref<string>("")
|
const passwordConfirm = ref<string>("");
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute();
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
const { t } = useI18n({})
|
const { t } = useI18n({});
|
||||||
// Define functions
|
// Define functions
|
||||||
const toggleMode = () => createMode.value = !createMode.value;
|
const toggleMode = () => (createMode.value = !createMode.value);
|
||||||
|
|
||||||
|
|
||||||
const submit = async (event: Event) => {
|
const submit = async (event: Event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -112,7 +109,7 @@ const submit = async (event: Event) => {
|
|||||||
error.value = t("login.wrongCredentials");
|
error.value = t("login.wrongCredentials");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// Run hooks
|
// Run hooks
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@ -123,6 +120,5 @@ onMounted(() => {
|
|||||||
sitekey: recaptchaKey,
|
sitekey: recaptchaKey,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -61,8 +61,6 @@ const { t } = useI18n();
|
|||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const layoutStore = useLayoutStore();
|
const layoutStore = useLayoutStore();
|
||||||
|
|
||||||
const user = computed(() => authStore.user)
|
const user = computed(() => authStore.user);
|
||||||
const loading = computed(() => layoutStore.loading)
|
const loading = computed(() => layoutStore.loading);
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -3,13 +3,27 @@
|
|||||||
<header-bar showMenu showLogo>
|
<header-bar showMenu showLogo>
|
||||||
<title />
|
<title />
|
||||||
|
|
||||||
<action v-if="fileStore.selectedCount" icon="file_download" :label="t('buttons.download')" @action="download"
|
<action
|
||||||
:counter="fileStore.selectedCount" />
|
v-if="fileStore.selectedCount"
|
||||||
<button v-if="isSingleFile()" class="action copy-clipboard" :data-clipboard-text="linkSelected()"
|
icon="file_download"
|
||||||
:aria-label="t('buttons.copyDownloadLinkToClipboard')" :data-title="t('buttons.copyDownloadLinkToClipboard')">
|
:label="t('buttons.download')"
|
||||||
|
@action="download"
|
||||||
|
:counter="fileStore.selectedCount"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
v-if="isSingleFile()"
|
||||||
|
class="action copy-clipboard"
|
||||||
|
:data-clipboard-text="linkSelected()"
|
||||||
|
:aria-label="t('buttons.copyDownloadLinkToClipboard')"
|
||||||
|
:data-title="t('buttons.copyDownloadLinkToClipboard')"
|
||||||
|
>
|
||||||
<i class="material-icons">content_paste</i>
|
<i class="material-icons">content_paste</i>
|
||||||
</button>
|
</button>
|
||||||
<action icon="check_circle" :label="t('buttons.selectMultiple')" @action="toggleMultipleSelection" />
|
<action
|
||||||
|
icon="check_circle"
|
||||||
|
:label="t('buttons.selectMultiple')"
|
||||||
|
@action="toggleMultipleSelection"
|
||||||
|
/>
|
||||||
</header-bar>
|
</header-bar>
|
||||||
|
|
||||||
<breadcrumbs :base="'/share/' + hash" />
|
<breadcrumbs :base="'/share/' + hash" />
|
||||||
@ -35,12 +49,21 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<input v-focus type="password" :placeholder="t('login.password')" v-model="password"
|
<input
|
||||||
@keyup.enter="fetchData" />
|
v-focus
|
||||||
|
type="password"
|
||||||
|
:placeholder="t('login.password')"
|
||||||
|
v-model="password"
|
||||||
|
@keyup.enter="fetchData"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button class="button button--flat" @click="fetchData" :aria-label="t('buttons.submit')"
|
<button
|
||||||
:data-title="t('buttons.submit')">
|
class="button button--flat"
|
||||||
|
@click="fetchData"
|
||||||
|
:aria-label="t('buttons.submit')"
|
||||||
|
:data-title="t('buttons.submit')"
|
||||||
|
>
|
||||||
{{ t("buttons.submit") }}
|
{{ t("buttons.submit") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -73,12 +96,19 @@
|
|||||||
<div class="share__box__element share__box__center">
|
<div class="share__box__element share__box__center">
|
||||||
<a target="_blank" :href="link" class="button button--flat">
|
<a target="_blank" :href="link" class="button button--flat">
|
||||||
<div>
|
<div>
|
||||||
<i class="material-icons">file_download</i>{{ t("buttons.download") }}
|
<i class="material-icons">file_download</i
|
||||||
|
>{{ t("buttons.download") }}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<a target="_blank" :href="inlineLink" class="button button--flat" v-if="!req.isDir">
|
<a
|
||||||
|
target="_blank"
|
||||||
|
:href="inlineLink"
|
||||||
|
class="button button--flat"
|
||||||
|
v-if="!req.isDir"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<i class="material-icons">open_in_new</i>{{ t("buttons.openFile") }}
|
<i class="material-icons">open_in_new</i
|
||||||
|
>{{ t("buttons.openFile") }}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -86,31 +116,59 @@
|
|||||||
<qrcode-vue :value="link" :size="200" level="M"></qrcode-vue>
|
<qrcode-vue :value="link" :size="200" level="M"></qrcode-vue>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="req.isDir && req.items.length > 0" class="share__box share__box__items">
|
<div
|
||||||
|
v-if="req.isDir && req.items.length > 0"
|
||||||
|
class="share__box share__box__items"
|
||||||
|
>
|
||||||
<div class="share__box__header" v-if="req.isDir">
|
<div class="share__box__header" v-if="req.isDir">
|
||||||
{{ t("files.files") }}
|
{{ t("files.files") }}
|
||||||
</div>
|
</div>
|
||||||
<div id="listing" class="list file-icons">
|
<div id="listing" class="list file-icons">
|
||||||
<item v-for="item in req.items.slice(0, showLimit)" :key="base64(item.name)" v-bind:index="item.index"
|
<item
|
||||||
v-bind:name="item.name" v-bind:isDir="item.isDir" v-bind:url="item.url" v-bind:modified="item.modified"
|
v-for="item in req.items.slice(0, showLimit)"
|
||||||
v-bind:type="item.type" v-bind:size="item.size" readOnly>
|
:key="base64(item.name)"
|
||||||
|
v-bind:index="item.index"
|
||||||
|
v-bind:name="item.name"
|
||||||
|
v-bind:isDir="item.isDir"
|
||||||
|
v-bind:url="item.url"
|
||||||
|
v-bind:modified="item.modified"
|
||||||
|
v-bind:type="item.type"
|
||||||
|
v-bind:size="item.size"
|
||||||
|
readOnly
|
||||||
|
>
|
||||||
</item>
|
</item>
|
||||||
<div v-if="req.items.length > showLimit" class="item" @click="showLimit += 100">
|
<div
|
||||||
|
v-if="req.items.length > showLimit"
|
||||||
|
class="item"
|
||||||
|
@click="showLimit += 100"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<p class="name">+ {{ req.items.length - showLimit }}</p>
|
<p class="name">+ {{ req.items.length - showLimit }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :class="{ active: fileStore.multiple }" id="multiple-selection">
|
<div
|
||||||
|
:class="{ active: fileStore.multiple }"
|
||||||
|
id="multiple-selection"
|
||||||
|
>
|
||||||
<p>{{ t("files.multipleSelectionEnabled") }}</p>
|
<p>{{ t("files.multipleSelectionEnabled") }}</p>
|
||||||
<div @click="() => (fileStore.multiple = false)" tabindex="0" role="button" :data-title="t('files.clear')"
|
<div
|
||||||
:aria-label="t('files.clear')" class="action">
|
@click="() => (fileStore.multiple = false)"
|
||||||
|
tabindex="0"
|
||||||
|
role="button"
|
||||||
|
:data-title="t('files.clear')"
|
||||||
|
:aria-label="t('files.clear')"
|
||||||
|
class="action"
|
||||||
|
>
|
||||||
<i class="material-icons">clear</i>
|
<i class="material-icons">clear</i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="req.isDir && req.items.length === 0" class="share__box share__box__items">
|
<div
|
||||||
|
v-else-if="req.isDir && req.items.length === 0"
|
||||||
|
class="share__box share__box__items"
|
||||||
|
>
|
||||||
<h2 class="message">
|
<h2 class="message">
|
||||||
<i class="material-icons">sentiment_dissatisfied</i>
|
<i class="material-icons">sentiment_dissatisfied</i>
|
||||||
<span>{{ t("files.lonely") }}</span>
|
<span>{{ t("files.lonely") }}</span>
|
||||||
@ -140,49 +198,57 @@ import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
|||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
const error = ref<null | any>(null)
|
const error = ref<null | any>(null);
|
||||||
const showLimit = ref<number>(100)
|
const showLimit = ref<number>(100);
|
||||||
const password = ref<string>("")
|
const password = ref<string>("");
|
||||||
const attemptedPasswordLogin = ref<boolean>(false)
|
const attemptedPasswordLogin = ref<boolean>(false);
|
||||||
const hash = ref<any>(null)
|
const hash = ref<any>(null);
|
||||||
const token = ref<any>(null)
|
const token = ref<any>(null);
|
||||||
const clip = ref<any>(null)
|
const clip = ref<any>(null);
|
||||||
|
|
||||||
const { t } = useI18n({})
|
const { t } = useI18n({});
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute();
|
||||||
const fileStore = useFileStore()
|
const fileStore = useFileStore();
|
||||||
const layoutStore = useLayoutStore()
|
const layoutStore = useLayoutStore();
|
||||||
|
|
||||||
watch(route, () => {
|
watch(route, () => {
|
||||||
showLimit.value = 100
|
showLimit.value = 100;
|
||||||
fetchData();
|
fetchData();
|
||||||
})
|
});
|
||||||
|
|
||||||
const req = computed(() => fileStore.req)
|
const req = computed(() => fileStore.req);
|
||||||
|
|
||||||
// Define computes
|
// Define computes
|
||||||
|
|
||||||
const icon = computed(() => {
|
const icon = computed(() => {
|
||||||
if (req.value === null) return "insert_drive_file"
|
if (req.value === null) return "insert_drive_file";
|
||||||
if (req.value.isDir) return "folder";
|
if (req.value.isDir) return "folder";
|
||||||
if (req.value.type === "image") return "insert_photo";
|
if (req.value.type === "image") return "insert_photo";
|
||||||
if (req.value.type === "audio") return "volume_up";
|
if (req.value.type === "audio") return "volume_up";
|
||||||
if (req.value.type === "video") return "movie";
|
if (req.value.type === "video") return "movie";
|
||||||
return "insert_drive_file";
|
return "insert_drive_file";
|
||||||
})
|
});
|
||||||
|
|
||||||
const link = computed(() => (req.value ? api.getDownloadURL(req.value) : ""))
|
const link = computed(() => (req.value ? api.getDownloadURL(req.value) : ""));
|
||||||
const inlineLink = computed(() => (req.value ? api.getDownloadURL(req.value, true) : ""))
|
const inlineLink = computed(() =>
|
||||||
|
req.value ? api.getDownloadURL(req.value, true) : ""
|
||||||
|
);
|
||||||
const humanSize = computed(() => {
|
const humanSize = computed(() => {
|
||||||
if (req.value) {
|
if (req.value) {
|
||||||
return (req.value.isDir ? req.value.items.length : filesize(req.value.size ?? 0))
|
return req.value.isDir
|
||||||
|
? req.value.items.length
|
||||||
|
: filesize(req.value.size ?? 0);
|
||||||
} else {
|
} else {
|
||||||
return ""
|
return "";
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
const humanTime = computed(() => dayjs(req.value?.modified).fromNow())
|
const humanTime = computed(() => dayjs(req.value?.modified).fromNow());
|
||||||
const modTime = computed(() => (req.value ? new Date(Date.parse(req.value.modified)).toLocaleString() : new Date()))
|
const modTime = computed(() =>
|
||||||
|
req.value
|
||||||
|
? new Date(Date.parse(req.value.modified)).toLocaleString()
|
||||||
|
: new Date()
|
||||||
|
);
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
const base64 = (name: any) => Base64.encodeURI(name);
|
const base64 = (name: any) => Base64.encodeURI(name);
|
||||||
@ -207,7 +273,6 @@ const fetchData = async () => {
|
|||||||
let file = await api.fetch(url, password.value);
|
let file = await api.fetch(url, password.value);
|
||||||
file.hash = hash.value;
|
file.hash = hash.value;
|
||||||
|
|
||||||
|
|
||||||
token.value = file.token || "";
|
token.value = file.token || "";
|
||||||
|
|
||||||
fileStore.updateRequest(file);
|
fileStore.updateRequest(file);
|
||||||
@ -217,7 +282,7 @@ const fetchData = async () => {
|
|||||||
} finally {
|
} finally {
|
||||||
layoutStore.loading = false;
|
layoutStore.loading = false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const keyEvent = (event: KeyboardEvent) => {
|
const keyEvent = (event: KeyboardEvent) => {
|
||||||
if (event.key === "Escape") {
|
if (event.key === "Escape") {
|
||||||
@ -227,13 +292,15 @@ const keyEvent = (event: KeyboardEvent) => {
|
|||||||
fileStore.selected = [];
|
fileStore.selected = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const toggleMultipleSelection = () => {
|
const toggleMultipleSelection = () => {
|
||||||
// toggle
|
// toggle
|
||||||
}
|
};
|
||||||
|
|
||||||
const isSingleFile = () => fileStore.selectedCount === 1 && !req.value?.items[fileStore.selected[0]].isDir
|
const isSingleFile = () =>
|
||||||
|
fileStore.selectedCount === 1 &&
|
||||||
|
!req.value?.items[fileStore.selected[0]].isDir;
|
||||||
|
|
||||||
const download = () => {
|
const download = () => {
|
||||||
if (isSingleFile()) {
|
if (isSingleFile()) {
|
||||||
@ -248,7 +315,7 @@ const download = () => {
|
|||||||
layoutStore.showHover({
|
layoutStore.showHover({
|
||||||
prompt: "download",
|
prompt: "download",
|
||||||
confirm: (format: any) => {
|
confirm: (format: any) => {
|
||||||
if (req.value === null) return false
|
if (req.value === null) return false;
|
||||||
layoutStore.closeHovers();
|
layoutStore.closeHovers();
|
||||||
|
|
||||||
let files: string[] = [];
|
let files: string[] = [];
|
||||||
@ -261,35 +328,32 @@ const download = () => {
|
|||||||
api.download(format, hash.value, token.value, ...files);
|
api.download(format, hash.value, token.value, ...files);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const linkSelected = () => {
|
const linkSelected = () => {
|
||||||
return isSingleFile() && req.value
|
return isSingleFile() && req.value
|
||||||
// @ts-ignore
|
? // @ts-ignore
|
||||||
? api.getDownloadURL({
|
api.getDownloadURL({
|
||||||
hash: hash.value,
|
hash: hash.value,
|
||||||
path: req.value.items[fileStore.selected[0]].path,
|
path: req.value.items[fileStore.selected[0]].path,
|
||||||
})
|
})
|
||||||
: "";
|
: "";
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// Created
|
// Created
|
||||||
hash.value = route.params.path[0];
|
hash.value = route.params.path[0];
|
||||||
await fetchData();
|
await fetchData();
|
||||||
|
|
||||||
|
|
||||||
window.addEventListener("keydown", keyEvent);
|
window.addEventListener("keydown", keyEvent);
|
||||||
clip.value = new Clipboard(".copy-clipboard");
|
clip.value = new Clipboard(".copy-clipboard");
|
||||||
clip.value.on("success", () => {
|
clip.value.on("success", () => {
|
||||||
// $showSuccess(this.t("success.linkCopied"));
|
// $showSuccess(this.t("success.linkCopied"));
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener("keydown", keyEvent);
|
window.removeEventListener("keydown", keyEvent);
|
||||||
clip.value.destroy();
|
clip.value.destroy();
|
||||||
})
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
Loading…
Reference in New Issue
Block a user