mirror of
https://gitlab.com/ric_harvey/MailHog.git
synced 2024-11-24 14:54:03 +00:00
259 lines
6.3 KiB
Go
259 lines
6.3 KiB
Go
|
package api
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"net/http"
|
||
|
"strconv"
|
||
|
|
||
|
"github.com/gorilla/pat"
|
||
|
"github.com/ian-kent/go-log/log"
|
||
|
"github.com/mailhog/MailHog-Server/config"
|
||
|
"github.com/mailhog/MailHog-Server/monkey"
|
||
|
"github.com/mailhog/MailHog-Server/websockets"
|
||
|
"github.com/mailhog/data"
|
||
|
)
|
||
|
|
||
|
// APIv2 implements version 2 of the MailHog API
|
||
|
//
|
||
|
// It is currently experimental and may change in future releases.
|
||
|
// Use APIv1 for guaranteed compatibility.
|
||
|
type APIv2 struct {
|
||
|
config *config.Config
|
||
|
messageChan chan *data.Message
|
||
|
wsHub *websockets.Hub
|
||
|
}
|
||
|
|
||
|
func createAPIv2(conf *config.Config, r *pat.Router) *APIv2 {
|
||
|
log.Println("Creating API v2 with WebPath: " + conf.WebPath)
|
||
|
apiv2 := &APIv2{
|
||
|
config: conf,
|
||
|
messageChan: make(chan *data.Message),
|
||
|
wsHub: websockets.NewHub(),
|
||
|
}
|
||
|
|
||
|
r.Path(conf.WebPath + "/api/v2/messages").Methods("GET").HandlerFunc(apiv2.messages)
|
||
|
r.Path(conf.WebPath + "/api/v2/messages").Methods("OPTIONS").HandlerFunc(apiv2.defaultOptions)
|
||
|
|
||
|
r.Path(conf.WebPath + "/api/v2/search").Methods("GET").HandlerFunc(apiv2.search)
|
||
|
r.Path(conf.WebPath + "/api/v2/search").Methods("OPTIONS").HandlerFunc(apiv2.defaultOptions)
|
||
|
|
||
|
r.Path(conf.WebPath + "/api/v2/jim").Methods("GET").HandlerFunc(apiv2.jim)
|
||
|
r.Path(conf.WebPath + "/api/v2/jim").Methods("POST").HandlerFunc(apiv2.createJim)
|
||
|
r.Path(conf.WebPath + "/api/v2/jim").Methods("PUT").HandlerFunc(apiv2.updateJim)
|
||
|
r.Path(conf.WebPath + "/api/v2/jim").Methods("DELETE").HandlerFunc(apiv2.deleteJim)
|
||
|
r.Path(conf.WebPath + "/api/v2/jim").Methods("OPTIONS").HandlerFunc(apiv2.defaultOptions)
|
||
|
|
||
|
r.Path(conf.WebPath + "/api/v2/outgoing-smtp").Methods("GET").HandlerFunc(apiv2.listOutgoingSMTP)
|
||
|
r.Path(conf.WebPath + "/api/v2/outgoing-smtp").Methods("OPTIONS").HandlerFunc(apiv2.defaultOptions)
|
||
|
|
||
|
r.Path(conf.WebPath + "/api/v2/websocket").Methods("GET").HandlerFunc(apiv2.websocket)
|
||
|
|
||
|
go func() {
|
||
|
for {
|
||
|
select {
|
||
|
case msg := <-apiv2.messageChan:
|
||
|
log.Println("Got message in APIv2 websocket channel")
|
||
|
apiv2.broadcast(msg)
|
||
|
}
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
return apiv2
|
||
|
}
|
||
|
|
||
|
func (apiv2 *APIv2) defaultOptions(w http.ResponseWriter, req *http.Request) {
|
||
|
if len(apiv2.config.CORSOrigin) > 0 {
|
||
|
w.Header().Add("Access-Control-Allow-Origin", apiv2.config.CORSOrigin)
|
||
|
w.Header().Add("Access-Control-Allow-Methods", "OPTIONS,GET,PUT,POST,DELETE")
|
||
|
w.Header().Add("Access-Control-Allow-Headers", "Content-Type")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type messagesResult struct {
|
||
|
Total int `json:"total"`
|
||
|
Count int `json:"count"`
|
||
|
Start int `json:"start"`
|
||
|
Items []data.Message `json:"items"`
|
||
|
}
|
||
|
|
||
|
func (apiv2 *APIv2) getStartLimit(w http.ResponseWriter, req *http.Request) (start, limit int) {
|
||
|
start = 0
|
||
|
limit = 50
|
||
|
|
||
|
s := req.URL.Query().Get("start")
|
||
|
if n, e := strconv.ParseInt(s, 10, 64); e == nil && n > 0 {
|
||
|
start = int(n)
|
||
|
}
|
||
|
|
||
|
l := req.URL.Query().Get("limit")
|
||
|
if n, e := strconv.ParseInt(l, 10, 64); e == nil && n > 0 {
|
||
|
if n > 250 {
|
||
|
n = 250
|
||
|
}
|
||
|
limit = int(n)
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (apiv2 *APIv2) messages(w http.ResponseWriter, req *http.Request) {
|
||
|
log.Println("[APIv2] GET /api/v2/messages")
|
||
|
|
||
|
apiv2.defaultOptions(w, req)
|
||
|
|
||
|
start, limit := apiv2.getStartLimit(w, req)
|
||
|
|
||
|
var res messagesResult
|
||
|
|
||
|
messages, err := apiv2.config.Storage.List(start, limit)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
res.Count = len([]data.Message(*messages))
|
||
|
res.Start = start
|
||
|
res.Items = []data.Message(*messages)
|
||
|
res.Total = apiv2.config.Storage.Count()
|
||
|
|
||
|
bytes, _ := json.Marshal(res)
|
||
|
w.Header().Add("Content-Type", "text/json")
|
||
|
w.Write(bytes)
|
||
|
}
|
||
|
|
||
|
func (apiv2 *APIv2) search(w http.ResponseWriter, req *http.Request) {
|
||
|
log.Println("[APIv2] GET /api/v2/search")
|
||
|
|
||
|
apiv2.defaultOptions(w, req)
|
||
|
|
||
|
start, limit := apiv2.getStartLimit(w, req)
|
||
|
|
||
|
kind := req.URL.Query().Get("kind")
|
||
|
if kind != "from" && kind != "to" && kind != "containing" {
|
||
|
w.WriteHeader(400)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
query := req.URL.Query().Get("query")
|
||
|
if len(query) == 0 {
|
||
|
w.WriteHeader(400)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
var res messagesResult
|
||
|
|
||
|
messages, total, _ := apiv2.config.Storage.Search(kind, query, start, limit)
|
||
|
|
||
|
res.Count = len([]data.Message(*messages))
|
||
|
res.Start = start
|
||
|
res.Items = []data.Message(*messages)
|
||
|
res.Total = total
|
||
|
|
||
|
b, _ := json.Marshal(res)
|
||
|
w.Header().Add("Content-Type", "application/json")
|
||
|
w.Write(b)
|
||
|
}
|
||
|
|
||
|
func (apiv2 *APIv2) jim(w http.ResponseWriter, req *http.Request) {
|
||
|
log.Println("[APIv2] GET /api/v2/jim")
|
||
|
|
||
|
apiv2.defaultOptions(w, req)
|
||
|
|
||
|
if apiv2.config.Monkey == nil {
|
||
|
w.WriteHeader(404)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
b, _ := json.Marshal(apiv2.config.Monkey)
|
||
|
w.Header().Add("Content-Type", "application/json")
|
||
|
w.Write(b)
|
||
|
}
|
||
|
|
||
|
func (apiv2 *APIv2) deleteJim(w http.ResponseWriter, req *http.Request) {
|
||
|
log.Println("[APIv2] DELETE /api/v2/jim")
|
||
|
|
||
|
apiv2.defaultOptions(w, req)
|
||
|
|
||
|
if apiv2.config.Monkey == nil {
|
||
|
w.WriteHeader(404)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
apiv2.config.Monkey = nil
|
||
|
}
|
||
|
|
||
|
func (apiv2 *APIv2) createJim(w http.ResponseWriter, req *http.Request) {
|
||
|
log.Println("[APIv2] POST /api/v2/jim")
|
||
|
|
||
|
apiv2.defaultOptions(w, req)
|
||
|
|
||
|
if apiv2.config.Monkey != nil {
|
||
|
w.WriteHeader(400)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
apiv2.config.Monkey = config.Jim
|
||
|
|
||
|
// Try, but ignore errors
|
||
|
// Could be better (e.g., ok if no json, error if badly formed json)
|
||
|
// but this works for now
|
||
|
apiv2.newJimFromBody(w, req)
|
||
|
|
||
|
w.WriteHeader(201)
|
||
|
}
|
||
|
|
||
|
func (apiv2 *APIv2) newJimFromBody(w http.ResponseWriter, req *http.Request) error {
|
||
|
var jim monkey.Jim
|
||
|
|
||
|
dec := json.NewDecoder(req.Body)
|
||
|
err := dec.Decode(&jim)
|
||
|
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
jim.ConfigureFrom(config.Jim)
|
||
|
|
||
|
config.Jim = &jim
|
||
|
apiv2.config.Monkey = &jim
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (apiv2 *APIv2) updateJim(w http.ResponseWriter, req *http.Request) {
|
||
|
log.Println("[APIv2] PUT /api/v2/jim")
|
||
|
|
||
|
apiv2.defaultOptions(w, req)
|
||
|
|
||
|
if apiv2.config.Monkey == nil {
|
||
|
w.WriteHeader(404)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
err := apiv2.newJimFromBody(w, req)
|
||
|
if err != nil {
|
||
|
w.WriteHeader(400)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (apiv2 *APIv2) listOutgoingSMTP(w http.ResponseWriter, req *http.Request) {
|
||
|
log.Println("[APIv2] GET /api/v2/outgoing-smtp")
|
||
|
|
||
|
apiv2.defaultOptions(w, req)
|
||
|
|
||
|
b, _ := json.Marshal(apiv2.config.OutgoingSMTP)
|
||
|
w.Header().Add("Content-Type", "application/json")
|
||
|
w.Write(b)
|
||
|
}
|
||
|
|
||
|
func (apiv2 *APIv2) websocket(w http.ResponseWriter, req *http.Request) {
|
||
|
log.Println("[APIv2] GET /api/v2/websocket")
|
||
|
|
||
|
apiv2.wsHub.Serve(w, req)
|
||
|
}
|
||
|
|
||
|
func (apiv2 *APIv2) broadcast(msg *data.Message) {
|
||
|
log.Println("[APIv2] BROADCAST /api/v2/websocket")
|
||
|
|
||
|
apiv2.wsHub.Broadcast(msg)
|
||
|
}
|