new file structure & koneksi ke DB

This commit is contained in:
mario
2025-05-14 10:23:33 +07:00
parent 8289881df3
commit dd4451c2a8
18 changed files with 753 additions and 243 deletions

View File

@@ -4,10 +4,12 @@ import (
"errors"
"fmt"
"net/url"
"time"
"golang.org/x/crypto/bcrypt"
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/models"
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/repository"
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/auth"
)
@@ -18,26 +20,37 @@ var (
// AuthService handles authentication operations
type AuthService struct {
jwtManager *auth.JWTManager
// When you implement database connection, add a db client here
// db *sqlx.DB
jwtManager *auth.JWTManager
userRepo *repository.UserRepository
patientRepo *repository.PatientRepository
}
// NewAuthService creates a new authentication service
func NewAuthService(jwtManager *auth.JWTManager) *AuthService {
return &AuthService{
jwtManager: jwtManager,
jwtManager: jwtManager,
userRepo: repository.NewUserRepository(),
patientRepo: repository.NewPatientRepository(),
}
}
// Login authenticates a user and generates tokens
func (s *AuthService) Login(email, password string) (*models.LoginResponse, error) {
// Find user in mock data
user := models.FindUserByCredentials(email, password)
// Find user in database
user, err := s.userRepo.GetUserByEmail(email)
if err != nil {
return nil, fmt.Errorf("error finding user: %w", err)
}
if user == nil {
return nil, ErrInvalidCredentials
}
// Verify password
if err := CheckPassword(password, user.Password); err != nil {
return nil, ErrInvalidCredentials
}
// Create token claims based on user role
additionalClaims := make(map[string]interface{})
var redirectURL string
@@ -45,7 +58,11 @@ func (s *AuthService) Login(email, password string) (*models.LoginResponse, erro
switch user.Role {
case "patient":
// Get patient data
patientData := models.FindPatientDataByUserID(user.ID)
patientData, err := s.patientRepo.GetPatientDetailsByUserID(user.ID)
if err != nil {
return nil, fmt.Errorf("error getting patient details: %w", err)
}
if patientData == nil {
return nil, ErrUserNotFound
}
@@ -53,15 +70,15 @@ func (s *AuthService) Login(email, password string) (*models.LoginResponse, erro
// Set patient-specific claims
additionalClaims["patient_id"] = patientData.PatientID
additionalClaims["patient_name"] = patientData.PatientName
additionalClaims["study_iuids"] = patientData.StudyIUIDs
additionalClaims["study_iuids"] = patientData.StudyInstanceUIDs
additionalClaims["accession_numbers"] = patientData.AccessionNumbers
// For redirectURL and home_url, use first study for simplicity
if len(patientData.StudyIUIDs) > 0 {
additionalClaims["home_url"] = fmt.Sprintf("viewer?StudyInstanceUIDs=%s", patientData.StudyIUIDs[0])
redirectURL = fmt.Sprintf("/viewer?StudyInstanceUIDs=%s", patientData.StudyIUIDs[0])
if len(patientData.StudyInstanceUIDs) > 0 {
additionalClaims["home_url"] = fmt.Sprintf("viewer?StudyInstanceUIDs=%s", patientData.StudyInstanceUIDs[0])
redirectURL = fmt.Sprintf("/viewer?StudyInstanceUIDs=%s", patientData.StudyInstanceUIDs[0])
} else {
// Fallback for empty studies array (shouldn't happen)
// Fallback for empty studies array
additionalClaims["home_url"] = "/"
redirectURL = "/"
}
@@ -79,7 +96,7 @@ func (s *AuthService) Login(email, password string) (*models.LoginResponse, erro
redirectURL = "/"
case "expertise_doctor":
case "expertise_doctor", "admin":
// Expertise doctors have full access
additionalClaims["home_url"] = "/"
additionalClaims["study_list"] = "enabled"
@@ -98,6 +115,12 @@ func (s *AuthService) Login(email, password string) (*models.LoginResponse, erro
return nil, err
}
// Store refresh token in database
expiresAt := time.Now().Add(s.jwtManager.GetRefreshExpiry())
if err := s.userRepo.StoreRefreshToken(user.ID, refreshToken, expiresAt); err != nil {
return nil, fmt.Errorf("failed to store refresh token: %w", err)
}
return &models.LoginResponse{
AccessToken: accessToken,
RefreshToken: refreshToken,
@@ -108,6 +131,27 @@ func (s *AuthService) Login(email, password string) (*models.LoginResponse, erro
// RefreshToken generates a new access token using a refresh token
func (s *AuthService) RefreshToken(refreshToken string) (string, error) {
// Check if token exists and is not revoked
dbToken, err := s.userRepo.GetRefreshToken(refreshToken)
if err != nil {
return "", fmt.Errorf("failed to get refresh token: %w", err)
}
if dbToken == nil || dbToken.IsRevoked {
return "", errors.New("invalid or revoked refresh token")
}
// Parse expiry time
expiresAt, err := time.Parse(time.RFC3339, dbToken.ExpiresAt)
if err != nil {
return "", errors.New("invalid token expiry format")
}
// Check if token is expired
if time.Now().After(expiresAt) {
return "", auth.ErrExpiredToken
}
// Validate the refresh token
claims, err := s.jwtManager.ValidateToken(refreshToken)
if err != nil {
@@ -148,7 +192,13 @@ func (s *AuthService) RefreshToken(refreshToken string) (string, error) {
}
// Generate a new access token with the same claims
accessToken, err := s.jwtManager.GenerateAccessToken(claims.UserID, claims.Email, claims.Role, claims.UserName, additionalClaims)
accessToken, err := s.jwtManager.GenerateAccessToken(
claims.UserID,
claims.Email,
claims.Role,
claims.UserName,
additionalClaims,
)
if err != nil {
return "", err
}
@@ -161,6 +211,11 @@ func (s *AuthService) ValidateToken(token string) (*auth.CustomClaims, error) {
return s.jwtManager.ValidateToken(token)
}
// Logout revokes a refresh token
func (s *AuthService) Logout(refreshToken string) error {
return s.userRepo.RevokeRefreshToken(refreshToken)
}
// HashPassword hashes a password using bcrypt
func HashPassword(password string) (string, error) {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
@@ -174,17 +229,3 @@ func HashPassword(password string) (string, error) {
func CheckPassword(password, hash string) error {
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
}
// Below functions would be implemented when connecting to a real database
// storeRefreshToken stores a refresh token in the database
func (s *AuthService) storeRefreshToken(userID, token string) error {
// TODO: In a real implementation, this would insert a record in the database
return nil
}
// revokeRefreshToken marks a refresh token as revoked
func (s *AuthService) revokeRefreshToken(token string) error {
// TODO: In a real implementation, this would update a record in the database
return nil
}