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:
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)