mirror of
https://github.com/axllent/mailpit.git
synced 2025-07-03 00:46:58 +02:00
Merge branch 'release/v1.2.7'
This commit is contained in:
@ -2,6 +2,12 @@
|
||||
|
||||
Notable changes to Mailpit will be documented in this file.
|
||||
|
||||
## v1.2.7
|
||||
|
||||
### Feature
|
||||
- Allow custom webroot
|
||||
|
||||
|
||||
## v1.2.6
|
||||
|
||||
### API
|
||||
|
@ -6,10 +6,10 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/axllent/mailpit/config"
|
||||
"github.com/axllent/mailpit/logger"
|
||||
"github.com/axllent/mailpit/server"
|
||||
"github.com/axllent/mailpit/smtpd"
|
||||
"github.com/axllent/mailpit/storage"
|
||||
"github.com/axllent/mailpit/utils/logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -103,6 +103,9 @@ func init() {
|
||||
if len(os.Getenv("MP_SMTP_SSL_KEY")) > 0 {
|
||||
config.SMTPSSLKey = os.Getenv("MP_SMTP_SSL_KEY")
|
||||
}
|
||||
if len(os.Getenv("MP_WEBROOT")) > 0 {
|
||||
config.Webroot = os.Getenv("MP_WEBROOT")
|
||||
}
|
||||
|
||||
// deprecated 2022/08/06
|
||||
if len(os.Getenv("MP_AUTH_FILE")) > 0 {
|
||||
@ -127,6 +130,7 @@ func init() {
|
||||
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().IntVarP(&config.MaxMessages, "max", "m", config.MaxMessages, "Max number of messages to store")
|
||||
rootCmd.Flags().StringVar(&config.Webroot, "webroot", config.Webroot, "Set the webroot for web UI & API")
|
||||
|
||||
rootCmd.Flags().StringVar(&config.UIAuthFile, "ui-auth-file", config.UIAuthFile, "A password file for web UI authentication")
|
||||
rootCmd.Flags().StringVar(&config.UISSLCert, "ui-ssl-cert", config.UISSLCert, "SSL certificate for web UI - requires ui-ssl-key")
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"runtime"
|
||||
|
||||
"github.com/axllent/mailpit/config"
|
||||
"github.com/axllent/mailpit/updater"
|
||||
"github.com/axllent/mailpit/utils/updater"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -3,9 +3,11 @@ package config
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/tg123/go-htpasswd"
|
||||
)
|
||||
@ -44,6 +46,9 @@ var (
|
||||
// UIAuth used for euthentication
|
||||
UIAuth *htpasswd.File
|
||||
|
||||
// Webroot to define the base path for the UI and API
|
||||
Webroot = "/"
|
||||
|
||||
// SMTPSSLCert file
|
||||
SMTPSSLCert string
|
||||
|
||||
@ -139,6 +144,16 @@ func VerifyConfig() error {
|
||||
SMTPAuth = a
|
||||
}
|
||||
|
||||
if strings.Contains(Webroot, " ") {
|
||||
return fmt.Errorf("Webroot cannot contain spaces (%s)", Webroot)
|
||||
}
|
||||
|
||||
s, err := url.JoinPath("/", Webroot, "/")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Webroot = s
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
"os"
|
||||
"os/user"
|
||||
|
||||
"github.com/axllent/mailpit/logger"
|
||||
"github.com/axllent/mailpit/utils/logger"
|
||||
flag "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/axllent/mailpit/config"
|
||||
"github.com/axllent/mailpit/storage"
|
||||
"github.com/axllent/mailpit/updater"
|
||||
"github.com/axllent/mailpit/utils/updater"
|
||||
)
|
||||
|
||||
type appVersion struct {
|
||||
|
@ -10,8 +10,8 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/axllent/mailpit/logger"
|
||||
"github.com/axllent/mailpit/storage"
|
||||
"github.com/axllent/mailpit/utils/logger"
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/jhillyerd/enmime"
|
||||
|
@ -10,9 +10,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/axllent/mailpit/config"
|
||||
"github.com/axllent/mailpit/logger"
|
||||
"github.com/axllent/mailpit/server/apiv1"
|
||||
"github.com/axllent/mailpit/server/websockets"
|
||||
"github.com/axllent/mailpit/utils/logger"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
@ -34,10 +34,17 @@ func Listen() {
|
||||
r := defaultRoutes()
|
||||
|
||||
// web UI websocket
|
||||
r.HandleFunc("/api/events", apiWebsocket).Methods("GET")
|
||||
r.HandleFunc(config.Webroot+"api/events", apiWebsocket).Methods("GET")
|
||||
|
||||
// virtual filesystem for others
|
||||
r.PathPrefix("/").Handler(middlewareHandler(http.FileServer(http.FS(serverRoot))))
|
||||
r.PathPrefix(config.Webroot).Handler(middlewareHandler(http.StripPrefix(config.Webroot, http.FileServer(http.FS(serverRoot)))))
|
||||
|
||||
// redirect to webroot if no trailing slash
|
||||
if config.Webroot != "/" {
|
||||
redir := strings.TrimRight(config.Webroot, "/")
|
||||
r.HandleFunc(redir, middleWareFunc(addSlashToWebroot)).Methods("GET")
|
||||
}
|
||||
|
||||
http.Handle("/", r)
|
||||
|
||||
if config.UIAuthFile != "" {
|
||||
@ -45,10 +52,10 @@ func Listen() {
|
||||
}
|
||||
|
||||
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%s", config.HTTPListen, config.Webroot)
|
||||
logger.Log().Fatal(http.ListenAndServeTLS(config.HTTPListen, config.UISSLCert, config.UISSLKey, nil))
|
||||
} else {
|
||||
logger.Log().Infof("[http] starting server on http://%s", config.HTTPListen)
|
||||
logger.Log().Infof("[http] starting server on http://%s%s", config.HTTPListen, config.Webroot)
|
||||
logger.Log().Fatal(http.ListenAndServe(config.HTTPListen, nil))
|
||||
}
|
||||
}
|
||||
@ -57,16 +64,16 @@ func defaultRoutes() *mux.Router {
|
||||
r := mux.NewRouter()
|
||||
|
||||
// API V1
|
||||
r.HandleFunc("/api/v1/messages", middleWareFunc(apiv1.GetMessages)).Methods("GET")
|
||||
r.HandleFunc("/api/v1/messages", middleWareFunc(apiv1.SetReadStatus)).Methods("PUT")
|
||||
r.HandleFunc("/api/v1/messages", middleWareFunc(apiv1.DeleteMessages)).Methods("DELETE")
|
||||
r.HandleFunc("/api/v1/search", middleWareFunc(apiv1.Search)).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}/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.GetMessage)).Methods("GET")
|
||||
r.HandleFunc("/api/v1/info", middleWareFunc(apiv1.AppInfo)).Methods("GET")
|
||||
r.HandleFunc(config.Webroot+"api/v1/messages", middleWareFunc(apiv1.GetMessages)).Methods("GET")
|
||||
r.HandleFunc(config.Webroot+"api/v1/messages", middleWareFunc(apiv1.SetReadStatus)).Methods("PUT")
|
||||
r.HandleFunc(config.Webroot+"api/v1/messages", middleWareFunc(apiv1.DeleteMessages)).Methods("DELETE")
|
||||
r.HandleFunc(config.Webroot+"api/v1/search", middleWareFunc(apiv1.Search)).Methods("GET")
|
||||
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}", middleWareFunc(apiv1.GetMessage)).Methods("GET")
|
||||
r.HandleFunc(config.Webroot+"api/v1/info", middleWareFunc(apiv1.AppInfo)).Methods("GET")
|
||||
|
||||
return r
|
||||
}
|
||||
@ -152,6 +159,11 @@ func middlewareHandler(h http.Handler) http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
// Redirect to webroot
|
||||
func addSlashToWebroot(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, config.Webroot, http.StatusFound)
|
||||
}
|
||||
|
||||
// Websocket to broadcast changes
|
||||
func apiWebsocket(w http.ResponseWriter, r *http.Request) {
|
||||
websockets.ServeWs(websockets.MessageHub, w, r)
|
||||
|
@ -195,14 +195,14 @@ export default {
|
||||
if (a.ContentID != '') {
|
||||
d.HTML = d.HTML.replace(
|
||||
new RegExp('cid:' + a.ContentID, 'g'),
|
||||
window.location.origin + '/api/v1/message/' + d.ID + '/part/' + a.PartID
|
||||
window.location.origin + window.location.pathname + 'api/v1/message/' + d.ID + '/part/' + a.PartID
|
||||
);
|
||||
}
|
||||
if (a.FileName.match(/^[a-zA-Z0-9\_\-\.]+$/)) {
|
||||
// some old email clients use the filename
|
||||
d.HTML = d.HTML.replace(
|
||||
new RegExp('src=(\'|")' + a.FileName + '(\'|")', 'g'),
|
||||
'src="' + window.location.origin + '/api/v1/message/' + d.ID + '/part/' + a.PartID + '"'
|
||||
'src="' + window.location.origin + window.location.pathname + 'api/v1/message/' + d.ID + '/part/' + a.PartID + '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -214,14 +214,14 @@ export default {
|
||||
if (a.ContentID != '') {
|
||||
d.HTML = d.HTML.replace(
|
||||
new RegExp('cid:' + a.ContentID, 'g'),
|
||||
window.location.origin + '/api/v1/message/' + d.ID + '/part/' + a.PartID
|
||||
window.location.origin + window.location.pathname + 'api/v1/message/' + d.ID + '/part/' + a.PartID
|
||||
);
|
||||
}
|
||||
if (a.FileName.match(/^[a-zA-Z0-9\_\-\.]+$/)) {
|
||||
// some old email clients use the filename
|
||||
d.HTML = d.HTML.replace(
|
||||
new RegExp('src=(\'|")' + a.FileName + '(\'|")', 'g'),
|
||||
'src="' + window.location.origin + '/api/v1/message/' + d.ID + '/part/' + a.PartID + '"'
|
||||
'src="' + window.location.origin + window.location.pathname + 'api/v1/message/' + d.ID + '/part/' + a.PartID + '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -587,7 +587,8 @@ export default {
|
||||
<span v-else>
|
||||
<small>
|
||||
{{ formatNumber(start + 1) }}-{{ formatNumber(start + items.length) }} <small>of</small> {{
|
||||
formatNumber(total) }}
|
||||
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'">
|
||||
@ -689,11 +690,13 @@ export default {
|
||||
</div>
|
||||
<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 }}</span>
|
||||
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 }}</b>
|
||||
message.From.Name : message.From.Address
|
||||
}}</b>
|
||||
</div>
|
||||
<div class="d-none d-lg-block text-truncate text-muted small privacy">
|
||||
{{ getPrimaryEmailTo(message) }}
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/axllent/mailpit/config"
|
||||
"github.com/axllent/mailpit/logger"
|
||||
"github.com/axllent/mailpit/utils/logger"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
|
@ -7,7 +7,7 @@ package websockets
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/axllent/mailpit/logger"
|
||||
"github.com/axllent/mailpit/utils/logger"
|
||||
)
|
||||
|
||||
// Hub maintains the set of active clients and broadcasts messages to the
|
||||
|
@ -7,8 +7,8 @@ import (
|
||||
"regexp"
|
||||
|
||||
"github.com/axllent/mailpit/config"
|
||||
"github.com/axllent/mailpit/logger"
|
||||
"github.com/axllent/mailpit/storage"
|
||||
"github.com/axllent/mailpit/utils/logger"
|
||||
"github.com/mhale/smtpd"
|
||||
)
|
||||
|
||||
|
@ -20,8 +20,8 @@ import (
|
||||
|
||||
"github.com/GuiaBolso/darwin"
|
||||
"github.com/axllent/mailpit/config"
|
||||
"github.com/axllent/mailpit/logger"
|
||||
"github.com/axllent/mailpit/server/websockets"
|
||||
"github.com/axllent/mailpit/utils/logger"
|
||||
"github.com/jhillyerd/enmime"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/leporo/sqlf"
|
||||
|
@ -10,8 +10,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/axllent/mailpit/config"
|
||||
"github.com/axllent/mailpit/logger"
|
||||
"github.com/axllent/mailpit/server/websockets"
|
||||
"github.com/axllent/mailpit/utils/logger"
|
||||
"github.com/jhillyerd/enmime"
|
||||
"github.com/k3a/html2text"
|
||||
"github.com/leporo/sqlf"
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/axllent/mailpit/logger"
|
||||
"github.com/axllent/mailpit/utils/logger"
|
||||
"github.com/axllent/semver"
|
||||
)
|
||||
|
Reference in New Issue
Block a user