home | blog | art | now | git gpg | email | rss

guestbook

Code for a website guestbook. Written in Go.
git clone git://pollux.codes/git/guestbook.git
Log | Files | Refs | README | LICENSE
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:
Ago.mod | 18++++++++++++++++++
Ago.sum | 47+++++++++++++++++++++++++++++++++++++++++++++++
Amain.go | 151++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/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>