@zaclinley/dsh-mailharbor 0.13.1 → 0.14.0
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/CHANGELOG.md +12 -0
- package/lib/client.js +23 -18
- package/lib/client.js.map +2 -2
- package/lib/index.js +83 -8
- package/lib/index.js.map +4 -4
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -128,14 +128,14 @@ function transportOptions(settings, credentials, security = {}) {
|
|
|
128
128
|
pool: false
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
|
-
function
|
|
131
|
+
function createTransport2(settings, credentials, security = {}) {
|
|
132
132
|
return nodemailer.createTransport(transportOptions(settings, credentials, security));
|
|
133
133
|
}
|
|
134
134
|
function recipientsOf(value) {
|
|
135
135
|
return value.split(/[,;]/).map((part) => addressOf(part)).filter((address) => isEmail(address));
|
|
136
136
|
}
|
|
137
137
|
async function verifySmtp(settings, credentials, security = {}) {
|
|
138
|
-
const transport =
|
|
138
|
+
const transport = createTransport2(settings, credentials, security);
|
|
139
139
|
try {
|
|
140
140
|
await transport.verify();
|
|
141
141
|
return { ok: true, host: settings.host, port: Number(settings.port) };
|
|
@@ -149,7 +149,7 @@ async function sendMail(settings, credentials, account, mail, security = {}) {
|
|
|
149
149
|
const to = recipientsOf(mail.to);
|
|
150
150
|
const cc = recipientsOf(mail.cc ?? "");
|
|
151
151
|
if (to.length === 0) throw new Error("\u6536\u4EF6\u4EBA\u4E0D\u80FD\u4E3A\u7A7A");
|
|
152
|
-
const transport =
|
|
152
|
+
const transport = createTransport2(settings, credentials, security);
|
|
153
153
|
try {
|
|
154
154
|
const info = await transport.sendMail({
|
|
155
155
|
from: settings.fromName === "" ? from : { name: settings.fromName, address: from },
|
|
@@ -572,6 +572,28 @@ var init_imap = __esm({
|
|
|
572
572
|
}
|
|
573
573
|
});
|
|
574
574
|
}
|
|
575
|
+
/** Append a message to the account's Drafts mailbox. */
|
|
576
|
+
async appendDraft(raw) {
|
|
577
|
+
const boxes = await this.mailboxes();
|
|
578
|
+
const box = boxes.find((item) => String(item.specialUse ?? "").toLowerCase() === "\\drafts") ?? boxes.find((item) => /drafts|草稿/i.test(item.name)) ?? boxes.find((item) => item.name.toLowerCase() === "drafts");
|
|
579
|
+
const path = box?.path ?? "Drafts";
|
|
580
|
+
return this.run(async (client) => {
|
|
581
|
+
const result = await client.append(path, raw, ["\\Draft"]);
|
|
582
|
+
if (result === false) throw new Error("\u670D\u52A1\u5668\u62D2\u7EDD\u4FDD\u5B58\u8349\u7A3F");
|
|
583
|
+
return { mailbox: path, uid: Number(result.uid) };
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
/** Delete one message (used when replacing a mirrored draft). */
|
|
587
|
+
async deleteMessage(mailbox, uid) {
|
|
588
|
+
return this.run(async (client) => {
|
|
589
|
+
const lock = await client.getMailboxLock(mailbox);
|
|
590
|
+
try {
|
|
591
|
+
await client.messageDelete(String(uid), { uid: true });
|
|
592
|
+
} finally {
|
|
593
|
+
lock.release();
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
}
|
|
575
597
|
/** Move one message to another mailbox (server-side). */
|
|
576
598
|
async move(mailbox, uid, target) {
|
|
577
599
|
return this.run(async (client) => {
|
|
@@ -1006,6 +1028,30 @@ function moveLocalMessage(file, id, mailbox) {
|
|
|
1006
1028
|
}
|
|
1007
1029
|
}
|
|
1008
1030
|
|
|
1031
|
+
// src/host/draft-mime.ts
|
|
1032
|
+
import { createTransport } from "nodemailer";
|
|
1033
|
+
async function draftMime(account, draft) {
|
|
1034
|
+
const transport = createTransport({ streamTransport: true, buffer: true, newline: "unix" });
|
|
1035
|
+
const from = account.smtp.fromName === "" ? account.email : `"${account.smtp.fromName}" <${account.email}>`;
|
|
1036
|
+
const info = await transport.sendMail({
|
|
1037
|
+
from,
|
|
1038
|
+
to: draft.to,
|
|
1039
|
+
cc: draft.cc,
|
|
1040
|
+
bcc: draft.bcc,
|
|
1041
|
+
subject: draft.subject,
|
|
1042
|
+
html: draft.html,
|
|
1043
|
+
attachments: draft.attachments.map((file) => ({
|
|
1044
|
+
filename: file.filename,
|
|
1045
|
+
contentType: file.contentType,
|
|
1046
|
+
content: Buffer.from(file.data, "base64")
|
|
1047
|
+
})),
|
|
1048
|
+
...draft.inReplyTo === void 0 ? {} : { inReplyTo: draft.inReplyTo },
|
|
1049
|
+
...draft.references === void 0 ? {} : { references: draft.references }
|
|
1050
|
+
});
|
|
1051
|
+
if (Buffer.isBuffer(info.message)) return info.message;
|
|
1052
|
+
return Buffer.from(info.message);
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1009
1055
|
// src/host/backup.ts
|
|
1010
1056
|
init_address();
|
|
1011
1057
|
import { createHash as createHash2 } from "node:crypto";
|
|
@@ -2747,7 +2793,7 @@ ${conversation.trim()}
|
|
|
2747
2793
|
}
|
|
2748
2794
|
|
|
2749
2795
|
// src/host/version.ts
|
|
2750
|
-
var VERSION = "0.
|
|
2796
|
+
var VERSION = "0.14.0";
|
|
2751
2797
|
|
|
2752
2798
|
// src/host/api.ts
|
|
2753
2799
|
init_smtp();
|
|
@@ -3031,13 +3077,40 @@ function buildRouter(runtime) {
|
|
|
3031
3077
|
router.post("/drafts", async ({ url, body }) => {
|
|
3032
3078
|
const { account } = accountFrom(runtime, url);
|
|
3033
3079
|
const payload = await body();
|
|
3034
|
-
const
|
|
3035
|
-
|
|
3080
|
+
const store = runtime.drafts(account);
|
|
3081
|
+
const draft = store.save(normalizeDraftInput(payload, account.id));
|
|
3082
|
+
if (payload.remote !== true) return { draft };
|
|
3083
|
+
if (!runtime.hasCredentials(account)) return { draft, syncError: "\u672A\u767B\u5F55\uFF0C\u8349\u7A3F\u4EC5\u4FDD\u5B58\u5728\u672C\u5730" };
|
|
3084
|
+
try {
|
|
3085
|
+
const raw = await draftMime(account, draft);
|
|
3086
|
+
const provider = runtime.provider(account);
|
|
3087
|
+
const appended = await provider.appendDraft(raw);
|
|
3088
|
+
if (draft.remoteUid !== void 0 && draft.remoteMailbox !== void 0) {
|
|
3089
|
+
try {
|
|
3090
|
+
await provider.deleteMessage(draft.remoteMailbox, draft.remoteUid);
|
|
3091
|
+
} catch {
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
const updated = store.save({ ...draft, remoteUid: appended.uid, remoteMailbox: appended.mailbox });
|
|
3095
|
+
return { draft: updated };
|
|
3096
|
+
} catch (error) {
|
|
3097
|
+
return {
|
|
3098
|
+
draft,
|
|
3099
|
+
syncError: error instanceof Error ? error.message : String(error)
|
|
3100
|
+
};
|
|
3101
|
+
}
|
|
3036
3102
|
});
|
|
3037
|
-
router.delete("/drafts/:id", ({ url, params }) => {
|
|
3103
|
+
router.delete("/drafts/:id", async ({ url, params }) => {
|
|
3038
3104
|
const { account } = accountFrom(runtime, url);
|
|
3105
|
+
const draft = runtime.drafts(account).get(params.id ?? "");
|
|
3039
3106
|
const removed = runtime.drafts(account).remove(params.id ?? "");
|
|
3040
3107
|
if (!removed) throw new HttpError(404, "\u8349\u7A3F\u4E0D\u5B58\u5728");
|
|
3108
|
+
if (draft?.remoteUid !== void 0 && draft.remoteMailbox !== void 0 && runtime.hasCredentials(account)) {
|
|
3109
|
+
try {
|
|
3110
|
+
await runtime.provider(account).deleteMessage(draft.remoteMailbox, draft.remoteUid);
|
|
3111
|
+
} catch {
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3041
3114
|
return { ok: true };
|
|
3042
3115
|
});
|
|
3043
3116
|
router.post("/send", async ({ url, body }) => {
|
|
@@ -4685,7 +4758,9 @@ var DraftStore = class {
|
|
|
4685
4758
|
data: text(file.data)
|
|
4686
4759
|
})) : [],
|
|
4687
4760
|
inReplyTo: text(input.inReplyTo),
|
|
4688
|
-
references: text(input.references)
|
|
4761
|
+
references: text(input.references),
|
|
4762
|
+
...input.remoteUid === void 0 ? existing?.remoteUid === void 0 ? {} : { remoteUid: existing.remoteUid } : { remoteUid: Number(input.remoteUid) },
|
|
4763
|
+
...input.remoteMailbox === void 0 ? existing?.remoteMailbox === void 0 ? {} : { remoteMailbox: existing.remoteMailbox } : { remoteMailbox: text(input.remoteMailbox) }
|
|
4689
4764
|
};
|
|
4690
4765
|
if (existing !== void 0) {
|
|
4691
4766
|
Object.assign(existing, fields, { updatedAt: now2 });
|