@novacraft-engineering/mailbox 0.4.27 → 0.4.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/api/mail/inbox/counts/route.ts +13 -3
- package/app/api/mail/send/route.ts +2 -1
- package/app/api/mail/threads/route.ts +4 -3
- package/app/layout.tsx +19 -4
- package/app/mail/InboxFilters.tsx +154 -0
- package/app/mail/page.module.css +170 -0
- package/app/mail/page.tsx +67 -25
- package/app/manifest.ts +2 -0
- package/app/robots.ts +2 -0
- package/lib/brand.client.ts +46 -25
- package/lib/color-scheme.test.ts +51 -0
- package/lib/color-scheme.ts +233 -0
- package/lib/emails/index.ts +11 -10
- package/lib/inbox-filters.test.ts +54 -0
- package/lib/inbox-filters.ts +111 -0
- package/lib/mail-provider.ts +3 -1
- package/lib/mailbox.ts +57 -17
- package/lib/receive.ts +35 -3
- package/package.json +1 -1
package/lib/receive.ts
CHANGED
|
@@ -2,8 +2,9 @@ import { BRAND, ADDRESS_DOMAINS } from '@/lib/brand'
|
|
|
2
2
|
import { sendPush } from '@/lib/push'
|
|
3
3
|
import { stripOwnPixel } from '@/lib/email-html'
|
|
4
4
|
import { FORWARDING_ENABLED, FORWARD_RECIPIENTS, MAIL_DOMAIN } from '@/lib/dev-auth'
|
|
5
|
-
import { ADDRESS_ALIASES, appendInbound, isDmarcAggregateReport, judgeMessage, noteSender, senderStanding, senderDomainOf, getAccountByAddress, inboundExists, recordContact, recordSentMeta, repairInbound } from '@/lib/mailbox'
|
|
6
|
-
import {
|
|
5
|
+
import { ADDRESS_ALIASES, appendInbound, classifyAddressed, isDmarcAggregateReport, judgeMessage, noteSender, senderStanding, senderDomainOf, getAccountByAddress, inboundExists, inboxFiltersFor, recordContact, recordSentMessage, recordSentMeta, repairInbound } from '@/lib/mailbox'
|
|
6
|
+
import { matchesInboxFilters } from '@/lib/inbox-filters'
|
|
7
|
+
import { archiveAddress, sendMail } from '@/lib/mail-provider'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Where the receiving API lives. The Resend SDK reads RESEND_BASE_URL for sending, so a
|
|
@@ -379,11 +380,29 @@ export async function ingestReceived(
|
|
|
379
380
|
return { owner: inbound.owner, subject: inbound.subject, from: inbound.from }
|
|
380
381
|
}
|
|
381
382
|
|
|
383
|
+
if (isArchiveCopy(inbound.owner, inbound.to, inbound.cc)) {
|
|
384
|
+
await recordSentMessage({
|
|
385
|
+
id: emailId,
|
|
386
|
+
from: inbound.from,
|
|
387
|
+
to: inbound.to,
|
|
388
|
+
cc: inbound.cc,
|
|
389
|
+
bcc: inbound.bcc,
|
|
390
|
+
replyTo: inbound.replyTo,
|
|
391
|
+
subject: inbound.subject,
|
|
392
|
+
html: inbound.html,
|
|
393
|
+
text: inbound.text,
|
|
394
|
+
createdAt: inbound.receivedAt,
|
|
395
|
+
attachments: inbound.attachments,
|
|
396
|
+
})
|
|
397
|
+
await recordSentMeta(emailId, inbound.owner, true)
|
|
398
|
+
return { owner: inbound.owner, subject: inbound.subject, from: inbound.from }
|
|
399
|
+
}
|
|
400
|
+
|
|
382
401
|
await appendInbound(inbound)
|
|
383
402
|
await noteSender(owner, senderDomain, 'received').catch(() => {})
|
|
384
403
|
const sender = parseSender(inbound.from)
|
|
385
404
|
await recordContact(sender.email, sender.name)
|
|
386
|
-
if (!isDmarcAggregateReport(inbound.subject)) {
|
|
405
|
+
if (!isDmarcAggregateReport(inbound.subject) && !(await filteredOut(inbound))) {
|
|
387
406
|
await sendPush(inbound.owner, {
|
|
388
407
|
title: sender.name || sender.email || 'New mail',
|
|
389
408
|
body: inbound.subject,
|
|
@@ -411,6 +430,19 @@ export async function ingestReceived(
|
|
|
411
430
|
return { owner: inbound.owner, subject: inbound.subject, from: inbound.from }
|
|
412
431
|
}
|
|
413
432
|
|
|
433
|
+
async function filteredOut(inbound: { owner: string | null; from: string; subject: string; text: string | null }): Promise<boolean> {
|
|
434
|
+
const login = inbound.owner ? (await getAccountByAddress(inbound.owner).catch(() => null))?.email : null
|
|
435
|
+
if (!login) return false
|
|
436
|
+
const filters = await inboxFiltersFor(login).catch(() => [])
|
|
437
|
+
return matchesInboxFilters(filters, { senders: [inbound.from], subject: inbound.subject, text: inbound.text })
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/** A copy of our own outgoing mail, BCC'd to the archive seat: it belongs in that seat's Sent, not its inbox. */
|
|
441
|
+
export function isArchiveCopy(owner: string | null | undefined, to: string[], cc: string[]): boolean {
|
|
442
|
+
const archive = archiveAddress()
|
|
443
|
+
return Boolean(archive) && (owner ?? '').trim().toLowerCase() === archive && classifyAddressed(owner, to, cc) === 'other'
|
|
444
|
+
}
|
|
445
|
+
|
|
414
446
|
export type ReceivedSummary = { id: string; from: string; to: string[]; subject: string; createdAt: string }
|
|
415
447
|
|
|
416
448
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@novacraft-engineering/mailbox",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.29",
|
|
4
4
|
"description": "A shared webmail app: one codebase, one deployment per mailbox. Threads, a rich composer with signatures, attachments on S3-compatible storage, sharing links, full-text search, web push and PWA install.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"webmail",
|