You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

161 lines
3.3 KiB

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())
}