mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-18 10:07:02 +00:00
Merge in REST changes
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
"Resources": {
|
"Resources": {
|
||||||
"Include": "README*,LICENSE*,bin,etc,themes"
|
"Include": "README*,LICENSE*,bin,etc,themes"
|
||||||
},
|
},
|
||||||
"PackageVersion": "20131001",
|
"PackageVersion": "20131010",
|
||||||
"PrereleaseInfo": "snapshot",
|
"PrereleaseInfo": "snapshot",
|
||||||
"FormatVersion": "0.8"
|
"FormatVersion": "0.8"
|
||||||
}
|
}
|
||||||
|
|||||||
1
bin/mailbox-list.sh
Executable file
1
bin/mailbox-list.sh
Executable file
@@ -0,0 +1 @@
|
|||||||
|
curl -i -H "Accept: application/json" --noproxy localhost http://localhost:9000/mailbox/$1
|
||||||
1
bin/message-delete.sh
Executable file
1
bin/message-delete.sh
Executable file
@@ -0,0 +1 @@
|
|||||||
|
curl -i -H "Accept: application/json" --noproxy localhost -X DELETE http://localhost:9000/mailbox/$1/$2
|
||||||
1
bin/message-source.sh
Executable file
1
bin/message-source.sh
Executable file
@@ -0,0 +1 @@
|
|||||||
|
curl -i -H "Accept: application/json" --noproxy localhost http://localhost:9000/mailbox/$1/$2/source
|
||||||
@@ -25,7 +25,7 @@ type SmtpConfig struct {
|
|||||||
type Pop3Config struct {
|
type Pop3Config struct {
|
||||||
Ip4address net.IP
|
Ip4address net.IP
|
||||||
Ip4port int
|
Ip4port int
|
||||||
Domain string
|
Domain string
|
||||||
MaxIdleSeconds int
|
MaxIdleSeconds int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,6 +35,7 @@ type WebConfig struct {
|
|||||||
TemplateDir string
|
TemplateDir string
|
||||||
TemplateCache bool
|
TemplateCache bool
|
||||||
PublicDir string
|
PublicDir string
|
||||||
|
GreetingFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
type DataStoreConfig struct {
|
type DataStoreConfig struct {
|
||||||
@@ -328,6 +329,13 @@ func parseWebConfig() error {
|
|||||||
}
|
}
|
||||||
webConfig.PublicDir = str
|
webConfig.PublicDir = str
|
||||||
|
|
||||||
|
option = "greeting.file"
|
||||||
|
str, err = Config.String(section, option)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to parse [%v]%v: '%v'", section, option, err)
|
||||||
|
}
|
||||||
|
webConfig.GreetingFile = str
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ template.cache=false
|
|||||||
# Path to the selected themes public (static) files
|
# Path to the selected themes public (static) files
|
||||||
public.dir=%(install.dir)s/themes/%(theme)s/public
|
public.dir=%(install.dir)s/themes/%(theme)s/public
|
||||||
|
|
||||||
|
# Path to the greeting HTML displayed on front page, can
|
||||||
|
# be moved out of installation dir for customization
|
||||||
|
greeting.file=%(install.dir)s/themes/greeting.html
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
[datastore]
|
[datastore]
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ template.cache=true
|
|||||||
# Path to the selected themes public (static) files
|
# Path to the selected themes public (static) files
|
||||||
public.dir=%(install.dir)s/themes/%(theme)s/public
|
public.dir=%(install.dir)s/themes/%(theme)s/public
|
||||||
|
|
||||||
|
# Path to the greeting HTML displayed on front page, can
|
||||||
|
# be moved out of installation dir for customization
|
||||||
|
greeting.file=%(install.dir)s/themes/greeting.html
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
[datastore]
|
[datastore]
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ template.cache=true
|
|||||||
# Path to the selected themes public (static) files
|
# Path to the selected themes public (static) files
|
||||||
public.dir=%(install.dir)s/themes/%(theme)s/public
|
public.dir=%(install.dir)s/themes/%(theme)s/public
|
||||||
|
|
||||||
|
# Path to the greeting HTML displayed on front page, can
|
||||||
|
# be moved out of installation dir for customization
|
||||||
|
greeting.file=%(install.dir)s/themes/greeting.html
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
[datastore]
|
[datastore]
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ template.cache=true
|
|||||||
# Path to the selected themes public (static) files
|
# Path to the selected themes public (static) files
|
||||||
public.dir=%(install.dir)s\themes\%(theme)s\public
|
public.dir=%(install.dir)s\themes\%(theme)s\public
|
||||||
|
|
||||||
|
# Path to the greeting HTML displayed on front page, can
|
||||||
|
# be moved out of installation dir for customization
|
||||||
|
greeting.file=%(install.dir)s\themes\greeting.html
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
[datastore]
|
[datastore]
|
||||||
|
|
||||||
|
|||||||
9
themes/greeting.html
Normal file
9
themes/greeting.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<p>Inbucket is an email testing service; it will accept email for any email
|
||||||
|
address and make it available to view without a password.</p>
|
||||||
|
|
||||||
|
<p>To view email for a particular address, enter the username portion
|
||||||
|
of the address into the box on the upper right and click <em>go</em>.</p>
|
||||||
|
|
||||||
|
<p>This message can be customized by editing greeting.html. Change the
|
||||||
|
configuration option <code>greeting.file</code> if you'd like to move it
|
||||||
|
outside of the Inbucket installation directory.</p>
|
||||||
@@ -47,7 +47,7 @@ Released for free under a Creative Commons Attribution 2.5 License
|
|||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
<p>Inbucket is an open source project hosted at
|
<p>Inbucket is an open source project hosted at
|
||||||
<a href="http://jhillyerd.github.com/inbucket/">github</a>.
|
<a href="http://jhillyerd.github.io/inbucket/">github</a>.
|
||||||
Design by <a href="http://www.freecsstemplates.org/">FCT</a>.</p>
|
Design by <a href="http://www.freecsstemplates.org/">FCT</a>.</p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -15,14 +15,14 @@
|
|||||||
function() {
|
function() {
|
||||||
$('.listEntry').removeClass("listEntrySelected")
|
$('.listEntry').removeClass("listEntrySelected")
|
||||||
$(this).addClass("listEntrySelected")
|
$(this).addClass("listEntrySelected")
|
||||||
$('#emailContent').load('/mailbox/show/{{.name}}/' + this.id)
|
$('#emailContent').load('/mailbox/{{.name}}/' + this.id)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
$("#messageList").slideDown()
|
$("#messageList").slideDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadList() {
|
function loadList() {
|
||||||
$('#messageList').load("/mailbox/list/{{.name}}", listLoaded)
|
$('#messageList').load("/mailbox/{{.name}}", listLoaded)
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadList() {
|
function reloadList() {
|
||||||
@@ -38,20 +38,20 @@
|
|||||||
function deleteMessage(id) {
|
function deleteMessage(id) {
|
||||||
$('#emailContent').empty()
|
$('#emailContent').empty()
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'DELETE',
|
||||||
url: '/mailbox/delete/{{.name}}/' + id,
|
url: '/mailbox/{{.name}}/' + id,
|
||||||
success: reloadList
|
success: reloadList
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function htmlView(id) {
|
function htmlView(id) {
|
||||||
window.open('/mailbox/html/{{.name}}/' + id, '_blank',
|
window.open('/mailbox/{{.name}}/' + id + "/html", '_blank',
|
||||||
'width=800,height=600,' +
|
'width=800,height=600,' +
|
||||||
'menubar=yes,resizable=yes,scrollbars=yes,status=yes,toolbar=yes')
|
'menubar=yes,resizable=yes,scrollbars=yes,status=yes,toolbar=yes')
|
||||||
}
|
}
|
||||||
|
|
||||||
function messageSource(id) {
|
function messageSource(id) {
|
||||||
window.open('/mailbox/source/{{.name}}/' + id, '_blank',
|
window.open('/mailbox/{{.name}}/' + id + "/source", '_blank',
|
||||||
'width=800,height=600,' +
|
'width=800,height=600,' +
|
||||||
'menubar=no,resizable=yes,scrollbars=yes,status=no,toolbar=no')
|
'menubar=no,resizable=yes,scrollbars=yes,status=no,toolbar=no')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,4 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "content"}}
|
{{define "content"}}{{.greeting}}{{end}}
|
||||||
<p>Inbucket is an email testing service; it will accept email for any email
|
|
||||||
address and make it available to view without a password.</p>
|
|
||||||
|
|
||||||
<p>To view email for a particular address, enter the username portion
|
|
||||||
of the address into the box on the upper right and click <em>go</em>.</p>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
|
|||||||
@@ -139,6 +139,24 @@
|
|||||||
<p>Metrics are polled every 10 seconds. Inbucket does not keep history for the
|
<p>Metrics are polled every 10 seconds. Inbucket does not keep history for the
|
||||||
10 minute graphs, but your web browser will accumulate the data over time.</p>
|
10 minute graphs, but your web browser will accumulate the data over time.</p>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<h3>Configuration</h3>
|
||||||
|
<table class="metrics">
|
||||||
|
<tr>
|
||||||
|
<th>SMTP Listener:</th>
|
||||||
|
<td><span>{{.smtpListener}}</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>POP3 Listener:</th>
|
||||||
|
<td><span>{{.pop3Listener}}</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>HTTP Listener:</th>
|
||||||
|
<td><span>{{.webListener}}</span></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<p class="last"> </p>
|
||||||
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h3>General Metrics</h3>
|
<h3>General Metrics</h3>
|
||||||
<table class="metrics">
|
<table class="metrics">
|
||||||
|
|||||||
@@ -5,18 +5,37 @@ import (
|
|||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
"github.com/jhillyerd/inbucket/smtpd"
|
"github.com/jhillyerd/inbucket/smtpd"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
Vars map[string]string
|
Vars map[string]string
|
||||||
Session *sessions.Session
|
Session *sessions.Session
|
||||||
DataStore smtpd.DataStore
|
DataStore smtpd.DataStore
|
||||||
|
IsJson bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) Close() {
|
func (c *Context) Close() {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// headerMatch returns true if the request header specified by name contains
|
||||||
|
// the specified value. Case is ignored.
|
||||||
|
func headerMatch(req *http.Request, name string, value string) bool {
|
||||||
|
name = http.CanonicalHeaderKey(name)
|
||||||
|
value = strings.ToLower(value)
|
||||||
|
|
||||||
|
if header := req.Header[name]; header != nil {
|
||||||
|
for _, hv := range header {
|
||||||
|
if value == strings.ToLower(hv) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func NewContext(req *http.Request) (*Context, error) {
|
func NewContext(req *http.Request) (*Context, error) {
|
||||||
vars := mux.Vars(req)
|
vars := mux.Vars(req)
|
||||||
sess, err := sessionStore.Get(req, "inbucket")
|
sess, err := sessionStore.Get(req, "inbucket")
|
||||||
@@ -25,6 +44,7 @@ func NewContext(req *http.Request) (*Context, error) {
|
|||||||
Vars: vars,
|
Vars: vars,
|
||||||
Session: sess,
|
Session: sess,
|
||||||
DataStore: ds,
|
DataStore: ds,
|
||||||
|
IsJson: headerMatch(req, "Accept", "application/json"),
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ctx, err
|
return ctx, err
|
||||||
|
|||||||
@@ -7,8 +7,14 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type JsonMessageHeader struct {
|
||||||
|
Mailbox, Id, From, Subject string
|
||||||
|
Date time.Time
|
||||||
|
}
|
||||||
|
|
||||||
func MailboxIndex(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
func MailboxIndex(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
||||||
name := req.FormValue("name")
|
name := req.FormValue("name")
|
||||||
if len(name) == 0 {
|
if len(name) == 0 {
|
||||||
@@ -37,6 +43,20 @@ func MailboxList(w http.ResponseWriter, req *http.Request, ctx *Context) (err er
|
|||||||
}
|
}
|
||||||
log.LogTrace("Got %v messsages", len(messages))
|
log.LogTrace("Got %v messsages", len(messages))
|
||||||
|
|
||||||
|
if ctx.IsJson {
|
||||||
|
jmessages := make([]*JsonMessageHeader, len(messages))
|
||||||
|
for i, msg := range messages {
|
||||||
|
jmessages[i] = &JsonMessageHeader{
|
||||||
|
Mailbox: name,
|
||||||
|
Id: msg.Id(),
|
||||||
|
From: msg.From(),
|
||||||
|
Subject: msg.Subject(),
|
||||||
|
Date: msg.Date(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RenderJson(w, jmessages)
|
||||||
|
}
|
||||||
|
|
||||||
return RenderPartial("mailbox/_list.html", w, map[string]interface{}{
|
return RenderPartial("mailbox/_list.html", w, map[string]interface{}{
|
||||||
"ctx": ctx,
|
"ctx": ctx,
|
||||||
"name": name,
|
"name": name,
|
||||||
@@ -214,6 +234,11 @@ func MailboxDelete(w http.ResponseWriter, req *http.Request, ctx *Context) (err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.IsJson {
|
||||||
|
return RenderJson(w, "OK")
|
||||||
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
io.WriteString(w, "OK")
|
io.WriteString(w, "OK")
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
13
web/rest.go
Normal file
13
web/rest.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RenderJson(w http.ResponseWriter, data interface{}) error {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
w.Header().Set("Expires", "-1")
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
return enc.Encode(data)
|
||||||
|
}
|
||||||
@@ -1,20 +1,38 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/jhillyerd/inbucket/config"
|
"github.com/jhillyerd/inbucket/config"
|
||||||
|
"html/template"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RootIndex(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
func RootIndex(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
||||||
|
greeting, err := ioutil.ReadFile(config.GetWebConfig().GreetingFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to load greeting: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
return RenderTemplate("root/index.html", w, map[string]interface{}{
|
return RenderTemplate("root/index.html", w, map[string]interface{}{
|
||||||
"ctx": ctx,
|
"ctx": ctx,
|
||||||
|
"greeting": template.HTML(string(greeting)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func RootStatus(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
func RootStatus(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
||||||
retentionMinutes := config.GetDataStoreConfig().RetentionMinutes
|
retentionMinutes := config.GetDataStoreConfig().RetentionMinutes
|
||||||
|
smtpListener := fmt.Sprintf("%s:%d", config.GetSmtpConfig().Ip4address.String(),
|
||||||
|
config.GetSmtpConfig().Ip4port)
|
||||||
|
pop3Listener := fmt.Sprintf("%s:%d", config.GetPop3Config().Ip4address.String(),
|
||||||
|
config.GetPop3Config().Ip4port)
|
||||||
|
webListener := fmt.Sprintf("%s:%d", config.GetWebConfig().Ip4address.String(),
|
||||||
|
config.GetWebConfig().Ip4port)
|
||||||
return RenderTemplate("root/status.html", w, map[string]interface{}{
|
return RenderTemplate("root/status.html", w, map[string]interface{}{
|
||||||
"ctx": ctx,
|
"ctx": ctx,
|
||||||
"retentionMinutes": retentionMinutes,
|
"retentionMinutes": retentionMinutes,
|
||||||
|
"smtpListener": smtpListener,
|
||||||
|
"pop3Listener": pop3Listener,
|
||||||
|
"webListener": webListener,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ func setupRoutes(cfg config.WebConfig) {
|
|||||||
r.Path("/").Handler(handler(RootIndex)).Name("RootIndex").Methods("GET")
|
r.Path("/").Handler(handler(RootIndex)).Name("RootIndex").Methods("GET")
|
||||||
r.Path("/status").Handler(handler(RootStatus)).Name("RootStatus").Methods("GET")
|
r.Path("/status").Handler(handler(RootStatus)).Name("RootStatus").Methods("GET")
|
||||||
r.Path("/mailbox").Handler(handler(MailboxIndex)).Name("MailboxIndex").Methods("GET")
|
r.Path("/mailbox").Handler(handler(MailboxIndex)).Name("MailboxIndex").Methods("GET")
|
||||||
r.Path("/mailbox/list/{name}").Handler(handler(MailboxList)).Name("MailboxList").Methods("GET")
|
r.Path("/mailbox/{name}").Handler(handler(MailboxList)).Name("MailboxList").Methods("GET")
|
||||||
r.Path("/mailbox/show/{name}/{id}").Handler(handler(MailboxShow)).Name("MailboxShow").Methods("GET")
|
r.Path("/mailbox/{name}/{id}").Handler(handler(MailboxShow)).Name("MailboxShow").Methods("GET")
|
||||||
r.Path("/mailbox/html/{name}/{id}").Handler(handler(MailboxHtml)).Name("MailboxHtml").Methods("GET")
|
r.Path("/mailbox/{name}/{id}/html").Handler(handler(MailboxHtml)).Name("MailboxHtml").Methods("GET")
|
||||||
r.Path("/mailbox/source/{name}/{id}").Handler(handler(MailboxSource)).Name("MailboxSource").Methods("GET")
|
r.Path("/mailbox/{name}/{id}/source").Handler(handler(MailboxSource)).Name("MailboxSource").Methods("GET")
|
||||||
r.Path("/mailbox/delete/{name}/{id}").Handler(handler(MailboxDelete)).Name("MailboxDelete").Methods("POST")
|
r.Path("/mailbox/{name}/{id}").Handler(handler(MailboxDelete)).Name("MailboxDelete").Methods("DELETE")
|
||||||
r.Path("/mailbox/dattach/{name}/{id}/{num}/{file}").Handler(handler(MailboxDownloadAttach)).Name("MailboxDownloadAttach").Methods("GET")
|
r.Path("/mailbox/dattach/{name}/{id}/{num}/{file}").Handler(handler(MailboxDownloadAttach)).Name("MailboxDownloadAttach").Methods("GET")
|
||||||
r.Path("/mailbox/vattach/{name}/{id}/{num}/{file}").Handler(handler(MailboxViewAttach)).Name("MailboxViewAttach").Methods("GET")
|
r.Path("/mailbox/vattach/{name}/{id}/{num}/{file}").Handler(handler(MailboxViewAttach)).Name("MailboxViewAttach").Methods("GET")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user