commit 51ecc9b770a720b8cce0dc6145c5064df2c00b02
parent 848069e97fc902ab1db7834952a48634efdb96d8
Author: Pollux <pollux@pollux.codes>
Date: Sun, 1 Jun 2025 15:57:22 -0500
feat: simple implementation
Signed-off-by: Pollux <pollux@pollux.codes>
Diffstat:
A | go.mod | | | 18 | ++++++++++++++++++ |
A | go.sum | | | 47 | +++++++++++++++++++++++++++++++++++++++++++++++ |
A | main.go | | | 151 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | test/form.html | | | 15 | +++++++++++++++ |
4 files changed, 231 insertions(+), 0 deletions(-)
diff --git a/go.mod b/go.mod
@@ -0,0 +1,18 @@
+module guestbook
+
+go 1.24.3
+
+require modernc.org/sqlite v1.37.1
+
+require (
+ github.com/dustin/go-humanize v1.0.1 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/ncruces/go-strftime v0.1.9 // indirect
+ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
+ golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
+ golang.org/x/sys v0.33.0 // indirect
+ modernc.org/libc v1.65.7 // indirect
+ modernc.org/mathutil v1.7.1 // indirect
+ modernc.org/memory v1.11.0 // indirect
+)
diff --git a/go.sum b/go.sum
@@ -0,0 +1,47 @@
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
+github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
+github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
+github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
+github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
+github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
+golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
+golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
+golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
+golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
+golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
+golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
+golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
+golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
+modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
+modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
+modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
+modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
+modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8=
+modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
+modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
+modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
+modernc.org/libc v1.65.7 h1:Ia9Z4yzZtWNtUIuiPuQ7Qf7kxYrxP1/jeHZzG8bFu00=
+modernc.org/libc v1.65.7/go.mod h1:011EQibzzio/VX3ygj1qGFt5kMjP0lHb0qCW5/D/pQU=
+modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
+modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
+modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
+modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
+modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
+modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
+modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
+modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
+modernc.org/sqlite v1.37.1 h1:EgHJK/FPoqC+q2YBXg7fUmES37pCHFc97sI7zSayBEs=
+modernc.org/sqlite v1.37.1/go.mod h1:XwdRtsE1MpiBcL54+MbKcaDvcuej+IYSMfLN6gSKV8g=
+modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
+modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
+modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
+modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
diff --git a/main.go b/main.go
@@ -0,0 +1,151 @@
+package main
+
+import (
+ "database/sql"
+ "fmt"
+ "html/template"
+ "log"
+ "net/http"
+ "time"
+ _ "modernc.org/sqlite"
+)
+
+var database *sql.DB
+
+var message_template string = `
+<html lang="en">
+ <head>
+ </head>
+ <body>
+ <main>
+{{ range . }}
+{{ if .Website }}
+<a href="{{ .Website }}">{{ .Name }}</a>: {{ .Message }} {{ .Time.Format "2006-01-02T15:04:05Z" }}<br>
+{{ else }}
+{{ .Name }}: {{ .Message }} {{ .Time.Format "2006-01-02T15:04:05Z" }}<br>
+{{ end }}
+{{ end }}
+ </main>
+ </body>
+</html>
+`
+
+type Message struct {
+ Name string
+ Website string
+ Email string
+ Message string
+ Time time.Time
+}
+
+func GetGuestbook(w http.ResponseWriter, r *http.Request) {
+
+ if r.Method != "GET" {
+ http.Error(w, "Unsupported method", http.StatusBadRequest)
+ return
+ }
+
+ // Pull database contents
+
+ rows, err := database.Query("SELECT name,website,email,message,time FROM Messages ORDER BY time DESC;")
+
+ if err != nil {
+ http.Error(w, "Error reading SQL database", http.StatusInternalServerError)
+ return
+ }
+
+ defer rows.Close()
+
+ messages := []*Message{}
+
+ for rows.Next() {
+ var m Message
+ err := rows.Scan(
+ &m.Name,
+ &m.Website,
+ &m.Email,
+ &m.Message,
+ &m.Time,
+ )
+
+ if err != nil {
+ http.Error(w, "Error reading SQL database", http.StatusInternalServerError)
+ return
+ }
+
+ messages = append(messages, &m)
+ }
+
+ err = rows.Err()
+ if err != nil {
+ http.Error(w, "Error reading SQL database", http.StatusInternalServerError)
+ return
+ }
+
+ // Apply guestbook template
+
+ tmpl, err := template.New("guestbook").Parse(message_template)
+ if err != nil {
+ http.Error(w, "Error parsing template", http.StatusInternalServerError)
+ return
+ }
+ err = tmpl.Execute(w, messages)
+ if err != nil {
+ http.Error(w, "Error executing template", http.StatusInternalServerError)
+ return
+ }
+}
+
+func PostToGuestbook(w http.ResponseWriter, r *http.Request) {
+
+ if r.Method != "POST" {
+ http.Error(w, "Unsupported method", http.StatusBadRequest)
+ return
+ }
+
+ err := r.ParseForm()
+ if err != nil {
+ http.Error(w, "Error parsing form response", http.StatusBadRequest)
+ }
+
+ name := r.PostForm.Get("name")
+ email := r.PostForm.Get("email")
+ website := r.PostForm.Get("website")
+ message := r.PostForm.Get("message")
+
+ stmt, err := database.Prepare("INSERT INTO Messages (name, email, website, message) VALUES (?, ?, ?, ?)")
+ if err != nil {
+ http.Error(w, "Error accessing database", http.StatusInternalServerError)
+ return
+ }
+
+ _, err = stmt.Exec(name, email, website, message)
+ if err != nil {
+ http.Error(w, "Error accessing database", http.StatusInternalServerError)
+ return
+ }
+}
+
+func main() {
+
+ var err error
+ database, err = sql.Open("sqlite", "guestbook.db")
+
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer database.Close()
+
+ err = database.Ping()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ http.HandleFunc("/", GetGuestbook)
+ http.HandleFunc("/post", PostToGuestbook)
+
+ fmt.Println("Starting server at port 48378")
+ if err := http.ListenAndServe(":48378", nil); err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/test/form.html b/test/form.html
@@ -0,0 +1,15 @@
+<html lang="en">
+ <head>
+ </head>
+ <body>
+ <main>
+ <form action="http://localhost:48378/post" method="post">
+ Name: <input type="text" name="name"><br>
+ Website (optional): <input type="url" name="website"><br>
+ Email (optional): <input type="email" name="email"><br>
+ Message: <textarea name="message" rows="5" cols="45"></textarea><br>
+ <input type="submit">
+ </form>
+ </main>
+ </body>
+</html>