Extract emails into separate package with clean interface

- Create packages/emails/ with EmailClient interface abstraction
- Wrap Postmark ServerClient in adapter for clean typing
- Add createLoggingEmailClient for dev mode (logs to console)
- Split email templates into individual files with full test coverage
- Update api-server to use new package via context injection
- Remove EMAIL_DEV_MODE - now uses POSTMARK_API_KEY presence
- Delete apps/api-server/src/utils/email.ts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
igm
2026-01-12 15:45:40 +08:00
parent f9f1dc7403
commit c2b815dd6a
34 changed files with 1626 additions and 442 deletions

View File

@@ -10,6 +10,7 @@ import type {
import type { Kysely } from "kysely";
import type { RPInfo } from "../../utils/webauthn.js";
import { ORPCError } from "@orpc/server";
import { sendVerificationEmail } from "@reviq/emails";
import { verifyRegistrationResponse } from "@simplewebauthn/server";
import {
COOKIE_NAMES,
@@ -21,7 +22,6 @@ import {
generateExpiry,
generateSecureBase58Token,
} from "../../utils/crypto.js";
import { sendVerificationEmail } from "../../utils/email.js";
import { getGeoInfo, getUserAgent } from "../../utils/geo.js";
import { hashPassword, validatePassword } from "../../utils/password.js";
import { createSession } from "../../utils/session.js";
@@ -300,8 +300,15 @@ export const signup = os.auth.signup.handler(async ({ input, context }) => {
})
.execute();
// Send verification email (stubbed)
await sendVerificationEmail(email, verificationToken);
// Send verification email
await sendVerificationEmail({
client: context.email.client,
fromAddress: context.email.fromAddress,
baseUrl: context.email.baseUrl,
email,
token: verificationToken,
expiryHours: 24,
});
return { success: true };
});