diff --git a/back/abl/Message-abl.ts b/back/abl/Message-abl.ts index 9c1f551..fe239b8 100644 --- a/back/abl/Message-abl.ts +++ b/back/abl/Message-abl.ts @@ -2,11 +2,11 @@ import statusCode from "../utils/statusCodes"; import { Response } from "express"; import { getMessageUid, getUserOfMailbox } from "../db/utils/mail"; import emailManager from "../mails/EmailManager"; -import { deleteMessage } from "../db/message/updateMessage-db"; import logger from "../system/Logger"; -import { deleteRoom, getRoomNbMessage, getRoomOnMessageId } from "../db/Room-db"; +import Message from "../mails/message/Message"; +import Room from "../mails/room/Room"; -export default class Message { +export default class MessageAbl { static async addFlag(body, res: Response) { const { mailboxId, messageId, flag } = body; const uid = (await getMessageUid(messageId))[0]?.uid; @@ -57,77 +57,83 @@ export default class Message { static deleteRemoteOnly = async (body, res: Response) => { const { mailboxId, messageId } = body; - const uid = (await getMessageUid(messageId))[0]?.uid; - if (!uid) { + const message = new Message().setMessageId(messageId); + + try { + await message.useUid(); + } catch (error) { + logger.err(error); res.status(statusCode.NOT_FOUND).send({ error: "Message uid not found." }); + return; } - const user = (await getUserOfMailbox(mailboxId))[0]?.user; - if (!user) { + try { + await message.useMailbox(mailboxId); + } catch (error) { res.status(statusCode.NOT_FOUND).send({ error: "Not account for this mailbox." }); + return; } - const mailbox = emailManager.getImap(user).getMailbox(mailboxId); - // add flag for deletion - mailbox - .addFlag(uid.toString(), ["\\Deleted"]) - .then(() => { - // move message to trash - mailbox.moveToTrash(uid.toString(), (err) => { - if (err) { - logger.err(err); - res.status(statusCode.METHOD_FAILURE).send({ error: err }); - } else { - res.status(statusCode.OK).send(); - } - }); - }) - .catch((err) => { - logger.log(err); - res.status(statusCode.METHOD_FAILURE).send({ error: err }); + try { + await message.mailbox.addFlag(message.uid.toString(), ["\\Deleted"]); + await message.mailbox.moveToTrash(message.uid.toString(), (err) => { + throw err; }); + } catch (err) { + logger.log(err); + res.status(statusCode.METHOD_FAILURE).send({ error: err }); + return; + } + + res.status(statusCode.OK).send(); }; - static async deleteEverywhere(body, res: Response) { + static deleteEverywhere = async (body, res: Response) => { const { mailboxId, messageId } = body; - const uid = (await getMessageUid(messageId))[0]?.uid; - if (!uid) { + const message = new Message().setMessageId(messageId); + await message.useFlags(); + + try { + await message.useUid(); + } catch (error) { + logger.err(error); res.status(statusCode.NOT_FOUND).send({ error: "Message uid not found." }); return; } - const user = (await getUserOfMailbox(mailboxId))[0]?.user; - if (!user) { - res.status(statusCode.NOT_FOUND).send({ error: "Not account for this mailbox." }); - return; + // if message not deleted remotly, delete it + if (!message.isDeleted) { + try { + await message.useMailbox(mailboxId); + } catch (error) { + res.status(statusCode.NOT_FOUND).send({ error: "Not account for this mailbox." }); + return; + } + + try { + await message.mailbox.addFlag(message.uid.toString(), ["\\Deleted"]); + await message.mailbox.moveToTrash(message.uid.toString(), (err) => { + throw err; + }); + } catch (err) { + logger.log(err); + res.status(statusCode.METHOD_FAILURE).send({ error: err }); + return; + } } - const roomId = (await getRoomOnMessageId(messageId))[0]?.room_id; - - emailManager - .getImap(user) - .getMailbox(mailboxId) - .removeFlag(uid.toString(), ["\\Deleted"]) - .then(async () => { - try { - await deleteMessage(messageId); - if (roomId) { - const nbMessage = (await getRoomNbMessage(roomId))[0].nbMessage; - if (nbMessage > 0) { - res.status(statusCode.OK).send(); - } else { - await deleteRoom(roomId); - res.status(statusCode.OK).json({ deleteRoom: true }).send(); - } - } - } catch (err) { - res.status(statusCode.METHOD_FAILURE).send({ error: err }); - console.log(err); - } - }) - .catch((err) => { - console.log(err); - res.status(statusCode.METHOD_FAILURE).send({ error: err }); - }); - } + const room = await new Room().setRoomIdOnMessageId(messageId); + try { + await message.delete(); + if (room.roomId && room.shouldDelete()) { + await room.delete(); + res.status(statusCode.OK).json({ deleteRoom: true }).send(); + return; + } + } catch (err) { + res.status(statusCode.METHOD_FAILURE).send({ error: err }); + return; + } + res.status(statusCode.OK).send(); + }; } diff --git a/back/abl/Room-abl.ts b/back/abl/Room-abl.ts index 8ea438f..8b2580c 100644 --- a/back/abl/Room-abl.ts +++ b/back/abl/Room-abl.ts @@ -15,7 +15,7 @@ function rmUserFromAddrs(addresses: { email: string }[], user: string) { addresses.splice(index, 1); } } -export default class Room { +export default class RoomAbl { // todo change name of reponse static async response(body, res: Response) { const { user, roomId, text, html } = body; diff --git a/back/db/Room-db.ts b/back/db/Room-db.ts index 42e940f..c4b630e 100644 --- a/back/db/Room-db.ts +++ b/back/db/Room-db.ts @@ -68,8 +68,12 @@ export async function getRoomOnMessageId(messageId: number) { return await execQueryAsync(query, values); } -export async function getRoomNbMessage(roomId: number) { - const query = `SELECT COUNT(room_id) AS nbMessage FROM app_room_message WHERE room_id = ?`; +export async function getRoomNbMessageAndThread(roomId: number): Promise<{ nbMessage: number; nbThread: number }[]> { + const query = ` + SELECT COUNT(arm.room_id) AS nbMessage, COUND(app_thread.room_id) AS nbThread + FROM app_room_message arm + INNER JOIN app_thread ON (app_thread.root_id = arm.room_id OR app_thread.parent_id = arm.room_id) + WHERE room_id = ?`; const values = [roomId]; return await execQueryAsync(query, values); } diff --git a/back/db/message/message-db.ts b/back/db/message/message-db.ts new file mode 100644 index 0000000..09fd71b --- /dev/null +++ b/back/db/message/message-db.ts @@ -0,0 +1,23 @@ +import { execQueryAsync } from "../db"; + +export async function getFlagsOnUid(uid: number): Promise<{ flag_id: number; flag_name: string }[]> { + const query = ` + SELECT * FROM flag_name + INNER JOIN flag ON flag.flag_id = flag_name.flag_id + INNER JOIN mailbox_message ON mailbox_message.message_id = flag.message_id + WHERE mailbox_message.uid = ? + `; + const values = [uid]; + return await execQueryAsync(query, values); +} + +export async function getFlagsOnId(messageId: number): Promise<{ flag_id: number; flag_name: string }[]> { + const query = ` + SELECT * FROM flag_name + INNER JOIN flag ON flag.flag_id = flag_name.flag_id + INNER JOIN mailbox_message ON mailbox_message.message_id = flag.message_id + WHERE mailbox_message.message_id = ? + `; + const values = [messageId]; + return await execQueryAsync(query, values); +} diff --git a/back/mails/message/Message.ts b/back/mails/message/Message.ts new file mode 100644 index 0000000..438f383 --- /dev/null +++ b/back/mails/message/Message.ts @@ -0,0 +1,88 @@ +import { getFlagsOnId, getFlagsOnUid } from "../../db/message/message-db"; +import { deleteMessage, getFlags } from "../../db/message/updateMessage-db"; +import { getMessageUid, getUserOfMailbox } from "../../db/utils/mail"; +import emailManager from "../EmailManager"; +import Mailbox from "../imap/Mailbox"; +import Room from "../room/Room"; + +export default class Message extends Room { + messageId: number; + uid: number; + private _flags: string[] | undefined; + private _mailbox: Mailbox; + + constructor() { + super(); + this.messageId; + this.flags; + this._mailbox; + } + + setMessageId(messageId: number): Message { + this.messageId = messageId; + return this; + } + + async useUid(): Promise { + if (!this.messageId) { + throw "Define message id before trying to find uid"; + } + this.uid = (await getMessageUid(this.messageId))[0]?.uid; + if (!this.uid) { + throw "Uid not found"; + } + return this; + } + + async useFlags(): Promise { + if (!this._flags) { + if (this.messageId) { + await getFlagsOnId(this.messageId); + return this; + } else if (this.uid) { + await getFlagsOnUid(this.uid); + return this; + } else { + throw "Neither message id or uid are set, please do so before attempting to load flags"; + } + } + } + + async useMailbox(mailboxId: number, user?: string): Promise { + if (!user) { + user = (await getUserOfMailbox(mailboxId))[0]?.user; + if (!user) { + throw "Cannot find user of this mailbox"; + } + } + this._mailbox = emailManager.getImap(user).getMailbox(mailboxId); + return this; + } + + get mailbox() { + if (!this._mailbox) { + throw "Call useMailbox before calling functions related to mailbox"; + } + return this._mailbox; + } + + get flags(): string[] { + if (!this._flags) { + throw "Flags not loaded, call useFlags before calling functions related to flags"; + } else { + return this._flags; + } + } + + get isDeleted(): boolean { + return this.flags.includes("\\Deleted"); + } + + delete = async (messageId?: number): Promise => { + if (!(this.messageId ?? messageId)) { + throw "Delete need to have the message id"; + } + await deleteMessage(this.messageId ?? messageId); + return this; + }; +} diff --git a/back/mails/room/Room.ts b/back/mails/room/Room.ts new file mode 100644 index 0000000..133582b --- /dev/null +++ b/back/mails/room/Room.ts @@ -0,0 +1,46 @@ +import { deleteRoom, getRoomNbMessageAndThread, getRoomOnMessageId } from "../../db/Room-db"; + +export default class Room { + private _roomId: number; + constructor() { + this._roomId; + } + + setRoomId(roomId: number): Room { + this._roomId = roomId; + return this; + } + + get roomId(): number { + return this._roomId; + } + + async setRoomIdOnMessageId(messageId: number): Promise { + const res = await getRoomOnMessageId(messageId); + if (res.length == 0) { + throw "Message has no room"; + } + this._roomId = res[0].room_id; + return this; + } + + async shouldDelete(): Promise { + if (!this._roomId) { + throw "shouldDelete needs to have a roomId set."; + } + const res = await getRoomNbMessageAndThread(this._roomId); + if (res.length === 0) return true; + if (res[0].nbMessage === 0 && res[0].nbThread === 0) { + return true; + } + return false; + } + + async delete(): Promise { + if (!this._roomId) { + throw "shouldDelete needs to have a roomId set."; + } + await deleteRoom(this._roomId); + return this; + } +} diff --git a/back/routes/message.ts b/back/routes/message.ts index 3b5394d..af83d40 100644 --- a/back/routes/message.ts +++ b/back/routes/message.ts @@ -1,22 +1,22 @@ import express from "express"; -import Message from "../abl/Message-abl"; +import MessageAbl from "../abl/Message-abl"; import validator from "../validator/validator"; const router = express.Router(); router.post("/addFlag", async (req, res) => { - await validator.validate("addFlag", req.body, res, Message.addFlag); + await validator.validate("addFlag", req.body, res, MessageAbl.addFlag); }); router.post("/removeFlag", async (req, res) => { - await validator.validate("removeFlag", req.body, res, Message.removeFlag); + await validator.validate("removeFlag", req.body, res, MessageAbl.removeFlag); }); router.post("/deleteRemote", async(req, res) => { - await validator.validate("delete", req.body, res, Message.deleteRemoteOnly); + await validator.validate("delete", req.body, res, MessageAbl.deleteRemoteOnly); }); router.post("/delete", async(req, res) => { - await validator.validate("delete", req.body, res, Message.deleteEverywhere); + await validator.validate("delete", req.body, res, MessageAbl.deleteEverywhere); }); diff --git a/back/routes/room.ts b/back/routes/room.ts index cd7733c..46577b8 100644 --- a/back/routes/room.ts +++ b/back/routes/room.ts @@ -1,5 +1,5 @@ import express from "express"; -import Room from "../abl/Room-abl"; +import RoomAbl from "../abl/Room-abl"; import validator from "../validator/validator"; const router = express.Router(); @@ -7,18 +7,18 @@ const router = express.Router(); * Return all messages from a room */ router.get("/:roomId/messages", async (req, res) => { - await validator.validate("getMessages", req.params, res, Room.getMessages); + await validator.validate("getMessages", req.params, res, RoomAbl.getMessages); }); /** * Return all members from a room */ router.get("/:roomId/members", async (req, res) => { - await validator.validate("getMembers", req.params, res, Room.getMembers); + await validator.validate("getMembers", req.params, res, RoomAbl.getMembers); }); router.post("/response", async (req, res) => { - await validator.validate("response", req.body, res, Room.response); + await validator.validate("response", req.body, res, RoomAbl.response); }); export default router;