diff --git a/.gitignore b/.gitignore index f8e7284..145574a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ golias gralias store.json config.toml +gralias.db diff --git a/gralias.db b/gralias.db deleted file mode 100644 index d891687..0000000 Binary files a/gralias.db and /dev/null differ diff --git a/handlers/auth.go b/handlers/auth.go index 0f6afbc..6f8756e 100644 --- a/handlers/auth.go +++ b/handlers/auth.go @@ -75,6 +75,7 @@ func HandleFrontLogin(w http.ResponseWriter, r *http.Request) { abortWithError(w, msg) return } + var makeplayer bool roomID := r.PostFormValue("room_id") // make sure username does not exists cleanName := utils.RemoveSpacesFromStr(username) @@ -90,7 +91,9 @@ func HandleFrontLogin(w http.ResponseWriter, r *http.Request) { // userstate, err := loadState(cleanName) userstate, err := repo.PlayerGetByName(r.Context(), cleanName) if err != nil || userstate == nil { + userstate = models.InitPlayer(cleanName) + makeplayer = true } fi := &models.FullInfo{ State: userstate, @@ -125,11 +128,13 @@ func HandleFrontLogin(w http.ResponseWriter, r *http.Request) { } // save state to cache // if err := saveState(cleanName, userstate); err != nil { - if err := repo.PlayerUpdate(r.Context(), userstate); err != nil { - // if err := saveFullInfo(r.Context(), fi); err != nil { - log.Error("failed to save state", "error", err) - abortWithError(w, err.Error()) - return + if makeplayer { + if err := repo.PlayerAdd(r.Context(), userstate); err != nil { + // if err := saveFullInfo(r.Context(), fi); err != nil { + log.Error("failed to save state", "error", err) + abortWithError(w, err.Error()) + return + } } } // if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil { diff --git a/repos/main.go b/repos/main.go index 3d4efb3..30ba722 100644 --- a/repos/main.go +++ b/repos/main.go @@ -4,6 +4,8 @@ import ( "context" "log/slog" "os" + "sync" + "time" "github.com/jmoiron/sqlx" _ "github.com/mattn/go-sqlite3" @@ -17,7 +19,9 @@ type AllRepos interface { } type RepoProvider struct { - DB *sqlx.DB + DB *sqlx.DB + mu sync.RWMutex + pathToDB string } func NewRepoProvider(pathToDB string) *RepoProvider { @@ -27,14 +31,65 @@ func NewRepoProvider(pathToDB string) *RepoProvider { os.Exit(1) } slog.Info("Successfully connected to database") - return &RepoProvider{ - DB: db, + rp := &RepoProvider{ + DB: db, + pathToDB: pathToDB, + } + + go rp.pingLoop() + + return rp +} + +func (rp *RepoProvider) pingLoop() { + ticker := time.NewTicker(1 * time.Minute) + defer ticker.Stop() + + for range ticker.C { + if err := rp.pingDB(); err != nil { + slog.Error("Database ping failed, attempting to reconnect...", "error", err) + rp.reconnect() + } } } +func (rp *RepoProvider) pingDB() error { + rp.mu.RLock() + defer rp.mu.RUnlock() + if rp.DB == nil { + return os.ErrClosed + } + return rp.DB.Ping() +} + +func (rp *RepoProvider) reconnect() { + rp.mu.Lock() + defer rp.mu.Unlock() + + // Double-check if connection is still down + if rp.DB != nil { + if err := rp.DB.Ping(); err == nil { + slog.Info("Database connection already re-established.") + return + } + // if ping fails, we continue to reconnect + rp.DB.Close() // close old connection + } + + slog.Info("Reconnecting to database...") + db, err := sqlx.Connect("sqlite3", rp.pathToDB) + if err != nil { + slog.Error("Failed to reconnect to database", "error", err) + rp.DB = nil // make sure DB is nil if connection failed + return + } + + rp.DB = db + slog.Info("Successfully reconnected to database") +} + func getDB(ctx context.Context, db *sqlx.DB) sqlx.ExtContext { - if tx, ok := ctx.Value("tx").(*sqlx.Tx); - ok { + if tx, ok := ctx.Value("tx").(*sqlx.Tx); ok { return tx } return db