diff --git a/internal/database/querier.go b/internal/database/querier.go index b1bb740..5f2de3e 100644 --- a/internal/database/querier.go +++ b/internal/database/querier.go @@ -14,7 +14,7 @@ type Querier interface { AddUserEbookFolder(ctx context.Context, arg AddUserEbookFolderParams) (UserEbookFolders, error) CreateEbook(ctx context.Context, arg CreateEbookParams) (Ebooks, error) CreateEbookRating(ctx context.Context, arg CreateEbookRatingParams) (EbookRatings, error) - CreateUser(ctx context.Context, arg CreateUserParams) (Users, error) + CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error) DeleteEbook(ctx context.Context, id pgtype.UUID) error DeleteEbookRating(ctx context.Context, arg DeleteEbookRatingParams) error DeleteReadingProgress(ctx context.Context, arg DeleteReadingProgressParams) error @@ -27,10 +27,11 @@ type Querier interface { GetReadingProgress(ctx context.Context, arg GetReadingProgressParams) (ReadingProgress, error) GetScanSettings(ctx context.Context, id pgtype.UUID) (GetScanSettingsRow, error) GetUser(ctx context.Context, id pgtype.UUID) (GetUserRow, error) - GetUserByEmail(ctx context.Context, email string) (Users, error) - GetUserByEmailOrUsername(ctx context.Context, email string) (Users, error) - GetUserByUsername(ctx context.Context, username string) (Users, error) + GetUserByEmail(ctx context.Context, email string) (GetUserByEmailRow, error) + GetUserByEmailOrUsername(ctx context.Context, email string) (GetUserByEmailOrUsernameRow, error) + GetUserByUsername(ctx context.Context, username string) (GetUserByUsernameRow, error) GetUserEbookFolders(ctx context.Context, userID pgtype.UUID) ([]UserEbookFolders, error) + GetUserForLogin(ctx context.Context, email string) (GetUserForLoginRow, error) GetUserPasswordHash(ctx context.Context, id pgtype.UUID) (string, error) ListEbooks(ctx context.Context, arg ListEbooksParams) ([]Ebooks, error) ListUsers(ctx context.Context) ([]ListUsersRow, error) diff --git a/internal/database/queries.sql.go b/internal/database/queries.sql.go index 7789f21..6fc196a 100644 --- a/internal/database/queries.sql.go +++ b/internal/database/queries.sql.go @@ -131,7 +131,7 @@ func (q *Queries) CreateEbookRating(ctx context.Context, arg CreateEbookRatingPa const CreateUser = `-- name: CreateUser :one INSERT INTO users (email, username, password_hash, theme) VALUES ($1, $2, $3, $4) -RETURNING id, email, username, password_hash, theme, created_at, updated_at, scan_frequency_minutes, auto_scan_enabled +RETURNING id, email, username, theme, created_at, updated_at ` type CreateUserParams struct { @@ -141,24 +141,30 @@ type CreateUserParams struct { Theme pgtype.Text `db:"theme" json:"theme"` } -func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (Users, error) { +type CreateUserRow struct { + ID pgtype.UUID `db:"id" json:"id"` + Email string `db:"email" json:"email"` + Username string `db:"username" json:"username"` + Theme pgtype.Text `db:"theme" json:"theme"` + CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` + UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` +} + +func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error) { row := q.db.QueryRow(ctx, CreateUser, arg.Email, arg.Username, arg.PasswordHash, arg.Theme, ) - var i Users + var i CreateUserRow err := row.Scan( &i.ID, &i.Email, &i.Username, - &i.PasswordHash, &i.Theme, &i.CreatedAt, &i.UpdatedAt, - &i.ScanFrequencyMinutes, - &i.AutoScanEnabled, ) return i, err } @@ -419,64 +425,82 @@ func (q *Queries) GetUser(ctx context.Context, id pgtype.UUID) (GetUserRow, erro } const GetUserByEmail = `-- name: GetUserByEmail :one -SELECT id, email, username, password_hash, theme, created_at, updated_at, scan_frequency_minutes, auto_scan_enabled FROM users WHERE email = $1 +SELECT id, email, username, theme, created_at, updated_at FROM users WHERE email = $1 ` -func (q *Queries) GetUserByEmail(ctx context.Context, email string) (Users, error) { +type GetUserByEmailRow struct { + ID pgtype.UUID `db:"id" json:"id"` + Email string `db:"email" json:"email"` + Username string `db:"username" json:"username"` + Theme pgtype.Text `db:"theme" json:"theme"` + CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` + UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` +} + +func (q *Queries) GetUserByEmail(ctx context.Context, email string) (GetUserByEmailRow, error) { row := q.db.QueryRow(ctx, GetUserByEmail, email) - var i Users + var i GetUserByEmailRow err := row.Scan( &i.ID, &i.Email, &i.Username, - &i.PasswordHash, &i.Theme, &i.CreatedAt, &i.UpdatedAt, - &i.ScanFrequencyMinutes, - &i.AutoScanEnabled, ) return i, err } const GetUserByEmailOrUsername = `-- name: GetUserByEmailOrUsername :one -SELECT id, email, username, password_hash, theme, created_at, updated_at, scan_frequency_minutes, auto_scan_enabled FROM users WHERE email = $1 OR username = $1 +SELECT id, email, username, theme, created_at, updated_at FROM users WHERE email = $1 OR username = $1 ` -func (q *Queries) GetUserByEmailOrUsername(ctx context.Context, email string) (Users, error) { +type GetUserByEmailOrUsernameRow struct { + ID pgtype.UUID `db:"id" json:"id"` + Email string `db:"email" json:"email"` + Username string `db:"username" json:"username"` + Theme pgtype.Text `db:"theme" json:"theme"` + CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` + UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` +} + +func (q *Queries) GetUserByEmailOrUsername(ctx context.Context, email string) (GetUserByEmailOrUsernameRow, error) { row := q.db.QueryRow(ctx, GetUserByEmailOrUsername, email) - var i Users + var i GetUserByEmailOrUsernameRow err := row.Scan( &i.ID, &i.Email, &i.Username, - &i.PasswordHash, &i.Theme, &i.CreatedAt, &i.UpdatedAt, - &i.ScanFrequencyMinutes, - &i.AutoScanEnabled, ) return i, err } const GetUserByUsername = `-- name: GetUserByUsername :one -SELECT id, email, username, password_hash, theme, created_at, updated_at, scan_frequency_minutes, auto_scan_enabled FROM users WHERE username = $1 +SELECT id, email, username, theme, created_at, updated_at FROM users WHERE username = $1 ` -func (q *Queries) GetUserByUsername(ctx context.Context, username string) (Users, error) { +type GetUserByUsernameRow struct { + ID pgtype.UUID `db:"id" json:"id"` + Email string `db:"email" json:"email"` + Username string `db:"username" json:"username"` + Theme pgtype.Text `db:"theme" json:"theme"` + CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` + UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` +} + +func (q *Queries) GetUserByUsername(ctx context.Context, username string) (GetUserByUsernameRow, error) { row := q.db.QueryRow(ctx, GetUserByUsername, username) - var i Users + var i GetUserByUsernameRow err := row.Scan( &i.ID, &i.Email, &i.Username, - &i.PasswordHash, &i.Theme, &i.CreatedAt, &i.UpdatedAt, - &i.ScanFrequencyMinutes, - &i.AutoScanEnabled, ) return i, err } @@ -510,6 +534,35 @@ func (q *Queries) GetUserEbookFolders(ctx context.Context, userID pgtype.UUID) ( return items, nil } +const GetUserForLogin = `-- name: GetUserForLogin :one +SELECT id, email, username, password_hash, theme, created_at, updated_at FROM users WHERE email = $1 OR username = $1 +` + +type GetUserForLoginRow struct { + ID pgtype.UUID `db:"id" json:"id"` + Email string `db:"email" json:"email"` + Username string `db:"username" json:"username"` + PasswordHash string `db:"password_hash" json:"password_hash"` + Theme pgtype.Text `db:"theme" json:"theme"` + CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` + UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` +} + +func (q *Queries) GetUserForLogin(ctx context.Context, email string) (GetUserForLoginRow, error) { + row := q.db.QueryRow(ctx, GetUserForLogin, email) + var i GetUserForLoginRow + err := row.Scan( + &i.ID, + &i.Email, + &i.Username, + &i.PasswordHash, + &i.Theme, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + const GetUserPasswordHash = `-- name: GetUserPasswordHash :one SELECT password_hash FROM users WHERE id = $1 ` diff --git a/internal/database/queries/queries.sql b/internal/database/queries/queries.sql index 212639a..cee6bd6 100644 --- a/internal/database/queries/queries.sql +++ b/internal/database/queries/queries.sql @@ -1,16 +1,19 @@ -- name: CreateUser :one INSERT INTO users (email, username, password_hash, theme) VALUES ($1, $2, $3, $4) -RETURNING *; +RETURNING id, email, username, theme, created_at, updated_at; -- name: GetUserByEmail :one -SELECT * FROM users WHERE email = $1; +SELECT id, email, username, theme, created_at, updated_at FROM users WHERE email = $1; -- name: GetUserByUsername :one -SELECT * FROM users WHERE username = $1; +SELECT id, email, username, theme, created_at, updated_at FROM users WHERE username = $1; -- name: GetUserByEmailOrUsername :one -SELECT * FROM users WHERE email = $1 OR username = $1; +SELECT id, email, username, theme, created_at, updated_at FROM users WHERE email = $1 OR username = $1; + +-- name: GetUserForLogin :one +SELECT id, email, username, password_hash, theme, created_at, updated_at FROM users WHERE email = $1 OR username = $1; -- name: GetUser :one SELECT id, email, username, theme, created_at, updated_at FROM users WHERE id = $1; diff --git a/internal/handlers/auth.go b/internal/handlers/auth.go index f324ba1..82f52aa 100644 --- a/internal/handlers/auth.go +++ b/internal/handlers/auth.go @@ -204,8 +204,8 @@ func (h *AuthHandler) Login(c echo.Context) error { return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()}) } - // Get user by email or username - user, err := h.db.GetUserByEmailOrUsername(c.Request().Context(), req.Login) + // Get user by email or username (includes password hash for verification) + user, err := h.db.GetUserForLogin(c.Request().Context(), req.Login) if err != nil { if c.Request().Header.Get("HX-Request") == "true" { return c.HTML(http.StatusUnauthorized, `
Invalid credentials
`)