sync flags on server start

This commit is contained in:
grimhilt 2023-04-17 13:08:54 +02:00
parent 0f063deff9
commit cd996d851a
3 changed files with 64 additions and 19 deletions

View File

@ -16,26 +16,38 @@ export async function getAllAccounts() {
return await execQueryAsync(query, values); return await execQueryAsync(query, values);
} }
export async function getAllMailboxes(accountId) { export async function getAllMailboxes(accountId: number) {
const query = 'SELECT * FROM mailbox WHERE mailbox.account_id = ?'; const query = "SELECT * FROM mailbox WHERE mailbox.account_id = ?";
const values = [accountId]; const values = [accountId];
return await execQueryAsync(query, values) return await execQueryAsync(query, values);
} }
export async function registerMailbox(accountId, mailboxName) { export async function registerMailbox(accountId: number, mailboxName: string) {
const query = `INSERT INTO mailbox (account_id, mailbox_name) VALUES (?, ?)`; const query = `INSERT INTO mailbox (account_id, mailbox_name) VALUES (?, ?)`;
const values = [accountId, mailboxName]; const values = [accountId, mailboxName];
return await execQueryAsyncWithId(query, values); return await execQueryAsyncWithId(query, values);
} }
export async function getMailbox(mailboxId) { export async function getMailbox(mailboxId: number) {
const query = `SELECT * FROM mailbox WHERE mailbox_id = ?`; const query = `SELECT * FROM mailbox WHERE mailbox_id = ?`;
const values = [mailboxId]; const values = [mailboxId];
return await execQueryAsync(query, values); return await execQueryAsync(query, values);
} }
export function updateMailbox(mailboxId, uidnext) { export function updateMailbox(mailboxId: number, uidnext: number) {
const query = `UPDATE mailbox SET uidnext = ? WHERE mailbox_id = ?`; const query = `UPDATE mailbox SET uidnext = ? WHERE mailbox_id = ?`;
const values = [uidnext, mailboxId]; const values = [uidnext, mailboxId];
execQuery(query, values); execQuery(query, values);
} }
export async function updateMailboxModseq(mailboxId: number, modseq: number) {
const query = `UPDATE mailbox SET nextmodseq = ? WHERE mailbox_id = ?`;
const values = [modseq, mailboxId];
return await execQueryAsync(query, values);
}
export async function getMailboxModseq(mailboxId: number): Promise<{ modseq: number }[]> {
const query = `SELECT nextmodseq AS modseq FROM mailbox WHERE mailbox_id = ?`;
const values = [mailboxId];
return await execQueryAsync(query, values);
}

View File

@ -1,5 +1,5 @@
import Imap, { ImapMessageAttributes, Box } from "imap"; import Imap, { ImapMessageAttributes, Box } from "imap";
import { getMailbox, updateMailbox } from "../../db/imap/imap-db"; import { getMailbox, getMailboxModseq, updateMailbox, updateMailboxModseq } from "../../db/imap/imap-db";
import { Attrs, AttrsWithEnvelope } from "../../interfaces/mail/attrs.interface"; import { Attrs, AttrsWithEnvelope } from "../../interfaces/mail/attrs.interface";
import logger from "../../system/Logger"; import logger from "../../system/Logger";
import RegisterMessageInApp from "../message/saveMessage"; import RegisterMessageInApp from "../message/saveMessage";
@ -33,29 +33,25 @@ export default class Mailbox {
async init() { async init() {
// get mailbox from the database // get mailbox from the database
this.box = (await getMailbox(this.id))[0]; this.box = (await getMailbox(this.id))[0];
const isReadOnly = false; const isReadOnly = false;
this.imap.openBox(this.boxName, isReadOnly, (err, box) => { this.imap.openBox(this.boxName, isReadOnly, (err, box) => {
if (err) logger.err(err); if (err) logger.err(err);
// sync only if has new messages // sync messages and flags
if (this.box.uidnext < box.uidnext) { this.initSync(box);
this.sync(this.box.uidnext, box.uidnext);
} else {
logger.log("Already up to date");
}
// wait for new mails // wait for new mails
this.imap.on("mail", (numNewMsgs: number) => { this.imap.on("mail", (numNewMsgs: number) => {
if (!this.syncing) { if (!this.syncing) {
// if not syncing restart a sync // if not syncing restart a sync
this.sync(this.box.uidnext, this.box.uidnext + numNewMsgs); this.syncMail(this.box.uidnext, this.box.uidnext + numNewMsgs);
} else { } else {
// else save number of message to sync latter // else save number of message to sync latter
this.msgToSync += numNewMsgs; this.msgToSync += numNewMsgs;
} }
}); });
// wait for flags update
this.imap.on("update", (seqno: number, info: ImapInfo) => { this.imap.on("update", (seqno: number, info: ImapInfo) => {
logger.log(`Update message ${info.uid} with ${info.flags}`); logger.log(`Update message ${info.uid} with ${info.flags}`);
const updateMsg = new updateMessage(info.uid, info.flags); const updateMsg = new updateMessage(info.uid, info.flags);
@ -64,7 +60,43 @@ export default class Mailbox {
}); });
} }
async sync(savedUid: number, currentUid: number) { async updateModseq(newModseq: number) {
updateMailboxModseq(this.id, newModseq).then(() => {
this.box.highestmodseq = newModseq;
});
}
async initSync(box: Box) {
// sync mail only if has new messages
if (this.box.uidnext < box.uidnext) {
this.syncMail(this.box.uidnext, box.uidnext);
} else {
logger.log("Mail already up to date");
}
// sync flags
const lastModseq = (await getMailboxModseq(this.id))[0]?.modseq ?? 0;
if (box.highestmodseq > lastModseq) {
const fetchStream = this.imap.fetch("1:*", { bodies: "", modifiers: { changedsince: lastModseq } });
fetchStream.on("message", (message) => {
message.once("attributes", (attrs) => {
const updateMsg = new updateMessage(attrs.uid, attrs.flags);
updateMsg.updateFlags();
});
});
fetchStream.once("error", function (err) {
logger.err("Fetch error when syncing flags: " + err);
});
fetchStream.once("end", function () {
logger.log("Done fetching new flags");
});
} else {
logger.log("Flags already up to date")
}
this.updateModseq(box.highestmodseq);
}
async syncMail(savedUid: number, currentUid: number) {
this.syncing = true; this.syncing = true;
const promises: Promise<unknown>[] = []; const promises: Promise<unknown>[] = [];
const mails: Attrs[] = []; const mails: Attrs[] = [];
@ -113,7 +145,7 @@ export default class Mailbox {
this.box.uidnext += this.msgToSync; this.box.uidnext += this.msgToSync;
// reset value to allow to detect new incoming message while syncing // reset value to allow to detect new incoming message while syncing
this.msgToSync = 0; this.msgToSync = 0;
this.sync(currentUid, this.box.uidnext); this.syncMail(currentUid, this.box.uidnext);
} }
}); });
} }

View File

@ -12,7 +12,8 @@ export default class updateMessage {
} }
async updateFlags() { async updateFlags() {
const messageId = (await getMessageIdOnUid(this.uid))[0].message_id; const messageId = (await getMessageIdOnUid(this.uid))[0]?.message_id;
if (!messageId) return;
const currentFlags = await getFlags(this.uid); const currentFlags = await getFlags(this.uid);
const flagsToAdd = this.flags.filter((flag) => !currentFlags.find((f) => flag == f.flag_name)); const flagsToAdd = this.flags.filter((flag) => !currentFlags.find((f) => flag == f.flag_name));