Upstream maildev/maildev state of the touched files before f6ee0b5f24
This commit is contained in:
1 parent
15ecda681f
commit
77bb28b844
6 files changed
+13
-69
No files matched your search
@@ -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/...`.
|
|
||||||
@@ -457,11 +457,11 @@ export class APIServer extends EventEmitter {
|
|||||||
`${apiPath}/email/:id/html`,
|
`${apiPath}/email/:id/html`,
|
||||||
async (request, reply) => {
|
async (request, reply) => {
|
||||||
const { id } = request.params
|
const { id } = request.params
|
||||||
|
const baseUrl = request.headers.host ?? ''
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.smtp) {
|
if (this.smtp) {
|
||||||
// No host: the HTML is rendered for a browser already on this origin
|
const html = await this.smtp.getEmailHtml(id, { baseUrl })
|
||||||
const html = await this.smtp.getEmailHtml(id, { basePath })
|
|
||||||
if (!html) {
|
if (!html) {
|
||||||
return reply.status(404).send({ error: 'Email has no HTML content' })
|
return reply.status(404).send({ error: 'Email has no HTML content' })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ describe('replaceCidReferences', () => {
|
|||||||
|
|
||||||
const result = replaceCidReferences(html, 'email1', attachments)
|
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:')
|
expect(result).not.toContain('cid:')
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ describe('replaceCidReferences', () => {
|
|||||||
|
|
||||||
const result = replaceCidReferences(html, 'email1', attachments)
|
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', () => {
|
it('should include baseUrl when provided', () => {
|
||||||
@@ -96,44 +96,7 @@ describe('replaceCidReferences', () => {
|
|||||||
|
|
||||||
const result = replaceCidReferences(html, 'email1', attachments, 'example.com')
|
const result = replaceCidReferences(html, 'email1', attachments, 'example.com')
|
||||||
|
|
||||||
expect(result).toContain('//example.com/api/email/email1/attachment/abc.png')
|
expect(result).toContain('//example.com/email/email1/attachment/abc.png')
|
||||||
})
|
|
||||||
|
|
||||||
it('should include basePath when MailDev is mounted under a subpath', () => {
|
|
||||||
const html = '<img src="cid:image123">'
|
|
||||||
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 = '<img src="cid:image123">'
|
|
||||||
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 = '<img src="cid:image123">'
|
|
||||||
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 = '<img src="cid:image123">'
|
|
||||||
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"')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should replace multiple cid references', () => {
|
it('should replace multiple cid references', () => {
|
||||||
|
|||||||
@@ -45,16 +45,14 @@ export function sanitizeHtml(html: string | undefined): string | undefined {
|
|||||||
* @param html - HTML content with cid: references
|
* @param html - HTML content with cid: references
|
||||||
* @param emailId - ID of the email
|
* @param emailId - ID of the email
|
||||||
* @param attachments - Array of attachments with contentId
|
* @param attachments - Array of attachments with contentId
|
||||||
* @param baseUrl - Optional host to prefix as `//host/...`; unset gives a root-relative URL
|
* @param baseUrl - Optional base URL for attachment URLs
|
||||||
* @param basePath - Optional pathname the API is mounted under, e.g. `/mail`
|
|
||||||
* @returns HTML with cid: references replaced with actual URLs
|
* @returns HTML with cid: references replaced with actual URLs
|
||||||
*/
|
*/
|
||||||
export function replaceCidReferences(
|
export function replaceCidReferences(
|
||||||
html: string,
|
html: string,
|
||||||
emailId: string,
|
emailId: string,
|
||||||
attachments: Array<{ contentId?: string; generatedFileName: string }>,
|
attachments: Array<{ contentId?: string; generatedFileName: string }>,
|
||||||
baseUrl?: string,
|
baseUrl?: string
|
||||||
basePath?: string
|
|
||||||
): string {
|
): string {
|
||||||
if (!attachments || attachments.length === 0) {
|
if (!attachments || attachments.length === 0) {
|
||||||
return html
|
return html
|
||||||
@@ -74,7 +72,7 @@ export function replaceCidReferences(
|
|||||||
'g'
|
'g'
|
||||||
)
|
)
|
||||||
|
|
||||||
const url = buildAttachmentUrl(emailId, attachment.generatedFileName, baseUrl, basePath)
|
const url = buildAttachmentUrl(emailId, attachment.generatedFileName, baseUrl)
|
||||||
const replacement = `src="${url}"`
|
const replacement = `src="${url}"`
|
||||||
|
|
||||||
result = result.replace(regex, replacement)
|
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/...`,
|
* Build URL for an attachment
|
||||||
* so both parts belong here or the src 404s.
|
|
||||||
*/
|
*/
|
||||||
function buildAttachmentUrl(
|
function buildAttachmentUrl(
|
||||||
emailId: string,
|
emailId: string,
|
||||||
filename: string,
|
filename: string,
|
||||||
baseUrl?: string,
|
baseUrl?: string
|
||||||
basePath?: string
|
|
||||||
): string {
|
): string {
|
||||||
const base = baseUrl ? `//${baseUrl}` : ''
|
const base = baseUrl ? `//${baseUrl}` : ''
|
||||||
const segments = basePath ? basePath.replace(/^\/+|\/+$/g, '') : ''
|
return `${base}/email/${emailId}/attachment/${encodeURIComponent(filename)}`
|
||||||
const prefix = segments ? `/${segments}` : ''
|
|
||||||
return `${base}${prefix}/api/email/${emailId}/attachment/${encodeURIComponent(filename)}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ export class SMTPServer extends EventEmitter {
|
|||||||
|
|
||||||
// Replace CID references with actual URLs
|
// Replace CID references with actual URLs
|
||||||
if (email.attachments && email.attachments.length > 0) {
|
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
|
return html
|
||||||
|
|||||||
@@ -218,10 +218,8 @@ export interface RelayResult {
|
|||||||
* HTML rendering options for email
|
* HTML rendering options for email
|
||||||
*/
|
*/
|
||||||
export interface HTMLRenderOptions {
|
export interface HTMLRenderOptions {
|
||||||
/** Host to prefix attachment URLs with; unset gives a root-relative URL */
|
/** Base URL for attachment URLs */
|
||||||
baseUrl?: string
|
baseUrl?: string
|
||||||
/** Pathname the API is mounted under, e.g. `/mail` */
|
|
||||||
basePath?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in new issue
Block a user