diff --git a/.changeset/fix-cid-attachment-url.md b/.changeset/fix-cid-attachment-url.md
deleted file mode 100644
index aec7c6f..0000000
--- a/.changeset/fix-cid-attachment-url.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-'@maildev/smtp': patch
-'@maildev/api': patch
----
-
-Fix the URL that `replaceCidReferences` rewrites inline (CID) attachments to. It
-built `/email/:id/attachment/:file`, which matches no route: the attachment is
-served from `/api/email/:id/attachment/:file`, under the configured base path.
-The URL is now root-relative and includes both parts, so it resolves at the root
-and under a subpath such as `/mail`. A base path that resolves to the root (`/`)
-adds no prefix, so it can no longer produce a protocol-relative `//api/email/...`.
diff --git a/packages/api/src/server.ts b/packages/api/src/server.ts
index 671c616..b5887ad 100644
--- a/packages/api/src/server.ts
+++ b/packages/api/src/server.ts
@@ -457,11 +457,11 @@ export class APIServer extends EventEmitter {
`${apiPath}/email/:id/html`,
async (request, reply) => {
const { id } = request.params
+ const baseUrl = request.headers.host ?? ''
try {
if (this.smtp) {
- // No host: the HTML is rendered for a browser already on this origin
- const html = await this.smtp.getEmailHtml(id, { basePath })
+ const html = await this.smtp.getEmailHtml(id, { baseUrl })
if (!html) {
return reply.status(404).send({ error: 'Email has no HTML content' })
}
diff --git a/packages/smtp/src/__tests__/sanitize.test.ts b/packages/smtp/src/__tests__/sanitize.test.ts
index 015f580..3d94460 100644
--- a/packages/smtp/src/__tests__/sanitize.test.ts
+++ b/packages/smtp/src/__tests__/sanitize.test.ts
@@ -73,7 +73,7 @@ describe('replaceCidReferences', () => {
const result = replaceCidReferences(html, 'email1', attachments)
- expect(result).toContain('/api/email/email1/attachment/abc.png')
+ expect(result).toContain('/email/email1/attachment/abc.png')
expect(result).not.toContain('cid:')
})
@@ -85,7 +85,7 @@ describe('replaceCidReferences', () => {
const result = replaceCidReferences(html, 'email1', attachments)
- expect(result).toContain('/api/email/email1/attachment/abc.png')
+ expect(result).toContain('/email/email1/attachment/abc.png')
})
it('should include baseUrl when provided', () => {
@@ -96,44 +96,7 @@ describe('replaceCidReferences', () => {
const result = replaceCidReferences(html, 'email1', attachments, 'example.com')
- expect(result).toContain('//example.com/api/email/email1/attachment/abc.png')
- })
-
- it('should include basePath when MailDev is mounted under a subpath', () => {
- const html = '
'
- const attachments = [
- { contentId: 'image123', generatedFileName: 'abc.png' },
- ]
-
- const result = replaceCidReferences(html, 'email1', attachments, undefined, '/mail')
-
- expect(result).toContain('src="/mail/api/email/email1/attachment/abc.png"')
- })
-
- it('should normalize a basePath given with or without slashes', () => {
- const html = '
'
- const attachments = [
- { contentId: 'image123', generatedFileName: 'abc.png' },
- ]
-
- for (const basePath of ['mail', '/mail', '/mail/']) {
- const result = replaceCidReferences(html, 'email1', attachments, undefined, basePath)
- expect(result).toContain('src="/mail/api/email/email1/attachment/abc.png"')
- }
- })
-
- it('should treat basePath "/" as no prefix', () => {
- const html = '
'
- const attachments = [{ contentId: 'image123', generatedFileName: 'abc.png' }]
- const result = replaceCidReferences(html, 'email1', attachments, undefined, '/')
- expect(result).toContain('src="/api/email/email1/attachment/abc.png"')
- })
-
- it('should combine baseUrl and basePath', () => {
- const html = '
'
- const attachments = [{ contentId: 'image123', generatedFileName: 'abc.png' }]
- const result = replaceCidReferences(html, 'email1', attachments, 'example.com', '/mail')
- expect(result).toContain('src="//example.com/mail/api/email/email1/attachment/abc.png"')
+ expect(result).toContain('//example.com/email/email1/attachment/abc.png')
})
it('should replace multiple cid references', () => {
diff --git a/packages/smtp/src/sanitize.ts b/packages/smtp/src/sanitize.ts
index a50db3a..fa54a29 100644
--- a/packages/smtp/src/sanitize.ts
+++ b/packages/smtp/src/sanitize.ts
@@ -45,16 +45,14 @@ export function sanitizeHtml(html: string | undefined): string | undefined {
* @param html - HTML content with cid: references
* @param emailId - ID of the email
* @param attachments - Array of attachments with contentId
- * @param baseUrl - Optional host to prefix as `//host/...`; unset gives a root-relative URL
- * @param basePath - Optional pathname the API is mounted under, e.g. `/mail`
+ * @param baseUrl - Optional base URL for attachment URLs
* @returns HTML with cid: references replaced with actual URLs
*/
export function replaceCidReferences(
html: string,
emailId: string,
attachments: Array<{ contentId?: string; generatedFileName: string }>,
- baseUrl?: string,
- basePath?: string
+ baseUrl?: string
): string {
if (!attachments || attachments.length === 0) {
return html
@@ -74,7 +72,7 @@ export function replaceCidReferences(
'g'
)
- const url = buildAttachmentUrl(emailId, attachment.generatedFileName, baseUrl, basePath)
+ const url = buildAttachmentUrl(emailId, attachment.generatedFileName, baseUrl)
const replacement = `src="${url}"`
result = result.replace(regex, replacement)
@@ -84,19 +82,15 @@ export function replaceCidReferences(
}
/**
- * Build URL for an attachment. The API serves these from `${basePath}/api/email/...`,
- * so both parts belong here or the src 404s.
+ * Build URL for an attachment
*/
function buildAttachmentUrl(
emailId: string,
filename: string,
- baseUrl?: string,
- basePath?: string
+ baseUrl?: string
): string {
const base = baseUrl ? `//${baseUrl}` : ''
- const segments = basePath ? basePath.replace(/^\/+|\/+$/g, '') : ''
- const prefix = segments ? `/${segments}` : ''
- return `${base}${prefix}/api/email/${emailId}/attachment/${encodeURIComponent(filename)}`
+ return `${base}/email/${emailId}/attachment/${encodeURIComponent(filename)}`
}
/**
diff --git a/packages/smtp/src/server.ts b/packages/smtp/src/server.ts
index c933860..15f0cb5 100644
--- a/packages/smtp/src/server.ts
+++ b/packages/smtp/src/server.ts
@@ -258,7 +258,7 @@ export class SMTPServer extends EventEmitter {
// Replace CID references with actual URLs
if (email.attachments && email.attachments.length > 0) {
- html = replaceCidReferences(html, id, email.attachments, options.baseUrl, options.basePath)
+ html = replaceCidReferences(html, id, email.attachments, options.baseUrl)
}
return html
diff --git a/packages/smtp/src/types.ts b/packages/smtp/src/types.ts
index 1b81d6b..427e956 100644
--- a/packages/smtp/src/types.ts
+++ b/packages/smtp/src/types.ts
@@ -218,10 +218,8 @@ export interface RelayResult {
* HTML rendering options for email
*/
export interface HTMLRenderOptions {
- /** Host to prefix attachment URLs with; unset gives a root-relative URL */
+ /** Base URL for attachment URLs */
baseUrl?: string
- /** Pathname the API is mounted under, e.g. `/mail` */
- basePath?: string
}
/**