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

main.go (3726B)


      1 package main
      2 
      3 import (
      4 	"database/sql"
      5 	"fmt"
      6 	"html/template"
      7 	"log"
      8 	"net/http"
      9 	"os"
     10 	"time"
     11 
     12 	_ "github.com/ncruces/go-sqlite3/driver"
     13 	_ "github.com/ncruces/go-sqlite3/embed"
     14 
     15 	"github.com/pelletier/go-toml/v2"
     16 )
     17 
     18 type Config struct {
     19 	GetPath string
     20 	PostPath string
     21 	DBFile string
     22 	TemplateFile string
     23 }
     24 
     25 type Message struct {
     26 	Name string
     27 	Website string
     28 	Email string
     29 	Message string
     30 	Time time.Time
     31 }
     32 
     33 var cfg Config
     34 var database *sql.DB
     35 var message_template string
     36 
     37 func GetGuestbook(w http.ResponseWriter, r *http.Request) {
     38 
     39 	if r.Method != "GET" {
     40 		http.Error(w, "Unsupported method", http.StatusBadRequest)
     41 		return
     42 	}
     43 
     44 	// Pull database contents
     45 
     46 	rows, err := database.Query("SELECT name,website,email,message,time FROM Messages ORDER BY time DESC;")
     47 	
     48 	if err != nil {
     49 		http.Error(w, "Error reading SQL database", http.StatusInternalServerError)
     50 		log.Print(err)
     51 		return
     52 	}
     53 	
     54 	defer rows.Close()
     55 
     56 	messages := []*Message{}
     57 
     58 	for rows.Next() {
     59 		var m Message
     60 		err := rows.Scan(
     61 			&m.Name,
     62 			&m.Website,
     63 			&m.Email,
     64 			&m.Message,
     65 			&m.Time,
     66 		)
     67 
     68 		if err != nil {
     69 			http.Error(w, "Error reading SQL database", http.StatusInternalServerError)
     70 			log.Print(err)
     71 			return
     72 		}
     73 
     74 		messages = append(messages, &m)
     75 	}
     76 
     77 	rows.Close()
     78 
     79 	err = rows.Err()
     80 	if err != nil {
     81 		http.Error(w, "Error reading SQL database", http.StatusInternalServerError)
     82 		log.Print(err)
     83 		return
     84 	}
     85 
     86 	// Apply guestbook template
     87 
     88 	tmpl, err := template.New("guestbook").Parse(message_template)
     89 	if err != nil {
     90 		http.Error(w, "Error parsing template", http.StatusInternalServerError)
     91 		log.Print(err)
     92 		return
     93 	}
     94 	err = tmpl.Execute(w, messages)
     95 	if err != nil {
     96 		http.Error(w, "Error executing template", http.StatusInternalServerError)
     97 		log.Print(err)
     98 		return
     99 	}
    100 }
    101 
    102 func PostToGuestbook(w http.ResponseWriter, r *http.Request) {
    103 	
    104 	if r.Method != "POST" {
    105 		http.Error(w, "Unsupported method", http.StatusBadRequest)
    106 		return
    107 	}
    108 
    109 	err := r.ParseForm()
    110 	if err != nil {
    111 		http.Error(w, "Error parsing form response", http.StatusBadRequest)
    112 		log.Print(err)
    113 		return
    114 	}
    115 
    116 	name := r.PostForm.Get("name")
    117 	email := r.PostForm.Get("email")
    118 	website := r.PostForm.Get("website")
    119 	message := r.PostForm.Get("message")
    120 
    121 	stmt, err := database.Prepare("INSERT INTO Messages (name, email, website, message) VALUES (?, ?, ?, ?)")
    122 	if err != nil {
    123 		http.Error(w, "Error accessing database", http.StatusInternalServerError)
    124 		log.Print(err)
    125 		return
    126 	}
    127 	defer stmt.Close()
    128 
    129 	_, err = stmt.Exec(name, email, website, message)
    130 	if err != nil {
    131 		http.Error(w, "Error accessing database", http.StatusInternalServerError)
    132 		log.Print(err)
    133 		return
    134 	}
    135 }
    136 
    137 func main() {
    138 
    139 	var err error
    140 
    141 	config_str, err := os.ReadFile("config.toml")
    142 	if err != nil {
    143 		log.Fatal(err)
    144 	}
    145 	err = toml.Unmarshal(config_str, &cfg)
    146 	if err != nil {
    147 		log.Fatal(err)
    148 	}
    149 	
    150 	database, err = sql.Open("sqlite3", cfg.DBFile)
    151 
    152 	if err != nil {
    153 		log.Fatal(err)
    154 	}
    155 
    156 	err = database.Ping()
    157 	if err != nil {
    158 		log.Fatal(err)
    159 	}
    160 
    161 	_, table_check := database.Query("SELECT * FROM Messages;")
    162 
    163 	if table_check != nil {
    164 		_, err = database.Exec(`
    165 			CREATE TABLE Messages (
    166 				name VARCHAR(256) NOT NULL,
    167 				website VARCHAR(256),
    168 				email VARCHAR(256),
    169 				message VARCHAR(4096) NOT NULL,
    170 				time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
    171 			)	
    172 		`)
    173 		if err != nil {
    174 			log.Fatal(err)
    175 		}
    176 	}
    177 
    178 	message_template_bytes, err := os.ReadFile(cfg.TemplateFile)
    179 	if err != nil {
    180 		log.Fatal(err)
    181 	}
    182 	message_template = string(message_template_bytes)
    183 
    184 	http.HandleFunc(cfg.GetPath, GetGuestbook)
    185 	http.HandleFunc(cfg.PostPath, PostToGuestbook)
    186 	
    187 	fmt.Println("Starting server at port 48378")
    188 	if err := http.ListenAndServe(":48378", nil); err != nil {
    189 		log.Fatal(err)
    190 	}
    191 }