From 136979f2c97f03747a0142b5c375db3417e50ffc Mon Sep 17 00:00:00 2001 From: Net <93821485+somnetsak123@users.noreply.github.com> Date: Thu, 4 Apr 2024 09:07:10 +0700 Subject: [PATCH] =?UTF-8?q?fix:=20=E0=B8=A5=E0=B8=9A=20=20storage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/storage/index.ts | 558 ------------------------------------ 1 file changed, 558 deletions(-) delete mode 100644 src/stores/storage/index.ts diff --git a/src/stores/storage/index.ts b/src/stores/storage/index.ts deleted file mode 100644 index e281be85..00000000 --- a/src/stores/storage/index.ts +++ /dev/null @@ -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>({}); - const file = ref>({}); - 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(); - 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( - '/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( - '/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;