129 lines
2.7 KiB
Go
129 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"slices"
|
|
"sort"
|
|
"strings"
|
|
|
|
"git.akyoto.dev/go/markdown"
|
|
"git.akyoto.dev/go/web"
|
|
"git.akyoto.dev/go/web/send"
|
|
)
|
|
|
|
type App struct {
|
|
html string
|
|
posts map[string]*Post
|
|
}
|
|
|
|
func (app *App) Init() {
|
|
app.html = loadClean("public/app.html")
|
|
css := loadClean("public/app.css")
|
|
app.html = strings.Replace(app.html, "{head}", fmt.Sprintf("{head}<style>%s</style>", css), 1)
|
|
app.posts = loadPosts("posts")
|
|
}
|
|
|
|
func (app *App) Run() {
|
|
s := web.NewServer()
|
|
|
|
s.Use(func(ctx web.Context) error {
|
|
defer func() {
|
|
err := recover()
|
|
|
|
if err != nil {
|
|
fmt.Println("Recovered panic:", err)
|
|
}
|
|
}()
|
|
|
|
return ctx.Next()
|
|
})
|
|
|
|
s.Use(func(ctx web.Context) error {
|
|
path := ctx.Request().Path()
|
|
|
|
if len(path) > 1 && strings.HasSuffix(path, "/") {
|
|
return ctx.Redirect(301, strings.TrimSuffix(path, "/"))
|
|
}
|
|
|
|
return ctx.Next()
|
|
})
|
|
|
|
render := func(ctx web.Context, head string, body string) error {
|
|
html := app.html
|
|
html = strings.Replace(html, "{head}", head, 1)
|
|
html = strings.Replace(html, "{body}", body, 1)
|
|
return send.HTML(ctx, html)
|
|
}
|
|
|
|
s.Get("/", func(ctx web.Context) error {
|
|
html := bytes.Buffer{}
|
|
html.WriteString(`<h2>Blog</h2><ul class="blog">`)
|
|
articles := []*Post{}
|
|
|
|
for _, post := range app.posts {
|
|
if !post.Published || !slices.Contains(post.Tags, "article") {
|
|
continue
|
|
}
|
|
|
|
articles = append(articles, post)
|
|
}
|
|
|
|
sort.Slice(articles, func(i, j int) bool {
|
|
return articles[i].Created > articles[j].Created
|
|
})
|
|
|
|
for _, post := range articles {
|
|
fmt.Fprintf(&html, `<li><a href="/%s">%s</a><time datetime="%s">%s</time></li>`, post.Slug, post.Title, post.Created, post.Created[:len("YYYY")])
|
|
}
|
|
|
|
html.WriteString(`</ul>`)
|
|
return render(ctx, "<title>akyoto.dev</title>", html.String())
|
|
})
|
|
|
|
s.Get("/:post", func(ctx web.Context) error {
|
|
slug := ctx.Request().Param("post")
|
|
post := app.posts[slug]
|
|
head := fmt.Sprintf(`<title>%s</title><meta name="keywords" content="%s">`, post.Title, strings.Join(post.Tags, ","))
|
|
content := ""
|
|
|
|
if slices.Contains(post.Tags, "article") {
|
|
content = fmt.Sprintf(
|
|
`<article><header><h1>%s</h1><time datetime="%s">%s</time></header>%s</article>`,
|
|
post.Title,
|
|
post.Created,
|
|
post.Created[:len("YYYY-MM-DD")],
|
|
markdown.Render(post.Content),
|
|
)
|
|
} else {
|
|
content = fmt.Sprintf(
|
|
`<h2>%s</h2>%s`,
|
|
post.Title,
|
|
markdown.Render(post.Content),
|
|
)
|
|
}
|
|
|
|
return render(ctx, head, content)
|
|
})
|
|
|
|
s.Run(":8080")
|
|
}
|
|
|
|
func load(path string) string {
|
|
dataBytes, err := os.ReadFile(path)
|
|
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return string(dataBytes)
|
|
}
|
|
|
|
func loadClean(path string) string {
|
|
data := load(path)
|
|
data = strings.ReplaceAll(data, "\t", "")
|
|
data = strings.ReplaceAll(data, "\n", "")
|
|
return data
|
|
}
|