mirror of
https://gitlab.com/ric_harvey/MailHog.git
synced 2024-11-23 22:34:04 +00:00
Add HTTP server and assets
This commit is contained in:
parent
c4aebd5d66
commit
4f4f921013
11 changed files with 366 additions and 25 deletions
|
@ -2,12 +2,20 @@ package mailhog
|
|||
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
BindAddr: "0.0.0.0:1025",
|
||||
Hostname: "mailhog.example",
|
||||
SMTPBindAddr: "0.0.0.0:1025",
|
||||
HTTPBindAddr: "0.0.0.0:8025",
|
||||
Hostname: "mailhog.example",
|
||||
MongoUri: "127.0.0.1:27017",
|
||||
MongoDb: "mailhog",
|
||||
MongoColl: "messages",
|
||||
}
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
BindAddr string
|
||||
SMTPBindAddr string
|
||||
HTTPBindAddr string
|
||||
Hostname string
|
||||
MongoUri string
|
||||
MongoDb string
|
||||
MongoColl string
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@ func ContentFromString(data string) *Content {
|
|||
if(strings.Contains(hdr, ": ")) {
|
||||
y := strings.SplitN(hdr, ": ", 2)
|
||||
key, value := y[0], y[1]
|
||||
// TODO multiple header fields
|
||||
h[key] = []string{value}
|
||||
} else {
|
||||
log.Printf("Found invalid header: '%s'", hdr)
|
||||
|
|
50
mailhog/http/server.go
Normal file
50
mailhog/http/server.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"github.com/ian-kent/MailHog/mailhog"
|
||||
"github.com/ian-kent/MailHog/mailhog/templates"
|
||||
"github.com/ian-kent/MailHog/mailhog/templates/images"
|
||||
"github.com/ian-kent/MailHog/mailhog/templates/js"
|
||||
)
|
||||
|
||||
var exitChannel chan int
|
||||
|
||||
func web_exit(w http.ResponseWriter, r *http.Request) {
|
||||
web_headers(w)
|
||||
fmt.Fprint(w, "Exiting MailHog!")
|
||||
exitChannel <- 1
|
||||
}
|
||||
|
||||
func web_index(w http.ResponseWriter, r *http.Request) {
|
||||
web_headers(w)
|
||||
fmt.Fprint(w, web_render(templates.Index()))
|
||||
}
|
||||
|
||||
func web_jscontroller(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/javascript")
|
||||
fmt.Fprint(w, js.Controllers())
|
||||
}
|
||||
|
||||
func web_imgcontroller(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "image/png")
|
||||
w.Write(images.Hog())
|
||||
}
|
||||
|
||||
func web_render(content string) string {
|
||||
return templates.Layout(content)
|
||||
}
|
||||
|
||||
func web_headers(w http.ResponseWriter) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
}
|
||||
|
||||
func Start(exitCh chan int, conf *mailhog.Config) {
|
||||
exitChannel = exitCh
|
||||
http.HandleFunc("/exit", web_exit)
|
||||
http.HandleFunc("/js/controllers.js", web_jscontroller)
|
||||
http.HandleFunc("/images/hog.png", web_imgcontroller)
|
||||
http.HandleFunc("/", web_index)
|
||||
http.ListenAndServe(conf.HTTPBindAddr, nil)
|
||||
}
|
|
@ -28,7 +28,6 @@ const (
|
|||
DONE
|
||||
)
|
||||
|
||||
// TODO add Received/Return-Path headers
|
||||
// TODO replace ".." lines with . in data
|
||||
|
||||
func StartSession(conn *net.TCPConn, conf *mailhog.Config) {
|
||||
|
@ -39,7 +38,7 @@ func StartSession(conn *net.TCPConn, conf *mailhog.Config) {
|
|||
}
|
||||
|
||||
func (c *Session) log(message string, args ...interface{}) {
|
||||
message = strings.Join([]string{"[%s, %d]", message}, " ")
|
||||
message = strings.Join([]string{"[SMTP %s, %d]", message}, " ")
|
||||
args = append([]interface{}{c.conn.RemoteAddr(), c.state}, args...)
|
||||
log.Printf(message, args...)
|
||||
}
|
||||
|
|
|
@ -10,13 +10,13 @@ import (
|
|||
|
||||
func Store(c *mailhog.Config, m *data.SMTPMessage) (string, error) {
|
||||
msg := data.ParseSMTPMessage(c, m)
|
||||
session, err := mgo.Dial("localhost:27017")
|
||||
session, err := mgo.Dial(c.MongoUri)
|
||||
if(err != nil) {
|
||||
log.Printf("Error connecting to MongoDB: %s", err)
|
||||
return "", err
|
||||
}
|
||||
defer session.Close()
|
||||
err = session.DB("mailhog").C("messages").Insert(msg)
|
||||
err = session.DB(c.MongoDb).C(c.MongoColl).Insert(msg)
|
||||
if err != nil {
|
||||
log.Printf("Error inserting message: %s", err)
|
||||
return "", err
|
||||
|
@ -24,15 +24,15 @@ func Store(c *mailhog.Config, m *data.SMTPMessage) (string, error) {
|
|||
return msg.Id, nil
|
||||
}
|
||||
|
||||
func Load(id string) (*data.Message, error) {
|
||||
session, err := mgo.Dial("localhost:27017")
|
||||
func Load(c *mailhog.Config, id string) (*data.Message, error) {
|
||||
session, err := mgo.Dial(c.MongoUri)
|
||||
if(err != nil) {
|
||||
log.Printf("Error connecting to MongoDB: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
defer session.Close()
|
||||
result := &data.Message{}
|
||||
err = session.DB("mailhog").C("messages").Find(bson.M{"id": id}).One(&result)
|
||||
err = session.DB(c.MongoDb).C(c.MongoColl).Find(bson.M{"id": id}).One(&result)
|
||||
if err != nil {
|
||||
log.Printf("Error loading message: %s", err)
|
||||
return nil, err
|
||||
|
|
60
mailhog/templates/images/hog.go
Normal file
60
mailhog/templates/images/hog.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
package images
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
func Hog() []byte {
|
||||
hog := `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
|
||||
qgAAAABJRU5ErkJggg==`
|
||||
data, _ := base64.StdEncoding.DecodeString(hog)
|
||||
return data
|
||||
}
|
90
mailhog/templates/index.go
Normal file
90
mailhog/templates/index.go
Normal file
|
@ -0,0 +1,90 @@
|
|||
package templates
|
||||
|
||||
func Index() string {
|
||||
return `
|
||||
<style>
|
||||
.messages {
|
||||
height: 30%;
|
||||
}
|
||||
.preview {
|
||||
height: 70%;
|
||||
border-top: 1px solid #CCCCCC;
|
||||
}
|
||||
.preview #headers {
|
||||
border-bottom: 1px solid #DDDDDD;
|
||||
}
|
||||
.selected {
|
||||
background: #DADAFA;
|
||||
}
|
||||
table tbody {
|
||||
overflow: scroll;
|
||||
}
|
||||
table td {
|
||||
padding: 2px 4px 2px 4px !important;
|
||||
}
|
||||
</style>
|
||||
<div class="modal fade" id="confirm-delete-all">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Delete all messages?</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure you want to delete all messages?</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-danger" ng-click="deleteAllConfirm()">Delete all messages</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="messages">
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>From</th>
|
||||
<th>To</th>
|
||||
<th>Subject</th>
|
||||
<th>Received</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr ng-repeat="message in messages" ng-click="selectMessage(message)" ng-class="{ selected: message == preview }">
|
||||
<td>
|
||||
{{ message.from.mailbox }}@{{ message.from.domain }}
|
||||
</td>
|
||||
<td>
|
||||
<span ng-repeat="to in message.to">
|
||||
{{ to.mailbox }}@{{ to.domain }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
{{ message.content.headers.Subject }}
|
||||
</td>
|
||||
<td>
|
||||
{{ date(message.created) }}
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-default" title="Delete" ng-click="deleteOne(message)"><span class="glyphicon glyphicon-remove"></span></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="preview">
|
||||
<table class="table" id="headers">
|
||||
<tr ng-repeat="(header, value) in preview.content.headers">
|
||||
<td>
|
||||
{{ header }}
|
||||
</td>
|
||||
<td>
|
||||
{{ value }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
{{ preview.content.body }}
|
||||
</div>
|
||||
`;
|
||||
}
|
43
mailhog/templates/js/controllers.go
Normal file
43
mailhog/templates/js/controllers.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package js
|
||||
|
||||
func Controllers() string {
|
||||
return `
|
||||
var mailhogApp = angular.module('mailhogApp', []);
|
||||
|
||||
mailhogApp.controller('MailCtrl', function ($scope, $http) {
|
||||
$scope.refresh = function() {
|
||||
$http.get('/api/v1/messages').success(function(data) {
|
||||
$scope.messages = data;
|
||||
});
|
||||
}
|
||||
$scope.refresh();
|
||||
|
||||
$scope.date = function(timestamp) {
|
||||
return (new Date(timestamp)).toString();
|
||||
};
|
||||
|
||||
$scope.selectMessage = function(message) {
|
||||
$scope.preview = message;
|
||||
}
|
||||
|
||||
$scope.deleteAll = function() {
|
||||
$('#confirm-delete-all').modal('show');
|
||||
}
|
||||
|
||||
$scope.deleteAllConfirm = function() {
|
||||
$('#confirm-delete-all').modal('hide');
|
||||
$http.post('/api/v1/messages/delete').success(function() {
|
||||
$scope.refresh();
|
||||
$scope.preview = null;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.deleteOne = function(message) {
|
||||
$http.post('/api/v1/messages/' + message._id + '/delete').success(function() {
|
||||
if($scope.preview._id == message._id) $scope.preview = null;
|
||||
$scope.refresh();
|
||||
});
|
||||
}
|
||||
});
|
||||
`;
|
||||
}
|
64
mailhog/templates/layout.go
Normal file
64
mailhog/templates/layout.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
package templates
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Layout(content string) string {
|
||||
html := `
|
||||
<!DOCTYPE html>
|
||||
<html ng-app="mailhogApp">
|
||||
<head>
|
||||
<title>MailHog</title>
|
||||
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css">
|
||||
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.15/angular.js"></script>
|
||||
<script src="/js/controllers.js"></script>
|
||||
<style>
|
||||
body, html { height: 100%; overflow: none; }
|
||||
.navbar {
|
||||
margin-bottom: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.messages {
|
||||
padding-top: 50px;
|
||||
}
|
||||
.navbar-header img {
|
||||
height: 35px;
|
||||
margin: 8px 0 0 5px;
|
||||
float: left;
|
||||
}
|
||||
.navbar-nav.navbar-right:last-child {
|
||||
margin-right: 0; /* bootstrap fix?! */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body ng-controller="MailCtrl">
|
||||
<nav class="navbar navbar-default navbar-static-top" role="navigation">
|
||||
<div class="navbar-header">
|
||||
<img src="/images/hog.png">
|
||||
<a class="navbar-brand" href="#">MailHog</a>
|
||||
</div>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Options <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" ng-click="refresh()">Refresh</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="#" ng-click="deleteAll()">Delete all messages</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a target="_blank" href="https://github.com/ian-kent/MailHog">GitHub</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<%= content %>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
return strings.Replace(html, "<%= content %>", content, -1);
|
||||
}
|
53
main.go
53
main.go
|
@ -4,36 +4,70 @@ import (
|
|||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"github.com/ian-kent/MailHog/mailhog"
|
||||
"github.com/ian-kent/MailHog/mailhog/http"
|
||||
"github.com/ian-kent/MailHog/mailhog/smtp"
|
||||
)
|
||||
|
||||
var conf *mailhog.Config
|
||||
var exitCh chan int
|
||||
|
||||
func config() {
|
||||
var listen, hostname string
|
||||
var smtpbindaddr, httpbindaddr, hostname, mongouri, mongodb, mongocoll string
|
||||
|
||||
flag.StringVar(&listen, "listen", "0.0.0.0:1025", "Bind interface and port, e.g. 0.0.0.0:1025 or just :1025")
|
||||
flag.StringVar(&smtpbindaddr, "smtpbindaddr", "0.0.0.0:1025", "SMTP bind interface and port, e.g. 0.0.0.0:1025 or just :1025")
|
||||
flag.StringVar(&httpbindaddr, "httpbindaddr", "0.0.0.0:8025", "HTTP bind interface and port, e.g. 0.0.0.0:8025 or just :8025")
|
||||
flag.StringVar(&hostname, "hostname", "mailhog.example", "Hostname for EHLO/HELO response, e.g. mailhog.example")
|
||||
flag.StringVar(&mongouri, "mongouri", "127.0.0.1:27017", "MongoDB URI, e.g. 127.0.0.1:27017")
|
||||
flag.StringVar(&mongodb, "mongodb", "mailhog", "MongoDB database, e.g. mailhog")
|
||||
flag.StringVar(&mongocoll, "mongocoll", "messages", "MongoDB collection, e.g. messages")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
conf = &mailhog.Config{
|
||||
BindAddr: listen,
|
||||
SMTPBindAddr: smtpbindaddr,
|
||||
HTTPBindAddr: httpbindaddr,
|
||||
Hostname: hostname,
|
||||
MongoUri: mongouri,
|
||||
MongoDb: mongodb,
|
||||
MongoColl: mongocoll,
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
config()
|
||||
|
||||
ln := listen(conf.BindAddr)
|
||||
exitCh = make(chan int)
|
||||
go web_listen()
|
||||
go smtp_listen()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-exitCh:
|
||||
log.Printf("Received exit signal")
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func web_listen() {
|
||||
log.Printf("[HTTP] Binding to address: %s\n", conf.HTTPBindAddr)
|
||||
http.Start(exitCh, conf)
|
||||
}
|
||||
|
||||
func smtp_listen() (*net.TCPListener) {
|
||||
log.Printf("[SMTP] Binding to address: %s\n", conf.SMTPBindAddr)
|
||||
ln, err := net.Listen("tcp", conf.SMTPBindAddr)
|
||||
if err != nil {
|
||||
log.Fatalf("[SMTP] Error listening on socket: %s\n", err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
log.Printf("Error accepting connection: %s\n", err)
|
||||
log.Printf("[SMTP] Error accepting connection: %s\n", err)
|
||||
continue
|
||||
}
|
||||
defer conn.Close()
|
||||
|
@ -41,12 +75,3 @@ func main() {
|
|||
go smtp.StartSession(conn.(*net.TCPConn), conf)
|
||||
}
|
||||
}
|
||||
|
||||
func listen(bind string) (*net.TCPListener) {
|
||||
log.Printf("Binding to address: %s\n", bind)
|
||||
ln, err := net.Listen("tcp", bind)
|
||||
if err != nil {
|
||||
log.Fatalf("Error listening on socket: %s\n", err)
|
||||
}
|
||||
return ln.(*net.TCPListener)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net"
|
||||
"strings"
|
||||
"regexp"
|
||||
"github.com/ian-kent/MailHog/mailhog"
|
||||
"github.com/ian-kent/MailHog/mailhog/storage"
|
||||
)
|
||||
|
||||
|
@ -86,7 +87,7 @@ func TestBasicHappyPath(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
assert.Equal(t, string(buf[0:n]), "221 Bye\n")
|
||||
|
||||
message, err := storage.Load(match[1])
|
||||
message, err := storage.Load(mailhog.DefaultConfig(), match[1])
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, message)
|
||||
|
||||
|
|
Loading…
Reference in a new issue