mirror of
https://github.com/34892002/edgeKey.git
synced 2026-05-06 23:33:10 +08:00
feat: smtp
This commit is contained in:
3
bun.lock
3
bun.lock
@@ -24,6 +24,7 @@
|
||||
"vike-photon": "^0.1.24",
|
||||
"vike-vue": "^0.9.11",
|
||||
"vue": "^3.5.30",
|
||||
"worker-mailer": "^1.2.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.2.1",
|
||||
@@ -1093,6 +1094,8 @@
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"worker-mailer": ["worker-mailer@1.2.1", "", {}, "sha512-gS2ei/mrpRqNs+AHmqxhT6vFPwCLw2qnz5ShmyGD0ULaU0Q9hxnFAcx9jhAip/MnD6+MjgnQu6hQQgA8mlOkVA=="],
|
||||
|
||||
"workerd": ["workerd@1.20260401.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260401.1", "@cloudflare/workerd-darwin-arm64": "1.20260401.1", "@cloudflare/workerd-linux-64": "1.20260401.1", "@cloudflare/workerd-linux-arm64": "1.20260401.1", "@cloudflare/workerd-windows-64": "1.20260401.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-mUYCd+ohaWJWF5nhDzxugWaAD/DM8Dw0ze3B7bu8JaA7S70+XQJXcvcvwE8C4qGcxSdCyqjsrFzqxKubECDwzg=="],
|
||||
|
||||
"wrangler": ["wrangler@4.80.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.2", "@cloudflare/unenv-preset": "2.16.0", "blake3-wasm": "2.1.5", "esbuild": "0.27.3", "miniflare": "4.20260401.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260401.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260401.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-2ZKF7uPeOZy65BGk3YfvqBCPo/xH1MrAlMmH9mVP+tCNBrTUMnwOHSj1HrZHgR8LttkAqhko0fGz+I4ax1rzyQ=="],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { badRequestError, externalServiceError } from "../../lib/app-error";
|
||||
import type { EmailApiConfigValue, EmailProviderAdapter, EmailSendInput } from "./types";
|
||||
import type { EmailApiConfigValue, EmailProviderAdapter, EmailSendInput, EmailSmtpConfigValue } from "./types";
|
||||
|
||||
function normalizeBaseUrl(value: string) {
|
||||
return value.replace(/\/+$/, "");
|
||||
@@ -102,6 +102,39 @@ async function sendMailjetEmail(config: EmailApiConfigValue, input: EmailSendInp
|
||||
};
|
||||
}
|
||||
|
||||
export function createSmtpEmailAdapter(config: EmailSmtpConfigValue): EmailProviderAdapter {
|
||||
return {
|
||||
async send(input) {
|
||||
if (!config.smtpHost || !config.smtpPort) {
|
||||
throw badRequestError("SMTP 配置不完整", "SMTP_CONFIG_INCOMPLETE");
|
||||
}
|
||||
|
||||
const { WorkerMailer } = await import("worker-mailer");
|
||||
await WorkerMailer.send(
|
||||
{
|
||||
host: config.smtpHost,
|
||||
port: config.smtpPort,
|
||||
secure: config.smtpSecure ?? false,
|
||||
credentials: config.smtpUsername
|
||||
? { username: config.smtpUsername, password: config.smtpPassword ?? "" }
|
||||
: undefined,
|
||||
authType: config.smtpAuthType ?? "plain",
|
||||
},
|
||||
{
|
||||
from: { email: config.fromEmail, name: config.fromName },
|
||||
to: input.toEmail,
|
||||
reply: input.replyTo || config.replyTo || undefined,
|
||||
subject: input.subject,
|
||||
text: input.text,
|
||||
html: input.html,
|
||||
},
|
||||
);
|
||||
|
||||
return {};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createApiEmailAdapter(config: EmailApiConfigValue): EmailProviderAdapter {
|
||||
return {
|
||||
async send(input) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { logger } from "../../lib/logger";
|
||||
import { validateEmailConfigInput, validateEmailTemplateInput, validateTestEmailInput } from "../../lib/validators/email";
|
||||
import { getAdminContext, logAdminOperation } from "../auth/service";
|
||||
import { getSiteSetting } from "../site/service";
|
||||
import { createApiEmailAdapter } from "./provider";
|
||||
import { createApiEmailAdapter, createSmtpEmailAdapter } from "./provider";
|
||||
import {
|
||||
activateEmailConfigById,
|
||||
createEmailConfigRecord,
|
||||
@@ -270,7 +270,19 @@ async function sendByChannel(config: EmailConfigValue, input: {
|
||||
}
|
||||
|
||||
if (config.provider === "SMTP") {
|
||||
throw badRequestError("当前版本暂未在 Worker 中实现 SMTP 直连发送,请先使用 API 分类", "SMTP_NOT_IMPLEMENTED");
|
||||
const adapter = createSmtpEmailAdapter(config);
|
||||
const result = await adapter.send({
|
||||
toEmail: input.toEmail,
|
||||
subject: input.subject,
|
||||
text: input.text,
|
||||
html: input.html,
|
||||
replyTo: config.replyTo,
|
||||
});
|
||||
return {
|
||||
provider: config.provider,
|
||||
apiProvider: undefined,
|
||||
messageId: result.messageId,
|
||||
};
|
||||
}
|
||||
|
||||
throw badRequestError("当前版本暂未接入 CloudFlare Email Send binding 的运行时发送,请先使用 API 分类", "CLOUDFLARE_NOT_IMPLEMENTED");
|
||||
|
||||
@@ -41,6 +41,7 @@ export interface EmailSmtpConfigValue extends EmailPushFlags {
|
||||
smtpSecure?: boolean;
|
||||
smtpUsername?: string;
|
||||
smtpPassword?: string;
|
||||
smtpAuthType?: "plain" | "login" | "cram-md5";
|
||||
}
|
||||
|
||||
export interface EmailCloudflareConfigValue extends EmailPushFlags {
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
"vike": "^0.4.255",
|
||||
"vike-photon": "^0.1.24",
|
||||
"vike-vue": "^0.9.11",
|
||||
"vue": "^3.5.30"
|
||||
"vue": "^3.5.30",
|
||||
"worker-mailer": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.2.1",
|
||||
|
||||
@@ -254,6 +254,13 @@
|
||||
<label class="flex flex-col gap-1.5">
|
||||
<span class="label-text font-medium">SMTP 密码</span>
|
||||
<input v-model="configForm.smtpPassword" class="input input-bordered w-full" />
|
||||
</label><label class="flex flex-col gap-1.5">
|
||||
<span class="label-text font-medium">认证方式</span>
|
||||
<select v-model="configForm.smtpAuthType" class="select select-bordered w-full">
|
||||
<option value="plain">PLAIN</option>
|
||||
<option value="login">LOGIN</option>
|
||||
<option value="cram-md5">CRAM-MD5</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<label class="label cursor-pointer justify-start gap-3 w-fit">
|
||||
@@ -574,6 +581,7 @@ interface ConfigFormState {
|
||||
smtpSecure: boolean;
|
||||
smtpUsername: string;
|
||||
smtpPassword: string;
|
||||
smtpAuthType: "plain" | "login" | "cram-md5";
|
||||
// Cloudflare
|
||||
cloudflareBindingName: string;
|
||||
cloudflareDestinationAddress: string;
|
||||
@@ -597,6 +605,7 @@ function createEmptyForm(): ConfigFormState {
|
||||
smtpSecure: false,
|
||||
smtpUsername: "",
|
||||
smtpPassword: "",
|
||||
smtpAuthType: "plain" as "plain" | "login" | "cram-md5",
|
||||
cloudflareBindingName: "",
|
||||
cloudflareDestinationAddress: "",
|
||||
cloudflareAllowedText: "",
|
||||
@@ -631,6 +640,7 @@ function openEditDialog(item: MailboxItem) {
|
||||
smtpSecure: (item as any).smtpSecure || false,
|
||||
smtpUsername: (item as any).smtpUsername || "",
|
||||
smtpPassword: (item as any).smtpPassword || "",
|
||||
smtpAuthType: (item as any).smtpAuthType || "plain",
|
||||
cloudflareBindingName: (item as any).cloudflareBindingName || "",
|
||||
cloudflareDestinationAddress: (item as any).cloudflareDestinationAddress || "",
|
||||
cloudflareAllowedText: Array.isArray((item as any).cloudflareAllowedDestinationAddresses)
|
||||
@@ -770,6 +780,7 @@ async function handleSaveConfig() {
|
||||
payload.smtpSecure = configForm.smtpSecure;
|
||||
payload.smtpUsername = configForm.smtpUsername;
|
||||
payload.smtpPassword = configForm.smtpPassword;
|
||||
payload.smtpAuthType = configForm.smtpAuthType;
|
||||
} else {
|
||||
payload.cloudflareBindingName = configForm.cloudflareBindingName;
|
||||
payload.cloudflareDestinationAddress = configForm.cloudflareDestinationAddress;
|
||||
|
||||
Reference in New Issue
Block a user