mirror of
https://github.com/axllent/mailpit.git
synced 2025-02-09 13:38:37 +02:00
Merge branch 'release/0.0.8'
This commit is contained in:
commit
9fc7202552
@ -3,6 +3,15 @@
|
|||||||
Notable changes to Mailpit will be documented in this file.
|
Notable changes to Mailpit will be documented in this file.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.0.8
|
||||||
|
|
||||||
|
### Bugfix
|
||||||
|
- Fix total/unread count after failed message inserts
|
||||||
|
|
||||||
|
### UI
|
||||||
|
- Add project links to help in CLI
|
||||||
|
|
||||||
|
|
||||||
## 0.0.7
|
## 0.0.7
|
||||||
|
|
||||||
### Bugfix
|
### Bugfix
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
# Building Mailpit from source
|
|
||||||
|
|
||||||
Go (>= version 1.8) and npm are required to compile mailpit from source.
|
|
||||||
|
|
||||||
```
|
|
||||||
git clone git@github.com:axllent/mailpit.git
|
|
||||||
cd mailpit
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building the UI
|
|
||||||
|
|
||||||
The Mailpit web user interface is built with node. In the project's root (top) directory run the following to install the required node modules:
|
|
||||||
|
|
||||||
|
|
||||||
### Installing the node modules
|
|
||||||
```
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Building the web UI
|
|
||||||
|
|
||||||
```
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also run `npm run watch` which will watch for changes and rebuild the HTML/CSS/JS automatically when changes are detected.
|
|
||||||
Please note that you must restart Mailpit (`go run .`) to run with the changes.
|
|
||||||
|
|
||||||
|
|
||||||
## Build the mailpit binary
|
|
||||||
|
|
||||||
One you have the assets compiled, you can build mailpit as follows:
|
|
||||||
```
|
|
||||||
go build -ldflags "-s -w"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building a stand-alone sendmail binary
|
|
||||||
|
|
||||||
This step is unnecessary, however if you do not intend to either symlink `sendmail` to mailpit or configure your existing sendmail to route mail to mailpit, you can optionally build a stand-alone sendmail binary.
|
|
||||||
|
|
||||||
```
|
|
||||||
cd sendmail
|
|
||||||
go build -ldflags "-s -w"
|
|
||||||
```
|
|
@ -6,6 +6,7 @@ It acts as both an SMTP server, and provides a web interface to view all capture
|
|||||||
|
|
||||||
Mailpit is inspired by [MailHog](#why-rewrite-mailhog), but much, much faster.
|
Mailpit is inspired by [MailHog](#why-rewrite-mailhog), but much, much faster.
|
||||||
|
|
||||||
|
![Mailpit](https://raw.githubusercontent.com/axllent/mailpit/develop/screenshot.png)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ Mailpit is inspired by [MailHog](#why-rewrite-mailhog), but much, much faster.
|
|||||||
- Configurable automatic email pruning (default keeps the most recent 500 emails)
|
- Configurable automatic email pruning (default keeps the most recent 500 emails)
|
||||||
- Fast SMTP processing & storing - approximately 300-600 emails per second depending on CPU, network speed & email size
|
- Fast SMTP processing & storing - approximately 300-600 emails per second depending on CPU, network speed & email size
|
||||||
- Can handle tens of thousands of emails
|
- Can handle tens of thousands of emails
|
||||||
- Multi-arch [Docker images](https://github.com/axllent/mailpit/wiki/Docker-image)
|
- Multi-arch [Docker images](https://github.com/axllent/mailpit/wiki/Docker-images)
|
||||||
|
|
||||||
|
|
||||||
## Planned features
|
## Planned features
|
||||||
@ -31,7 +32,7 @@ Mailpit is inspired by [MailHog](#why-rewrite-mailhog), but much, much faster.
|
|||||||
|
|
||||||
Download a pre-built binary in the [releases](https://github.com/axllent/mailpit/releases/latest). The `mailpit` can be placed in your `$PATH`, or simply run as `./mailpit`. See `mailpit -h` for options.
|
Download a pre-built binary in the [releases](https://github.com/axllent/mailpit/releases/latest). The `mailpit` can be placed in your `$PATH`, or simply run as `./mailpit`. See `mailpit -h` for options.
|
||||||
|
|
||||||
To build mailpit from source see [building from source](README-BUILDING.md).
|
To build Mailpit from source see [building from source](https://github.com/axllent/mailpit/wiki/Building-from-source).
|
||||||
|
|
||||||
|
|
||||||
### Configuring sendmail
|
### Configuring sendmail
|
||||||
@ -43,11 +44,11 @@ You can use `mailpit sendmail` as your sendmail configuration in `php.ini`:
|
|||||||
sendmail_path = /usr/local/bin/mailpit sendmail
|
sendmail_path = /usr/local/bin/mailpit sendmail
|
||||||
```
|
```
|
||||||
|
|
||||||
If mailpit is found on the same host as sendmail, you can symlink the mailpit binary to sendmail, eg: `ln -s /usr/local/bin/mailpit /usr/sbin/sendmail` (only if mailpit is running on default 1025 port).
|
If Mailpit is found on the same host as sendmail, you can symlink the Mailpit binary to sendmail, eg: `ln -s /usr/local/bin/mailpit /usr/sbin/sendmail` (only if Mailpit is running on default 1025 port).
|
||||||
|
|
||||||
You can use your default system `sendmail` binary to route directly to port `1025` (configurable) by calling `/usr/sbin/sendmail -S localhost:1025`.
|
You can use your default system `sendmail` binary to route directly to port `1025` (configurable) by calling `/usr/sbin/sendmail -S localhost:1025`.
|
||||||
|
|
||||||
You can build a mailpit-specific sendmail binary from source ( see [building from source](README-BUILDING.md)).
|
You can build a Mailpit-specific sendmail binary from source ( see [building from source](https://github.com/axllent/mailpit/wiki/Building-from-source)).
|
||||||
|
|
||||||
|
|
||||||
## Why rewrite MailHog?
|
## Why rewrite MailHog?
|
||||||
|
17
cmd/root.go
17
cmd/root.go
@ -20,7 +20,11 @@ var rootCmd = &cobra.Command{
|
|||||||
Short: "Mailpit is an email testing tool for developers",
|
Short: "Mailpit is an email testing tool for developers",
|
||||||
Long: `Mailpit is an email testing tool for developers.
|
Long: `Mailpit is an email testing tool for developers.
|
||||||
|
|
||||||
It acts as an SMTP server, and provides a web interface to view all captured emails.`,
|
It acts as an SMTP server, and provides a web interface to view all captured emails.
|
||||||
|
|
||||||
|
Documentation:
|
||||||
|
https://github.com/axllent/mailpit
|
||||||
|
https://github.com/axllent/mailpit/wiki`,
|
||||||
Run: func(_ *cobra.Command, _ []string) {
|
Run: func(_ *cobra.Command, _ []string) {
|
||||||
if err := config.VerifyConfig(); err != nil {
|
if err := config.VerifyConfig(); err != nil {
|
||||||
logger.Log().Error(err.Error())
|
logger.Log().Error(err.Error())
|
||||||
@ -60,9 +64,12 @@ func SendmailExecute() {
|
|||||||
func init() {
|
func init() {
|
||||||
// hide autocompletion
|
// hide autocompletion
|
||||||
rootCmd.CompletionOptions.HiddenDefaultCmd = true
|
rootCmd.CompletionOptions.HiddenDefaultCmd = true
|
||||||
// rootCmd.Flags().SortFlags = false
|
rootCmd.Flags().SortFlags = false
|
||||||
// hide help
|
// hide help command
|
||||||
rootCmd.SetHelpCommand(&cobra.Command{Hidden: true})
|
rootCmd.SetHelpCommand(&cobra.Command{Hidden: true})
|
||||||
|
// hide help flag
|
||||||
|
rootCmd.PersistentFlags().BoolP("help", "h", false, "This help")
|
||||||
|
rootCmd.PersistentFlags().Lookup("help").Hidden = true
|
||||||
|
|
||||||
// defaults from envars if provided
|
// defaults from envars if provided
|
||||||
if len(os.Getenv("MP_DATA_DIR")) > 0 {
|
if len(os.Getenv("MP_DATA_DIR")) > 0 {
|
||||||
@ -84,7 +91,7 @@ func init() {
|
|||||||
rootCmd.Flags().StringVarP(&config.DataDir, "data", "d", config.DataDir, "Optional path to store peristent data")
|
rootCmd.Flags().StringVarP(&config.DataDir, "data", "d", config.DataDir, "Optional path to store peristent data")
|
||||||
rootCmd.Flags().StringVarP(&config.SMTPListen, "smtp", "s", config.SMTPListen, "SMTP bind interface and port")
|
rootCmd.Flags().StringVarP(&config.SMTPListen, "smtp", "s", config.SMTPListen, "SMTP bind interface and port")
|
||||||
rootCmd.Flags().StringVarP(&config.HTTPListen, "listen", "l", config.HTTPListen, "HTTP bind interface and port for UI")
|
rootCmd.Flags().StringVarP(&config.HTTPListen, "listen", "l", config.HTTPListen, "HTTP bind interface and port for UI")
|
||||||
rootCmd.Flags().IntVarP(&config.MaxMessages, "max", "m", config.MaxMessages, "Max number of messages per mailbox")
|
rootCmd.Flags().IntVarP(&config.MaxMessages, "max", "m", config.MaxMessages, "Max number of messages to store")
|
||||||
rootCmd.Flags().StringVarP(&config.AuthFile, "auth-file", "a", config.AuthFile, "A username:bcryptpw mapping file")
|
rootCmd.Flags().StringVarP(&config.AuthFile, "auth-file", "a", config.AuthFile, "A password file for authentication (see wiki)")
|
||||||
rootCmd.Flags().BoolVarP(&config.VerboseLogging, "verbose", "v", false, "Verbose logging")
|
rootCmd.Flags().BoolVarP(&config.VerboseLogging, "verbose", "v", false, "Verbose logging")
|
||||||
}
|
}
|
||||||
|
BIN
screenshot.png
Normal file
BIN
screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"net"
|
"net"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"github.com/axllent/mailpit/config"
|
"github.com/axllent/mailpit/config"
|
||||||
"github.com/axllent/mailpit/logger"
|
"github.com/axllent/mailpit/logger"
|
||||||
@ -19,7 +20,15 @@ func mailHandler(origin net.Addr, from string, to []string, data []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err := storage.Store(storage.DefaultMailbox, data); err != nil {
|
if _, err := storage.Store(storage.DefaultMailbox, data); err != nil {
|
||||||
logger.Log().Errorf("error storing message: %s", err.Error())
|
// Value with size 4800709 exceeded 1048576 limit
|
||||||
|
re := regexp.MustCompile(`(Value with size \d+ exceeded \d+ limit)`)
|
||||||
|
tooLarge := re.FindStringSubmatch(err.Error())
|
||||||
|
if len(tooLarge) > 0 {
|
||||||
|
logger.Log().Errorf("[db] error storing message: %s", tooLarge[0])
|
||||||
|
} else {
|
||||||
|
logger.Log().Errorf("[db] error storing message")
|
||||||
|
logger.Log().Errorf(err.Error())
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,6 +209,8 @@ func Store(mailbox string, b []byte) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
statsAddNewMessage(mailbox)
|
||||||
|
|
||||||
// save the raw email in a separate collection
|
// save the raw email in a separate collection
|
||||||
raw := clover.NewDocument()
|
raw := clover.NewDocument()
|
||||||
raw.Set("_id", id)
|
raw.Set("_id", id)
|
||||||
@ -218,12 +220,11 @@ func Store(mailbox string, b []byte) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
// delete the summary because the data insert failed
|
// delete the summary because the data insert failed
|
||||||
logger.Log().Debugf("[db] error inserting raw message, rolling back")
|
logger.Log().Debugf("[db] error inserting raw message, rolling back")
|
||||||
_ = DeleteOneMessage(mailbox, id)
|
DeleteOneMessage(mailbox, id)
|
||||||
|
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
statsAddNewMessage(mailbox)
|
|
||||||
|
|
||||||
count++
|
count++
|
||||||
if count%100 == 0 {
|
if count%100 == 0 {
|
||||||
logger.Log().Infof("100 messages added in %s", time.Since(per100start))
|
logger.Log().Infof("100 messages added in %s", time.Since(per100start))
|
||||||
@ -399,10 +400,7 @@ func GetMessage(mailbox, id string) (*data.Message, error) {
|
|||||||
from = &mail.Address{Name: env.GetHeader("From")}
|
from = &mail.Address{Name: env.GetHeader("From")}
|
||||||
}
|
}
|
||||||
|
|
||||||
date, err := env.Date()
|
date, _ := env.Date()
|
||||||
if err != nil {
|
|
||||||
// date =
|
|
||||||
}
|
|
||||||
|
|
||||||
obj := data.Message{
|
obj := data.Message{
|
||||||
ID: q.ObjectId(),
|
ID: q.ObjectId(),
|
||||||
@ -522,11 +520,18 @@ func UnreadMessage(mailbox, id string) error {
|
|||||||
|
|
||||||
// DeleteOneMessage will delete a single message from a mailbox
|
// DeleteOneMessage will delete a single message from a mailbox
|
||||||
func DeleteOneMessage(mailbox, id string) error {
|
func DeleteOneMessage(mailbox, id string) error {
|
||||||
|
q, err := db.FindById(mailbox, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
unreadStatus := !q.Get("Read").(bool)
|
||||||
|
|
||||||
if err := db.DeleteById(mailbox, id); err != nil {
|
if err := db.DeleteById(mailbox, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
statsDeleteOneMessage(mailbox)
|
statsDeleteOneMessage(mailbox, unreadStatus)
|
||||||
|
|
||||||
return db.DeleteById(mailbox+"_data", id)
|
return db.DeleteById(mailbox+"_data", id)
|
||||||
}
|
}
|
||||||
|
@ -63,13 +63,23 @@ func statsAddNewMessage(mailbox string) {
|
|||||||
statsLock.Unlock()
|
statsLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deleting one will always mean it was read
|
// Delete one message from the totals. If the message was unread,
|
||||||
func statsDeleteOneMessage(mailbox string) {
|
// then it will also deduct one from the Unread status.
|
||||||
|
func statsDeleteOneMessage(mailbox string, unread bool) {
|
||||||
statsLock.Lock()
|
statsLock.Lock()
|
||||||
s, ok := mailboxStats[mailbox]
|
s, ok := mailboxStats[mailbox]
|
||||||
if ok {
|
if ok {
|
||||||
|
// deduct from the totals
|
||||||
|
if s.Total > 0 {
|
||||||
|
s.Total = s.Total - 1
|
||||||
|
}
|
||||||
|
// only deduct if the original was unread
|
||||||
|
if unread && s.Unread > 0 {
|
||||||
|
s.Unread = s.Unread - 1
|
||||||
|
}
|
||||||
|
|
||||||
mailboxStats[mailbox] = data.MailboxStats{
|
mailboxStats[mailbox] = data.MailboxStats{
|
||||||
Total: s.Total - 1,
|
Total: s.Total,
|
||||||
Unread: s.Unread,
|
Unread: s.Unread,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user