mirror of
https://github.com/axllent/mailpit.git
synced 2025-04-15 11:56:44 +02:00
Merge branch 'release/1.2.2'
This commit is contained in:
commit
a2b6107dd6
@ -4,9 +4,15 @@ Notable changes to Mailpit will be documented in this file.
|
|||||||
|
|
||||||
## 1.2.2
|
## 1.2.2
|
||||||
|
|
||||||
|
### API
|
||||||
|
- Add API endpoint to return message headers
|
||||||
|
|
||||||
### Libs
|
### Libs
|
||||||
- Update go modules
|
- Update go modules
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- Add API test for raw & message headers
|
||||||
|
|
||||||
|
|
||||||
## 1.2.1
|
## 1.2.1
|
||||||
|
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
package data
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// MailboxSummary struct
|
|
||||||
type MailboxSummary struct {
|
|
||||||
Name string
|
|
||||||
Slug string
|
|
||||||
Total int
|
|
||||||
Unread int
|
|
||||||
LastMessage time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebsocketNotification struct for responses
|
|
||||||
type WebsocketNotification struct {
|
|
||||||
Type string
|
|
||||||
Data interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MailboxStats struct for quick mailbox total/read lookups
|
|
||||||
type MailboxStats struct {
|
|
||||||
Total int
|
|
||||||
Unread int
|
|
||||||
}
|
|
@ -1,6 +1,8 @@
|
|||||||
# Message
|
# Message
|
||||||
|
|
||||||
Returns a summary of the message and attachments.
|
## Message summary
|
||||||
|
|
||||||
|
Returns a JSON summary of the message and attachments.
|
||||||
|
|
||||||
**URL** : `api/v1/message/<ID>`
|
**URL** : `api/v1/message/<ID>`
|
||||||
|
|
||||||
@ -70,6 +72,39 @@ Returns a summary of the message and attachments.
|
|||||||
|
|
||||||
Returns the attachment using the MIME type provided by the attachment `ContentType`.
|
Returns the attachment using the MIME type provided by the attachment `ContentType`.
|
||||||
|
|
||||||
|
---
|
||||||
|
## Headers
|
||||||
|
|
||||||
|
**URL** : `api/v1/message/<ID>/headers`
|
||||||
|
|
||||||
|
**Method** : `GET`
|
||||||
|
|
||||||
|
Returns all message headers as a JSON array.
|
||||||
|
Each unique header key contains an array of one or more values (email headers can be listed multiple times.)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Content-Type": [
|
||||||
|
"multipart/related; type=\"multipart/alternative\"; boundary=\"----=_NextPart_000_0013_01C6A60C.47EEAB80\""
|
||||||
|
],
|
||||||
|
"Date": [
|
||||||
|
"Wed, 12 Jul 2006 23:38:30 +1200"
|
||||||
|
],
|
||||||
|
"Delivered-To": [
|
||||||
|
"user@example.com",
|
||||||
|
"user-alias@example.com"
|
||||||
|
],
|
||||||
|
"From": [
|
||||||
|
"\"User Name\" \\u003remote@example.com\\u003e"
|
||||||
|
],
|
||||||
|
"Message-Id": [
|
||||||
|
"\\u003c001701c6a5a7$b3205580$0201010a@HomeOfficeSM\\u003e"
|
||||||
|
],
|
||||||
|
....
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
## Raw (source) email
|
## Raw (source) email
|
||||||
|
|
||||||
|
@ -8,12 +8,12 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"net/smtp"
|
"net/smtp"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
|
||||||
|
"github.com/axllent/mailpit/logger"
|
||||||
flag "github.com/spf13/pflag"
|
flag "github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -80,6 +80,6 @@ func Run() {
|
|||||||
err = smtp.SendMail(smtpAddr, nil, fromAddr, recip, body)
|
err = smtp.SendMail(smtpAddr, nil, fromAddr, recip, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "error sending mail")
|
fmt.Fprintln(os.Stderr, "error sending mail")
|
||||||
log.Fatal(err)
|
logger.Log().Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,11 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/mail"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/axllent/mailpit/config"
|
"github.com/axllent/mailpit/config"
|
||||||
"github.com/axllent/mailpit/data"
|
|
||||||
"github.com/axllent/mailpit/storage"
|
"github.com/axllent/mailpit/storage"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
@ -19,10 +19,10 @@ type MessagesResult struct {
|
|||||||
Unread int `json:"unread"`
|
Unread int `json:"unread"`
|
||||||
Count int `json:"count"`
|
Count int `json:"count"`
|
||||||
Start int `json:"start"`
|
Start int `json:"start"`
|
||||||
Messages []data.Summary `json:"messages"`
|
Messages []storage.Summary `json:"messages"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Messages returns a paginated list of messages
|
// Messages returns a paginated list of messages as JSON
|
||||||
func Messages(w http.ResponseWriter, r *http.Request) {
|
func Messages(w http.ResponseWriter, r *http.Request) {
|
||||||
start, limit := getStartLimit(r)
|
start, limit := getStartLimit(r)
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ func Messages(w http.ResponseWriter, r *http.Request) {
|
|||||||
_, _ = w.Write(bytes)
|
_, _ = w.Write(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search returns a max of 200 of the latest messages
|
// Search returns up to 200 of the latest messages as JSON
|
||||||
func Search(w http.ResponseWriter, r *http.Request) {
|
func Search(w http.ResponseWriter, r *http.Request) {
|
||||||
search := strings.TrimSpace(r.URL.Query().Get("query"))
|
search := strings.TrimSpace(r.URL.Query().Get("query"))
|
||||||
if search == "" {
|
if search == "" {
|
||||||
@ -76,7 +76,7 @@ func Search(w http.ResponseWriter, r *http.Request) {
|
|||||||
_, _ = w.Write(bytes)
|
_, _ = w.Write(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message (method: GET) returns a *data.Message
|
// Message (method: GET) returns the *data.Message as JSON
|
||||||
func Message(w http.ResponseWriter, r *http.Request) {
|
func Message(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
@ -115,6 +115,32 @@ func DownloadAttachment(w http.ResponseWriter, r *http.Request) {
|
|||||||
_, _ = w.Write(a.Content)
|
_, _ = w.Write(a.Content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Headers (method: GET) returns the message headers as JSON
|
||||||
|
func Headers(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
|
id := vars["id"]
|
||||||
|
|
||||||
|
data, err := storage.GetMessageRaw(id)
|
||||||
|
if err != nil {
|
||||||
|
httpError(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := strings.NewReader(string(data))
|
||||||
|
m, err := mail.ReadMessage(reader)
|
||||||
|
if err != nil {
|
||||||
|
httpError(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
headers := m.Header
|
||||||
|
bytes, _ := json.Marshal(headers)
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
_, _ = w.Write(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
// DownloadRaw (method: GET) returns the full email source as plain text
|
// DownloadRaw (method: GET) returns the full email source as plain text
|
||||||
func DownloadRaw(w http.ResponseWriter, r *http.Request) {
|
func DownloadRaw(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"embed"
|
"embed"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -47,10 +46,10 @@ func Listen() {
|
|||||||
|
|
||||||
if config.UISSLCert != "" && config.UISSLKey != "" {
|
if config.UISSLCert != "" && config.UISSLKey != "" {
|
||||||
logger.Log().Infof("[http] starting secure server on https://%s", config.HTTPListen)
|
logger.Log().Infof("[http] starting secure server on https://%s", config.HTTPListen)
|
||||||
log.Fatal(http.ListenAndServeTLS(config.HTTPListen, config.UISSLCert, config.UISSLKey, nil))
|
logger.Log().Fatal(http.ListenAndServeTLS(config.HTTPListen, config.UISSLCert, config.UISSLKey, nil))
|
||||||
} else {
|
} else {
|
||||||
logger.Log().Infof("[http] starting server on http://%s", config.HTTPListen)
|
logger.Log().Infof("[http] starting server on http://%s", config.HTTPListen)
|
||||||
log.Fatal(http.ListenAndServe(config.HTTPListen, nil))
|
logger.Log().Fatal(http.ListenAndServe(config.HTTPListen, nil))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,9 +61,10 @@ func defaultRoutes() *mux.Router {
|
|||||||
r.HandleFunc("/api/v1/messages", middleWareFunc(apiv1.SetReadStatus)).Methods("PUT")
|
r.HandleFunc("/api/v1/messages", middleWareFunc(apiv1.SetReadStatus)).Methods("PUT")
|
||||||
r.HandleFunc("/api/v1/messages", middleWareFunc(apiv1.DeleteMessages)).Methods("DELETE")
|
r.HandleFunc("/api/v1/messages", middleWareFunc(apiv1.DeleteMessages)).Methods("DELETE")
|
||||||
r.HandleFunc("/api/v1/search", middleWareFunc(apiv1.Search)).Methods("GET")
|
r.HandleFunc("/api/v1/search", middleWareFunc(apiv1.Search)).Methods("GET")
|
||||||
r.HandleFunc("/api/v1/message/{id}/raw", middleWareFunc(apiv1.DownloadRaw)).Methods("GET")
|
|
||||||
r.HandleFunc("/api/v1/message/{id}/part/{partID}", middleWareFunc(apiv1.DownloadAttachment)).Methods("GET")
|
r.HandleFunc("/api/v1/message/{id}/part/{partID}", middleWareFunc(apiv1.DownloadAttachment)).Methods("GET")
|
||||||
r.HandleFunc("/api/v1/message/{id}/part/{partID}/thumb", middleWareFunc(apiv1.Thumbnail)).Methods("GET")
|
r.HandleFunc("/api/v1/message/{id}/part/{partID}/thumb", middleWareFunc(apiv1.Thumbnail)).Methods("GET")
|
||||||
|
r.HandleFunc("/api/v1/message/{id}/raw", middleWareFunc(apiv1.DownloadRaw)).Methods("GET")
|
||||||
|
r.HandleFunc("/api/v1/message/{id}/headers", middleWareFunc(apiv1.Headers)).Methods("GET")
|
||||||
r.HandleFunc("/api/v1/message/{id}", middleWareFunc(apiv1.Message)).Methods("GET")
|
r.HandleFunc("/api/v1/message/{id}", middleWareFunc(apiv1.Message)).Methods("GET")
|
||||||
r.HandleFunc("/api/v1/info", middleWareFunc(apiv1.AppInfo)).Methods("GET")
|
r.HandleFunc("/api/v1/info", middleWareFunc(apiv1.AppInfo)).Methods("GET")
|
||||||
|
|
||||||
|
@ -54,15 +54,24 @@ func Test_APIv1(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// read first 10
|
// read first 10
|
||||||
t.Log("Read first 10 messages")
|
t.Log("Read first 10 messages including raw & headers")
|
||||||
putIDS := []string{}
|
putIDS := []string{}
|
||||||
for indx, msg := range m.Messages {
|
for indx, msg := range m.Messages {
|
||||||
if indx == 10 {
|
if indx == 10 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := clientGet(ts.URL + "/api/v1/message/" + msg.ID)
|
if _, err := clientGet(ts.URL + "/api/v1/message/" + msg.ID); err != nil {
|
||||||
if err != nil {
|
t.Errorf(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// test RAW
|
||||||
|
if _, err := clientGet(ts.URL + "/api/v1/message/" + msg.ID + "/raw"); err != nil {
|
||||||
|
t.Errorf(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// test headers
|
||||||
|
if _, err := clientGet(ts.URL + "/api/v1/message/" + msg.ID + "/headers"); err != nil {
|
||||||
t.Errorf(err.Error())
|
t.Errorf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
package websockets
|
package websockets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/axllent/mailpit/config"
|
"github.com/axllent/mailpit/config"
|
||||||
|
"github.com/axllent/mailpit/logger"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ func ServeWs(hub *Hub, w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
conn, err := upgrader.Upgrade(w, r, nil)
|
conn, err := upgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
logger.Log().Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ package websockets
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/axllent/mailpit/data"
|
|
||||||
"github.com/axllent/mailpit/logger"
|
"github.com/axllent/mailpit/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,6 +26,12 @@ type Hub struct {
|
|||||||
unregister chan *Client
|
unregister chan *Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WebsocketNotification struct for responses
|
||||||
|
type WebsocketNotification struct {
|
||||||
|
Type string
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
// NewHub returns a new hub configuration
|
// NewHub returns a new hub configuration
|
||||||
func NewHub() *Hub {
|
func NewHub() *Hub {
|
||||||
return &Hub{
|
return &Hub{
|
||||||
@ -68,7 +73,7 @@ func Broadcast(t string, msg interface{}) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w := data.WebsocketNotification{}
|
w := WebsocketNotification{}
|
||||||
w.Type = t
|
w.Type = t
|
||||||
w.Data = msg
|
w.Data = msg
|
||||||
b, err := json.Marshal(w)
|
b, err := json.Marshal(w)
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
|
|
||||||
"github.com/GuiaBolso/darwin"
|
"github.com/GuiaBolso/darwin"
|
||||||
"github.com/axllent/mailpit/config"
|
"github.com/axllent/mailpit/config"
|
||||||
"github.com/axllent/mailpit/data"
|
|
||||||
"github.com/axllent/mailpit/logger"
|
"github.com/axllent/mailpit/logger"
|
||||||
"github.com/axllent/mailpit/server/websockets"
|
"github.com/axllent/mailpit/server/websockets"
|
||||||
"github.com/jhillyerd/enmime"
|
"github.com/jhillyerd/enmime"
|
||||||
@ -231,7 +230,7 @@ func Store(body []byte) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return summary
|
// return summary
|
||||||
c := &data.Summary{}
|
c := &Summary{}
|
||||||
if err := json.Unmarshal(b, c); err != nil {
|
if err := json.Unmarshal(b, c); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -247,8 +246,8 @@ func Store(body []byte) (string, error) {
|
|||||||
|
|
||||||
// List returns a subset of messages from the mailbox,
|
// List returns a subset of messages from the mailbox,
|
||||||
// sorted latest to oldest
|
// sorted latest to oldest
|
||||||
func List(start, limit int) ([]data.Summary, error) {
|
func List(start, limit int) ([]Summary, error) {
|
||||||
results := []data.Summary{}
|
results := []Summary{}
|
||||||
|
|
||||||
q := sqlf.From("mailbox").
|
q := sqlf.From("mailbox").
|
||||||
Select(`ID, Data, Read`).
|
Select(`ID, Data, Read`).
|
||||||
@ -260,7 +259,7 @@ func List(start, limit int) ([]data.Summary, error) {
|
|||||||
var id string
|
var id string
|
||||||
var summary string
|
var summary string
|
||||||
var read int
|
var read int
|
||||||
em := data.Summary{}
|
em := Summary{}
|
||||||
|
|
||||||
if err := row.Scan(&id, &summary, &read); err != nil {
|
if err := row.Scan(&id, &summary, &read); err != nil {
|
||||||
logger.Log().Error(err)
|
logger.Log().Error(err)
|
||||||
@ -291,8 +290,8 @@ func List(start, limit int) ([]data.Summary, error) {
|
|||||||
// The search is broken up by segments (exact phrases can be quoted), and interprits specific terms such as:
|
// The search is broken up by segments (exact phrases can be quoted), and interprits specific terms such as:
|
||||||
// is:read, is:unread, has:attachment, to:<term>, from:<term> & subject:<term>
|
// is:read, is:unread, has:attachment, to:<term>, from:<term> & subject:<term>
|
||||||
// Negative searches also also included by prefixing the search term with a `-` or `!`
|
// Negative searches also also included by prefixing the search term with a `-` or `!`
|
||||||
func Search(search string) ([]data.Summary, error) {
|
func Search(search string) ([]Summary, error) {
|
||||||
results := []data.Summary{}
|
results := []Summary{}
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
s := strings.ToLower(search)
|
s := strings.ToLower(search)
|
||||||
@ -305,8 +304,7 @@ func Search(search string) ([]data.Summary, error) {
|
|||||||
p := shellwords.NewParser()
|
p := shellwords.NewParser()
|
||||||
args, err := p.Parse(s)
|
args, err := p.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// return errors.New("Your search contains invalid characters")
|
return results, errors.New("Your search contains invalid characters")
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate the SQL based on arguments
|
// generate the SQL based on arguments
|
||||||
@ -317,7 +315,7 @@ func Search(search string) ([]data.Summary, error) {
|
|||||||
var summary string
|
var summary string
|
||||||
var read int
|
var read int
|
||||||
var ignore string
|
var ignore string
|
||||||
em := data.Summary{}
|
em := Summary{}
|
||||||
|
|
||||||
if err := row.Scan(&id, &summary, &read, &ignore, &ignore, &ignore, &ignore); err != nil {
|
if err := row.Scan(&id, &summary, &read, &ignore, &ignore, &ignore, &ignore); err != nil {
|
||||||
logger.Log().Error(err)
|
logger.Log().Error(err)
|
||||||
@ -348,7 +346,7 @@ func Search(search string) ([]data.Summary, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetMessage returns a data.Message generated from the mailbox_data collection.
|
// GetMessage returns a data.Message generated from the mailbox_data collection.
|
||||||
func GetMessage(id string) (*data.Message, error) {
|
func GetMessage(id string) (*Message, error) {
|
||||||
raw, err := GetMessageRaw(id)
|
raw, err := GetMessageRaw(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -371,7 +369,7 @@ func GetMessage(id string) (*data.Message, error) {
|
|||||||
|
|
||||||
date, _ := env.Date()
|
date, _ := env.Date()
|
||||||
|
|
||||||
obj := data.Message{
|
obj := Message{
|
||||||
ID: id,
|
ID: id,
|
||||||
Read: true,
|
Read: true,
|
||||||
From: from,
|
From: from,
|
||||||
@ -384,28 +382,26 @@ func GetMessage(id string) (*data.Message, error) {
|
|||||||
Text: env.Text,
|
Text: env.Text,
|
||||||
}
|
}
|
||||||
|
|
||||||
html := env.HTML
|
|
||||||
|
|
||||||
// strip base tags
|
// strip base tags
|
||||||
var re = regexp.MustCompile(`(?U)<base .*>`)
|
var re = regexp.MustCompile(`(?U)<base .*>`)
|
||||||
html = re.ReplaceAllString(html, "")
|
html := re.ReplaceAllString(env.HTML, "")
|
||||||
obj.HTML = html
|
obj.HTML = html
|
||||||
|
|
||||||
for _, i := range env.Inlines {
|
for _, i := range env.Inlines {
|
||||||
if i.FileName != "" || i.ContentID != "" {
|
if i.FileName != "" || i.ContentID != "" {
|
||||||
obj.Inline = append(obj.Inline, data.AttachmentSummary(i))
|
obj.Inline = append(obj.Inline, AttachmentSummary(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, i := range env.OtherParts {
|
for _, i := range env.OtherParts {
|
||||||
if i.FileName != "" || i.ContentID != "" {
|
if i.FileName != "" || i.ContentID != "" {
|
||||||
obj.Inline = append(obj.Inline, data.AttachmentSummary(i))
|
obj.Inline = append(obj.Inline, AttachmentSummary(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, a := range env.Attachments {
|
for _, a := range env.Attachments {
|
||||||
if a.FileName != "" || a.ContentID != "" {
|
if a.FileName != "" || a.ContentID != "" {
|
||||||
obj.Attachments = append(obj.Attachments, data.AttachmentSummary(a))
|
obj.Attachments = append(obj.Attachments, AttachmentSummary(a))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -650,7 +646,7 @@ func DeleteAllMessages() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StatsGet returns the total/unread statistics for a mailbox
|
// StatsGet returns the total/unread statistics for a mailbox
|
||||||
func StatsGet() data.MailboxStats {
|
func StatsGet() MailboxStats {
|
||||||
var (
|
var (
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
total = CountTotal()
|
total = CountTotal()
|
||||||
@ -661,7 +657,7 @@ func StatsGet() data.MailboxStats {
|
|||||||
|
|
||||||
dbLastAction = time.Now()
|
dbLastAction = time.Now()
|
||||||
|
|
||||||
return data.MailboxStats{
|
return MailboxStats{
|
||||||
Total: total,
|
Total: total,
|
||||||
Unread: unread,
|
Unread: unread,
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
// Package data contains the message & mailbox structs
|
package storage
|
||||||
package data
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/mail"
|
"net/mail"
|
||||||
@ -48,6 +47,12 @@ type Summary struct {
|
|||||||
Attachments int
|
Attachments int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MailboxStats struct for quick mailbox total/read lookups
|
||||||
|
type MailboxStats struct {
|
||||||
|
Total int
|
||||||
|
Unread int
|
||||||
|
}
|
||||||
|
|
||||||
// AttachmentSummary returns a summary of the attachment without any binary data
|
// AttachmentSummary returns a summary of the attachment without any binary data
|
||||||
func AttachmentSummary(a *enmime.Part) Attachment {
|
func AttachmentSummary(a *enmime.Part) Attachment {
|
||||||
o := Attachment{}
|
o := Attachment{}
|
Loading…
x
Reference in New Issue
Block a user