Skip to content

[add] Lark Email API based on Node-Mailer 8#57

Merged
TechQuery merged 2 commits intomainfrom
TechQuery/issue56
Feb 26, 2026
Merged

[add] Lark Email API based on Node-Mailer 8#57
TechQuery merged 2 commits intomainfrom
TechQuery/issue56

Conversation

@TechQuery
Copy link
Member

@TechQuery TechQuery commented Feb 25, 2026

PR-57 PR-57 PR-57 Powered by Pull Request Badge

  1. [add] Lark Email API based on Node-Mailer 8 (resolve 基于 SMTP 协议实现“群发飞书邮件”接口 #56)
  2. [migrate] upgrade GitHub actions to latest Vercel CLI & Lark chat bot
  3. [optimize] update Upstream packages

Summary by CodeRabbit

  • 新功能
    • 添加了SMTP邮件服务配置支持,包含邮件服务器地址、端口(默认465)、用户名与密码等可配置项。
    • 新增邮件发送API端点,允许系统通过配置的SMTP服务发送邮件消息。

[optimize] update Upstream packages
@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

📝 Walkthrough

Walkthrough

扩展环境变量以支持 SMTP(SMTP_HOST、SMTP_PORT、SMTP_USER、SMTP_PASSWORD),并新增一个 Next.js Pages API 路由 pages/api/Lark/mail/[address]/message.ts,使用 Nodemailer 基于 SMTP 发送邮件,禁用默认 body 解析,并使用 safeAPI + verifyJWT 中间件。

Changes

Cohort / File(s) Summary
环境配置扩展
models/configuration.ts
导出新增 SMTP 环境变量:SMTP_HOSTSMTP_PORT(默认 465)、SMTP_USERSMTP_PASSWORD,以供邮件发送模块使用。
邮件 API 路由实现
pages/api/Lark/mail/[address]/message.ts
新增 Pages Router API 端点;禁用 Next.js 默认 body 解析;使用 nodemailer 基于配置创建 SMTP transporter;路由读取请求体(Mail.Options)、强制 fromSMTP_USER 并发送邮件;应用 safeAPIverifyJWT 中间件,导出 config 与默认 handler。

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant NextAPI as Next.js API (with safeAPI & verifyJWT)
  participant Handler
  participant Transporter as Nodemailer Transporter
  participant SMTP as SMTP Server

  Client->>NextAPI: POST /api/Lark/mail/:address/message (raw body)
  NextAPI->>NextAPI: verifyJWT / safeAPI 中间件
  NextAPI->>Handler: forward request
  Handler->>Transporter: transporter.sendMail(mailOptions with from=SMTP_USER)
  Transporter->>SMTP: SMTP transaction (AUTH, MAIL FROM, RCPT TO, DATA)
  SMTP-->>Transporter: 2xx success / error
  Transporter-->>Handler: send result
  Handler-->>Client: 200 OK / error response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

feature

Poem

SMTP 风起邮件行,
Nodemailer 携钥匙,
API 一端验真容,
群发轻启送平安,
代码静候上线时 ✉️

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR标题清晰准确地描述了主要变更——添加基于Node-Mailer 8的Lark邮件API实现,与代码变更相符。
Linked Issues check ✅ Passed PR实现了#56的核心需求:在pages/api/Lark/mail/[address]/message.ts创建基于SMTP协议的邮件API,使用Nodemailer库,参考了指定的参考档案。
Out of Scope Changes check ✅ Passed 所有代码变更(配置导出扩展和新API端点)均与#56的SMTP邮件API实现需求相关,范围内。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch TechQuery/issue56

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@dosubot
Copy link

dosubot bot commented Feb 25, 2026

Documentation Updates

2 document(s) were updated by changes in this PR:

Backend Framework and API Design
View Changes
@@ -128,11 +128,12 @@
 
 #### Lark Email API
 
-The system includes an email sending API at `/api/Lark/mail/[address]/message` that uses nodemailer (v8) to send emails through an SMTP server. This endpoint is built using Next.js API routes with `next-ssr-middleware` and accepts POST requests with nodemailer `Mail.Options` in the request body.
+The system includes an email sending API at `/api/Lark/mail/[address]/message` that uses nodemailer 8.0.1 to send emails through an SMTP server. This endpoint is built using Next.js API routes with `next-ssr-middleware` and accepts POST requests with nodemailer `Mail.Options` in the request body.
 
 The email API:
 
 - Uses `createTransport` to configure an SMTP connection with credentials from environment variables.
+- Secured with JWT verification middleware (`verifyJWT`).
 - Automatically sets the "from" address to the configured `SMTP_USER`.
 - Sends emails through the configured SMTP server using `sendMail`.
 - Returns the result of the send operation to the caller.
@@ -148,13 +149,13 @@
     auth: { user: SMTP_USER, pass: SMTP_PASSWORD },
   });
 
-router.post('/bot/message', async context => {
+router.post('/bot/message', safeAPI, verifyJWT, async context => {
   const input = Reflect.get(context.request, 'body') as Mail.Options;
   context.body = await transporter.sendMail({ ...input, from: SMTP_USER });
 });
 ```
 
-The API configuration disables Next.js's built-in body parser (`bodyParser: false`) to allow `next-ssr-middleware` to handle request parsing with Koa-compatible middleware.
+The endpoint integrates with Next.js API routes using `next-ssr-middleware` utilities (`createKoaRouter` and `withKoaRouter`). The API configuration disables Next.js's built-in body parser (`bodyParser: false`) to allow `next-ssr-middleware` to handle request parsing with Koa-compatible middleware.
 
 ### Environment and Configuration
 
CI/CD and Deployment Automation
View Changes
@@ -1,25 +1,54 @@
 ## Continuous Integration and Deployment Automation
 
 ### GitHub Actions CI/CD Pipeline
-The project uses GitHub Actions workflows for continuous integration and deployment. Workflow files are located in the `.github/workflows` directory and include jobs for building, testing, and deploying the application. For example, the `main.yml` workflow is triggered on every push to any branch. It checks out the code, runs build steps, and, if configured, deploys to Vercel. Environment secrets such as `VERCEL_TOKEN`, `VERCEL_ORG_ID`, and `VERCEL_PROJECT_ID` are required for deployment steps. The workflow uses the `amondnet/vercel-action@v25` action to deploy to Vercel, and production deployments are triggered when the branch is `main` [(source)](https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/blob/f5df98635f7a8bdab44cded44633ecbc3145a73f/.github/workflows/main.yml).
+The project uses GitHub Actions workflows for continuous integration and deployment. Workflow files are located in the `.github/workflows` directory and include jobs for building, testing, and deploying the application. For example, the `main.yml` workflow is triggered on every push to any branch. It checks out the code, runs build steps, and, if configured, deploys to Vercel. Environment secrets such as `VERCEL_TOKEN`, `VERCEL_ORG_ID`, and `VERCEL_PROJECT_ID` are required for deployment steps. The workflow uses the Vercel CLI to deploy, and production deployments are triggered when the branch is `main` [(source)](https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/blob/f5df98635f7a8bdab44cded44633ecbc3145a73f/.github/workflows/main.yml).
 
 Other repositories may include additional workflows such as `deploy-production.yml`, `init-template.yml`, and `publish-type.yml` for specialized deployment and initialization tasks [(source)](https://github.com/Open-Source-Bazaar/ActivityHub-service/pull/8).
 
 ### Vercel Deployment
-Vercel deployment is fully automated via GitHub Actions. The deployment step uses the `amondnet/vercel-action@v25` action, passing in the necessary secrets for authentication. The deployment is triggered on every push, but the `--prod` flag is only used when deploying from the `main` branch. After deployment, the workflow outputs a preview URL, which is included in notifications [(source)](https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/blob/f5df98635f7a8bdab44cded44633ecbc3145a73f/.github/workflows/main.yml).
+Vercel deployment is fully automated via GitHub Actions using the Vercel CLI. The deployment step first sets up Node.js 24, then installs the Vercel CLI globally with `npm install vercel -g`. The deployment script uses `set -euo pipefail` for error handling and safety. For production deployments (from the `main` branch), it runs `vercel -t "$VERCEL_TOKEN" --prod`, while preview deployments use `vercel -t "$VERCEL_TOKEN"`. After deployment, the script parses the deployment URL from the output using `grep` and regex, then uses the `vercel inspect` command with JSON output to extract deployment details. The preview URL is set as a GitHub Actions output for use in subsequent steps, such as notifications [(source)](https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/blob/f5df98635f7a8bdab44cded44633ecbc3145a73f/.github/workflows/main.yml).
 
 Example deployment step:
 ```yaml
+- uses: actions/setup-node@v6
+  if: ${{ env.VERCEL_TOKEN && env.VERCEL_ORG_ID && env.VERCEL_PROJECT_ID }}
+  with:
+    node-version: 24
+
 - name: Deploy to Vercel
   id: vercel-deployment
-  uses: amondnet/vercel-action@v25
-  with:
-    vercel-token: ${{ secrets.VERCEL_TOKEN }}
-    github-token: ${{ secrets.GITHUB_TOKEN }}
-    vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
-    vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
-    working-directory: ./
-    vercel-args: ${{ github.ref == 'refs/heads/main' && ' --prod' || '' }}
+  if: ${{ env.VERCEL_TOKEN && env.VERCEL_ORG_ID && env.VERCEL_PROJECT_ID }}
+  shell: bash
+  run: |
+    set -euo pipefail
+
+    npm install vercel -g
+
+    if [[ "$GITHUB_REF" == 'refs/heads/main' ]]; then
+      DeployOutput=$(vercel -t "$VERCEL_TOKEN" --prod)
+    else
+      DeployOutput=$(vercel -t "$VERCEL_TOKEN")
+    fi
+    echo "$DeployOutput"
+
+    ParsedURL=$(echo "$DeployOutput" | grep -Eo 'https://[^[:space:]]*\.vercel\.app' | tail -n 1)
+
+    if [[ -z "$ParsedURL" ]]; then
+      echo "Failed to parse Vercel URL from deploy output"
+      exit 1
+    fi
+    vercel inspect "$ParsedURL" -t "$VERCEL_TOKEN" -F json > vercel-inspect.json
+
+    InspectURL=$(jq -r '.url // empty' vercel-inspect.json)
+
+    if [[ -z "$InspectURL" ]]; then
+      echo "Failed to parse inspect url from vercel-inspect.json"
+      exit 1
+    fi
+    if [[ "$InspectURL" != http* ]]; then
+      InspectURL="https://$InspectURL"
+    fi
+    echo "preview-url=$InspectURL" >> "$GITHUB_OUTPUT"
 ```
 
 ### Docker Compose Usage
@@ -82,7 +111,7 @@
 Supporting scripts in `.github/scripts` handle parsing, distribution, and aggregation logic. These workflows automate the reward process, ensuring transparency and reducing manual effort for both contributors and maintainers.
 
 ### Lark Notifications
-Lark (Feishu) notifications are integrated via GitHub Actions. After deployment, a notification is sent using the `foxundermoon/feishu-action@v2` action, with the webhook URL stored in the `LARK_CHATBOT_HOOK_URL` secret. The notification includes repository URL, branch, author, and preview deployment URL. Additionally, a dedicated workflow (`Lark-notification.yml`) sends notifications to Lark on various GitHub events (push, issues, pull requests, discussions, comments, releases). This workflow serializes event messages using a Deno script and sends them to Lark via webhook [(source)](https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/blob/f5df98635f7a8bdab44cded44633ecbc3145a73f/.github/workflows/main.yml), [(source)](https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/blob/f5df98635f7a8bdab44cded44633ecbc3145a73f/.github/workflows/Lark-notification.yml).
+Lark (Feishu) notifications are integrated via GitHub Actions. After deployment, a notification is sent using the `foxundermoon/feishu-action@v2` action, with the webhook URL stored in the `LARK_CHATBOT_HOOK_URL` secret. The notification uses an interactive card format (`msg_type: interactive`) with schema version 2.0, featuring a header with a blue template and markdown-formatted content in the body. The notification includes repository URL, branch, author, and preview deployment URL. Additionally, a dedicated workflow (`Lark-notification.yml`) sends notifications to Lark on various GitHub events (push, issues, pull requests, discussions, comments, releases). This workflow serializes event messages using a Deno script and sends them to Lark via webhook [(source)](https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/blob/f5df98635f7a8bdab44cded44633ecbc3145a73f/.github/workflows/main.yml), [(source)](https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/blob/f5df98635f7a8bdab44cded44633ecbc3145a73f/.github/workflows/Lark-notification.yml).
 
 Example notification step:
 ```yaml
@@ -90,32 +119,24 @@
   uses: foxundermoon/feishu-action@v2
   with:
     url: ${{ secrets.LARK_CHATBOT_HOOK_URL }}
-    msg_type: post
+    msg_type: interactive
     content: |
-      post:
-        zh_cn:
-          title: Vercel 预览环境
-          content:
-            - - tag: text
-                text: Git 仓库:
-              - tag: a
-                text: ${{ github.server_url }}/${{ github.repository }}
-                href: ${{ github.server_url }}/${{ github.repository }}
-            - - tag: text
-                text: 代码分支:
-              - tag: a
-                text: ${{ github.ref }}
-                href: ${{ github.server_url }}/${{ github.repository }}/tree/${{ github.ref_name }}
-            - - tag: text
-                text: 提交作者:
-              - tag: a
-                text: ${{ github.actor }}
-                href: ${{ github.server_url }}/${{ github.actor }}
-            - - tag: text
-                text: 预览链接:
-              - tag: a
-                text: ${{ steps.vercel-deployment.outputs.preview-url }}
-                href: ${{ steps.vercel-deployment.outputs.preview-url }}
+      schema: "2.0"
+      config:
+        wide_screen_mode: true
+      header:
+        title:
+          tag: plain_text
+          content: Vercel 部署通知
+        template: blue
+      body:
+        elements:
+          - tag: markdown
+            content: |
+              **Git 仓库:** [${{ github.server_url }}/${{ github.repository }}](${{ github.server_url }}/${{ github.repository }})
+              **代码分支:** [${{ github.ref }}](${{ github.server_url }}/${{ github.repository }}/tree/${{ github.ref_name }})
+              **提交作者:** [${{ github.actor }}](${{ github.server_url }}/${{ github.actor }})
+              **预览链接:** [${{ steps.vercel-deployment.outputs.preview-url }}](${{ steps.vercel-deployment.outputs.preview-url }})
 ```
 
 This setup ensures robust automation, observability, and standardization across the development and deployment lifecycle.

How did I do? Any feedback?  Join Discord

@TechQuery TechQuery added the enhancement Some improvements label Feb 25, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
models/configuration.ts (1)

9-17: SMTP_PORT 默认值类型不一致,建议改为字符串 '465'

process.env 的所有值类型为 string | undefined,而默认值 465number,导致 TypeScript 推断 SMTP_PORT 的类型为 string | number。虽然消费方用 +SMTP_PORT 强转可以正常工作,但类型层面不够干净。

♻️ 建议修改
-  SMTP_PORT = 465,
+  SMTP_PORT = '465',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@models/configuration.ts` around lines 9 - 17, The default for SMTP_PORT is
currently the number 465, causing SMTP_PORT to be typed as string | number;
change the default to the string '465' in the destructuring of process.env
(i.e., export const { SMTP_PORT = '465', ... } = process.env) so SMTP_PORT
remains string | undefined and type-consistent with other env vars; update any
call sites that relied on a numeric type (e.g., places using +SMTP_PORT or
Number(SMTP_PORT)) if needed.
pages/api/Lark/mail/[address]/message.ts (1)

23-23: Reflect.get 读取 body 属性不够直观

Reflect.get(context.request, 'body') 等价于 (context.request as any).body,建议用更具可读性的写法,或直接通过类型扩展声明 body 属性。

♻️ 建议修改
-  const input = Reflect.get(context.request, 'body') as Mail.Options;
+  const input = (context.request as typeof context.request & { body: Mail.Options }).body;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pages/api/Lark/mail/`[address]/message.ts at line 23, 将使用
Reflect.get(context.request, 'body') 读取 body 的写法改为更直观的访问或通过类型扩展声明 body:直接使用
(context.request as { body: Mail.Options }).body 或 在请求上下文的类型定义中为 request 添加
body: Mail.Options,然后用 context.request.body 赋值给 const input。替换掉 Reflect.get 并确保
input 保持 Mail.Options 类型以提高可读性和类型安全(定位符:const
input、Reflect.get、context.request、Mail.Options)。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pages/api/Lark/mail/`[address]/message.ts:
- Around line 22-26: 接口 router.post('/bot/message' ) 当前未做认证,允许任何请求调用
transporter.sendMail 导致 open relay 风险;在该路由前加入与项目其它 API(如 pages/api/signature
路由相同)的认证中间件(例如 JWT 验证中间件或校验函数),在处理函数内验证请求身份并拒绝未授权请求,再调用 transporter.sendMail({
...input, from: SMTP_USER });,确保未通过认证时返回 401/403 并且不触发发送邮件。
- Around line 22-26: The route handler router.post('/bot/message', async context
=> { ... }) ignores the dynamic [address] param—extract the dynamic address from
the request (e.g. context.params.address or context.request.params.address
depending on the router) and enforce it as the recipient: if address is missing
return a 400, if input.to exists either validate it matches the address or
override/merge with the address, then call transporter.sendMail with the
resolved to address (preserve SMTP_USER for from). Update the handler to
validate/normalize input (Mail.Options) against the extracted address and return
an error when there is a mismatch or when the address is required but absent.
- Line 25: Wrap the asynchronous call to transporter.sendMail in a try/catch
around the use of transporter.sendMail(...) so failures are caught and not
allowed to bubble up; on error, log the exception and set an appropriate HTTP
error response on context (do not leak stack traces). Also ensure the from
header is never undefined by providing a fallback when building the mail input
(use SMTP_USER || '<no-reply@example.com>' or derive from config) before calling
transporter.sendMail, and validate input to avoid sending invalid headers;
update references: transporter.sendMail, SMTP_USER, and context.body
accordingly.

---

Nitpick comments:
In `@models/configuration.ts`:
- Around line 9-17: The default for SMTP_PORT is currently the number 465,
causing SMTP_PORT to be typed as string | number; change the default to the
string '465' in the destructuring of process.env (i.e., export const { SMTP_PORT
= '465', ... } = process.env) so SMTP_PORT remains string | undefined and
type-consistent with other env vars; update any call sites that relied on a
numeric type (e.g., places using +SMTP_PORT or Number(SMTP_PORT)) if needed.

In `@pages/api/Lark/mail/`[address]/message.ts:
- Line 23: 将使用 Reflect.get(context.request, 'body') 读取 body 的写法改为更直观的访问或通过类型扩展声明
body:直接使用 (context.request as { body: Mail.Options }).body 或 在请求上下文的类型定义中为
request 添加 body: Mail.Options,然后用 context.request.body 赋值给 const input。替换掉
Reflect.get 并确保 input 保持 Mail.Options 类型以提高可读性和类型安全(定位符:const
input、Reflect.get、context.request、Mail.Options)。

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fef93c5 and 0b0118c.

⛔ Files ignored due to path filters (3)
  • .env is excluded by none and included by none
  • package.json is excluded by none and included by none
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !pnpm-lock.yaml and included by none
📒 Files selected for processing (2)
  • models/configuration.ts
  • pages/api/Lark/mail/[address]/message.ts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
pages/api/Lark/mail/[address]/message.ts (1)

23-26: ⚠️ Potential issue | 🟠 Major

动态路由参数 [address] 未被使用,导致接口语义偏离路径设计

Line 23-26 当前完全忽略 context.params.address,收件人仍由请求体控制。这样会让 /api/Lark/mail/[address]/message 这个路径约束失效,也偏离 issue #56 的接口语义。建议将路径参数作为最终 to(或严格校验二者一致)。

建议修改
 router.post('/bot/message', safeAPI, verifyJWT, async context => {
-  const input = Reflect.get(context.request, 'body') as Mail.Options;
+  const { address } = context.params ?? {};
+  if (!address) context.throw(400, 'address is required');
+  const input = context.request?.body as Mail.Options;

-  context.body = await transporter.sendMail({ ...input, from: SMTP_USER });
+  if (input?.to && input.to !== address) {
+    context.throw(400, '`to` must match path param address');
+  }
+  context.body = await transporter.sendMail({
+    ...input,
+    to: address,
+    from: SMTP_USER,
+  });
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pages/api/Lark/mail/`[address]/message.ts around lines 23 - 26, The route
handler registered with router.post('/bot/message') currently ignores the
dynamic route param context.params.address and uses the request body for
recipients; update the handler (in the async function that obtains input via
Reflect.get(context.request, 'body') and calls transporter.sendMail) to either
(a) override/assign input.to = context.params.address so the path-determined
address is the final recipient, or (b) validate that input.to (or
input.recipients) matches context.params.address and reject the request if they
differ; ensure the change references the existing Mail.Options input, preserves
SMTP_USER as the from address, and returns an appropriate error when validation
fails.
🧹 Nitpick comments (2)
pages/api/Lark/mail/[address]/message.ts (2)

24-24: 避免使用 Reflect.get 读取请求体,改为可选链与直接属性访问

Line 24 用 Reflect.get(context.request, 'body') 会弱化可读性和类型约束。这里可直接使用 context.request?.body
As per coding guidelines, "Use optional chaining and modern ECMAScript features".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pages/api/Lark/mail/`[address]/message.ts at line 24, Replace the Reflect.get
call used to populate input — currently written as Reflect.get(context.request,
'body') — with direct optional-chained property access (context.request?.body)
and assign it to the existing input variable typed as Mail.Options; ensure you
handle the possible undefined case (e.g., validate or provide a fallback/early
return) where context.request or body may be missing so downstream code using
input remains safe.

3-3: Mail 仅作类型使用时应改为 type-only import

Line 3 的 Mail 仅用于 Mail.Options 类型标注,建议改为 import type,避免不必要的运行时加载内部模块路径,降低对 Nodemailer 内部结构的耦合风险。

建议修改
-import Mail from 'nodemailer/lib/mailer';
+import type Mail from 'nodemailer/lib/mailer';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pages/api/Lark/mail/`[address]/message.ts at line 3, The import of Mail is
only used for types (Mail.Options); change the current value import "import Mail
from 'nodemailer/lib/mailer'" to a type-only import (e.g., "import type Mail
from 'nodemailer/lib/mailer'") so the Mail symbol is erased at runtime and
avoids pulling nodemailer internals; update any usages like Mail.Options in the
file to continue using the type and run type-checking to ensure no runtime
dependencies remain.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@pages/api/Lark/mail/`[address]/message.ts:
- Around line 23-26: The route handler registered with
router.post('/bot/message') currently ignores the dynamic route param
context.params.address and uses the request body for recipients; update the
handler (in the async function that obtains input via
Reflect.get(context.request, 'body') and calls transporter.sendMail) to either
(a) override/assign input.to = context.params.address so the path-determined
address is the final recipient, or (b) validate that input.to (or
input.recipients) matches context.params.address and reject the request if they
differ; ensure the change references the existing Mail.Options input, preserves
SMTP_USER as the from address, and returns an appropriate error when validation
fails.

---

Nitpick comments:
In `@pages/api/Lark/mail/`[address]/message.ts:
- Line 24: Replace the Reflect.get call used to populate input — currently
written as Reflect.get(context.request, 'body') — with direct optional-chained
property access (context.request?.body) and assign it to the existing input
variable typed as Mail.Options; ensure you handle the possible undefined case
(e.g., validate or provide a fallback/early return) where context.request or
body may be missing so downstream code using input remains safe.
- Line 3: The import of Mail is only used for types (Mail.Options); change the
current value import "import Mail from 'nodemailer/lib/mailer'" to a type-only
import (e.g., "import type Mail from 'nodemailer/lib/mailer'") so the Mail
symbol is erased at runtime and avoids pulling nodemailer internals; update any
usages like Mail.Options in the file to continue using the type and run
type-checking to ensure no runtime dependencies remain.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b0118c and a1ef353.

⛔ Files ignored due to path filters (2)
  • .github/workflows/Lark-notification.yml is excluded by none and included by none
  • .github/workflows/main.yml is excluded by none and included by none
📒 Files selected for processing (1)
  • pages/api/Lark/mail/[address]/message.ts

@TechQuery TechQuery merged commit d84dddc into main Feb 26, 2026
4 checks passed
@TechQuery TechQuery deleted the TechQuery/issue56 branch February 26, 2026 19:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Some improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

基于 SMTP 协议实现“群发飞书邮件”接口

1 participant