package main import ( "net/http" "flag" "fmt" "strings" "crypto/md5" "encoding/json" "os" "html/template" "log" "path" _"github.com/lib/pq" "github.com/jmoiron/sqlx" ) type Config struct { Listen string `json:"listen"` DBStr string `json:"db"` TmplPath string `json:"tmpl"` } type Server struct { mux *http.ServeMux conf *Config db *sqlx.DB } type note struct { Hash string Name string Location string } const scheme = ` CREATE TABLE IF NOT EXISTS notes ( hash VARCHAR(4) PRIMARY KEY, name TEXT NOT NULL, location TEXT NOT NULL, available BOOL DEFAULT True ) ` func main() { confFlag := flag.String("c", "config.json", "config file") flag.Parse() f, err := os.Open(*confFlag) if err != nil { log.Fatal(err) } var conf Config err = json.NewDecoder(f).Decode(&conf) if err != nil { log.Fatal(err) } s := NewServer(&conf) if err != nil { log.Fatal(err) } err = s.Connect() if err != nil { log.Fatal(err) } log.Fatal(s.Run()) } func NewServer(conf *Config) *Server { s := &Server{} s.mux = http.NewServeMux() s.mux.HandleFunc("/", s.httpRoot) s.conf = conf return s } func (s *Server) Connect() error { var err error s.db, err = sqlx.Connect("postgres", s.conf.DBStr) if err != nil { return err } s.db.MustExec(scheme) return nil } func (s *Server) Run() error { log.Printf("Listening on %s", s.conf.Listen) return http.ListenAndServe(s.conf.Listen, s.mux) } func (s *Server) renderTemplate(w http.ResponseWriter, data interface{}, pathname string) error { tmpl, err := template.ParseFiles(path.Join(s.conf.TmplPath, pathname)) if err != nil { log.Print(err) return err } err = tmpl.Execute(w, data) if err != nil { log.Print(err) } return err } func (s *Server) httpCreateNode(w http.ResponseWriter, r *http.Request) { var ( name = r.FormValue("name") location = r.FormValue("location") ) hash := fmt.Sprintf("%x", md5.Sum([]byte(name)))[0:4] _, err := s.db.ExecContext(r.Context(), ` INSERT INTO notes (hash, name, location) VALUES ($1, $2, $3)`, hash, name, location) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } var ref strings.Builder fmt.Fprintf(&ref, "#%s", hash) if location != "" { fmt.Fprintf(&ref, "/%s", location) } fmt.Fprintf(w, `

Note: %s

Location: %s

Hash: %s"

Ref: %s

`, name, location, hash, ref.String()) } func (s *Server) httpRoot(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { w.WriteHeader(http.StatusNotFound) return } if r.Method == "POST" { s.httpCreateNode(w, r) return } var page struct { Notes []note Msg string } err := s.db.SelectContext(r.Context(), &page.Notes, ` SELECT hash, name, location FROM notes WHERE available = True`) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } s.renderTemplate(w, &page, "root.template") }