mirror of
https://github.com/GRFreire/nthmail.git
synced 2026-01-10 13:29:37 +00:00
add separate page for viewing the email
This commit is contained in:
parent
3d33831803
commit
ddcf9dea62
@ -1,7 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/russross/blackfriday/v2"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
templ inbox_body(rcpt_addr string, ms []mail_obj) {
|
templ inbox_body(rcpt_addr string, ms []mail_obj) {
|
||||||
@ -18,7 +18,7 @@ templ inbox_body(rcpt_addr string, ms []mail_obj) {
|
|||||||
<ul>
|
<ul>
|
||||||
for _, m := range ms {
|
for _, m := range ms {
|
||||||
<li>
|
<li>
|
||||||
@mail_comp(m)
|
@mail_comp(m, rcpt_addr)
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
@ -26,59 +26,12 @@ templ inbox_body(rcpt_addr string, ms []mail_obj) {
|
|||||||
</html>
|
</html>
|
||||||
}
|
}
|
||||||
|
|
||||||
templ mail_comp(m mail_obj) {
|
templ mail_comp(m mail_obj, rcpt_addr string) {
|
||||||
<h3>From: { m.From } at { m.Date }</h3>
|
<a href={ templ.SafeURL(fmt.Sprintf("/%s/%d", rcpt_addr, m.Id)) }>
|
||||||
if m.To != "" {
|
<div>
|
||||||
<h3>To: { m.To }</h3>
|
<p><b>{ m.Subject }</b></p>
|
||||||
}
|
<p>From { m.From }</p>
|
||||||
if m.Bcc != "" {
|
</div>
|
||||||
<h3>Bcc: { m.Bcc }</h3>
|
<p>{ m.Date }</p>
|
||||||
}
|
</a>
|
||||||
<h3>Subject: { m.Subject }</h3>
|
|
||||||
if m.MediaType == NotMultipart {
|
|
||||||
for _, b := range m.Body {
|
|
||||||
@body_plain(b.Data)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if m.MediaType == Mixed {
|
|
||||||
for _, b := range m.Body {
|
|
||||||
switch b.MimeType {
|
|
||||||
case PlainText:
|
|
||||||
@body_plain(b.Data)
|
|
||||||
case Html:
|
|
||||||
@body_html(b.Data)
|
|
||||||
case Markdown:
|
|
||||||
@body_plain(b.Data)
|
|
||||||
default:
|
|
||||||
@body_plain(b.Data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch m.Body[m.PreferedBodyIndex].MimeType {
|
|
||||||
case PlainText:
|
|
||||||
@body_plain(m.Body[m.PreferedBodyIndex].Data)
|
|
||||||
case Html:
|
|
||||||
@body_html(m.Body[m.PreferedBodyIndex].Data)
|
|
||||||
case Markdown:
|
|
||||||
@body_markdown(m.Body[m.PreferedBodyIndex].Data)
|
|
||||||
default:
|
|
||||||
@body_plain(m.Body[m.PreferedBodyIndex].Data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
templ body_plain(s string) {
|
|
||||||
<p>{ s }</p>
|
|
||||||
}
|
|
||||||
|
|
||||||
templ body_html(s string) {
|
|
||||||
<p>
|
|
||||||
@templ.Raw(s)
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
|
|
||||||
templ body_markdown(s string) {
|
|
||||||
@body_html(string(blackfriday.Run([]byte(s))))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
54
cmd/web_server/mail.templ
Normal file
54
cmd/web_server/mail.templ
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/russross/blackfriday/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
templ mail_body_comp(rcpt_addr string, m mail_obj) {
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"/>
|
||||||
|
<title>nthmail.xyz</title>
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
|
<meta name="description" content="A temporary mail service"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h3>{m.Subject}</h3>
|
||||||
|
<h3>{m.From}</h3>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
@mime_type(m.Body[m.PreferedBodyIndex])
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
templ mime_type(b mail_body) {
|
||||||
|
switch b.MimeType {
|
||||||
|
case Html:
|
||||||
|
@body_html(b.Data)
|
||||||
|
case Markdown:
|
||||||
|
@body_markdown(b.Data)
|
||||||
|
case PlainText:
|
||||||
|
@body_plain(b.Data)
|
||||||
|
default:
|
||||||
|
@body_plain(b.Data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
templ body_plain(s string) {
|
||||||
|
<p>{ s }</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
templ body_html(s string) {
|
||||||
|
<p>
|
||||||
|
@templ.Raw(s)
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
templ body_markdown(s string) {
|
||||||
|
@body_html(string(blackfriday.Run([]byte(s))))
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -20,7 +20,6 @@ import (
|
|||||||
"github.com/GRFreire/nthmail/pkg/rig"
|
"github.com/GRFreire/nthmail/pkg/rig"
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
"github.com/microcosm-cc/bluemonday"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type db_mail struct {
|
type db_mail struct {
|
||||||
@ -51,6 +50,7 @@ type mail_body struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mail_obj struct {
|
type mail_obj struct {
|
||||||
|
Id int
|
||||||
From string
|
From string
|
||||||
Date string
|
Date string
|
||||||
To string
|
To string
|
||||||
@ -83,8 +83,9 @@ func parse_mime_format(s string) (MIMEType, bool) {
|
|||||||
return t, true
|
return t, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse_mail(dbm db_mail, policy *bluemonday.Policy) (mail_obj, error) {
|
func parse_mail(dbm db_mail, header_only bool) (mail_obj, error) {
|
||||||
var m mail_obj
|
var m mail_obj
|
||||||
|
m.Id = dbm.Id
|
||||||
|
|
||||||
mail_msg, err := mail.ReadMessage(bytes.NewReader(dbm.Data))
|
mail_msg, err := mail.ReadMessage(bytes.NewReader(dbm.Data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -99,6 +100,10 @@ func parse_mail(dbm db_mail, policy *bluemonday.Policy) (mail_obj, error) {
|
|||||||
m.Bcc, _ = dec.DecodeHeader(mail_msg.Header.Get("Bcc"))
|
m.Bcc, _ = dec.DecodeHeader(mail_msg.Header.Get("Bcc"))
|
||||||
m.Subject, _ = dec.DecodeHeader(mail_msg.Header.Get("Subject"))
|
m.Subject, _ = dec.DecodeHeader(mail_msg.Header.Get("Subject"))
|
||||||
|
|
||||||
|
if header_only {
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
content_type := mail_msg.Header.Get("Content-Type")
|
content_type := mail_msg.Header.Get("Content-Type")
|
||||||
mediaType, params, err := mime.ParseMediaType(content_type)
|
mediaType, params, err := mime.ParseMediaType(content_type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -236,7 +241,6 @@ func set_format_index(m mail_obj, format MIMEType, pref bool) mail_obj {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if b.MimeType == Html {
|
if b.MimeType == Html {
|
||||||
m.PreferedBodyIndex = i
|
m.PreferedBodyIndex = i
|
||||||
continue
|
continue
|
||||||
@ -249,7 +253,6 @@ func set_format_index(m mail_obj, format MIMEType, pref bool) mail_obj {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,11 +282,6 @@ func main() {
|
|||||||
http.Redirect(res, req, inbox_addr, 307)
|
http.Redirect(res, req, inbox_addr, 307)
|
||||||
})
|
})
|
||||||
|
|
||||||
p := bluemonday.UGCPolicy()
|
|
||||||
p.AllowStyles()
|
|
||||||
p.AllowStyling()
|
|
||||||
p.AllowElements("style")
|
|
||||||
p.AllowUnsafe(true)
|
|
||||||
router.Get("/{rcpt-addr}", func(res http.ResponseWriter, req *http.Request) {
|
router.Get("/{rcpt-addr}", func(res http.ResponseWriter, req *http.Request) {
|
||||||
rcpt_addr := chi.URLParam(req, "rcpt-addr")
|
rcpt_addr := chi.URLParam(req, "rcpt-addr")
|
||||||
if len(rcpt_addr) == 0 {
|
if len(rcpt_addr) == 0 {
|
||||||
@ -321,7 +319,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
format, f_pref := parse_mime_format(req.URL.Query().Get("format"))
|
|
||||||
var mails []mail_obj
|
var mails []mail_obj
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var m db_mail
|
var m db_mail
|
||||||
@ -334,7 +331,78 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mail_obj, err := parse_mail(m, p)
|
mail_obj, err := parse_mail(m, true)
|
||||||
|
if err != nil {
|
||||||
|
res.WriteHeader(500)
|
||||||
|
res.Write([]byte("internal server error"))
|
||||||
|
|
||||||
|
log.Println("could not parse mail")
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mails = append(mails, mail_obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := inbox_body(rcpt_addr, mails)
|
||||||
|
body.Render(req.Context(), res)
|
||||||
|
})
|
||||||
|
|
||||||
|
router.Get("/{rcpt-addr}/{mail-id}", func(res http.ResponseWriter, req *http.Request) {
|
||||||
|
rcpt_addr := chi.URLParam(req, "rcpt-addr")
|
||||||
|
if len(rcpt_addr) == 0 {
|
||||||
|
res.WriteHeader(404)
|
||||||
|
res.Write([]byte("inbox not found"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mail_id := chi.URLParam(req, "mail-id")
|
||||||
|
if len(rcpt_addr) == 0 {
|
||||||
|
res.WriteHeader(404)
|
||||||
|
res.Write([]byte("mail not found"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
res.WriteHeader(500)
|
||||||
|
res.Write([]byte("internal server error"))
|
||||||
|
|
||||||
|
log.Println("could not begin db transaction")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare("SELECT mails.id, mails.arrived_at, mails.rcpt_addr, mails.from_addr, mails.data FROM mails WHERE mails.rcpt_addr = ? AND mails.id = ?")
|
||||||
|
if err != nil {
|
||||||
|
res.WriteHeader(500)
|
||||||
|
res.Write([]byte("internal server error"))
|
||||||
|
|
||||||
|
log.Println("could not prepare db stmt")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
row := stmt.QueryRow(rcpt_addr, mail_id)
|
||||||
|
if err != nil {
|
||||||
|
res.WriteHeader(500)
|
||||||
|
res.Write([]byte("internal server error"))
|
||||||
|
|
||||||
|
log.Println("could not query db stmt")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
format, f_pref := parse_mime_format(req.URL.Query().Get("format"))
|
||||||
|
var m db_mail
|
||||||
|
err = row.Scan(&m.Id, &m.Arrived_at, &m.Rcpt_addr, &m.From_addr, &m.Data)
|
||||||
|
if err != nil {
|
||||||
|
res.WriteHeader(500)
|
||||||
|
res.Write([]byte("internal server error"))
|
||||||
|
|
||||||
|
log.Println("could not scan db row")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mail_obj, err := parse_mail(m, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.WriteHeader(500)
|
res.WriteHeader(500)
|
||||||
res.Write([]byte("internal server error"))
|
res.Write([]byte("internal server error"))
|
||||||
@ -346,10 +414,7 @@ func main() {
|
|||||||
|
|
||||||
mail_obj = set_format_index(mail_obj, format, f_pref)
|
mail_obj = set_format_index(mail_obj, format, f_pref)
|
||||||
|
|
||||||
mails = append(mails, mail_obj)
|
body := mail_body_comp(rcpt_addr, mail_obj)
|
||||||
}
|
|
||||||
|
|
||||||
body := inbox_body(rcpt_addr, mails)
|
|
||||||
body.Render(req.Context(), res)
|
body.Render(req.Context(), res)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user