Initial commit
This commit is contained in:
200
internal/route/email.go
Normal file
200
internal/route/email.go
Normal file
@ -0,0 +1,200 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
|
||||
"forge.cadoles.com/wpetit/fake-smtp/internal/command"
|
||||
"forge.cadoles.com/wpetit/fake-smtp/internal/model"
|
||||
"forge.cadoles.com/wpetit/fake-smtp/internal/query"
|
||||
"forge.cadoles.com/wpetit/fake-smtp/internal/storm"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/cqrs"
|
||||
"gitlab.com/wpetit/goweb/middleware/container"
|
||||
"gitlab.com/wpetit/goweb/service/template"
|
||||
)
|
||||
|
||||
func serveEmailPage(w http.ResponseWriter, r *http.Request) {
|
||||
emailID, err := getEmailID(r)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
email, err := openEmail(ctx, emailID)
|
||||
if err != nil {
|
||||
if errors.Is(err, storm.ErrNotFound) {
|
||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
panic(errors.Wrap(err, "could not open email"))
|
||||
}
|
||||
|
||||
ctn := container.Must(ctx)
|
||||
tmpl := template.Must(ctn)
|
||||
|
||||
data := extendTemplateData(w, r, template.Data{
|
||||
"Email": email,
|
||||
})
|
||||
|
||||
if err := tmpl.RenderPage(w, "email.html.tmpl", data); err != nil {
|
||||
panic(errors.Wrapf(err, "could not render '%s' page", r.URL.Path))
|
||||
}
|
||||
}
|
||||
|
||||
func serveEmailHTMLContent(w http.ResponseWriter, r *http.Request) {
|
||||
emailID, err := getEmailID(r)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
email, err := openEmail(ctx, emailID)
|
||||
if err != nil {
|
||||
if errors.Is(err, storm.ErrNotFound) {
|
||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
panic(errors.Wrap(err, "could not open email"))
|
||||
}
|
||||
|
||||
html := email.HTML
|
||||
|
||||
if html == "" {
|
||||
http.Error(w, http.StatusText(http.StatusNoContent), http.StatusNoContent)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
policy := bluemonday.UGCPolicy()
|
||||
sanitizedHTML := policy.Sanitize(email.HTML)
|
||||
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
w.Write([]byte(sanitizedHTML))
|
||||
}
|
||||
|
||||
func serveEmailAttachment(w http.ResponseWriter, r *http.Request) {
|
||||
emailID, err := getEmailID(r)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
email, err := openEmail(ctx, emailID)
|
||||
if err != nil {
|
||||
if errors.Is(err, storm.ErrNotFound) {
|
||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
panic(errors.Wrap(err, "could not open email"))
|
||||
}
|
||||
|
||||
attachmentIndex, err := getAttachmentIndex(r)
|
||||
if err != nil {
|
||||
if errors.Is(err, storm.ErrNotFound) {
|
||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
panic(errors.Wrap(err, "could not open attachment"))
|
||||
}
|
||||
|
||||
if attachmentIndex < 0 || attachmentIndex >= len(email.Attachments) {
|
||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
attachment := email.Attachments[attachmentIndex]
|
||||
|
||||
w.Header().Set("Content-Type", attachment.ContentType)
|
||||
|
||||
if _, err := w.Write(attachment.Data); err != nil {
|
||||
panic(errors.Wrap(err, "could not send attachment"))
|
||||
}
|
||||
}
|
||||
|
||||
func handleEmailDelete(w http.ResponseWriter, r *http.Request) {
|
||||
emailID, err := getEmailID(r)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
ctn := container.Must(ctx)
|
||||
bus := cqrs.Must(ctn)
|
||||
|
||||
deleteEmail := &command.DeleteEmailRequest{
|
||||
EmailID: emailID,
|
||||
}
|
||||
|
||||
if _, err := bus.Exec(ctx, deleteEmail); err != nil {
|
||||
panic(errors.Wrap(err, "could not delete email"))
|
||||
}
|
||||
|
||||
http.Error(w, http.StatusText(http.StatusNoContent), http.StatusNoContent)
|
||||
}
|
||||
|
||||
func getEmailID(r *http.Request) (int, error) {
|
||||
rawEmailID := chi.URLParam(r, "id")
|
||||
|
||||
emailID, err := strconv.ParseInt(rawEmailID, 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int(emailID), nil
|
||||
}
|
||||
|
||||
func openEmail(ctx context.Context, emailID int) (*model.Email, error) {
|
||||
ctn := container.Must(ctx)
|
||||
bus := cqrs.Must(ctn)
|
||||
req := &query.OpenEmailRequest{
|
||||
EmailID: int(emailID),
|
||||
}
|
||||
|
||||
result, err := bus.Query(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
openEmailData, ok := result.Data().(*query.OpenEmailData)
|
||||
if !ok {
|
||||
return nil, errors.New("unexpected result data")
|
||||
}
|
||||
|
||||
return openEmailData.Email, nil
|
||||
}
|
||||
|
||||
func getAttachmentIndex(r *http.Request) (int, error) {
|
||||
rawAttachmendIndex := chi.URLParam(r, "attachmendIndex")
|
||||
|
||||
attachmendIndex, err := strconv.ParseInt(rawAttachmendIndex, 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int(attachmendIndex), nil
|
||||
}
|
22
internal/route/helper.go
Normal file
22
internal/route/helper.go
Normal file
@ -0,0 +1,22 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/middleware/container"
|
||||
"gitlab.com/wpetit/goweb/service/template"
|
||||
)
|
||||
|
||||
func extendTemplateData(w http.ResponseWriter, r *http.Request, data template.Data) template.Data {
|
||||
ctn := container.Must(r.Context())
|
||||
data, err := template.Extend(data,
|
||||
template.WithBuildInfo(w, r, ctn),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "could not extend template data"))
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
55
internal/route/inbox.go
Normal file
55
internal/route/inbox.go
Normal file
@ -0,0 +1,55 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"forge.cadoles.com/wpetit/fake-smtp/internal/command"
|
||||
"forge.cadoles.com/wpetit/fake-smtp/internal/query"
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/cqrs"
|
||||
"gitlab.com/wpetit/goweb/middleware/container"
|
||||
"gitlab.com/wpetit/goweb/service/template"
|
||||
)
|
||||
|
||||
func serveInboxPage(w http.ResponseWriter, r *http.Request) {
|
||||
ctn := container.Must(r.Context())
|
||||
tmpl := template.Must(ctn)
|
||||
|
||||
bus := cqrs.Must(ctn)
|
||||
|
||||
getInbox := &query.GetInboxRequest{}
|
||||
ctx := r.Context()
|
||||
|
||||
result, err := bus.Query(ctx, getInbox)
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "could not retrieve inbox"))
|
||||
}
|
||||
|
||||
inboxData, ok := result.Data().(*query.InboxData)
|
||||
if !ok {
|
||||
panic(errors.New("unexpected data"))
|
||||
}
|
||||
|
||||
data := extendTemplateData(w, r, template.Data{
|
||||
"Emails": inboxData.Emails,
|
||||
})
|
||||
|
||||
if err := tmpl.RenderPage(w, "inbox.html.tmpl", data); err != nil {
|
||||
panic(errors.Wrapf(err, "could not render '%s' page", r.URL.Path))
|
||||
}
|
||||
}
|
||||
|
||||
func handleClearInbox(w http.ResponseWriter, r *http.Request) {
|
||||
ctn := container.Must(r.Context())
|
||||
|
||||
bus := cqrs.Must(ctn)
|
||||
|
||||
clearInbox := &command.ClearInboxRequest{}
|
||||
ctx := r.Context()
|
||||
|
||||
if _, err := bus.Exec(ctx, clearInbox); err != nil {
|
||||
panic(errors.Wrap(err, "could not clear inbox"))
|
||||
}
|
||||
|
||||
http.Error(w, http.StatusText(http.StatusNoContent), http.StatusNoContent)
|
||||
}
|
24
internal/route/mount.go
Normal file
24
internal/route/mount.go
Normal file
@ -0,0 +1,24 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"forge.cadoles.com/wpetit/fake-smtp/internal/config"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"gitlab.com/wpetit/goweb/static"
|
||||
)
|
||||
|
||||
func Mount(r *chi.Mux, config *config.Config) error {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Get("/", serveInboxPage)
|
||||
r.Delete("/emails", handleClearInbox)
|
||||
r.Get("/emails/{id}", serveEmailPage)
|
||||
r.Get("/emails/{id}/html", serveEmailHTMLContent)
|
||||
r.Get("/emails/{id}/attachments/{attachmendIndex}", serveEmailAttachment)
|
||||
r.Delete("/emails/{id}", handleEmailDelete)
|
||||
})
|
||||
|
||||
notFoundHandler := r.NotFoundHandler()
|
||||
r.Get("/*", static.Dir(config.HTTP.PublicDir, "", notFoundHandler))
|
||||
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user