mirror of https://git.sr.ht/~statianzo/todogo
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.
159 lines
3.3 KiB
159 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)
|
|
}
|
|
err = s.db.ToggleTodo(id)
|
|
|
|
if err != nil {
|
|
http.Error(w, "failed to toggle", http.StatusInternalServerError)
|
|
}
|
|
|
|
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())
|
|
}
|