@ beesolve/电子邮件服务-dashboard
用于取景的 SvelteKit 仪表板 @beesolve/email-service 提供状态——部署在CloudFront后方的AWS Lambda上,并有电子邮件编码认证. 它运行着自己的事件-摄入量最大的Lambda,将电子邮件发送生命周期(从EventBridge)投放到自己的DynamoDB表格中,因此UI拥有持久,可查询的信息+状态+汇总数据.
[尖叫声] 前1.0——不稳定的API. 这个软件包是
0.x活动 发展。 打破的改变可以降落在任何0.x释放无主 版本凸起。 选中一个精确的版本并在升级前审查更改日志 . 一旦API稳定,包裹到达1.0.0,它跟随语义 版本和此通知被删除 .
建筑
email-service ──► SES ──► EventBridge (beesolve.email.api / aws.ses)
│
▼
event-ingest Lambda (SQS-buffered)
│ parse → Messages.upsert (transactional)
▼
┌────────────┐ ┌──────────────┐ ┌──────────┐
│ CloudFront │ ──► │ Lambda (SSR) │ ──► │ DynamoDB │ (dashboard's own projection)
└────────────┘ └──────────────┘ └──────────┘
│ ▲
└── /auth/* ──► AuthGateway │ OTP emails via
(authorizer) └─ @beesolve/email-service
仪表板显示的
- 概览 ——全球汇总计数器(收到/发送/交付/回放/投诉/拒绝/失败)并得出总计.
- 信件 ——按月份,最先发送,标出;每条消息的状态徽章.
- 信件细节 ——每个接收人交付时间表,变异状态细节(弹出诊断,投诉反馈,拒绝理由,交付延迟),以及点播 "视图信息正文" 动作(通过
email.getMessage(requestId),从未存储 —— 仅在来源时可用EmailLog记录仍然存在). - 收件人 ——所有有收受者收受计数器的收受者,以及收受者的信息历史.
- 电子邮件编码登录(无密码)和管理员用户管理.
打开和点击故意超出范围.
先决条件
npm install \
@beesolve/email-service-dashboard \
@beesolve/email-service \
@beesolve/auth-service \
aws-cdk \
aws-cdk-lib \
constructs
@beesolve/email-service——发送交易邮件并发布此仪表板项目生命周期事件@beesolve/auth-service——与会话管理层进行电子邮件码认证
CDK 设置
只有 ./cdk 构造导出。 构造一个 AuthGateway 首先,把它交给仪表板:
import { AuthGateway } from "@beesolve/auth-service/cdk";
import { EmailServiceDashboard } from "@beesolve/email-service-dashboard/cdk";
import { App, Stack } from "aws-cdk-lib";
import { EventBus } from "aws-cdk-lib/aws-events";
const frontendUri = process.env.FRONTEND_URI;
if (frontendUri == null) throw new Error("FRONTEND_URI environment variable is required");
const app = new App();
const stack = new Stack(app, "EmailDashboardStack", {
env: { account: "123456789012", region: "eu-central-1" },
});
const authEventBus = new EventBus(stack, "AuthEventBus");
const auth = new AuthGateway(stack, "Auth", {
stage: "prod",
frontendUri,
allowSignUp: false,
authorizerCache: "disabled",
// Give the dashboard's own auth its own bus so its sign-in OTP events don't
// collide with the project's real auth on the default bus. Optional — omit to
// share the default bus and rely on `appId` for source-based isolation instead.
eventBusArn: authEventBus.eventBusArn,
});
const dashboard = new EmailServiceDashboard(stack, "Dashboard", {
auth,
emailSender: {
name: "Email Dashboard",
emailAddress: "[email protected]",
},
// isProd?: boolean — enables PITR on the table
// removalPolicy?: RemovalPolicy
});
这个 EmailServiceDashboard 构建条款:
- a 迪纳摩DB
TableV2(交付状况预测),一个反向GSI - svelteKit app on Lambda (通过
kit-on-lambda在CloudFront后面 有Auth cookie执行和一个/auth/*行为 - a 一个
@beesolve/email-serviceEmails检察官办公室邮件的构造 - 消费者Lambda + EventBridge规则 发送检察官办公室的签名邮件
auth.eventBus过滤在auth.eventSource(所以它跟随任何公共汽车/appId授权部署用途) - 以 SQS 队列 + DLQ 为前方
source: ["beesolve.email.api", "aws.ses"]在默认总线上,将交付生命周期投放到表格中
设计两辆公交车. 仪表盘上的自动签注可以活在 专用总线(通过)
auth是因为eventBusArn),但交付状态事件 显示 总是到帐户上default公共汽车-SES配置集 事件目的地只能瞄准默认总线。 两个内部规则是: 因此受约束:auth.eventBus,电子邮件事件规则为default因此,仪表板没有eventBusName道具 (英语).
数据层( 远程函数)
仪表板通过SvelteKit远程函数读取和写入其数据,而不是
+page.server.ts load编号 :actions定义 src/lib/remote/*.remote.ts, (中文(简体) ),
按域(overview, (中文(简体) ), messages, (中文(简体) ), recipients, (中文(简体) ), users, (中文(简体) ), setup:
query(...)用于读取(概览、信件列表、信件细节、收件人列表、, 收件人详情). URL 过滤器等year编号 :month编号 :cursor以 瓦利博特验证的论据.form(...)用于用户提交的突变(邀请用户,编辑用户,完整设置).command(...)对事件处理员引用的程序变异。 这个 点播信件正文( e)loadMessageBody是一个command因此,S3只在 用户触发它,在细节中永远不会query。 。 。 .
每个函数通过 getRequestEvent().locals (单位:千美元)locals.user, (中文(简体) ),
locals.session, (中文(简体) ), locals.services页:1 hooks.server.ts 众生. 行政警卫
生活在远程函数内。 变异后, 相关的 query(...).refresh()
需要单飞UI更新.
错误映射集中到 src/lib/server/httpErrors.ts数字 :
toRemoteError(error, { redirectOnUnauthorized }) 将域错误转换为 SvelteKit
error()编号 :redirect(),以及 requireUser(user) 守备本届会议。 打电话
toRemoteError(...) 直接内置 a catch - 从来没有 throw 它的结果.
远程函数由 experimental.remoteFunctions 财务报告和审定财务报表
compilerOptions.experimental.async 内 vite.config.ts。唯一保留的服务器
负载是 +layout.server.ts (供应 user 和安置的转机
setup/+page.server.ts 重定向后卫, 和登录通用/排出负载 .
环境变量在 src/env.ts 通过 defineEnvVars 进口,
已打入并验证,从 $app/env/private。 。 。 .
第一次部署
SvelteKit大楼需要 FRONTEND_URI 环境变量(CloudFront URL),它直到第一次部署后才存在:
- 设定
FRONTEND_URI到占位符 URL( 例如).https://placeholder.cloudfront.net(中文(简体) ) - 部署 — 从 CDK 输出注意 CloudFront URL
- 更新
FRONTEND_URI使用真实的 URL 并重新部署
地方发展
bun install
cp .env.local.example .env.local # then fill in real values
aws sso login # (or export AWS_PROFILE) so SDK clients can reach AWS
bun run dev
bun run dev 网址:http://localhost:5173。 这个 dev 脚本运行 bun --env-file=.env.local vite dev这样 .env.local 装入 process.env 在服务器启动前。 然后,表 vars 被宣布为 src/env.ts (单位:千美元)defineEnvVars)并读取、打印和验证,从 $app/env/private 内 hooks.server.ts; 工作空间 SDK 客户端仍然从 process.env 在导入时间.
假冒会议
当地没有Lambda授权人,所以 @beesolve/auth-service 自动输入 dev 会话。 设定 DEV_USER_EMAIL 内 .env.local 改为 真实 用户的电子邮件, 因此应用程序解决了用户的角色, 并显示他们看到的用户界面 。 这是由守护 import.meta.env.DEV 绝不列入已部署的捆绑.
必需的 vars
DASHBOARD_TABLE_NAME——预测表名称(由部署中的构造注入)DASHBOARD_REVERSE_INDEX——倒置GSI名称(被部署中的构造所注入)DASHBOARD_REQUESTS_BUCKET· 桶内有按需请求信件的机构DEV_USER_EMAIL——仅本地开发;会话被假冒的用户
表格/桶瓦公布于 src/env.ts 并经过 defineEnvVars
第一次从 $app/env/private,所以他们必须在场的应用程序
开始—— build 脚本集 build-placeholder 完全出于这个原因的数值
(见下文).
部署说明(克特-上-上-上-上-上-上-下-下-下-下-下-下-下-下-下-下)
- 外部化
@beesolve/lambda-fetch-api苏维埃社会主义共和国. 必须设置 Vite 配置ssr.external: ["@beesolve/lambda-fetch-api"]没有它,Vite就会产生重复AsyncLocalStoragesDK的处理器上下文在运行时丢失. - 使用默认窗体动作 . SvelteKit 命名为窗体动作使用
?/name在 URL 中; 属性/在查询字符串中被 CloudFront 拒绝/误入正题。 使用默认( 未命名) 窗体动作 . - 占位符 构建环境 . 被封为
src/env.ts由defineEnvVars何时$app/env/private在安全部门改革分析期间首先导入,所以build脚本用品DASHBOARD_TABLE_NAME编号 :DASHBOARD_REVERSE_INDEX编号 :DASHBOARD_REQUESTS_BUCKET占位符让建筑完成.