mirror of
https://github.com/axllent/mailpit.git
synced 2025-05-19 22:23:25 +02:00
383 lines
8.2 KiB
Go
383 lines
8.2 KiB
Go
package apiv1
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"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
|
|
Total float64 `json:"total"`
|
|
|
|
// Total number of unread messages in mailbox
|
|
Unread float64 `json:"unread"`
|
|
|
|
// Legacy - now undocumented in API specs but left for backwards compatibility.
|
|
// Removed from API documentation 2023-07-12
|
|
// swagger:ignore
|
|
Count float64 `json:"count"`
|
|
|
|
// Total number of messages matching current query
|
|
MessagesCount float64 `json:"messages_count"`
|
|
|
|
// Pagination offset
|
|
Start int `json:"start"`
|
|
|
|
// All current tags
|
|
Tags []string `json:"tags"`
|
|
|
|
// Messages summary
|
|
// in: body
|
|
Messages []storage.MessageSummary `json:"messages"`
|
|
}
|
|
|
|
// GetMessages returns a paginated list of messages as JSON
|
|
func GetMessages(w http.ResponseWriter, r *http.Request) {
|
|
// swagger:route GET /api/v1/messages messages GetMessagesParams
|
|
//
|
|
// # List messages
|
|
//
|
|
// Returns messages from the mailbox ordered from newest to oldest.
|
|
//
|
|
// Produces:
|
|
// - application/json
|
|
//
|
|
// Schemes: http, https
|
|
//
|
|
// Responses:
|
|
// 200: MessagesSummaryResponse
|
|
// 400: ErrorResponse
|
|
|
|
start, beforeTS, limit := getStartLimit(r)
|
|
|
|
messages, err := storage.List(start, beforeTS, limit)
|
|
if err != nil {
|
|
httpError(w, err.Error())
|
|
return
|
|
}
|
|
|
|
stats := storage.StatsGet()
|
|
|
|
var res MessagesSummary
|
|
|
|
res.Start = start
|
|
res.Messages = messages
|
|
res.Count = float64(len(messages)) // legacy - now undocumented in API specs
|
|
res.Total = stats.Total
|
|
res.Unread = stats.Unread
|
|
res.Tags = stats.Tags
|
|
res.MessagesCount = stats.Total
|
|
|
|
w.Header().Add("Content-Type", "application/json")
|
|
if err := json.NewEncoder(w).Encode(res); err != nil {
|
|
httpError(w, err.Error())
|
|
}
|
|
}
|
|
|
|
// swagger:parameters SetReadStatusParams
|
|
type setReadStatusParams struct {
|
|
// in: body
|
|
Body struct {
|
|
// Read status
|
|
//
|
|
// required: false
|
|
// default: false
|
|
// example: true
|
|
Read bool
|
|
|
|
// Array of message database IDs
|
|
//
|
|
// required: false
|
|
// example: ["4oRBnPtCXgAqZniRhzLNmS", "hXayS6wnCgNnt6aFTvmOF6"]
|
|
IDs []string
|
|
}
|
|
}
|
|
|
|
// 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 SetReadStatusParams
|
|
//
|
|
// # Set read status
|
|
//
|
|
// If no IDs are provided then all messages are updated.
|
|
//
|
|
// Consumes:
|
|
// - application/json
|
|
//
|
|
// Produces:
|
|
// - text/plain
|
|
//
|
|
// Schemes: http, https
|
|
//
|
|
// Responses:
|
|
// 200: OKResponse
|
|
// 400: ErrorResponse
|
|
|
|
decoder := json.NewDecoder(r.Body)
|
|
|
|
var data struct {
|
|
Read bool
|
|
IDs []string
|
|
}
|
|
|
|
err := decoder.Decode(&data)
|
|
if err != nil {
|
|
httpError(w, err.Error())
|
|
return
|
|
}
|
|
|
|
ids := data.IDs
|
|
|
|
if len(ids) == 0 {
|
|
if data.Read {
|
|
err := storage.MarkAllRead()
|
|
if err != nil {
|
|
httpError(w, err.Error())
|
|
return
|
|
}
|
|
} else {
|
|
err := storage.MarkAllUnread()
|
|
if err != nil {
|
|
httpError(w, err.Error())
|
|
return
|
|
}
|
|
}
|
|
} else {
|
|
if data.Read {
|
|
for _, id := range ids {
|
|
if err := storage.MarkRead(id); err != nil {
|
|
httpError(w, err.Error())
|
|
return
|
|
}
|
|
}
|
|
} else {
|
|
for _, id := range ids {
|
|
if err := storage.MarkUnread(id); err != nil {
|
|
httpError(w, err.Error())
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
w.Header().Add("Content-Type", "text/plain")
|
|
_, _ = 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
|
|
//
|
|
// # Delete messages
|
|
//
|
|
// Delete individual or all messages. If no IDs are provided then all messages are deleted.
|
|
//
|
|
// Consumes:
|
|
// - application/json
|
|
//
|
|
// Produces:
|
|
// - text/plain
|
|
//
|
|
// Schemes: http, https
|
|
//
|
|
// Responses:
|
|
// 200: OKResponse
|
|
// 400: ErrorResponse
|
|
|
|
decoder := json.NewDecoder(r.Body)
|
|
var data struct {
|
|
IDs []string
|
|
}
|
|
err := decoder.Decode(&data)
|
|
if err != nil || len(data.IDs) == 0 {
|
|
if err := storage.DeleteAllMessages(); err != nil {
|
|
httpError(w, err.Error())
|
|
return
|
|
}
|
|
} else {
|
|
if err := storage.DeleteMessages(data.IDs); err != nil {
|
|
httpError(w, err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
w.Header().Add("Content-Type", "text/plain")
|
|
_, _ = 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
|
|
// type integer
|
|
Start string `json:"start"`
|
|
|
|
// Limit results
|
|
//
|
|
// in: query
|
|
// required: false
|
|
// type integer
|
|
Limit string `json:"limit"`
|
|
|
|
// [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
|
|
//
|
|
// # Search messages
|
|
//
|
|
// Returns messages matching [a search](https://mailpit.axllent.org/docs/usage/search-filters/), sorted by received date (descending).
|
|
//
|
|
// Produces:
|
|
// - application/json
|
|
//
|
|
// Schemes: http, https
|
|
//
|
|
// Responses:
|
|
// 200: MessagesSummaryResponse
|
|
// 400: ErrorResponse
|
|
|
|
search := strings.TrimSpace(r.URL.Query().Get("query"))
|
|
if search == "" {
|
|
httpError(w, "Error: no search query")
|
|
return
|
|
}
|
|
|
|
start, beforeTS, limit := getStartLimit(r)
|
|
|
|
messages, results, err := storage.Search(search, r.URL.Query().Get("tz"), start, beforeTS, limit)
|
|
if err != nil {
|
|
httpError(w, err.Error())
|
|
return
|
|
}
|
|
|
|
stats := storage.StatsGet()
|
|
|
|
var res MessagesSummary
|
|
|
|
res.Start = start
|
|
res.Messages = messages
|
|
res.Count = float64(len(messages)) // legacy - now undocumented in API specs
|
|
res.Total = stats.Total // total messages in mailbox
|
|
res.MessagesCount = float64(results)
|
|
res.Unread = stats.Unread
|
|
res.Tags = stats.Tags
|
|
|
|
w.Header().Add("Content-Type", "application/json")
|
|
if err := json.NewEncoder(w).Encode(res); err != nil {
|
|
httpError(w, err.Error())
|
|
}
|
|
}
|
|
|
|
// 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
|
|
//
|
|
// # Delete messages by search
|
|
//
|
|
// Delete all messages matching [a search](https://mailpit.axllent.org/docs/usage/search-filters/).
|
|
//
|
|
// Produces:
|
|
// - application/json
|
|
//
|
|
// Schemes: http, https
|
|
//
|
|
// Responses:
|
|
// 200: OKResponse
|
|
// 400: ErrorResponse
|
|
|
|
search := strings.TrimSpace(r.URL.Query().Get("query"))
|
|
if search == "" {
|
|
httpError(w, "Error: no search query")
|
|
return
|
|
}
|
|
|
|
if err := storage.DeleteSearch(search, r.URL.Query().Get("tz")); err != nil {
|
|
httpError(w, err.Error())
|
|
return
|
|
}
|
|
|
|
w.Header().Add("Content-Type", "text/plain")
|
|
_, _ = w.Write([]byte("ok"))
|
|
}
|