import type { DocumentStore } from '@/stores/documents' import { useDocumentStore } from '@/stores/documents' import createWebSocket from './WS' export type FUID = string type BaseDocument = { name: string key: FUID } export type FolderDocument = BaseDocument & { type: 'folder' | 'file' size: number sizedisp: string mtime: number modified: string } export type Document = FolderDocument export type errorEvent = { error: { code: number message: string redirect: string } } // Raw types the backend /api/watch sends us export type FileEntry = { id: FUID size: number mtime: number } export type DirEntry = { id: FUID size: number mtime: number dir: DirList } export type DirList = Record export type UpdateEntry = { name: string deleted?: boolean id?: FUID size?: number mtime?: number dir?: DirList } // Helper structure for selections export interface SelectedItems { selected: Set missing: Set rootdir: DirList entries: Record fullpath: Record relpath: Record url: Record ids: FUID[] } export const url_document_watch_ws = '/api/watch' export const url_document_upload_ws = '/api/upload' export const url_document_get = '/files' export class DocumentHandler { constructor(private store: DocumentStore = useDocumentStore()) { this.handleWebSocketMessage = this.handleWebSocketMessage.bind(this) } handleWebSocketMessage(event: MessageEvent) { const msg = JSON.parse(event.data) if ('error' in msg) { if (msg.error.code === 401) { this.store.user.isLoggedIn = false this.store.user.isOpenLoginModal = true } else { this.store.error = msg.error.message } // The server closes the websocket after errors, so we need to reopen it setTimeout(() => { this.store.wsWatch = createWebSocket( url_document_watch_ws, this.handleWebSocketMessage ) }, 1000) } switch (true) { case !!msg.root: this.handleRootMessage(msg) break case !!msg.update: this.handleUpdateMessage(msg) break case !!msg.error: this.handleError(msg) break default: } } private handleRootMessage({ root }: { root: DirEntry }) { if (this.store && this.store.root) { this.store.user.isLoggedIn = true this.store.root = root } } private handleUpdateMessage(updateData: { update: UpdateEntry[] }) { let node: DirEntry = this.store.root for (const elem of updateData.update) { if (elem.deleted) { delete node.dir[elem.name] break // Deleted elements can't have further children } if (elem.name !== undefined) { // @ts-ignore node = node.dir[elem.name] ||= {} } if (elem.id !== undefined) node.id = elem.id if (elem.size !== undefined) node.size = elem.size if (elem.mtime !== undefined) node.mtime = elem.mtime if (elem.dir !== undefined) node.dir = elem.dir } } private handleError(msg: errorEvent) { if (msg.error.code === 401) { this.store.user.isOpenLoginModal = true this.store.user.isLoggedIn = false return } } } export class DocumentUploadHandler { constructor(private store: DocumentStore = useDocumentStore()) { this.handleWebSocketMessage = this.handleWebSocketMessage.bind(this) } handleWebSocketMessage(event: MessageEvent) { const msg = JSON.parse(event.data) switch (true) { case !!msg.written: this.handleWrittenMessage(msg) break default: } } private handleWrittenMessage(msg: { written: number }) { // if (this.store && this.store.root) this.store.root = root; console.log('Written message', msg.written) } }