Squashed commit of the following:
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
This commit is contained in:
59
internal/api/repository/doctor.go
Normal file
59
internal/api/repository/doctor.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
// DBDoctor represents a doctor from the database
|
||||
type DBDoctor struct {
|
||||
ID int `db:"id"`
|
||||
Doctor_UsersID string `db:"Doctor_UsersID"`
|
||||
DoctorID string `db:"DoctorID"`
|
||||
DoctorName string `db:"DoctorName"`
|
||||
DoctorCreatedAt time.Time `db:"DoctorCreatedAt"`
|
||||
DoctorLastUpdatedAt time.Time `db:"DoctorLastUpdatedAt"`
|
||||
}
|
||||
|
||||
// DoctorRepository handles database operations related to doctors
|
||||
type DoctorRepository struct {
|
||||
*Repository
|
||||
}
|
||||
|
||||
// NewDoctorRepository creates a new doctor repository
|
||||
func NewDoctorRepository() *DoctorRepository {
|
||||
return &DoctorRepository{
|
||||
Repository: NewRepository(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetDoctorDetailsByUserID retrieves doctor details for a user
|
||||
func (r *DoctorRepository) GetDoctorDetailsByUserID(userID string) (*DBDoctor, error) {
|
||||
var dbDoctor DBDoctor
|
||||
|
||||
query := `SELECT * FROM doctor WHERE Doctor_UsersID = ?`
|
||||
err := database.DB.Get(&dbDoctor, query, userID)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("database error getting doctor details: %w", err)
|
||||
}
|
||||
|
||||
return &dbDoctor, nil
|
||||
}
|
||||
|
||||
// CreateDoctorTx creates a new doctor record within a transaction
|
||||
func (r *DoctorRepository) CreateDoctorTx(tx *sqlx.Tx, doctorDetails *models.DoctorDetails, userID string) error {
|
||||
query := `INSERT INTO doctor (Doctor_UsersID, DoctorID, DoctorName, DoctorCreatedAt, DoctorLastUpdatedAt)
|
||||
VALUES (?, ?, ?, NOW(), NOW())`
|
||||
|
||||
_, err := tx.Exec(query, userID, doctorDetails.DoctorID, doctorDetails.DoctorName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("database error creating doctor: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
82
internal/api/repository/patient.go
Normal file
82
internal/api/repository/patient.go
Normal file
@@ -0,0 +1,82 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// DBPatient represents a patient from the database
|
||||
type DBPatient struct {
|
||||
ID int `db:"id"`
|
||||
Patient_UsersID string `db:"Patient_UsersID"`
|
||||
PatientMedrec string `db:"PatientMedrec"`
|
||||
PatientName string `db:"PatientName"`
|
||||
PatientDoB time.Time `db:"PatientDoB"`
|
||||
PatientCreatedAt time.Time `db:"PatientCreatedAt"`
|
||||
PatientUpdatedAt time.Time `db:"PatientUpdatedAt"`
|
||||
}
|
||||
|
||||
// PatientRepository handles database operations related to patients
|
||||
type PatientRepository struct {
|
||||
*Repository
|
||||
}
|
||||
|
||||
// NewPatientRepository creates a new patient repository
|
||||
func NewPatientRepository() *PatientRepository {
|
||||
return &PatientRepository{
|
||||
Repository: NewRepository(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetPatientDetailsByUserID retrieves patient details for a user
|
||||
func (r *PatientRepository) GetPatientDetailsByUserID(userID string) (*models.PatientDetails, error) {
|
||||
var dbPatient DBPatient
|
||||
|
||||
query := `SELECT * FROM patient WHERE Patient_UsersID = ?`
|
||||
err := database.DB.Get(&dbPatient, query, userID)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("database error getting patient details: %w", err)
|
||||
}
|
||||
|
||||
// Create StudyRepository to get patient studies
|
||||
studyRepo := NewStudyRepository()
|
||||
studyUIDs, accessionNumbers, err := studyRepo.GetPatientStudies(dbPatient.PatientMedrec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.PatientDetails{
|
||||
PatientID: dbPatient.PatientMedrec,
|
||||
PatientName: dbPatient.PatientName,
|
||||
StudyInstanceUIDs: studyUIDs,
|
||||
AccessionNumbers: accessionNumbers,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreatePatientTx creates a new patient record within a transaction
|
||||
func (r *PatientRepository) CreatePatientTx(tx *sqlx.Tx, patientRecord *models.PatientDetails, userID string) error {
|
||||
// Parse DOB to time.Time. 2006-1-02 = reference format YYYY-MM-DD in Go, not a default value
|
||||
dob, err := time.Parse("2006-01-02", patientRecord.DateOfBirth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid date of birth format: %w", err)
|
||||
}
|
||||
|
||||
query := `INSERT INTO patient (Patient_UsersID, PatientMedrec, PatientName, PatientDoB, PatientCreatedAt, PatientUpdatedAt)
|
||||
VALUES (?, ?, ?, ?, NOW(), NOW())`
|
||||
|
||||
_, err = tx.Exec(query, userID, patientRecord.PatientID, patientRecord.PatientName, dob)
|
||||
if err != nil {
|
||||
return fmt.Errorf("database error creating patient: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
23
internal/api/repository/repository.go
Normal file
23
internal/api/repository/repository.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/database"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
// Repository provides a base interface to the database
|
||||
type Repository struct {
|
||||
db *sqlx.DB // Changed from *sql.DB to *sqlx.DB
|
||||
}
|
||||
|
||||
// NewRepository creates a new database repository
|
||||
func NewRepository() *Repository {
|
||||
return &Repository{
|
||||
db: database.DB,
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the database connection (no-op as DB is managed by database package)
|
||||
func (r *Repository) Close() error {
|
||||
return nil
|
||||
}
|
||||
167
internal/api/repository/shortlink.go
Normal file
167
internal/api/repository/shortlink.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"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"
|
||||
)
|
||||
|
||||
// DBShortLink represents a shortlink from the database
|
||||
type DBShortLink struct {
|
||||
ShortlinkID int `db:"ShortlinkID"`
|
||||
ShortlinkCode string `db:"ShortlinkCode"`
|
||||
Shortlink_PatientID string `db:"Shortlink_PatientID"`
|
||||
Shortlink_Study_IUID string `db:"Shortlink_Study_IUID"`
|
||||
ShortlinkHashDoB string `db:"ShortlinkHashDoB"`
|
||||
ShortlinkExpiredAt time.Time `db:"ShortlinkExpiredAt"`
|
||||
ShortlinkIsRevoked bool `db:"ShortlinkIsRevoked"`
|
||||
ShortlinkRemainingTries int `db:"ShortlinkRemainingTries"`
|
||||
ShortlinkCreatedAt time.Time `db:"ShortlinkCreatedAt"`
|
||||
ShortlinkCreate_UserID int `db:"ShortlinkCreate_UserID"`
|
||||
}
|
||||
|
||||
// ShortLinkRepository handles database operations related to shortlinks
|
||||
type ShortLinkRepository struct {
|
||||
*Repository
|
||||
}
|
||||
|
||||
// NewShortLinkRepository creates a new shortlink repository
|
||||
func NewShortLinkRepository() *ShortLinkRepository {
|
||||
return &ShortLinkRepository{
|
||||
Repository: NewRepository(),
|
||||
}
|
||||
}
|
||||
|
||||
// ToShortLink converts a DBShortLink to a ShortLink model
|
||||
func (s *DBShortLink) ToShortLink() *models.ShortLink {
|
||||
return &models.ShortLink{
|
||||
ID: fmt.Sprintf("%d", s.ShortlinkID),
|
||||
Token: s.ShortlinkCode,
|
||||
PatientID: s.Shortlink_PatientID,
|
||||
StudyUID: s.Shortlink_Study_IUID,
|
||||
HashedDOB: s.ShortlinkHashDoB,
|
||||
ExpiresAt: s.ShortlinkExpiredAt.Format(time.RFC3339),
|
||||
IsRevoked: s.ShortlinkIsRevoked,
|
||||
RemainingTries: s.ShortlinkRemainingTries,
|
||||
CreatedAt: s.ShortlinkCreatedAt.Format(time.RFC3339),
|
||||
CreatedByID: fmt.Sprintf("%d", s.ShortlinkCreate_UserID),
|
||||
}
|
||||
}
|
||||
|
||||
// GetShortLinkByToken retrieves a shortlink by token
|
||||
func (r *ShortLinkRepository) GetShortLinkByToken(token string) (*models.ShortLink, error) {
|
||||
var dbShortLink DBShortLink
|
||||
|
||||
query := `SELECT * FROM shortlink WHERE ShortlinkCode = ?`
|
||||
err := database.DB.Get(&dbShortLink, query, token)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("database error getting shortlink: %w", err)
|
||||
}
|
||||
|
||||
return dbShortLink.ToShortLink(), nil
|
||||
}
|
||||
|
||||
// CreateShortLinkTx stores a new shortlink in the database within a transaction
|
||||
func (r *ShortLinkRepository) CreateShortLinkTx(tx *sqlx.Tx, shortLink *models.ShortLink) error {
|
||||
query := `INSERT INTO shortlink (
|
||||
ShortlinkCode,
|
||||
Shortlink_PatientID,
|
||||
Shortlink_Study_IUID,
|
||||
ShortlinkHashDoB,
|
||||
ShortlinkExpiredAt,
|
||||
ShortlinkIsRevoked,
|
||||
ShortlinkRemainingTries,
|
||||
ShortlinkCreatedAt,
|
||||
ShortlinkCreate_UserID)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), ?)`
|
||||
|
||||
createdByID, err := strconv.Atoi(shortLink.CreatedByID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid created by ID: %w", err)
|
||||
}
|
||||
|
||||
expiresAt, err := time.Parse(time.RFC3339, shortLink.ExpiresAt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid expiration date: %w", err)
|
||||
}
|
||||
|
||||
_, err = tx.Exec(
|
||||
query,
|
||||
shortLink.Token,
|
||||
shortLink.PatientID,
|
||||
shortLink.StudyUID,
|
||||
shortLink.HashedDOB,
|
||||
expiresAt,
|
||||
shortLink.IsRevoked,
|
||||
shortLink.RemainingTries,
|
||||
createdByID,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("database error creating shortlink: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateShortLinkTx updates an existing shortlink in the database within a transaction
|
||||
func (r *ShortLinkRepository) UpdateShortLinkTx(tx *sqlx.Tx, shortLink *models.ShortLink) error {
|
||||
query := `UPDATE shortlink SET
|
||||
ShortlinkIsRevoked = ?,
|
||||
ShortlinkRemainingTries = ?,
|
||||
ShortlinkExpiredAt = ?
|
||||
WHERE ShortlinkCode = ?`
|
||||
|
||||
expiresAt, err := time.Parse(time.RFC3339, shortLink.ExpiresAt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid expiration date: %w", err)
|
||||
}
|
||||
|
||||
_, err = tx.Exec(
|
||||
query,
|
||||
shortLink.IsRevoked,
|
||||
shortLink.RemainingTries,
|
||||
expiresAt,
|
||||
shortLink.Token,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("database error updating shortlink: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetActiveShortLinkByPatientAndStudy retrieves an active (unexpired, not revoked) shortlink
|
||||
// for the given patient ID and study UID
|
||||
func (r *ShortLinkRepository) GetActiveShortLinkByPatientAndStudy(patientID string, studyUID string) (*models.ShortLink, error) {
|
||||
var dbShortLink DBShortLink
|
||||
|
||||
query := `SELECT * FROM shortlink
|
||||
WHERE Shortlink_PatientID = ?
|
||||
AND Shortlink_Study_IUID = ?
|
||||
AND ShortlinkExpiredAt > NOW()
|
||||
AND ShortlinkIsRevoked = FALSE
|
||||
ORDER BY ShortlinkExpiredAt DESC
|
||||
LIMIT 1`
|
||||
|
||||
err := database.DB.Get(&dbShortLink, query, patientID, studyUID)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("database error getting active shortlink: %w", err)
|
||||
}
|
||||
|
||||
return dbShortLink.ToShortLink(), nil
|
||||
}
|
||||
100
internal/api/repository/study.go
Normal file
100
internal/api/repository/study.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
// DBStudy represents a study from the database
|
||||
type DBStudy struct {
|
||||
ID int `db:"id"`
|
||||
Study_PatientID string `db:"Study_PatientID"`
|
||||
StudyIUID string `db:"StudyIUID"`
|
||||
StudyAccessionNumber string `db:"StudyAccessionNumber"`
|
||||
StudyDate time.Time `db:"StudyDate"`
|
||||
StudyCreatedAt time.Time `db:"StudyCreatedAt"`
|
||||
StudyUpdatedAt time.Time `db:"StudyUpdatedAt"`
|
||||
}
|
||||
|
||||
// StudyRepository handles database operations related to studies
|
||||
type StudyRepository struct {
|
||||
*Repository
|
||||
}
|
||||
|
||||
// NewStudyRepository creates a new study repository
|
||||
func NewStudyRepository() *StudyRepository {
|
||||
return &StudyRepository{
|
||||
Repository: NewRepository(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetPatientStudies retrieves all studies for a patient
|
||||
func (r *StudyRepository) GetPatientStudies(patientID string) ([]string, []string, error) {
|
||||
var studies []DBStudy
|
||||
|
||||
query := `SELECT * FROM study WHERE Study_PatientID = ?`
|
||||
err := database.DB.Select(&studies, query, patientID)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("database error getting patient studies: %w", err)
|
||||
}
|
||||
|
||||
var studyUIDs []string
|
||||
var accessionNumbers []string
|
||||
|
||||
for _, study := range studies {
|
||||
studyUIDs = append(studyUIDs, study.StudyIUID)
|
||||
if study.StudyAccessionNumber != "" {
|
||||
accessionNumbers = append(accessionNumbers, study.StudyAccessionNumber)
|
||||
}
|
||||
}
|
||||
|
||||
return studyUIDs, accessionNumbers, nil
|
||||
}
|
||||
|
||||
// GetStudyByUID retrieves a study by its UID
|
||||
func (r *StudyRepository) GetStudyByUID(studyUID string) (*DBStudy, error) {
|
||||
var study DBStudy
|
||||
|
||||
query := `SELECT * FROM study WHERE StudyIUID = ?`
|
||||
err := database.DB.Get(&study, query, studyUID)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("database error getting study: %w", err)
|
||||
}
|
||||
|
||||
return &study, nil
|
||||
}
|
||||
|
||||
// CreateStudyTx creates a new study record for a patient within a transaction
|
||||
func (r *StudyRepository) CreateStudyTx(tx *sqlx.Tx, patientID string, study models.Study) error {
|
||||
// Parse study date if provided
|
||||
var studyDate *time.Time
|
||||
if study.StudyDate != "" {
|
||||
parsedTime, err := time.Parse("2006-01-02", study.StudyDate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid study date format: %w", err)
|
||||
}
|
||||
studyDate = &parsedTime
|
||||
}
|
||||
|
||||
query := `INSERT INTO study
|
||||
(Study_PatientID, StudyIUID, StudyAccessionNumber, StudyDate, StudyCreatedAt, StudyUpdatedAt)
|
||||
VALUES (?, ?, ?, ?, NOW(), NOW())`
|
||||
|
||||
_, err := tx.Exec(query,
|
||||
patientID,
|
||||
study.StudyInstanceUID,
|
||||
study.AccessionNumber,
|
||||
studyDate)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("database error creating study: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
169
internal/api/repository/user.go
Normal file
169
internal/api/repository/user.go
Normal file
@@ -0,0 +1,169 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user