commitd2ec8c0f07Author: mario <dev.mario@sismedika@gmail.com> Date: Thu May 15 15:42:33 2025 +0700 add: db tx commit and rollback implementation commit264435f67eAuthor: mario <dev.mario@sismedika@gmail.com> Date: Thu May 15 14:34:20 2025 +0700 fix: shortlink generation logic update/create commit047ab1937aAuthor: mario <dev.mario@sismedika@gmail.com> Date: Thu May 15 11:06:04 2025 +0700 fix: if multiple studies patient, show first study by default commitc13f834b92Author: mario <dev.mario@sismedika@gmail.com> Date: Thu May 15 09:46:32 2025 +0700 add: register and login with DB query AND some struct type correction commitdd4451c2a8Author: mario <dev.mario@sismedika@gmail.com> Date: Wed May 14 10:23:33 2025 +0700 new file structure & koneksi ke DB commit8289881df3Author: mario <dev.mario@sismedika@gmail.com> Date: Tue May 13 16:49:07 2025 +0700 edit: rm debug route commitdd784da232Author: mario <dev.mario@sismedika@gmail.com> Date: Tue May 13 15:44:11 2025 +0700 add: implement shortlink commit2687a761ccAuthor: mario <dev.mario@sismedika@gmail.com> Date: Tue May 13 11:47:19 2025 +0700 add new dummy doctor user commiteb67eaca46Author: mario <dev.mario@sismedika@gmail.com> Date: Tue May 13 11:46:28 2025 +0700 add: ref_doctor studylist filter commit0d4825d152Author: mario <dev.mario@sismedika@gmail.com> Date: Tue May 13 10:07:16 2025 +0700 edit study_iuids & accNum in patient jwt to array commit2d1f135fdaAuthor: mario <dev.mario@sismedika@gmail.com> Date: Tue May 13 09:52:45 2025 +0700 patient see their multiple studies commit13bb380f51Author: mario <dev.mario@sismedika@gmail.com> Date: Fri May 9 10:13:16 2025 +0700 add: cors handler route and readme commit6c9ab574ceAuthor: mario <dev.mario@sismedika@gmail.com> Date: Mon May 5 11:50:36 2025 +0700 add: login & token validation tapi belum connect ke DB commit297c9a6a01Author: mario <dev.mario@sismedika@gmail.com> Date: Mon Apr 28 15:37:02 2025 +0700 add readme.md commit9b8e0260f3Author: mario <dev.mario@sismedika@gmail.com> Date: Mon Apr 7 15:46:07 2025 +0700 connected-to-google commitf340bc5916Author: mario <dev.mario@sismedika.com> Date: Mon Apr 7 11:14:18 2025 +0700 init
170 lines
4.7 KiB
Go
170 lines
4.7 KiB
Go
package repository
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
|
|
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/models"
|
|
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/database"
|
|
"github.com/jmoiron/sqlx"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
// DBUser represents a user from the database
|
|
type DBUser struct {
|
|
UserID int `db:"UserID"`
|
|
UserEmail string `db:"UserEmail"`
|
|
UserPassword string `db:"UserPassword"`
|
|
UserRole string `db:"UserRole"`
|
|
UserName string `db:"UserName"`
|
|
UserCreatedAt time.Time `db:"UserCreatedAt"`
|
|
UserUpdatedAt time.Time `db:"UserUpdatedAt"`
|
|
}
|
|
|
|
// DBRefreshToken represents a refresh token from the database
|
|
type DBRefreshToken struct {
|
|
ID int `db:"id"`
|
|
Token string `db:"token"`
|
|
UserID string `db:"user_id"`
|
|
ExpiresAt time.Time `db:"expires_at"`
|
|
IsRevoked bool `db:"is_revoked"`
|
|
CreatedAt time.Time `db:"created_at"`
|
|
}
|
|
|
|
// UserRepository handles database operations related to users
|
|
type UserRepository struct {
|
|
*Repository
|
|
}
|
|
|
|
// NewUserRepository creates a new user repository
|
|
func NewUserRepository() *UserRepository {
|
|
return &UserRepository{
|
|
Repository: NewRepository(),
|
|
}
|
|
}
|
|
|
|
// ToUser converts a DBUser to a User model
|
|
func (u *DBUser) ToUser() *models.User {
|
|
return &models.User{
|
|
ID: fmt.Sprintf("%d", u.UserID),
|
|
Email: u.UserEmail,
|
|
Password: u.UserPassword,
|
|
Role: u.UserRole,
|
|
Name: u.UserName,
|
|
CreatedAt: u.UserCreatedAt.Format(time.RFC3339),
|
|
UpdatedAt: u.UserUpdatedAt.Format(time.RFC3339),
|
|
}
|
|
}
|
|
|
|
// GetUserByEmail retrieves a user by email
|
|
func (r *UserRepository) GetUserByEmail(email string) (*models.User, error) {
|
|
var dbUser DBUser
|
|
|
|
query := `SELECT * FROM user WHERE UserEmail = ?`
|
|
err := database.DB.Get(&dbUser, query, email)
|
|
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
return nil, fmt.Errorf("database error getting user by email: %w", err)
|
|
}
|
|
|
|
return dbUser.ToUser(), nil
|
|
}
|
|
|
|
// GetUserByID retrieves a user by ID
|
|
func (r *UserRepository) GetUserByID(id string) (*models.User, error) {
|
|
var dbUser DBUser
|
|
|
|
query := `SELECT * FROM user WHERE UserID = ?`
|
|
err := database.DB.Get(&dbUser, query, id)
|
|
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
return nil, fmt.Errorf("database error getting user by ID: %w", err)
|
|
}
|
|
|
|
return dbUser.ToUser(), nil
|
|
}
|
|
|
|
// StoreRefreshToken saves a refresh token to the database
|
|
func (r *UserRepository) StoreRefreshToken(userID string, token string, expiresAt time.Time) error {
|
|
query := `INSERT INTO refresh_tokens (token, user_id, expires_at, is_revoked, created_at)
|
|
VALUES (?, ?, ?, false, NOW())`
|
|
|
|
_, err := database.DB.Exec(query, token, userID, expiresAt)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("database error storing refresh token: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetRefreshToken retrieves a refresh token from the database
|
|
func (r *UserRepository) GetRefreshToken(token string) (*models.RefreshToken, error) {
|
|
var dbToken DBRefreshToken
|
|
|
|
query := `SELECT * FROM refresh_tokens WHERE token = ?`
|
|
err := database.DB.Get(&dbToken, query, token)
|
|
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
return nil, fmt.Errorf("database error getting refresh token: %w", err)
|
|
}
|
|
|
|
return &models.RefreshToken{
|
|
ID: fmt.Sprintf("%d", dbToken.ID),
|
|
UserID: dbToken.UserID,
|
|
Token: dbToken.Token,
|
|
ExpiresAt: dbToken.ExpiresAt.Format(time.RFC3339),
|
|
IsRevoked: dbToken.IsRevoked,
|
|
CreatedAt: dbToken.CreatedAt.Format(time.RFC3339),
|
|
}, nil
|
|
}
|
|
|
|
// RevokeRefreshToken marks a refresh token as revoked
|
|
func (r *UserRepository) RevokeRefreshToken(token string) error {
|
|
query := `UPDATE refresh_tokens SET is_revoked = true WHERE token = ?`
|
|
_, err := database.DB.Exec(query, token)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("database error revoking refresh token: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CreateUserTx creates a new user within a transaction
|
|
func (r *UserRepository) CreateUserTx(tx *sqlx.Tx, user *models.User) error {
|
|
// Hash the password before storing
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to hash password: %w", err)
|
|
}
|
|
|
|
query := `INSERT INTO user (UserEmail, UserPassword, UserRole, UserName, UserCreatedAt, UserUpdatedAt)
|
|
VALUES (?, ?, ?, ?, NOW(), NOW())`
|
|
|
|
result, err := tx.Exec(query, user.Email, string(hashedPassword), user.Role, user.Name)
|
|
if err != nil {
|
|
return fmt.Errorf("database error creating user: %w", err)
|
|
}
|
|
|
|
// Get the last inserted ID
|
|
id, err := result.LastInsertId()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get last insert ID: %w", err)
|
|
}
|
|
|
|
// Update the user ID
|
|
user.ID = fmt.Sprintf("%d", id)
|
|
return nil
|
|
}
|