From 4e715ab1e1053698c3eb02c13cc726ce9bd26667 Mon Sep 17 00:00:00 2001
From: Steven Le Rouzic
Date: Tue, 16 Apr 2024 22:48:07 +0200
Subject: DB migration & timer token
---
model/timer.go | 1 +
static/style.css | 1 +
timer.db | Bin 0 -> 28672 bytes
timer.go | 102 ++++++++++++++++++++++++++++++++++------------
view/login.templ | 2 +-
view/login_templ.go | 4 +-
view/timers_list.templ | 8 ++--
view/timers_list_templ.go | 16 +++++---
8 files changed, 96 insertions(+), 38 deletions(-)
create mode 100644 timer.db
diff --git a/model/timer.go b/model/timer.go
index 1420be5..27e46da 100644
--- a/model/timer.go
+++ b/model/timer.go
@@ -6,6 +6,7 @@ type Timer struct {
StartTime Time
EndTime Time
Owner UUID
+ Token string
}
func (self Timer) IsFinished() bool {
diff --git a/static/style.css b/static/style.css
index 17df2b0..f153f6e 100644
--- a/static/style.css
+++ b/static/style.css
@@ -9,3 +9,4 @@ body {
.error {
color: red;
}
+
diff --git a/timer.db b/timer.db
new file mode 100644
index 0000000..bbcc582
Binary files /dev/null and b/timer.db differ
diff --git a/timer.go b/timer.go
index ef65b8b..5209e73 100644
--- a/timer.go
+++ b/timer.go
@@ -20,16 +20,34 @@ import (
"stevenlr.com/timer/view"
)
+func generateRandomString(len int) (string, error) {
+ bin := make([]byte, len)
+ _, err := rand.Read(bin)
+ if err != nil {
+ return "", err
+ }
+ return base64.StdEncoding.EncodeToString(bin), nil
+}
+
+func generateSessionId() (string, error) {
+ return generateRandomString(66)
+}
+
+func generateTimerToken() (string, error) {
+ return generateRandomString(66)
+}
+
func insertTimer(tx *sql.Tx, name string, seconds int, ownerId model.UUID) error {
now := model.MakeTimeNow()
end := model.Time(time.Time(now).Add(time.Duration(seconds) * time.Second))
id := model.MakeUUID()
+ token, _ := generateTimerToken()
_, err := tx.Exec(`
- INSERT INTO Timer VALUES ($1, $2, $3, $4, $5)`, id, name, now, end, ownerId)
+ INSERT INTO Timer VALUES ($1, $2, $3, $4, $5, $6)`, id, name, now, end, ownerId, token)
return err
}
-func initializeDatabase(db *sql.DB) error {
+func initializeDatabaseV1(db *sql.DB) error {
tx, err := db.Begin()
if err != nil {
return err
@@ -48,19 +66,13 @@ func initializeDatabase(db *sql.DB) error {
StartTime TEXT NOT NULL,
EndTime TEXT NOT NULL,
Owner BLOB NOT NULL,
- PRIMARY KEY (id)
+ Token TEXT NOT NULL UNIQUE,
+ PRIMARY KEY (Id)
)`)
if err != nil {
return err
}
- userUuidStr := "7015cee7-89a5-4057-b7c9-7e0128ad5086"
- var userId model.UUID
- err = userId.Scan(userUuidStr)
- if err != nil {
- return err
- }
-
_, err = tx.Exec(`
CREATE TABLE User (
Id BLOB NOT NULL UNIQUE,
@@ -73,14 +85,39 @@ func initializeDatabase(db *sql.DB) error {
return err
}
- userPasswordClear := "steven"
+ userName := "admin"
+ userPassword := "admin"
+ salt, err := generateRandomString(33)
+ if err != nil {
+ return err
+ }
+
+ password, err := bcrypt.GenerateFromPassword([]byte(salt+userPassword), bcrypt.MinCost)
+ if err != nil {
+ return err
+ }
+
+ _, err = tx.Exec(`INSERT INTO User VALUES ($1, $2, $3, $4)`, model.MakeUUID(), userName, salt, password)
+ if err != nil {
+ return err
+ }
+
+ return tx.Commit()
+}
+
+func migrateDatabaseV2(db *sql.DB) error {
+ tx, err := db.Begin()
+ if err != nil {
+ return err
+ }
+ defer tx.Rollback()
- password, err := bcrypt.GenerateFromPassword([]byte(userUuidStr+userPasswordClear), bcrypt.MinCost)
+ _, err = tx.Exec(`PRAGMA user_version = 2`)
if err != nil {
return err
}
- _, err = tx.Exec(`INSERT INTO User VALUES ($1, $2, $3, $4)`, userId, "steven", userUuidStr, password)
+ _, err = tx.Exec("CREATE INDEX TimerTokenIndex ON Timer(Token)")
if err != nil {
return err
}
@@ -88,6 +125,30 @@ func initializeDatabase(db *sql.DB) error {
return tx.Commit()
}
+func initializeDatabase(db *sql.DB) error {
+ initialVersion := 0
+ row := db.QueryRow("PRAGMA user_version")
+ row.Scan(&initialVersion)
+
+ if initialVersion < 1 {
+ log.Println("Initializing DB V1")
+ err := initializeDatabaseV1(db)
+ if err != nil {
+ return err
+ }
+ }
+
+ if initialVersion < 2 {
+ log.Println("Migrating DB to V2")
+ err := migrateDatabaseV2(db)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
func queryAllTimers(db *sql.DB, owner model.UUID) []model.Timer {
rows, err := db.Query("SELECT Id, Name FROM Timer WHERE Owner=$1", owner)
if err != nil {
@@ -135,10 +196,10 @@ func queryTimer(db *sql.DB, idStr string, userId model.UUID) *model.Timer {
return nil
}
- row := db.QueryRow("SELECT Id, Name, StartTime, EndTime, Owner FROM Timer WHERE Id=$1 AND Owner=$2", id, userId)
+ row := db.QueryRow("SELECT Id, Name, StartTime, EndTime, Owner, Token FROM Timer WHERE Id=$1 AND Owner=$2", id, userId)
var t model.Timer
- if err := row.Scan(&t.Id, &t.Name, &t.StartTime, &t.EndTime, &t.Owner); err == nil {
+ if err := row.Scan(&t.Id, &t.Name, &t.StartTime, &t.EndTime, &t.Owner, &t.Token); err == nil {
return &t
}
@@ -372,15 +433,6 @@ func (server *MyServer) handlePutTimer(w http.ResponseWriter, r *http.Request) {
view.TimersList(timers, user != nil).Render(context.Background(), w)
}
-func generateSessionId() (string, error) {
- bin := make([]byte, 64)
- _, err := rand.Read(bin)
- if err != nil {
- return "", err
- }
- return base64.StdEncoding.EncodeToString(bin), nil
-}
-
func (server *MyServer) handlePostLogin(w http.ResponseWriter, r *http.Request) {
if server.findCurrentUser(w, r) != nil {
w.Header().Add("HX-Redirect", "/")
@@ -433,7 +485,7 @@ func (server *MyServer) handlePostLogout(w http.ResponseWriter, r *http.Request)
func main() {
log.Println("Starting...")
- db, err := sql.Open("sqlite3", ":memory:")
+ db, err := sql.Open("sqlite3", "file:timer.db")
if err != nil {
log.Fatalln(err)
}
diff --git a/view/login.templ b/view/login.templ
index f8ea6e3..3450e01 100644
--- a/view/login.templ
+++ b/view/login.templ
@@ -13,7 +13,7 @@ templ LoginFormError(currentUser *model.User, err string) {
if err != "" {
- { err }
+ { err }
}
diff --git a/view/login_templ.go b/view/login_templ.go
index db3d53e..0a5c79b 100644
--- a/view/login_templ.go
+++ b/view/login_templ.go
@@ -37,14 +37,14 @@ func LoginFormError(currentUser *model.User, err string) templ.Component {
return templ_7745c5c3_Err
}
if err != "" {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(err)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\login.templ`, Line: 16, Col: 50}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\login.templ`, Line: 16, Col: 45}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
diff --git a/view/timers_list.templ b/view/timers_list.templ
index c68979c..3deb7e3 100644
--- a/view/timers_list.templ
+++ b/view/timers_list.templ
@@ -37,11 +37,11 @@ templ TimerCreateForm(timerName string, err string) {
templ TimersList(timers []model.Timer, isSignedIn bool) {
-
Timers
- for _, t := range timers {
- @timer(t)
- }
if isSignedIn {
+
Timers
+ for _, t := range timers {
+ @timer(t)
+ }
Create timer
@TimerCreateForm("", "")
}
diff --git a/view/timers_list_templ.go b/view/timers_list_templ.go
index 007febe..60ce8ac 100644
--- a/view/timers_list_templ.go
+++ b/view/timers_list_templ.go
@@ -147,18 +147,22 @@ func TimersList(timers []model.Timer, isSignedIn bool) templ.Component {
templ_7745c5c3_Var8 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Timers
")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- for _, t := range timers {
- templ_7745c5c3_Err = timer(t).Render(ctx, templ_7745c5c3_Buffer)
+ if isSignedIn {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Timers
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- }
- if isSignedIn {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Create timer
")
+ for _, t := range timers {
+ templ_7745c5c3_Err = timer(t).Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" Create timer
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
--
cgit