sophuwu.site > myweb
moved template into own package
allow raw html as .HTML in templates
fixed db issues
finished blog handler?
sophuwu sophie@skisiel.com
Wed, 11 Dec 2024 03:48:20 +0100
commit

d2b5dc0bd5066297e5ab8888cebefb5dbed3f323

parent

3c66640e3266ab28f78bccceaf423d80178a5551

M blogs.goblogs.go

@@ -1,35 +1,43 @@

package main import ( - "fmt" - "github.com/asdine/storm/v3" + _ "github.com/asdine/storm/v3" + _ "go.etcd.io/bbolt" "net/http" + "net/url" + "path/filepath" + "sophuwu.site/myweb/template" "strings" "time" ) type BlogMeta struct { - ID string `storm:"id"` - Title string + ID string `storm:"unique"` + Title string `storm:"index"` Date string `storm:"index"` - Desc string + Desc string `storm:"index"` +} + +type BlogContent struct { + ID string `storm:"unique"` + Content string `storm:"index"` +} + +func IdGen(title, date string) string { + title = strings.ReplaceAll(title, " ", "-") + return filepath.Join(date, url.PathEscape(title)) } func NewBlog(title, desc, body string, date ...string) error { if len(date) == 0 { date = append(date, time.Now().Format("2006-01-02")) } - id := Sha1Base64(title, date[0]) - - exists, err := DB.KeyExists("BlogContent", id) - if err != nil { - return err - } - if exists { - return fmt.Errorf("blog already exists") - } + id := IdGen(title, date[0]) - err = DB.Set("BlogContent", id, body) + err := DB.Save(&BlogContent{ + ID: id, + Content: body, + }) if err != nil { return err }

@@ -40,11 +48,12 @@ Title: title,

Date: date[0], Desc: desc, } - return DB.Save(&blg) + err = DB.Save(&blg) + return err } -func GetBlog(id string) (meta BlogMeta, content string, err error) { - err = DB.Get("BlogContent", id, &content) +func GetBlog(id string) (meta BlogMeta, content BlogContent, err error) { + err = DB.One("ID", id, &content) if err != nil { return }

@@ -52,9 +61,25 @@ err = DB.One("ID", id, &meta)

return } +func SortBlogsDate(blogs []BlogMeta) []BlogMeta { + for i := 0; i < len(blogs); i++ { + for j := i + 1; j < len(blogs); j++ { + if blogs[i].Date < blogs[j].Date { + blogs[i], blogs[j] = blogs[j], blogs[i] + } + } + } + return blogs +} + func GetBlogs() ([]BlogMeta, error) { var blogs []BlogMeta - err := DB.AllByIndex("Date", &blogs, storm.Limit(10), storm.Reverse()) + // err := DB.All(&blogs) + err := DB.AllByIndex("Date", &blogs) + if err != nil { + return nil, err + } + blogs = SortBlogsDate(blogs) return blogs, err }

@@ -65,12 +90,21 @@ blogs, err := GetBlogs()

if CheckHttpErr(err, w, r, 500) { return } + d := template.Data("Sophie's Blogs", "I sometimes write blogs about random things that I find interesting. Here you can read all my posts about various things I found interesting at some point.") + d["blogs"] = []BlogMeta(blogs) + d.Set("NoBlogs", len(blogs)) - // "Title": "Sophie's Blogs", - // "Desc": "I sometimes write blogs about random things that I find interesting. Here you can read all my posts about various things I found interesting at some point.", - // "Blogs": blogs, - err = Templates.Use(w, "blogs") + err = template.Use(w, r, "blogs", d) CheckHttpErr(err, w, r, 500) return } + meta, content, err := GetBlog(path) + if CheckHttpErr(err, w, r, 404) { + return + } + data := template.Data(meta.Title, meta.Desc) + data.Set("Date", meta.Date) + data.SetHTML(content.Content) + err = template.Use(w, r, "blog", data) + CheckHttpErr(err, w, r, 500) }
M db.godb.go

@@ -2,16 +2,15 @@ package main

import ( "github.com/asdine/storm/v3" - "go.etcd.io/bbolt" "log" "sophuwu.site/myweb/config" - "time" + "sophuwu.site/myweb/template" ) var DB *storm.DB func OpenDB() { - db, err := storm.Open(config.DBPath, storm.BoltOptions(0660, &bbolt.Options{Timeout: time.Second})) + db, err := storm.Open(config.DBPath) if err != nil { log.Fatalf("failed to open db: %v", err) }

@@ -24,3 +23,13 @@ if err != nil {

log.Println(err) } } + +func GetPageData(page string) (template.HTMLDataMap, error) { + var d template.HTMLDataMap + err := DB.Get("pages", page, &d) + return d, err +} + +func SetPageData(page string, data template.HTMLDataMap) error { + return DB.Set("pages", page, data) +}
M go.modgo.mod

@@ -2,9 +2,8 @@ module sophuwu.site/myweb

go 1.22.5 -require github.com/asdine/storm/v3 v3.2.1 - require ( - go.etcd.io/bbolt v1.3.4 // indirect - golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 // indirect + github.com/asdine/storm/v3 v3.2.1 + go.etcd.io/bbolt v1.3.4 + golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 )
M main.gomain.go

@@ -2,27 +2,20 @@ package main

import ( "context" - "crypto/sha1" - "encoding/base64" "errors" - "fmt" "golang.org/x/sys/unix" "log" "net/http" "os" "os/signal" "sophuwu.site/myweb/config" + "sophuwu.site/myweb/template" ) -func Sha1Base64(data ...any) string { - h := sha1.New() - h.Write([]byte(fmt.Sprint(data...))) - return base64.StdEncoding.EncodeToString(h.Sum(nil)) -} - func CheckHttpErr(err error, w http.ResponseWriter, r *http.Request, code int) bool { if err != nil { HttpErr(w, r, code) + log.Printf("err: %v: HTTP %d: %s %s\n", err, code, r.Method, r.URL.Path) return true } return false

@@ -30,16 +23,14 @@ }

func HttpErr(w http.ResponseWriter, r *http.Request, code int) { http.Error(w, http.StatusText(code), code) - log.Printf("HTTP %d: %s %s\n", code, r.Method, r.URL.Path) } func HttpIndex(w http.ResponseWriter, r *http.Request) { - var d HTMLDataMap - err := DB.Get("pages", "index", &d) + d, err := GetPageData("index") if CheckHttpErr(err, w, r, 500) { return } - err = Templates.Use(w, r, "index", d) + err = template.Use(w, r, "index", d) _ = CheckHttpErr(err, w, r, 500) }

@@ -60,18 +51,14 @@ })

} func main() { - err := Templates.Init() OpenDB() - - d := HTMLData(config.Name, fmt.Sprintf("About %s. look at animations I've made, read about things I've found interesting. Links to my social media.", config.Name)) - d.SetHTML("Content", "<h1>Welcome to my website</h1><p>Here you can find animations I've made, blogs I've written, and other things I've found interesting.</p>") - DB.Set("pages", "index", &d) - + err := template.Init(config.Templates) if err != nil { log.Fatalf("Error initializing templates: %v", err) } http.HandleFunc("/", HttpIndex) + http.HandleFunc("/blog/", BlogHandler) http.HandleFunc(HttpFS("/static/", config.StaticPath)) http.HandleFunc(HttpFS("/media/", config.MediaPath))
D template.go

@@ -1,80 +0,0 @@

-package main - -import ( - "html/template" - "net/http" - "os" - "path/filepath" - "sophuwu.site/myweb/config" -) - -type HTMLTemplates_t struct { - templates *template.Template - fillFunc func(w http.ResponseWriter, name string, data HTMLDataMap) error -} - -type HTMLDataMap map[string]any - -func HTMLData(title, desc string) HTMLDataMap { - var data = make(HTMLDataMap) - data["Title"] = title - data["Description"] = desc - return data -} - -func (d *HTMLDataMap) Set(key string, value any) { - (*d)[key] = value -} - -func (d *HTMLDataMap) SetHTML(key string, value string) { - (*d)[key] = template.HTML(value) -} - -func (d *HTMLDataMap) SetIfEmpty(key string, value any) { - if _, ok := (*d)[key]; !ok { - (*d)[key] = value - } -} - -var Templates HTMLTemplates_t - -func (h *HTMLTemplates_t) ParseTemplates() error { - index := template.New("index") - index.Parse(filepath.Join(config.Templates, "index.html")) - index.Option() - - tmp, err := template.ParseGlob(config.Templates) - if err != nil { - return err - } - h.templates = tmp - return nil -} - -func (h *HTMLTemplates_t) Init() error { - if os.Getenv("DEBUG") == "1" { - h.fillFunc = func(w http.ResponseWriter, name string, data HTMLDataMap) error { - err := h.ParseTemplates() - if err != nil { - return err - } - return h.templates.ExecuteTemplate(w, name, data) - } - } else { - h.fillFunc = func(w http.ResponseWriter, name string, data HTMLDataMap) error { - return h.templates.ExecuteTemplate(w, name, data) - } - } - - return h.ParseTemplates() -} - -func (h *HTMLTemplates_t) Use(w http.ResponseWriter, r *http.Request, name string, data HTMLDataMap) error { - data.SetIfEmpty("Url", config.URL+r.URL.Path) - data.SetIfEmpty("Email", config.Email) - data.SetIfEmpty("Name", config.Name) - if data["Content"] != nil { - data["HTML"] = template.HTML(data["Content"].(string)) - } - return h.fillFunc(w, name, data) -}
A template/template.go

@@ -0,0 +1,78 @@

+package template + +import ( + "html/template" + "net/http" + "os" + "path/filepath" + "sophuwu.site/myweb/config" +) + +type HTMLDataMap map[string]any + +func Data(title, desc string) HTMLDataMap { + var data = make(HTMLDataMap) + data["Title"] = title + data["Description"] = desc + return data +} + +func (d *HTMLDataMap) Set(key string, value any) { + (*d)[key] = value +} + +func (d *HTMLDataMap) SetHTML(value string) { + (*d)["Content"] = value +} + +func (d *HTMLDataMap) SetIfEmpty(key string, value any) { + if _, ok := (*d)[key]; !ok { + (*d)[key] = value + } +} + +var templates *template.Template +var fillFunc func(w http.ResponseWriter, name string, data HTMLDataMap) error +var templatesDir string + +func parseTemplates() error { + index := template.New("index") + index.Parse(filepath.Join(templatesDir, "index.html")) + index.Option() + + tmp, err := template.ParseGlob(templatesDir) + if err != nil { + return err + } + templates = tmp + return nil +} + +func Init(path string) error { + templatesDir = path + if os.Getenv("DEBUG") == "1" { + fillFunc = func(w http.ResponseWriter, name string, data HTMLDataMap) error { + err := parseTemplates() + if err != nil { + return err + } + return templates.ExecuteTemplate(w, name, data) + } + } else { + fillFunc = func(w http.ResponseWriter, name string, data HTMLDataMap) error { + return templates.ExecuteTemplate(w, name, data) + } + } + + return parseTemplates() +} + +func Use(w http.ResponseWriter, r *http.Request, name string, data HTMLDataMap) error { + data.SetIfEmpty("Url", config.URL+r.URL.Path) + data.SetIfEmpty("Email", config.Email) + data.SetIfEmpty("Name", config.Name) + if data["Content"] != nil { + data["HTML"] = template.HTML(data["Content"].(string)) + } + return fillFunc(w, name, data) +}
M webhome/static/style_dark.csswebhome/static/style_dark.css

@@ -40,7 +40,7 @@ }

::-webkit-scrollbar-thumb:hover { background: #888899; } -.project-meta { +.date { color: #aaaaaa; } .filebutton {
M webhome/static/style_light.csswebhome/static/style_light.css

@@ -40,7 +40,7 @@ }

::-webkit-scrollbar-thumb:hover { background: #dddddd; } -.project-meta { +.date { color: #333; } .filebutton {
M webhome/static/style_main.csswebhome/static/style_main.css

@@ -29,11 +29,14 @@

html, body, p { font-family: "Ubuntu Sans", sans-serif; } -h1, h2, h3, h4, h5, h6, button, a{ +h1, h2, h3, h4, h5, h6, button, a, .date { font-family: "Comfortaa", cursive; } pre, code { font-family: "Victor Mono", monospace; +} +a { + text-decoration: none; } html {

@@ -101,6 +104,8 @@ padding: 0;

border: none; border-bottom-style: solid; border-bottom-width: 1px; + width: calc(100% + 1rlh); + transform: translateX(-0.5rlh); } button:hover {

@@ -134,4 +139,34 @@ font-size: inherit;

font-weight: normal!important; font-style: normal; height: 1em; +} + +h2 { + margin: 0.5lh 0 0.25lh; + font-size: 1.5rem; +} +p { + line-height: 1.2; + margin: 0.25lh 0; + text-align: justify; +} +.date { + font-size: 0.75rem; + font-weight: bold; + line-height: 1.5; +} +.ref-name { + margin: 0.25lh 0; + font-size: 1.2rem; +} +.blog-row { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + flex-wrap: wrap; + margin: 0.25em 0; +} +article { + margin: 1lh 0; }
A webhome/templates/blog.html

@@ -0,0 +1,15 @@

+{{ define "blog" }} +<html lang="en"> +{{ template "head" . }} +<body> +{{ template "nav" . }} +<main> + <h2>{{ .Title }}</h2> + <span class="date">{{ .Date }}</span> + <article> + {{- .HTML -}} + </article> +</main> +</body> +</html> +{{ end }}
M webhome/templates/blogs.htmlwebhome/templates/blogs.html

@@ -4,21 +4,21 @@ {{ template "head" . }}

<body> {{ template "nav" . }} <main> - <h1>Blogs</h1> - {{ if .Desc }} + <h2>Blogs</h2> + <p> + {{ .Description }} + </p> <p> - {{ .Desc }} + {{ .NoBlogs }} blogs found. Showing newest first. </p> - {{ end }} <hr> - {{ range .Blogs }} - <section> - <a href="/blog/{{ .ID }}" class="index-name">{{ .Name }}</a> - <span style="margin: 0 10px;">{{ .Date }}</span> - <br> - <span style="text-align: justify; width: 100% !important;" class="desc">{{ .Desc }}</span> - <hr> + {{- range .blogs -}} + <section class="blog-row"> + <a href="/blog/{{- .ID -}}" class="ref-name">{{- .Title -}}</a> + <span class="date">{{- .Date -}}</span> + <p >{{- .Desc -}}</p> </section> + <hr> {{ end }} </main> </body>