Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"html/template"
"log/slog"
"net/http"
"strings"
)
Expand Down Expand Up @@ -30,14 +31,18 @@ func registerRoutes(mux *http.ServeMux, store *Store) {
mux.HandleFunc("POST /lists/{token}/items/{id}/toggle-required", handleToggleRequired(store))
mux.HandleFunc("POST /lists/{token}/items/{id}/assignee", handleUpdateAssignee(store))
mux.HandleFunc("POST /lists/{token}/items/{id}/delete", handleDeleteItem(store))
mux.HandleFunc("POST /lists/{token}/delete", handleDeleteList(store))
}

func handleIndex(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
tmpl.ExecuteTemplate(w, "index.html", nil)
if err := tmpl.ExecuteTemplate(w, "index.html", nil); err != nil {
slog.Error("テンプレート描画エラー", "template", "index.html", "error", err)
http.Error(w, "テンプレートの描画に失敗しました", http.StatusInternalServerError)
}
}

func handleCreateList(store *Store) http.HandlerFunc {
Expand Down Expand Up @@ -65,7 +70,10 @@ func handleShowList(store *Store) http.HandlerFunc {
"List": l,
"ShareURL": "http://" + r.Host + "/lists/" + l.ShareToken,
}
tmpl.ExecuteTemplate(w, "list.html", data)
if err := tmpl.ExecuteTemplate(w, "list.html", data); err != nil {
slog.Error("テンプレート描画エラー", "template", "list.html", "error", err)
http.Error(w, "テンプレートの描画に失敗しました", http.StatusInternalServerError)
}
}
}

Expand Down Expand Up @@ -120,3 +128,14 @@ func handleDeleteItem(store *Store) http.HandlerFunc {
http.Redirect(w, r, "/lists/"+token, http.StatusSeeOther)
}
}

func handleDeleteList(store *Store) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
token := r.PathValue("token")
if !store.DeleteList(token) {
http.NotFound(w, r)
return
}
http.Redirect(w, r, "/", http.StatusSeeOther)
}
}
21 changes: 16 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
package main

import (
"fmt"
"log"
"log/slog"
"net/http"
"os"
)

// getEnv は環境変数を取得し、未設定の場合はデフォルト値を返す。
func getEnv(key, defaultVal string) string {
if v := os.Getenv(key); v != "" {
return v
}
return defaultVal
}

func main() {
store := NewStore()
mux := http.NewServeMux()
registerRoutes(mux, store)

addr := ":8080"
fmt.Println("BringIt server started on http://localhost" + addr)
log.Fatal(http.ListenAndServe(addr, mux))
addr := ":" + getEnv("PORT", "8080")
slog.Info("BringItサーバーを起動しました", "addr", "http://localhost"+addr)
if err := http.ListenAndServe(addr, mux); err != nil {
slog.Error("サーバーの起動に失敗しました", "error", err)
os.Exit(1)
}
}
11 changes: 6 additions & 5 deletions model.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ type List struct {

// Item represents a single item in a packing list.
type Item struct {
ID string
Name string
Assignee string
Required bool
Prepared bool
ID string
Name string
Assignee string
Required bool
Prepared bool
UpdatedAt time.Time // アイテムの最終更新日時
}
27 changes: 22 additions & 5 deletions store.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ func (s *Store) genID() string {

func generateToken() string {
b := make([]byte, 8)
rand.Read(b)
if _, err := rand.Read(b); err != nil {
panic("乱数生成に失敗しました: " + err.Error())
}
return hex.EncodeToString(b)
}

Expand Down Expand Up @@ -61,10 +63,11 @@ func (s *Store) AddItem(token, name, assignee string, required bool) *Item {
return nil
}
item := &Item{
ID: s.genID(),
Name: name,
Assignee: assignee,
Required: required,
ID: s.genID(),
Name: name,
Assignee: assignee,
Required: required,
UpdatedAt: time.Now(),
}
l.Items = append(l.Items, item)
return item
Expand All @@ -88,6 +91,7 @@ func (s *Store) TogglePrepared(token, itemID string) {
defer s.mu.Unlock()
if it := s.findItem(token, itemID); it != nil {
it.Prepared = !it.Prepared
it.UpdatedAt = time.Now()
}
}

Expand All @@ -96,6 +100,7 @@ func (s *Store) ToggleRequired(token, itemID string) {
defer s.mu.Unlock()
if it := s.findItem(token, itemID); it != nil {
it.Required = !it.Required
it.UpdatedAt = time.Now()
}
}

Expand All @@ -104,7 +109,19 @@ func (s *Store) UpdateAssignee(token, itemID, assignee string) {
defer s.mu.Unlock()
if it := s.findItem(token, itemID); it != nil {
it.Assignee = assignee
it.UpdatedAt = time.Now()
}
}

// DeleteList はトークンに対応するリストを削除する。削除に成功した場合は true を返す。
func (s *Store) DeleteList(token string) bool {
s.mu.Lock()
defer s.mu.Unlock()
if _, ok := s.lists[token]; !ok {
return false
}
delete(s.lists, token)
return true
}

func (s *Store) DeleteItem(token, itemID string) {
Expand Down
Loading