fix: ลบ storage

This commit is contained in:
Net 2024-04-04 09:07:10 +07:00
parent 22d7f89623
commit 136979f2c9

View file

@ -1,558 +0,0 @@
import { computed, reactive, ref } from 'vue';
import { defineStore } from 'pinia';
import { io } from 'socket.io-client';
import axios from 'axios';
import { api } from 'src/boot/axios';
import useLoader from '../loader';
import { useUtils } from '../utils';
type Path = string;
export interface StorageFolder {
pathname: string;
name: string;
size?: number;
createdAt: string;
createdBy: string;
}
export interface StorageFile {
pathname: string;
path: string;
fileName: string;
fileSize: number;
fileType: string;
title: string;
description: string;
author: string;
category: string[];
keyword: string[];
updatedAt: string;
updatedBy: string;
createdAt: string;
createdBy: string;
}
export interface Structure extends StorageFolder {
folder: Structure[];
file: StorageFile[];
}
type Tree = Structure[];
export type SearchOptions = {
path?: string[];
recursive?: boolean;
exact?: boolean;
within?: string;
};
export type SearchInfo = {
field: string;
value: string;
exact?: boolean;
};
export type SearchOperator = {
AND?: (SearchInfo | SearchOperator)[];
OR?: (SearchInfo | SearchOperator)[];
};
export type FileMetadata = {
title?: string;
description?: string;
category?: string[];
keyword?: string[];
author?: string;
};
function normalizePath(path: string | string[]) {
return Array.isArray(path)
? path.join('/') + '/'
: path.split('/').filter(Boolean).join('/') + '/';
}
const useStorage = defineStore('storageStore', () => {
const utils = useUtils();
const loader = useLoader();
const init = ref(false);
const folder = ref<Record<Path, StorageFolder[]>>({});
const file = ref<Record<Path, StorageFile[]>>({});
const tree = computed(() => {
let structure: Tree = [];
// parse list of folder and list of file into tree
Object.entries(folder.value).forEach(([key, value]) => {
const arr = key.split('/').filter(Boolean);
// Once run then it is init
if (!init.value) init.value = true;
if (arr.length === 0) {
structure = value.map((v) => ({
pathname: v.pathname,
name: v.name,
createdAt: v.createdAt,
createdBy: v.createdBy,
folder: [],
file: [],
}));
} else {
let current: Structure | undefined;
// traverse into tree
arr.forEach((v, i) => {
current =
i === 0
? structure.find((x) => x.name === v)
: current?.folder.find((x) => x.name === v);
});
// set data in tree (object is references to the same object)
if (current) {
current.folder = value.map((v) => ({
pathname: v.pathname,
name: v.name,
createdAt: v.createdAt,
createdBy: v.createdBy,
folder: [],
file: [],
}));
current.file = file.value[key] ?? [];
}
}
});
return structure;
});
const currentFile = ref<StorageFile>();
const currentInfo = reactive<{
path: string;
dept: number;
}>({
path: '',
dept: 0,
});
if (!init.value) goto(sessionStorage.getItem('path') || '');
async function getStorage(path: string = '') {
const arr = path.split('/').filter(Boolean);
const res = await api.post<(typeof folder.value)[string]>('/storage/list', {
operation: 'folder',
path: arr,
});
if (res.status === 200 && res.data && Array.isArray(res.data))
folder.value[normalizePath(path)] = res.data.sort((a, b) =>
a.pathname.localeCompare(b.pathname),
);
}
async function getStorageFile(path: string = '') {
const arr = path.split('/').filter(Boolean);
const res = await api.post<(typeof file.value)[string]>('/storage/list', {
operation: 'file',
path: arr,
});
if (res.status === 200 && res.data && Array.isArray(res.data))
file.value[normalizePath(path)] = res.data.sort((a, b) =>
a.pathname.localeCompare(b.pathname),
);
}
async function goto(path: string = '', force = false) {
loader.show();
const arr = path.split('/').filter(Boolean);
// get all parent to the root structure
// this will also triggher init structure as it get root structure
for (let i = 0; i < arr.length; i++) {
const current = normalizePath(arr.slice(0, i - arr.length));
if (!folder.value[current] || force) await getStorage(current);
}
path = normalizePath(path);
// only get this path once, after that will get from socket.io-client instead
if (!folder.value[path] || force) await getStorage(path);
if (!file.value[path] || force) await getStorageFile(path);
currentFile.value = undefined;
currentInfo.path = path;
currentInfo.dept = path.split('/').filter(Boolean).length;
sessionStorage.setItem('path', path);
loader.hide();
}
async function gotoParent() {
const arr = currentInfo.path.split('/').filter(Boolean);
await goto(normalizePath(arr.slice(0, -1)));
}
// socket.io zone
const socket = io(new URL(import.meta.env.VITE_API_BASE_URL).origin);
socket.on('connect', () => console.info('Socket.io connected.'));
socket.on('disconnect', () => console.info('Socket.io disconnected.'));
socket.on(
'FolderCreate',
(data: {
pathname: string;
name: string;
createdAt: string;
createdBy: string;
}) => {
const arr = data.pathname.split('/').filter(Boolean);
const path = normalizePath(arr.slice(0, -1));
if (folder.value[path]) {
const idx = folder.value[path].findIndex(
(v) => v.pathname === data.pathname,
);
if (idx === -1) {
folder.value[path].push({
pathname: data.pathname,
name: data.name,
createdAt: data.createdAt,
createdBy: data.createdBy,
});
}
folder.value[path].sort((a, b) => a.pathname.localeCompare(b.pathname));
}
},
);
// NOTE:
// API planned to make new endpoint that can move and rename in one go.
// Need to change if api handle move and rename file instead of just edit.
socket.on('FolderMove', (data: { from: string; to: string }) => {
const src = data.from.split('/').filter(Boolean);
const dst = data.to.split('/').filter(Boolean);
const path = normalizePath(src.slice(0, -1));
if (folder.value[path]) {
const val = folder.value[path].find((v) => v.pathname === data.from);
if (val) {
val.pathname = data.to;
val.name = dst[dst.length - 1];
}
}
const regex = new RegExp(`^${data.from}`);
for (const key in folder.value) {
if (key.startsWith(data.from)) {
folder.value[key.replace(regex, data.to)] = folder.value[key].map(
(v) => {
v.pathname = v.pathname.replace(regex, data.to);
return v;
},
);
delete folder.value[key];
}
}
for (const key in file.value) {
if (key.startsWith(data.from)) {
file.value[key.replace(regex, data.to)] = file.value[key].map((v) => {
v.pathname = v.pathname.replace(regex, data.to);
return v;
});
delete file.value[key];
}
}
});
socket.on('FolderDelete', (data: { pathname: string }) => {
for (const key in folder.value) {
if (key.startsWith(data.pathname)) {
delete folder.value[key];
}
}
for (const key in file.value) {
if (key.startsWith(data.pathname)) {
delete file.value[key];
}
}
const arr = data.pathname.split('/').filter(Boolean);
const path = normalizePath(arr.slice(0, -1));
if (folder.value[path]) {
folder.value[path] = folder.value[path].filter(
(v) => v.pathname !== data.pathname,
);
}
if (
currentInfo.path.length >= normalizePath(arr).length &&
currentInfo.path.startsWith(normalizePath(arr))
) {
utils.openDialog({
title: 'แจ้งเตือน',
message: 'ข้อมูลที่คุณกำลังเข้าถึงอยู่ถูกลบ',
});
goto();
}
});
socket.on('FileUpload', (data: StorageFile) => {
const arr = data.pathname.split('/').filter(Boolean);
const path = normalizePath(arr.slice(0, -1));
if (file.value[path]) {
const idx = file.value[path].findIndex(
(v) => v.pathname === data.pathname,
);
if (idx !== -1) file.value[path][idx] = data;
else file.value[path].push(data);
file.value[path].sort((a, b) => a.pathname.localeCompare(b.pathname));
}
});
socket.on('FileDelete', (data: { pathname: string }) => {
const arr = data.pathname.split('/').filter(Boolean);
const path = normalizePath(arr.slice(0, -1));
if (file.value[path]) {
file.value[path] = file.value[path].filter(
(v) => v.pathname !== data.pathname,
);
}
});
socket.on('FileMove', (data: { from: StorageFile; to: StorageFile }) => {
const arr = data.from.pathname.split('/').filter(Boolean);
const path = normalizePath(arr.slice(0, -1));
if (file.value[path]) {
const idx = file.value[path].findIndex(
(v) => v.pathname === data.from.pathname,
);
if (idx !== -1) file.value[path][idx] = data.to;
}
});
socket.on('FileUpdate', (data: StorageFile) => {
const arr = data.pathname.split('/').filter(Boolean);
const path = normalizePath(arr.slice(0, -1));
if (file.value[path]) {
const idx = file.value[path].findIndex(
(v) => v.pathname === data.pathname,
);
if (idx !== -1) file.value[path][idx] = data;
}
});
socket.on('FileUploadRequest', (data: StorageFile) => {
const arr = data.pathname.split('/').filter(Boolean);
const path = normalizePath(arr.slice(0, -1));
if (file.value[path]) {
const idx = file.value[path].findIndex(
(v) => v.pathname === data.pathname,
);
if (idx !== -1) file.value[path][idx] = data;
else file.value[path].push(data);
file.value[path].sort((a, b) => a.pathname.localeCompare(b.pathname));
}
});
async function createFolder(name: string, path: string = currentInfo.path) {
loader.show();
if (
folder.value[normalizePath(path)]?.findIndex((v) => v.name === name) !==
-1
) {
utils.openDialog({
title: 'แจ้งเตือน',
message: `พบชื่อ \"${name}\" ซ้ำในระบบ`,
});
} else {
const arrayPath: string[] = path.split('/').filter(Boolean);
await api.post('/storage/folder', {
path: arrayPath,
name: name.replace(/^\./, ''),
});
}
loader.hide();
}
async function editFolder(name: string, path: string) {
loader.show();
const arrayPath: string[] = path.split('/').filter(Boolean);
const beforeName = arrayPath.pop();
await api.put('/storage/folder', {
from: {
name: beforeName,
path: arrayPath,
},
to: {
name: name,
path: arrayPath,
},
});
loader.hide();
}
async function deleteFolder(path: string) {
loader.show();
await api.delete<(typeof file.value)[string]>('/storage/folder', {
data: { path: path.split('/').filter(Boolean) },
});
loader.hide();
}
async function createFile(
file: File,
data: FileMetadata,
path: string = currentInfo.path,
) {
const arr = path.split('/').filter(Boolean);
loader.show();
const res = await api.post('/storage/file', {
path: arr,
file: file.name,
...data,
});
if (res && res.status === 200 && res.data && res.data.uploadUrl) {
await axios
.put(res.data.uploadUrl, file, {
headers: { 'Content-Type': file.type },
onUploadProgress: (e) => console.log(e),
})
.catch((e) => console.error(e));
}
loader.hide();
}
async function updateFile(pathname: string, data: FileMetadata, file?: File) {
const arr = pathname.split('/');
if (arr.length < 1) return;
loader.show();
const srcFile = arr.pop();
const res = await api.put('/storage/file', {
...data,
from: {
file: srcFile,
path: arr,
},
to: file?.name
? {
file: file.name,
path: arr,
}
: undefined,
upload: !!file,
});
if (res && res.status === 200 && res.data && res.data.uploadUrl) {
await axios
.put(res.data.uploadUrl, file, {
headers: { 'Content-Type': file?.type },
onUploadProgress: (e) => console.log(e),
})
.catch((e) => console.error(e));
}
loader.hide();
}
async function deleteFile(pathname: string) {
const arr = pathname.split('/');
if (arr.length < 1) return;
loader.show();
await api.delete('/storage/file', {
data: { path: arr.slice(0, -1), file: arr[arr.length - 1] },
});
loader.hide();
}
async function searchFile(params: SearchOperator & SearchOptions) {
loader.show();
return (await api
.post(`/search${params.within ? `?within=${params.within}` : ''}`, {
...params,
within: undefined,
})
.then((r) => r.data)
.finally(loader.hide)) as StorageFile[];
}
async function getFileUrl(pathname: string | undefined) {
if (!pathname) return;
const arr = pathname.split('/');
const file = arr.pop();
const res = await api.post<StorageFile & { downloadUrl: string }>(
'/storage/file/download',
{ path: arr, file },
);
if (res.status === 200 && res.data && res.data.downloadUrl) {
return res.data.downloadUrl;
}
}
async function downloadFile(pathname: string | undefined) {
if (!pathname) return;
const arr = pathname.split('/');
const file = arr.pop();
const res = await api.post<StorageFile & { downloadUrl: string }>(
'/storage/file/download',
{ path: arr, file },
);
if (res.status === 200 && res.data && res.data.downloadUrl) {
await axios
.get(res.data.downloadUrl, {
method: 'GET',
responseType: 'blob',
headers: {
'Content-Type': 'application/json',
Accept: res.data.fileType,
},
})
.then((r) => {
const a = document.createElement('a');
a.href = window.URL.createObjectURL(r.data);
a.download = res.data.fileName;
a.click();
});
}
}
return {
// information
currentInfo,
currentFile,
folder,
file,
tree,
// fetch
getStorage,
getStorageFile,
// traverse
goto,
gotoParent,
// operation
createFolder,
editFolder,
deleteFolder,
createFile,
updateFile,
deleteFile,
searchFile,
getFileUrl,
downloadFile,
};
});
export default useStorage;