mirror of
https://github.com/axllent/mailpit.git
synced 2025-02-01 13:08:02 +02:00
Merge branch 'release/v1.5.0'
This commit is contained in:
commit
c4a695e627
19
CHANGELOG.md
19
CHANGELOG.md
@ -2,6 +2,22 @@
|
||||
|
||||
Notable changes to Mailpit will be documented in this file.
|
||||
|
||||
## [v1.5.0]
|
||||
|
||||
### API
|
||||
- Return received datetime when message does not contain a date header
|
||||
|
||||
### Bugfix
|
||||
- Fix JavaScript error when adding the first tag manually
|
||||
|
||||
### Feature
|
||||
- OpenAPI / Swagger schema
|
||||
- Download raw message, HTML/text body parts or attachments via single button
|
||||
- Rename SSL to TLS, add deprecation warnings to flags & ENV variables referring to SSL
|
||||
- Options to support auth without STARTTLS, and accept any login
|
||||
- Option to use message dates as received dates (new messages only)
|
||||
|
||||
|
||||
## [v1.4.0]
|
||||
|
||||
### API
|
||||
@ -427,6 +443,3 @@ This release includes a major backend storage change (SQLite) that will render a
|
||||
|
||||
### Feature
|
||||
- Unread statistics
|
||||
|
||||
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
![CodeQL](https://github.com/axllent/mailpit/actions/workflows/codeql-analysis.yml/badge.svg)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/axllent/mailpit)](https://goreportcard.com/report/github.com/axllent/mailpit)
|
||||
|
||||
Mailpit is a multi-platform email testing tool 7 API for developers.
|
||||
Mailpit is a multi-platform email testing tool & API for developers.
|
||||
|
||||
It acts as both an SMTP server, and provides a web interface to view all captured emails.
|
||||
|
||||
|
@ -4,6 +4,8 @@ Mailpit provides a simple REST API to access and delete stored messages.
|
||||
|
||||
If the Mailpit server is set to use Basic Authentication, then API requests must use Basic Authentication too.
|
||||
|
||||
You can view the Swagger API documentation directly within Mailpit by going to `http://0.0.0.0:8025/api/v1/`.
|
||||
|
||||
The API is split into three main parts:
|
||||
|
||||
- [Messages](Messages.md) - Listing, deleting & marking messages as read/unread.
|
||||
|
@ -5,20 +5,25 @@ import { sassPlugin } from 'esbuild-sass-plugin'
|
||||
const doWatch = process.env.WATCH == 'true' ? true : false;
|
||||
const doMinify = process.env.MINIFY == 'true' ? true : false;
|
||||
|
||||
const ctx = await esbuild.context({
|
||||
entryPoints: ["server/ui-src/app.js"],
|
||||
bundle: true,
|
||||
minify: doMinify,
|
||||
sourcemap: false,
|
||||
outfile: "server/ui/dist/app.js",
|
||||
plugins: [pluginVue(), sassPlugin()],
|
||||
loader: {
|
||||
".svg": "file",
|
||||
".woff": "file",
|
||||
".woff2": "file",
|
||||
},
|
||||
logLevel: "info"
|
||||
})
|
||||
const ctx = await esbuild.context(
|
||||
{
|
||||
entryPoints: [
|
||||
"server/ui-src/app.js",
|
||||
"server/ui-src/docs.js"
|
||||
],
|
||||
bundle: true,
|
||||
minify: doMinify,
|
||||
sourcemap: false,
|
||||
outdir: "server/ui/dist/",
|
||||
plugins: [pluginVue(), sassPlugin()],
|
||||
loader: {
|
||||
".svg": "file",
|
||||
".woff": "file",
|
||||
".woff2": "file",
|
||||
},
|
||||
logLevel: "info"
|
||||
}
|
||||
)
|
||||
|
||||
if (doWatch) {
|
||||
await ctx.watch()
|
||||
|
2717
package-lock.json
generated
2717
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,6 +14,7 @@
|
||||
"bootstrap5-tags": "^1.4.41",
|
||||
"moment": "^2.29.4",
|
||||
"prismjs": "^1.29.0",
|
||||
"rapidoc": "^9.3.4",
|
||||
"tinycon": "^0.6.8",
|
||||
"vue": "^3.2.13"
|
||||
},
|
||||
|
@ -16,6 +16,34 @@ import (
|
||||
|
||||
// GetMessages returns a paginated list of messages as JSON
|
||||
func GetMessages(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:route GET /api/v1/messages messages GetMessages
|
||||
//
|
||||
// # List messages
|
||||
//
|
||||
// Returns messages from the mailbox ordered from newest to oldest.
|
||||
//
|
||||
// Produces:
|
||||
// - application/json
|
||||
//
|
||||
// Schemes: http, https
|
||||
//
|
||||
// Parameters:
|
||||
// + name: start
|
||||
// in: query
|
||||
// description: pagination offset
|
||||
// required: false
|
||||
// type: integer
|
||||
// default: 0
|
||||
// + name: limit
|
||||
// in: query
|
||||
// description: limit results
|
||||
// required: false
|
||||
// type: integer
|
||||
// default: 50
|
||||
//
|
||||
// Responses:
|
||||
// 200: MessagesSummaryResponse
|
||||
// default: ErrorResponse
|
||||
start, limit := getStartLimit(r)
|
||||
|
||||
messages, err := storage.List(start, limit)
|
||||
@ -40,11 +68,38 @@ func GetMessages(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write(bytes)
|
||||
}
|
||||
|
||||
// Search returns up to 200 of the latest messages as JSON
|
||||
// Search returns the latest messages as JSON
|
||||
func Search(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:route GET /api/v1/search messages MessagesSummary
|
||||
//
|
||||
// # Search messages
|
||||
//
|
||||
// Returns the latest messages matching a search.
|
||||
//
|
||||
// Produces:
|
||||
// - application/json
|
||||
//
|
||||
// Schemes: http, https
|
||||
//
|
||||
// Parameters:
|
||||
// + name: query
|
||||
// in: query
|
||||
// description: search query
|
||||
// required: true
|
||||
// type: string
|
||||
// + name: limit
|
||||
// in: query
|
||||
// description: limit results
|
||||
// required: false
|
||||
// type: integer
|
||||
// default: 50
|
||||
//
|
||||
// Responses:
|
||||
// 200: MessagesSummaryResponse
|
||||
// default: ErrorResponse
|
||||
search := strings.TrimSpace(r.URL.Query().Get("query"))
|
||||
if search == "" {
|
||||
fourOFour(w)
|
||||
httpError(w, "Error: no search query")
|
||||
return
|
||||
}
|
||||
|
||||
@ -72,15 +127,37 @@ func Search(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write(bytes)
|
||||
}
|
||||
|
||||
// GetMessage (method: GET) returns the *data.Message as JSON
|
||||
// GetMessage (method: GET) returns the Message as JSON
|
||||
func GetMessage(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:route GET /api/v1/message/{ID} message Message
|
||||
//
|
||||
// # Get message summary
|
||||
//
|
||||
// Returns the summary of a message, marking the message as read.
|
||||
//
|
||||
// Produces:
|
||||
// - application/json
|
||||
//
|
||||
// Schemes: http, https
|
||||
//
|
||||
// Parameters:
|
||||
// + name: ID
|
||||
// in: path
|
||||
// description: message id
|
||||
// required: true
|
||||
// type: string
|
||||
//
|
||||
// Responses:
|
||||
// 200: Message
|
||||
// default: ErrorResponse
|
||||
|
||||
vars := mux.Vars(r)
|
||||
|
||||
id := vars["id"]
|
||||
|
||||
msg, err := storage.GetMessage(id)
|
||||
if err != nil {
|
||||
httpError(w, "Message not found")
|
||||
fourOFour(w)
|
||||
return
|
||||
}
|
||||
|
||||
@ -91,6 +168,35 @@ func GetMessage(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// 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 Attachment
|
||||
//
|
||||
// # Get message attachment
|
||||
//
|
||||
// This will return the attachment part using the appropriate Content-Type.
|
||||
//
|
||||
// Produces:
|
||||
// - application/*
|
||||
// - image/*
|
||||
// - text/*
|
||||
//
|
||||
// Schemes: http, https
|
||||
//
|
||||
// Parameters:
|
||||
// + name: ID
|
||||
// in: path
|
||||
// description: message id
|
||||
// required: true
|
||||
// type: string
|
||||
// + name: PartID
|
||||
// in: path
|
||||
// description: attachment part id
|
||||
// required: true
|
||||
// type: string
|
||||
//
|
||||
// Responses:
|
||||
// 200: BinaryResponse
|
||||
// default: ErrorResponse
|
||||
|
||||
vars := mux.Vars(r)
|
||||
|
||||
id := vars["id"]
|
||||
@ -98,7 +204,7 @@ func DownloadAttachment(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
a, err := storage.GetAttachmentPart(id, partID)
|
||||
if err != nil {
|
||||
httpError(w, err.Error())
|
||||
fourOFour(w)
|
||||
return
|
||||
}
|
||||
fileName := a.FileName
|
||||
@ -111,15 +217,37 @@ func DownloadAttachment(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write(a.Content)
|
||||
}
|
||||
|
||||
// Headers (method: GET) returns the message headers as JSON
|
||||
func Headers(w http.ResponseWriter, r *http.Request) {
|
||||
// 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 Headers
|
||||
//
|
||||
// # Get message headers
|
||||
//
|
||||
// Returns the message headers as an array.
|
||||
//
|
||||
// Produces:
|
||||
// - application/json
|
||||
//
|
||||
// Schemes: http, https
|
||||
//
|
||||
// Parameters:
|
||||
// + name: ID
|
||||
// in: path
|
||||
// description: message id
|
||||
// required: true
|
||||
// type: string
|
||||
//
|
||||
// Responses:
|
||||
// 200: MessageHeaders
|
||||
// default: ErrorResponse
|
||||
|
||||
vars := mux.Vars(r)
|
||||
|
||||
id := vars["id"]
|
||||
|
||||
data, err := storage.GetMessageRaw(id)
|
||||
if err != nil {
|
||||
httpError(w, err.Error())
|
||||
fourOFour(w)
|
||||
return
|
||||
}
|
||||
|
||||
@ -130,8 +258,7 @@ func Headers(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
headers := m.Header
|
||||
bytes, _ := json.Marshal(headers)
|
||||
bytes, _ := json.Marshal(m.Header)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
_, _ = w.Write(bytes)
|
||||
@ -139,6 +266,28 @@ func Headers(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// 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 Raw
|
||||
//
|
||||
// # Get message source
|
||||
//
|
||||
// Returns the full email source as plain text.
|
||||
//
|
||||
// Produces:
|
||||
// - text/plain
|
||||
//
|
||||
// Schemes: http, https
|
||||
//
|
||||
// Parameters:
|
||||
// + name: ID
|
||||
// in: path
|
||||
// description: message id
|
||||
// required: true
|
||||
// type: string
|
||||
//
|
||||
// Responses:
|
||||
// 200: TextResponse
|
||||
// default: ErrorResponse
|
||||
|
||||
vars := mux.Vars(r)
|
||||
|
||||
id := vars["id"]
|
||||
@ -147,7 +296,7 @@ func DownloadRaw(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
data, err := storage.GetMessageRaw(id)
|
||||
if err != nil {
|
||||
httpError(w, err.Error())
|
||||
fourOFour(w)
|
||||
return
|
||||
}
|
||||
|
||||
@ -159,8 +308,32 @@ func DownloadRaw(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// DeleteMessages (method: DELETE) deletes all messages matching IDS.
|
||||
// If no IDs are provided then all messages are deleted.
|
||||
func DeleteMessages(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:route DELETE /api/v1/messages messages Delete
|
||||
//
|
||||
// # Delete messages
|
||||
//
|
||||
// If no IDs are provided then all messages are deleted.
|
||||
//
|
||||
// Consumes:
|
||||
// - application/json
|
||||
//
|
||||
// Produces:
|
||||
// - text/plain
|
||||
//
|
||||
// Schemes: http, https
|
||||
//
|
||||
// Parameters:
|
||||
// + name: ids
|
||||
// in: body
|
||||
// description: Message ids to delete
|
||||
// required: false
|
||||
// type: DeleteRequest
|
||||
//
|
||||
// Responses:
|
||||
// 200: OKResponse
|
||||
// default: ErrorResponse
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var data struct {
|
||||
IDs []string
|
||||
@ -185,7 +358,33 @@ func DeleteMessages(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// SetReadStatus (method: PUT) will update the status to Read/Unread for all provided IDs
|
||||
// If no IDs are provided then all messages are updated.
|
||||
func SetReadStatus(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:route PUT /api/v1/messages messages SetReadStatus
|
||||
//
|
||||
// # Set read status
|
||||
//
|
||||
// If no IDs are provided then all messages are updated.
|
||||
//
|
||||
// Consumes:
|
||||
// - application/json
|
||||
//
|
||||
// Produces:
|
||||
// - text/plain
|
||||
//
|
||||
// Schemes: http, https
|
||||
//
|
||||
// Parameters:
|
||||
// + name: ids
|
||||
// in: body
|
||||
// description: Message ids to update
|
||||
// required: false
|
||||
// type: SetReadStatusRequest
|
||||
//
|
||||
// Responses:
|
||||
// 200: OKResponse
|
||||
// default: ErrorResponse
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
|
||||
var data struct {
|
||||
@ -239,6 +438,31 @@ func SetReadStatus(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// SetTags (method: PUT) will set the tags for all provided IDs
|
||||
func SetTags(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:route PUT /api/v1/tags tags SetTags
|
||||
//
|
||||
// # Set message tags
|
||||
//
|
||||
// To remove all tags from a message, pass an empty tags array.
|
||||
//
|
||||
// Consumes:
|
||||
// - application/json
|
||||
//
|
||||
// Produces:
|
||||
// - text/plain
|
||||
//
|
||||
// Schemes: http, https
|
||||
//
|
||||
// Parameters:
|
||||
// + name: ids
|
||||
// in: body
|
||||
// description: Message ids to update
|
||||
// required: true
|
||||
// type: SetTagsRequest
|
||||
//
|
||||
// Responses:
|
||||
// 200: OKResponse
|
||||
// default: ErrorResponse
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
|
||||
var data struct {
|
||||
|
@ -11,19 +11,41 @@ import (
|
||||
"github.com/axllent/mailpit/utils/updater"
|
||||
)
|
||||
|
||||
type appVersion struct {
|
||||
Version string
|
||||
// Response includes the current and latest Mailpit versions, database info, and memory usage
|
||||
//
|
||||
// swagger:model AppInformation
|
||||
type appInformation struct {
|
||||
// Current Mailpit version
|
||||
Version string
|
||||
// Latest Mailpit version
|
||||
LatestVersion string
|
||||
Database string
|
||||
DatabaseSize int64
|
||||
Messages int
|
||||
Memory uint64
|
||||
// Database path
|
||||
Database string
|
||||
// Database size in bytes
|
||||
DatabaseSize int64
|
||||
// Total number of messages in the database
|
||||
Messages int
|
||||
// Current memory usage in bytes
|
||||
Memory uint64
|
||||
}
|
||||
|
||||
// AppInfo returns some basic details about the running app, and latest release.
|
||||
func AppInfo(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
info := appVersion{}
|
||||
// swagger:route GET /api/v1/info application AppInformation
|
||||
//
|
||||
// # Get the application information
|
||||
//
|
||||
// Returns basic runtime information, message totals and latest release version.
|
||||
//
|
||||
// Produces:
|
||||
// - application/octet-stream
|
||||
//
|
||||
// Schemes: http, https
|
||||
//
|
||||
// Responses:
|
||||
// 200: InfoResponse
|
||||
// default: ErrorResponse
|
||||
info := appInformation{}
|
||||
info.Version = config.Version
|
||||
|
||||
var m runtime.MemStats
|
||||
|
@ -1,6 +1,30 @@
|
||||
package apiv1
|
||||
|
||||
import "github.com/axllent/mailpit/storage"
|
||||
import (
|
||||
"github.com/axllent/mailpit/storage"
|
||||
)
|
||||
|
||||
// MessagesSummary is a summary of a list of messages
|
||||
type MessagesSummary struct {
|
||||
// Total number of messages in mailbox
|
||||
Total int `json:"total"`
|
||||
|
||||
// Total number of unread messages in mailbox
|
||||
Unread int `json:"unread"`
|
||||
|
||||
// Number of results returned
|
||||
Count int `json:"count"`
|
||||
|
||||
// Pagination offset
|
||||
Start int `json:"start"`
|
||||
|
||||
// All current tags
|
||||
Tags []string `json:"tags"`
|
||||
|
||||
// Messages summary
|
||||
// in:body
|
||||
Messages []storage.MessageSummary `json:"messages"`
|
||||
}
|
||||
|
||||
// The following structs & aliases are provided for easy import
|
||||
// and understanding of the JSON structure.
|
||||
@ -8,16 +32,6 @@ import "github.com/axllent/mailpit/storage"
|
||||
// MessageSummary - summary of a single message
|
||||
type MessageSummary = storage.MessageSummary
|
||||
|
||||
// MessagesSummary - summary of a list of messages
|
||||
type MessagesSummary struct {
|
||||
Total int `json:"total"`
|
||||
Unread int `json:"unread"`
|
||||
Count int `json:"count"`
|
||||
Start int `json:"start"`
|
||||
Tags []string `json:"tags"`
|
||||
Messages []MessageSummary `json:"messages"`
|
||||
}
|
||||
|
||||
// Message data
|
||||
type Message = storage.Message
|
||||
|
||||
|
19
server/apiv1/swagger-config.yml
Normal file
19
server/apiv1/swagger-config.yml
Normal file
@ -0,0 +1,19 @@
|
||||
consumes:
|
||||
- application/json
|
||||
info:
|
||||
description: |-
|
||||
OpenAPI 2.0 documentation for [Mailpit](https://github.com/axllent/mailpit).
|
||||
title: Mailpit API
|
||||
contact:
|
||||
name: GitHub
|
||||
url: https://github.com/axllent/mailpit
|
||||
license:
|
||||
name: MIT license
|
||||
url: https://github.com/axllent/mailpit/blob/develop/LICENSE
|
||||
version: "v1"
|
||||
paths: {}
|
||||
produces:
|
||||
- application/json
|
||||
schemes:
|
||||
- http
|
||||
swagger: "2.0"
|
83
server/apiv1/swagger.go
Normal file
83
server/apiv1/swagger.go
Normal file
@ -0,0 +1,83 @@
|
||||
package apiv1
|
||||
|
||||
// These structs are for the purpose of defining swagger HTTP responses
|
||||
|
||||
// Application information
|
||||
// swagger:response InfoResponse
|
||||
type infoResponse struct {
|
||||
// Application information
|
||||
Body appInformation
|
||||
}
|
||||
|
||||
// Message summary
|
||||
// swagger:response MessagesSummaryResponse
|
||||
type messagesSummaryResponse struct {
|
||||
// The message summary
|
||||
// in: body
|
||||
Body MessagesSummary
|
||||
}
|
||||
|
||||
// Message headers
|
||||
// swagger:model MessageHeaders
|
||||
type messageHeaders map[string][]string
|
||||
|
||||
// Delete request
|
||||
// swagger:model DeleteRequest
|
||||
type deleteRequest struct {
|
||||
// ids
|
||||
// in:body
|
||||
IDs []string `json:"ids"`
|
||||
}
|
||||
|
||||
// Set read status request
|
||||
// swagger:model SetReadStatusRequest
|
||||
type setReadStatusRequest struct {
|
||||
// Read status
|
||||
Read bool `json:"read"`
|
||||
|
||||
// ids
|
||||
// in:body
|
||||
IDs []string `json:"ids"`
|
||||
}
|
||||
|
||||
// Set tags request
|
||||
// swagger:model SetTagsRequest
|
||||
type setTagsRequest struct {
|
||||
// Tags
|
||||
// in:body
|
||||
Tags []string `json:"tags"`
|
||||
|
||||
// ids
|
||||
// in:body
|
||||
IDs []string `json:"ids"`
|
||||
}
|
||||
|
||||
// Binary data reponse inherits the attachment's content type
|
||||
// swagger:response BinaryResponse
|
||||
type binaryResponse struct {
|
||||
// in: body
|
||||
Body string
|
||||
}
|
||||
|
||||
// Plain text response
|
||||
// swagger:response TextResponse
|
||||
type textResponse struct {
|
||||
// in: body
|
||||
Body string
|
||||
}
|
||||
|
||||
// Error reponse
|
||||
// swagger:response ErrorResponse
|
||||
type errorResponse struct {
|
||||
// The error message
|
||||
// in: body
|
||||
Body string
|
||||
}
|
||||
|
||||
// Plain text "ok" reponse
|
||||
// swagger:response OKResponse
|
||||
type okResponse struct {
|
||||
// Default reponse
|
||||
// in: body
|
||||
Body string
|
||||
}
|
@ -24,6 +24,33 @@ var (
|
||||
|
||||
// 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 Thumbnail
|
||||
//
|
||||
// # Get an attachment image thumbnail
|
||||
//
|
||||
// This will return a cropped 180x120 JPEG thumbnail of an image attachment.
|
||||
// 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.
|
||||
//
|
||||
// Produces:
|
||||
// - image/jpeg
|
||||
//
|
||||
// Schemes: http, https
|
||||
//
|
||||
// Parameters:
|
||||
// + name: ID
|
||||
// in: path
|
||||
// description: message id
|
||||
// required: true
|
||||
// type: string
|
||||
// + name: PartID
|
||||
// in: path
|
||||
// description: attachment part id
|
||||
// required: true
|
||||
// type: string
|
||||
//
|
||||
// Responses:
|
||||
// 200: BinaryResponse
|
||||
// default: ErrorResponse
|
||||
vars := mux.Vars(r)
|
||||
|
||||
id := vars["id"]
|
||||
|
@ -85,7 +85,7 @@ func defaultRoutes() *mux.Router {
|
||||
r.HandleFunc(config.Webroot+"api/v1/message/{id}/part/{partID}", middleWareFunc(apiv1.DownloadAttachment)).Methods("GET")
|
||||
r.HandleFunc(config.Webroot+"api/v1/message/{id}/part/{partID}/thumb", middleWareFunc(apiv1.Thumbnail)).Methods("GET")
|
||||
r.HandleFunc(config.Webroot+"api/v1/message/{id}/raw", middleWareFunc(apiv1.DownloadRaw)).Methods("GET")
|
||||
r.HandleFunc(config.Webroot+"api/v1/message/{id}/headers", middleWareFunc(apiv1.Headers)).Methods("GET")
|
||||
r.HandleFunc(config.Webroot+"api/v1/message/{id}/headers", middleWareFunc(apiv1.GetHeaders)).Methods("GET")
|
||||
r.HandleFunc(config.Webroot+"api/v1/message/{id}", middleWareFunc(apiv1.GetMessage)).Methods("GET")
|
||||
r.HandleFunc(config.Webroot+"api/v1/info", middleWareFunc(apiv1.AppInfo)).Methods("GET")
|
||||
|
||||
|
@ -128,9 +128,8 @@ export default {
|
||||
self.start = response.data.start;
|
||||
self.items = response.data.messages;
|
||||
self.tags = response.data.tags;
|
||||
if (!self.existingTags.length) {
|
||||
self.existingTags = JSON.parse(JSON.stringify(self.tags));
|
||||
}
|
||||
self.existingTags = JSON.parse(JSON.stringify(self.tags));
|
||||
|
||||
// if pagination > 0 && results == 0 reload first page (prune)
|
||||
if (response.data.count == 0 && response.data.start > 0) {
|
||||
self.start = 0;
|
||||
@ -543,6 +542,14 @@ export default {
|
||||
self.appInfo = response.data;
|
||||
self.modal('AppInfoModal').show();
|
||||
});
|
||||
},
|
||||
|
||||
downloadMessageBody: function (str, ext) {
|
||||
let dl = document.createElement('a');
|
||||
dl.href = "data:text/plain," + encodeURIComponent(str);
|
||||
dl.target = '_blank';
|
||||
dl.download = this.message.ID + '.' + ext;
|
||||
dl.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -576,10 +583,49 @@ export default {
|
||||
:href="'#' + messagePrev" title="View previous message">
|
||||
<i class="bi bi-caret-left-fill"></i>
|
||||
</a>
|
||||
<a :href="'api/v1/message/' + message.ID + '/raw?dl=1'" class="btn btn-outline-light me-2 float-end"
|
||||
title="Download message">
|
||||
<i class="bi bi-file-arrow-down-fill"></i> <span class="d-none d-md-inline">Download</span>
|
||||
</a>
|
||||
<div class="dropdown float-end" id="DownloadBtn">
|
||||
<button type="button" class="btn btn-outline-light dropdown-toggle" data-bs-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
<i class="bi bi-file-arrow-down-fill"></i>
|
||||
<span class="d-none d-md-inline ms-1">Download</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li>
|
||||
<a :href="'api/v1/message/' + message.ID + '/raw?dl=1'" class="dropdown-item"
|
||||
title="Message source including headers, body and attachments">
|
||||
Raw message
|
||||
</a>
|
||||
</li>
|
||||
<li v-if="message.HTML">
|
||||
<button v-on:click="downloadMessageBody(message.HTML, 'html')" class="dropdown-item">
|
||||
HTML body
|
||||
</button>
|
||||
</li>
|
||||
<li v-if="message.Text">
|
||||
<button v-on:click="downloadMessageBody(message.Text, 'txt')" class="dropdown-item">
|
||||
Text body
|
||||
</button>
|
||||
</li>
|
||||
<li v-if="allAttachments(message).length">
|
||||
<hr class="dropdown-divider">
|
||||
</li>
|
||||
<li v-for="part in allAttachments(message)">
|
||||
<a :href="'api/v1/message/' + message.ID + '/part/' + part.PartID" type="button"
|
||||
class="row m-0 dropdown-item d-flex" target="_blank"
|
||||
:title="part.FileName != '' ? part.FileName : '[ unknown ]'" style="min-width: 350px">
|
||||
<div class="col-auto p-0 pe-1">
|
||||
<i class="bi" :class="attachmentIcon(part)"></i>
|
||||
</div>
|
||||
<div class="col text-truncate p-0 pe-1">
|
||||
{{ part.FileName != '' ? part.FileName : '[ unknown ]' }}
|
||||
</div>
|
||||
<div class="col-auto text-muted small p-0">
|
||||
{{ getFileSize(part.Size) }}
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col col-md-9 col-lg-5" v-if="!message">
|
||||
@ -590,13 +636,13 @@ export default {
|
||||
<span v-if="!total" class="ms-2">Mailpit</span>
|
||||
</a>
|
||||
<div v-if="total" class="ms-md-2 d-flex bg-white border rounded-start flex-fill position-relative">
|
||||
<input type="text" class="form-control border-0" v-model.trim="search"
|
||||
placeholder="Search mailbox">
|
||||
<input type="text" class="form-control border-0" v-model.trim="search" placeholder="Search mailbox">
|
||||
<span class="btn btn-link position-absolute end-0 text-muted" v-if="search"
|
||||
v-on:click="resetSearch"><i class="bi bi-x-circle"></i></span>
|
||||
</div>
|
||||
<button v-if="total" class="btn btn-outline-light" type="submit"><i
|
||||
class="bi bi-search"></i></button>
|
||||
<button v-if="total" class="btn btn-outline-light" type="submit">
|
||||
<i class="bi bi-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -618,6 +664,7 @@ export default {
|
||||
<option value="100">100</option>
|
||||
<option value="200">200</option>
|
||||
</select>
|
||||
|
||||
<span v-if="searching">
|
||||
<b>{{ formatNumber(items.length) }} result<template v-if="items.length != 1">s</template></b>
|
||||
</span>
|
||||
@ -626,8 +673,8 @@ export default {
|
||||
{{ formatNumber(start + 1) }}-{{ formatNumber(start + items.length) }} <small>of</small>
|
||||
{{ formatNumber(total) }}
|
||||
</small>
|
||||
<button class="btn btn-outline-light ms-2 me-1" :disabled="!canPrev" v-on:click="viewPrev"
|
||||
v-if="!searching" :title="'View previous ' + limit + ' messages'">
|
||||
<button class="btn btn-outline-light ms-2 me-1" :disabled="!canPrev" v-on:click="viewPrev" v-if="!searching"
|
||||
:title="'View previous ' + limit + ' messages'">
|
||||
<i class="bi bi-caret-left-fill"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-light" :disabled="!canNext" v-on:click="viewNext" v-if="!searching"
|
||||
@ -679,16 +726,16 @@ export default {
|
||||
<button class="list-group-item list-group-item-action" :disabled="!selectedHasUnread()"
|
||||
v-on:click="markSelectedRead">
|
||||
<i class="bi bi-eye-fill"></i>
|
||||
Mark selected read
|
||||
Mark read
|
||||
</button>
|
||||
<button class="list-group-item list-group-item-action" :disabled="!selectedHasRead()"
|
||||
v-on:click="markSelectedUnread">
|
||||
<i class="bi bi-eye-slash"></i>
|
||||
Mark selected unread
|
||||
Mark unread
|
||||
</button>
|
||||
<button class="list-group-item list-group-item-action" v-on:click="deleteMessages">
|
||||
<i class="bi bi-trash-fill me-1 text-danger"></i>
|
||||
Delete selected
|
||||
Delete
|
||||
</button>
|
||||
<button class="list-group-item list-group-item-action" v-on:click="selected = []">
|
||||
<i class="bi bi-x-circle me-1"></i>
|
||||
@ -735,13 +782,13 @@ export default {
|
||||
<div class="text-truncate d-lg-none privacy">
|
||||
<span v-if="message.From" :title="message.From.Address">{{
|
||||
message.From.Name ?
|
||||
message.From.Name : message.From.Address
|
||||
message.From.Name : message.From.Address
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="text-truncate d-none d-lg-block privacy">
|
||||
<b v-if="message.From" :title="message.From.Address">{{
|
||||
message.From.Name ?
|
||||
message.From.Name : message.From.Address
|
||||
message.From.Name : message.From.Address
|
||||
}}</b>
|
||||
</div>
|
||||
<div class="d-none d-lg-block text-truncate text-muted small privacy">
|
||||
@ -810,8 +857,7 @@ export default {
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="MarkAllReadModal" tabindex="-1" aria-labelledby="MarkAllReadModalLabel"
|
||||
aria-hidden="true">
|
||||
<div class="modal fade" id="MarkAllReadModal" tabindex="-1" aria-labelledby="MarkAllReadModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
@ -874,6 +920,12 @@ export default {
|
||||
</a>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<a class="btn btn-primary w-100" href="api/v1/" target="_blank">
|
||||
<i class="bi bi-braces"></i>
|
||||
OpenAPI / Swagger API documentation
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<a class="btn btn-primary w-100" href="https://github.com/axllent/mailpit" target="_blank">
|
||||
<i class="bi bi-github"></i>
|
||||
@ -882,8 +934,7 @@ export default {
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<a class="btn btn-primary w-100" href="https://github.com/axllent/mailpit/wiki"
|
||||
target="_blank">
|
||||
<a class="btn btn-primary w-100" href="https://github.com/axllent/mailpit/wiki" target="_blank">
|
||||
Documentation
|
||||
<i class="bi bi-box-arrow-up-right"></i>
|
||||
</a>
|
||||
|
@ -149,10 +149,6 @@ body.blur {
|
||||
}
|
||||
}
|
||||
|
||||
// .tag.active {
|
||||
// font-weight: bold;
|
||||
// }
|
||||
|
||||
.form-select.tag-selector {
|
||||
display: none;
|
||||
}
|
||||
@ -166,6 +162,17 @@ body.blur {
|
||||
}
|
||||
}
|
||||
|
||||
#DownloadBtn {
|
||||
@include media-breakpoint-down(sm) {
|
||||
position: static;
|
||||
|
||||
.dropdown-menu {
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* PrismJS 1.29.0 - modified!
|
||||
https://prismjs.com/download.html#themes=prism-coy&languages=markup+css */
|
||||
code[class*="language-"],
|
||||
|
1
server/ui-src/docs.js
Normal file
1
server/ui-src/docs.js
Normal file
@ -0,0 +1 @@
|
||||
import "rapidoc";
|
@ -2,8 +2,8 @@
|
||||
<script>
|
||||
import commonMixins from '../mixins.js';
|
||||
import Prism from "prismjs";
|
||||
import Tags from "bootstrap5-tags";
|
||||
import Attachments from './Attachments.vue';
|
||||
import MessageTags from './MessageTags.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@ -12,8 +12,7 @@ export default {
|
||||
},
|
||||
|
||||
components: {
|
||||
Attachments,
|
||||
MessageTags
|
||||
Attachments
|
||||
},
|
||||
|
||||
mixins: [commonMixins],
|
||||
@ -22,36 +21,54 @@ export default {
|
||||
return {
|
||||
srcURI: false,
|
||||
iframes: [], // for resizing
|
||||
tagComponent: false, // to force rerendering of component
|
||||
showTags: false, // to force rerendering of component
|
||||
messageTags: [],
|
||||
allTags: [],
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
message: {
|
||||
handler(newQuestion) {
|
||||
handler() {
|
||||
let self = this;
|
||||
self.tagComponent = false;
|
||||
self.showTags = false;
|
||||
self.messageTags = self.message.Tags;
|
||||
self.allTags = self.existingTags;
|
||||
// delay to select first tab and add HTML highlighting (prev/next)
|
||||
self.$nextTick(function () {
|
||||
self.renderUI();
|
||||
self.tagComponent = true;
|
||||
self.showTags = true;
|
||||
self.$nextTick(function () {
|
||||
Tags.init("select[multiple]");
|
||||
});
|
||||
});
|
||||
},
|
||||
// force eager callback execution
|
||||
immediate: true
|
||||
},
|
||||
messageTags() {
|
||||
// save changed to tags
|
||||
if (this.showTags) {
|
||||
this.saveTags();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
let self = this;
|
||||
self.tagComponent = false;
|
||||
self.showTags = false;
|
||||
self.allTags = self.existingTags;
|
||||
window.addEventListener("resize", self.resizeIframes);
|
||||
self.renderUI();
|
||||
var tabEl = document.getElementById('nav-raw-tab');
|
||||
tabEl.addEventListener('shown.bs.tab', function (event) {
|
||||
self.srcURI = 'api/v1/message/' + self.message.ID + '/raw';
|
||||
});
|
||||
self.tagComponent = true;
|
||||
|
||||
self.showTags = true;
|
||||
self.$nextTick(function () {
|
||||
Tags.init("select[multiple]");
|
||||
});
|
||||
},
|
||||
|
||||
unmounted: function () {
|
||||
@ -105,6 +122,20 @@ export default {
|
||||
if (s) {
|
||||
s.style.height = s.contentWindow.document.body.scrollHeight + 50 + 'px';
|
||||
}
|
||||
},
|
||||
|
||||
saveTags: function () {
|
||||
let self = this;
|
||||
|
||||
var data = {
|
||||
ids: [this.message.ID],
|
||||
tags: this.messageTags
|
||||
}
|
||||
|
||||
self.put('api/v1/tags', data, function (response) {
|
||||
self.scrollInPlace = true;
|
||||
self.$emit('loadMessages');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,31 +193,30 @@ export default {
|
||||
<th class="small">Date</th>
|
||||
<td>{{ messageDate(message.Date) }}</td>
|
||||
</tr>
|
||||
<MessageTags :message="message" :existingTags="existingTags"
|
||||
@load-messages="$emit('loadMessages')" v-if="tagComponent">
|
||||
</MessageTags>
|
||||
|
||||
<tr class="small" v-if="showTags">
|
||||
<th>Tags</th>
|
||||
<td>
|
||||
<select class="form-select small tag-selector" v-model="messageTags" multiple
|
||||
data-allow-new="true" data-clear-end="true" data-allow-clear="true"
|
||||
data-placeholder="Add tags..." data-badge-style="secondary"
|
||||
data-regex="^([a-zA-Z0-9\-\ \_]){3,}$" data-separator="|,|">
|
||||
<option value="">Type a tag...</option>
|
||||
<!-- you need at least one option with the placeholder -->
|
||||
<option v-for="t in allTags" :value="t">{{ t }}</option>
|
||||
</select>
|
||||
<div class="invalid-feedback">Please select a valid tag.</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-auto text-md-end mt-md-3">
|
||||
<!-- <p class="text-muted small d-none d-md-block mb-2"><small>{{ messageDate(message.Date) }}</small></p>
|
||||
<p class="text-muted small d-none d-md-block"><small>Size: {{ getFileSize(message.Size) }}</small></p> -->
|
||||
<div class="dropdown mt-2 mt-md-0" v-if="allAttachments(message)">
|
||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
<div class="col-md-auto d-none d-md-block text-end mt-md-3">
|
||||
<div class="mt-2 mt-md-0" v-if="allAttachments(message)">
|
||||
<span class="badge rounded-pill text-bg-secondary p-2">
|
||||
Attachment<span v-if="allAttachments(message).length > 1">s</span>
|
||||
({{ allAttachments(message).length }})
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li v-for="part in allAttachments(message)">
|
||||
<a :href="'api/v1/message/' + message.ID + '/part/' + part.PartID" type="button"
|
||||
class="dropdown-item" target="_blank">
|
||||
<i class="bi" :class="attachmentIcon(part)"></i>
|
||||
{{ part.FileName != '' ? part.FileName : '[ unknown ]' }}
|
||||
<small class="text-muted ms-2">{{ getFileSize(part.Size) }}</small>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -196,8 +226,8 @@ export default {
|
||||
<button class="nav-link" id="nav-html-tab" data-bs-toggle="tab" data-bs-target="#nav-html" type="button"
|
||||
role="tab" aria-controls="nav-html" aria-selected="true" v-if="message.HTML">HTML</button>
|
||||
<button class="nav-link" id="nav-html-source-tab" data-bs-toggle="tab" data-bs-target="#nav-html-source"
|
||||
type="button" role="tab" aria-controls="nav-html-source" aria-selected="false"
|
||||
v-if="message.HTML">HTML Source</button>
|
||||
type="button" role="tab" aria-controls="nav-html-source" aria-selected="false" v-if="message.HTML">HTML
|
||||
Source</button>
|
||||
<button class="nav-link" id="nav-plain-text-tab" data-bs-toggle="tab" data-bs-target="#nav-plain-text"
|
||||
type="button" role="tab" aria-controls="nav-plain-text" aria-selected="false"
|
||||
:class="message.HTML == '' ? 'show' : ''">Text</button>
|
||||
@ -208,8 +238,8 @@ export default {
|
||||
<div class="tab-content mb-5" id="nav-tabContent">
|
||||
<div v-if="message.HTML != ''" class="tab-pane fade show" id="nav-html" role="tabpanel"
|
||||
aria-labelledby="nav-html-tab" tabindex="0">
|
||||
<iframe target-blank="" class="tab-pane" id="preview-html" :srcdoc="message.HTML"
|
||||
v-on:load="resizeIframe" seamless frameborder="0" style="width: 100%; height: 100%;">
|
||||
<iframe target-blank="" class="tab-pane" id="preview-html" :srcdoc="message.HTML" v-on:load="resizeIframe"
|
||||
seamless frameborder="0" style="width: 100%; height: 100%;">
|
||||
</iframe>
|
||||
<Attachments v-if="allAttachments(message).length" :message="message"
|
||||
:attachments="allAttachments(message)"></Attachments>
|
||||
@ -218,8 +248,8 @@ export default {
|
||||
tabindex="0" v-if="message.HTML">
|
||||
<pre><code class="language-html">{{ message.HTML }}</code></pre>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="nav-plain-text" role="tabpanel" aria-labelledby="nav-plain-text-tab"
|
||||
tabindex="0" :class="message.HTML == '' ? 'show' : ''">
|
||||
<div class="tab-pane fade" id="nav-plain-text" role="tabpanel" aria-labelledby="nav-plain-text-tab" tabindex="0"
|
||||
:class="message.HTML == '' ? 'show' : ''">
|
||||
<div class="text-view">{{ message.Text }}</div>
|
||||
<Attachments v-if="allAttachments(message).length" :message="message"
|
||||
:attachments="allAttachments(message)"></Attachments>
|
||||
|
@ -1,72 +0,0 @@
|
||||
|
||||
<script>
|
||||
import commonMixins from '../mixins.js';
|
||||
import Tags from "bootstrap5-tags";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
message: Object,
|
||||
existingTags: Array
|
||||
},
|
||||
|
||||
mixins: [commonMixins],
|
||||
|
||||
data() {
|
||||
return {
|
||||
messageTags: [],
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
let self = this;
|
||||
self.loaded = false;
|
||||
self.messageTags = self.message.Tags;
|
||||
// delay until vue has rendered
|
||||
self.$nextTick(function () {
|
||||
Tags.init("select[multiple]");
|
||||
self.$nextTick(function () {
|
||||
self.loaded = true;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
watch: {
|
||||
messageTags() {
|
||||
if (this.loaded) {
|
||||
this.saveTags();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
saveTags: function () {
|
||||
let self = this;
|
||||
|
||||
var data = {
|
||||
ids: [this.message.ID],
|
||||
tags: this.messageTags
|
||||
}
|
||||
|
||||
self.put('api/v1/tags', data, function (response) {
|
||||
self.scrollInPlace = true;
|
||||
self.$emit('loadMessages');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<tr class="small">
|
||||
<th>Tags</th>
|
||||
<td>
|
||||
<select class="form-select small tag-selector" v-model="messageTags" multiple data-allow-new="true"
|
||||
data-clear-end="true" data-allow-clear="true" data-placeholder="Add tags..."
|
||||
data-badge-style="secondary" data-regex="^([a-zA-Z0-9\-\ \_]){3,}$" data-separator="|,|">
|
||||
<option value="">Type a tag...</option><!-- you need at least one option with the placeholder -->
|
||||
<option v-for="t in existingTags" :value="t">{{ t }}</option>
|
||||
</select>
|
||||
<div class="invalid-feedback">Please select a valid tag.</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
28
server/ui/api/v1/index.html
Normal file
28
server/ui/api/v1/index.html
Normal file
@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>Mailpit API v1 documentation</title>
|
||||
<meta name="referrer" content="no-referrer">
|
||||
<meta name="robots" content="noindex, nofollow, noarchive">
|
||||
<link rel="icon" href="../../favicon.svg">
|
||||
<script src="../../dist/docs.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<rapi-doc id="thedoc" spec-url="swagger.json" theme="light" layout="column" render-style="read" load-fonts="false"
|
||||
regular-font="system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', 'Noto Sans', 'Liberation Sans', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'"
|
||||
mono-font="Courier New, Courier, System, fixed-width" font-size="large" allow-spec-url-load="false"
|
||||
allow-spec-file-load="false" allow-server-selection="false" allow-search="false" allow-advanced-search="false"
|
||||
bg-color="#ffffff" nav-bg-color="#e3e8ec" nav-text-color="#212529" nav-hover-bg-color="#fff"
|
||||
header-color="#2c3e50" primary-color="#2c3e50" text-color="#212529">
|
||||
<div slot="header">Mailpit API v1 documentation</div>
|
||||
<a target='_blank' href="../../" slot="logo">
|
||||
<img src="../../mailpit.svg" width="40" alt="Mailpit" />
|
||||
</a>
|
||||
</rapi-doc>
|
||||
</body>
|
||||
|
||||
</html>
|
820
server/ui/api/v1/swagger.json
Normal file
820
server/ui/api/v1/swagger.json
Normal file
@ -0,0 +1,820 @@
|
||||
{
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"schemes": [
|
||||
"http"
|
||||
],
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "OpenAPI 2.0 documentation for [Mailpit](https://github.com/axllent/mailpit).",
|
||||
"title": "Mailpit API",
|
||||
"contact": {
|
||||
"name": "GitHub",
|
||||
"url": "https://github.com/axllent/mailpit"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT license",
|
||||
"url": "https://github.com/axllent/mailpit/blob/develop/LICENSE"
|
||||
},
|
||||
"version": "v1"
|
||||
},
|
||||
"paths": {
|
||||
"/api/v1/info": {
|
||||
"get": {
|
||||
"description": "Returns basic runtime information, message totals and latest release version.",
|
||||
"produces": [
|
||||
"application/octet-stream"
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"application"
|
||||
],
|
||||
"summary": "Get the application information",
|
||||
"operationId": "AppInformation",
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/InfoResponse"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/responses/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/message/{ID}": {
|
||||
"get": {
|
||||
"description": "Returns the summary of a message, marking the message as read.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"message"
|
||||
],
|
||||
"summary": "Get message summary",
|
||||
"operationId": "Message",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "message id",
|
||||
"name": "ID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Message",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Message"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/responses/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/message/{ID}/headers": {
|
||||
"get": {
|
||||
"description": "Returns the message headers as an array.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"message"
|
||||
],
|
||||
"summary": "Get message headers",
|
||||
"operationId": "Headers",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "message id",
|
||||
"name": "ID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "MessageHeaders",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/MessageHeaders"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/responses/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/message/{ID}/part/{PartID}": {
|
||||
"get": {
|
||||
"description": "This will return the attachment part using the appropriate Content-Type.",
|
||||
"produces": [
|
||||
"application/*",
|
||||
"image/*",
|
||||
"text/*"
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"message"
|
||||
],
|
||||
"summary": "Get message attachment",
|
||||
"operationId": "Attachment",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "message id",
|
||||
"name": "ID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "attachment part id",
|
||||
"name": "PartID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/BinaryResponse"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/responses/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/message/{ID}/part/{PartID}/thumb": {
|
||||
"get": {
|
||||
"description": "This will return a cropped 180x120 JPEG thumbnail of an image attachment.\nIf the image is smaller than 180x120 then the image is padded. If the attachment is not an image then a blank image is returned.",
|
||||
"produces": [
|
||||
"image/jpeg"
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"message"
|
||||
],
|
||||
"summary": "Get an attachment image thumbnail",
|
||||
"operationId": "Thumbnail",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "message id",
|
||||
"name": "ID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "attachment part id",
|
||||
"name": "PartID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/BinaryResponse"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/responses/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/message/{ID}/raw": {
|
||||
"get": {
|
||||
"description": "Returns the full email source as plain text.",
|
||||
"produces": [
|
||||
"text/plain"
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"message"
|
||||
],
|
||||
"summary": "Get message source",
|
||||
"operationId": "Raw",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "message id",
|
||||
"name": "ID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/TextResponse"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/responses/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/messages": {
|
||||
"get": {
|
||||
"description": "Returns messages from the mailbox ordered from newest to oldest.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"messages"
|
||||
],
|
||||
"summary": "List messages",
|
||||
"operationId": "GetMessages",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"description": "pagination offset",
|
||||
"name": "start",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"default": 50,
|
||||
"description": "limit results",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/MessagesSummaryResponse"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/responses/ErrorResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "If no IDs are provided then all messages are updated.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"text/plain"
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"messages"
|
||||
],
|
||||
"summary": "Set read status",
|
||||
"operationId": "SetReadStatus",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Message ids to update",
|
||||
"name": "ids",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"description": "Message ids to update",
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/SetReadStatusRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/OKResponse"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/responses/ErrorResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "If no IDs are provided then all messages are deleted.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"text/plain"
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"messages"
|
||||
],
|
||||
"summary": "Delete messages",
|
||||
"operationId": "Delete",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Message ids to delete",
|
||||
"name": "ids",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"description": "Message ids to delete",
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/DeleteRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/OKResponse"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/responses/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/search": {
|
||||
"get": {
|
||||
"description": "Returns the latest messages matching a search.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"messages"
|
||||
],
|
||||
"summary": "Search messages",
|
||||
"operationId": "MessagesSummary",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "search query",
|
||||
"name": "query",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"default": 50,
|
||||
"description": "limit results",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/MessagesSummaryResponse"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/responses/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/tags": {
|
||||
"put": {
|
||||
"description": "To remove all tags from a message, pass an empty tags array.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"text/plain"
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"tags"
|
||||
],
|
||||
"summary": "Set message tags",
|
||||
"operationId": "SetTags",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Message ids to update",
|
||||
"name": "ids",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"description": "Message ids to update",
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/SetTagsRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/OKResponse"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/responses/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"Address": {
|
||||
"description": "An address such as \"Barry Gibbs \u003cbg@example.com\u003e\" is represented\nas Address{Name: \"Barry Gibbs\", Address: \"bg@example.com\"}.",
|
||||
"type": "object",
|
||||
"title": "Address represents a single mail address.",
|
||||
"properties": {
|
||||
"Address": {
|
||||
"type": "string"
|
||||
},
|
||||
"Name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"x-go-package": "net/mail"
|
||||
},
|
||||
"AppInformation": {
|
||||
"description": "Response includes the current and latest Mailpit versions, database info, and memory usage",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Database": {
|
||||
"description": "Database path",
|
||||
"type": "string"
|
||||
},
|
||||
"DatabaseSize": {
|
||||
"description": "Database size in bytes",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"LatestVersion": {
|
||||
"description": "Latest Mailpit version",
|
||||
"type": "string"
|
||||
},
|
||||
"Memory": {
|
||||
"description": "Current memory usage in bytes",
|
||||
"type": "integer",
|
||||
"format": "uint64"
|
||||
},
|
||||
"Messages": {
|
||||
"description": "Total number of messages in the database",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"Version": {
|
||||
"description": "Current Mailpit version",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"x-go-name": "appInformation",
|
||||
"x-go-package": "github.com/axllent/mailpit/server/apiv1"
|
||||
},
|
||||
"Attachment": {
|
||||
"description": "Attachment struct for inline and attachments",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ContentID": {
|
||||
"description": "content id",
|
||||
"type": "string"
|
||||
},
|
||||
"ContentType": {
|
||||
"description": "content type",
|
||||
"type": "string"
|
||||
},
|
||||
"FileName": {
|
||||
"description": "file name",
|
||||
"type": "string"
|
||||
},
|
||||
"PartID": {
|
||||
"description": "attachment part id",
|
||||
"type": "string"
|
||||
},
|
||||
"Size": {
|
||||
"description": "size in bytes",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
},
|
||||
"x-go-package": "github.com/axllent/mailpit/storage"
|
||||
},
|
||||
"DeleteRequest": {
|
||||
"description": "Delete request",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ids": {
|
||||
"description": "ids\nin:body",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-go-name": "IDs"
|
||||
}
|
||||
},
|
||||
"x-go-name": "deleteRequest",
|
||||
"x-go-package": "github.com/axllent/mailpit/server/apiv1"
|
||||
},
|
||||
"Message": {
|
||||
"description": "Message data excluding physical attachments",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Attachments": {
|
||||
"description": "Message attachments",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Attachment"
|
||||
}
|
||||
},
|
||||
"Bcc": {
|
||||
"description": "Bcc addresses",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Address"
|
||||
}
|
||||
},
|
||||
"Cc": {
|
||||
"description": "Cc addresses",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Address"
|
||||
}
|
||||
},
|
||||
"Date": {
|
||||
"description": "Message date if set, else date received",
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"From": {
|
||||
"$ref": "#/definitions/Address"
|
||||
},
|
||||
"HTML": {
|
||||
"description": "Message body HTML",
|
||||
"type": "string"
|
||||
},
|
||||
"ID": {
|
||||
"description": "Unique message database id",
|
||||
"type": "string"
|
||||
},
|
||||
"Inline": {
|
||||
"description": "Inline message attachments",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Attachment"
|
||||
}
|
||||
},
|
||||
"Read": {
|
||||
"description": "Read status",
|
||||
"type": "boolean"
|
||||
},
|
||||
"Size": {
|
||||
"description": "Message size in bytes",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"Subject": {
|
||||
"description": "Message subject",
|
||||
"type": "string"
|
||||
},
|
||||
"Tags": {
|
||||
"description": "Message tags",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"Text": {
|
||||
"description": "Message body text",
|
||||
"type": "string"
|
||||
},
|
||||
"To": {
|
||||
"description": "To addresses",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Address"
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-go-package": "github.com/axllent/mailpit/storage"
|
||||
},
|
||||
"MessageHeaders": {
|
||||
"description": "Message headers",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"x-go-name": "messageHeaders",
|
||||
"x-go-package": "github.com/axllent/mailpit/server/apiv1"
|
||||
},
|
||||
"MessageSummary": {
|
||||
"description": "MessageSummary struct for frontend messages",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Attachments": {
|
||||
"description": "Whether the message has any attachments",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"Bcc": {
|
||||
"description": "Bcc addresses",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Address"
|
||||
}
|
||||
},
|
||||
"Cc": {
|
||||
"description": "Cc addresses",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Address"
|
||||
}
|
||||
},
|
||||
"Created": {
|
||||
"description": "Created time",
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"From": {
|
||||
"$ref": "#/definitions/Address"
|
||||
},
|
||||
"ID": {
|
||||
"description": "Unique message database id",
|
||||
"type": "string"
|
||||
},
|
||||
"Read": {
|
||||
"description": "Read status",
|
||||
"type": "boolean"
|
||||
},
|
||||
"Size": {
|
||||
"description": "Message size in bytes (total)",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"Subject": {
|
||||
"description": "Email subject",
|
||||
"type": "string"
|
||||
},
|
||||
"Tags": {
|
||||
"description": "Message tags",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"To": {
|
||||
"description": "To address",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Address"
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-go-package": "github.com/axllent/mailpit/storage"
|
||||
},
|
||||
"MessagesSummary": {
|
||||
"description": "MessagesSummary is a summary of a list of messages",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"count": {
|
||||
"description": "Number of results returned",
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"x-go-name": "Count"
|
||||
},
|
||||
"messages": {
|
||||
"description": "Messages summary\nin:body",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/MessageSummary"
|
||||
},
|
||||
"x-go-name": "Messages"
|
||||
},
|
||||
"start": {
|
||||
"description": "Pagination offset",
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"x-go-name": "Start"
|
||||
},
|
||||
"tags": {
|
||||
"description": "All current tags",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-go-name": "Tags"
|
||||
},
|
||||
"total": {
|
||||
"description": "Total number of messages in mailbox",
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"x-go-name": "Total"
|
||||
},
|
||||
"unread": {
|
||||
"description": "Total number of unread messages in mailbox",
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"x-go-name": "Unread"
|
||||
}
|
||||
},
|
||||
"x-go-package": "github.com/axllent/mailpit/server/apiv1"
|
||||
},
|
||||
"SetReadStatusRequest": {
|
||||
"description": "Set read status request",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ids": {
|
||||
"description": "ids\nin:body",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-go-name": "IDs"
|
||||
},
|
||||
"read": {
|
||||
"description": "Read status",
|
||||
"type": "boolean",
|
||||
"x-go-name": "Read"
|
||||
}
|
||||
},
|
||||
"x-go-name": "setReadStatusRequest",
|
||||
"x-go-package": "github.com/axllent/mailpit/server/apiv1"
|
||||
},
|
||||
"SetTagsRequest": {
|
||||
"description": "Set tags request",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ids": {
|
||||
"description": "ids\nin:body",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-go-name": "IDs"
|
||||
},
|
||||
"tags": {
|
||||
"description": "Tags\nin:body",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-go-name": "Tags"
|
||||
}
|
||||
},
|
||||
"x-go-name": "setTagsRequest",
|
||||
"x-go-package": "github.com/axllent/mailpit/server/apiv1"
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"BinaryResponse": {
|
||||
"description": "Binary data reponse inherits the attachment's content type"
|
||||
},
|
||||
"ErrorResponse": {
|
||||
"description": "Error reponse"
|
||||
},
|
||||
"InfoResponse": {
|
||||
"description": "Application information",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/AppInformation"
|
||||
},
|
||||
"headers": {
|
||||
"Body": {
|
||||
"description": "Application information"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MessagesSummaryResponse": {
|
||||
"description": "Message summary",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/MessagesSummary"
|
||||
}
|
||||
},
|
||||
"OKResponse": {
|
||||
"description": "Plain text \"ok\" reponse"
|
||||
},
|
||||
"TextResponse": {
|
||||
"description": "Plain text response"
|
||||
}
|
||||
}
|
||||
}
|
@ -7,45 +7,81 @@ import (
|
||||
"github.com/jhillyerd/enmime"
|
||||
)
|
||||
|
||||
// Message struct for loading messages. It does not include physical attachments.
|
||||
// Message data excluding physical attachments
|
||||
//
|
||||
// swagger:model Message
|
||||
type Message struct {
|
||||
ID string
|
||||
Read bool
|
||||
From *mail.Address
|
||||
To []*mail.Address
|
||||
Cc []*mail.Address
|
||||
Bcc []*mail.Address
|
||||
Subject string
|
||||
Date time.Time
|
||||
Tags []string
|
||||
Text string
|
||||
HTML string
|
||||
Size int
|
||||
Inline []Attachment
|
||||
// Unique message database id
|
||||
ID string
|
||||
// Read status
|
||||
Read bool
|
||||
// From address
|
||||
From *mail.Address
|
||||
// To addresses
|
||||
To []*mail.Address
|
||||
// Cc addresses
|
||||
Cc []*mail.Address
|
||||
// Bcc addresses
|
||||
Bcc []*mail.Address
|
||||
// Message subject
|
||||
Subject string
|
||||
// Message date if set, else date received
|
||||
Date time.Time
|
||||
// Message tags
|
||||
Tags []string
|
||||
// Message body text
|
||||
Text string
|
||||
// Message body HTML
|
||||
HTML string
|
||||
// Message size in bytes
|
||||
Size int
|
||||
// Inline message attachments
|
||||
Inline []Attachment
|
||||
// Message attachments
|
||||
Attachments []Attachment
|
||||
}
|
||||
|
||||
// Attachment struct for inline and attachments
|
||||
//
|
||||
// swagger:model Attachment
|
||||
type Attachment struct {
|
||||
PartID string
|
||||
FileName string
|
||||
// attachment part id
|
||||
PartID string
|
||||
// file name
|
||||
FileName string
|
||||
// content type
|
||||
ContentType string
|
||||
ContentID string
|
||||
Size int
|
||||
// content id
|
||||
ContentID string
|
||||
// size in bytes
|
||||
Size int
|
||||
}
|
||||
|
||||
// MessageSummary struct for frontend messages
|
||||
//
|
||||
// swagger:model MessageSummary
|
||||
type MessageSummary struct {
|
||||
ID string
|
||||
Read bool
|
||||
From *mail.Address
|
||||
To []*mail.Address
|
||||
Cc []*mail.Address
|
||||
Bcc []*mail.Address
|
||||
Subject string
|
||||
Created time.Time
|
||||
Tags []string
|
||||
Size int
|
||||
// Unique message database id
|
||||
ID string
|
||||
// Read status
|
||||
Read bool
|
||||
// From address
|
||||
From *mail.Address
|
||||
// To address
|
||||
To []*mail.Address
|
||||
// Cc addresses
|
||||
Cc []*mail.Address
|
||||
// Bcc addresses
|
||||
Bcc []*mail.Address
|
||||
// Email subject
|
||||
Subject string
|
||||
// Created time
|
||||
Created time.Time
|
||||
// Message tags
|
||||
Tags []string
|
||||
// Message size in bytes (total)
|
||||
Size int
|
||||
// Whether the message has any attachments
|
||||
Attachments int
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user