diff options
Diffstat (limited to 'main.go')
-rw-r--r-- | main.go | 86 |
1 files changed, 75 insertions, 11 deletions
@@ -1,24 +1,25 @@ package main import ( + "bufio" "crypto/md5" - "mime" - "io/ioutil" - "io" "encoding/json" - "sync" "flag" "fmt" "html/template" + "io" + "io/ioutil" "log" - "strconv" + "mime" "net/http" "os" "path" + "strconv" "strings" + "sync" "github.com/jmoiron/sqlx" - _"github.com/lib/pq" + "github.com/lib/pq" ) type Config struct { @@ -32,6 +33,8 @@ type Config struct { MaxPages uint `json:"max_pages"` } +type cmdFunction func (w http.ResponseWriter, r *http.Request, args []string) + type Server struct { mux *http.ServeMux conf *Config @@ -39,6 +42,8 @@ type Server struct { fslock sync.RWMutex filestore map[string][]string + + cmds map[string]cmdFunction } type note struct { @@ -103,6 +108,10 @@ func NewServer(conf *Config) *Server { s.filestore = map[string][]string{} + s.cmds = map[string]cmdFunction{} + s.addCmd("a", s.cmdCreateNode) + s.addCmd("c", s.cmdChangeLoc) + return s } @@ -154,6 +163,7 @@ func (s *Server) httpLog(r *http.Request, format string, args... interface{}) { } func (s *Server) Error(w http.ResponseWriter, r *http.Request, err string, code int) { + // Do not leak internal errors if code < 500 { http.Error(w, err, code) } else { @@ -163,10 +173,14 @@ func (s *Server) Error(w http.ResponseWriter, r *http.Request, err string, code s.httpLog(r, "ERR: %s", err) } -func (s *Server) httpCreateNode(w http.ResponseWriter, r *http.Request) { +func (s *Server) cmdCreateNode(w http.ResponseWriter, r *http.Request, args []string) { + if len(args) < 3 { + s.Error(w, r, "Please specify both name and location", http.StatusBadRequest) + return + } var ( - name = r.FormValue("name") - location = r.FormValue("location") + name = args[1] + location = args[2] ) hash := fmt.Sprintf("%x", md5.Sum([]byte(name)))[0:4] @@ -196,6 +210,56 @@ func (s *Server) httpCreateNode(w http.ResponseWriter, r *http.Request) { </html>`, name, location, hash, ref.String()) } +func (s *Server) cmdChangeLoc(w http.ResponseWriter, r *http.Request, args []string) { + argc := len(args) + if argc < 3 { + s.Error(w, r, "Please specify both id and new location", http.StatusBadRequest) + return + } + + var ( + hashes = args[1:argc-1] + loc = args[argc-1] + ) + + _, err := s.db.ExecContext(r.Context(), ` + UPDATE notes SET location = $1 WHERE hash = ANY($2)`, + loc, pq.Array(hashes)) + if err != nil { + s.Error(w, r, err.Error(), http.StatusInternalServerError) + return + } + + http.Redirect(w, r, "/", http.StatusSeeOther) +} + +func (s *Server) addCmd(name string, f cmdFunction) { + s.cmds[name] = f +} + +func (s *Server) httpRunCmd(w http.ResponseWriter, r *http.Request) { + cmd := r.FormValue("cmd") + + scanner := bufio.NewScanner(strings.NewReader(cmd)) + scanner.Split(bufio.ScanWords) + var words []string + for scanner.Scan() { + words = append(words, scanner.Text()) + } + if err := scanner.Err(); err != nil { + s.Error(w, r, err.Error(), http.StatusInternalServerError) + return + } + + cmd = strings.ToLower(words[0]) + f, ok := s.cmds[cmd] + if !ok { + s.Error(w, r, fmt.Sprintf("No such command '%s'", cmd), http.StatusBadRequest) + return + } + f(w, r, words) +} + func (s *Server) httpRoot(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { w.WriteHeader(http.StatusNotFound) @@ -203,7 +267,7 @@ func (s *Server) httpRoot(w http.ResponseWriter, r *http.Request) { } if r.Method == "POST" { - s.httpCreateNode(w, r) + s.httpRunCmd(w, r) return } @@ -217,7 +281,7 @@ func (s *Server) httpRoot(w http.ResponseWriter, r *http.Request) { page.Root = s.conf.Root err := s.db.SelectContext(r.Context(), &page.Notes, ` - SELECT hash, name, location FROM notes WHERE available = True`) + SELECT hash, name, location FROM notes WHERE available = True ORDER BY name`) if err != nil { s.Error(w, r, err.Error(), http.StatusInternalServerError) return |