package main import ( "fmt" "log" "net/http" "strconv" "text/template" "time" "github.com/gorilla/mux" ) type Server struct { template *template.Template srv *Service } func parseTaskFromForm(r *http.Request) (*Entry, error) { err := r.ParseForm() if err != nil { return nil, err } from, err := TimeFromString(r.PostFormValue("from")) if err != nil { return nil, err } to, err := TimeFromString(r.PostFormValue("to")) if err != nil { return nil, err } var id int64 if r.PostFormValue("id") == "" { id = -1 } else { id, err = strconv.ParseInt(r.PostFormValue("id"), 10, 32) if err != nil { return nil, err } } task := &Entry { Id: int(id), From: from, To: to, Comment: r.PostFormValue("comment"), } return task, nil } func (s *Server) renderTemplate(w http.ResponseWriter, name string, page interface{}) { tmpl, err := template.ParseFiles("templates/index.html", "templates/parts/entry.html", "templates/parts/entryRows.html") if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } err = tmpl.ExecuteTemplate(w, name, page) if err != nil { log.Println(err) fmt.Fprintf(w, "RENDER ERROR: %s\n", err.Error()) } } func writeError(w http.ResponseWriter, err string, code int) { log.Println(err) w.WriteHeader(code) fmt.Fprintf(w, "%d: %s\n", code, err) return } func (s *Server) rootHandle(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { w.WriteHeader(http.StatusNotFound) fmt.Fprintln(w, "Not found 404") return } page, err := s.srv.GetRootPage() if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } s.renderTemplate(w, "index.html", page) } func (s *Server) postStart(w http.ResponseWriter, r *http.Request) { now := time.Now() task, err := parseTaskFromForm(r) if err != nil { writeError(w, err.Error(), http.StatusBadRequest) return } _, err = s.srv.Db.StartNewEntry(now, task) if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } page, err := s.srv.GetEntryPage(nil) if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } w.Header().Add("HX-Trigger", "changedEntries") s.renderTemplate(w, "entry.html", page) } func (s *Server) postStop(w http.ResponseWriter, r *http.Request) { now := time.Now() task, err := parseTaskFromForm(r) if err != nil { writeError(w, err.Error(), http.StatusBadRequest) return } _, err = s.srv.Db.SaveEntry(task) if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } err = s.srv.Db.StopEntry(now) if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } w.Header().Add("HX-Trigger", "changedEntries") s.getTracking(w, r) } func (s *Server) getTracking(w http.ResponseWriter, _ *http.Request) { page, err := s.srv.GetEntryPage(nil) if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } s.renderTemplate(w, "entry.html", page) } func (s *Server) putSave(w http.ResponseWriter, r *http.Request) { task, err := parseTaskFromForm(r) if err != nil { writeError(w, err.Error(), http.StatusBadRequest) return } task, err = s.srv.Db.SaveEntry(task) if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } var detached *Entry = nil if r.URL.Query().Get("detached") == "true" { detached = task } page, err := s.srv.GetEntryPage(detached) if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } w.Header().Add("HX-Trigger", "changedEntries") s.renderTemplate(w, "entry.html", page) } func (s *Server) getNew(w http.ResponseWriter, _ *http.Request) { task := &Entry { Id: -1, From: nil, To: nil, Tag: nil, Comment: "", } page, err := s.srv.GetEntryPage(task) if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } s.renderTemplate(w, "entry.html", page) } func (s *Server) getEdit(w http.ResponseWriter, r *http.Request) { idLarge, err := strconv.ParseInt(r.URL.Query().Get("id"), 10, 32) if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } id := int(idLarge) entries, err := s.srv.Db.QueryEntry(&id) if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } if len(entries) < 1 { writeError(w, "could not find entry by id", http.StatusNotFound) return } page, err := s.srv.GetEntryPage(entries[0]) if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } s.renderTemplate(w, "entry.html", page) } func (s *Server) getEntries(w http.ResponseWriter, _ *http.Request) { page, err := s.srv.GetRootPage() if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } s.renderTemplate(w, "entryRows.html", page) } func (s *Server) getTasksList(w http.ResponseWriter, _ *http.Request) { page, err := s.srv.GetTasksPage() if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } s.renderTemplate(w, "tasksPage.html", page) } func main() { fmt.Println("Hello world!") s := Server{ template: nil, srv: NewService(NewInMemDb()), } r := mux.NewRouter() r.HandleFunc("/start", s.postStart).Methods("POST") r.HandleFunc("/newDetached", s.getNew).Methods("GET") r.HandleFunc("/tracking", s.getTracking).Methods("GET") r.HandleFunc("/stop", s.postStop).Methods("POST") r.HandleFunc("/save", s.putSave).Methods("PUT") r.HandleFunc("/entryRows", s.getEntries).Methods("GET") r.HandleFunc("/edit", s.getEdit).Methods("GET") r.HandleFunc("/task/list", s.getTasksList).Methods("GET") r.HandleFunc("/", s.rootHandle) http.Handle("/", r) log.Fatal(http.ListenAndServe(":8080", nil)) }