bodytrack/api/internal/services/db.go

168 lines
3.6 KiB
Go

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
}