From ab771cf76cc1b07b62632e398886be1a1c61e2af Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Sat, 29 Oct 2022 10:52:22 +1300 Subject: [PATCH 1/3] Move utils to subfolder --- cmd/root.go | 2 +- cmd/version.go | 2 +- sendmail/cmd/cmd.go | 2 +- server/apiv1/info.go | 2 +- server/apiv1/thumbnails.go | 2 +- server/server.go | 2 +- server/websockets/client.go | 2 +- server/websockets/hub.go | 2 +- smtpd/smtpd.go | 2 +- storage/database.go | 2 +- storage/utils.go | 2 +- {logger => utils/logger}/logger.go | 0 {updater => utils/updater}/targz.go | 0 {updater => utils/updater}/unzip.go | 0 {updater => utils/updater}/updater.go | 2 +- 15 files changed, 12 insertions(+), 12 deletions(-) rename {logger => utils/logger}/logger.go (100%) rename {updater => utils/updater}/targz.go (100%) rename {updater => utils/updater}/unzip.go (100%) rename {updater => utils/updater}/updater.go (99%) diff --git a/cmd/root.go b/cmd/root.go index 4fafb67..213368d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -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" ) diff --git a/cmd/version.go b/cmd/version.go index c4e7b35..a526cb1 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -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" ) diff --git a/sendmail/cmd/cmd.go b/sendmail/cmd/cmd.go index 6bf2ef7..df2c181 100644 --- a/sendmail/cmd/cmd.go +++ b/sendmail/cmd/cmd.go @@ -13,7 +13,7 @@ import ( "os" "os/user" - "github.com/axllent/mailpit/logger" + "github.com/axllent/mailpit/utils/logger" flag "github.com/spf13/pflag" ) diff --git a/server/apiv1/info.go b/server/apiv1/info.go index 5154faa..d08fb90 100644 --- a/server/apiv1/info.go +++ b/server/apiv1/info.go @@ -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 { diff --git a/server/apiv1/thumbnails.go b/server/apiv1/thumbnails.go index d77dec7..0092e56 100644 --- a/server/apiv1/thumbnails.go +++ b/server/apiv1/thumbnails.go @@ -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" diff --git a/server/server.go b/server/server.go index e43a75c..b0cc484 100644 --- a/server/server.go +++ b/server/server.go @@ -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" ) diff --git a/server/websockets/client.go b/server/websockets/client.go index d857d93..ddb1e04 100644 --- a/server/websockets/client.go +++ b/server/websockets/client.go @@ -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" ) diff --git a/server/websockets/hub.go b/server/websockets/hub.go index c19c6f4..0df4c66 100644 --- a/server/websockets/hub.go +++ b/server/websockets/hub.go @@ -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 diff --git a/smtpd/smtpd.go b/smtpd/smtpd.go index d29b75c..88f0325 100644 --- a/smtpd/smtpd.go +++ b/smtpd/smtpd.go @@ -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" ) diff --git a/storage/database.go b/storage/database.go index c4c6980..1945259 100644 --- a/storage/database.go +++ b/storage/database.go @@ -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" diff --git a/storage/utils.go b/storage/utils.go index 48ad7c0..73dd5a3 100644 --- a/storage/utils.go +++ b/storage/utils.go @@ -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" diff --git a/logger/logger.go b/utils/logger/logger.go similarity index 100% rename from logger/logger.go rename to utils/logger/logger.go diff --git a/updater/targz.go b/utils/updater/targz.go similarity index 100% rename from updater/targz.go rename to utils/updater/targz.go diff --git a/updater/unzip.go b/utils/updater/unzip.go similarity index 100% rename from updater/unzip.go rename to utils/updater/unzip.go diff --git a/updater/updater.go b/utils/updater/updater.go similarity index 99% rename from updater/updater.go rename to utils/updater/updater.go index 7c94031..9acc638 100644 --- a/updater/updater.go +++ b/utils/updater/updater.go @@ -13,7 +13,7 @@ import ( "path/filepath" "runtime" - "github.com/axllent/mailpit/logger" + "github.com/axllent/mailpit/utils/logger" "github.com/axllent/semver" ) From cbc3fe59a8c49c234bb721fb8f1d5a069b470aa3 Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Mon, 31 Oct 2022 22:13:41 +1300 Subject: [PATCH 2/3] Feature: Allow custom webroot Allow Mailpit to run on a custom webroot, resolves #19 --- cmd/root.go | 4 ++++ config/config.go | 15 +++++++++++++++ server/server.go | 40 ++++++++++++++++++++++++++-------------- server/ui-src/App.vue | 43 +++++++++++++++++++++++-------------------- 4 files changed, 68 insertions(+), 34 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 213368d..bab5887 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -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") diff --git a/config/config.go b/config/config.go index c4a6c88..639b2ec 100644 --- a/config/config.go +++ b/config/config.go @@ -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 } diff --git a/server/server.go b/server/server.go index b0cc484..3d16568 100644 --- a/server/server.go +++ b/server/server.go @@ -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) diff --git a/server/ui-src/App.vue b/server/ui-src/App.vue index 62bee1a..3c1a04c 100644 --- a/server/ui-src/App.vue +++ b/server/ui-src/App.vue @@ -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 + '"' ); } } @@ -522,7 +522,7 @@ export default {
-
+