Compare commits
2 Commits
c60041a1bb
...
b78064caeb
| Author | SHA1 | Date | |
|---|---|---|---|
|
b78064caeb
|
|||
|
40d743c8c2
|
@@ -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";
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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";
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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", () => {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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", () => {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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("======================");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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()}`,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user