E-Mail Lesestatus und Ordnerhierarchie synchronisieren
KI-AGENT: Synchronisiert Gelesen/Ungelesen mit IMAP, gleicht vorhandene Nachrichten-Flags beim Sync ab und zeigt verschachtelte IMAP-Ordner unter ihren Elternordnern an.
This commit is contained in:
@@ -301,6 +301,25 @@ export function emailSyncService(server: FastifyInstance) {
|
||||
return saved
|
||||
}
|
||||
|
||||
const updateCachedMessageFlags = async (
|
||||
mailboxId: string,
|
||||
uid: number,
|
||||
flags: string[],
|
||||
) => {
|
||||
await server.db
|
||||
.update(emailMessages)
|
||||
.set({
|
||||
flags,
|
||||
seen: flags.includes("\\Seen"),
|
||||
flagged: flags.includes("\\Flagged"),
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(and(
|
||||
eq(emailMessages.mailboxId, mailboxId),
|
||||
eq(emailMessages.uid, uid),
|
||||
))
|
||||
}
|
||||
|
||||
const syncMailboxMessages = async (
|
||||
account: MailAccountConnection,
|
||||
client: ImapFlow,
|
||||
@@ -323,9 +342,23 @@ export function emailSyncService(server: FastifyInstance) {
|
||||
const newUids = allUids
|
||||
.filter((uid: number) => !state?.highestUid || uid > state.highestUid)
|
||||
.slice(-limit)
|
||||
const flagSyncUids = allUids.slice(-limit)
|
||||
|
||||
highestUid = Math.max(state?.highestUid || 0, ...newUids, 0)
|
||||
|
||||
if (flagSyncUids.length) {
|
||||
for await (const message of client.fetch(flagSyncUids, {
|
||||
uid: true,
|
||||
flags: true,
|
||||
}, { uid: true })) {
|
||||
await updateCachedMessageFlags(
|
||||
mailbox.id,
|
||||
Number(message.uid),
|
||||
flagsFromMessage(message.flags),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (newUids.length) {
|
||||
for await (const message of client.fetch(newUids, {
|
||||
uid: true,
|
||||
@@ -385,6 +418,67 @@ export function emailSyncService(server: FastifyInstance) {
|
||||
}
|
||||
}
|
||||
|
||||
const setMessageSeen = async (
|
||||
tenantId: number,
|
||||
userId: string,
|
||||
messageId: string,
|
||||
seen: boolean,
|
||||
) => {
|
||||
const rows = await server.db
|
||||
.select()
|
||||
.from(emailMessages)
|
||||
.where(and(
|
||||
eq(emailMessages.tenantId, tenantId),
|
||||
eq(emailMessages.userId, userId),
|
||||
eq(emailMessages.id, messageId),
|
||||
))
|
||||
.limit(1)
|
||||
|
||||
const message = rows[0]
|
||||
if (!message) return null
|
||||
|
||||
const account = await getAccount(tenantId, userId, message.accountId)
|
||||
if (!account) {
|
||||
throw new Error("E-Mail Konto nicht gefunden")
|
||||
}
|
||||
|
||||
const client = createClient(account)
|
||||
await client.connect()
|
||||
|
||||
try {
|
||||
const lock = await client.getMailboxLock(message.mailboxPath)
|
||||
try {
|
||||
await client.mailboxOpen(message.mailboxPath)
|
||||
if (seen) {
|
||||
await client.messageFlagsAdd(message.uid, ["\\Seen"], { uid: true })
|
||||
} else {
|
||||
await client.messageFlagsRemove(message.uid, ["\\Seen"], { uid: true })
|
||||
}
|
||||
} finally {
|
||||
lock.release()
|
||||
}
|
||||
} finally {
|
||||
await client.logout().catch(() => client.close())
|
||||
}
|
||||
|
||||
const currentFlags = Array.isArray(message.flags) ? message.flags : []
|
||||
const nextFlags = seen
|
||||
? Array.from(new Set([...currentFlags, "\\Seen"]))
|
||||
: currentFlags.filter((flag) => flag !== "\\Seen")
|
||||
|
||||
const [updated] = await server.db
|
||||
.update(emailMessages)
|
||||
.set({
|
||||
flags: nextFlags,
|
||||
seen,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(emailMessages.id, messageId))
|
||||
.returning()
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
const listMailboxes = async (tenantId: number, userId: string, accountId: string) => {
|
||||
return await server.db
|
||||
.select()
|
||||
@@ -451,5 +545,6 @@ export function emailSyncService(server: FastifyInstance) {
|
||||
listMailboxes,
|
||||
listMessages,
|
||||
getMessage,
|
||||
setMessageSeen,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user