Files
oxmc ecc340e7b1
Release Please / release-please (push) Has been cancelled
Docker Build and Push (Development) / build-and-push-dev (push) Has been cancelled
feat: add dark mode UI and SQLite as alternate database backend
Dark mode: CSS custom properties + .dark class toggled via Redux/localStorage,
with a theme toggle in the Header and Settings page. Foundation didn't exist
yet despite prior assumption, so it was built from scratch.

SQLite: new DB_ENGINE=mongo|sqlite env var selects the metadata backend at
runtime (Mongo stays default, no behavior change unless opted in). File/thumbnail
bytes continue to use the existing fs/S3 storage path. Implemented via a shared
DB interface (dbTypes.ts) with parallel mongoDB/ and sqliteDB/ implementations,
selected through dbFactory.ts, with JWT/encryption logic extracted into
userCrypto.ts so both engines share it.
2026-07-05 04:18:33 -07:00

183 lines
5.1 KiB
TypeScript

import InternalServerError from "../../utils/InternalServerError";
import NotFoundError from "../../utils/NotFoundError";
import { FolderListQueryType } from "../../types/folder-types";
import { getFileDB, getFolderDB, getUserDB } from "../../db/dbFactory";
type userAccessType = {
_id: string;
emailVerified: boolean;
email: string;
s3Enabled: boolean;
};
const fileDB = getFileDB();
const folderDB = getFolderDB();
const userDB = getUserDB();
class FolderService {
createFolder = async (userID: string, name: string, parent: string) => {
const newFolderParentList = [];
if (parent !== "/") {
const parentFolder = await folderDB.getFolderInfo(parent, userID);
if (!parentFolder) throw new Error("Parent not found");
newFolderParentList.push(
...parentFolder.parentList,
parentFolder._id?.toString()
);
} else {
newFolderParentList.push("/");
}
const newFolderData = {
name,
parent: parent || "/",
parentList: newFolderParentList,
owner: userID,
};
const folder = await folderDB.createFolder(newFolderData);
if (!folder) throw new InternalServerError("Upload Folder Error");
return folder;
};
getFolderInfo = async (userID: string, folderID: string) => {
const currentFolder = await folderDB.getFolderInfo(folderID, userID);
if (!currentFolder) throw new NotFoundError("Folder Info Not Found Error");
return currentFolder;
};
getFolderList = async (queryData: FolderListQueryType, sortBy: string) => {
const folderList = await folderDB.getFolderList(queryData, sortBy);
if (!folderList) throw new NotFoundError("Folder List Not Found Error");
return folderList;
};
renameFolder = async (userID: string, folderID: string, title: string) => {
const folder = await folderDB.renameFolder(folderID, userID, title);
if (!folder) throw new NotFoundError("Rename Folder Not Found");
};
trashFolder = async (userID: string, folderID: string) => {
const folder = await folderDB.getFolderInfo(folderID, userID);
if (!folder) throw new NotFoundError("Trash Folder Not Found Error");
const parentList = [];
if (folder.parent !== "/") {
await folderDB.moveFolder(folderID, userID, "/", ["/"]);
parentList.push("/", folder._id!.toString());
} else {
parentList.push(...folder.parentList, folder._id!.toString());
}
await folderDB.trashFolder(folderID, userID);
await folderDB.trashFoldersByParent(parentList, userID);
await fileDB.trashFilesByParent(parentList.toString(), userID);
};
restoreFolder = async (userID: string, folderID: string) => {
const folder = await folderDB.restoreFolder(folderID, userID);
if (!folder) throw new NotFoundError("Restore Folder Not Found Error");
const parentList = [...folder.parentList, folder._id!.toString()];
await folderDB.restoreFoldersByParent(parentList, userID);
await fileDB.restoreFilesByParent(parentList.toString(), userID);
};
getMoveFolderList = async (
userID: string,
parent?: string,
search?: string,
folderIDs?: string[]
) => {
const folderList = await folderDB.getMoveFolderList(
userID,
parent,
search,
folderIDs
);
return folderList;
};
moveFolder = async (userID: string, folderID: string, parentID: string) => {
const folder = await folderDB.getFolderInfo(folderID, userID);
if (!folder) throw new NotFoundError("Move Folder Not Found Error");
const foldersByIncludedParent =
await folderDB.getFolderListByIncludedParent(userID, folderID);
const startParentList = [];
if (parentID !== "/") {
const folderToMoveTo = await folderDB.getFolderInfo(parentID, userID);
if (!folderToMoveTo) {
throw new NotFoundError("Move Folder Not Found Error");
}
startParentList.push(
...folderToMoveTo.parentList,
folderToMoveTo._id.toString()
);
} else {
startParentList.push("/");
}
await Promise.all([
folderDB.moveFolder(folderID, userID, parentID, startParentList),
fileDB.moveMultipleFiles(
userID,
folderID,
folderID,
[...startParentList, folderID].toString()
),
]);
for (let i = 0; i < foldersByIncludedParent.length; i++) {
const currentFolder = foldersByIncludedParent[i];
const currentParentIndex = currentFolder.parentList.indexOf(folderID);
const newParentList = [];
newParentList.push(
...startParentList,
folderID,
...currentFolder.parentList.slice(currentParentIndex + 1)
);
await Promise.all([
folderDB.moveFolder(
currentFolder._id.toString(),
userID,
currentFolder.parent,
newParentList
),
fileDB.moveMultipleFiles(
userID,
currentFolder._id.toString(),
currentFolder._id.toString(),
[...newParentList, currentFolder._id.toString()].toString()
),
]);
}
const updatedFolder = await folderDB.getFolderInfo(folderID, userID);
return updatedFolder;
};
}
export default FolderService;