DB migration & timer token
This commit is contained in:
@ -6,6 +6,7 @@ type Timer struct {
|
||||
StartTime Time
|
||||
EndTime Time
|
||||
Owner UUID
|
||||
Token string
|
||||
}
|
||||
|
||||
func (self Timer) IsFinished() bool {
|
||||
|
@ -9,3 +9,4 @@ body {
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
|
104
timer.go
104
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,19 @@ func initializeDatabase(db *sql.DB) error {
|
||||
return err
|
||||
}
|
||||
|
||||
userPasswordClear := "steven"
|
||||
|
||||
password, err := bcrypt.GenerateFromPassword([]byte(userUuidStr+userPasswordClear), bcrypt.MinCost)
|
||||
userName := "admin"
|
||||
userPassword := "admin"
|
||||
salt, err := generateRandomString(33)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.Exec(`INSERT INTO User VALUES ($1, $2, $3, $4)`, userId, "steven", userUuidStr, password)
|
||||
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
|
||||
}
|
||||
@ -88,6 +105,50 @@ func initializeDatabase(db *sql.DB) error {
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func migrateDatabaseV2(db *sql.DB) error {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
_, err = tx.Exec(`PRAGMA user_version = 2`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.Exec("CREATE INDEX TimerTokenIndex ON Timer(Token)")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ templ LoginFormError(currentUser *model.User, err string) {
|
||||
<input type="password" name="password" placeholder="Password" />
|
||||
<button type="submit">Sign in</button>
|
||||
if err != "" {
|
||||
<span style="color:red;">{ err }</span>
|
||||
<span class="error">{ err }</span>
|
||||
}
|
||||
</p>
|
||||
</form>
|
||||
|
@ -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("<span style=\"color:red;\">")
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<span class=\"error\">")
|
||||
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 {
|
||||
|
@ -37,11 +37,11 @@ templ TimerCreateForm(timerName string, err string) {
|
||||
|
||||
templ TimersList(timers []model.Timer, isSignedIn bool) {
|
||||
<div class="timers-list">
|
||||
<h1>Timers</h1>
|
||||
for _, t := range timers {
|
||||
@timer(t)
|
||||
}
|
||||
if isSignedIn {
|
||||
<h1>Timers</h1>
|
||||
for _, t := range timers {
|
||||
@timer(t)
|
||||
}
|
||||
<h4>Create timer</h4>
|
||||
@TimerCreateForm("", "")
|
||||
}
|
||||
|
@ -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("<div class=\"timers-list\"><h1>Timers</h1>")
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"timers-list\">")
|
||||
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("<h1>Timers</h1>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
if isSignedIn {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h4>Create timer</h4>")
|
||||
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(" <h4>Create timer</h4>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
Reference in New Issue
Block a user