Compare commits
3 Commits
e9676f8136
...
31476615a8
Author | SHA1 | Date | |
---|---|---|---|
|
31476615a8 | ||
|
8877efaeca | ||
|
67700bcb42 |
18
blog.go
Normal file
18
blog.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import "html/template"
|
||||
|
||||
type Post struct {
|
||||
Id string
|
||||
Title string
|
||||
Author string
|
||||
Date string
|
||||
HTML template.HTML
|
||||
Text string
|
||||
}
|
||||
|
||||
type Blog struct {
|
||||
Title string
|
||||
Lang string
|
||||
Posts []Post
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
package main
|
||||
|
||||
type BlogEntry struct {
|
||||
Id string
|
||||
Title string
|
||||
Author string
|
||||
HTML string
|
||||
Text string
|
||||
}
|
@@ -1,5 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type MailBoxConfiguration struct {
|
||||
Server string
|
||||
Port string
|
||||
@@ -12,5 +18,60 @@ type BlogConfiguration struct {
|
||||
Title string
|
||||
ShortName string
|
||||
MailBox MailBoxConfiguration
|
||||
WWWRoot string
|
||||
}
|
||||
|
||||
func (configuration *BlogConfiguration) Prompt() error {
|
||||
var err error
|
||||
configuration.Title, err = prompt("Blog Title", nil, "", configuration.Title, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configuration.ShortName, err = prompt("Blog Short Name (only letters and numbers, no special signes)", nil, "blog", configuration.ShortName, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString(configuration.ShortName) {
|
||||
return fmt.Errorf("short same invalid")
|
||||
}
|
||||
|
||||
/*
|
||||
configuration.WWWRoot, err = prompt("Web Root directory", nil, fmt.Sprintf("/var/www/html/%s", configuration.ShortName), configuration.WWWRoot, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*/
|
||||
configuration.MailBox.Server, err = prompt("IMAP Server Hostname or IP", nil, "", configuration.MailBox.Server, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configuration.MailBox.Port, err = prompt("IMAP Server Port", nil, "993", configuration.MailBox.Port, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = strconv.ParseUint(configuration.MailBox.Port, 10, 16)
|
||||
if err != nil {
|
||||
return fmt.Errorf("imap server port invalid")
|
||||
}
|
||||
configuration.MailBox.User, err = prompt("IMAP User Login", nil, "", configuration.MailBox.User, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configuration.MailBox.Password, err = prompt("IMAP User Password", nil, "", configuration.MailBox.Password, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
options := [...]string{"SSL/TLS", "StartTLS", "NoTLS"}
|
||||
configuration.MailBox.SSL, err = prompt("IMAP SSL/TLS", options[:], "SSL/TLS", configuration.MailBox.SSL, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configuration.MailBox.InBox, err = prompt("IMAP INBOX", nil, "INBOX", configuration.MailBox.InBox, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
1
go.mod
1
go.mod
@@ -5,6 +5,7 @@ go 1.23.2
|
||||
require (
|
||||
github.com/emersion/go-imap/v2 v2.0.0-beta.5
|
||||
github.com/emersion/go-message v0.18.1
|
||||
golang.org/x/net v0.6.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
1
go.sum
1
go.sum
@@ -12,6 +12,7 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
16
html/entry.tmpl
Normal file
16
html/entry.tmpl
Normal file
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="{{.Lang}}">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>{{.Title}}</title>
|
||||
</head>
|
||||
<h1>{{.Title}}</h1>
|
||||
<body>
|
||||
<h2>{{(index .Posts 0).Title}}</h2>
|
||||
<div>
|
||||
{{(index .Posts 0).HTML}}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
27
html/index.tmpl
Normal file
27
html/index.tmpl
Normal file
@@ -0,0 +1,27 @@
|
||||
<!doctype html>
|
||||
<html lang="{{.Lang}}">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>{{.Title}}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{.Title}}</h1>
|
||||
{{range .Posts}}
|
||||
<article>
|
||||
<a href=/proxy/8080/post/{{ .Id }}>
|
||||
<h2>{{.Title}}</h2>
|
||||
</a>
|
||||
<div>
|
||||
{{.HTML}}
|
||||
</div>
|
||||
<footer>
|
||||
<p>
|
||||
{{.Date}}
|
||||
</p>
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
{{ end }}
|
||||
</body>
|
||||
</html>
|
@@ -3,16 +3,22 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
"mime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
/*
|
||||
_ "golang.org/x/net/html"
|
||||
_ "golang.org/x/net/html/atom"
|
||||
*/
|
||||
"github.com/emersion/go-imap/v2"
|
||||
"github.com/emersion/go-imap/v2/imapclient"
|
||||
"github.com/emersion/go-message/charset"
|
||||
"github.com/emersion/go-message/mail"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
type MailBox struct {
|
||||
@@ -82,7 +88,7 @@ func (mb *MailBox) ListFolders() ([]string, error) {
|
||||
}
|
||||
return folders, nil
|
||||
}
|
||||
func (mb *MailBox) GetMessages() ([]BlogEntry, error) {
|
||||
func (mb *MailBox) GetMessages() ([]Post, error) {
|
||||
|
||||
inbox, err := mb.Client.Select(mb.InBox, nil).Wait()
|
||||
if err != nil {
|
||||
@@ -93,9 +99,9 @@ func (mb *MailBox) GetMessages() ([]BlogEntry, error) {
|
||||
if inbox.NumMessages <= 0 {
|
||||
return nil, fmt.Errorf("nomessages")
|
||||
}
|
||||
entries := make([]BlogEntry, 0)
|
||||
posts := make([]Post, 0)
|
||||
for i := uint32(1); i <= inbox.NumMessages; i++ {
|
||||
var entry BlogEntry
|
||||
var post Post
|
||||
seqSet := imap.SeqSetNum(i)
|
||||
bodySection := &imap.FetchItemBodySection{}
|
||||
fetchOptions := &imap.FetchOptions{
|
||||
@@ -106,14 +112,15 @@ func (mb *MailBox) GetMessages() ([]BlogEntry, error) {
|
||||
if err != nil {
|
||||
log.Fatal("Error fetching mails:", err)
|
||||
}
|
||||
entry.Title = strings.Trim(messages[0].Envelope.Subject, "\t ")
|
||||
if !strings.HasPrefix(strings.ToLower(entry.Title), "blog ") {
|
||||
post.Title = strings.Trim(messages[0].Envelope.Subject, "\t ")
|
||||
if !strings.HasPrefix(strings.ToLower(post.Title), "blog ") {
|
||||
continue
|
||||
}
|
||||
|
||||
entry.Id = messages[0].Envelope.MessageID
|
||||
entry.Title = entry.Title[5:]
|
||||
entry.Author = messages[0].Envelope.From[0].Name
|
||||
post.Id = messages[0].Envelope.MessageID
|
||||
post.Title = post.Title[5:] // remove first chars = blog+whitespace
|
||||
post.Author = messages[0].Envelope.From[0].Name
|
||||
post.Date = messages[0].Envelope.Date.String()
|
||||
|
||||
section := messages[0].FindBodySection(bodySection)
|
||||
ioReader := bytes.NewReader(section)
|
||||
@@ -148,9 +155,33 @@ func (mb *MailBox) GetMessages() ([]BlogEntry, error) {
|
||||
}
|
||||
switch mediaType {
|
||||
case "text/plain":
|
||||
entry.Text = string(body)
|
||||
post.Text = string(body)
|
||||
case "text/html":
|
||||
entry.HTML = string(body)
|
||||
post.HTML = ""
|
||||
nodes, err := html.Parse(strings.NewReader(string(body)))
|
||||
if err != nil {
|
||||
post.HTML = ""
|
||||
break
|
||||
}
|
||||
htmlNode, err := findHTMLNode(nodes)
|
||||
if err != nil {
|
||||
post.HTML = ""
|
||||
break
|
||||
}
|
||||
log.Println("Trying to generate html for", htmlNode.Data)
|
||||
var buf bytes.Buffer
|
||||
err = nil
|
||||
for child := htmlNode.FirstChild; child != nil; child = child.NextSibling {
|
||||
log.Println("html for", child.Data)
|
||||
if err = html.Render(&buf, child); err != nil {
|
||||
post.HTML = ""
|
||||
break
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
post.HTML = template.HTML(buf.String())
|
||||
}
|
||||
|
||||
default:
|
||||
log.Println("Content-Type:", part.Header.Get("Content-Type"))
|
||||
|
||||
@@ -160,11 +191,30 @@ func (mb *MailBox) GetMessages() ([]BlogEntry, error) {
|
||||
log.Printf("Unkown part type %+v", part)
|
||||
}
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
posts = append(posts, post)
|
||||
}
|
||||
return entries, nil
|
||||
return posts, nil
|
||||
}
|
||||
|
||||
func (mb *MailBox) Close() {
|
||||
mb.Client.Close()
|
||||
}
|
||||
|
||||
func findHTMLNode(node *html.Node) (*html.Node, error) {
|
||||
if node.Type == html.ElementNode {
|
||||
log.Println(node.Data)
|
||||
}
|
||||
if node.Type == html.ElementNode && node.Data == "body" {
|
||||
log.Println("Found", node.Data)
|
||||
return node, nil
|
||||
}
|
||||
for e := node.FirstChild; e != nil; e = node.NextSibling {
|
||||
log.Println("Search in", e.Data)
|
||||
n, err := findHTMLNode(e)
|
||||
if err == nil {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
}
|
||||
return nil, fmt.Errorf("notfound")
|
||||
}
|
91
mailblog.go
91
mailblog.go
@@ -1,18 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func slash(w http.ResponseWriter, r *http.Request) {
|
||||
//go:embed html/index.tmpl
|
||||
var indexTemplate string
|
||||
|
||||
}
|
||||
//go:embed html/entry.tmpl
|
||||
var entryTemplate string
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -38,56 +38,7 @@ func main() {
|
||||
if len(args) > 0 {
|
||||
switch args[0] {
|
||||
case "configure":
|
||||
|
||||
configuration.Title, err = prompt("Blog Title", nil, "", configuration.Title, false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
configuration.ShortName, err = prompt("Blog Short Name (only letters and numbers, no special signes)", nil, "blog", configuration.ShortName, false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if !regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString(configuration.ShortName) {
|
||||
log.Fatal("Short Name invalid")
|
||||
}
|
||||
|
||||
configuration.WWWRoot, err = prompt("Web Root directory", nil, fmt.Sprintf("/var/www/html/%s", configuration.ShortName), configuration.WWWRoot, false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
configuration.MailBox.Server, err = prompt("IMAP Server Hostname or IP", nil, "", configuration.MailBox.Server, false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
configuration.MailBox.Port, err = prompt("IMAP Server Port", nil, "993", configuration.MailBox.Port, false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = strconv.ParseUint(configuration.MailBox.Port, 10, 16)
|
||||
if err != nil {
|
||||
log.Fatal("IMAP Server Port invalid")
|
||||
}
|
||||
configuration.MailBox.User, err = prompt("IMAP User Login", nil, "", configuration.MailBox.User, false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
configuration.MailBox.Password, err = prompt("IMAP User Password", nil, "", configuration.MailBox.Password, false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
options := [...]string{"SSL/TLS", "StartTLS", "NoTLS"}
|
||||
configuration.MailBox.SSL, err = prompt("IMAP SSL/TLS", options[:], "SSL/TLS", configuration.MailBox.SSL, false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
configuration.MailBox.InBox, err = prompt("IMAP INBOX", nil, "INBOX", configuration.MailBox.InBox, false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
configuration.Prompt()
|
||||
file, err = os.Create(fmt.Sprintf("%s/.config/mailblog.json", home))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -108,30 +59,14 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
folders, err := mb.ListFolders()
|
||||
defer mb.Close()
|
||||
|
||||
var blog Blog
|
||||
blog.Lang = "fr-FR"
|
||||
blog.Title = configuration.Title
|
||||
blog.Posts, err = mb.GetMessages()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println(folders)
|
||||
msgs, err := mb.GetMessages()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, m := range msgs {
|
||||
log.Println(m.Id)
|
||||
log.Println(m.Title)
|
||||
log.Println(m.Author)
|
||||
log.Println("Text version")
|
||||
log.Println(m.Text)
|
||||
log.Println("HTML version")
|
||||
log.Println(m.HTML)
|
||||
log.Println(m.Id)
|
||||
log.Println()
|
||||
}
|
||||
|
||||
mb.Close()
|
||||
|
||||
http.HandleFunc("/", slash)
|
||||
http.ListenAndServe("0.0.0.0:8080", nil)
|
||||
StartServer(&blog)
|
||||
}
|
||||
|
57
web.go
Normal file
57
web.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func StartServer(blog *Blog) {
|
||||
|
||||
http.HandleFunc("GET /{$}",
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
tpl, err := template.New("index").Parse(indexTemplate)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = tpl.ExecuteTemplate(w, "index", blog)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
})
|
||||
http.HandleFunc("GET /post/{id}",
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.PathValue("id")
|
||||
log.Println("showing post:", id)
|
||||
for _, p := range blog.Posts {
|
||||
log.Println("Examining:", p.Id)
|
||||
if p.Id == id {
|
||||
tpl, err := template.New("entry").Parse(entryTemplate)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var post Blog
|
||||
post.Title = blog.Title
|
||||
post.Lang = blog.Lang
|
||||
post.Posts = make([]Post, 1)
|
||||
post.Posts[0] = p
|
||||
err = tpl.ExecuteTemplate(w, "entry", post)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(404)
|
||||
|
||||
})
|
||||
|
||||
http.HandleFunc("GET /favicon.ico",
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("favicon.ico")
|
||||
w.WriteHeader(200)
|
||||
})
|
||||
|
||||
http.ListenAndServe("0.0.0.0:8080", nil)
|
||||
}
|
Reference in New Issue
Block a user