Reste à prévoir la pagination et la recherche

This commit is contained in:
Laurent Ulrich
2025-08-20 16:19:55 +02:00
parent 10ebdaad4a
commit 9dd396e95f
8 changed files with 183 additions and 70 deletions

46
blog.go
View File

@@ -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)
if err != nil {
return "", err
}
// 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 {
log.Println(err)
}
htmlContent += content
}
})
return htmlContent, nil
}

View File

@@ -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
}

View File

@@ -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>

View File

@@ -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
View 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 {
}
*/

View File

@@ -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
}

View File

@@ -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)
err := mb.Connect()
if err != nil {
log.Fatal(err)
}
defer mb.Close()
posts, err := mb.GetMessages()
if err != nil {
log.Println(err)
for {
err := mb.Connect()
if err != nil {
log.Fatal(err)
}
posts, err := mb.GetMessages()
if err != nil {
log.Println(err)
mb.Close()
time.Sleep(10 * time.Second)
continue
}
mb.Close()
blog.mutex.Lock()
blog.Posts = posts
blog.mutex.Unlock()
time.Sleep(10 * time.Second)
}
blog.mutex.Lock()
defer blog.mutex.Unlock()
blog.Posts = posts
}

33
web.go
View File

@@ -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
}
*/