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