cerca

lean forum software (pmc local branch)
Log | Files | Refs | README | LICENSE

commit 1c2cf70aa541cc09da1f97f0cb658bf748db6596
parent 810dbb9a032d2efe1af86c6615a74aca9debe389
Author: cblgh <cblgh@cblgh.org>
Date:   Wed, 12 Jan 2022 14:20:18 +0100

improve naming, add relative time & styling

Diffstat:
M.gitignore | 2++
Mdatabase/database.go | 13++++++++++++-
Ahtml/html.go | 7+++++++
Mhtml/thread.html | 3++-
Mrun.go | 5+++--
Mserver/server.go | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
6 files changed, 92 insertions(+), 29 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,2 +1,4 @@ .*.sw[a-z] +*.db data/ +data/.gitkeep diff --git a/database/database.go b/database/database.go @@ -1,15 +1,18 @@ package database import ( - "cerca/util" "context" "database/sql" + "errors" "fmt" "html/template" "log" "net/url" + "os" "time" + "cerca/util" + _ "github.com/mattn/go-sqlite3" ) @@ -18,6 +21,14 @@ type DB struct { } func InitDB(filepath string) DB { + if _, err := os.Stat(filepath); errors.Is(err, os.ErrNotExist) { + file, err := os.Create(filepath) + if err != nil { + log.Fatal(err) + } + defer file.Close() + } + db, err := sql.Open("sqlite3", filepath) util.Check(err, "opening sqlite3 database at %s", filepath) if db == nil { diff --git a/html/html.go b/html/html.go @@ -0,0 +1,7 @@ +package html + +import "embed" + +// Templates contain the raw HTML of all of our templates. +//go:embed *.html +var Templates embed.FS diff --git a/html/thread.html b/html/thread.html @@ -2,7 +2,8 @@ <h2>{{ .Data.Title }}</h2> {{ range $index, $post := .Data.Posts }} <div> - <p><b>{{ $post.Author }}</b></p> + <p style="display: grid; grid-auto-flow: column; justify-content: space-between;"><span><b>{{ $post.Author }}</b></span><span><time datetime="{{ $post.Publish | formatDate }}">{{ + $post.Publish | formatDateRelative }}</time></span></p> {{ $post.Content }} </div> {{ end }} diff --git a/run.go b/run.go @@ -1,12 +1,13 @@ package main import ( - "cerca/server" - "cerca/util" "flag" "fmt" "os" "strings" + + "cerca/server" + "cerca/util" ) func readAllowlist(location string) []string { diff --git a/server/server.go b/server/server.go @@ -4,17 +4,18 @@ import ( "context" "errors" "fmt" + "html/template" "net/http" "net/url" "strconv" "strings" - "syscall" + "time" "cerca/crypto" "cerca/database" + cercaHTML "cerca/html" "cerca/server/session" "cerca/util" - "html/template" "github.com/carlmjohnson/requests" ) @@ -93,27 +94,67 @@ func (h RequestHandler) IsLoggedIn(req *http.Request) (bool, int) { return true, userid } -var views = []string{"index", "head", "footer", "login-component", "login", "register", "register-success", "thread", "new-thread", "generic-message", "about"} +var ( + templateFuncs = template.FuncMap{ + "formatDateTime": func(t time.Time) string { + return t.Format("2006-01-02 15:04:05") + }, + "formatDateTimeRFC3339": func(t time.Time) string { + return t.Format(time.RFC3339Nano) + }, + "formatDate": func(t time.Time) string { + return t.Format("2006-01-02") + }, + "formatDateRelative": func(t time.Time) string { + diff := time.Since(t) + if diff < time.Hour*24 { + return "today" + } else if diff >= time.Hour*24 && diff < time.Hour*48 { + return "yesterday" + } + return t.Format("2006-01-02") + }, + } + + templates = template.Must(generateTemplates()) +) -// wrap the contents of `views` to the format expected by template.ParseFiles() -func wrapViews() []string { - for i, item := range views { - views[i] = fmt.Sprintf("html/%s.html", item) +func generateTemplates() (*template.Template, error) { + views := []string{ + "about", + "footer", + "generic-message", + "head", + "index", + "login", + "login-component", + "new-thread", + "register", + "register-success", + "thread", + } + + rootTemplate := template.New("root") + + for _, view := range views { + newTemplate, err := rootTemplate.Funcs(templateFuncs).ParseFS(cercaHTML.Templates, fmt.Sprintf("%s.html", view)) + if err != nil { + return nil, fmt.Errorf("could not get files: %w", err) + } + rootTemplate = newTemplate } - return views -} -var templates = template.Must(template.ParseFiles(wrapViews()...)) + return rootTemplate, nil +} func (h RequestHandler) renderView(res http.ResponseWriter, viewName string, data TemplateData) { if data.Title == "" { data.Title = strings.ReplaceAll(viewName, "-", " ") } - errTemp := templates.ExecuteTemplate(res, viewName+".html", data) - if errors.Is(errTemp, syscall.EPIPE) { - fmt.Println("had a broken pipe, continuing") - } else { - util.Check(errTemp, "rendering %s view", viewName) + + view := fmt.Sprintf("%s.html", viewName) + if err := templates.ExecuteTemplate(res, view, data); err != nil { + util.Check(err, "rendering %q view", view) } } @@ -127,16 +168,16 @@ func (h RequestHandler) ThreadRoute(res http.ResponseWriter, req *http.Request) loggedIn, userid := h.IsLoggedIn(req) threadid, err := strconv.Atoi(parts[2]) - if err != nil { - dump(err) - title := "Page not found" - data := GenericMessageData{ - Title: title, - Message: "The visited page does not exist (anymore?)", - } - h.renderView(res, "generic-message", TemplateData{Data: data, LoggedIn: loggedIn}) - return - } + if err != nil { + dump(err) + title := "Page not found" + data := GenericMessageData{ + Title: title, + Message: "The visited page does not exist (anymore?)", + } + h.renderView(res, "generic-message", TemplateData{Data: data, LoggedIn: loggedIn}) + return + } if req.Method == "POST" && loggedIn { // handle POST (=> add a reply, then show the thread)