fix: shortlink generation logic update/create
This commit is contained in:
@@ -27,6 +27,7 @@ type GenerateShortLinkResponse struct {
|
|||||||
ShortToken string `json:"short_token"`
|
ShortToken string `json:"short_token"`
|
||||||
FullURL string `json:"full_url"`
|
FullURL string `json:"full_url"`
|
||||||
ExpiresAt string `json:"expires_at"`
|
ExpiresAt string `json:"expires_at"`
|
||||||
|
IsExisting bool `json:"is_existing"` // Indicates if this is an existing link that was reused
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShortLinkAuthRequest represents the shortlink authentication request
|
// ShortLinkAuthRequest represents the shortlink authentication request
|
||||||
|
|||||||
@@ -139,3 +139,28 @@ func (r *ShortLinkRepository) UpdateShortLink(shortLink *models.ShortLink) error
|
|||||||
|
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -94,6 +94,31 @@ func (s *ShortLinkService) GenerateShortLink(req *models.GenerateShortLinkReques
|
|||||||
return nil, errors.New("invalid date of birth format, expected YYYY-MM-DD")
|
return nil, errors.New("invalid date of birth format, expected YYYY-MM-DD")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if an unexpired shortlink already exists for this patient and study
|
||||||
|
existingShortLink, err := s.shortLinkRepo.GetActiveShortLinkByPatientAndStudy(req.PatientID, req.StudyUID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Error checking for existing shortlinks", zap.Error(err))
|
||||||
|
return nil, ErrCreationFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an active shortlink exists, return it instead of creating a new one
|
||||||
|
if existingShortLink != nil {
|
||||||
|
s.logger.Info("Returning existing active shortlink",
|
||||||
|
zap.String("patientID", req.PatientID),
|
||||||
|
zap.String("studyUID", req.StudyUID),
|
||||||
|
zap.String("token", existingShortLink.Token))
|
||||||
|
|
||||||
|
// Generate the full URL using the configured base URL
|
||||||
|
fullURL := fmt.Sprintf("%s/short-auth?short=%s", s.baseURL, existingShortLink.Token)
|
||||||
|
|
||||||
|
return &models.GenerateShortLinkResponse{
|
||||||
|
ShortToken: existingShortLink.Token,
|
||||||
|
FullURL: fullURL,
|
||||||
|
ExpiresAt: existingShortLink.ExpiresAt,
|
||||||
|
IsExisting: true,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Set expiration if not provided
|
// Set expiration if not provided
|
||||||
expiresIn := s.defaultExpiryTime
|
expiresIn := s.defaultExpiryTime
|
||||||
if req.ExpiresIn > 0 {
|
if req.ExpiresIn > 0 {
|
||||||
@@ -139,6 +164,7 @@ func (s *ShortLinkService) GenerateShortLink(req *models.GenerateShortLinkReques
|
|||||||
ShortToken: token,
|
ShortToken: token,
|
||||||
FullURL: fullURL,
|
FullURL: fullURL,
|
||||||
ExpiresAt: shortLink.ExpiresAt,
|
ExpiresAt: shortLink.ExpiresAt,
|
||||||
|
IsExisting: false,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,23 +10,6 @@
|
|||||||
GET {{baseUrl}}/health
|
GET {{baseUrl}}/health
|
||||||
Accept: application/json
|
Accept: application/json
|
||||||
|
|
||||||
### Login Success
|
|
||||||
POST {{baseUrl}}/auth/login
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
|
||||||
"email": "patient1@example.com",
|
|
||||||
"password": "password123"
|
|
||||||
}
|
|
||||||
|
|
||||||
### Refresh TOken
|
|
||||||
POST {{baseUrl}}/auth/refresh
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
|
||||||
"refresh_token": {{ refresh_token}}
|
|
||||||
}
|
|
||||||
|
|
||||||
### 2. QIDO-RS: Search for Studies
|
### 2. QIDO-RS: Search for Studies
|
||||||
# Returns all studies (should return a list of DICOM studies if any exist)
|
# Returns all studies (should return a list of DICOM studies if any exist)
|
||||||
GET {{baseUrl}}/dicomWeb/studies
|
GET {{baseUrl}}/dicomWeb/studies
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMiIsImVtYWlsIjoicGF0aWVudCIsInJvbGUiOiJwYXRpZW50IiwidXNlcl9uYW1lIjoiUGF0aWVudCBVc2VyIiwidG9rZW5fdHlwZSI6ImFjY2VzcyIsInBhdGllbnRfaWQiOiIwMDIxMTYyMiIsInBhdGllbnRfbmFtZSI6IkRJRElUIFNVWUFUTkFeUi4xMDA0OS4xOCIsImFjY2Vzc2lvbl9udW1iZXIiOiJDUi4xODA3MTMuMDM2Iiwic3R1ZHlfaXVpZCI6IjEuMi44MjYuMC4xLjM2ODAwNDMuOS43MzA3LjEuMjAxODA3MTMwMzYiLCJob21lX3VybCI6InZpZXdlcj9TdHVkeUluc3RhbmNlVUlEcz0xLjIuODI2LjAuMS4zNjgwMDQzLjkuNzMwNy4xLjIwMTgwNzEzMDM2Iiwic3R1ZHlfbGlzdCI6ImRpc2FibGVkIiwiZXhwIjoxNzQ2ODUyMDU2LCJpYXQiOjE3NDY3NjU2NTZ9.wGKCU77toQ5D27DRcNJE_XQjoJmxxzqoPbmf5N7D0cw
|
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMSIsImVtYWlsIjoicGF0aWVudDFAZXhhbXBsZS5jb20iLCJyb2xlIjoicGF0aWVudCIsInVzZXJfbmFtZSI6IkRJRElUIFNVWUFUTkFeUi4xMDA0OS4xOCIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJwYXRpZW50X2lkIjoiMDAyMTE2MjIiLCJwYXRpZW50X25hbWUiOiJESURJVCBTVVlBVE5BXlIuMTAwNDkuMTgiLCJzdHVkeV9pdWlkcyI6WyIxLjIuODI2LjAuMS4zNjgwMDQzLjkuNzMwNy4xLjIwMTgwNTMwMDY2IiwiMS4yLjgyNi4wLjEuMzY4MDA0My45LjczMDcuMS4yMDE4MDcxMzAzNiJdLCJhY2Nlc3Npb25fbnVtYmVycyI6WyJDUi4xODA1MzAuMDY2IiwiQ1IuMTgwNzEzLjAzNiJdLCJob21lX3VybCI6InZpZXdlcj9TdHVkeUluc3RhbmNlVUlEcz0xLjIuODI2LjAuMS4zNjgwMDQzLjkuNzMwNy4xLjIwMTgwNTMwMDY2Iiwic3R1ZHlfbGlzdCI6ImRpc2FibGVkIiwiZXhwIjoxNzQ3MzY0OTY1LCJpYXQiOjE3NDcyNzg1NjV9.I6L7nnT-xexPtkTIfIXetT41KGQlVPbCY8l_2fTgJdg
|
||||||
@token_exp_doctor = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMSIsImVtYWlsIjoiYWRtaW4iLCJyb2xlIjoiZXhwZXJ0aXNlX2RvY3RvciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJleHAiOjE3NDY1MDQ1MTYsImlhdCI6MTc0NjQxODExNn0.vlDrns1oPFXHE5--TmWqwzvzxnfcCPcV2UW8_4GwDwE
|
@token_exp_doctor = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMSIsImVtYWlsIjoiYWRtaW4iLCJyb2xlIjoiZXhwZXJ0aXNlX2RvY3RvciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJleHAiOjE3NDY1MDQ1MTYsImlhdCI6MTc0NjQxODExNn0.vlDrns1oPFXHE5--TmWqwzvzxnfcCPcV2UW8_4GwDwE
|
||||||
@baseUrl = http://localhost:5555
|
@baseUrl = http://localhost:5555
|
||||||
|
|
||||||
@@ -25,11 +25,11 @@ Content-Type: application/json
|
|||||||
# Destination URL / OHIF Viewer URL: http://152.42.173.210:3000/viewer?StudyInstanceUIDs=1.2.826.0.1.3680043.9.7307.1.202503196393.01
|
# Destination URL / OHIF Viewer URL: http://152.42.173.210:3000/viewer?StudyInstanceUIDs=1.2.826.0.1.3680043.9.7307.1.202503196393.01
|
||||||
|
|
||||||
### Study where StudyIUID
|
### Study where StudyIUID
|
||||||
GET {{baseUrl}}/dicomWeb/studies?limit=101&offset=0&fuzzymatching=true&includefield=00081030,00080060&StudyInstanceUID=1.2.826.0.1.3680043.9.7307.1.20180713036
|
GET {{baseUrl}}/dicomWeb/studies?limit=101&offset=0&fuzzymatching=true&includefield=00081030,00080060&StudyInstanceUID=1.2.826.0.1.3680043.9.7307.1.20180530066
|
||||||
Authorization: Bearer {{token}}
|
Authorization: Bearer {{token}}
|
||||||
|
|
||||||
### Series List
|
### Series List
|
||||||
GET {{baseUrl}}/dicomWeb/studies/1.2.826.0.1.3680043.9.7307.1.202503196393.01/series?includefield=00080021,00080031,0008103E,00200011
|
GET {{baseUrl}}/dicomWeb/studies/1.2.826.0.1.3680043.9.7307.1.20180530066/series?includefield=00080021,00080031,0008103E,00200011
|
||||||
Authorization: Bearer {{token}}
|
Authorization: Bearer {{token}}
|
||||||
|
|
||||||
### Semua Study dari Patient ID
|
### Semua Study dari Patient ID
|
||||||
|
|||||||
@@ -79,12 +79,12 @@ Content-Type: application/json
|
|||||||
|
|
||||||
{
|
{
|
||||||
"email": "expert@example.com",
|
"email": "expert@example.com",
|
||||||
"password": "expert123",
|
"password": "password123",
|
||||||
"name": "Dr. Expert Johnson",
|
"name": "Dr. Expertise",
|
||||||
"role": "expertise_doctor",
|
"role": "expertise_doctor",
|
||||||
"doctor": {
|
"doctor": {
|
||||||
"doctor_id": "EX456",
|
"doctor_id": "EX456",
|
||||||
"doctor_name": "Dr. Expert Johnson"
|
"doctor_name": "Dr. Expertise"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ Content-Type: application/json
|
|||||||
|
|
||||||
{
|
{
|
||||||
"email": "admin@example.com",
|
"email": "admin@example.com",
|
||||||
"password": "admin123",
|
"password": "password123",
|
||||||
"name": "Admin User",
|
"name": "Admin User",
|
||||||
"role": "admin"
|
"role": "admin"
|
||||||
}
|
}
|
||||||
@@ -148,4 +148,12 @@ Content-Type: application/json
|
|||||||
{
|
{
|
||||||
"email": "doctor@example.com",
|
"email": "doctor@example.com",
|
||||||
"password": "password123"
|
"password": "password123"
|
||||||
|
}
|
||||||
|
|
||||||
|
### Refresh TOken
|
||||||
|
POST {{baseUrl}}/auth/refresh
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"refresh_token": {{ refresh_token}}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,8 @@
|
|||||||
### Shortlink Authentication Test File
|
### Shortlink Authentication Test File
|
||||||
@baseUrl = http://localhost:5555
|
@baseUrl = http://localhost:5555
|
||||||
@adminToken = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMSIsImVtYWlsIjoiYWRtaW4iLCJyb2xlIjoiZXhwZXJ0aXNlX2RvY3RvciIsInVzZXJfbmFtZSI6IkFkbWluIFVzZXIiLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiaG9tZV91cmwiOiIvIiwic3R1ZHlfbGlzdCI6ImVuYWJsZWQiLCJleHAiOjE3NDcyMDY5NTAsImlhdCI6MTc0NzEyMDU1MH0.M3fhlKB8MX-NxGdEgnaA9-AhMXnXjUjRsWYOBXntJe4
|
@adminToken = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiNiIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJyb2xlIjoiYWRtaW4iLCJ1c2VyX25hbWUiOiJBZG1pbiBVc2VyIiwidG9rZW5fdHlwZSI6ImFjY2VzcyIsImhvbWVfdXJsIjoiLyIsInN0dWR5X2xpc3QiOiJlbmFibGVkIiwiZXhwIjoxNzQ3MzY0NjgxLCJpYXQiOjE3NDcyNzgyODF9.bkzxV8f26wN_r6uI5T6o58TNX4U0z-Wel1hCAl5-8ag
|
||||||
|
|
||||||
### 0. Login as Admin and take the token
|
### 1. Generate Short Link Single Study
|
||||||
POST http://localhost:5555/auth/login
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
|
||||||
"email": "patient1@example.com",
|
|
||||||
"password": "password123"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
### 1. Generate Short Link (Admin/Expertise Doctor Only)
|
|
||||||
POST {{baseUrl}}/generate-link
|
POST {{baseUrl}}/generate-link
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Authorization: Bearer {{adminToken}}
|
Authorization: Bearer {{adminToken}}
|
||||||
@@ -24,27 +14,51 @@ Authorization: Bearer {{adminToken}}
|
|||||||
"expires_in": 48
|
"expires_in": 48
|
||||||
}
|
}
|
||||||
|
|
||||||
### 2. Authenticate with Short Link and DOB
|
### 2. Generate Short Link Multiple Studies
|
||||||
# Use the short_token from the response of request #1
|
POST {{baseUrl}}/generate-link
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer {{adminToken}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"patient_id": "00211622",
|
||||||
|
"study_uid": "1.2.826.0.1.3680043.9.7307.1.20180713036",
|
||||||
|
"dob": "1980-01-01",
|
||||||
|
"expires_in": 48
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
POST {{baseUrl}}/generate-link
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer {{adminToken}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"patient_id": "00211622",
|
||||||
|
"study_uid": "1.2.826.0.1.3680043.9.7307.1.20180530066",
|
||||||
|
"dob": "1980-01-01",
|
||||||
|
"expires_in": 48
|
||||||
|
}
|
||||||
|
|
||||||
|
### Authenticate correct
|
||||||
POST {{baseUrl}}/auth/shortlink
|
POST {{baseUrl}}/auth/shortlink
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"short_token": "oP-tLWeu",
|
"short_token": "aePWCTrU",
|
||||||
"dob": "2001-10-07"
|
"dob": "1980-01-01"
|
||||||
}
|
}
|
||||||
|
|
||||||
### 3. Try authentication with incorrect DOB
|
### Authenticate with Incorrect DOB
|
||||||
POST {{baseUrl}}/auth/shortlink
|
POST {{baseUrl}}/auth/shortlink
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"short_token": "erB5xT5S",
|
"short_token": "HNHh_zem",
|
||||||
"dob": "1985-04-16"
|
"dob": "2001-10-00"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
### 4. Access DICOM data with the JWT from successful shortlink auth
|
### 4. Access DICOM data with the JWT from successful shortlink auth
|
||||||
# Replace with token from successful auth in request #2
|
# Replace with token from successful auth in request #2
|
||||||
GET {{baseUrl}}/dicomWeb/studies/1.2.826.0.1.3680043.9.7307.1.202503196393.01
|
GET {{baseUrl}}/dicomWeb/studies/1.2.826.0.1.3680043.9.7307.1.20180713036
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoic2hvcnRsaW5rX3NsXzgzZGNmNzQ1NGYwZiIsImVtYWlsIjoicGF0aWVudF9zbF84M2RjZjc0NTRmMGZAc2hvcnRsaW5rLmxvY2FsIiwicm9sZSI6InBhdGllbnQiLCJ1c2VyX25hbWUiOiJQYXRpZW50IiwidG9rZW5fdHlwZSI6ImFjY2VzcyIsInBhdGllbnRfaWQiOiJNUjAwMDAwMzU5IiwicGF0aWVudF9uYW1lIjoiUGF0aWVudCIsInN0dWR5X2l1aWRzIjpbIjEuMi44MjYuMC4xLjM2ODAwNDMuOS43MzA3LjEuMjAyNTAzMTk2MzkzLjAxIl0sImhvbWVfdXJsIjoidmlld2VyP1N0dWR5SW5zdGFuY2VVSURzPTEuMi44MjYuMC4xLjM2ODAwNDMuOS43MzA3LjEuMjAyNTAzMTk2MzkzLjAxIiwic3R1ZHlfbGlzdCI6ImRpc2FibGVkIiwiZXhwIjoxNzQ3MjA3MDI4LCJpYXQiOjE3NDcxMjA2Mjh9.RMGF9ParYAmOXbJqd0DP2kl0X6O0n8j_LI6FF9el4qM
|
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoic2hvcnRsaW5rXzIiLCJlbWFpbCI6InBhdGllbnRfMkBzaG9ydGxpbmsubG9jYWwiLCJyb2xlIjoicGF0aWVudCIsInVzZXJfbmFtZSI6IlBhdGllbnQiLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwicGF0aWVudF9pZCI6IjAwMjExNjIyIiwicGF0aWVudF9uYW1lIjoiUGF0aWVudCIsInN0dWR5X2l1aWRzIjpbIjEuMi44MjYuMC4xLjM2ODAwNDMuOS43MzA3LjEuMjAxODA3MTMwMzYiXSwiaG9tZV91cmwiOiJ2aWV3ZXI_U3R1ZHlJbnN0YW5jZVVJRHM9MS4yLjgyNi4wLjEuMzY4MDA0My45LjczMDcuMS4yMDE4MDcxMzAzNiIsInN0dWR5X2xpc3QiOiJkaXNhYmxlZCIsImV4cCI6MTc0NzM2OTc1NSwiaWF0IjoxNzQ3MjgzMzU1fQ.rj1Xr7StW_O3rE2Pwq6L-WGBAW1tFcUq8bt5nu4u050
|
||||||
Reference in New Issue
Block a user