reply message from thread and room

This commit is contained in:
grimhilt 2023-04-14 20:54:44 +02:00
parent 7ad22e55c1
commit 4799e477be
10 changed files with 117 additions and 20 deletions

View File

@ -1,6 +1,6 @@
import { Response } from "express"; import { Response } from "express";
import { getAccounts, registerAccount } from "../db/api-db"; import { getAccounts, registerAccount } from "../db/api-db";
import { getAddresseId } from "../db/utils/mail"; import { getAddressId } from "../db/utils/mail";
import statusCodes from "../utils/statusCodes"; import statusCodes from "../utils/statusCodes";
export default class Account { export default class Account {
@ -12,7 +12,7 @@ export default class Account {
static async register(body, res: Response) { static async register(body, res: Response) {
const { email, pwd, xoauth, xoauth2, host, port, tls } = body; const { email, pwd, xoauth, xoauth2, host, port, tls } = body;
getAddresseId(email).then((addressId) => { getAddressId(email).then((addressId) => {
registerAccount(addressId, pwd, xoauth, xoauth2, host, port, tls) registerAccount(addressId, pwd, xoauth, xoauth2, host, port, tls)
.then((mailboxId) => { .then((mailboxId) => {
res.status(statusCodes.OK).json({ id: mailboxId }); res.status(statusCodes.OK).json({ id: mailboxId });

View File

@ -2,24 +2,58 @@ import statusCode from "../utils/statusCodes";
import { Response } from "express"; import { Response } from "express";
import { RoomType } from "../mails/message/saveMessage"; import { RoomType } from "../mails/message/saveMessage";
import { getRoomType } from "../db/message/saveMessage-db"; import { getRoomType } from "../db/message/saveMessage-db";
import { getRoomOwner } from "../db/Room-db"; import { getLastMsgData, getRoomOwner } from "../db/Room-db";
import emailManager from "../mails/EmailManager"; import emailManager from "../mails/EmailManager";
import MailBuilder from "../mails/utils/mailBuilder"; import MailBuilder from "../mails/utils/mailBuilder";
import { getAddresses } from "../db/utils/mail";
function rmUserFromAddrs(addresses: { email: string }[], user: string) {
let index = addresses.findIndex((a) => a.email == user);
if (index != -1) {
addresses.splice(index, 1);
}
}
export default class Room { export default class Room {
// todo change name // todo change name of reponse
static async response(body, res: Response) { static async response(body, res: Response) {
const { user, roomId, text, html } = body; const { user, roomId, text, html } = body;
console.log(body)
const roomType = (await getRoomType(roomId))[0].room_type; const roomType = (await getRoomType(roomId))[0].room_type;
if (roomType === RoomType.DM) { if (roomType === RoomType.DM) {
const ownerEmail = (await getRoomOwner(roomId))[0].email; const ownerEmail = (await getRoomOwner(roomId))[0].email;
const mailBuilder = new MailBuilder(); const mailBuilder = new MailBuilder();
mailBuilder.from(user).to(ownerEmail).text(text).html(html); mailBuilder.from(user).to(ownerEmail).text(text).html(html);
emailManager.getSmtp(user).sendMail(mailBuilder.message); emailManager.getSmtp(user).sendMail(mailBuilder.message);
// send new msg to recipient of dm res.status(statusCode.OK).send();
} else if (roomType === RoomType.GROUP || roomType === RoomType.THREAD) { } else if (roomType === RoomType.GROUP || roomType === RoomType.THREAD) {
// get all cc and to from of previous message and add them const lastMsgData = (await getLastMsgData(roomId))[0];
console.log(lastMsgData);
const mailBuilder = new MailBuilder();
mailBuilder.inReplySubject(lastMsgData.subject).inReplyTo(lastMsgData.messageID).text(text).html(html);
const from = await getAddresses(lastMsgData.fromA);
let to = lastMsgData.toA ? await getAddresses(lastMsgData.toA) : [];
let cc = lastMsgData.ccA ? await getAddresses(lastMsgData.ccA) : [];
// remove us from recipients
rmUserFromAddrs(to, user);
rmUserFromAddrs(from, user);
// add sender of previous as recipient if it is not us
if (from.findIndex((a) => a.email == user) == -1) {
to = to.concat(from);
}
mailBuilder
.from(user)
.to(to.map((a) => a.email))
.cc(cc.map((a) => a.email));
emailManager.getSmtp(user).sendMail(mailBuilder.message);
res.status(statusCode.OK).send();
} else { } else {
res.status(statusCode.FORBIDDEN).send({ error: "Cannot add a new message in a room or a channel." }); res.status(statusCode.FORBIDDEN).send({ error: "Cannot add a new message in a room or a channel." });
} }

View File

@ -1,4 +1,5 @@
import { execQueryAsync } from "./db"; import { execQueryAsync } from "./db";
import { queryCcId, queryFromId, queryToId } from "./utils/addressQueries";
export async function getRoomOwner(roomId: number) { export async function getRoomOwner(roomId: number) {
const query = ` const query = `
@ -10,3 +11,51 @@ export async function getRoomOwner(roomId: number) {
const values = [roomId]; const values = [roomId];
return await execQueryAsync(query, values); return await execQueryAsync(query, values);
} }
export async function getLastMsgData(roomId: number) {
const query = `
SELECT
msg.message_id AS id,
GROUP_CONCAT(fromT.address_id) AS fromA,
GROUP_CONCAT(toT.address_id) AS toA,
GROUP_CONCAT(ccT.address_id) AS ccA,
subjectT.value AS subject,
content.text AS content,
message.idate AS date,
message.messageID AS messageID
FROM app_room_message msg
${queryFromId} fromT ON msg.message_id = fromT.message_id
${queryToId} toT ON msg.message_id = toT.message_id
${queryCcId} ccT ON msg.message_id = ccT.message_id
LEFT JOIN (
SELECT header_field.message_id, header_field.value
FROM header_field
INNER JOIN field_name
WHERE
field_name.field_id = header_field.field_id AND
field_name.field_name = 'subject'
) subjectT ON msg.message_id = subjectT.message_id
LEFT JOIN (
SELECT bodypart.text, header_field.message_id FROM bodypart
INNER JOIN header_field
INNER JOIN field_name
WHERE
field_name.field_id = header_field.field_id AND
field_name.field_name = 'html' AND
bodypart.bodypart_id = header_field.bodypart_id
) content ON msg.message_id = content.message_id
INNER JOIN message ON message.message_id = msg.message_id
WHERE msg.room_id = ?
GROUP BY msg.message_id
ORDER BY message.idate DESC
LIMIT 1
`;
const values = [roomId];
return await execQueryAsync(query, values);
}

View File

@ -1,6 +1,6 @@
import { execQueryAsync, execQueryAsyncWithId } from "../db"; import { execQueryAsync, execQueryAsyncWithId } from "../db";
export async function getAddresseId(email: string, name?: string): Promise<number> { export async function getAddressId(email: string, name?: string): Promise<number> {
const localpart = email.split("@")[0]; const localpart = email.split("@")[0];
const domain = email.split("@")[1]; const domain = email.split("@")[1];
const query = `INSERT INTO address const query = `INSERT INTO address
@ -10,6 +10,12 @@ export async function getAddresseId(email: string, name?: string): Promise<numbe
return await execQueryAsyncWithId(query, values); return await execQueryAsyncWithId(query, values);
} }
export async function getAddresses(ids: number | number[]): Promise<{ id: number, email: string }[]> {
const query = `SELECT address_id AS id, email FROM address WHERE address_id IN (?)`;
const values = [ids];
return await execQueryAsync(query, values);
}
export async function getFieldId(field: string): Promise<number> { export async function getFieldId(field: string): Promise<number> {
const query = `INSERT INTO field_name (field_name) VALUES (?) ON DUPLICATE KEY UPDATE field_id=LAST_INSERT_ID(field_id)`; const query = `INSERT INTO field_name (field_name) VALUES (?) ON DUPLICATE KEY UPDATE field_id=LAST_INSERT_ID(field_id)`;
const values = [field]; const values = [field];

View File

@ -11,7 +11,7 @@ import {
getThreadInfoOnId, getThreadInfoOnId,
} from "../../db/message/saveMessage-db"; } from "../../db/message/saveMessage-db";
import { findRoomByOwner, getAddresseId, getUserIdOfMailbox } from "../../db/utils/mail"; import { findRoomByOwner, getAddressId, getUserIdOfMailbox } from "../../db/utils/mail";
import { nbMembers } from "../utils/envelopeUtils"; import { nbMembers } from "../utils/envelopeUtils";
import logger from "../../system/Logger"; import logger from "../../system/Logger";
import { Attrs, Envelope, User } from "../../interfaces/mail/attrs.interface"; import { Attrs, Envelope, User } from "../../interfaces/mail/attrs.interface";
@ -57,7 +57,7 @@ export default class RegisterMessageInApp {
async init() { async init() {
if (this.envelope.from) { if (this.envelope.from) {
this.ownerId = await getAddresseId(createAddress(this.envelope.from[0])); // todo use sender or from ? this.ownerId = await getAddressId(createAddress(this.envelope.from[0])); // todo use sender or from ?
} else { } else {
throw new Error("Envelope must have a 'from' field"); throw new Error("Envelope must have a 'from' field");
} }
@ -152,7 +152,7 @@ export default class RegisterMessageInApp {
if (this.isDm()) { if (this.isDm()) {
// create or add new message to DM // create or add new message to DM
if (!this.envelope.to) throw new Error("Who send a DM and put the recipient in cc ?"); if (!this.envelope.to) throw new Error("Who send a DM and put the recipient in cc ?");
const userTo = await getAddresseId(createAddress(this.envelope.to[0])); const userTo = await getAddressId(createAddress(this.envelope.to[0]));
await this.createOrRegisterOnExistence(userTo, RoomType.DM); await this.createOrRegisterOnExistence(userTo, RoomType.DM);
} else { } else {
// it is not a reply and not a dm // it is not a reply and not a dm

View File

@ -1,4 +1,4 @@
import { getAddresseId, getFlagId } from "../../db/utils/mail"; import { getAddressId, getFlagId } from "../../db/utils/mail";
import { EmailAddress, ParsedMail, simpleParser } from "mailparser"; import { EmailAddress, ParsedMail, simpleParser } from "mailparser";
import moment from "moment"; import moment from "moment";
import Imap from "imap"; import Imap from "imap";
@ -91,7 +91,7 @@ async function saveFromParsedData(parsed: ParsedMail, messageId: number) {
// save address field // save address field
getFieldId(key).then((fieldId) => { getFieldId(key).then((fieldId) => {
parsed[key].value.forEach((addr: EmailAddress, nb: number) => { parsed[key].value.forEach((addr: EmailAddress, nb: number) => {
getAddresseId(addr.address, addr.name).then(async (addressId) => { getAddressId(addr.address, addr.name).then(async (addressId) => {
await saveAddress_fields(messageId, fieldId, addressId, nb); await saveAddress_fields(messageId, fieldId, addressId, nb);
}); });
}); });

View File

@ -43,4 +43,10 @@ export default class MailBuilder {
this.message.inReplyTo = messageID; this.message.inReplyTo = messageID;
return this; return this;
} }
inReplySubject(originSubject: string): MailBuilder {
// todo concate if multiple ?
this.message.subject = "RE: " + originSubject;
return this;
}
} }

View File

@ -20,7 +20,7 @@ const boxId = 1;
jest.mock("../../db/utils/mail", () => { jest.mock("../../db/utils/mail", () => {
return { return {
findRoomByOwner: jest.fn(), findRoomByOwner: jest.fn(),
getAddresseId: jest.fn(), getAddressId: jest.fn(),
getUserIdOfMailbox: jest.fn(), getUserIdOfMailbox: jest.fn(),
}; };
}); });
@ -38,7 +38,7 @@ jest.mock("../../db/message/saveMessage-db", () => {
getThreadInfoOnId: jest.fn(), getThreadInfoOnId: jest.fn(),
}; };
}); });
import { getAddresseId, getUserIdOfMailbox, findRoomByOwner } from "../../db/utils/mail"; import { getAddressId, getUserIdOfMailbox, findRoomByOwner } from "../../db/utils/mail";
import { import {
createRoom, createRoom,
registerMessageInRoom, registerMessageInRoom,
@ -69,7 +69,7 @@ import { AttrsWithEnvelopeTest, createReplyWithSameMembers } from "../test-utils
// if only me reply -> channel // if only me reply -> channel
beforeAll(async () => { beforeAll(async () => {
mocked(getAddresseId).mockImplementation(db.getAddresseId); mocked(getAddressId).mockImplementation(db.getAddressId);
mocked(getUserIdOfMailbox).mockImplementation(db.getUserIdOfMailbox); mocked(getUserIdOfMailbox).mockImplementation(db.getUserIdOfMailbox);
mocked(findRoomByOwner).mockImplementation(db.findRoomByOwner); mocked(findRoomByOwner).mockImplementation(db.findRoomByOwner);

View File

@ -146,7 +146,7 @@ export default class saveMessageDatabase {
}); });
}; };
getAddresseId = (email: string, name?: string): Promise<number> => { getAddressId = (email: string, name?: string): Promise<number> => {
const match = this.users.find((user) => user.user.mailbox + "@" + user.user.host == email); const match = this.users.find((user) => user.user.mailbox + "@" + user.user.host == email);
return new Promise((resolve, reject) => resolve(match?.id)); return new Promise((resolve, reject) => resolve(match?.id));
}; };

View File

@ -30,6 +30,8 @@ const send = () => {
html: htmlContent, html: htmlContent,
}); });
}; };
// todo subject input when dm of group...
</script> </script>
<template> <template>