diff --git a/model/time.go b/model/time.go index baf3ce6..e0c399c 100644 --- a/model/time.go +++ b/model/time.go @@ -1,34 +1,42 @@ package model import ( - "time" - "errors" - sqldriver "database/sql/driver" + sqldriver "database/sql/driver" + "errors" + "time" ) type Time time.Time func MakeTimeNow() Time { - return Time(time.Now().UTC()) + return Time(time.Now().UTC()) } func (self Time) AsUTCString() string { - return time.Time(self).Format(time.RFC3339) + return time.Time(self).Format(time.RFC3339) } func (self Time) Value() (sqldriver.Value, error) { - return self.AsUTCString(), nil + return self.AsUTCString(), nil } func (self *Time) Scan(value any) error { - if valueStr, ok := value.(string); ok { - parsedTime, err := time.Parse(time.RFC3339, valueStr) - if err == nil { - *self = Time(parsedTime.UTC()) - } - return err - } else { - return errors.New("Expected a string") - } + if valueStr, ok := value.(string); ok { + parsedTime, err := time.Parse(time.RFC3339, valueStr) + if err == nil { + *self = Time(parsedTime.UTC()) + } + return err + } else { + return errors.New("Expected a string") + } +} + +func (from Time) Compare(to Time) int { + return time.Time(from).Compare(time.Time(to)) +} + +func (self *Time) Add(duration time.Duration) { + *self = Time(time.Time(*self).Add(duration)) } diff --git a/model/timer.go b/model/timer.go index 4c81962..a47369d 100644 --- a/model/timer.go +++ b/model/timer.go @@ -1,9 +1,12 @@ package model type Timer struct { - Id UUID - Name string - StartTime Time - EndTime Time + Id UUID + Name string + StartTime Time + EndTime Time } +func (self Timer) IsFinished() bool { + return MakeTimeNow().Compare(self.EndTime) >= 0 +} diff --git a/model/uuid.go b/model/uuid.go index 4a25e41..6cade35 100644 --- a/model/uuid.go +++ b/model/uuid.go @@ -1,28 +1,27 @@ -package model - -import ( - sqldriver "database/sql/driver" - "github.com/google/uuid" -) - -type UUID struct { - payload uuid.UUID -} - -func MakeUUID() UUID { - id, _ := uuid.NewV7() - return UUID { payload: id } -} - -func (self UUID) Value() (sqldriver.Value, error) { - return self.payload[:], nil -} - -func (self *UUID) Scan(value any) error { - return self.payload.Scan(value) -} - -func (self UUID) String() string { - return self.payload.String() -} - +package model + +import ( + sqldriver "database/sql/driver" + "github.com/google/uuid" +) + +type UUID struct { + payload uuid.UUID +} + +func MakeUUID() UUID { + id, _ := uuid.NewV7() + return UUID{payload: id} +} + +func (self UUID) Value() (sqldriver.Value, error) { + return self.payload[:], nil +} + +func (self *UUID) Scan(value any) error { + return self.payload.Scan(value) +} + +func (self UUID) String() string { + return self.payload.String() +} diff --git a/timer.go b/timer.go index 587d24e..e76f950 100644 --- a/timer.go +++ b/timer.go @@ -3,6 +3,7 @@ package main import ( "context" "database/sql" + "errors" "log" "net/http" "strconv" @@ -50,12 +51,12 @@ func initializeDatabase(db *sql.DB) error { return err } - err = insertTimer(tx, "My timer", 600) + err = insertTimer(tx, "My timer", 6) if err != nil { return err } - err = insertTimer(tx, "My timer2", 600) + err = insertTimer(tx, "My timer2", 6) if err != nil { return err } @@ -86,7 +87,7 @@ func initializeDatabase(db *sql.DB) error { return err } - _, err = tx.Exec(`INSERT INTO Timer VALUES ($1, $2, $3, $4)`, userId, "steven", userUuidStr, password) + _, err = tx.Exec(`INSERT INTO User VALUES ($1, $2, $3, $4)`, userId, "steven", userUuidStr, password) if err != nil { return err } @@ -139,7 +140,17 @@ func deleteTimer(db *sql.DB, idStr string) bool { } affected, err := res.RowsAffected() - return err == nil && affected > 0 + return err == nil && affected == 1 +} + +func updateTimerEndTime(db *sql.DB, id model.UUID, endTime model.Time) bool { + res, err := db.Exec("UPDATE Timer SET EndTime=$1 WHERE Id=$2", endTime, id) + if err != nil { + return false + } + + affected, err := res.RowsAffected() + return err == nil && affected == 1 } type Session struct { @@ -174,6 +185,65 @@ func (server *MyServer) handleTimer(w http.ResponseWriter, r *http.Request) { } } +func parseDuration(value string) (time.Duration, error) { + const nullDuration = time.Duration(0) + if len(value) == 0 { + return nullDuration, errors.New("Empty duration string") + } + + var unit time.Duration + switch value[len(value)-1] { + case 's': + unit = time.Second + case 'm': + unit = time.Minute + case 'h': + unit = time.Hour + case 'd': + unit = time.Duration(24) * time.Hour + case 'w': + unit = time.Duration(24*7) * time.Hour + default: + return nullDuration, errors.New("Invalid duration format") + } + + amount, err := strconv.ParseInt(value[0:len(value)-1], 10, 64) + if err != nil || amount < 0 { + return nullDuration, errors.New("Invalid duration value") + } + + return time.Duration(amount) * unit, nil +} + +func (server *MyServer) handleTimerAddTime(w http.ResponseWriter, r *http.Request) { + timer := queryTimer(server.db, r.PathValue("timerId")) + if timer == nil { + server.handleNotFound(w, r) + return + } + + if timer.IsFinished() { + w.WriteHeader(http.StatusBadRequest) + return + } + + duration, err := parseDuration(r.PathValue("timeToAdd")) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + timer.EndTime.Add(duration) + res := updateTimerEndTime(server.db, timer.Id, timer.EndTime) + if !res { + w.WriteHeader(http.StatusBadRequest) + return + } + + view.TimerView(*timer).Render(context.Background(), w) +} + func (server *MyServer) handleDeleteTimer(w http.ResponseWriter, r *http.Request) { success := deleteTimer(server.db, r.PathValue("timerId")) if !success { @@ -244,6 +314,7 @@ func main() { http.Handle("GET /static/", http.StripPrefix("/static/", fs)) http.HandleFunc("GET /timer/{timerId}", myServer.handleTimer) + http.HandleFunc("POST /timer/{timerId}/addTime/{timeToAdd}", myServer.handleTimerAddTime) http.HandleFunc("DELETE /timer/{timerId}", myServer.handleDeleteTimer) http.HandleFunc("PUT /timer", myServer.handlePutTimer) http.HandleFunc("GET /", myServer.handleMain) diff --git a/view/timer.templ b/view/timer.templ index 8d20933..a447d01 100644 --- a/view/timer.templ +++ b/view/timer.templ @@ -1,26 +1,43 @@ package view import ( - "stevenlr.com/timer/model" + "fmt" + "stevenlr.com/timer/model" ) templ TimerView(timer model.Timer) { -
Start time:
End time:
- Total time:
-
- Remaining time:
-
Start time:
End time:
+ Total time:
+
+ Remaining time:
+
+ + + + + + + + + +
+ } +") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("