package main import ( "embed" "log" "net/http" "strconv" "jxs.me/todogo/database" "jxs.me/todogo/views" ) //go:embed public/* var publicFS embed.FS type server struct { views *views.Views router *http.ServeMux db *database.DB } func newServer() *server { srv := &server{ views: views.New(), router: http.NewServeMux(), db: database.New(), } srv.routes() return srv } func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.router.ServeHTTP(w, r) } func (s *server) HandleList() http.HandlerFunc { type viewModel struct { Todos []database.Todo Only string Incomplete int } list := s.views.Renderer("list.html") return func(w http.ResponseWriter, r *http.Request) { q := r.URL.Query() only := q.Get("only") var dbTodos []database.Todo var err error switch only { case "active": dbTodos, err = s.db.GetTodosByCompleted(false) case "completed": dbTodos, err = s.db.GetTodosByCompleted(true) default: dbTodos, err = s.db.GetAllTodos() } if err != nil { http.Error(w, "Failed to fetch todos", http.StatusInternalServerError) return } incomplete := s.db.GetIncompleteTodosCount() vm := viewModel{ Todos: dbTodos, Only: only, Incomplete: incomplete, } list(w, r, vm) } } func (s *server) HandleCreate() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { title := r.Form.Get("title") if err := s.db.InsertTodo(title); err != nil { http.Error(w, "failed to insert", http.StatusInternalServerError) return } http.Redirect(w, r, "/", http.StatusSeeOther) } } func (s *server) HandleToggle() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { rawId := r.Form.Get("id") id, err := strconv.Atoi(rawId) if err != nil { http.Error(w, "Bad id", http.StatusBadRequest) return } err = s.db.ToggleTodo(id) if err != nil { http.Error(w, "failed to toggle", http.StatusInternalServerError) return } http.Redirect(w, r, "/", http.StatusSeeOther) } } func (s *server) HandleDestroy() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { rawId := r.Form.Get("id") id, err := strconv.Atoi(rawId) if err != nil { http.Error(w, "Bad id", http.StatusBadRequest) return } err = s.db.DeleteTodo(id) if err != nil { http.Error(w, "failed to toggle", http.StatusInternalServerError) return } http.Redirect(w, r, "/", http.StatusSeeOther) } } func (s *server) HandleClear() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { err := s.db.DeleteCompletedTodos() if err != nil { http.Error(w, "failed to clear", http.StatusInternalServerError) return } http.Redirect(w, r, "/", http.StatusSeeOther) } } func (s *server) routes() { s.router.Handle("/public/", http.FileServer(http.FS(publicFS))) s.router.HandleFunc("/", WithMethod("GET", s.HandleList())) s.router.HandleFunc("/todo", WithForm(WithMethod("POST", s.HandleCreate()))) s.router.HandleFunc("/toggle", WithForm(WithMethod("POST", s.HandleToggle()))) s.router.HandleFunc("/destroy", WithForm(WithMethod("POST", s.HandleDestroy()))) s.router.HandleFunc("/clear", WithMethod("POST", s.HandleClear())) } func run() error { srv := newServer() log.Print("Running on 8080") return http.ListenAndServe(":8080", srv) } func main() { log.Fatal(run()) }