/**
* @fileoverview Diese Datei enthält Controller-Funktionen für die Verwaltung von Ordnern.
* Sie ermöglicht das Abrufen, Erstellen, Umbenennen und Löschen von Ordnern sowie das Abrufen der Ordnerstruktur eines Benutzers.
*
* @author Luca, Miray
* Die Funktionen wurden mit Unterstützung von KI-Tools angepasst und optimiert.
*/
const Folder = require("../../database/Folder.js");
const File = require("../../database/File.js");
const sequelize = require("../../sequelize.config.js");
const { Op } = require("sequelize");
const db = require("../../ConnectPostgres.js");
/**
* Ruft die vollständige Ordnerstruktur eines Benutzers ab, einschließlich untergeordneter Ordner und enthaltener Dateien.
*
* @async
* @function getFolderTree
* @param {Object} req - Das Request-Objekt mit der Benutzer-Session.
* @param {Object} res - Das Response-Objekt für die Serverantwort.
* @returns {Promise<void>} Antwort mit der hierarchischen Ordnerstruktur und nicht zugewiesenen Dateien.
* @throws {Error} Falls ein Fehler beim Abrufen der Ordner auftritt.
*/
exports.getFolderTree = async (req, res) => {
if (!req.session.userId) {
return res
.status(401)
.json({ message: "Unauthorized: User not logged in" });
}
const userId = parseInt(req.session.userId, 10);
if (isNaN(userId)) {
return res.status(400).json({ message: "Invalid user ID" });
}
try {
// Holen der Ordner mit rekursiver Abfrage
const folders = await sequelize.query(
`
WITH RECURSIVE folder_tree AS (
SELECT folder_id, parent_folder_id, folder_name, 1 AS level
FROM main.folders
WHERE user_id = :userId AND parent_folder_id IS NULL
UNION ALL
SELECT f.folder_id, f.parent_folder_id, f.folder_name, ft.level + 1
FROM main.folders f
JOIN folder_tree ft ON f.parent_folder_id = ft.folder_id
WHERE f.user_id = :userId
)
SELECT ft.*, array_agg(files.file_id || ':' || files.file_name) AS files
FROM folder_tree ft
LEFT JOIN main.files files ON files.folder_id = ft.folder_id
GROUP BY ft.folder_id, ft.parent_folder_id, ft.folder_name, ft.level
ORDER BY ft.level, ft.folder_name;
`,
{
type: sequelize.QueryTypes.SELECT,
replacements: { userId },
}
);
// Dateien ohne zugewiesenen Ordner holen
const unassignedFiles = await File.findAll({
attributes: ["file_id", "file_name"],
where: {
folder_id: null,
user_id: userId,
},
});
// Funktion zum Erstellen der Ordnerstruktur
const buildTree = (folders, parentId = null) => {
return folders
.filter((folder) => folder.parent_folder_id === parentId)
.map((folder) => ({
id: folder.folder_id,
name: folder.folder_name,
files: folder.files[0]
? folder.files
.filter((file) => file !== null) // Überprüfe, ob der Eintrag nicht null ist
.map((file) => {
const [id, name] = file.split(":");
return { id, name };
})
: [],
children: buildTree(folders, folder.folder_id),
}));
};
const folderTree = buildTree(folders);
// Unassigned Files hinzufügen
const unassignedFilesList = unassignedFiles.map((file) => ({
id: file.file_id,
name: file.file_name,
}));
res.json({ folderTree, unassignedFiles: unassignedFilesList });
} catch (error) {
console.error("Error fetching folders:", error);
res.status(500).json({ message: "Error fetching folders" });
}
};
// Funktion zum Erstellen eines neuen Ordners
const { generateEmbedding } = require("../models/modelEmbedding"); // Importiere die Funktion zum Generieren von Embeddings
/**
* Erstellt einen neuen Ordner für den Benutzer.
*
* @async
* @function createFolder
* @param {Object} req - Das Request-Objekt mit dem neuen Ordnernamen und optional einer Parent-Folder-ID.
* @param {Object} res - Das Response-Objekt für die Serverantwort.
* @returns {Promise<void>} Antwort mit der ID des neu erstellten Ordners.
* @throws {Error} Falls die Erstellung fehlschlägt.
*/
exports.createFolder = async (req, res) => {
if (!req.session.userId) {
return res
.status(401)
.json({ message: "Unauthorized: User not logged in" });
}
const userId = parseInt(req.session.userId, 10);
const { folderName, parentFolderId } = req.body;
if (!folderName) {
return res.status(400).json({ message: "Folder name is required" });
}
try {
// Falls parentFolderId nicht angegeben oder ungültig ist, auf NULL setzen
const parentFolderIdToUse = parentFolderId
? parseInt(parentFolderId, 10)
: null;
// Generiere das Embedding für den Ordnernamen
const embedding = await generateEmbedding(folderName);
// Erstelle den neuen Ordner mit Sequelize
const newFolder = await Folder.create({
user_id: userId,
folder_name: folderName,
parent_folder_id: parentFolderIdToUse,
embedding: sequelize.literal(`'[${embedding.join(", ")}]'`),
});
res.status(201).json({
message: "Folder created successfully",
folderId: newFolder.folder_id,
});
} catch (error) {
console.error("Error creating folder:", error);
res.status(500).json({ message: "Error creating folder" });
}
};
/**
* Ruft alle Ordner eines Benutzers ab.
*
* @async
* @function getFolders
* @param {Object} req - Das Request-Objekt mit der Benutzer-Session.
* @param {Object} res - Das Response-Objekt für die Serverantwort.
* @returns {Promise<void>} Antwort mit einer Liste der Ordner.
* @throws {Error} Falls das Abrufen der Ordner fehlschlägt.
*/
exports.getFolders = async (req, res) => {
if (!req.session.userId) {
return res
.status(401)
.json({ message: "Unauthorized: User not logged in" });
}
const userId = parseInt(req.session.userId, 10);
if (isNaN(userId)) {
return res.status(400).json({ message: "Invalid user ID" });
}
try {
// Finde alle Ordner für den angemeldeten Benutzer
const folders = await Folder.findAll({
where: { user_id: userId },
attributes: ["folder_id", "folder_name"], // Nur benötigte Attribute abrufen
});
// Ergebnisse als JSON zurückgeben
res.json(folders);
} catch (error) {
console.error("Error fetching folders:", error);
res.status(500).json({ message: "Error fetching folders" });
}
};
/**
* Löscht einen Ordner und alle untergeordneten Ordner sowie die darin enthaltenen Dateien.
*
* @async
* @function deleteFolder
* @param {Object} req - Das Request-Objekt mit der ID des zu löschenden Ordners.
* @param {Object} res - Das Response-Objekt für die Serverantwort.
* @returns {Promise<void>} Antwort mit einer Erfolgsmeldung oder einer Fehlermeldung.
* @throws {Error} Falls das Löschen des Ordners fehlschlägt.
*/
exports.deleteFolder = async (req, res) => {
if (!req.session.userId) {
return res
.status(401)
.json({ message: "Unauthorized: User not logged in" });
}
const userId = parseInt(req.session.userId, 10);
const folderId = parseInt(req.params.folderId, 10);
if (isNaN(userId) || isNaN(folderId)) {
return res.status(400).json({ message: "Invalid user ID or folder ID" });
}
try {
// Überprüfen, ob der Ordner existiert und dem Benutzer gehört
const folder = await Folder.findOne({
where: {
folder_id: folderId,
user_id: userId,
},
});
if (!folder) {
return res
.status(404)
.json({ message: "Folder not found or not authorized" });
}
// Funktion zum rekursiven Finden von Unterordnern
const findAllSubfolders = async (parentId) => {
const subfolders = await Folder.findAll({
where: { parent_folder_id: parentId, user_id: userId },
});
let allSubfolderIds = subfolders.map((subfolder) => subfolder.folder_id);
for (const subfolder of subfolders) {
const childSubfolderIds = await findAllSubfolders(subfolder.folder_id);
allSubfolderIds = allSubfolderIds.concat(childSubfolderIds);
}
return allSubfolderIds;
};
// Finde alle Unterordner-IDs
const subfolderIds = await findAllSubfolders(folderId);
const folderIdsToDelete = [folderId, ...subfolderIds];
// Löschen der Dateien in allen Unterordnern
await File.destroy({
where: {
folder_id: {
[Op.in]: folderIdsToDelete,
},
},
});
// Löschen der Unterordner
await Folder.destroy({
where: {
folder_id: {
[Op.in]: folderIdsToDelete,
},
},
});
res
.status(200)
.json({ message: "Folder and its subfolders deleted successfully" });
} catch (error) {
console.error("Error deleting folder and its subfolders:", error);
res
.status(500)
.json({ message: "Error deleting folder and its subfolders" });
}
};
/**
* Ändert den Namen eines vorhandenen Ordners.
*
* @async
* @function renameFolder
* @param {Object} req - Das Request-Objekt mit der Ordner-ID und dem neuen Namen.
* @param {Object} res - Das Response-Objekt für die Serverantwort.
* @returns {Promise<void>} Antwort mit der aktualisierten Ordnerinformation.
* @throws {Error} Falls das Umbenennen fehlschlägt.
*/
exports.renameFolder = async (req, res) => {
if (!req.session.userId) {
return res
.status(401)
.json({ message: "Unauthorized: User not logged in" });
}
const userId = parseInt(req.session.userId, 10);
const { folderId, newFolderName } = req.body;
if (!folderId || !newFolderName) {
return res
.status(400)
.json({ message: "Ordner-ID und neuer Ordnername sind erforderlich." });
}
try {
// Find and update the folder using Sequelize
const folder = await Folder.findOne({
where: {
folder_id: folderId,
user_id: userId,
},
});
if (!folder) {
return res
.status(404)
.json({ message: "Ordner nicht gefunden oder nicht autorisiert." });
}
// Update folder name
folder.folder_name = newFolderName;
await folder.save();
res.json({
message: "Ordner erfolgreich umbenannt.",
folder: folder,
});
} catch (error) {
console.error("Fehler beim Umbenennen des Ordners:", error);
res.status(500).json({ message: "Fehler beim Umbenennen des Ordners." });
}
};