Reste à prévoir la pagination et la recherche
This commit is contained in:
42
blog.go
42
blog.go
@@ -6,6 +6,7 @@ import (
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
@@ -17,8 +18,9 @@ type Post struct {
|
||||
Title string
|
||||
Author string
|
||||
Date string
|
||||
DateTime time.Time
|
||||
HTML template.HTML
|
||||
ShortText string
|
||||
ShortText template.HTML
|
||||
Text string
|
||||
}
|
||||
|
||||
@@ -44,28 +46,25 @@ func (b *Blog) GetPost(Id string) (Post, bool) {
|
||||
return ret, false
|
||||
}
|
||||
func (p *Post) GenerateExcerpt() {
|
||||
fmt.Println("----", p.Title)
|
||||
|
||||
p.ShortText = ""
|
||||
if len(p.HTML) > 0 {
|
||||
fmt.Println("Short text is html", p.HTML)
|
||||
heading, err := p.GetFirstHeading(string(p.HTML))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
paragraph, err := p.GetFirstParagraph(string(p.HTML))
|
||||
paragraph, err := p.GetFirstParagraphs(string(p.HTML))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if len(heading) > 0 {
|
||||
log.Println("heading is:", heading)
|
||||
p.ShortText += heading
|
||||
p.ShortText += template.HTML(heading)
|
||||
}
|
||||
if len(paragraph) > 0 {
|
||||
log.Println("paragraph is:", paragraph)
|
||||
p.ShortText += paragraph
|
||||
p.ShortText += template.HTML(paragraph)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Short text is text", p.Text)
|
||||
p.ShortText = p.Text
|
||||
p.ShortText = template.HTML(p.Text)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -88,22 +87,31 @@ func (p *Post) GetFirstHeading(html string) (string, error) {
|
||||
|
||||
return htmlContent, nil
|
||||
}
|
||||
func (p *Post) GetFirstParagraph(html string) (string, error) {
|
||||
func (p *Post) GetFirstParagraphs(html string) (string, error) {
|
||||
|
||||
doc, err := goquery.NewDocumentFromReader(strings.NewReader(html))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
firstParagraph := doc.Find("p").First()
|
||||
if firstParagraph.Length() == 0 {
|
||||
return "", fmt.Errorf("no paragraph found")
|
||||
}
|
||||
var htmlContent string
|
||||
var numberOfTextChars int
|
||||
|
||||
htmlContent, err := goquery.OuterHtml(firstParagraph)
|
||||
// Eventuellement trouver un moyen de ne plus examiner le reste du document
|
||||
// si 200 caractères atteints
|
||||
doc.Find("p,div,h1,h2,h3,h4,h5").Each(func(i int, s *goquery.Selection) {
|
||||
|
||||
if numberOfTextChars < 200 {
|
||||
|
||||
numberOfTextChars += len(s.Text())
|
||||
|
||||
content, err := goquery.OuterHtml(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
log.Println(err)
|
||||
}
|
||||
htmlContent += content
|
||||
}
|
||||
})
|
||||
|
||||
return htmlContent, nil
|
||||
}
|
||||
|
@@ -14,10 +14,15 @@ type MailBoxConfiguration struct {
|
||||
SSL string
|
||||
InBox string
|
||||
}
|
||||
type ServiceConfiguration struct {
|
||||
Address string
|
||||
Port string
|
||||
}
|
||||
type BlogConfiguration struct {
|
||||
Title string
|
||||
ShortName string
|
||||
MailBox MailBoxConfiguration
|
||||
Service ServiceConfiguration
|
||||
}
|
||||
|
||||
func (configuration *BlogConfiguration) Prompt() error {
|
||||
@@ -31,7 +36,7 @@ func (configuration *BlogConfiguration) Prompt() error {
|
||||
return err
|
||||
}
|
||||
if !regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString(configuration.ShortName) {
|
||||
return fmt.Errorf("short same invalid")
|
||||
return fmt.Errorf("short name invalid")
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -73,5 +78,15 @@ func (configuration *BlogConfiguration) Prompt() error {
|
||||
return err
|
||||
}
|
||||
|
||||
configuration.Service.Address, err = prompt("Listen address", nil, "127.0.0.1", configuration.Service.Address, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configuration.Service.Port, err = prompt("Listen port", nil, "8080", configuration.Service.Port, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -3,14 +3,23 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="stylesheet" href="../styles.css">
|
||||
<title>{{.BlogTitle}}</title>
|
||||
</head>
|
||||
<h1><a href="/proxy/8080/">{{.BlogTitle}}</a></h1>
|
||||
<h1 class="blogtitle"><a href="/proxy/8080/">{{.BlogTitle}}</a></h1>
|
||||
<body>
|
||||
<h2>{{.Title}}</h2>
|
||||
<div>
|
||||
<article class="post">
|
||||
<header class="postheader">
|
||||
<h2 class="posttitle">{{.Title}}</h2>
|
||||
<p class="postdate"><time datetime="{{.Date}}">{{.Date}}</Time></p>
|
||||
</header>
|
||||
<div class="postcontent">
|
||||
{{ if .HTML }}
|
||||
{{.HTML}}
|
||||
{{ else }}
|
||||
{{.Text}}
|
||||
{{ end }}
|
||||
</div>
|
||||
</article>
|
||||
</body>
|
||||
|
||||
</html>
|
@@ -4,36 +4,19 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>{{.Title}}</title>
|
||||
<style>
|
||||
a:link {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:visited {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
color: orange;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="/proxy/8080/">{{.Title}}</a></h1>
|
||||
<h1 class="blogtitle"><a href="/proxy/8080/">{{.Title}}</a></h1>
|
||||
{{range .Posts}}
|
||||
<article>
|
||||
<h2><a href=/proxy/8080/post/{{ .Id }}>{{.Title}}</a></h2>
|
||||
<address>
|
||||
{{.Author}}
|
||||
</address>
|
||||
<article class="shortpost">
|
||||
<header class="shortpostheader">
|
||||
<h2 class="shortposttitle"><a href=/proxy/8080/post/{{ .Id }}>{{.Title}}</a></h2>
|
||||
<p class="shortpostdate"><time datetime="{{.Date}}">{{.Date}}</Time></p>
|
||||
</header>
|
||||
<div>
|
||||
<b>Extrait:</b>
|
||||
<blockquote>{{.ShortText}}</blockquote>
|
||||
<blockquote class="shortpostcontent">{{.ShortText}}</blockquote>
|
||||
</div>
|
||||
<footer>
|
||||
<p>Publié le:<time datetime="{{.Date}}">{{.Date}}</Time></p>
|
||||
</footer>
|
||||
</article>
|
||||
{{ end }}
|
||||
</body>
|
||||
|
59
html/styles.css
Normal file
59
html/styles.css
Normal file
@@ -0,0 +1,59 @@
|
||||
a:link {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:visited {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
color: orange;
|
||||
text-decoration: none;
|
||||
}
|
||||
.blogtitle {
|
||||
color: coral;
|
||||
text-decoration:underline;
|
||||
}
|
||||
/*
|
||||
.shortpost {
|
||||
|
||||
}
|
||||
.shortpostheader {
|
||||
|
||||
}
|
||||
*/
|
||||
.shortposttitle {
|
||||
font-weight: bold;
|
||||
}
|
||||
.shortpostdate {
|
||||
font-style: italic;
|
||||
font-size: smaller;
|
||||
font-weight: lighter;
|
||||
}
|
||||
/*
|
||||
.shortpostcontent {
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
.post {
|
||||
|
||||
}
|
||||
.postheader {
|
||||
|
||||
}
|
||||
*/
|
||||
.posttitle {
|
||||
font-weight: bold;
|
||||
}
|
||||
.postdate {
|
||||
font-style: italic;
|
||||
font-size: smaller;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
/*
|
||||
.postcontent {
|
||||
|
||||
}
|
||||
*/
|
9
imap.go
9
imap.go
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"mime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -69,12 +70,11 @@ func (mb *MailBox) Connect() error {
|
||||
log.Println("Error connnecting to", imapServer, ":", err)
|
||||
return err
|
||||
}
|
||||
log.Println("Connected")
|
||||
|
||||
err = mb.Client.Login(mb.User, mb.Password).Wait()
|
||||
if err != nil {
|
||||
log.Fatal("failed to login:", err)
|
||||
}
|
||||
log.Println("Logged in")
|
||||
return nil
|
||||
}
|
||||
func (mb *MailBox) ListFolders() ([]string, error) {
|
||||
@@ -122,6 +122,7 @@ func (mb *MailBox) GetMessages() ([]Post, error) {
|
||||
post.Author = messages[0].Envelope.To[0].Name
|
||||
|
||||
post.Date = messages[0].Envelope.Date.Format("2006/01/02 15:04")
|
||||
post.DateTime = messages[0].Envelope.Date
|
||||
|
||||
section := messages[0].FindBodySection(bodySection)
|
||||
|
||||
@@ -186,7 +187,6 @@ func (mb *MailBox) GetMessages() ([]Post, error) {
|
||||
post.HTML = template.HTML(buf.String())
|
||||
post.GenerateExcerpt()
|
||||
}
|
||||
|
||||
default:
|
||||
log.Println("Content-Type:", part.Header.Get("Content-Type"))
|
||||
|
||||
@@ -198,6 +198,9 @@ func (mb *MailBox) GetMessages() ([]Post, error) {
|
||||
}
|
||||
posts = append(posts, post)
|
||||
}
|
||||
sort.Slice(posts, func(i, j int) bool {
|
||||
return posts[i].DateTime.After(posts[j].DateTime)
|
||||
})
|
||||
return posts, nil
|
||||
}
|
||||
|
||||
|
21
mailblog.go
21
mailblog.go
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:embed html/index.tmpl
|
||||
@@ -14,6 +15,9 @@ var indexTemplate string
|
||||
//go:embed html/entry.tmpl
|
||||
var entryTemplate string
|
||||
|
||||
//go:embed html/styles.css
|
||||
var nativeCSS string
|
||||
|
||||
func main() {
|
||||
|
||||
var err error
|
||||
@@ -26,7 +30,6 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
configurationFileName := fmt.Sprintf("%s/.config/mailblog.json", home)
|
||||
log.Println("Mailblog starting using", configurationFileName)
|
||||
|
||||
file, err := os.Open(configurationFileName)
|
||||
if err == nil {
|
||||
@@ -60,24 +63,34 @@ func main() {
|
||||
blog.Lang = "fr-FR"
|
||||
blog.Title = configuration.Title
|
||||
go MailboxFetcher(configuration, &blog)
|
||||
StartServer(&blog)
|
||||
StartServer(configuration, &blog)
|
||||
}
|
||||
|
||||
func MailboxFetcher(configuration BlogConfiguration, blog *Blog) {
|
||||
|
||||
var mb MailBox
|
||||
mb.Configure(&configuration.MailBox)
|
||||
|
||||
for {
|
||||
err := mb.Connect()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer mb.Close()
|
||||
|
||||
posts, err := mb.GetMessages()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
mb.Close()
|
||||
time.Sleep(10 * time.Second)
|
||||
continue
|
||||
}
|
||||
mb.Close()
|
||||
|
||||
blog.mutex.Lock()
|
||||
defer blog.mutex.Unlock()
|
||||
blog.Posts = posts
|
||||
blog.mutex.Unlock()
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
33
web.go
33
web.go
@@ -3,14 +3,17 @@ package main
|
||||
import (
|
||||
"html/template"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func StartServer(blog *Blog) {
|
||||
func StartServer(configuration BlogConfiguration, blog *Blog) {
|
||||
|
||||
mux := http.NewServeMux()
|
||||
/* Handle home page (/): list of blog entries */
|
||||
http.HandleFunc("GET /{$}",
|
||||
mux.HandleFunc("GET /{$}",
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("%+v", r.Header)
|
||||
tpl, err := template.New("index").Parse(indexTemplate)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -24,7 +27,7 @@ func StartServer(blog *Blog) {
|
||||
|
||||
})
|
||||
/* Handle one post display(/post/{post ID}) */
|
||||
http.HandleFunc("GET /post/{id}",
|
||||
mux.HandleFunc("GET /post/{id}",
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.PathValue("id")
|
||||
post, found := blog.GetPost(id)
|
||||
@@ -42,11 +45,31 @@ func StartServer(blog *Blog) {
|
||||
|
||||
})
|
||||
|
||||
http.HandleFunc("GET /favicon.ico",
|
||||
mux.HandleFunc("GET /styles.css",
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Content-Type", "text/css")
|
||||
w.Write([]byte(nativeCSS))
|
||||
})
|
||||
|
||||
mux.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)
|
||||
addr := net.JoinHostPort(configuration.Service.Address, configuration.Service.Port)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: addr,
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
server.ListenAndServe()
|
||||
//http.ListenAndServe(addr, server)
|
||||
}
|
||||
|
||||
/*
|
||||
func isCodeServerProxy(r *http.Request) bool {
|
||||
return len(r.Header.Get("code-server-session")) > 0
|
||||
}
|
||||
*/
|
||||
|
Reference in New Issue
Block a user