edit: field Shortcode dan expire duration
This commit is contained in:
@@ -22,7 +22,7 @@ auth:
|
|||||||
|
|
||||||
shortlink:
|
shortlink:
|
||||||
base_url: "http://localhost:3333" # The base URL for generated OHIF Auth shortlinks
|
base_url: "http://localhost:3333" # The base URL for generated OHIF Auth shortlinks
|
||||||
default_expiry_hours: 30 * 24 # Default expiry time for shortlinks (30 days)
|
default_expiry_hours: 720 # Default expiry time for shortlinks (30 days = 30 * 24 = 720 hours)
|
||||||
max_attempts: 5 # Maximum number of failed login attempts
|
max_attempts: 5 # Maximum number of failed login attempts
|
||||||
|
|
||||||
database:
|
database:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package models
|
|||||||
// ShortLink represents a short URL token for patient access
|
// ShortLink represents a short URL token for patient access
|
||||||
type ShortLink struct {
|
type ShortLink struct {
|
||||||
ID string `db:"id" json:"id"`
|
ID string `db:"id" json:"id"`
|
||||||
Token string `db:"token" json:"token"` // The short token used in the URL
|
Shortcode string `db:"shortcode" json:"shortcode"` // The short token used in the URL
|
||||||
PatientID string `db:"patient_id" json:"patient_id"`
|
PatientID string `db:"patient_id" json:"patient_id"`
|
||||||
StudyUID string `db:"study_uid" json:"study_uid"` // The StudyInstanceUID this token grants access to
|
StudyUID string `db:"study_uid" json:"study_uid"` // The StudyInstanceUID this token grants access to
|
||||||
HashedDOB string `db:"hashed_dob" json:"-"` // Hashed Date of Birth for verification
|
HashedDOB string `db:"hashed_dob" json:"-"` // Hashed Date of Birth for verification
|
||||||
@@ -26,6 +26,7 @@ type GenerateShortLinkRequest struct {
|
|||||||
type GenerateShortLinkResponse struct {
|
type GenerateShortLinkResponse struct {
|
||||||
ShortToken string `json:"short_token"`
|
ShortToken string `json:"short_token"`
|
||||||
FullURL string `json:"full_url"`
|
FullURL string `json:"full_url"`
|
||||||
|
URI string `json:"uri"` // The URI path and query without the base 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
|
IsExisting bool `json:"is_existing"` // Indicates if this is an existing link that was reused
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func (s *DBShortLink) ToShortLink() *models.ShortLink {
|
|||||||
|
|
||||||
return &models.ShortLink{
|
return &models.ShortLink{
|
||||||
ID: fmt.Sprintf("%d", s.ShortlinkID),
|
ID: fmt.Sprintf("%d", s.ShortlinkID),
|
||||||
Token: s.ShortlinkCode,
|
Shortcode: s.ShortlinkCode,
|
||||||
PatientID: s.Shortlink_PatientID,
|
PatientID: s.Shortlink_PatientID,
|
||||||
StudyUID: s.Shortlink_Study_IUID,
|
StudyUID: s.Shortlink_Study_IUID,
|
||||||
HashedDOB: s.ShortlinkHashDoB,
|
HashedDOB: s.ShortlinkHashDoB,
|
||||||
@@ -113,7 +113,7 @@ func (r *ShortLinkRepository) CreateShortLinkTx(tx *sqlx.Tx, shortLink *models.S
|
|||||||
|
|
||||||
_, err = tx.Exec(
|
_, err = tx.Exec(
|
||||||
query,
|
query,
|
||||||
shortLink.Token,
|
shortLink.Shortcode,
|
||||||
shortLink.PatientID,
|
shortLink.PatientID,
|
||||||
shortLink.StudyUID,
|
shortLink.StudyUID,
|
||||||
shortLink.HashedDOB,
|
shortLink.HashedDOB,
|
||||||
@@ -148,7 +148,7 @@ func (r *ShortLinkRepository) UpdateShortLinkTx(tx *sqlx.Tx, shortLink *models.S
|
|||||||
shortLink.IsRevoked,
|
shortLink.IsRevoked,
|
||||||
shortLink.RemainingTries,
|
shortLink.RemainingTries,
|
||||||
expiresAt,
|
expiresAt,
|
||||||
shortLink.Token,
|
shortLink.Shortcode,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -110,14 +110,16 @@ func (s *ShortLinkService) GenerateShortLink(req *models.GenerateShortLinkReques
|
|||||||
s.logger.Info("Returning existing active shortlink",
|
s.logger.Info("Returning existing active shortlink",
|
||||||
zap.String("patientID", req.PatientID),
|
zap.String("patientID", req.PatientID),
|
||||||
zap.String("studyUID", req.StudyUID),
|
zap.String("studyUID", req.StudyUID),
|
||||||
zap.String("token", existingShortLink.Token))
|
zap.String("token", existingShortLink.Shortcode))
|
||||||
|
|
||||||
// Generate the full URL using the configured base URL
|
// Generate the full URL and URI
|
||||||
fullURL := fmt.Sprintf("%s/short-auth?short=%s", s.baseURL, existingShortLink.Token)
|
uri := fmt.Sprintf("short-auth?short=%s", existingShortLink.Shortcode)
|
||||||
|
fullURL := fmt.Sprintf("%s/%s", s.baseURL, uri)
|
||||||
|
|
||||||
return &models.GenerateShortLinkResponse{
|
return &models.GenerateShortLinkResponse{
|
||||||
ShortToken: existingShortLink.Token,
|
ShortToken: existingShortLink.Shortcode,
|
||||||
FullURL: fullURL,
|
FullURL: fullURL,
|
||||||
|
URI: uri,
|
||||||
ExpiresAt: existingShortLink.ExpiresAt,
|
ExpiresAt: existingShortLink.ExpiresAt,
|
||||||
IsExisting: true,
|
IsExisting: true,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -161,7 +163,7 @@ func (s *ShortLinkService) GenerateShortLink(req *models.GenerateShortLinkReques
|
|||||||
|
|
||||||
// Create the short link record
|
// Create the short link record
|
||||||
shortLink := &models.ShortLink{
|
shortLink := &models.ShortLink{
|
||||||
Token: *unusedShortcode,
|
Shortcode: *unusedShortcode,
|
||||||
PatientID: req.PatientID,
|
PatientID: req.PatientID,
|
||||||
StudyUID: req.StudyUID,
|
StudyUID: req.StudyUID,
|
||||||
HashedDOB: hashedDOB,
|
HashedDOB: hashedDOB,
|
||||||
@@ -181,14 +183,14 @@ func (s *ShortLinkService) GenerateShortLink(req *models.GenerateShortLinkReques
|
|||||||
|
|
||||||
// Get the ID of the created shortlink
|
// Get the ID of the created shortlink
|
||||||
var shortlinkID int
|
var shortlinkID int
|
||||||
err = tx.Get(&shortlinkID, "SELECT ShortlinkID FROM shortlink WHERE ShortlinkCode = ?", shortLink.Token)
|
err = tx.Get(&shortlinkID, "SELECT ShortlinkID FROM shortlink WHERE ShortlinkCode = ?", shortLink.Shortcode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to get shortlink ID", zap.Error(err))
|
s.logger.Error("Failed to get shortlink ID", zap.Error(err))
|
||||||
return nil, ErrCreationFailed
|
return nil, ErrCreationFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark the shortcode as used
|
// Mark the shortcode as used
|
||||||
err = s.shortCodeRepo.MarkShortCodeAsUsed(tx, shortLink.Token, shortlinkID)
|
err = s.shortCodeRepo.MarkShortCodeAsUsed(tx, shortLink.Shortcode, shortlinkID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to mark shortcode as used", zap.Error(err))
|
s.logger.Error("Failed to mark shortcode as used", zap.Error(err))
|
||||||
return nil, ErrCreationFailed
|
return nil, ErrCreationFailed
|
||||||
@@ -203,12 +205,14 @@ func (s *ShortLinkService) GenerateShortLink(req *models.GenerateShortLinkReques
|
|||||||
// Clear the tx to prevent the deferred rollback
|
// Clear the tx to prevent the deferred rollback
|
||||||
tx = nil
|
tx = nil
|
||||||
|
|
||||||
// Generate the full URL using the configured base URL
|
// Generate the full URL and URI
|
||||||
fullURL := fmt.Sprintf("%s/short-auth?short=%s", s.baseURL, shortLink.Token)
|
uri := fmt.Sprintf("short-auth?short=%s", shortLink.Shortcode)
|
||||||
|
fullURL := fmt.Sprintf("%s/%s", s.baseURL, uri)
|
||||||
|
|
||||||
return &models.GenerateShortLinkResponse{
|
return &models.GenerateShortLinkResponse{
|
||||||
ShortToken: shortLink.Token,
|
ShortToken: shortLink.Shortcode,
|
||||||
FullURL: fullURL,
|
FullURL: fullURL,
|
||||||
|
URI: uri,
|
||||||
ExpiresAt: shortLink.ExpiresAt,
|
ExpiresAt: shortLink.ExpiresAt,
|
||||||
IsExisting: false,
|
IsExisting: false,
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
### Local OHIF Proxy Test File
|
### Local OHIF Proxy Test File
|
||||||
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMSIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJyb2xlIjoiYWRtaW4iLCJ1c2VyX25hbWUiOiJBZG1pbiBVc2VyIiwidG9rZW5fdHlwZSI6ImFjY2VzcyIsImhvbWVfdXJsIjoiLyIsInN0dWR5X2xpc3QiOiJlbmFibGVkIiwiZXhwIjoxNzQ3Mzg0MDE2LCJpYXQiOjE3NDcyOTc2MTZ9.Ak1DECP1MXzQAPyU-AJM6Tsu6-sw04UtWYvY37-SaT4
|
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMSIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJyb2xlIjoiYWRtaW4iLCJ1c2VyX25hbWUiOiJBZG1pbiBVc2VyIiwidG9rZW5fdHlwZSI6ImFjY2VzcyIsImhvbWVfdXJsIjoiLyIsInN0dWR5X2xpc3QiOiJlbmFibGVkIiwiZXhwIjoxNzQ3NDcyMTE5LCJpYXQiOjE3NDczODU3MTl9.rLB8q2Wwt2aL813lf-GwuS14dO5WlJPPS3sP5OGJdO0
|
||||||
@baseUrl = http://localhost:5555
|
@baseUrl = http://localhost:5555
|
||||||
|
|
||||||
# @baseUrl = http://devone.aplikasi.web.id:5555
|
# @baseUrl = http://devone.aplikasi.web.id:5555
|
||||||
@@ -76,4 +76,12 @@ Accept: image/jpeg
|
|||||||
####
|
####
|
||||||
GET {{baseUrl}}/dicomWeb/studies?limit=101&offset=0&fuzzymatching=true&includefield=00081030,00080060&StudyInstanceUID=1.2.826.0.1.3680043.9.7307.1.202503196393.01
|
GET {{baseUrl}}/dicomWeb/studies?limit=101&offset=0&fuzzymatching=true&includefield=00081030,00080060&StudyInstanceUID=1.2.826.0.1.3680043.9.7307.1.202503196393.01
|
||||||
|
|
||||||
|
### 13. DELETE Study
|
||||||
|
# Deletes a specific study
|
||||||
|
DELETE {{baseUrl}}/dicomWeb/studies/1.2.826.0.1.3680043.0.1252.1.20250516.144411.1639014.3
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
|
|
||||||
|
###
|
||||||
|
curl -X DELETE \
|
||||||
|
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
|
||||||
|
"https://healthcare.googleapis.com/v1/projects/ohifproxy/locations/asia-southeast2/datasets/sas-storage/dicomStores/store-1/dicomWeb/studies/1.2.826.0.1.3680043.0.1252.1.20250516.144411.1639014.3"
|
||||||
@@ -7,7 +7,7 @@ POST {{baseUrl}}/auth/login
|
|||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"email": "doctor@example.com",
|
"email": "admin@example.com",
|
||||||
"password": "password123"
|
"password": "password123"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
@pydicomApiKey=2f0ff447b2c3aeef2004e83a750ded97e29ba8c0ccc70053d5e26f5d715e42ff
|
@pydicomApiKey=2f0ff447b2c3aeef2004e83a750ded97e29ba8c0ccc70053d5e26f5d715e42ff
|
||||||
|
|
||||||
### Test the PYDICOM upload endpoint
|
### Test the PYDICOM upload endpoint
|
||||||
POST {{baseUrl}}/uploaded_dicom
|
POST {{baseUrl}}/uploaded-dicom
|
||||||
X-PYDICOM-API-KEY: {{pydicomApiKey}}
|
X-PYDICOM-API-KEY: {{pydicomApiKey}}
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
@@ -36,3 +36,27 @@ Content-Type: application/json
|
|||||||
# "expires_at": "2025-05-18T02:04:46Z",
|
# "expires_at": "2025-05-18T02:04:46Z",
|
||||||
# "is_existing": true
|
# "is_existing": true
|
||||||
# }
|
# }
|
||||||
|
|
||||||
|
POST {{baseUrl}}/uploaded_dicom
|
||||||
|
X-PYDICOM-API-KEY: {{pydicomApiKey}}
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"email": "randomuser@example.com",
|
||||||
|
"password": "securepassword456",
|
||||||
|
"name": "John Doe",
|
||||||
|
"role": "patient",
|
||||||
|
"patient": {
|
||||||
|
"patient_id": "MR00000789",
|
||||||
|
"patient_name": "John Doe",
|
||||||
|
"date_of_birth": "1990-07-15"
|
||||||
|
},
|
||||||
|
"studies": [
|
||||||
|
{
|
||||||
|
"study_instance_uid": "1.2.826.0.1.3680043.9.1234.1.202507151234.01",
|
||||||
|
"accession_number": "CR.150720.1234.01",
|
||||||
|
"study_date": "2025-07-15",
|
||||||
|
"study_description": "CT Scan"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user