package services import ( "context" "database/sql" "fmt" "log" "time" _ "github.com/lib/pq" ) var dbConn *sql.DB type DB interface { // VerifyEmail(email string) error // RegisterUser(email, username, password string) (User, error) GetWeightLog(int64, time.Time, time.Time) ([]WeightLog, error) LogWeight(int64, WeightLog) error UpdateProfile(int64, User) error GetProfileByID(int64) (User, error) GetProfileByEmail(string) (User, error) } func Connect(host, user, password string) (DB, error) { psqlconn := fmt.Sprintf("host=%s port=5432 user=%s password=%s dbname=bodytrack sslmode=disable", host, user, password) pgClient, err := sql.Open("postgres", psqlconn) if err != nil { return nil, err } if err := pgClient.Ping(); err != nil { return nil, err } return pgdb{DB: pgClient}, nil } type pgdb struct{ *sql.DB } func (db pgdb) GetWeightLog(id int64, start, end time.Time) ([]WeightLog, error) { getWeightLogSql := ` SELECT value, recordedTs FROM stats.weightlog WHERE userId = $1 AND recordedTs >= $2 AND recordedTs <= $3 ORDER BY recordedTs DESC;` var results []WeightLog rows, err := db.Query(getWeightLogSql, id, start, end) if err != nil { return nil, err } if err := rows.Err(); err != nil { return nil, err } for rows.Next() { var wl WeightLog if err := rows.Scan(&wl.Value, &wl.RecordedTS); err != nil { log.Printf("could no scane val for weightlog: %v", err) } results = append(results, wl) } defer rows.Close() return results, nil } func (db pgdb) LogWeight(id int64, w WeightLog) error { logWeightSql := ` INSERT INTO stats.weightlog (userId, value, recordedTs) VALUES ($1, $2, $3); ` tx, err := db.BeginTx(context.Background(), nil) if err != nil { return err } defer tx.Commit() if _, err := tx.Exec(logWeightSql, id, w.Value, w.RecordedTS); err != nil { tx.Rollback() return err } return nil } func (db pgdb) UpdateProfile(id int64, u User) error { updateProfileSql := ` UPDATE accounts.profile SET username = $2, displayunit = $3, birthdate = $4, height = $5 WHERE userId = $1; ` updateUserSql := ` UPDATE accounts.users SET email = $2, passwordHash = $3 WHERE id = $1; ` tx, err := db.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelReadCommitted}) if err != nil { return err } defer tx.Commit() if _, err := tx.Exec(updateProfileSql, id, u.Username, u.DisplayUnit, u.Birthdate, u.Height); err != nil { tx.Rollback() return fmt.Errorf("could not update profile: %w", err) } if _, err := tx.Exec(updateUserSql, id, u.Email, u.PasswordHash); err != nil { tx.Rollback() return fmt.Errorf("could not update user: %w", err) } return nil } func (db pgdb) GetProfileByID(id int64) (User, error) { sql := `SELECT u.id, u.email, u.passwordhash, p.username, p.displayunit, p.birthdate, p.height FROM Accounts.Users u JOIN Accounts.Profile p on u.id = p.userId WHERE u.id = $1 LIMIT 1; ` var u User if err := db.QueryRow(sql, id).Scan( &u.ID, &u.Email, &u.PasswordHash, &u.Username, &u.DisplayUnit, &u.Birthdate, &u.Height, ); err != nil { return u, err } return u, nil } func (db pgdb) GetProfileByEmail(email string) (User, error) { sql := `SELECT u.id, u.email, u.passwordhash, p.username, p.displayunit, p.birthdate, p.height FROM Accounts.Users u JOIN Accounts.Profile p on u.id = p.userId WHERE u.email = $1 LIMIT 1; ` var u User if err := db.QueryRow(sql, email).Scan( &u.ID, &u.Email, &u.PasswordHash, &u.Username, &u.DisplayUnit, &u.Birthdate, &u.Height, ); err != nil { return u, err } return u, nil }