import db from "../connections/sqlite"; import { ObjectId } from "mongodb"; import { IFileDB } from "../dbTypes"; import { FileListQueryType } from "../../types/file-types"; const rowToFile = (row: any) => { if (!row) return null; return { _id: row.id, length: row.length, chunkSize: row.chunkSize, uploadDate: row.uploadDate, filename: row.filename, metadata: { owner: row.owner, parent: row.parent, parentList: row.parentList, hasThumbnail: !!row.hasThumbnail, isVideo: !!row.isVideo, thumbnailID: row.thumbnailID || undefined, size: row.size, IV: row.iv, linkType: row.linkType || undefined, link: row.link || undefined, filePath: row.filePath || undefined, s3ID: row.s3ID || undefined, personalFile: row.personalFile ? true : undefined, trashed: row.trashed ? true : null, processingFile: row.processingFile ? true : null, }, }; }; const sortColumn = (sortBy: string): { column: string; direction: string } => { if (sortBy === "date_desc") return { column: "uploadDate", direction: "DESC" }; if (sortBy === "date_asc") return { column: "uploadDate", direction: "ASC" }; if (sortBy === "alp_desc") return { column: "filename COLLATE NOCASE", direction: "DESC" }; return { column: "filename COLLATE NOCASE", direction: "ASC" }; }; class FileDB implements IFileDB { constructor() {} // READ getPublicFile = async (fileID: string) => { const row = db.prepare("SELECT * FROM files WHERE id = ?").get(fileID); return rowToFile(row); }; getPublicInfo = async (fileID: string, tempToken: string) => { const row = db .prepare("SELECT * FROM files WHERE id = ? AND link = ?") .get(fileID, tempToken); return rowToFile(row); }; getFileInfo = async (fileID: string, userID: string) => { const row = db .prepare("SELECT * FROM files WHERE id = ? AND owner = ?") .get(fileID, userID); return rowToFile(row); }; getQuickList = async (userID: string, limit: number) => { const rows = db .prepare( `SELECT * FROM files WHERE owner = ? AND trashed = 0 AND processingFile = 0 ORDER BY uploadDate DESC LIMIT ?` ) .all(userID, limit); return rows.map(rowToFile); }; getList = async ( queryData: FileListQueryType, sortBy: string, limit: number ) => { const { userID, search, parent, startAtDate, startAtName, trashMode, mediaMode, mediaFilter, } = queryData; const conditions: string[] = ["owner = ?"]; const params: any[] = [userID]; if (search && search !== "") { conditions.push("filename LIKE ? COLLATE NOCASE"); params.push(`%${search}%`); } else if (!mediaMode) { conditions.push("parent = ?"); params.push(parent); } if (sortBy === "date_desc" && startAtDate) { conditions.push("uploadDate < ?"); params.push(new Date(startAtDate).toISOString()); } else if (sortBy === "date_asc" && startAtDate) { conditions.push("uploadDate > ?"); params.push(new Date(startAtDate).toISOString()); } else if (sortBy === "alp_desc" && startAtName) { conditions.push("filename < ? COLLATE NOCASE"); params.push(startAtName); } else if (sortBy === "alp_asc" && startAtName) { conditions.push("filename > ? COLLATE NOCASE"); params.push(startAtName); } conditions.push("trashed = ?"); params.push(trashMode ? 1 : 0); if (mediaMode) { conditions.push("hasThumbnail = 1"); if (mediaFilter === "photos") conditions.push("isVideo = 0"); else if (mediaFilter === "videos") conditions.push("isVideo = 1"); } conditions.push("processingFile = 0"); const { column, direction } = sortColumn(sortBy); const rows = db .prepare( `SELECT * FROM files WHERE ${conditions.join( " AND " )} ORDER BY ${column} ${direction} LIMIT ?` ) .all(...params, limit); return rows.map(rowToFile); }; getFileSearchList = async ( userID: string, searchQuery: RegExp | string, trashMode: boolean, mediaMode: boolean ) => { const search = searchQuery instanceof RegExp ? searchQuery.source : searchQuery; const conditions = [ "owner = ?", "filename LIKE ? COLLATE NOCASE", "trashed = ?", "processingFile = 0", ]; const params: any[] = [userID, `%${search}%`, trashMode ? 1 : 0]; if (mediaMode) conditions.push("hasThumbnail = 1"); const rows = db .prepare( `SELECT * FROM files WHERE ${conditions.join(" AND ")} LIMIT 10` ) .all(...params); return rows.map(rowToFile); }; getFileListByIncludedParent = async ( userID: string, parentListString: string ) => { const rows = db .prepare( "SELECT * FROM files WHERE owner = ? AND parentList LIKE ?" ) .all(userID, `%${parentListString}%`); return rows.map(rowToFile); }; getFileListByOwner = async (userID: string) => { const rows = db.prepare("SELECT * FROM files WHERE owner = ?").all(userID); return rows.map(rowToFile); }; getFileListByParent = async (userID: string, parent: string) => { const rows = db .prepare("SELECT * FROM files WHERE owner = ? AND parent = ?") .all(userID, parent); return rows.map(rowToFile); }; // UPDATE updateFileUploadedFile = async ( fileID: string, userID: string, parent: string, parentList: string ) => { db.prepare( `UPDATE files SET parent = ?, parentList = ?, processingFile = 0 WHERE id = ? AND owner = ?` ).run(parent, parentList, fileID, userID); return this.getFileInfo(fileID, userID); }; updateFolderUploadedFile = async ( fileID: string, userID: string, parent: string, parentList: string ) => { return this.updateFileUploadedFile(fileID, userID, parent, parentList); }; setThumbnail = async (fileID: string, thumbnailID: string) => { const result = db .prepare( `UPDATE files SET hasThumbnail = 1, thumbnailID = ? WHERE id = ? AND hasThumbnail = 0` ) .run(thumbnailID, fileID); if (result.changes === 0) return null; const row = db.prepare("SELECT * FROM files WHERE id = ?").get(fileID); return rowToFile(row); }; setVideoThumbnail = async ( fileID: string, userID: string, thumbnailID: string ) => { const result = db .prepare( `UPDATE files SET hasThumbnail = 1, thumbnailID = ?, isVideo = 1 WHERE id = ? AND owner = ?` ) .run(thumbnailID, fileID, userID); if (result.changes === 0) return null; return this.getFileInfo(fileID, userID); }; removeOneTimePublicLink = async (fileID: string) => { db.prepare( "UPDATE files SET linkType = NULL, link = NULL WHERE id = ?" ).run(fileID); const row = db.prepare("SELECT * FROM files WHERE id = ?").get(fileID); return rowToFile(row); }; removeLink = async (fileID: string, userID: string) => { db.prepare( "UPDATE files SET linkType = NULL, link = NULL WHERE id = ? AND owner = ?" ).run(fileID, userID); return this.getFileInfo(fileID, userID); }; makePublic = async (fileID: string, userID: string, token: string) => { const existing: any = db .prepare("SELECT * FROM files WHERE id = ? AND owner = ?") .get(fileID, userID); if (!existing) return null; db.prepare( "UPDATE files SET linkType = 'public', link = ? WHERE id = ? AND owner = ?" ).run(token, fileID, userID); return this.getFileInfo(fileID, userID); }; makeOneTimePublic = async (fileID: string, userID: string, token: string) => { const existing: any = db .prepare("SELECT * FROM files WHERE id = ? AND owner = ?") .get(fileID, userID); if (!existing) return null; db.prepare( "UPDATE files SET linkType = 'one', link = ? WHERE id = ? AND owner = ?" ).run(token, fileID, userID); return this.getFileInfo(fileID, userID); }; trashFile = async ( fileID: string, parent: string, parentList: string, userID: string ) => { const existing: any = db .prepare("SELECT * FROM files WHERE id = ? AND owner = ?") .get(fileID, userID); if (!existing) return null; db.prepare( "UPDATE files SET trashed = 1, parent = ?, parentList = ? WHERE id = ? AND owner = ?" ).run(parent, parentList, fileID, userID); return this.getFileInfo(fileID, userID); }; restoreFile = async (fileID: string, userID: string) => { const existing: any = db .prepare("SELECT * FROM files WHERE id = ? AND owner = ?") .get(fileID, userID); if (!existing) return null; db.prepare("UPDATE files SET trashed = 0 WHERE id = ? AND owner = ?").run( fileID, userID ); return this.getFileInfo(fileID, userID); }; removeTempToken = async (user: any, tempToken: string) => { user.tempTokens = user.tempTokens.filter( (filterToken: any) => filterToken.token !== tempToken ); return user; }; trashFilesByParent = async (parentList: string, userID: string) => { const result = db .prepare( "UPDATE files SET trashed = 1 WHERE owner = ? AND parentList LIKE ?" ) .run(userID, `%${parentList}%`); return result; }; restoreFilesByParent = async (parentList: string, userID: string) => { const result = db .prepare( "UPDATE files SET trashed = 0 WHERE owner = ? AND parentList LIKE ?" ) .run(userID, `%${parentList}%`); return result; }; renameFile = async (fileID: string, userID: string, title: string) => { const existing: any = db .prepare("SELECT * FROM files WHERE id = ? AND owner = ?") .get(fileID, userID); if (!existing) return null; db.prepare("UPDATE files SET filename = ? WHERE id = ? AND owner = ?").run( title, fileID, userID ); return this.getFileInfo(fileID, userID); }; moveFile = async ( fileID: string, userID: string, parent: string, parentList: string ) => { const existing: any = db .prepare("SELECT * FROM files WHERE id = ? AND owner = ?") .get(fileID, userID); if (!existing) return null; db.prepare( "UPDATE files SET parent = ?, parentList = ? WHERE id = ? AND owner = ?" ).run(parent, parentList, fileID, userID); return this.getFileInfo(fileID, userID); }; moveMultipleFiles = async ( userID: string, currentParent: string, newParent: string, newParentList: string ) => { db.prepare( "UPDATE files SET parent = ?, parentList = ? WHERE owner = ? AND parent = ?" ).run(newParent, newParentList, userID, currentParent); }; // DELETE deleteFile = async (fileID: string, userID: string) => { const result = db .prepare("DELETE FROM files WHERE id = ? AND owner = ?") .run(fileID, userID); return result; }; // CREATE createFile = async (data: { filename: string; uploadDate: string; length: number; metadata: any; }) => { const id = new ObjectId().toString(); const m = data.metadata || {}; db.prepare( `INSERT INTO files ( id, filename, length, chunkSize, uploadDate, owner, parent, parentList, hasThumbnail, isVideo, thumbnailID, size, iv, linkType, link, filePath, s3ID, personalFile, trashed, processingFile ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` ).run( id, data.filename, data.length, null, data.uploadDate, m.owner, m.parent, m.parentList, m.hasThumbnail ? 1 : 0, m.isVideo ? 1 : 0, m.thumbnailID || null, m.size, m.IV, m.linkType || null, m.link || null, m.filePath || null, m.s3ID || null, m.personalFile ? 1 : 0, m.trashed ? 1 : 0, m.processingFile ? 1 : 0 ); const row = db.prepare("SELECT * FROM files WHERE id = ?").get(id); return rowToFile(row); }; } export default FileDB;