Compare commits
4 Commits
e2bd0bafea
...
a5d325818b
Author | SHA1 | Date | |
---|---|---|---|
|
a5d325818b | ||
|
9fbf5e5cf3 | ||
|
1d761fa6df | ||
|
7f28823758 |
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,11 +1,10 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
.env
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
@ -22,15 +21,13 @@ pnpm-debug.log*
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
build
|
||||
.tmp
|
||||
|
||||
.s.*
|
||||
log*
|
||||
config.json
|
||||
.direnv
|
||||
.envrc
|
||||
*.txt
|
||||
*.json
|
||||
tmp
|
||||
test.*
|
||||
*.png
|
||||
|
@ -1,8 +1,8 @@
|
||||
const statusCode = require("../utils/statusCodes").statusCodes;
|
||||
const { registerAccount } = require("../db/api");
|
||||
const { getAddresseId } = require("../db/mail");
|
||||
import statusCode from "../utils/statusCodes";
|
||||
import { registerAccount } from "../db/api";
|
||||
import { getAddresseId } from "../db/mail";
|
||||
|
||||
async function addAccount(body, res) {
|
||||
export async function addAccount(body, res) {
|
||||
const { email, pwd, xoauth, xoauth2, host, port, tls } = body;
|
||||
getAddresseId(email).then((addressId) => {
|
||||
registerAccount(addressId, pwd, xoauth, xoauth2, host, port, tls)
|
||||
@ -15,8 +15,4 @@ async function addAccount(body, res) {
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addAccount,
|
||||
};
|
||||
|
||||
// todo change mailbox to account
|
@ -1,8 +1,8 @@
|
||||
const statusCode = require("../utils/statusCodes").statusCodes;
|
||||
const { getMembers } = require("../db/api.js");
|
||||
const { logger } = require("../system/Logger");
|
||||
import statusCode from "../utils/statusCodes";
|
||||
import { getMembers } from "../db/api";
|
||||
import logger from "../system/Logger";
|
||||
|
||||
async function members(body, res) {
|
||||
export async function members(body, res) {
|
||||
const { roomId } = body;
|
||||
getMembers(roomId).then((addresses) => {
|
||||
res.status(statusCode.OK).json(addresses);
|
||||
@ -11,7 +11,3 @@ async function members(body, res) {
|
||||
res.status(statusCode.INTERNAL_SERVER_ERROR);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
members,
|
||||
};
|
@ -1,17 +0,0 @@
|
||||
const statusCode = require("../utils/statusCodes").statusCodes;
|
||||
const { getMessages } = require("../db/api.js");
|
||||
const { logger } = require("../system/Logger");
|
||||
|
||||
async function messages(body, res) {
|
||||
const { roomId } = body;
|
||||
getMessages(roomId).then((messages) => {
|
||||
res.status(statusCode.OK).json(messages);
|
||||
}).catch((err) => {
|
||||
logger.err(err)
|
||||
res.status(statusCode.INTERNAL_SERVER_ERROR);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
messages,
|
||||
};
|
13
back/controllers/messages.ts
Normal file
13
back/controllers/messages.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import statusCode from "../utils/statusCodes";
|
||||
import { getMessages } from "../db/api";
|
||||
import logger from "../system/Logger";
|
||||
|
||||
export async function messages(body, res) {
|
||||
const { roomId } = body;
|
||||
getMessages(roomId).then((messages) => {
|
||||
res.status(statusCode.OK).json(messages);
|
||||
}).catch((err) => {
|
||||
logger.error(err)
|
||||
res.status(statusCode.INTERNAL_SERVER_ERROR);
|
||||
});
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
const statusCode = require("../utils/statusCodes").statusCodes;
|
||||
const { getRooms } = require("../db/api.js");
|
||||
const { logger } = require("../system/Logger");
|
||||
import statusCode from "../utils/statusCodes";
|
||||
import { getRooms } from "../db/api";
|
||||
import logger from "../system/Logger";
|
||||
|
||||
async function rooms(body, res) {
|
||||
export async function rooms(body, res) {
|
||||
const { mailboxId, offset, limit } = body;
|
||||
getRooms(mailboxId).then((rooms) => {
|
||||
res.status(statusCode.OK).json(rooms);
|
||||
@ -11,7 +11,3 @@ async function rooms(body, res) {
|
||||
res.status(statusCode.INTERNAL_SERVER_ERROR);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
rooms,
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
const { execQueryAsync, execQueryAsyncWithId } = require("./db.js");
|
||||
const { queryCcId, queryToId, queryFromId } = require("./utils/addressQueries.js");
|
||||
import { execQueryAsync, execQueryAsyncWithId } from "./db";
|
||||
import { queryCcId, queryToId, queryFromId } from "./utils/addressQueries";
|
||||
|
||||
async function registerAccount(userId, pwd, xoauth, xoauth2, host, port, tls) {
|
||||
export async function registerAccount(userId, pwd, xoauth, xoauth2, host, port, tls) {
|
||||
const query = `
|
||||
INSERT INTO app_account
|
||||
(user_id, account_pwd, xoauth, xoauth2, host, port, tls) VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
@ -10,7 +10,7 @@ async function registerAccount(userId, pwd, xoauth, xoauth2, host, port, tls) {
|
||||
return await execQueryAsyncWithId(query, values);
|
||||
}
|
||||
|
||||
async function getAccounts() {
|
||||
export async function getAccounts() {
|
||||
// todo mailbox or account id ?
|
||||
const query = `
|
||||
SELECT
|
||||
@ -27,7 +27,7 @@ async function getAccounts() {
|
||||
return await execQueryAsync(query, values);
|
||||
}
|
||||
|
||||
async function getRooms(mailboxId) {
|
||||
export async function getRooms(mailboxId) {
|
||||
const query = `
|
||||
SELECT
|
||||
app_room.room_id AS id,
|
||||
@ -51,7 +51,7 @@ async function getRooms(mailboxId) {
|
||||
return await execQueryAsync(query, values);
|
||||
}
|
||||
|
||||
async function getMessages(roomId) {
|
||||
export async function getMessages(roomId) {
|
||||
// todo attachements name
|
||||
const query = `
|
||||
SELECT
|
||||
@ -97,7 +97,7 @@ async function getMessages(roomId) {
|
||||
return await execQueryAsync(query, values);
|
||||
}
|
||||
|
||||
async function getMembers(roomId) {
|
||||
export async function getMembers(roomId) {
|
||||
const query = `
|
||||
SELECT
|
||||
address.address_id,
|
||||
@ -110,11 +110,3 @@ async function getMembers(roomId) {
|
||||
const values = [roomId];
|
||||
return await execQueryAsync(query, values);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
registerAccount,
|
||||
getAccounts,
|
||||
getRooms,
|
||||
getMessages,
|
||||
getMembers
|
||||
};
|
@ -1,12 +1,13 @@
|
||||
const mysql = require("mysql");
|
||||
const { logger } = require("../system/Logger");
|
||||
const MYSQL = require("./config.json").mysql;
|
||||
import mysql from "mysql";
|
||||
import logger from "../system/Logger";
|
||||
require("dotenv").config();
|
||||
|
||||
const db = mysql.createConnection({
|
||||
host: MYSQL.host,
|
||||
user: MYSQL.user,
|
||||
password: MYSQL.pwd,
|
||||
database: MYSQL.database,
|
||||
// todo remove export
|
||||
export const db = mysql.createConnection({
|
||||
host: process.env.HOST_DB,
|
||||
user: process.env.USER_DB,
|
||||
password: process.env.PASSWORD_DB,
|
||||
database: process.env.NAME_DB,
|
||||
});
|
||||
|
||||
db.connect(function (err) {
|
||||
@ -17,7 +18,7 @@ db.connect(function (err) {
|
||||
}
|
||||
});
|
||||
|
||||
function execQueryAsync(query, values) {
|
||||
export function execQueryAsync(query: string, values: any[]): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.query(query, values, (err, results, fields) => {
|
||||
if (err) {
|
||||
@ -29,7 +30,7 @@ function execQueryAsync(query, values) {
|
||||
});
|
||||
}
|
||||
|
||||
function execQueryAsyncWithId(query, values) {
|
||||
export function execQueryAsyncWithId(query: string, values: any[]): Promise<number> {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.query(query, values, (err, results, fields) => {
|
||||
if (err) {
|
||||
@ -41,19 +42,12 @@ function execQueryAsyncWithId(query, values) {
|
||||
});
|
||||
}
|
||||
|
||||
function execQuery(query, values) {
|
||||
export function execQuery(query: string, values: any[]) {
|
||||
db.query(query, values, (err, results, fields) => {
|
||||
if (err) {
|
||||
logger.error(err);
|
||||
throw (err);
|
||||
throw err;
|
||||
}
|
||||
return results;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
db, // todo remove this
|
||||
execQuery,
|
||||
execQueryAsync,
|
||||
execQueryAsyncWithId
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
const { execQueryAsyncWithId, execQueryAsync, execQuery } = require("../db");
|
||||
import { execQueryAsyncWithId, execQueryAsync, execQuery } from "../db";
|
||||
|
||||
async function getAllAccounts() {
|
||||
export async function getAllAccounts() {
|
||||
const query = `
|
||||
SELECT
|
||||
app_account.account_id AS id,
|
||||
@ -16,34 +16,26 @@ async function getAllAccounts() {
|
||||
return await execQueryAsync(query, values);
|
||||
}
|
||||
|
||||
async function getAllMailboxes(accountId) {
|
||||
export async function getAllMailboxes(accountId) {
|
||||
const query = 'SELECT * FROM mailbox WHERE mailbox.account_id = ?';
|
||||
const values = [accountId];
|
||||
return await execQueryAsync(query, values)
|
||||
}
|
||||
|
||||
async function registerMailbox(accountId, mailboxName) {
|
||||
export async function registerMailbox(accountId, mailboxName) {
|
||||
const query = `INSERT INTO mailbox (account_id, mailbox_name) VALUES (?, ?)`;
|
||||
const values = [accountId, mailboxName];
|
||||
return await execQueryAsyncWithId(query, values);
|
||||
}
|
||||
|
||||
async function getMailbox(mailboxId) {
|
||||
export async function getMailbox(mailboxId) {
|
||||
const query = `SELECT * FROM mailbox WHERE mailbox_id = ?`;
|
||||
const values = [mailboxId];
|
||||
return await execQueryAsync(query, values);
|
||||
}
|
||||
|
||||
function updateMailbox(mailboxId, uidnext) {
|
||||
export function updateMailbox(mailboxId, uidnext) {
|
||||
const query = `UPDATE mailbox SET uidnext = ? WHERE mailbox_id = ?`;
|
||||
const values = [uidnext, mailboxId];
|
||||
execQuery(query, values);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAllAccounts,
|
||||
getAllMailboxes,
|
||||
registerMailbox,
|
||||
getMailbox,
|
||||
updateMailbox
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
const { execQueryAsync, execQueryAsyncWithId } = require("./db.js");
|
||||
import { execQueryAsync, execQueryAsyncWithId } from "./db";
|
||||
|
||||
async function getAddresseId(email, name) {
|
||||
export async function getAddresseId(email: string, name?: string): Promise<number> {
|
||||
console.log("get address id")
|
||||
const localpart = email.split("@")[0];
|
||||
const domain = email.split("@")[1];
|
||||
const query = `INSERT INTO address
|
||||
@ -10,19 +11,19 @@ async function getAddresseId(email, name) {
|
||||
return await execQueryAsyncWithId(query, values);
|
||||
}
|
||||
|
||||
async function getFieldId(field) {
|
||||
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 values = [field]
|
||||
const values = [field];
|
||||
return await execQueryAsyncWithId(query, values);
|
||||
}
|
||||
|
||||
async function findRoomByOwner(ownerId) {
|
||||
export async function findRoomByOwner(ownerId: number): Promise<{ room_id: number }[]> {
|
||||
const query = `SELECT room_id FROM app_room WHERE owner_id = ?`;
|
||||
const values = [ownerId];
|
||||
return await execQueryAsync(query, values);
|
||||
}
|
||||
|
||||
async function getUserIdOfMailbox(boxId) {
|
||||
export async function getUserIdOfMailbox(boxId: number): Promise<{ user_id: number }[]> {
|
||||
const query = `
|
||||
SELECT app_account.user_id
|
||||
FROM mailbox
|
||||
@ -32,10 +33,3 @@ async function getUserIdOfMailbox(boxId) {
|
||||
const values = [boxId];
|
||||
return await execQueryAsync(query, values);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAddresseId,
|
||||
getFieldId,
|
||||
findRoomByOwner,
|
||||
getUserIdOfMailbox
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
const { transformEmojis } = require("../utils/string.js");
|
||||
const { db, execQuery, execQueryAsync, execQueryAsyncWithId } = require("./db.js");
|
||||
import { transformEmojis } from "../utils/string";
|
||||
import { execQuery, execQueryAsync, execQueryAsyncWithId } from "./db";
|
||||
|
||||
async function registerMessage(timestamp, rfc822size, messageId) {
|
||||
export async function registerMessage(timestamp, rfc822size, messageId) {
|
||||
const query = `
|
||||
INSERT INTO message
|
||||
(idate, messageID, rfc822size) VALUES (?, ?, ?)
|
||||
@ -11,7 +11,7 @@ async function registerMessage(timestamp, rfc822size, messageId) {
|
||||
return await execQueryAsyncWithId(query, values);
|
||||
}
|
||||
|
||||
function registerMailbox_message(mailboxId, uid, messageId, modseq, seen, deleted) {
|
||||
export function registerMailbox_message(mailboxId, uid, messageId, modseq, seen, deleted) {
|
||||
const query = `
|
||||
INSERT IGNORE INTO mailbox_message
|
||||
(mailbox_id, uid, message_id, modseq, seen, deleted) VALUES (?, ?, ?, ?, ?, ?)
|
||||
@ -20,7 +20,7 @@ function registerMailbox_message(mailboxId, uid, messageId, modseq, seen, delete
|
||||
execQuery(query, values);
|
||||
}
|
||||
|
||||
function registerBodypart(messageId, part, bodypartId, bytes, nbLines) {
|
||||
export function registerBodypart(messageId, part, bodypartId, bytes, nbLines) {
|
||||
const query = `
|
||||
INSERT IGNORE INTO part_number
|
||||
(message_id, part, bodypart_id, bytes, nb_lines) VALUES (?, ?, ?, ?, ?)
|
||||
@ -29,14 +29,14 @@ function registerBodypart(messageId, part, bodypartId, bytes, nbLines) {
|
||||
execQuery(query, values);
|
||||
}
|
||||
|
||||
async function saveBodypart(bytes, hash, text, data) {
|
||||
export async function saveBodypart(bytes, hash, text, data) {
|
||||
text = transformEmojis(text);
|
||||
const query = `INSERT IGNORE INTO bodypart (bytes, hash, text, data) VALUES (?, ?, ?, ?)`;
|
||||
const values = [bytes, hash, text, data];
|
||||
return await execQueryAsyncWithId(query, values);
|
||||
}
|
||||
|
||||
async function saveHeader_fields(messageId, fieldId, bodypartId, part, value) {
|
||||
export async function saveHeader_fields(messageId, fieldId, bodypartId, part, value) {
|
||||
value = transformEmojis(value);
|
||||
const query = `
|
||||
INSERT IGNORE INTO header_field
|
||||
@ -46,7 +46,7 @@ async function saveHeader_fields(messageId, fieldId, bodypartId, part, value) {
|
||||
return await execQueryAsync(query, values);
|
||||
}
|
||||
|
||||
async function saveAddress_fields(messageId, fieldId, addressId, number) {
|
||||
export async function saveAddress_fields(messageId, fieldId, addressId, number) {
|
||||
const query = `
|
||||
INSERT IGNORE INTO address_field
|
||||
(message_id , field_id, address_id, number) VALUES (?, ?, ?, ?)
|
||||
@ -55,7 +55,7 @@ async function saveAddress_fields(messageId, fieldId, addressId, number) {
|
||||
return await execQueryAsync(query, values);
|
||||
}
|
||||
|
||||
function saveSource(messageId, content) {
|
||||
export function saveSource(messageId, content) {
|
||||
content = transformEmojis(content);
|
||||
const query = `
|
||||
INSERT INTO source (message_id, content) VALUES (?, ?)
|
||||
@ -64,13 +64,3 @@ function saveSource(messageId, content) {
|
||||
const values = [messageId, content, content];
|
||||
execQuery(query, values);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
registerMessage,
|
||||
registerMailbox_message,
|
||||
saveHeader_fields,
|
||||
saveAddress_fields,
|
||||
registerBodypart,
|
||||
saveBodypart,
|
||||
saveSource
|
||||
}
|
@ -1,64 +1,89 @@
|
||||
const { transformEmojis } = require("../utils/string.js");
|
||||
const { db, execQueryAsync, execQueryAsyncWithId, execQuery } = require("./db.js");
|
||||
const { queryFromId, queryToId, queryCcId } = require("./utils/addressQueries.js");
|
||||
import { transformEmojis } from "../utils/string";
|
||||
import { db, execQueryAsync, execQueryAsyncWithId, execQuery } from "./db";
|
||||
import { queryFromId, queryToId, queryCcId } from "./utils/addressQueries";
|
||||
|
||||
async function createRoom(roomName, ownerId, messageId) {
|
||||
if (!roomName) roomName = "No room name";
|
||||
roomName = transformEmojis(roomName);
|
||||
const query = `INSERT IGNORE INTO app_room (room_name, owner_id, message_id) VALUES (?, ?, ?)`;
|
||||
const values = [roomName.substring(0, 255), ownerId, messageId];
|
||||
return await execQueryAsyncWithId(query, values);
|
||||
// todo add members
|
||||
export async function getAllMembers(messageId: number) {
|
||||
const query = `
|
||||
SELECT GROUP_CONCAT(address.address_id) AS is
|
||||
FROM address
|
||||
INNER JOIN address_field ON
|
||||
address_field.address_id = address.address_id AND
|
||||
address_field.message_id = ?
|
||||
GROUP BY address_field.message_id
|
||||
`;
|
||||
const values = [messageId];
|
||||
return await execQueryAsync(query, values);
|
||||
}
|
||||
|
||||
async function registerMessageInRoom(messageId, roomId, isSeen, idate) {
|
||||
export async function registerMember(roomId: number, memberId: number) {
|
||||
const query = `INSERT IGNORE INTO app_room_member (room_id, member_id) VALUES (?, ?)`;
|
||||
const values = [roomId, memberId];
|
||||
return await execQueryAsync(query, values);
|
||||
}
|
||||
|
||||
export async function createRoom(
|
||||
roomName: string | null | undefined,
|
||||
ownerId: number,
|
||||
messageId: number,
|
||||
roomType: number,
|
||||
) {
|
||||
if (!roomName) roomName = "No room name";
|
||||
roomName = transformEmojis(roomName);
|
||||
const query = `INSERT IGNORE INTO app_room (room_name, owner_id, message_id, room_type) VALUES (?, ?, ?, ?)`;
|
||||
const values = [roomName.substring(0, 255), ownerId, messageId, roomType];
|
||||
return await execQueryAsyncWithId(query, values);
|
||||
}
|
||||
|
||||
// todo date not good
|
||||
export async function registerMessageInRoom(
|
||||
messageId: number,
|
||||
roomId: number,
|
||||
isSeen: boolean,
|
||||
idate: string | undefined | null,
|
||||
) {
|
||||
if (!idate) idate = new Date().toString();
|
||||
const query = `INSERT IGNORE INTO app_room_message (message_id, room_id) VALUES (?, ?)`;
|
||||
const values = [messageId, roomId];
|
||||
await execQueryAsync(query, values);
|
||||
|
||||
updateLastUpdateRoom(roomId, idate);
|
||||
|
||||
if (!isSeen) {
|
||||
incrementNotSeenRoom(roomId);
|
||||
}
|
||||
// if (!isSeen) {
|
||||
// incrementNotSeenRoom(roomId);
|
||||
// }
|
||||
}
|
||||
|
||||
function updateLastUpdateRoom(roomId, idate) {
|
||||
export function updateLastUpdateRoom(roomId: number, idate: string) {
|
||||
const query = `UPDATE app_room SET lastUpdate = ? WHERE room_id = ?`;
|
||||
const values = [idate, roomId];
|
||||
execQuery(query, values);
|
||||
}
|
||||
|
||||
function incrementNotSeenRoom(roomId) {
|
||||
export function incrementNotSeenRoom(roomId: number) {
|
||||
// todo
|
||||
}
|
||||
|
||||
async function createThread(threadName, ownerId, messageId, parentRoomId, isDm) {
|
||||
const rootRoomId = -1; // todo
|
||||
const threadId = await createRoom(threadName, ownerId, messageId);
|
||||
const query = `INSERT INTO app_thread (room_id, parent_room_id, root_room_id, isDm) VALUES (?, ?, ?, ?)`;
|
||||
const values = [threadId, parentRoomId, rootRoomId, isDm];
|
||||
export async function getRoomInfo(messageID: string): Promise<{ room_id: number; root_id: number }[]> {
|
||||
const query = `
|
||||
SELECT
|
||||
app_room.room_id
|
||||
app_thread.root_id
|
||||
FROM app_room
|
||||
LEFT JOIN app_thread ON app_thread.room_id = app_room.room_id
|
||||
INNER JOIN app_room_message ON app_room_message.room_id = app_room.room_id
|
||||
INNER JOIN message ON message.message_id = app_room_message.message_id
|
||||
WHERE message.messageID = ?
|
||||
`;
|
||||
const values = [messageID];
|
||||
return await execQueryAsync(query, values);
|
||||
// todo add members
|
||||
}
|
||||
|
||||
async function registerMessageInThread(messageId, threadId, isSeen) {
|
||||
// todo check if it is still a thread or should be a room
|
||||
// todo isdm
|
||||
console.log("register message in thread")
|
||||
export async function registerThread(roomId: number, parentId: number, rootId: number) {
|
||||
const query = `INSERT IGNORE INTO app_thread (room_id, parent_id, root_id) VALUES (?, ?, ?)`;
|
||||
const values = [roomId, parentId, rootId];
|
||||
return await execQueryAsync(query, values);
|
||||
}
|
||||
|
||||
function updateLastUpdateThread(threadId) {
|
||||
// todo
|
||||
// check for parent
|
||||
}
|
||||
|
||||
function incrementNotSeenThread(threadId) {
|
||||
// todo
|
||||
// also increment root room
|
||||
}
|
||||
|
||||
async function isRoomGroup(roomId) {
|
||||
export async function isRoomGroup(roomId: number): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = `SELECT isGroup FROM app_room WHERE room_id = '${roomId}'`;
|
||||
db.query(query, (err, results, fields) => {
|
||||
@ -68,13 +93,14 @@ async function isRoomGroup(roomId) {
|
||||
});
|
||||
}
|
||||
|
||||
async function findRoomsFromMessage(messageId) {
|
||||
export async function findRoomsFromMessage(messageID: string) {
|
||||
// todo find message in room not started
|
||||
const query = `SELECT room_id FROM app_room_message WHERE message_id = ? ORDER BY room_id`;
|
||||
const values = [messageId];
|
||||
const values = [messageID];
|
||||
return await execQueryAsync(query, values);
|
||||
}
|
||||
|
||||
async function hasSameMembersAsParent(messageId, messageID) {
|
||||
export async function hasSameMembersAsParent(messageId: number, messageID: string) {
|
||||
const query1 = `
|
||||
SELECT
|
||||
GROUP_CONCAT(fromT.address_id) AS fromA,
|
||||
@ -117,13 +143,3 @@ async function hasSameMembersAsParent(messageId, messageID) {
|
||||
addressesMsg1.reduce((a, b) => a && addressesMsg2.includes(b), true)
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createRoom,
|
||||
registerMessageInRoom,
|
||||
createThread,
|
||||
registerMessageInThread,
|
||||
isRoomGroup,
|
||||
findRoomsFromMessage,
|
||||
hasSameMembersAsParent,
|
||||
};
|
@ -122,11 +122,11 @@ CREATE TABLE app_room (
|
||||
room_name VARCHAR(255) NOT NULL,
|
||||
owner_id INT NOT NULL,
|
||||
message_id INT NOT NULL,
|
||||
isGroup BOOLEAN NOT NULL DEFAULT false,
|
||||
room_type INT NOT NULL DEFAULT 0,
|
||||
notSeen INT NOT NULL DEFAULT 0,
|
||||
lastUpdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP(),
|
||||
PRIMARY KEY (room_id),
|
||||
UNIQUE KEY (owner_id, message_id, isGroup),
|
||||
UNIQUE KEY (owner_id, message_id, room_type),
|
||||
FOREIGN KEY (owner_id) REFERENCES address(address_id),
|
||||
FOREIGN KEY (message_id) REFERENCES message(message_id)
|
||||
);
|
||||
@ -134,14 +134,13 @@ CREATE TABLE app_room (
|
||||
-- 12
|
||||
CREATE TABLE app_thread (
|
||||
room_id INT NOT NULL,
|
||||
parent_room_id INT,
|
||||
root_room_id INT,
|
||||
isDm BOOLEAN NOT NULL DEFAULT false,
|
||||
parent_id INT,
|
||||
root_id INT,
|
||||
PRIMARY KEY (room_id),
|
||||
UNIQUE KEY (room_id, parent_room_id, root_room_id),
|
||||
UNIQUE KEY (room_id, parent_id, root_id),
|
||||
FOREIGN KEY (room_id) REFERENCES app_room(room_id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (parent_room_id) REFERENCES app_room(room_id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (root_room_id) REFERENCES app_room(room_id) ON DELETE SET NULL
|
||||
FOREIGN KEY (parent_id) REFERENCES app_room(room_id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (root_id) REFERENCES app_room(room_id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
-- 13
|
||||
@ -157,6 +156,7 @@ CREATE TABLE app_room_message (
|
||||
CREATE TABLE app_room_member (
|
||||
room_id INT NOT NULL,
|
||||
member_id INT NOT NULL,
|
||||
UNIQUE KEY (room_id, member_id),
|
||||
FOREIGN KEY (room_id) REFERENCES app_room(room_id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (member_id) REFERENCES address(address_id)
|
||||
);
|
||||
|
@ -1,4 +1,4 @@
|
||||
const queryAddress = (type) => `
|
||||
const queryAddress = (type: string): string => `
|
||||
LEFT JOIN (
|
||||
SELECT address_field.address_id, address_field.message_id
|
||||
FROM address_field
|
||||
@ -9,12 +9,6 @@ const queryAddress = (type) => `
|
||||
)
|
||||
`;
|
||||
|
||||
const queryFromId = queryAddress("from");
|
||||
const queryToId = queryAddress("to");
|
||||
const queryCcId = queryAddress("cc");
|
||||
|
||||
module.exports = {
|
||||
queryFromId,
|
||||
queryToId,
|
||||
queryCcId
|
||||
}
|
||||
export const queryFromId = queryAddress("from");
|
||||
export const queryToId = queryAddress("to");
|
||||
export const queryCcId = queryAddress("cc");
|
46
back/interfaces/mail/attrs.interface.ts
Normal file
46
back/interfaces/mail/attrs.interface.ts
Normal file
@ -0,0 +1,46 @@
|
||||
export interface User {
|
||||
name: string;
|
||||
mailbox: string;
|
||||
host: string;
|
||||
}
|
||||
|
||||
export interface Envelope {
|
||||
date?: string | null;
|
||||
subject?: string | null;
|
||||
from?: User[] | null;
|
||||
sender?: User[] | null;
|
||||
replyTo?: User[] | null;
|
||||
to?: User[] | null;
|
||||
cc?: User[] | null;
|
||||
bcc?: User[] | null;
|
||||
inReplyTo?: string | null;
|
||||
messageId: string;
|
||||
}
|
||||
|
||||
export interface Attrs {
|
||||
/** A 32-bit ID that uniquely identifies this message within its mailbox. */
|
||||
uid: number;
|
||||
/** A list of flags currently set on this message. */
|
||||
flags: string[];
|
||||
/** The internal server date for the message. */
|
||||
date: string;
|
||||
/** The message's body structure (only set if requested with fetch()). */
|
||||
struct?: any[] | undefined;
|
||||
envelope?: Envelope;
|
||||
/** The RFC822 message size (only set if requested with fetch()). */
|
||||
size?: number | undefined;
|
||||
}
|
||||
|
||||
export interface AttrsWithEnvelope {
|
||||
/** A 32-bit ID that uniquely identifies this message within its mailbox. */
|
||||
uid: number;
|
||||
/** A list of flags currently set on this message. */
|
||||
flags: string[];
|
||||
/** The internal server date for the message. */
|
||||
date: string;
|
||||
/** The message's body structure (only set if requested with fetch()). */
|
||||
struct?: any[] | undefined;
|
||||
envelope: Envelope;
|
||||
/** The RFC822 message size (only set if requested with fetch()). */
|
||||
size?: number | undefined;
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
const MYSQL = require("./sql/config.json").mysql;
|
||||
|
||||
module.exports = {
|
||||
databaseOptions: {
|
||||
host: "localhost",
|
||||
port: 3306,
|
||||
user: MYSQL.user,
|
||||
password: MYSQL.pwd,
|
||||
database: "mail_test",
|
||||
},
|
||||
createDatabase: true,
|
||||
dbSchema: "./sql/structureV2.sql",
|
||||
truncateDatabase: true,
|
||||
};
|
@ -1,9 +1,16 @@
|
||||
const { getMailbox, updateMailbox } = require("../../db/imap/imap");
|
||||
const { logger } = require("../../system/Logger");
|
||||
const { registerMessageInApp } = require("../saveMessage");
|
||||
const { saveMessage } = require("../storeMessage");
|
||||
import Imap, { ImapMessageAttributes, MailBoxes } from "imap";
|
||||
import { getMailbox, updateMailbox } from "../../db/imap/imap";
|
||||
import { Attrs } from "../../interfaces/mail/attrs.interface";
|
||||
import logger from "../../system/Logger";
|
||||
import RegisterMessageInApp from "../saveMessage";
|
||||
import { saveMessage } from "../storeMessage";
|
||||
|
||||
export default class Box {
|
||||
imap: Imap;
|
||||
boxName: string;
|
||||
id: number;
|
||||
box: MailBoxes;
|
||||
|
||||
class Box {
|
||||
constructor(_imap, _boxId, _boxName) {
|
||||
this.imap = _imap;
|
||||
this.boxName = _boxName;
|
||||
@ -23,16 +30,18 @@ class Box {
|
||||
}
|
||||
|
||||
sync(savedUid, currentUid) {
|
||||
const promises = [];
|
||||
const mails = [];
|
||||
const promises: Promise<unknown>[] = [];
|
||||
const mails: ImapMessageAttributes[] = [];
|
||||
logger.log(`Syncing from ${savedUid} to ${currentUid} uid`);
|
||||
const f = this.imap.seq.fetch(`${savedUid}:${currentUid}`, {
|
||||
// const f = this.imap.seq.fetch(`${savedUid}:${currentUid}`, {
|
||||
size: true,
|
||||
envelope: true,
|
||||
});
|
||||
|
||||
f.on("message", (msg, seqno) => {
|
||||
msg.once("attributes", (attrs) => {
|
||||
msg.once("attributes", (attrs: Attrs) => {
|
||||
console.log(attrs.envelope)
|
||||
mails.push(attrs);
|
||||
promises.push(saveMessage(attrs, this.id, this.imap));
|
||||
});
|
||||
@ -44,14 +53,15 @@ class Box {
|
||||
|
||||
f.once("end", async () => {
|
||||
let step = 20;
|
||||
logger.log(promises.length)
|
||||
for (let i = 0; i < promises.length; i += step) {
|
||||
for (let j = i; j < (i + step && promises.length); j++) {
|
||||
console.log(j, promises.length, promises[j])
|
||||
await new Promise((resolve, reject) => {
|
||||
promises[j]
|
||||
.then(async (res) => {
|
||||
await registerMessageInApp(res, mails[j], this.id);
|
||||
resolve();
|
||||
const register = new RegisterMessageInApp(res, mails[j], this.id);
|
||||
await register.save();
|
||||
resolve("");
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
@ -61,16 +71,7 @@ class Box {
|
||||
logger.log(`Saved messages ${i + step > promises.length ? promises.length : i + step}/${mails.length}`);
|
||||
updateMailbox(this.id, mails[i].uid);
|
||||
}
|
||||
// await Promise.all(promises).then(async (res) => {
|
||||
// for (let i = 0; i < mails.length; i++) {
|
||||
// logger.log(`Saved message ${i}/${mails.length}`);
|
||||
// }
|
||||
// });
|
||||
updateMailbox(this.id, currentUid);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Box,
|
||||
};
|
@ -1,9 +1,15 @@
|
||||
const Imap = require("imap");
|
||||
const { getAllMailboxes, registerMailbox } = require("../../db/imap/imap");
|
||||
const { logger } = require("../../system/Logger");
|
||||
const { Box } = require("./Box");
|
||||
import { Account } from "./ImapSync";
|
||||
|
||||
import Imap from "imap";
|
||||
import { getAllMailboxes, registerMailbox } from "../../db/imap/imap";
|
||||
import logger from "../../system/Logger";
|
||||
import Box from "./Box";
|
||||
|
||||
export class ImapInstance {
|
||||
imap: Imap;
|
||||
account: Account;
|
||||
boxes: Box[];
|
||||
|
||||
class ImapInstance {
|
||||
constructor(account) {
|
||||
this.imap = new Imap({
|
||||
user: account.user,
|
||||
@ -69,7 +75,3 @@ class ImapInstance {
|
||||
return allBox;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ImapInstance,
|
||||
};
|
@ -1,28 +0,0 @@
|
||||
const { getAllAccounts } = require("../../db/imap/imap");
|
||||
const { logger } = require("../../system/Logger");
|
||||
const { ImapInstance } = require("./ImapInstance");
|
||||
|
||||
class ImapSync {
|
||||
constructor() {
|
||||
this.instances = [];
|
||||
}
|
||||
|
||||
init() {
|
||||
getAllAccounts().then((accounts) => {
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
accounts[i].password = accounts[i]?.password.toString().replace(/[\u{0080}-\u{FFFF}]/gu,"");
|
||||
this.addInstance(accounts[i]);
|
||||
}
|
||||
}).catch((err) => {
|
||||
logger.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
addInstance(config) {
|
||||
this.instances.push(new ImapInstance(config));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ImapSync
|
||||
}
|
33
back/mails/imap/ImapSync.ts
Normal file
33
back/mails/imap/ImapSync.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { getAllAccounts } from "../../db/imap/imap";
|
||||
import logger from "../../system/Logger";
|
||||
import { ImapInstance } from "./ImapInstance";
|
||||
|
||||
export interface Account {
|
||||
id: number;
|
||||
user: string
|
||||
password?: string
|
||||
}
|
||||
|
||||
export default class ImapSync {
|
||||
instances: ImapInstance[]
|
||||
|
||||
constructor() {
|
||||
this.instances = [];
|
||||
}
|
||||
|
||||
init() {
|
||||
getAllAccounts().then((accounts: Account[]) => {
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
accounts[i].password = accounts[i]?.password?.toString().replace(/[\u{0080}-\u{FFFF}]/gu,"");
|
||||
if (accounts[i].id == 2) continue; //debug_todo
|
||||
this.addInstance(accounts[i]);
|
||||
}
|
||||
}).catch((err) => {
|
||||
logger.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
addInstance(config) {
|
||||
this.instances.push(new ImapInstance(config));
|
||||
}
|
||||
}
|
@ -1,145 +0,0 @@
|
||||
const {
|
||||
createRoom,
|
||||
registerMessageInRoom,
|
||||
createThread,
|
||||
registerMessageInThread,
|
||||
isRoomGroup,
|
||||
findRoomsFromMessage,
|
||||
hasSameMembersAsParent,
|
||||
} = require("../db/saveMessageApp");
|
||||
|
||||
const { findRoomByOwner, getAddresseId, getUserIdOfMailbox } = require("../db/mail");
|
||||
const { nbMembers } = require("./utils/statusUtils");
|
||||
|
||||
/**
|
||||
* take object address and join mailbox and host to return mailbox@host
|
||||
*/
|
||||
function createAddress(elt) {
|
||||
return `${elt.mailbox}@${elt.host}`;
|
||||
}
|
||||
|
||||
async function initiateRoom(envelope, ownerId, messageId, isSeen) {
|
||||
await createRoom(envelope.subject, ownerId, messageId).then(async (roomId) => {
|
||||
// todo register members
|
||||
await registerMessageInRoom(messageId, roomId, isSeen, envelope.date);
|
||||
});
|
||||
}
|
||||
|
||||
const roomType = {
|
||||
ROOM: 'room',
|
||||
CHANNEL: 'channel',
|
||||
GROUP: 'group',
|
||||
DM: 'dm',
|
||||
THREAD: 'thread'
|
||||
}
|
||||
|
||||
class registerMessageInApp {
|
||||
constructor(_messageId, _attrs, _boxId) {
|
||||
this.messageId = _messageId;
|
||||
this.attrs = _attrs;
|
||||
this.envelope = this.attrs.envelope;
|
||||
this.messageID = this.envelope.messageId;
|
||||
this.boxId = _boxId;
|
||||
this.isSeen = this.attrs.flags.includes("\\Seen") ? 1 : 0;
|
||||
this.ownerId;
|
||||
this.userId;
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.ownerId = await getAddresseId(createAddress(this.envelope.from[0])); // todo use sender or from ?
|
||||
}
|
||||
|
||||
isDm = () => nbMembers(this.envelope) == 2;
|
||||
|
||||
async isFromUs() {
|
||||
if (!this.userId) this.userId = (await getUserIdOfMailbox(this.boxId))[0]?.user_id;
|
||||
return this.ownerId == this.userId;
|
||||
}
|
||||
|
||||
async initiateRoom(owner, roomType) {
|
||||
// todo roomType
|
||||
await createRoom(this.envelope.subject, owner, this.messageId).then(async (roomId) => {
|
||||
// todo register members
|
||||
await registerMessageInRoom(this.messageId, roomId, this.isSeen, this.envelope.date);
|
||||
});
|
||||
}
|
||||
|
||||
async createOrRegisterOnExistence(owner, roomType) {
|
||||
await findRoomByOwner(owner).then(async (res) => {
|
||||
if (res.length == 0) {
|
||||
// first message with this sender
|
||||
await initiateRoom(owner, roomType);
|
||||
} else {
|
||||
// not a reply, add to the list of message if this sender
|
||||
await registerMessageInRoom(this.messageId, res[0].room_id, this.isSeen, this.envelope.date);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async createOrRegisterOnMembers(roomId) {
|
||||
const hasSameMembers = await hasSameMembersAsParent(this.messageID, this.envelope.inReplyTo);
|
||||
if (hasSameMembers) {
|
||||
await registerMessageInRoom(this.messageId, roomId, this.isSeen, this.envelope.date);
|
||||
} else {
|
||||
await createThread(this.envelope.subject, this.ownerId, this.messageId, roomId, this.isDm()).then(
|
||||
async (threadId) => {
|
||||
await registerMessageInThread(this.messageId, threadId, this.isSeen);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async save() {
|
||||
await this.init();
|
||||
if (this.envelope.inReplyTo) {
|
||||
this.saveReply();
|
||||
} else {
|
||||
if (await this.isFromUs()) {
|
||||
if (this.isDm()) {
|
||||
// create or add new message to DM
|
||||
const userTo = await getAddresseId(createAddress(this.envelope.to[0]));
|
||||
await this.createOrRegisterOnExistence(userTo, roomType.DM);
|
||||
} else {
|
||||
// it is not a reply and not a dm
|
||||
// so it is a channel, which can be possibly a group
|
||||
initiateRoom(this.envelope, this.ownerId, this.messageId, this.isSeen);
|
||||
}
|
||||
} else {
|
||||
await this.createOrRegisterOnExistence(this.ownerId, roomType.ROOM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async saveReply() {
|
||||
const messageID = envelope.messageId;
|
||||
await findRoomsFromMessage(messageId).then(async (rooms) => {
|
||||
if (rooms.length == 0) {
|
||||
// no rooms, so is a transfer
|
||||
// todo test if members of transferred message are included
|
||||
} else if (rooms.length === 1) {
|
||||
// only one room so message is only in a room and not in a thread
|
||||
// as a thread is associated to a room
|
||||
await isRoomGroup(rooms[0].room_id).then(async (isGroup) => {
|
||||
if (isGroup) {
|
||||
this.createOrRegisterOnMembers(rooms[0].room_id);
|
||||
} else {
|
||||
// reply from channel
|
||||
// todo
|
||||
// if (sender == owner) { // correction from the original sender
|
||||
// // leave in the same channel
|
||||
// }
|
||||
}
|
||||
});
|
||||
} else if (rooms.length > 1) {
|
||||
// get the lowest thread (order by room_id)
|
||||
const roomId = rooms[rooms.length - 1].room_id;
|
||||
this.createOrRegisterOnMembers(roomId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
registerMessageInApp,
|
||||
roomType
|
||||
};
|
183
back/mails/saveMessage.ts
Normal file
183
back/mails/saveMessage.ts
Normal file
@ -0,0 +1,183 @@
|
||||
import {
|
||||
createRoom,
|
||||
registerMessageInRoom,
|
||||
isRoomGroup,
|
||||
findRoomsFromMessage,
|
||||
hasSameMembersAsParent,
|
||||
registerThread,
|
||||
registerMember,
|
||||
getAllMembers,
|
||||
getRoomInfo,
|
||||
} from "../db/saveMessageApp";
|
||||
|
||||
import { findRoomByOwner, getAddresseId, getUserIdOfMailbox } from "../db/mail";
|
||||
import { nbMembers } from "./utils/envelopeUtils";
|
||||
import logger from "../system/Logger";
|
||||
import { ImapMessageAttributes } from "imap";
|
||||
import { Attrs, Envelope, User } from "../interfaces/mail/attrs.interface";
|
||||
|
||||
/**
|
||||
* take object address and join mailbox and host to return mailbox@host
|
||||
*/
|
||||
function createAddress(elt: User): string {
|
||||
return `${elt.mailbox}@${elt.host}`;
|
||||
}
|
||||
|
||||
export const roomType = {
|
||||
ROOM: 0,
|
||||
CHANNEL: 1,
|
||||
GROUP: 2,
|
||||
DM: 3,
|
||||
THREAD: 4,
|
||||
};
|
||||
|
||||
export default class RegisterMessageInApp {
|
||||
messageId: number;
|
||||
attrs: Attrs;
|
||||
envelope: Envelope;
|
||||
messageID?: string;
|
||||
boxId: number;
|
||||
isSeen: boolean;
|
||||
ownerId: number;
|
||||
userId: number;
|
||||
inReplyTo: string;
|
||||
|
||||
constructor(_messageId: number, _attrs: Attrs, _boxId: number) {
|
||||
this.messageId = _messageId;
|
||||
this.attrs = _attrs;
|
||||
if (!this.attrs.envelope) throw new Error("Envelope must exist in attributes");
|
||||
this.envelope = this.attrs.envelope;
|
||||
this.messageID = this.envelope?.messageId;
|
||||
this.boxId = _boxId;
|
||||
this.isSeen = this.attrs.flags.includes("\\Seen") ? true : false;
|
||||
this.ownerId = -1;
|
||||
this.userId = -1;
|
||||
this.inReplyTo = "";
|
||||
}
|
||||
|
||||
async init() {
|
||||
if (this.envelope.from) {
|
||||
this.ownerId = await getAddresseId(createAddress(this.envelope.from[0])); // todo use sender or from ?
|
||||
} else {
|
||||
throw new Error("Envelope must have a 'from' field");
|
||||
}
|
||||
}
|
||||
|
||||
isDm = () => nbMembers(this.envelope) == 2;
|
||||
|
||||
async isFromUs() {
|
||||
if (this.userId == -1) {
|
||||
await getUserIdOfMailbox(this.boxId).then((res) => {
|
||||
this.userId = res[0]?.user_id;
|
||||
});
|
||||
}
|
||||
return this.ownerId == this.userId;
|
||||
}
|
||||
|
||||
async registerMembers(roomId: number) {
|
||||
getAllMembers(this.messageId).then((res) => {
|
||||
res[0].id.split(",").foreach(async (memberId: number) => {
|
||||
await registerMember(roomId, memberId);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async initiateRoom(owner: number, roomType: number) {
|
||||
try {
|
||||
const roomId = await createRoom(this.envelope.subject, owner, this.messageId, roomType);
|
||||
await registerMessageInRoom(this.messageId, roomId, this.isSeen, this.envelope.date);
|
||||
this.registerMembers(roomId);
|
||||
return roomId;
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
async createOrRegisterOnExistence(owner: number, roomType: number) {
|
||||
await findRoomByOwner(owner).then(async (res) => {
|
||||
if (res.length == 0) {
|
||||
// first message with this sender
|
||||
await this.initiateRoom(owner, roomType);
|
||||
} else {
|
||||
// not a reply, add to the list of message if this sender
|
||||
await registerMessageInRoom(this.messageId, res[0].room_id, this.isSeen, this.envelope.date);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async initiateThread() {
|
||||
await createRoom(this.envelope.subject, this.ownerId, this.messageId, roomType.THREAD).then(
|
||||
async (roomId: number) => {
|
||||
// find parent room infos
|
||||
await getRoomInfo(this.inReplyTo).then(async (room) => {
|
||||
// todo room not lenght, reply to transfer ?
|
||||
let root_id = room[0].root_id;
|
||||
if (!root_id) root_id = room[0].room_id;
|
||||
await registerThread(roomId, room[0].room_id, root_id);
|
||||
});
|
||||
// impl register previous message ?
|
||||
await registerMessageInRoom(this.messageId, roomId, this.isSeen, this.envelope.date);
|
||||
await this.registerMembers(roomId);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async createOrRegisterOnMembers(roomId: number) {
|
||||
const hasSameMembers = await hasSameMembersAsParent(this.messageId, this.inReplyTo);
|
||||
if (hasSameMembers) {
|
||||
await registerMessageInRoom(this.messageId, roomId, this.isSeen, this.envelope.date);
|
||||
} else {
|
||||
await this.initiateThread();
|
||||
}
|
||||
}
|
||||
|
||||
async save() {
|
||||
await this.init();
|
||||
if (this.envelope.inReplyTo) {
|
||||
this.inReplyTo = this.envelope.inReplyTo;
|
||||
this.saveReply();
|
||||
} else {
|
||||
if (await this.isFromUs()) {
|
||||
if (this.isDm()) {
|
||||
// create or add new message to DM
|
||||
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]));
|
||||
await this.createOrRegisterOnExistence(userTo, roomType.DM);
|
||||
} else {
|
||||
// it is not a reply and not a dm
|
||||
// so it is a channel, which can be possibly a group
|
||||
this.initiateRoom(this.ownerId, roomType.ROOM);
|
||||
}
|
||||
} else {
|
||||
await this.createOrRegisterOnExistence(this.ownerId, roomType.ROOM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async saveReply() {
|
||||
await findRoomsFromMessage(this.inReplyTo).then(async (rooms) => {
|
||||
if (rooms.length == 0) {
|
||||
// no rooms, so is a transfer
|
||||
// todo test if members of transferred message are included
|
||||
} else if (rooms.length === 1) {
|
||||
// only one room so message is only in a room and not in a thread
|
||||
// as a thread is associated to a room
|
||||
await isRoomGroup(rooms[0].room_id).then(async (isGroup: boolean) => {
|
||||
if (isGroup) {
|
||||
this.createOrRegisterOnMembers(rooms[0].room_id);
|
||||
} else {
|
||||
// reply from channel
|
||||
// todo
|
||||
// if (sender == owner) { // correction from the original sender
|
||||
// // leave in the same channel
|
||||
// }
|
||||
}
|
||||
});
|
||||
} else if (rooms.length > 1) {
|
||||
// get the lowest thread (order by room_id)
|
||||
const roomId = rooms[rooms.length - 1].room_id;
|
||||
this.createOrRegisterOnMembers(roomId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
const { getAddresseId } = require("../db/mail");
|
||||
const { simpleParser } = require("mailparser");
|
||||
const moment = require("moment");
|
||||
const {
|
||||
import { getAddresseId } from "../db/mail";
|
||||
import {simpleParser} from "mailparser";
|
||||
import moment from "moment";
|
||||
import {
|
||||
registerMessage,
|
||||
registerMailbox_message,
|
||||
saveHeader_fields,
|
||||
@ -9,12 +9,12 @@ const {
|
||||
registerBodypart,
|
||||
saveBodypart,
|
||||
saveSource,
|
||||
} = require("../db/saveMessage");
|
||||
} from "../db/saveMessage";
|
||||
|
||||
const { getFieldId } = require("../db/mail");
|
||||
const { logger } = require("../system/Logger");
|
||||
import { getFieldId } from "../db/mail";
|
||||
import logger from "../system/Logger";
|
||||
|
||||
function saveMessage(attrs, mailboxId, imap) {
|
||||
export function saveMessage(attrs, mailboxId, imap) {
|
||||
const envelope = attrs.envelope;
|
||||
const ts = moment(new Date(envelope.date).getTime()).format("YYYY-MM-DD HH:mm:ss");
|
||||
const rfc822size = attrs.size;
|
||||
@ -68,7 +68,7 @@ function saveMessage(attrs, mailboxId, imap) {
|
||||
}
|
||||
|
||||
async function saveFromParsedData(parsed, messageId) {
|
||||
const promises = [];
|
||||
const promises: Promise<any>[] = [];
|
||||
Object.keys(parsed).forEach((key) => {
|
||||
if (["from", "to", "cc", "bcc", "replyTo"].includes(key)) {
|
||||
promises.push(
|
||||
@ -118,9 +118,6 @@ async function saveFromParsedData(parsed, messageId) {
|
||||
// todo when transfered
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
saveMessage,
|
||||
};
|
||||
|
||||
if (process.env["NODE_DEV"] == "TEST") {
|
||||
module.exports = {
|
17
back/mails/utils/envelopeUtils.ts
Normal file
17
back/mails/utils/envelopeUtils.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Envelope, User } from "../../interfaces/mail/attrs.interface";
|
||||
|
||||
export function nbMembers(envelope: Envelope) {
|
||||
return getMembers(envelope).length;
|
||||
}
|
||||
|
||||
export function getMembers(envelope: Envelope) {
|
||||
const members: User[] = [];
|
||||
const fields = ["from", "to", "sender", "replyTo", "cc", "bcc"] as const;
|
||||
fields.forEach((field) => {
|
||||
envelope[field]?.forEach((member: User) => {
|
||||
if (members.find((m) => m.mailbox === member.mailbox && m.host === member.host)) return;
|
||||
members.push(member);
|
||||
});
|
||||
});
|
||||
return members;
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
function nbMembers(envelope) {
|
||||
let nbMembers =
|
||||
(envelope.bcc?.length ?? 0) +
|
||||
(envelope.cc?.length ?? 0) +
|
||||
(envelope.to?.length ?? 0) +
|
||||
(envelope.from?.length ?? 0);
|
||||
if (
|
||||
envelope.sender?.length > 0 &&
|
||||
!(envelope.sender[0].mailbox == envelope.from[0].mailbox && envelope.sender[0].host == envelope.from[0].host)
|
||||
) {
|
||||
nbMembers += envelope.sender?.length ?? 0;
|
||||
}
|
||||
return nbMembers;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
nbMembers,
|
||||
};
|
1621
back/package-lock.json
generated
1621
back/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,15 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "npx tsc",
|
||||
"start": "node build/server.js",
|
||||
"dev": "concurrently \"npx tsc --watch\" \"nodemon -q build/server.js\"",
|
||||
"clean": "rm -rf build"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": "^8.12.0",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.0.3",
|
||||
"express": "^4.18.2",
|
||||
"imap": "^0.8.19",
|
||||
"imap-simple": "^5.1.0",
|
||||
@ -12,9 +19,22 @@
|
||||
"vue-router": "^4.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^29.5.0"
|
||||
"@babel/preset-typescript": "^7.21.4",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/imap": "^0.8.35",
|
||||
"@types/jest": "^29.5.0",
|
||||
"@types/mailparser": "^3.0.2",
|
||||
"@types/moment": "^2.13.0",
|
||||
"@types/node": "^18.15.11",
|
||||
"concurrently": "^8.0.1",
|
||||
"jest": "^29.5.0",
|
||||
"ts-jest": "^29.0.5",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.9.5"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "ts-jest",
|
||||
"testEnvironment": "node",
|
||||
"testMatch": [
|
||||
"<rootDir>/test//**/*-test.[jt]s?(x)"
|
||||
]
|
||||
|
@ -1,16 +1,17 @@
|
||||
const statusCodes = require("../utils/statusCodes.js").statusCodes;
|
||||
const express = require("express");
|
||||
import statusCodes from "../utils/statusCodes";
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
|
||||
const Ajv = require("ajv");
|
||||
const addFormats = require("ajv-formats");
|
||||
import Ajv from "ajv";
|
||||
import addFormats from "ajv-formats";
|
||||
const ajv = new Ajv({ allErrors: true });
|
||||
addFormats(ajv);
|
||||
const schema_account = require("../schemas/account_schema.json");
|
||||
const { addAccount } = require("../controllers/addAccount.js");
|
||||
const { getAccounts } = require("../db/api.js");
|
||||
const { rooms } = require("../controllers/rooms.js");
|
||||
const { messages } = require("../controllers/messages.js");
|
||||
import schema_account from "../schemas/account_schema.json";
|
||||
import { addAccount } from "../controllers/addAccount";
|
||||
import { getAccounts } from "../db/api";
|
||||
import { rooms } from "../controllers/rooms";
|
||||
import { messages } from "../controllers/messages";
|
||||
import { members } from "../controllers/members";
|
||||
|
||||
const validate_account = ajv.compile(schema_account);
|
||||
|
||||
@ -50,4 +51,4 @@ router.post("/account", async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
export default router;
|
@ -1,7 +1,7 @@
|
||||
const express = require("express");
|
||||
const cors = require("cors");
|
||||
import express from "express";
|
||||
import cors from "cors";
|
||||
const app = express();
|
||||
const { ImapSync } = require("./mails/imap/ImapSync");
|
||||
import ImapSync from "./mails/imap/ImapSync";
|
||||
|
||||
app.use(express.json());
|
||||
app.use(
|
||||
@ -12,8 +12,8 @@ app.use(
|
||||
app.use(cors());
|
||||
app.listen(process.env.PORT || 5500);
|
||||
|
||||
const mailRouter = require("./routes/mail");
|
||||
import mailRouter from "./routes/mail";
|
||||
app.use("/api/mail", mailRouter);
|
||||
|
||||
const imapSync = new ImapSync();
|
||||
imapSync.init()
|
||||
imapSync.init();
|
@ -3,19 +3,19 @@ class Logger {
|
||||
|
||||
}
|
||||
|
||||
log(content) {
|
||||
console.log(this._prefix("log"), content);
|
||||
log(...content): void {
|
||||
// console.log(this._prefix("log"), content);
|
||||
}
|
||||
|
||||
warn(content) {
|
||||
console.warn(this._prefix("warn"), content);
|
||||
warn(...content): void {
|
||||
// console.warn(this._prefix("warn"), content);
|
||||
}
|
||||
|
||||
error(content) {
|
||||
console.error(this._prefix("err"), content);
|
||||
error(...content): void {
|
||||
// console.error(this._prefix("err"), content);
|
||||
}
|
||||
|
||||
_prefix(type) {
|
||||
_prefix(type: string): string {
|
||||
let typeStr = "";
|
||||
switch (type) {
|
||||
case "log":
|
||||
@ -39,7 +39,4 @@ class Logger {
|
||||
}
|
||||
|
||||
const logger = new Logger();
|
||||
|
||||
module.exports = {
|
||||
logger,
|
||||
}
|
||||
export default logger;
|
@ -1,41 +0,0 @@
|
||||
const { getAddresseId, getUserIdOfMailbox } = require("../../db/mail");
|
||||
const { generateAttrs, generateUsers } = require("../test-utils/test-attrsUtils");
|
||||
const { registerMessageInApp, roomType } = require("../../mails/saveMessage");
|
||||
|
||||
jest.mock("../../db/mail");
|
||||
|
||||
// todo mock db
|
||||
|
||||
describe("saveMessage", () => {
|
||||
describe("is not a reply", () => {
|
||||
it("DM from user should be assigned to other user", async () => {
|
||||
const users = generateUsers(2);
|
||||
const attrs = generateAttrs({ from: [users[0].user], to: [users[1].user] });
|
||||
|
||||
getUserIdOfMailbox.mockReturnValue([{ user_id: users[0].id }]);
|
||||
getAddresseId.mockImplementation((email) => {
|
||||
const match = users.find((user) => user.user.mailbox + "@" + user.user.host == email);
|
||||
return match.id;
|
||||
});
|
||||
|
||||
const register = new registerMessageInApp(1, attrs, 1);
|
||||
|
||||
const createOrRegisterOnExistence = jest
|
||||
.spyOn(register, "createOrRegisterOnExistence")
|
||||
.mockImplementation(() => undefined);
|
||||
|
||||
await register.save();
|
||||
|
||||
expect(createOrRegisterOnExistence).toHaveBeenCalledWith(users[1].id, roomType.DM);
|
||||
});
|
||||
|
||||
it("DM message from user should be added to DM room", async () => {
|
||||
// todo multiple messages
|
||||
});
|
||||
// it("first GROUP message should create a group", () => {});
|
||||
});
|
||||
describe("replies", () => {
|
||||
it("", () => {});
|
||||
});
|
||||
describe("", () => {});
|
||||
});
|
112
back/test/mail/saveMessage-test.ts
Normal file
112
back/test/mail/saveMessage-test.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import { generateAttrs, generateUsers } from "../test-utils/test-attrsUtils";
|
||||
import registerMessageInApp, { roomType } from "../../mails/saveMessage";
|
||||
import { jest, describe, it, expect } from "@jest/globals";
|
||||
|
||||
import { getAddresseId, getUserIdOfMailbox } from "../../db/mail";
|
||||
// todo esbuild
|
||||
// todo mock db
|
||||
|
||||
// new message from us
|
||||
// to multiple people -> room
|
||||
// if response has same member => group
|
||||
// if response is dm => channel
|
||||
// to one person => dm
|
||||
|
||||
// new message from other
|
||||
// to only me -> room
|
||||
// if no reply to multiple message => channel
|
||||
// else => dm
|
||||
// to multiple people -> room
|
||||
// // make it better
|
||||
// if multiple members reply -> group
|
||||
// if only me reply -> channel
|
||||
|
||||
const users = generateUsers(5);
|
||||
const ownUser = users[0];
|
||||
const messageId = 1;
|
||||
const boxId = 1;
|
||||
|
||||
jest.mock("../../db/mail", () => ({
|
||||
getAddresseId: jest.fn().mockImplementation((email) => {
|
||||
const match = users.find((user) => user.user.mailbox + "@" + user.user.host == email);
|
||||
return new Promise((resolve, reject) => resolve(match?.id));
|
||||
}),
|
||||
getUserIdOfMailbox: jest.fn().mockImplementation((boxId) => {
|
||||
return new Promise((resolve, reject) => resolve([{ user_id: ownUser.id }]));
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("saveMessage", () => {
|
||||
describe("functions", () => {
|
||||
it("isFromUs", async () => {
|
||||
const attrs = generateAttrs({ from: [ownUser.user], to: [users[1].user] });
|
||||
const register = new registerMessageInApp(messageId, attrs, boxId);
|
||||
await register.init();
|
||||
const res = await register.isFromUs();
|
||||
|
||||
expect(res).toBe(true);
|
||||
|
||||
const attrs2 = generateAttrs({ from: [users[2].user], to: [users[1].user] });
|
||||
const register2 = new registerMessageInApp(messageId, attrs2, boxId);
|
||||
await register2.init();
|
||||
const res2 = await register2.isFromUs();
|
||||
|
||||
expect(res2).toBe(false);
|
||||
});
|
||||
});
|
||||
describe("implementation", () => {
|
||||
describe("new first message from us", () => {
|
||||
it("new first message from us to one recipient should create a DM", async () => {
|
||||
const attrs = generateAttrs({ from: [ownUser.user], to: [users[1].user] });
|
||||
|
||||
const register = new registerMessageInApp(messageId, attrs, boxId);
|
||||
|
||||
const createOrRegisterOnExistence = jest
|
||||
.spyOn(register, "createOrRegisterOnExistence")
|
||||
.mockImplementation(
|
||||
(owner: number, roomType: number) => new Promise((resolve, reject) => resolve()),
|
||||
);
|
||||
|
||||
await register.save();
|
||||
|
||||
expect(createOrRegisterOnExistence).toHaveBeenCalledWith(users[1].id, roomType.DM);
|
||||
});
|
||||
it("new first message from us to multiple recipients should create a ROOM", async () => {
|
||||
const attrs = generateAttrs({ from: [ownUser.user], to: [users[1].user, users[2].user] });
|
||||
|
||||
const register = new registerMessageInApp(messageId, attrs, boxId);
|
||||
|
||||
const initiateRoom = jest
|
||||
.spyOn(register, "initiateRoom")
|
||||
.mockImplementation((owner: number, roomType: number) => Promise.resolve(1));
|
||||
|
||||
await register.save();
|
||||
|
||||
expect(initiateRoom).toHaveBeenCalledWith(ownUser.id, roomType.ROOM);
|
||||
});
|
||||
// it("response to new first message to multiple recipients with same members should change room type to GROUP", () => {});
|
||||
// it("response to new first message to multiple recipients with different members should change room type to CHANNEL", () => {});
|
||||
});
|
||||
describe("new first message from other", () => {
|
||||
it("new first message from other to me only should create a room", async () => {
|
||||
const attrs = generateAttrs({ from: [users[1].user], to: [ownUser.user] });
|
||||
|
||||
const register = new registerMessageInApp(messageId, attrs, boxId);
|
||||
|
||||
const createOrRegisterOnExistence = jest
|
||||
.spyOn(register, "createOrRegisterOnExistence")
|
||||
.mockImplementation((owner: number, roomType: number) => {
|
||||
return new Promise((resolve, reject) => resolve());
|
||||
});
|
||||
|
||||
await register.save();
|
||||
|
||||
expect(createOrRegisterOnExistence).toHaveBeenCalledWith(users[1].id, roomType.ROOM);
|
||||
});
|
||||
});
|
||||
// describe("replies", () => {
|
||||
// it("", () => {});
|
||||
// });
|
||||
// describe("", () => {});
|
||||
});
|
||||
});
|
35
back/test/mail/utils/envelopeUtils-test.ts
Normal file
35
back/test/mail/utils/envelopeUtils-test.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { nbMembers } from "../../../mails/utils/envelopeUtils";
|
||||
import { generateAttrs, generateUsers } from "../../test-utils/test-attrsUtils";
|
||||
import { describe, it, expect } from '@jest/globals';
|
||||
|
||||
describe("envelopeUtils", () => {
|
||||
const names = generateUsers(6);
|
||||
describe("nbMembers", () => {
|
||||
it("sender and from shouldn't be counted twice if there are the same", () => {
|
||||
const envelope = generateAttrs({
|
||||
from: [names[0].user],
|
||||
sender: [names[0].user],
|
||||
}).envelope;
|
||||
expect(nbMembers(envelope)).toBe(1);
|
||||
});
|
||||
it("sender and from shoud be counted twice if there are the same", () => {
|
||||
const envelope = generateAttrs({
|
||||
from: [names[0].user],
|
||||
sender: [names[1].user],
|
||||
}).envelope;
|
||||
expect(nbMembers(envelope)).toBe(2);
|
||||
});
|
||||
it("should count every members", () => {
|
||||
// todo should merge identic members
|
||||
const envelope = generateAttrs({
|
||||
from: [names[0].user],
|
||||
sender: [names[1].user],
|
||||
replyTo: [names[2].user],
|
||||
to: [names[3].user],
|
||||
cc: [names[4].user],
|
||||
bcc: [names[5].user],
|
||||
}).envelope;
|
||||
expect(nbMembers(envelope)).toBe(6);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,40 +0,0 @@
|
||||
const { nbMembers } = require("../../../mails/utils/statusUtils");
|
||||
|
||||
describe("statusUtils", () => {
|
||||
it("sender and from shouldn't be counted twice if there are the same", () => {
|
||||
const envelope = {
|
||||
from: [{ name: "", mailbox: "user_1", host: "provider.com" }],
|
||||
sender: [{ name: "", mailbox: "user_1", host: "provider.com" }],
|
||||
replyTo: [{ name: "", mailbox: "user_1", host: "provider.com" }],
|
||||
to: null,
|
||||
cc: null,
|
||||
bcc: null,
|
||||
inReplyTo: null,
|
||||
};
|
||||
expect(nbMembers(envelope)).toBe(1);
|
||||
});
|
||||
it("sender and from shoud be counted twice if there are the same", () => {
|
||||
const envelope = {
|
||||
from: [{ name: "", mailbox: "user_1", host: "provider.com" }],
|
||||
sender: [{ name: "", mailbox: "user_2", host: "provider.com" }],
|
||||
replyTo: [{ name: "", mailbox: "user_1", host: "provider.com" }],
|
||||
to: null,
|
||||
cc: null,
|
||||
bcc: null,
|
||||
inReplyTo: null,
|
||||
};
|
||||
expect(nbMembers(envelope)).toBe(2);
|
||||
});
|
||||
it("should count every members", () => {
|
||||
const envelope = {
|
||||
from: [{ name: "", mailbox: "user_1", host: "provider.com" }],
|
||||
sender: [{ name: "", mailbox: "user_2", host: "provider.com" }],
|
||||
replyTo: [{ name: "", mailbox: "user_1", host: "provider.com" }],
|
||||
to: [{ name: "", mailbox: "user_1", host: "provider.com" }],
|
||||
cc: [{ name: "", mailbox: "user_1", host: "provider.com" }],
|
||||
bcc: [{ name: "", mailbox: "user_1", host: "provider.com" }],
|
||||
inReplyTo: null,
|
||||
};
|
||||
expect(nbMembers(envelope)).toBe(5);
|
||||
});
|
||||
});
|
19
back/test/sql/saveMessageApp-tes.js
Normal file
19
back/test/sql/saveMessageApp-tes.js
Normal file
@ -0,0 +1,19 @@
|
||||
// beforeAll(async () => {
|
||||
// // const schema = fs.readFileSync('../../sql/structureV2.sql', 'utf8');
|
||||
// // await setupDB(mysqlConfig, schema);
|
||||
// });
|
||||
|
||||
// afterAll(async () => {
|
||||
// global.db.query("DROP database mail_test");
|
||||
// });
|
||||
|
||||
// describe('saveMessageApp', async () => {
|
||||
|
||||
// beforeEach(() => {
|
||||
|
||||
// });
|
||||
|
||||
// it("", () => {
|
||||
|
||||
// });
|
||||
// });
|
@ -1,19 +0,0 @@
|
||||
beforeAll(async () => {
|
||||
// const schema = fs.readFileSync('../../sql/structureV2.sql', 'utf8');
|
||||
// await setupDB(mysqlConfig, schema);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
global.db.query("DROP database mail_test");
|
||||
});
|
||||
|
||||
describe('saveMessageApp', async () => {
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
});
|
||||
|
||||
it("", () => {
|
||||
|
||||
});
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
const mysql = require("mysql");
|
||||
const mysql from ";
|
||||
|
||||
export class TestDb {
|
||||
constructor (options) {
|
||||
|
@ -201,6 +201,4 @@ const names = [
|
||||
"Lori",
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
names,
|
||||
};
|
||||
export default names;
|
73
back/test/test-utils/test-attrsUtils.ts
Normal file
73
back/test/test-utils/test-attrsUtils.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import { AttrsWithEnvelope, User } from "../../interfaces/mail/attrs.interface";
|
||||
import names from "./names";
|
||||
|
||||
interface Options {
|
||||
subject?: string;
|
||||
from?: User[];
|
||||
sender?: User[];
|
||||
replyTo?: User[];
|
||||
to?: User[];
|
||||
cc?: User[];
|
||||
bcc?: User[];
|
||||
inReplyTo?: string;
|
||||
messageId?: string;
|
||||
date?: string;
|
||||
flags?: string[];
|
||||
uid?: number;
|
||||
modseq?: number;
|
||||
}
|
||||
|
||||
export function generateAttrs(options: Options): AttrsWithEnvelope {
|
||||
const attrs = {
|
||||
"size": 42,
|
||||
"envelope": {
|
||||
date: "2023-03-21T15:25:42.000Z",
|
||||
subject: options.subject ?? "subject" + randomString(10),
|
||||
from: options.from ?? undefined,
|
||||
sender: options.sender ?? undefined,
|
||||
replyTo: options.replyTo ?? undefined,
|
||||
to: options.to ?? undefined,
|
||||
cc: options.cc ?? undefined,
|
||||
bcc: options.bcc ?? undefined,
|
||||
inReplyTo: options.inReplyTo ?? undefined,
|
||||
messageId: options.messageId ?? randomString(10),
|
||||
},
|
||||
"date": options.date ?? new Date().toDateString(),
|
||||
"flags": options.flags ?? [],
|
||||
"uid": options.uid ?? randomInt(3),
|
||||
"modseq": options.modseq ?? randomInt(7),
|
||||
"x-gm-labels": ["\\Inbox"],
|
||||
"x-gm-msgid": "1760991478422670209",
|
||||
"x-gm-thrid": "1760991478422670209",
|
||||
};
|
||||
return attrs;
|
||||
}
|
||||
|
||||
export function generateUsers(nb: number) {
|
||||
const users: {user: User, id: number}[] = [];
|
||||
for (let i = 0; i < nb; i++) {
|
||||
users.push({
|
||||
user: {
|
||||
name: "",
|
||||
mailbox: names[i],
|
||||
host: "provider.com",
|
||||
},
|
||||
id: i,
|
||||
});
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
function randomString(length: number): string {
|
||||
let result = "";
|
||||
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
const charactersLength = characters.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function randomInt(length: number): number {
|
||||
return parseInt((Math.random() * Math.pow(10, length)).toFixed());
|
||||
}
|
17
back/tsconfig.json
Normal file
17
back/tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./build",
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true,
|
||||
"types": ["node", "jest"],
|
||||
"paths": {
|
||||
"*": ["node_modules/*", "src/types/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
function removeDuplicates(array) {
|
||||
export function removeDuplicates(array: []) {
|
||||
let unique = [];
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (!unique.includes(array[i])) {
|
||||
@ -7,7 +7,3 @@ function removeDuplicates(array) {
|
||||
}
|
||||
return unique;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
removeDuplicates,
|
||||
};
|
@ -1,5 +1,4 @@
|
||||
// from https://github.com/prettymuchbryce/http-status-codes/blob/master/src/utils.ts
|
||||
|
||||
const statusCodes = {
|
||||
"CONTINUE": 100,
|
||||
"SWITCHING_PROTOCOLS": 101,
|
||||
@ -59,6 +58,4 @@ const statusCodes = {
|
||||
"NETWORK_AUTHENTICATION_REQUIRED": 511,
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
statusCodes: statusCodes
|
||||
};
|
||||
export default statusCodes;
|
@ -1,4 +1,4 @@
|
||||
function transformEmojis(str) {
|
||||
export function transformEmojis(str :string): string {
|
||||
if (!str) return str;
|
||||
// Use a regular expression to match emojis in the string
|
||||
const regex =
|
||||
@ -6,20 +6,14 @@ function transformEmojis(str) {
|
||||
|
||||
// Replace each matched emoji with its Unicode code point
|
||||
const transformedStr = str.replace(regex, (match) => {
|
||||
return "\\u{" + match.codePointAt(0).toString(16).toUpperCase() + "}";
|
||||
return "\\u{" + match.codePointAt(0)?.toString(16).toUpperCase() + "}";
|
||||
});
|
||||
|
||||
return transformedStr;
|
||||
}
|
||||
|
||||
function decodeEmojis(text) {
|
||||
export function decodeEmojis(text: string): string {
|
||||
const regex = /\\u{([^}]+)}/g;
|
||||
const decodedText = text.replace(regex, (_, hex) => String.fromCodePoint(parseInt(hex, 16)));
|
||||
return decodedText;
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
transformEmojis,
|
||||
decodeEmojis
|
||||
}
|
2923
back/yarn.lock
2923
back/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
['@babel/preset-env', {targets: {node: 'current'}}],
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
};
|
Loading…
Reference in New Issue
Block a user