mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 17:47:03 +00:00
Merge in REST changes
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
"Resources": {
|
||||
"Include": "README*,LICENSE*,bin,etc,themes"
|
||||
},
|
||||
"PackageVersion": "20131001",
|
||||
"PackageVersion": "20131010",
|
||||
"PrereleaseInfo": "snapshot",
|
||||
"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 {
|
||||
Ip4address net.IP
|
||||
Ip4port int
|
||||
Domain string
|
||||
Domain string
|
||||
MaxIdleSeconds int
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ type WebConfig struct {
|
||||
TemplateDir string
|
||||
TemplateCache bool
|
||||
PublicDir string
|
||||
GreetingFile string
|
||||
}
|
||||
|
||||
type DataStoreConfig struct {
|
||||
@@ -328,6 +329,13 @@ func parseWebConfig() error {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,10 @@ template.cache=false
|
||||
# Path to the selected themes public (static) files
|
||||
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]
|
||||
|
||||
|
||||
@@ -81,6 +81,10 @@ template.cache=true
|
||||
# Path to the selected themes public (static) files
|
||||
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]
|
||||
|
||||
|
||||
@@ -81,6 +81,10 @@ template.cache=true
|
||||
# Path to the selected themes public (static) files
|
||||
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]
|
||||
|
||||
|
||||
@@ -81,6 +81,10 @@ template.cache=true
|
||||
# Path to the selected themes public (static) files
|
||||
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]
|
||||
|
||||
|
||||
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 id="footer">
|
||||
<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>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
function() {
|
||||
$('.listEntry').removeClass("listEntrySelected")
|
||||
$(this).addClass("listEntrySelected")
|
||||
$('#emailContent').load('/mailbox/show/{{.name}}/' + this.id)
|
||||
$('#emailContent').load('/mailbox/{{.name}}/' + this.id)
|
||||
}
|
||||
)
|
||||
$("#messageList").slideDown()
|
||||
}
|
||||
|
||||
function loadList() {
|
||||
$('#messageList').load("/mailbox/list/{{.name}}", listLoaded)
|
||||
$('#messageList').load("/mailbox/{{.name}}", listLoaded)
|
||||
}
|
||||
|
||||
function reloadList() {
|
||||
@@ -38,20 +38,20 @@
|
||||
function deleteMessage(id) {
|
||||
$('#emailContent').empty()
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/mailbox/delete/{{.name}}/' + id,
|
||||
type: 'DELETE',
|
||||
url: '/mailbox/{{.name}}/' + id,
|
||||
success: reloadList
|
||||
})
|
||||
}
|
||||
|
||||
function htmlView(id) {
|
||||
window.open('/mailbox/html/{{.name}}/' + id, '_blank',
|
||||
window.open('/mailbox/{{.name}}/' + id + "/html", '_blank',
|
||||
'width=800,height=600,' +
|
||||
'menubar=yes,resizable=yes,scrollbars=yes,status=yes,toolbar=yes')
|
||||
}
|
||||
|
||||
function messageSource(id) {
|
||||
window.open('/mailbox/source/{{.name}}/' + id, '_blank',
|
||||
window.open('/mailbox/{{.name}}/' + id + "/source", '_blank',
|
||||
'width=800,height=600,' +
|
||||
'menubar=no,resizable=yes,scrollbars=yes,status=no,toolbar=no')
|
||||
}
|
||||
|
||||
@@ -9,11 +9,4 @@
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<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}}
|
||||
|
||||
{{define "content"}}{{.greeting}}{{end}}
|
||||
|
||||
@@ -139,6 +139,24 @@
|
||||
<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>
|
||||
|
||||
<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">
|
||||
<h3>General Metrics</h3>
|
||||
<table class="metrics">
|
||||
|
||||
@@ -5,18 +5,37 @@ import (
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/jhillyerd/inbucket/smtpd"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
Vars map[string]string
|
||||
Session *sessions.Session
|
||||
DataStore smtpd.DataStore
|
||||
IsJson bool
|
||||
}
|
||||
|
||||
func (c *Context) Close() {
|
||||
// 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) {
|
||||
vars := mux.Vars(req)
|
||||
sess, err := sessionStore.Get(req, "inbucket")
|
||||
@@ -25,6 +44,7 @@ func NewContext(req *http.Request) (*Context, error) {
|
||||
Vars: vars,
|
||||
Session: sess,
|
||||
DataStore: ds,
|
||||
IsJson: headerMatch(req, "Accept", "application/json"),
|
||||
}
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
|
||||
@@ -7,8 +7,14 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"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) {
|
||||
name := req.FormValue("name")
|
||||
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))
|
||||
|
||||
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{}{
|
||||
"ctx": ctx,
|
||||
"name": name,
|
||||
@@ -214,6 +234,11 @@ func MailboxDelete(w http.ResponseWriter, req *http.Request, ctx *Context) (err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ctx.IsJson {
|
||||
return RenderJson(w, "OK")
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
io.WriteString(w, "OK")
|
||||
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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jhillyerd/inbucket/config"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
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{}{
|
||||
"ctx": ctx,
|
||||
"greeting": template.HTML(string(greeting)),
|
||||
})
|
||||
}
|
||||
|
||||
func RootStatus(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
||||
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{}{
|
||||
"ctx": ctx,
|
||||
"ctx": ctx,
|
||||
"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("/status").Handler(handler(RootStatus)).Name("RootStatus").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/show/{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/source/{name}/{id}").Handler(handler(MailboxSource)).Name("MailboxSource").Methods("GET")
|
||||
r.Path("/mailbox/delete/{name}/{id}").Handler(handler(MailboxDelete)).Name("MailboxDelete").Methods("POST")
|
||||
r.Path("/mailbox/{name}").Handler(handler(MailboxList)).Name("MailboxList").Methods("GET")
|
||||
r.Path("/mailbox/{name}/{id}").Handler(handler(MailboxShow)).Name("MailboxShow").Methods("GET")
|
||||
r.Path("/mailbox/{name}/{id}/html").Handler(handler(MailboxHtml)).Name("MailboxHtml").Methods("GET")
|
||||
r.Path("/mailbox/{name}/{id}/source").Handler(handler(MailboxSource)).Name("MailboxSource").Methods("GET")
|
||||
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/vattach/{name}/{id}/{num}/{file}").Handler(handler(MailboxViewAttach)).Name("MailboxViewAttach").Methods("GET")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user