153 lines
3.1 KiB
Go
153 lines
3.1 KiB
Go
package repository
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
models "vibeStonk/server/models/v1"
|
|
)
|
|
|
|
var (
|
|
ErrUserNotFound = errors.New("user not found")
|
|
)
|
|
|
|
func newSqliteUsersRepo(db *sql.DB) (UserRepo, error) {
|
|
repo := &sqliteUsersRepo{db: db}
|
|
if err := repo.initialize(); err != nil {
|
|
return nil, err
|
|
}
|
|
return repo, nil
|
|
}
|
|
|
|
type sqliteUsersRepo struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
// initialize creates the users table if it doesn't exist
|
|
func (s *sqliteUsersRepo) initialize() error {
|
|
query := `
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id TEXT PRIMARY KEY,
|
|
username TEXT UNIQUE NOT NULL,
|
|
pref_name TEXT,
|
|
hash TEXT
|
|
);
|
|
`
|
|
_, err := s.db.Exec(query)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create user table: %w", err)
|
|
}
|
|
|
|
query = `
|
|
CREATE INDEX IF NOT EXISTS users_usernames
|
|
ON users(id, username);
|
|
`
|
|
_, err = s.db.Exec(query)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create username index for users table: %w", err)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (s *sqliteUsersRepo) Create(user *models.User) (*models.User, error) {
|
|
// Note: The hash field is in the proto definition but not in the generated struct
|
|
// In a real implementation, we would need to handle this differently
|
|
query := `
|
|
INSERT INTO users (id, username, pref_name, hash)
|
|
VALUES (?, ?, ?, ?)
|
|
`
|
|
_, err := s.db.Exec(query, user.Id, user.UserName, user.PrefName, user.Hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
func (s *sqliteUsersRepo) Get(id string) (*models.User, error) {
|
|
query := `
|
|
SELECT id, username, pref_name, hash
|
|
FROM users
|
|
WHERE id = ?
|
|
`
|
|
row := s.db.QueryRow(query, id)
|
|
|
|
user := &models.User{}
|
|
err := row.Scan(&user.Id, &user.UserName, &user.PrefName, &user.Hash)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, ErrUserNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
return user, nil
|
|
}
|
|
|
|
func (s *sqliteUsersRepo) GetByUsername(username string) (*models.User, error) {
|
|
query := `
|
|
SELECT id, username, pref_name, hash
|
|
FROM users
|
|
WHERE username = ?
|
|
`
|
|
row := s.db.QueryRow(query, username)
|
|
|
|
user := &models.User{}
|
|
err := row.Scan(&user.Id, &user.UserName, &user.PrefName, &user.Hash)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, ErrUserNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
return user, nil
|
|
}
|
|
|
|
func (s *sqliteUsersRepo) Update(user *models.User) error {
|
|
// Note: The hash field is in the proto definition but not in the generated struct
|
|
// In a real implementation, we would need to handle this differently
|
|
query := `
|
|
UPDATE users
|
|
SET username = ?, pref_name = ?
|
|
WHERE id = ?
|
|
`
|
|
result, err := s.db.Exec(query, user.UserName, user.PrefName, user.Id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return ErrUserNotFound
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *sqliteUsersRepo) Delete(user *models.User) error {
|
|
query := `
|
|
DELETE FROM users
|
|
WHERE id = ?
|
|
`
|
|
result, err := s.db.Exec(query, user.Id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return ErrUserNotFound
|
|
}
|
|
|
|
return nil
|
|
}
|