Compare commits

..

2 Commits

Author SHA1 Message Date
igm
b78064caeb Merge branch 'email-cleanup'
Some checks failed
CI / ci (push) Has been cancelled
2026-01-12 17:23:04 +08:00
igm
40d743c8c2 Apply linter formatting fixes to emails package
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 17:12:06 +08:00
16 changed files with 60 additions and 61 deletions

View File

@@ -2,18 +2,15 @@ import type { APIContext } from "./context.js";
import { LoggingHandlerPlugin } from "@orpc/experimental-pino"; import { LoggingHandlerPlugin } from "@orpc/experimental-pino";
import { RPCHandler } from "@orpc/server/fetch"; import { RPCHandler } from "@orpc/server/fetch";
import { createDb } from "@reviq/db"; import { createDb } from "@reviq/db";
import { import { createLoggingEmailClient, createPostmarkClient } from "@reviq/emails";
createLoggingEmailClient,
createPostmarkClient,
} from "@reviq/emails";
import pino from "pino"; import pino from "pino";
import { import {
BASE_URL, BASE_URL,
DEFAULT_PORT, DEFAULT_PORT,
DEFAULT_RP_NAME, DEFAULT_RP_NAME,
EMAIL_FROM, EMAIL_FROM,
POSTMARK_API_KEY,
getAllowedOrigins, getAllowedOrigins,
POSTMARK_API_KEY,
} from "./constants.js"; } from "./constants.js";
import { router } from "./router.js"; import { router } from "./router.js";

View File

@@ -1,9 +1,9 @@
import { ServerClient } from "postmark";
import type { import type {
ClientSendParams, ClientSendParams,
ClientSendResult, ClientSendResult,
EmailClient, EmailClient,
} from "./types.js"; } from "./types.js";
import { ServerClient } from "postmark";
export function createPostmarkClient(apiKey: string): EmailClient { export function createPostmarkClient(apiKey: string): EmailClient {
if (!apiKey) { if (!apiKey) {

View File

@@ -1,39 +1,36 @@
export type {
LoginConfirmationEmailParams,
SendLoginConfirmationEmailParams,
} from "./login-confirmation.js";
export type {
OrgInviteEmailParams,
SendOrgInviteEmailParams,
} from "./org-invite.js";
export type {
PasswordResetEmailParams,
SendPasswordResetEmailParams,
} from "./password-reset.js";
export type {
SendVerificationEmailParams,
VerificationEmailParams,
} from "./verification.js";
export { export {
buildLoginConfirmationEmailHtml, buildLoginConfirmationEmailHtml,
buildLoginConfirmationEmailText, buildLoginConfirmationEmailText,
sendLoginConfirmationEmail, sendLoginConfirmationEmail,
} from "./login-confirmation.js"; } from "./login-confirmation.js";
export type {
LoginConfirmationEmailParams,
SendLoginConfirmationEmailParams,
} from "./login-confirmation.js";
export { export {
buildOrgInviteEmailHtml, buildOrgInviteEmailHtml,
buildOrgInviteEmailText, buildOrgInviteEmailText,
sendOrgInviteEmail, sendOrgInviteEmail,
} from "./org-invite.js"; } from "./org-invite.js";
export type {
OrgInviteEmailParams,
SendOrgInviteEmailParams,
} from "./org-invite.js";
export { export {
buildPasswordResetEmailHtml, buildPasswordResetEmailHtml,
buildPasswordResetEmailText, buildPasswordResetEmailText,
sendPasswordResetEmail, sendPasswordResetEmail,
} from "./password-reset.js"; } from "./password-reset.js";
export type {
PasswordResetEmailParams,
SendPasswordResetEmailParams,
} from "./password-reset.js";
export { export {
buildVerificationEmailHtml, buildVerificationEmailHtml,
buildVerificationEmailText, buildVerificationEmailText,
sendVerificationEmail, sendVerificationEmail,
} from "./verification.js"; } from "./verification.js";
export type {
SendVerificationEmailParams,
VerificationEmailParams,
} from "./verification.js";

View File

@@ -1,5 +1,5 @@
import { describe, expect, it, mock, beforeEach } from "bun:test";
import type { EmailClient } from "../types.js"; import type { EmailClient } from "../types.js";
import { beforeEach, describe, expect, it, mock } from "bun:test";
import { import {
buildLoginConfirmationEmailHtml, buildLoginConfirmationEmailHtml,
buildLoginConfirmationEmailText, buildLoginConfirmationEmailText,

View File

@@ -1,3 +1,4 @@
import type { EmailClient, EmailResult } from "../types.js";
import { buildUrl, formatExpiryMinutes } from "../helpers.js"; import { buildUrl, formatExpiryMinutes } from "../helpers.js";
import { sendEmail } from "../send.js"; import { sendEmail } from "../send.js";
import { import {
@@ -8,7 +9,6 @@ import {
headingStyles, headingStyles,
paragraphStyles, paragraphStyles,
} from "../styles.js"; } from "../styles.js";
import type { EmailClient, EmailResult } from "../types.js";
export interface LoginConfirmationEmailParams { export interface LoginConfirmationEmailParams {
confirmUrl: string; confirmUrl: string;

View File

@@ -1,5 +1,5 @@
import { describe, expect, it, mock, beforeEach } from "bun:test";
import type { EmailClient } from "../types.js"; import type { EmailClient } from "../types.js";
import { beforeEach, describe, expect, it, mock } from "bun:test";
import { import {
buildOrgInviteEmailHtml, buildOrgInviteEmailHtml,
buildOrgInviteEmailText, buildOrgInviteEmailText,
@@ -93,9 +93,7 @@ describe("buildOrgInviteEmailText", () => {
it("should include the invite URL", () => { it("should include the invite URL", () => {
const text = buildOrgInviteEmailText(params); const text = buildOrgInviteEmailText(params);
expect(text).toContain( expect(text).toContain("https://example.com/invite/accept?token=invite123");
"https://example.com/invite/accept?token=invite123",
);
}); });
it("should include the organization name", () => { it("should include the organization name", () => {

View File

@@ -1,4 +1,5 @@
import type { OrgRole } from "@reviq/db-schema"; import type { OrgRole } from "@reviq/db-schema";
import type { EmailClient, EmailResult } from "../types.js";
import { import {
buildUrl, buildUrl,
escapeHtml, escapeHtml,
@@ -15,7 +16,6 @@ import {
headingStyles, headingStyles,
paragraphStyles, paragraphStyles,
} from "../styles.js"; } from "../styles.js";
import type { EmailClient, EmailResult } from "../types.js";
export interface OrgInviteEmailParams { export interface OrgInviteEmailParams {
email: string; email: string;

View File

@@ -1,5 +1,5 @@
import { describe, expect, it, mock, beforeEach } from "bun:test";
import type { EmailClient } from "../types.js"; import type { EmailClient } from "../types.js";
import { beforeEach, describe, expect, it, mock } from "bun:test";
import { import {
buildPasswordResetEmailHtml, buildPasswordResetEmailHtml,
buildPasswordResetEmailText, buildPasswordResetEmailText,

View File

@@ -1,3 +1,4 @@
import type { EmailClient, EmailResult } from "../types.js";
import { buildUrl, formatExpiryHours } from "../helpers.js"; import { buildUrl, formatExpiryHours } from "../helpers.js";
import { sendEmail } from "../send.js"; import { sendEmail } from "../send.js";
import { import {
@@ -8,7 +9,6 @@ import {
headingStyles, headingStyles,
paragraphStyles, paragraphStyles,
} from "../styles.js"; } from "../styles.js";
import type { EmailClient, EmailResult } from "../types.js";
export interface PasswordResetEmailParams { export interface PasswordResetEmailParams {
resetUrl: string; resetUrl: string;

View File

@@ -1,5 +1,5 @@
import { describe, expect, it, mock, beforeEach } from "bun:test";
import type { EmailClient } from "../types.js"; import type { EmailClient } from "../types.js";
import { beforeEach, describe, expect, it, mock } from "bun:test";
import { import {
buildVerificationEmailHtml, buildVerificationEmailHtml,
buildVerificationEmailText, buildVerificationEmailText,
@@ -14,7 +14,9 @@ describe("buildVerificationEmailHtml", () => {
it("should include the verify URL", () => { it("should include the verify URL", () => {
const html = buildVerificationEmailHtml(params); const html = buildVerificationEmailHtml(params);
expect(html).toContain('href="https://example.com/auth/verify?token=abc123"'); expect(html).toContain(
'href="https://example.com/auth/verify?token=abc123"',
);
}); });
it("should include the expiry time", () => { it("should include the expiry time", () => {

View File

@@ -1,3 +1,4 @@
import type { EmailClient, EmailResult } from "../types.js";
import { buildUrl, formatExpiryHours } from "../helpers.js"; import { buildUrl, formatExpiryHours } from "../helpers.js";
import { sendEmail } from "../send.js"; import { sendEmail } from "../send.js";
import { import {
@@ -8,7 +9,6 @@ import {
headingStyles, headingStyles,
paragraphStyles, paragraphStyles,
} from "../styles.js"; } from "../styles.js";
import type { EmailClient, EmailResult } from "../types.js";
export interface VerificationEmailParams { export interface VerificationEmailParams {
verifyUrl: string; verifyUrl: string;

View File

@@ -1,5 +1,5 @@
import { DurationFormat } from "@formatjs/intl-durationformat";
import type { OrgRole } from "@reviq/db-schema"; import type { OrgRole } from "@reviq/db-schema";
import { DurationFormat } from "@formatjs/intl-durationformat";
export function buildUrl( export function buildUrl(
baseUrl: string, baseUrl: string,

View File

@@ -1,21 +1,4 @@
// Client factories // Client factories
export { createPostmarkClient } from "./client.js";
export { createLoggingEmailClient } from "./logging-client.js";
// Core types
export type { EmailClient, EmailResult } from "./types.js";
// Base send function
export { sendEmail } from "./send.js";
export type { SendEmailParams } from "./send.js";
// Email-specific send functions
export {
sendLoginConfirmationEmail,
sendOrgInviteEmail,
sendPasswordResetEmail,
sendVerificationEmail,
} from "./emails/index.js";
// Email param types // Email param types
export type { export type {
@@ -24,3 +7,17 @@ export type {
SendPasswordResetEmailParams, SendPasswordResetEmailParams,
SendVerificationEmailParams, SendVerificationEmailParams,
} from "./emails/index.js"; } from "./emails/index.js";
export type { SendEmailParams } from "./send.js";
// Core types
export type { EmailClient, EmailResult } from "./types.js";
export { createPostmarkClient } from "./client.js";
// Email-specific send functions
export {
sendLoginConfirmationEmail,
sendOrgInviteEmail,
sendPasswordResetEmail,
sendVerificationEmail,
} from "./emails/index.js";
export { createLoggingEmailClient } from "./logging-client.js";
// Base send function
export { sendEmail } from "./send.js";

View File

@@ -1,4 +1,4 @@
import { describe, expect, it, mock, beforeEach, afterEach } from "bun:test"; import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test";
import { createLoggingEmailClient } from "./logging-client.js"; import { createLoggingEmailClient } from "./logging-client.js";
describe("createLoggingEmailClient", () => { describe("createLoggingEmailClient", () => {
@@ -30,9 +30,15 @@ describe("createLoggingEmailClient", () => {
expect(result.messageId).toMatch(/^dev-mode-\d+$/); expect(result.messageId).toMatch(/^dev-mode-\d+$/);
expect(logOutput).toContain("=== DEV MODE EMAIL ==="); expect(logOutput).toContain("=== DEV MODE EMAIL ===");
expect(logOutput.some((line) => line.includes("From: noreply@example.com"))).toBe(true); expect(
expect(logOutput.some((line) => line.includes("To: user@example.com"))).toBe(true); logOutput.some((line) => line.includes("From: noreply@example.com")),
expect(logOutput.some((line) => line.includes("Subject: Test Subject"))).toBe(true); ).toBe(true);
expect(
logOutput.some((line) => line.includes("To: user@example.com")),
).toBe(true);
expect(
logOutput.some((line) => line.includes("Subject: Test Subject")),
).toBe(true);
expect(logOutput.some((line) => line.includes("Hello"))).toBe(true); expect(logOutput.some((line) => line.includes("Hello"))).toBe(true);
expect(logOutput).toContain("======================"); expect(logOutput).toContain("======================");
}); });

View File

@@ -17,7 +17,9 @@ export function createLoggingEmailClient(): EmailClient {
console.log("======================"); console.log("======================");
messageIdCounter++; messageIdCounter++;
return Promise.resolve({ messageId: `dev-mode-${messageIdCounter.toString()}` }); return Promise.resolve({
messageId: `dev-mode-${messageIdCounter.toString()}`,
});
}, },
}; };
} }

View File

@@ -1,5 +1,5 @@
import { describe, expect, it, mock } from "bun:test";
import type { EmailClient } from "./types.js"; import type { EmailClient } from "./types.js";
import { describe, expect, it, mock } from "bun:test";
import { sendEmail } from "./send.js"; import { sendEmail } from "./send.js";
describe("sendEmail", () => { describe("sendEmail", () => {