From e29883fa1c78d27c94ba07ecdd5fdde48052b933 Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Sun, 22 Jun 2025 15:03:55 +1200 Subject: [PATCH] Chore: Refactor API Swagger definitions and remove unused structs - Removed deprecated `thumbnailParams` struct from `thumbnails.go`. - Updated `server_test.go` to use an anonymous struct for response unmarshalling. - Enhanced `swagger.json` with detailed definitions for SendRequest and SendMessageResponse. - Introduced new `swaggerParams.go` to define Swagger parameters for various API endpoints. - Created `swaggerResponses.go` to define Swagger responses for API endpoints. - Cleaned up unused JSON error message definitions and consolidated error responses. - Improved documentation for Chaos triggers and web UI configuration responses. --- .vscode/settings.json | 1 + internal/smtpd/chaos/chaos.go | 6 +- server/apiv1/api.go | 4 +- server/apiv1/application.go | 93 +---- server/apiv1/chaos.go | 18 - server/apiv1/message.go | 46 --- server/apiv1/messages.go | 125 ------- server/apiv1/other.go | 44 --- server/apiv1/release.go | 19 - server/apiv1/send.go | 176 ++------- server/apiv1/swagger.go | 41 --- server/apiv1/swaggerParams.go | 416 +++++++++++++++++++++ server/apiv1/swaggerResponses.go | 142 +++++++ server/apiv1/tags.go | 46 --- server/apiv1/testing.go | 29 -- server/apiv1/thumbnails.go | 15 - server/server_test.go | 4 +- server/ui/api/v1/swagger.json | 615 +++++++++++++++---------------- 18 files changed, 906 insertions(+), 934 deletions(-) delete mode 100644 server/apiv1/swagger.go create mode 100644 server/apiv1/swaggerParams.go create mode 100644 server/apiv1/swaggerResponses.go diff --git a/.vscode/settings.json b/.vscode/settings.json index 09c563f..bedb688 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -26,6 +26,7 @@ "Mechs", "navhtml", "neostandard", + "nolint", "popperjs", "readyz", "RSET", diff --git a/internal/smtpd/chaos/chaos.go b/internal/smtpd/chaos/chaos.go index d9994e0..e093b06 100644 --- a/internal/smtpd/chaos/chaos.go +++ b/internal/smtpd/chaos/chaos.go @@ -25,7 +25,8 @@ var ( ) // Triggers for the Chaos configuration -// swagger:model Triggers +// +// swagger:model ChaosTriggers type Triggers struct { // Sender trigger to fail on From, Sender Sender Trigger @@ -36,7 +37,8 @@ type Triggers struct { } // Trigger for Chaos -// swagger:model Trigger +// +// swagger:model ChaosTrigger type Trigger struct { // SMTP error code to return. The value must range from 400 to 599. // required: true diff --git a/server/apiv1/api.go b/server/apiv1/api.go index 1008f14..718877c 100644 --- a/server/apiv1/api.go +++ b/server/apiv1/api.go @@ -35,9 +35,7 @@ func httpJSONError(w http.ResponseWriter, msg string) { w.Header().Set("Referrer-Policy", "no-referrer") w.Header().Set("Content-Security-Policy", config.ContentSecurityPolicy) w.WriteHeader(http.StatusBadRequest) - e := JSONErrorMessage{ - Error: msg, - } + e := struct{ Error string }{Error: msg} w.Header().Add("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(e); err != nil { diff --git a/server/apiv1/application.go b/server/apiv1/application.go index 5e8bad8..e7da7c1 100644 --- a/server/apiv1/application.go +++ b/server/apiv1/application.go @@ -10,16 +10,7 @@ import ( "github.com/axllent/mailpit/internal/stats" ) -// Application information -// swagger:response AppInfoResponse -type appInfoResponse struct { - // Application information - // - // in: body - Body stats.AppInformation -} - -// AppInfo returns some basic details about the running app, and latest release. +// AppInfo returns some basic details about the running app including the latest release (unless disabled). func AppInfo(w http.ResponseWriter, _ *http.Request) { // swagger:route GET /api/v1/info application AppInformation // @@ -42,59 +33,9 @@ func AppInfo(w http.ResponseWriter, _ *http.Request) { } } -// Response includes global web UI settings -// -// swagger:model WebUIConfiguration -type webUIConfiguration struct { - // Optional label to identify this Mailpit instance - Label string - // Message Relay information - MessageRelay struct { - // Whether message relaying (release) is enabled - Enabled bool - // The configured SMTP server address - SMTPServer string - // Enforced Return-Path (if set) for relay bounces - ReturnPath string - // Only allow relaying to these recipients (regex) - AllowedRecipients string - // Block relaying to these recipients (regex) - BlockedRecipients string - // Overrides the "From" address for all relayed messages - OverrideFrom string - // Preserve the original Message-IDs when relaying messages - PreserveMessageIDs bool - - // DEPRECATED 2024/03/12 - // swagger:ignore - RecipientAllowlist string - } - - // Whether SpamAssassin is enabled - SpamAssassin bool - - // Whether Chaos support is enabled at runtime - ChaosEnabled bool - - // Whether messages with duplicate IDs are ignored - DuplicatesIgnored bool - - // Whether the delete button should be hidden - HideDeleteAllButton bool -} - -// Web UI configuration response -// swagger:response WebUIConfigurationResponse -type webUIConfigurationResponse struct { - // Web UI configuration settings - // - // in: body - Body webUIConfiguration -} - // WebUIConfig returns configuration settings for the web UI. func WebUIConfig(w http.ResponseWriter, _ *http.Request) { - // swagger:route GET /api/v1/webui application WebUIConfiguration + // swagger:route GET /api/v1/webui application WebUIConfigurationResponse // // # Get web UI configuration // @@ -110,29 +51,29 @@ func WebUIConfig(w http.ResponseWriter, _ *http.Request) { // 200: WebUIConfigurationResponse // 400: ErrorResponse - conf := webUIConfiguration{} + conf := webUIConfigurationResponse{} - conf.Label = config.Label - conf.MessageRelay.Enabled = config.ReleaseEnabled + conf.Body.Label = config.Label + conf.Body.MessageRelay.Enabled = config.ReleaseEnabled if config.ReleaseEnabled { - conf.MessageRelay.SMTPServer = fmt.Sprintf("%s:%d", config.SMTPRelayConfig.Host, config.SMTPRelayConfig.Port) - conf.MessageRelay.ReturnPath = config.SMTPRelayConfig.ReturnPath - conf.MessageRelay.AllowedRecipients = config.SMTPRelayConfig.AllowedRecipients - conf.MessageRelay.BlockedRecipients = config.SMTPRelayConfig.BlockedRecipients - conf.MessageRelay.OverrideFrom = config.SMTPRelayConfig.OverrideFrom - conf.MessageRelay.PreserveMessageIDs = config.SMTPRelayConfig.PreserveMessageIDs + conf.Body.MessageRelay.SMTPServer = fmt.Sprintf("%s:%d", config.SMTPRelayConfig.Host, config.SMTPRelayConfig.Port) + conf.Body.MessageRelay.ReturnPath = config.SMTPRelayConfig.ReturnPath + conf.Body.MessageRelay.AllowedRecipients = config.SMTPRelayConfig.AllowedRecipients + conf.Body.MessageRelay.BlockedRecipients = config.SMTPRelayConfig.BlockedRecipients + conf.Body.MessageRelay.OverrideFrom = config.SMTPRelayConfig.OverrideFrom + conf.Body.MessageRelay.PreserveMessageIDs = config.SMTPRelayConfig.PreserveMessageIDs // DEPRECATED 2024/03/12 - conf.MessageRelay.RecipientAllowlist = config.SMTPRelayConfig.AllowedRecipients + conf.Body.MessageRelay.RecipientAllowlist = config.SMTPRelayConfig.AllowedRecipients } - conf.SpamAssassin = config.EnableSpamAssassin != "" - conf.ChaosEnabled = chaos.Enabled - conf.DuplicatesIgnored = config.IgnoreDuplicateIDs - conf.HideDeleteAllButton = config.HideDeleteAllButton + conf.Body.SpamAssassin = config.EnableSpamAssassin != "" + conf.Body.ChaosEnabled = chaos.Enabled + conf.Body.DuplicatesIgnored = config.IgnoreDuplicateIDs + conf.Body.HideDeleteAllButton = config.HideDeleteAllButton w.Header().Add("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(conf); err != nil { + if err := json.NewEncoder(w).Encode(conf.Body); err != nil { httpError(w, err.Error()) } } diff --git a/server/apiv1/chaos.go b/server/apiv1/chaos.go index b538e8b..b7ac7fb 100644 --- a/server/apiv1/chaos.go +++ b/server/apiv1/chaos.go @@ -7,18 +7,6 @@ import ( "github.com/axllent/mailpit/internal/smtpd/chaos" ) -// ChaosTriggers are the Chaos triggers -type ChaosTriggers chaos.Triggers - -// Response for the Chaos triggers configuration -// swagger:response ChaosResponse -type chaosResponse struct { - // The current Chaos triggers - // - // in: body - Body ChaosTriggers -} - // GetChaos returns the current Chaos triggers func GetChaos(w http.ResponseWriter, _ *http.Request) { // swagger:route GET /api/v1/chaos testing getChaos @@ -50,12 +38,6 @@ func GetChaos(w http.ResponseWriter, _ *http.Request) { } } -// swagger:parameters setChaosParams -type setChaosParams struct { - // in: body - Body ChaosTriggers -} - // SetChaos sets the Chaos configuration. func SetChaos(w http.ResponseWriter, r *http.Request) { // swagger:route PUT /api/v1/chaos testing setChaosParams diff --git a/server/apiv1/message.go b/server/apiv1/message.go index 455876b..c747120 100644 --- a/server/apiv1/message.go +++ b/server/apiv1/message.go @@ -11,15 +11,6 @@ import ( "github.com/gorilla/mux" ) -// swagger:parameters GetMessageParams -type getMessageParams struct { - // Message database ID or "latest" - // - // in: path - // required: true - ID string -} - // GetMessage (method: GET) returns the Message as JSON func GetMessage(w http.ResponseWriter, r *http.Request) { // swagger:route GET /api/v1/message/{ID} message GetMessageParams @@ -66,19 +57,6 @@ func GetMessage(w http.ResponseWriter, r *http.Request) { } } -// swagger:parameters GetHeadersParams -type getHeadersParams struct { - // Message database ID or "latest" - // - // in: path - // required: true - ID string -} - -// Message headers -// swagger:model MessageHeadersResponse -type messageHeaders map[string][]string - // GetHeaders (method: GET) returns the message headers as JSON func GetHeaders(w http.ResponseWriter, r *http.Request) { // swagger:route GET /api/v1/message/{ID}/headers message GetHeadersParams @@ -132,21 +110,6 @@ func GetHeaders(w http.ResponseWriter, r *http.Request) { } } -// swagger:parameters AttachmentParams -type attachmentParams struct { - // Message database ID or "latest" - // - // in: path - // required: true - ID string - - // Attachment part ID - // - // in: path - // required: true - PartID string -} - // DownloadAttachment (method: GET) returns the attachment data func DownloadAttachment(w http.ResponseWriter, r *http.Request) { // swagger:route GET /api/v1/message/{ID}/part/{PartID} message AttachmentParams @@ -199,15 +162,6 @@ func DownloadAttachment(w http.ResponseWriter, r *http.Request) { _, _ = w.Write(a.Content) } -// swagger:parameters DownloadRawParams -type downloadRawParams struct { - // Message database ID or "latest" - // - // in: path - // required: true - ID string -} - // DownloadRaw (method: GET) returns the full email source as plain text func DownloadRaw(w http.ResponseWriter, r *http.Request) { // swagger:route GET /api/v1/message/{ID}/raw message DownloadRawParams diff --git a/server/apiv1/messages.go b/server/apiv1/messages.go index 9832941..48009cc 100644 --- a/server/apiv1/messages.go +++ b/server/apiv1/messages.go @@ -8,35 +8,6 @@ import ( "github.com/axllent/mailpit/internal/storage" ) -// swagger:parameters GetMessagesParams -type getMessagesParams struct { - // Pagination offset - // - // in: query - // name: start - // required: false - // default: 0 - // type: integer - Start int `json:"start"` - - // Limit number of results - // - // in: query - // name: limit - // required: false - // default: 50 - // type: integer - Limit int `json:"limit"` -} - -// Summary of messages -// swagger:response MessagesSummaryResponse -type messagesSummaryResponse struct { - // The messages summary - // in: body - Body MessagesSummary -} - // MessagesSummary is a summary of a list of messages type MessagesSummary struct { // Total number of messages in mailbox @@ -111,39 +82,6 @@ func GetMessages(w http.ResponseWriter, r *http.Request) { } } -// swagger:parameters SetReadStatusParams -type setReadStatusParams struct { - // in: body - Body struct { - // Read status - // - // required: false - // default: false - // example: true - Read bool - - // Optional array of message database IDs - // - // required: false - // default: [] - // example: ["4oRBnPtCXgAqZniRhzLNmS", "hXayS6wnCgNnt6aFTvmOF6"] - IDs []string - - // Optional messages matching a search - // - // required: false - // example: tag:backups - Search string - } - - // Optional [timezone identifier](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) used only for `before:` & `after:` searches (eg: "Pacific/Auckland"). - // - // in: query - // required: false - // type string - TZ string `json:"tz"` -} - // SetReadStatus (method: PUT) will update the status to Read/Unread for all provided IDs. func SetReadStatus(w http.ResponseWriter, r *http.Request) { // swagger:route PUT /api/v1/messages messages SetReadStatusParams @@ -225,19 +163,6 @@ func SetReadStatus(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("ok")) } -// swagger:parameters DeleteMessagesParams -type deleteMessagesParams struct { - // Delete request - // in: body - Body struct { - // Array of message database IDs - // - // required: false - // example: ["4oRBnPtCXgAqZniRhzLNmS", "hXayS6wnCgNnt6aFTvmOF6"] - IDs []string - } -} - // DeleteMessages (method: DELETE) deletes all messages matching IDS. func DeleteMessages(w http.ResponseWriter, r *http.Request) { // swagger:route DELETE /api/v1/messages messages DeleteMessagesParams @@ -279,39 +204,6 @@ func DeleteMessages(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("ok")) } -// swagger:parameters SearchParams -type searchParams struct { - // Search query - // - // in: query - // required: true - // type: string - Query string `json:"query"` - - // Pagination offset - // - // in: query - // required: false - // default: 0 - // type integer - Start string `json:"start"` - - // Limit results - // - // in: query - // required: false - // default: 50 - // type integer - Limit string `json:"limit"` - - // Optional [timezone identifier](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) used only for `before:` & `after:` searches (eg: "Pacific/Auckland"). - // - // in: query - // required: false - // type string - TZ string `json:"tz"` -} - // Search returns the latest messages as JSON func Search(w http.ResponseWriter, r *http.Request) { // swagger:route GET /api/v1/search messages SearchParams @@ -369,23 +261,6 @@ func Search(w http.ResponseWriter, r *http.Request) { } } -// swagger:parameters DeleteSearchParams -type deleteSearchParams struct { - // Search query - // - // in: query - // required: true - // type: string - Query string `json:"query"` - - // [Timezone identifier](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) used only for `before:` & `after:` searches (eg: "Pacific/Auckland"). - // - // in: query - // required: false - // type string - TZ string `json:"tz"` -} - // DeleteSearch will delete all messages matching a search func DeleteSearch(w http.ResponseWriter, r *http.Request) { // swagger:route DELETE /api/v1/search messages DeleteSearchParams diff --git a/server/apiv1/other.go b/server/apiv1/other.go index ed77ff9..3b908e5 100644 --- a/server/apiv1/other.go +++ b/server/apiv1/other.go @@ -15,19 +15,6 @@ import ( "github.com/jhillyerd/enmime/v2" ) -// swagger:parameters HTMLCheckParams -type htmlCheckParams struct { - // Message database ID or "latest" - // - // in: path - // description: Message database ID or "latest" - // required: true - ID string -} - -// HTMLCheckResponse summary response -type HTMLCheckResponse = htmlcheck.Response - // HTMLCheck returns a summary of the HTML client support func HTMLCheck(w http.ResponseWriter, r *http.Request) { // swagger:route GET /api/v1/message/{ID}/html-check other HTMLCheckParams @@ -93,25 +80,6 @@ func HTMLCheck(w http.ResponseWriter, r *http.Request) { } } -// swagger:parameters LinkCheckParams -type linkCheckParams struct { - // Message database ID or "latest" - // - // in: path - // required: true - ID string - - // Follow redirects - // - // in: query - // required: false - // default: false - Follow string `json:"follow"` -} - -// LinkCheckResponse summary response -type LinkCheckResponse = linkcheck.Response - // LinkCheck returns a summary of links in the email func LinkCheck(w http.ResponseWriter, r *http.Request) { // swagger:route GET /api/v1/message/{ID}/link-check other LinkCheckParams @@ -170,18 +138,6 @@ func LinkCheck(w http.ResponseWriter, r *http.Request) { } } -// swagger:parameters SpamAssassinCheckParams -type spamAssassinCheckParams struct { - // Message database ID or "latest" - // - // in: path - // required: true - ID string -} - -// SpamAssassinResponse summary response -type SpamAssassinResponse = spamassassin.Result - // SpamAssassinCheck returns a summary of SpamAssassin results (if enabled) func SpamAssassinCheck(w http.ResponseWriter, r *http.Request) { // swagger:route GET /api/v1/message/{ID}/sa-check other SpamAssassinCheckParams diff --git a/server/apiv1/release.go b/server/apiv1/release.go index bc7eeb8..f2c8e03 100644 --- a/server/apiv1/release.go +++ b/server/apiv1/release.go @@ -17,25 +17,6 @@ import ( "github.com/lithammer/shortuuid/v4" ) -// swagger:parameters ReleaseMessageParams -type releaseMessageParams struct { - // Message database ID - // - // in: path - // description: Message database ID - // required: true - ID string - - // in: body - Body struct { - // Array of email addresses to relay the message to - // - // required: true - // example: ["user1@example.com", "user2@example.com"] - To []string - } -} - // ReleaseMessage (method: POST) will release a message via a pre-configured external SMTP server. func ReleaseMessage(w http.ResponseWriter, r *http.Request) { // swagger:route POST /api/v1/message/{ID}/release message ReleaseMessageParams diff --git a/server/apiv1/send.go b/server/apiv1/send.go index f945b5a..0d601a4 100644 --- a/server/apiv1/send.go +++ b/server/apiv1/send.go @@ -17,130 +17,6 @@ import ( "github.com/jhillyerd/enmime/v2" ) -// swagger:parameters SendMessageParams -type sendMessageParams struct { - // in: body - Body *SendRequest -} - -// SendRequest to send a message via HTTP -// swagger:model SendRequest -type SendRequest struct { - // "From" recipient - // required: true - From struct { - // Optional name - // example: John Doe - Name string - // Email address - // example: john@example.com - // required: true - Email string - } - - // "To" recipients - To []struct { - // Optional name - // example: Jane Doe - Name string - // Email address - // example: jane@example.com - // required: true - Email string - } - - // Cc recipients - Cc []struct { - // Optional name - // example: Manager - Name string - // Email address - // example: manager@example.com - // required: true - Email string - } - - // Bcc recipients email addresses only - // example: ["jack@example.com"] - Bcc []string - - // Optional Reply-To recipients - ReplyTo []struct { - // Optional name - // example: Secretary - Name string - // Email address - // example: secretary@example.com - // required: true - Email string - } - - // Subject - // example: Mailpit message via the HTTP API - Subject string - - // Message body (text) - // example: Mailpit is awesome! - Text string - - // Message body (HTML) - // example:

Mailpit is awesome!

- HTML string - - // Attachments - Attachments []struct { - // Base64-encoded string of the file content - // required: true - // example: iVBORw0KGgoAAAANSUhEUgAAAEEAAAA8CAMAAAAOlSdoAAAACXBIWXMAAAHrAAAB6wGM2bZBAAAAS1BMVEVHcEwRfnUkZ2gAt4UsSF8At4UtSV4At4YsSV4At4YsSV8At4YsSV4At4YsSV4sSV4At4YsSV4At4YtSV4At4YsSV4At4YtSV8At4YsUWYNAAAAGHRSTlMAAwoXGiktRE5dbnd7kpOlr7zJ0d3h8PD8PCSRAAACWUlEQVR42pXT4ZaqIBSG4W9rhqQYocG+/ys9Y0Z0Br+x3j8zaxUPewFh65K+7yrIMeIY4MT3wPfEJCidKXEMnLaVkxDiELiMz4WEOAZSFghxBIypCOlKiAMgXfIqTnBgSm8CIQ6BImxEUxEckClVQiHGj4Ba4AQHikAIClwTE9KtIghAhUJwoLkmLnCiAHJLRKgIMsEtVUKbBUIwoAg2C4QgQBE6l4VCnApBgSKYLLApCnCa0+96AEMW2BQcmC+Pr3nfp7o5Exy49gIADcIqUELGfeA+bp93LmAJp8QJoEcN3C7NY3sbVANixMyI0nku20/n5/ZRf3KI2k6JEDWQtxcbdGuAqu3TAXG+/799Oyyas1B1MnMiA+XyxHp9q0PUKGPiRAau1fZbLRZV09wZcT8/gHk8QQAxXn8VgaDqcUmU6O/r28nbVwXAqca2mRNtPAF5+zoP2MeN9Fy4NgC6RfcbgE7XITBRYTtOE3U3C2DVff7pk+PkUxgAbvtnPXJaD6DxulMLwOhPS/M3MQkgg1ZFrIXnmfaZoOfpKiFgzeZD/WuKqQEGrfJYkyWf6vlG3xUgTuscnkNkQsb599q124kdpMUjCa/XARHs1gZymVtGt3wLkiFv8rUgTxitYCex5EVGec0Y9VmoDTFBSQte2TfXGXlf7hbdaUM9Sk7fisEN9qfBBTK+FZcvM9fQSdkl2vj4W2oX/bRogO3XasiNH7R0eW7fgRM834ImTg+Lg6BEnx4vz81rhr+MYPBBQg1v8GndEOrthxaCTxNAOut8WKLGZQl+MPz88Q9tAO/hVuSeqQAAAABJRU5ErkJggg== - Content string - // Filename - // required: true - // example: mailpit.png - Filename string - // Optional Content Type for the the attachment. - // If this field is not set (or empty) then the content type is automatically detected. - // required: false - // example: image/png - ContentType string - // Optional Content-ID (`cid`) for attachment. - // If this field is set then the file is attached inline. - // required: false - // example: mailpit-logo - ContentID string - } - - // Mailpit tags - // example: ["Tag 1","Tag 2"] - Tags []string - - // Optional headers in {"key":"value"} format - // example: {"X-IP":"1.2.3.4"} - Headers map[string]string -} - -// JSONErrorMessage struct -type JSONErrorMessage struct { - // Error message - // example: invalid format - Error string -} - -// Confirmation message for HTTP send API -// swagger:response sendMessageResponse -type sendMessageResponse struct { - // Response for sending messages via the HTTP API - // - // in: body - Body SendMessageConfirmation -} - -// SendMessageConfirmation struct -type SendMessageConfirmation struct { - // Database ID - // example: iAfZVVe2UQfNSG5BAjgYwa - ID string -} - // SendMessageHandler handles HTTP requests to send a new message func SendMessageHandler(w http.ResponseWriter, r *http.Request) { // swagger:route POST /api/v1/send message SendMessageParams @@ -158,8 +34,8 @@ func SendMessageHandler(w http.ResponseWriter, r *http.Request) { // Schemes: http, https // // Responses: - // 200: sendMessageResponse - // 400: jsonErrorResponse + // 200: SendMessageResponse + // 400: JSONErrorResponse if config.DemoMode { httpJSONError(w, "this functionality has been disabled for demonstration purposes") @@ -168,9 +44,9 @@ func SendMessageHandler(w http.ResponseWriter, r *http.Request) { decoder := json.NewDecoder(r.Body) - data := SendRequest{} + data := sendMessageParams{} - if err := decoder.Decode(&data); err != nil { + if err := decoder.Decode(&data.Body); err != nil { httpJSONError(w, err.Error()) return } @@ -188,14 +64,14 @@ func SendMessageHandler(w http.ResponseWriter, r *http.Request) { } w.Header().Add("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(SendMessageConfirmation{ID: id}); err != nil { + if err := json.NewEncoder(w).Encode(struct{ ID string }{ID: id}); err != nil { httpError(w, err.Error()) } } // Send will validate the message structure and attempt to send to Mailpit. // It returns a sending summary or an error. -func (d SendRequest) Send(remoteAddr string, httpAuthUser *string) (string, error) { +func (d sendMessageParams) Send(remoteAddr string, httpAuthUser *string) (string, error) { ip, _, err := net.SplitHostPort(remoteAddr) if err != nil { return "", fmt.Errorf("error parsing request RemoteAddr: %s", err.Error()) @@ -206,16 +82,16 @@ func (d SendRequest) Send(remoteAddr string, httpAuthUser *string) (string, erro addresses := []string{} msg := enmime.Builder(). - From(d.From.Name, d.From.Email). - Subject(d.Subject). - Text([]byte(d.Text)) + From(d.Body.From.Name, d.Body.From.Email). + Subject(d.Body.Subject). + Text([]byte(d.Body.Text)) - if d.HTML != "" { - msg = msg.HTML([]byte(d.HTML)) + if d.Body.HTML != "" { + msg = msg.HTML([]byte(d.Body.HTML)) } - if len(d.To) > 0 { - for _, a := range d.To { + if len(d.Body.To) > 0 { + for _, a := range d.Body.To { if _, err := mail.ParseAddress(a.Email); err == nil { msg = msg.To(a.Name, a.Email) addresses = append(addresses, a.Email) @@ -225,8 +101,8 @@ func (d SendRequest) Send(remoteAddr string, httpAuthUser *string) (string, erro } } - if len(d.Cc) > 0 { - for _, a := range d.Cc { + if len(d.Body.Cc) > 0 { + for _, a := range d.Body.Cc { if _, err := mail.ParseAddress(a.Email); err == nil { msg = msg.CC(a.Name, a.Email) addresses = append(addresses, a.Email) @@ -236,8 +112,8 @@ func (d SendRequest) Send(remoteAddr string, httpAuthUser *string) (string, erro } } - if len(d.Bcc) > 0 { - for _, e := range d.Bcc { + if len(d.Body.Bcc) > 0 { + for _, e := range d.Body.Bcc { if _, err := mail.ParseAddress(e); err == nil { msg = msg.BCC("", e) addresses = append(addresses, e) @@ -247,8 +123,8 @@ func (d SendRequest) Send(remoteAddr string, httpAuthUser *string) (string, erro } } - if len(d.ReplyTo) > 0 { - for _, a := range d.ReplyTo { + if len(d.Body.ReplyTo) > 0 { + for _, a := range d.Body.ReplyTo { if _, err := mail.ParseAddress(a.Email); err == nil { msg = msg.ReplyTo(a.Name, a.Email) } else { @@ -259,13 +135,13 @@ func (d SendRequest) Send(remoteAddr string, httpAuthUser *string) (string, erro restrictedHeaders := []string{"To", "From", "Cc", "Bcc", "Reply-To", "Date", "Subject", "Content-Type", "Mime-Version"} - if len(d.Tags) > 0 { - msg = msg.Header("X-Tags", strings.Join(d.Tags, ", ")) + if len(d.Body.Tags) > 0 { + msg = msg.Header("X-Tags", strings.Join(d.Body.Tags, ", ")) restrictedHeaders = append(restrictedHeaders, "X-Tags") } - if len(d.Headers) > 0 { - for k, v := range d.Headers { + if len(d.Body.Headers) > 0 { + for k, v := range d.Body.Headers { // check header isn't in "restricted" headers if tools.InArray(k, restrictedHeaders) { return "", fmt.Errorf("cannot overwrite header: \"%s\"", k) @@ -274,8 +150,8 @@ func (d SendRequest) Send(remoteAddr string, httpAuthUser *string) (string, erro } } - if len(d.Attachments) > 0 { - for _, a := range d.Attachments { + if len(d.Body.Attachments) > 0 { + for _, a := range d.Body.Attachments { // workaround: split string because JS readAsDataURL() returns the base64 string // with the mime type prefix eg: data:image/png;base64, parts := strings.Split(a.Content, ",") @@ -307,5 +183,5 @@ func (d SendRequest) Send(remoteAddr string, httpAuthUser *string) (string, erro return "", fmt.Errorf("error building message: %s", err.Error()) } - return smtpd.SaveToDatabase(ipAddr, d.From.Email, addresses, buff.Bytes(), httpAuthUser) + return smtpd.SaveToDatabase(ipAddr, d.Body.From.Email, addresses, buff.Bytes(), httpAuthUser) } diff --git a/server/apiv1/swagger.go b/server/apiv1/swagger.go deleted file mode 100644 index b043db2..0000000 --- a/server/apiv1/swagger.go +++ /dev/null @@ -1,41 +0,0 @@ -package apiv1 - -// These structs are for the purpose of defining swagger HTTP parameters & responses - -// Binary data response which inherits the attachment's content type. -// swagger:response BinaryResponse -type binaryResponse string - -// Plain text response -// swagger:response TextResponse -type textResponse string - -// HTML response -// swagger:response HTMLResponse -type htmlResponse string - -// Server error will return with a 400 status code -// with the error message in the body -// swagger:response ErrorResponse -type errorResponse string - -// Not found error will return a 404 status code -// swagger:response NotFoundResponse -type notFoundResponse string - -// Plain text "ok" response -// swagger:response OKResponse -type okResponse string - -// Plain JSON array response -// swagger:response ArrayResponse -type arrayResponse []string - -// JSON error response -// swagger:response jsonErrorResponse -type jsonErrorResponse struct { - // A JSON-encoded error response - // - // in: body - Body JSONErrorMessage -} diff --git a/server/apiv1/swaggerParams.go b/server/apiv1/swaggerParams.go new file mode 100644 index 0000000..7bc3bb2 --- /dev/null +++ b/server/apiv1/swaggerParams.go @@ -0,0 +1,416 @@ +// Package apiv1 provides the API v1 endpoints for Mailpit. +// +// These structs are for the purpose of defining swagger HTTP parameters in go-swagger +// in order to generate a spec file. They are lowercased to avoid exporting them as public types. +// +//nolint:unused +package apiv1 + +import "github.com/axllent/mailpit/internal/smtpd/chaos" + +// swagger:parameters setChaosParams +type setChaosParams struct { + // in: body + Body chaos.Triggers +} + +// swagger:parameters AttachmentParams +type attachmentParams struct { + // Message database ID or "latest" + // + // in: path + // required: true + ID string + + // Attachment part ID + // + // in: path + // required: true + PartID string +} + +// swagger:parameters DownloadRawParams +type downloadRawParams struct { + // Message database ID or "latest" + // + // in: path + // required: true + ID string +} + +// swagger:parameters GetMessageParams +type getMessageParams struct { + // Message database ID or "latest" + // + // in: path + // required: true + ID string +} + +// swagger:parameters GetHeadersParams +type getHeadersParams struct { + // Message database ID or "latest" + // + // in: path + // required: true + ID string +} + +// swagger:parameters GetMessagesParams +type getMessagesParams struct { + // Pagination offset + // + // in: query + // name: start + // required: false + // default: 0 + // type: integer + Start int `json:"start"` + + // Limit number of results + // + // in: query + // name: limit + // required: false + // default: 50 + // type: integer + Limit int `json:"limit"` +} + +// swagger:parameters SetReadStatusParams +type setReadStatusParams struct { + // in: body + Body struct { + // Read status + // + // required: false + // default: false + // example: true + Read bool + + // Optional array of message database IDs + // + // required: false + // default: [] + // example: ["4oRBnPtCXgAqZniRhzLNmS", "hXayS6wnCgNnt6aFTvmOF6"] + IDs []string + + // Optional messages matching a search + // + // required: false + // example: tag:backups + Search string + } + + // Optional [timezone identifier](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) used only for `before:` & `after:` searches (eg: "Pacific/Auckland"). + // + // in: query + // required: false + // type string + TZ string `json:"tz"` +} + +// swagger:parameters DeleteMessagesParams +type deleteMessagesParams struct { + // Delete request + // in: body + Body struct { + // Array of message database IDs + // + // required: false + // example: ["4oRBnPtCXgAqZniRhzLNmS", "hXayS6wnCgNnt6aFTvmOF6"] + IDs []string + } +} + +// swagger:parameters SearchParams +type searchParams struct { + // Search query + // + // in: query + // required: true + // type: string + Query string `json:"query"` + + // Pagination offset + // + // in: query + // required: false + // default: 0 + // type integer + Start string `json:"start"` + + // Limit results + // + // in: query + // required: false + // default: 50 + // type integer + Limit string `json:"limit"` + + // Optional [timezone identifier](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) used only for `before:` & `after:` searches (eg: "Pacific/Auckland"). + // + // in: query + // required: false + // type string + TZ string `json:"tz"` +} + +// swagger:parameters DeleteSearchParams +type deleteSearchParams struct { + // Search query + // + // in: query + // required: true + // type: string + Query string `json:"query"` + + // [Timezone identifier](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) used only for `before:` & `after:` searches (eg: "Pacific/Auckland"). + // + // in: query + // required: false + // type string + TZ string `json:"tz"` +} + +// swagger:parameters HTMLCheckParams +type htmlCheckParams struct { + // Message database ID or "latest" + // + // in: path + // description: Message database ID or "latest" + // required: true + ID string +} + +// swagger:parameters LinkCheckParams +type linkCheckParams struct { + // Message database ID or "latest" + // + // in: path + // required: true + ID string + + // Follow redirects + // + // in: query + // required: false + // default: false + Follow string `json:"follow"` +} + +// swagger:parameters ReleaseMessageParams +type releaseMessageParams struct { + // Message database ID + // + // in: path + // description: Message database ID + // required: true + ID string + + // in: body + Body struct { + // Array of email addresses to relay the message to + // + // required: true + // example: ["user1@example.com", "user2@example.com"] + To []string + } +} + +// swagger:parameters SendMessageParams +type sendMessageParams struct { + // in: body + // Body SendRequest + Body struct { + // "From" recipient + // required: true + From struct { + // Optional name + // example: John Doe + Name string + // Email address + // example: john@example.com + // required: true + Email string + } + + // "To" recipients + To []struct { + // Optional name + // example: Jane Doe + Name string + // Email address + // example: jane@example.com + // required: true + Email string + } + + // Cc recipients + Cc []struct { + // Optional name + // example: Manager + Name string + // Email address + // example: manager@example.com + // required: true + Email string + } + + // Bcc recipients email addresses only + // example: ["jack@example.com"] + Bcc []string + + // Optional Reply-To recipients + ReplyTo []struct { + // Optional name + // example: Secretary + Name string + // Email address + // example: secretary@example.com + // required: true + Email string + } + + // Subject + // example: Mailpit message via the HTTP API + Subject string + + // Message body (text) + // example: Mailpit is awesome! + Text string + + // Message body (HTML) + // example:

Mailpit is awesome!

+ HTML string + + // Attachments + Attachments []struct { + // Base64-encoded string of the file content + // required: true + // example: iVBORw0KGgoAAAANSUhEUgAAAEEAAAA8CAMAAAAOlSdoAAAACXBIWXMAAAHrAAAB6wGM2bZBAAAAS1BMVEVHcEwRfnUkZ2gAt4UsSF8At4UtSV4At4YsSV4At4YsSV8At4YsSV4At4YsSV4sSV4At4YsSV4At4YtSV4At4YsSV4At4YtSV8At4YsUWYNAAAAGHRSTlMAAwoXGiktRE5dbnd7kpOlr7zJ0d3h8PD8PCSRAAACWUlEQVR42pXT4ZaqIBSG4W9rhqQYocG+/ys9Y0Z0Br+x3j8zaxUPewFh65K+7yrIMeIY4MT3wPfEJCidKXEMnLaVkxDiELiMz4WEOAZSFghxBIypCOlKiAMgXfIqTnBgSm8CIQ6BImxEUxEckClVQiHGj4Ba4AQHikAIClwTE9KtIghAhUJwoLkmLnCiAHJLRKgIMsEtVUKbBUIwoAg2C4QgQBE6l4VCnApBgSKYLLApCnCa0+96AEMW2BQcmC+Pr3nfp7o5Exy49gIADcIqUELGfeA+bp93LmAJp8QJoEcN3C7NY3sbVANixMyI0nku20/n5/ZRf3KI2k6JEDWQtxcbdGuAqu3TAXG+/799Oyyas1B1MnMiA+XyxHp9q0PUKGPiRAau1fZbLRZV09wZcT8/gHk8QQAxXn8VgaDqcUmU6O/r28nbVwXAqca2mRNtPAF5+zoP2MeN9Fy4NgC6RfcbgE7XITBRYTtOE3U3C2DVff7pk+PkUxgAbvtnPXJaD6DxulMLwOhPS/M3MQkgg1ZFrIXnmfaZoOfpKiFgzeZD/WuKqQEGrfJYkyWf6vlG3xUgTuscnkNkQsb599q124kdpMUjCa/XARHs1gZymVtGt3wLkiFv8rUgTxitYCex5EVGec0Y9VmoDTFBSQte2TfXGXlf7hbdaUM9Sk7fisEN9qfBBTK+FZcvM9fQSdkl2vj4W2oX/bRogO3XasiNH7R0eW7fgRM834ImTg+Lg6BEnx4vz81rhr+MYPBBQg1v8GndEOrthxaCTxNAOut8WKLGZQl+MPz88Q9tAO/hVuSeqQAAAABJRU5ErkJggg== + Content string + // Filename + // required: true + // example: mailpit.png + Filename string + // Optional Content Type for the the attachment. + // If this field is not set (or empty) then the content type is automatically detected. + // required: false + // example: image/png + ContentType string + // Optional Content-ID (`cid`) for attachment. + // If this field is set then the file is attached inline. + // required: false + // example: mailpit-logo + ContentID string + } + + // Mailpit tags + // example: ["Tag 1","Tag 2"] + Tags []string + + // Optional headers in {"key":"value"} format + // example: {"X-IP":"1.2.3.4"} + Headers map[string]string + } +} + +// swagger:parameters SetTagsParams +type setTagsParams struct { + // in: body + Body struct { + // Array of tag names to set + // + // required: true + // example: ["Tag 1", "Tag 2"] + Tags []string + + // Array of message database IDs + // + // required: true + // example: ["4oRBnPtCXgAqZniRhzLNmS", "hXayS6wnCgNnt6aFTvmOF6"] + IDs []string + } +} + +// swagger:parameters RenameTagParams +type renameTagParams struct { + // The url-encoded tag name to rename + // + // in: path + // required: true + // type: string + Tag string + + // in: body + Body struct { + // New name + // + // required: true + // example: New name + Name string + } +} + +// swagger:parameters DeleteTagParams +type deleteTagParams struct { + // The url-encoded tag name to delete + // + // in: path + // required: true + Tag string +} + +// swagger:parameters GetMessageHTMLParams +type getMessageHTMLParams struct { + // Message database ID or "latest" + // + // in: path + // required: true + ID string + + // If this is route is to be embedded in an iframe, set embed to `1` in the URL to 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. + // + // in: query + // required: false + // type: string + Embed string `json:"embed"` +} + +// swagger:parameters GetMessageTextParams +type getMessageTextParams struct { + // Message database ID or "latest" + // + // in: path + // required: true + ID string +} + +// swagger:parameters SpamAssassinCheckParams +type spamAssassinCheckParams struct { + // Message database ID or "latest" + // + // in: path + // required: true + ID string +} + +// swagger:parameters ThumbnailParams +type thumbnailParams struct { + // Message database ID or "latest" + // + // in: path + // required: true + ID string + + // Attachment part ID + // + // in: path + // required: true + PartID string +} diff --git a/server/apiv1/swaggerResponses.go b/server/apiv1/swaggerResponses.go new file mode 100644 index 0000000..274653d --- /dev/null +++ b/server/apiv1/swaggerResponses.go @@ -0,0 +1,142 @@ +// Package apiv1 provides the API v1 endpoints for Mailpit. +// +// These structs are for the purpose of defining swagger HTTP responses in go-swagger +// in order to generate a spec file. They are lowercased to avoid exporting them as public types. +// +//nolint:unused +package apiv1 + +import ( + "github.com/axllent/mailpit/internal/smtpd/chaos" + "github.com/axllent/mailpit/internal/stats" +) + +// Binary data response which inherits the attachment's content type. +// swagger:response BinaryResponse +type binaryResponse string + +// Plain text response +// swagger:response TextResponse +type textResponse string + +// HTML response +// swagger:response HTMLResponse +type htmlResponse string + +// Server error will return with a 400 status code +// with the error message in the body +// swagger:response ErrorResponse +type errorResponse string + +// Not found error will return a 404 status code +// swagger:response NotFoundResponse +type notFoundResponse string + +// Plain text "ok" response +// swagger:response OKResponse +type okResponse string + +// Plain JSON array response +// swagger:response ArrayResponse +type arrayResponse []string + +// JSON error response +// swagger:response JSONErrorResponse +type jsonErrorResponse struct { + // A JSON-encoded error response + // + // in: body + Body struct { + // Error message + // example: invalid format + Error string + } +} + +// Web UI configuration response +// swagger:response WebUIConfigurationResponse +type webUIConfigurationResponse struct { + // Web UI configuration settings + // + // in: body + Body struct { + // Optional label to identify this Mailpit instance + Label string + // Message Relay information + MessageRelay struct { + // Whether message relaying (release) is enabled + Enabled bool + // The configured SMTP server address + SMTPServer string + // Enforced Return-Path (if set) for relay bounces + ReturnPath string + // Only allow relaying to these recipients (regex) + AllowedRecipients string + // Block relaying to these recipients (regex) + BlockedRecipients string + // Overrides the "From" address for all relayed messages + OverrideFrom string + // Preserve the original Message-IDs when relaying messages + PreserveMessageIDs bool + + // DEPRECATED 2024/03/12 + // swagger:ignore + RecipientAllowlist string + } + + // Whether SpamAssassin is enabled + SpamAssassin bool + + // Whether Chaos support is enabled at runtime + ChaosEnabled bool + + // Whether messages with duplicate IDs are ignored + DuplicatesIgnored bool + + // Whether the delete button should be hidden + HideDeleteAllButton bool + } +} + +// Application information +// swagger:response AppInfoResponse +type appInfoResponse struct { + // Application information + // + // in: body + Body stats.AppInformation +} + +// Response for the Chaos triggers configuration +// swagger:response ChaosResponse +type chaosResponse struct { + // The current Chaos triggers + // + // in: body + Body chaos.Triggers +} + +// Message headers +// swagger:model MessageHeadersResponse +type messageHeadersResponse map[string][]string + +// Summary of messages +// swagger:response MessagesSummaryResponse +type messagesSummaryResponse struct { + // The messages summary + // in: body + Body MessagesSummary +} + +// Confirmation message for HTTP send API +// swagger:response SendMessageResponse +type sendMessageResponse struct { + // Response for sending messages via the HTTP API + // + // in: body + Body struct { + // Database ID + // example: iAfZVVe2UQfNSG5BAjgYwa + ID string + } +} diff --git a/server/apiv1/tags.go b/server/apiv1/tags.go index cd58c64..5c59cc6 100644 --- a/server/apiv1/tags.go +++ b/server/apiv1/tags.go @@ -32,24 +32,6 @@ func GetAllTags(w http.ResponseWriter, _ *http.Request) { } } -// swagger:parameters SetTagsParams -type setTagsParams struct { - // in: body - Body struct { - // Array of tag names to set - // - // required: true - // example: ["Tag 1", "Tag 2"] - Tags []string - - // Array of message database IDs - // - // required: true - // example: ["4oRBnPtCXgAqZniRhzLNmS", "hXayS6wnCgNnt6aFTvmOF6"] - IDs []string - } -} - // SetMessageTags (method: PUT) will set the tags for all provided IDs func SetMessageTags(w http.ResponseWriter, r *http.Request) { // swagger:route PUT /api/v1/tags tags SetTagsParams @@ -98,25 +80,6 @@ func SetMessageTags(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("ok")) } -// swagger:parameters RenameTagParams -type renameTagParams struct { - // The url-encoded tag name to rename - // - // in: path - // required: true - // type: string - Tag string - - // in: body - Body struct { - // New name - // - // required: true - // example: New name - Name string - } -} - // RenameTag (method: PUT) used to rename a tag func RenameTag(w http.ResponseWriter, r *http.Request) { // swagger:route PUT /api/v1/tags/{Tag} tags RenameTagParams @@ -161,15 +124,6 @@ func RenameTag(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("ok")) } -// swagger:parameters DeleteTagParams -type deleteTagParams struct { - // The url-encoded tag name to delete - // - // in: path - // required: true - Tag string -} - // DeleteTag (method: DELETE) used to delete a tag func DeleteTag(w http.ResponseWriter, r *http.Request) { // swagger:route DELETE /api/v1/tags/{Tag} tags DeleteTagParams diff --git a/server/apiv1/testing.go b/server/apiv1/testing.go index 80db613..a9b255f 100644 --- a/server/apiv1/testing.go +++ b/server/apiv1/testing.go @@ -16,26 +16,6 @@ import ( "golang.org/x/net/html/atom" ) -// swagger:parameters GetMessageHTMLParams -type getMessageHTMLParams struct { - // Message database ID or "latest" - // - // in: path - // required: true - ID string - - // If this is route is to be embedded in an iframe, set embed to `1` in the URL to 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. - // - // in: query - // required: false - // type: string - Embed string `json:"embed"` -} - // GetMessageHTML (method: GET) returns a rendered version of a message's HTML part func GetMessageHTML(w http.ResponseWriter, r *http.Request) { // swagger:route GET /view/{ID}.html testing GetMessageHTMLParams @@ -123,15 +103,6 @@ func GetMessageHTML(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte(htmlStr)) } -// swagger:parameters GetMessageTextParams -type getMessageTextParams struct { - // Message database ID or "latest" - // - // in: path - // required: true - ID string -} - // GetMessageText (method: GET) returns a message's text part func GetMessageText(w http.ResponseWriter, r *http.Request) { // swagger:route GET /view/{ID}.txt testing GetMessageTextParams diff --git a/server/apiv1/thumbnails.go b/server/apiv1/thumbnails.go index 97920b5..af98206 100644 --- a/server/apiv1/thumbnails.go +++ b/server/apiv1/thumbnails.go @@ -22,21 +22,6 @@ var ( thumbHeight = 120 ) -// swagger:parameters ThumbnailParams -type thumbnailParams struct { - // Message database ID or "latest" - // - // in: path - // required: true - ID string - - // Attachment part ID - // - // in: path - // required: true - PartID string -} - // Thumbnail returns a thumbnail image for an attachment (images only) func Thumbnail(w http.ResponseWriter, r *http.Request) { // swagger:route GET /api/v1/message/{ID}/part/{PartID}/thumb message ThumbnailParams diff --git a/server/server_test.go b/server/server_test.go index ccc3799..a4ee108 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -283,7 +283,9 @@ func TestAPIv1Send(t *testing.T) { t.Errorf("Expected nil, received %s", err.Error()) } - resp := apiv1.SendMessageConfirmation{} + resp := struct { + ID string + }{} if err := json.Unmarshal(b, &resp); err != nil { t.Error(err.Error()) diff --git a/server/ui/api/v1/swagger.json b/server/ui/api/v1/swagger.json index c370deb..e5fc5fd 100644 --- a/server/ui/api/v1/swagger.json +++ b/server/ui/api/v1/swagger.json @@ -785,16 +785,185 @@ "name": "Body", "in": "body", "schema": { - "$ref": "#/definitions/SendRequest" + "type": "object", + "required": [ + "From" + ], + "properties": { + "Attachments": { + "description": "Attachments", + "type": "array", + "items": { + "type": "object", + "required": [ + "Content", + "Filename" + ], + "properties": { + "Content": { + "description": "Base64-encoded string of the file content", + "type": "string", + "example": "iVBORw0KGgoAAAANSUhEUgAAAEEAAAA8CAMAAAAOlSdoAAAACXBIWXMAAAHrAAAB6wGM2bZBAAAAS1BMVEVHcEwRfnUkZ2gAt4UsSF8At4UtSV4At4YsSV4At4YsSV8At4YsSV4At4YsSV4sSV4At4YsSV4At4YtSV4At4YsSV4At4YtSV8At4YsUWYNAAAAGHRSTlMAAwoXGiktRE5dbnd7kpOlr7zJ0d3h8PD8PCSRAAACWUlEQVR42pXT4ZaqIBSG4W9rhqQYocG+/ys9Y0Z0Br+x3j8zaxUPewFh65K+7yrIMeIY4MT3wPfEJCidKXEMnLaVkxDiELiMz4WEOAZSFghxBIypCOlKiAMgXfIqTnBgSm8CIQ6BImxEUxEckClVQiHGj4Ba4AQHikAIClwTE9KtIghAhUJwoLkmLnCiAHJLRKgIMsEtVUKbBUIwoAg2C4QgQBE6l4VCnApBgSKYLLApCnCa0+96AEMW2BQcmC+Pr3nfp7o5Exy49gIADcIqUELGfeA+bp93LmAJp8QJoEcN3C7NY3sbVANixMyI0nku20/n5/ZRf3KI2k6JEDWQtxcbdGuAqu3TAXG+/799Oyyas1B1MnMiA+XyxHp9q0PUKGPiRAau1fZbLRZV09wZcT8/gHk8QQAxXn8VgaDqcUmU6O/r28nbVwXAqca2mRNtPAF5+zoP2MeN9Fy4NgC6RfcbgE7XITBRYTtOE3U3C2DVff7pk+PkUxgAbvtnPXJaD6DxulMLwOhPS/M3MQkgg1ZFrIXnmfaZoOfpKiFgzeZD/WuKqQEGrfJYkyWf6vlG3xUgTuscnkNkQsb599q124kdpMUjCa/XARHs1gZymVtGt3wLkiFv8rUgTxitYCex5EVGec0Y9VmoDTFBSQte2TfXGXlf7hbdaUM9Sk7fisEN9qfBBTK+FZcvM9fQSdkl2vj4W2oX/bRogO3XasiNH7R0eW7fgRM834ImTg+Lg6BEnx4vz81rhr+MYPBBQg1v8GndEOrthxaCTxNAOut8WKLGZQl+MPz88Q9tAO/hVuSeqQAAAABJRU5ErkJggg==" + }, + "ContentID": { + "description": "Optional Content-ID (`cid`) for attachment.\nIf this field is set then the file is attached inline.", + "type": "string", + "example": "mailpit-logo" + }, + "ContentType": { + "description": "Optional Content Type for the the attachment.\nIf this field is not set (or empty) then the content type is automatically detected.", + "type": "string", + "example": "image/png" + }, + "Filename": { + "description": "Filename", + "type": "string", + "example": "mailpit.png" + } + } + } + }, + "Bcc": { + "description": "Bcc recipients email addresses only", + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "jack@example.com" + ] + }, + "Cc": { + "description": "Cc recipients", + "type": "array", + "items": { + "type": "object", + "required": [ + "Email" + ], + "properties": { + "Email": { + "description": "Email address", + "type": "string", + "example": "manager@example.com" + }, + "Name": { + "description": "Optional name", + "type": "string", + "example": "Manager" + } + } + } + }, + "From": { + "description": "\"From\" recipient", + "type": "object", + "required": [ + "Email" + ], + "properties": { + "Email": { + "description": "Email address", + "type": "string", + "example": "john@example.com" + }, + "Name": { + "description": "Optional name", + "type": "string", + "example": "John Doe" + } + } + }, + "HTML": { + "description": "Message body (HTML)", + "type": "string", + "example": "\u003cdiv style=\"text-align:center\"\u003e\u003cp style=\"font-family: arial; font-size: 24px;\"\u003eMailpit is \u003cb\u003eawesome\u003c/b\u003e!\u003c/p\u003e\u003cp\u003e\u003cimg src=\"cid:mailpit-logo\" /\u003e\u003c/p\u003e\u003c/div\u003e" + }, + "Headers": { + "description": "Optional headers in {\"key\":\"value\"} format", + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "X-IP": "1.2.3.4" + } + }, + "ReplyTo": { + "description": "Optional Reply-To recipients", + "type": "array", + "items": { + "type": "object", + "required": [ + "Email" + ], + "properties": { + "Email": { + "description": "Email address", + "type": "string", + "example": "secretary@example.com" + }, + "Name": { + "description": "Optional name", + "type": "string", + "example": "Secretary" + } + } + } + }, + "Subject": { + "description": "Subject", + "type": "string", + "example": "Mailpit message via the HTTP API" + }, + "Tags": { + "description": "Mailpit tags", + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "Tag 1", + "Tag 2" + ] + }, + "Text": { + "description": "Message body (text)", + "type": "string", + "example": "Mailpit is awesome!" + }, + "To": { + "description": "\"To\" recipients", + "type": "array", + "items": { + "type": "object", + "required": [ + "Email" + ], + "properties": { + "Email": { + "description": "Email address", + "type": "string", + "example": "jane@example.com" + }, + "Name": { + "description": "Optional name", + "type": "string", + "example": "Jane Doe" + } + } + } + } + } } } ], "responses": { "200": { - "$ref": "#/responses/sendMessageResponse" + "$ref": "#/responses/SendMessageResponse" }, "400": { - "$ref": "#/responses/jsonErrorResponse" + "$ref": "#/responses/JSONErrorResponse" } } } @@ -984,7 +1153,7 @@ "application" ], "summary": "Get web UI configuration", - "operationId": "WebUIConfiguration", + "operationId": "WebUIConfigurationResponse", "responses": { "200": { "$ref": "#/responses/WebUIConfigurationResponse" @@ -1203,9 +1372,46 @@ }, "x-go-package": "github.com/axllent/mailpit/internal/storage" }, + "ChaosTrigger": { + "description": "Trigger for Chaos", + "type": "object", + "required": [ + "ErrorCode", + "Probability" + ], + "properties": { + "ErrorCode": { + "description": "SMTP error code to return. The value must range from 400 to 599.", + "type": "integer", + "format": "int64", + "example": 451 + }, + "Probability": { + "description": "Probability (chance) of triggering the error. The value must range from 0 to 100.", + "type": "integer", + "format": "int64", + "example": 5 + } + }, + "x-go-name": "Trigger", + "x-go-package": "github.com/axllent/mailpit/internal/smtpd/chaos" + }, "ChaosTriggers": { - "description": "ChaosTriggers are the Chaos triggers", - "$ref": "#/definitions/Triggers" + "description": "Triggers for the Chaos configuration", + "type": "object", + "properties": { + "Authentication": { + "$ref": "#/definitions/ChaosTrigger" + }, + "Recipient": { + "$ref": "#/definitions/ChaosTrigger" + }, + "Sender": { + "$ref": "#/definitions/ChaosTrigger" + } + }, + "x-go-name": "Triggers", + "x-go-package": "github.com/axllent/mailpit/internal/smtpd/chaos" }, "HTMLCheckResponse": { "description": "Response represents the HTML check response struct", @@ -1384,18 +1590,6 @@ "x-go-name": "Warning", "x-go-package": "github.com/axllent/mailpit/internal/htmlcheck" }, - "JSONErrorMessage": { - "description": "JSONErrorMessage struct", - "type": "object", - "properties": { - "Error": { - "description": "Error message", - "type": "string", - "example": "invalid format" - } - }, - "x-go-package": "github.com/axllent/mailpit/server/apiv1" - }, "Link": { "description": "Link struct", "type": "object", @@ -1571,7 +1765,7 @@ "type": "string" } }, - "x-go-name": "messageHeaders", + "x-go-name": "messageHeadersResponse", "x-go-package": "github.com/axllent/mailpit/server/apiv1" }, "MessageSummary": { @@ -1731,192 +1925,6 @@ }, "x-go-package": "github.com/axllent/mailpit/internal/spamassassin" }, - "SendMessageConfirmation": { - "description": "SendMessageConfirmation struct", - "type": "object", - "properties": { - "ID": { - "description": "Database ID", - "type": "string", - "example": "iAfZVVe2UQfNSG5BAjgYwa" - } - }, - "x-go-package": "github.com/axllent/mailpit/server/apiv1" - }, - "SendRequest": { - "description": "SendRequest to send a message via HTTP", - "type": "object", - "required": [ - "From" - ], - "properties": { - "Attachments": { - "description": "Attachments", - "type": "array", - "items": { - "type": "object", - "required": [ - "Content", - "Filename" - ], - "properties": { - "Content": { - "description": "Base64-encoded string of the file content", - "type": "string", - "example": "iVBORw0KGgoAAAANSUhEUgAAAEEAAAA8CAMAAAAOlSdoAAAACXBIWXMAAAHrAAAB6wGM2bZBAAAAS1BMVEVHcEwRfnUkZ2gAt4UsSF8At4UtSV4At4YsSV4At4YsSV8At4YsSV4At4YsSV4sSV4At4YsSV4At4YtSV4At4YsSV4At4YtSV8At4YsUWYNAAAAGHRSTlMAAwoXGiktRE5dbnd7kpOlr7zJ0d3h8PD8PCSRAAACWUlEQVR42pXT4ZaqIBSG4W9rhqQYocG+/ys9Y0Z0Br+x3j8zaxUPewFh65K+7yrIMeIY4MT3wPfEJCidKXEMnLaVkxDiELiMz4WEOAZSFghxBIypCOlKiAMgXfIqTnBgSm8CIQ6BImxEUxEckClVQiHGj4Ba4AQHikAIClwTE9KtIghAhUJwoLkmLnCiAHJLRKgIMsEtVUKbBUIwoAg2C4QgQBE6l4VCnApBgSKYLLApCnCa0+96AEMW2BQcmC+Pr3nfp7o5Exy49gIADcIqUELGfeA+bp93LmAJp8QJoEcN3C7NY3sbVANixMyI0nku20/n5/ZRf3KI2k6JEDWQtxcbdGuAqu3TAXG+/799Oyyas1B1MnMiA+XyxHp9q0PUKGPiRAau1fZbLRZV09wZcT8/gHk8QQAxXn8VgaDqcUmU6O/r28nbVwXAqca2mRNtPAF5+zoP2MeN9Fy4NgC6RfcbgE7XITBRYTtOE3U3C2DVff7pk+PkUxgAbvtnPXJaD6DxulMLwOhPS/M3MQkgg1ZFrIXnmfaZoOfpKiFgzeZD/WuKqQEGrfJYkyWf6vlG3xUgTuscnkNkQsb599q124kdpMUjCa/XARHs1gZymVtGt3wLkiFv8rUgTxitYCex5EVGec0Y9VmoDTFBSQte2TfXGXlf7hbdaUM9Sk7fisEN9qfBBTK+FZcvM9fQSdkl2vj4W2oX/bRogO3XasiNH7R0eW7fgRM834ImTg+Lg6BEnx4vz81rhr+MYPBBQg1v8GndEOrthxaCTxNAOut8WKLGZQl+MPz88Q9tAO/hVuSeqQAAAABJRU5ErkJggg==" - }, - "ContentID": { - "description": "Optional Content-ID (`cid`) for attachment.\nIf this field is set then the file is attached inline.", - "type": "string", - "example": "mailpit-logo" - }, - "ContentType": { - "description": "Optional Content Type for the the attachment.\nIf this field is not set (or empty) then the content type is automatically detected.", - "type": "string", - "example": "image/png" - }, - "Filename": { - "description": "Filename", - "type": "string", - "example": "mailpit.png" - } - } - } - }, - "Bcc": { - "description": "Bcc recipients email addresses only", - "type": "array", - "items": { - "type": "string" - }, - "example": [ - "jack@example.com" - ] - }, - "Cc": { - "description": "Cc recipients", - "type": "array", - "items": { - "type": "object", - "required": [ - "Email" - ], - "properties": { - "Email": { - "description": "Email address", - "type": "string", - "example": "manager@example.com" - }, - "Name": { - "description": "Optional name", - "type": "string", - "example": "Manager" - } - } - } - }, - "From": { - "description": "\"From\" recipient", - "type": "object", - "required": [ - "Email" - ], - "properties": { - "Email": { - "description": "Email address", - "type": "string", - "example": "john@example.com" - }, - "Name": { - "description": "Optional name", - "type": "string", - "example": "John Doe" - } - } - }, - "HTML": { - "description": "Message body (HTML)", - "type": "string", - "example": "\u003cdiv style=\"text-align:center\"\u003e\u003cp style=\"font-family: arial; font-size: 24px;\"\u003eMailpit is \u003cb\u003eawesome\u003c/b\u003e!\u003c/p\u003e\u003cp\u003e\u003cimg src=\"cid:mailpit-logo\" /\u003e\u003c/p\u003e\u003c/div\u003e" - }, - "Headers": { - "description": "Optional headers in {\"key\":\"value\"} format", - "type": "object", - "additionalProperties": { - "type": "string" - }, - "example": { - "X-IP": "1.2.3.4" - } - }, - "ReplyTo": { - "description": "Optional Reply-To recipients", - "type": "array", - "items": { - "type": "object", - "required": [ - "Email" - ], - "properties": { - "Email": { - "description": "Email address", - "type": "string", - "example": "secretary@example.com" - }, - "Name": { - "description": "Optional name", - "type": "string", - "example": "Secretary" - } - } - } - }, - "Subject": { - "description": "Subject", - "type": "string", - "example": "Mailpit message via the HTTP API" - }, - "Tags": { - "description": "Mailpit tags", - "type": "array", - "items": { - "type": "string" - }, - "example": [ - "Tag 1", - "Tag 2" - ] - }, - "Text": { - "description": "Message body (text)", - "type": "string", - "example": "Mailpit is awesome!" - }, - "To": { - "description": "\"To\" recipients", - "type": "array", - "items": { - "type": "object", - "required": [ - "Email" - ], - "properties": { - "Email": { - "description": "Email address", - "type": "string", - "example": "jane@example.com" - }, - "Name": { - "description": "Optional name", - "type": "string", - "example": "Jane Doe" - } - } - } - } - }, - "x-go-package": "github.com/axllent/mailpit/server/apiv1" - }, "SpamAssassinResponse": { "description": "Result is a SpamAssassin result", "type": "object", @@ -1944,107 +1952,6 @@ }, "x-go-name": "Result", "x-go-package": "github.com/axllent/mailpit/internal/spamassassin" - }, - "Trigger": { - "description": "Trigger for Chaos", - "type": "object", - "required": [ - "ErrorCode", - "Probability" - ], - "properties": { - "ErrorCode": { - "description": "SMTP error code to return. The value must range from 400 to 599.", - "type": "integer", - "format": "int64", - "example": 451 - }, - "Probability": { - "description": "Probability (chance) of triggering the error. The value must range from 0 to 100.", - "type": "integer", - "format": "int64", - "example": 5 - } - }, - "x-go-package": "github.com/axllent/mailpit/internal/smtpd/chaos" - }, - "Triggers": { - "description": "Triggers for the Chaos configuration", - "type": "object", - "properties": { - "Authentication": { - "$ref": "#/definitions/Trigger" - }, - "Recipient": { - "$ref": "#/definitions/Trigger" - }, - "Sender": { - "$ref": "#/definitions/Trigger" - } - }, - "x-go-package": "github.com/axllent/mailpit/internal/smtpd/chaos" - }, - "WebUIConfiguration": { - "description": "Response includes global web UI settings", - "type": "object", - "properties": { - "ChaosEnabled": { - "description": "Whether Chaos support is enabled at runtime", - "type": "boolean" - }, - "DuplicatesIgnored": { - "description": "Whether messages with duplicate IDs are ignored", - "type": "boolean" - }, - "HideDeleteAllButton": { - "description": "Whether the delete button should be hidden", - "type": "boolean" - }, - "Label": { - "description": "Optional label to identify this Mailpit instance", - "type": "string" - }, - "MessageRelay": { - "description": "Message Relay information", - "type": "object", - "properties": { - "AllowedRecipients": { - "description": "Only allow relaying to these recipients (regex)", - "type": "string" - }, - "BlockedRecipients": { - "description": "Block relaying to these recipients (regex)", - "type": "string" - }, - "Enabled": { - "description": "Whether message relaying (release) is enabled", - "type": "boolean" - }, - "OverrideFrom": { - "description": "Overrides the \"From\" address for all relayed messages", - "type": "string" - }, - "PreserveMessageIDs": { - "description": "Preserve the original Message-IDs when relaying messages", - "type": "boolean" - }, - "ReturnPath": { - "description": "Enforced Return-Path (if set) for relay bounces", - "type": "string" - }, - "SMTPServer": { - "description": "The configured SMTP server address", - "type": "string" - } - } - }, - "SpamAssassin": { - "description": "Whether SpamAssassin is enabled", - "type": "boolean" - } - }, - "x-go-name": "webUIConfiguration", - "x-go-package": "github.com/axllent/mailpit/server/apiv1" } }, "responses": { @@ -2087,6 +1994,19 @@ "type": "string" } }, + "JSONErrorResponse": { + "description": "JSON error response", + "schema": { + "type": "object", + "properties": { + "Error": { + "description": "Error message", + "type": "string", + "example": "invalid format" + } + } + } + }, "MessagesSummaryResponse": { "description": "Summary of messages", "schema": { @@ -2105,6 +2025,19 @@ "type": "string" } }, + "SendMessageResponse": { + "description": "Confirmation message for HTTP send API", + "schema": { + "type": "object", + "properties": { + "ID": { + "description": "Database ID", + "type": "string", + "example": "iAfZVVe2UQfNSG5BAjgYwa" + } + } + } + }, "TextResponse": { "description": "Plain text response", "schema": { @@ -2114,19 +2047,63 @@ "WebUIConfigurationResponse": { "description": "Web UI configuration response", "schema": { - "$ref": "#/definitions/WebUIConfiguration" - } - }, - "jsonErrorResponse": { - "description": "JSON error response", - "schema": { - "$ref": "#/definitions/JSONErrorMessage" - } - }, - "sendMessageResponse": { - "description": "Confirmation message for HTTP send API", - "schema": { - "$ref": "#/definitions/SendMessageConfirmation" + "type": "object", + "properties": { + "ChaosEnabled": { + "description": "Whether Chaos support is enabled at runtime", + "type": "boolean" + }, + "DuplicatesIgnored": { + "description": "Whether messages with duplicate IDs are ignored", + "type": "boolean" + }, + "HideDeleteAllButton": { + "description": "Whether the delete button should be hidden", + "type": "boolean" + }, + "Label": { + "description": "Optional label to identify this Mailpit instance", + "type": "string" + }, + "MessageRelay": { + "description": "Message Relay information", + "type": "object", + "properties": { + "AllowedRecipients": { + "description": "Only allow relaying to these recipients (regex)", + "type": "string" + }, + "BlockedRecipients": { + "description": "Block relaying to these recipients (regex)", + "type": "string" + }, + "Enabled": { + "description": "Whether message relaying (release) is enabled", + "type": "boolean" + }, + "OverrideFrom": { + "description": "Overrides the \"From\" address for all relayed messages", + "type": "string" + }, + "PreserveMessageIDs": { + "description": "Preserve the original Message-IDs when relaying messages", + "type": "boolean" + }, + "ReturnPath": { + "description": "Enforced Return-Path (if set) for relay bounces", + "type": "string" + }, + "SMTPServer": { + "description": "The configured SMTP server address", + "type": "string" + } + } + }, + "SpamAssassin": { + "description": "Whether SpamAssassin is enabled", + "type": "boolean" + } + } } } }