summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.json5
-rw-r--r--main.go169
-rw-r--r--root.template57
3 files changed, 231 insertions, 0 deletions
diff --git a/config.json b/config.json
new file mode 100644
index 0000000..ec59916
--- /dev/null
+++ b/config.json
@@ -0,0 +1,5 @@
+{
+ "listen": "localhost:8080",
+ "db": "user=julian dbname=noteman sslmode=disable",
+ "tmpl": ""
+}
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..dcb7d47
--- /dev/null
+++ b/main.go
@@ -0,0 +1,169 @@
+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, `
+ <html>
+ <p><b>Note:</b> %s</p>
+ <p><b>Location:</b> %s</p>
+ <p><b>Hash:</b> %s"</p>
+ <p><b>Ref:</b> %s</p>
+ </html>`, 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")
+}
diff --git a/root.template b/root.template
new file mode 100644
index 0000000..a492b7a
--- /dev/null
+++ b/root.template
@@ -0,0 +1,57 @@
+<html>
+ <head>
+ <title>Noteman</title>
+ <style>
+ .box {
+ padding: 10px;
+ border: 1px solid black;
+ }
+ #name {
+ width: 300px;
+ }
+ td {
+ padding: 0 15px
+ }
+ </style>
+ </head>
+ <body>
+ <div class="box">
+ <table>
+ <tr>
+ <th>Location format</th>
+ </tr>
+ <tr>
+ <td>Page</td>
+ <td>P</td>
+ </tr>
+ <tr>
+ <td>Book</td>
+ <td>B[id]S[page]</td>
+ </tr>
+ <tr>
+ <td>Notes git repo</td>
+ <td>G</td>
+ </tr>
+ </table><br><br>
+ <form method="POST">
+ <label for="name">Name</label>
+ <input id="name" name="name" type="text">
+ <label for="location">Location</label>
+ <input id="location" name="location" type="text">
+ <input type="submit" value="go">
+ </form>
+ </div><br><br>
+ <table>
+ <tr>
+ <th>Ref</th>
+ <th>Name</th>
+ </tr>
+ {{ range .Notes }}
+ <tr>
+ <td>#{{ .Hash }}/{{ .Location }}</td>
+ <td>{{ .Name }}</td>
+ </tr>
+ {{ end }}
+ </table>
+ </body>
+</html>