UNPKG

mailpit-api

Version:

A TypeScript client for interacting with Mailpit's REST API.

539 lines (538 loc) 18.8 kB
// src/index.ts import axios, { isAxiosError } from "axios"; var MailpitClient = class { axiosInstance; /** * Creates an instance of {@link MailpitClient}. * @param baseURL - The base URL of the Mailpit API. * @param auth - Optional authentication credentials. * @param auth.username - The username for basic authentication. * @param auth.password - The password for basic authentication. * @example No Auth * ```typescript * const mailpit = new MailpitClient("http://localhost:8025"); * ``` * @example Basic Auth * ```typescript * const mailpit = new MailpitClient("http://localhost:8025", { * username: "admin", * password: "supersecret", * }); * ``` */ constructor(baseURL, auth) { this.axiosInstance = axios.create({ baseURL, auth, validateStatus: function(status) { return status === 200; } }); } async handleRequest(request, options = { fullResponse: false }) { try { const response = await request(); return options.fullResponse ? response : response.data; } catch (error) { if (isAxiosError(error)) { const url = error.config?.url || "UNKNOWN URL"; const method = error.config?.method?.toUpperCase() || "UNKNOWN METHOD"; if (error.response) { throw new Error( `Mailpit API Error: ${error.response.status.toString()} ${error.response.statusText} at ${method} ${url}: ${JSON.stringify(error.response.data)}` ); } else if (error.request) { throw new Error( `Mailpit API Error: No response received from server at ${method} ${url}` ); } else { throw new Error( `Mailpit API Error: ${error.toString()} at ${method} ${url}` ); } } else { throw new Error(`Unexpected Error: ${error}`); } } } /** * Retrieves information about the Mailpit instance. * * @returns Basic runtime information, message totals and latest release version. * @example * ```typescript * const info = await mailpit.getInfo(); * ``` */ async getInfo() { return await this.handleRequest( () => this.axiosInstance.get("/api/v1/info") ); } /** * Retrieves the configuration of the Mailpit web UI. * @remarks Intended for web UI only! * @returns Configuration settings * @example * ```typescript * const config = await mailpit.getConfiguration(); * ``` */ async getConfiguration() { return await this.handleRequest( () => this.axiosInstance.get("/api/v1/webui") ); } /** * Retrieves a summary of a specific message and marks it as read. * @param id - The message database ID. Defaults to `latest` to return the latest message. * @returns Message summary * @example * ```typescript * const message = await mailpit.getMessageSummary(); * ``` */ async getMessageSummary(id = "latest") { return await this.handleRequest( () => this.axiosInstance.get( `/api/v1/message/${id}` ) ); } /** * Retrieves the headers of a specific message. * @remarks Header keys are returned alphabetically. * @param id - The message database ID. Defaults to `latest` to return the latest message. * @returns Message headers * @example * ```typescript * const headers = await mailpit.getMessageHeaders(); * ``` */ async getMessageHeaders(id = "latest") { return await this.handleRequest( () => this.axiosInstance.get( `/api/v1/message/${id}/headers` ) ); } /** * Retrieves a specific attachment from a message. * @param id - Message database ID or "latest" * @param partID - The attachment part ID * @returns Attachment as binary data and the content type * @example * ```typescript * const message = await mailpit.getMessageSummary(); * if (message.Attachments.length) { * const attachment = await mailpit.getMessageAttachment(message.ID, message.Attachments[0].PartID); * // Do something with the attachment data * } * ``` */ async getMessageAttachment(id, partID) { const response = await this.handleRequest( () => this.axiosInstance.get( `/api/v1/message/${id}/part/${partID}`, { responseType: "arraybuffer" } ), { fullResponse: true } ); return { data: response.data, contentType: response.headers["content-type"] }; } /** * Generates a cropped 180x120 JPEG thumbnail of an image attachment from a message. * Only image attachments are supported. * @remarks * If the image is smaller than 180x120 then the image is padded. * If the attachment is not an image then a blank image is returned. * @param id - Message database ID or "latest" * @param partID - The attachment part ID * @returns Image attachment thumbnail as binary data and the content type * @example * ```typescript * const message = await mailpit.getMessageSummary(); * if (message.Attachments.length) { * const thumbnail = await mailpit.getAttachmentThumbnail(message.ID, message.Attachments[0].PartID); * // Do something with the thumbnail data * } * ``` */ async getAttachmentThumbnail(id, partID) { const response = await this.handleRequest( () => this.axiosInstance.get( `/api/v1/message/${id}/part/${partID}/thumb`, { responseType: "arraybuffer" } ), { fullResponse: true } ); return { data: response.data, contentType: response.headers["content-type"] }; } /** * Retrieves the full email message source as plain text. * @param id - The message database ID. Defaults to `latest` to return the latest message. * @returns Plain text message source * @example * ```typescript * const messageSource = await mailpit.getMessageSource(); * ``` */ async getMessageSource(id = "latest") { return await this.handleRequest( () => this.axiosInstance.get(`/api/v1/message/${id}/raw`) ); } /** * Release a message via a pre-configured external SMTP server. * @remarks This is only enabled if message relaying has been configured. * @param id - The message database ID. Use `latest` to return the latest message. * @param relayTo - Array of email addresses to relay the message to * @returns Plain text "ok" response * @example * ```typescript * const message = await mailpit.releaseMessage("latest", ["user1@example.test", "user2@example.test"]); * ``` */ async releaseMessage(id, relayTo) { return await this.handleRequest( () => this.axiosInstance.post(`/api/v1/message/${id}/release`, relayTo) ); } /** * Sends a message * @param sendReqest - The request containing the message details. * @returns Response containing database messsage ID * @example * ```typescript * await mailpit.sendMessage( * From: { Email: "user@example.test", Name: "First LastName" }, * To: [{ Email: "rec@example.test", Name: "Recipient Name"}, {Email: "another@example.test"}], * Subject: "Test Email", * ); * ``` */ async sendMessage(sendReqest) { return await this.handleRequest( () => this.axiosInstance.post( `/api/v1/send`, sendReqest ) ); } /** * Retrieves a list of message summaries ordered from newest to oldest. * @remarks Only contains the number of attachments and a snippet of the message body. * @see {@link MailpitClient.getMessageSummary | getMessageSummary()} for more attachment and body details for a specific message. * @param start - The pagination offset. Defaults to `0`. * @param limit - The number of messages to retrieve. Defaults to `50`. * @returns A list of message summaries * @example * ```typescript * const messages = await.listMessages(); * ``` */ async listMessages(start = 0, limit = 50) { return await this.handleRequest( () => this.axiosInstance.get( `/api/v1/messages`, { params: { start, limit } } ) ); } /** * Set the read status of messages. * @remarks You can optionally provide an array of `IDs` **OR** a `Search` filter. If neither is set then all messages are updated. * @param readStatus - The request containing the message database IDs/search string and the read status. * @param readStatus.Read - The read status to set. Defaults to `false`. * @param readStatus.IDs - The optional IDs of the messages to update. * @param readStatus.Search - The optional search string to filter messages. * @param params - Optional parameters for defining the time zone when using the `before:` and `after:` search filters. * @see {@link https://mailpit.axllent.org/docs/usage/search-filters/ | Search filters} * @returns Plain text "ok" response * @example * ```typescript * // Set all messages as unread * await mailpit.setReadStatus(); * * // Set all messages as read * await mailpit.setReadStatus({ Read: true }); * * // Set specific messages as read using IDs * await mailpit.setReadStatus({ IDs: ["1", "2", "3"], Read: true }); * * // Set specific messages as read using search * await mailpit.setReadStatus({ Search: "from:example.test", Read: true }); * * // Set specific messages as read using after: search with time zone * await mailpit.setReadStatus({ Search: "after:2025-04-30", Read: true }, { tz: "America/Chicago" }); * ``` */ async setReadStatus(readStatus, params) { return await this.handleRequest( () => this.axiosInstance.put(`/api/v1/messages`, readStatus, { params }) ); } /** * Delete individual or all messages. * @remarks If no `IDs` are provided then all messages are deleted. * @param deleteRequest - The request containing the message database IDs to delete. * @returns Plain text "ok" response * @example * ```typescript * // Delete all messages * await mailpit.deleteMessages(); * * // Delete specific messages * await mailpit.deleteMessages({ IDs: ["1", "2", "3"] }); * ``` */ async deleteMessages(deleteRequest) { return await this.handleRequest( () => this.axiosInstance.delete(`/api/v1/messages`, { data: deleteRequest }) ); } /** * Retrieve messages matching a search, sorted by received date (descending). * @see {@link https://mailpit.axllent.org/docs/usage/search-filters/ | Search filters} * @remarks Only contains the number of attachments and a snippet of the message body. * @see {@link MailpitClient.getMessageSummary | getMessageSummary()} for more attachment and body details for a specific message. * @param search - The search request containing the query and optional parameters. * @returns A list of message summaries matching the search criteria. * @example * ```typescript * // Search for messages from a the domain example.test * const messages = await mailpit.searchMessages({query: "from:example.test"}); * ``` */ async searchMessages(search) { return await this.handleRequest( () => this.axiosInstance.get(`/api/v1/search`, { params: search }) ); } /** * Delete all messages matching a search. * @see {@link https://mailpit.axllent.org/docs/usage/search-filters/ | Search filters} * @param search - The search request containing the query. * @returns Plain text "ok" response * @example * ```typescript * // Delete all messages from the domain example.test * await mailpit.deleteMessagesBySearch({query: "from:example.test"}); * ``` */ async deleteMessagesBySearch(search) { return await this.handleRequest( () => this.axiosInstance.delete(`/api/v1/search`, { params: search }) ); } /** * Performs an HTML check on a specific message. * @param id - The message database ID. Defaults to `latest` to return the latest message. * @returns The summary of the message HTML checker * @example * ```typescript * const htmlCheck = await mailpit.htmlCheck(); * ``` */ async htmlCheck(id = "latest") { return await this.handleRequest( () => this.axiosInstance.get( `/api/v1/message/${id}/html-check` ) ); } /** * Performs a link check on a specific message. * @param id - The message database ID. Defaults to `latest` to return the latest message. * @param follow - Whether to follow links. Defaults to `false`. * @returns The summary of the message Link checker. * @example * ```typescript * const linkCheck = await mailpit.linkCheck(); * ``` */ async linkCheck(id = "latest", follow = false) { return await this.handleRequest( () => this.axiosInstance.get( `/api/v1/message/${id}/link-check`, { params: { follow } } ) ); } /** * Performs a SpamAssassin check (if enabled) on a specific message. * @param id - The message database ID. Defaults to `latest` to return the latest message. * @returns The SpamAssassin summary (if enabled) * @example * ```typescript * const spamAssassinCheck = await mailpit.spamAssassinCheck(); * ``` */ async spamAssassinCheck(id = "latest") { return await this.handleRequest( () => this.axiosInstance.get( `/api/v1/message/${id}/sa-check` ) ); } /** * Retrieves a list of all the unique tags. * @returns All unique message tags * @example * ```typescript * const tags = await mailpit.getTags(); * ``` */ async getTags() { return await this.handleRequest( () => this.axiosInstance.get(`/api/v1/tags`) ); } /** * Sets and removes tag(s) on message(s). This will overwrite any existing tags for selected message database IDs. * @param request - The request containing the message IDs and tags. To remove all tags from a message, pass an empty `Tags` array or exclude `Tags` entirely. * @remarks * Tags are limited to the following characters: `a-z`, `A-Z`, `0-9`, `-`, `.`, `spaces`, and `_`, and must be a minimum of 1 character. * Other characters are silently stripped from the tag. * @returns Plain text "ok" response * @example * ```typescript * // Set tags on message(s) * await mailpit.setTags({ IDs: ["1", "2", "3"], Tags: ["tag1", "tag2"] }); * // Remove tags from message(s) * await mailpit.setTags({ IDs: ["1", "2", "3"]}); * ``` */ async setTags(request) { return await this.handleRequest( () => this.axiosInstance.put(`/api/v1/tags`, request) ); } /** * Renames an existing tag. * @param tag - The current name of the tag. * @param newTagName - A new name for the tag. * @remarks * Tags are limited to the following characters: `a-z`, `A-Z`, `0-9`, `-`, `.`, `spaces`, and `_`, and must be a minimum of 1 character. * Other characters are silently stripped from the tag. * @returns Plain text "ok" response * @example * ```typescript * await mailpit.renameTag("Old Tag Name", "New Tag Name"); * ``` */ async renameTag(tag, newTagName) { const encodedTag = encodeURIComponent(tag); return await this.handleRequest( () => this.axiosInstance.put(`/api/v1/tags/${encodedTag}`, { Name: newTagName }) ); } /** * Deletes a tag from all messages. * @param tag - The name of the tag to delete. * @remarks This does NOT delete any messages * @returns Plain text "ok" response * ```typescript * await mailpit.deleteTag("Tag 1"); * ``` */ async deleteTag(tag) { const encodedTag = encodeURIComponent(tag); return await this.handleRequest( () => this.axiosInstance.delete(`/api/v1/tags/${encodedTag}`) ); } /** * Retrieves the current Chaos triggers configuration (if enabled). * @remarks This will return an error if Chaos is not enabled at runtime. * @returns The Chaos triggers configuration * @example * ```typescript * const triggers = await mailpit.getChaosTriggers(); * ``` */ async getChaosTriggers() { return await this.handleRequest( () => this.axiosInstance.get("/api/v1/chaos") ); } /** * Sets and/or resets the Chaos triggers configuration (if enabled). * @param triggers - The request containing the chaos triggers. Omitted triggers will reset to the default `0%` probabibility. * @remarks This will return an error if Chaos is not enabled at runtime. * @returns The updated Chaos triggers configuration * @example * ```typescript * // Reset all triggers to `0%` probability * const triggers = await mailpit.setChaosTriggers(); * // Set `Sender` and reset `Authentication` and `Recipient` triggers * const triggers = await mailpit.setChaosTriggers({ Sender: { ErrorCode: 451, Probability: 5 } }); * ``` */ async setChaosTriggers(triggers = {}) { return await this.handleRequest( () => this.axiosInstance.put( "/api/v1/chaos", triggers ) ); } /** * Renders the HTML part of a specific message which can be used for UI integration testing. * @remarks * Attached inline images are modified to link to the API provided they exist. * If the message does not contain an HTML part then a 404 error is returned. * * * @param id - The message database ID. Defaults to `latest` to return the latest message. * @param embed - Whether this route is to be embedded in an iframe. Defaults to `undefined`. Set to `1` to embed. * The `embed` parameter will add `target="_blank"` and `rel="noreferrer noopener"` to all links. * In addition, a small script will be added to the end of the document to post (postMessage()) the height of the document back to the parent window for optional iframe height resizing. * Note that this will also transform the message into a full HTML document (if it isn't already), so this option is useful for viewing but not programmatic testing. * @returns Rendered HTML * @example * ```typescript * const html = await mailpit.renderMessageHTML(); * ``` */ async renderMessageHTML(id = "latest", embed) { return await this.handleRequest( () => this.axiosInstance.get(`/view/${id}.html`, { params: { embed } }) ); } /** * Renders just the message's text part which can be used for UI integration testing. * @param id - The message database ID. Defaults to `latest` to return the latest message. * @returns Plain text * @example * ```typescript * const html = await mailpit.renderMessageText(); * ``` */ async renderMessageText(id = "latest") { return await this.handleRequest( () => this.axiosInstance.get(`/view/${id}.txt`) ); } }; export { MailpitClient };