API Dokümantasyonu
Hospital Storage, AWS S3 protokolüyle tam uyumlu bir nesne depolama API'si sunar. Mevcut S3 istemcileri, SDK'lar ve araçlar doğrudan çalışır — yalnızca endpoint URL'i değiştirin.
X-Request-ID, x-amz-request-id ve x-amz-id-2 header'larını içerir. Destek taleplerinde bu değerleri paylaşın.Kimlik Doğrulama
Tüm korumalı endpoint'ler AWS Signature V4 gerektirir. Sunucu saati ile ±15 dakika tolerans uygulanır.
Authorization: AWS4-HMAC-SHA256 Credential=<ACCESS_KEY>/<YYYYMMDD>/tr/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=<hex_signature>
| Header | Açıklama |
|---|---|
| Authorization | AWS4-HMAC-SHA256 formatında imza |
| X-Amz-Date | ISO8601 — örn: 20240115T120000Z |
| X-Amz-Content-Sha256 | Body SHA-256 hash'i |
| Host | Sunucu adresi |
# Bir kez çalıştır aws configure set aws_access_key_id ERISIM_ANAHTARINIZ aws configure set aws_secret_access_key GIZLI_ANAHTARINIZ aws configure set default.region tr aws s3 ls --endpoint-url http://localhost:8080
Hata Yanıtları
{ "error": { "code": "NoSuchBucket", "message": "bucket bulunamadi" } }
| HTTP | Kod | Açıklama |
|---|---|---|
| 400 | InvalidRequest | Eksik veya hatalı parametre |
| 403 | AccessDenied | Geçersiz imza veya yetersiz yetki |
| 404 | NoSuchBucket / NoSuchKey | Kaynak bulunamadı |
| 409 | BucketAlreadyExists / ObjectLocked | Çakışma veya WORM kilidi |
| 429 | RateLimitExceeded | 100 req/s tenant limiti aşıldı |
| 503 | StorageFull / ServiceUnavailable | Disk >%95 veya bağımlılık hatası |
| 503 | LeaderElectionInProgress | Cluster lider seçimi — 2s bekle, tekrar dene |
Roller
Retry-After header'ı gelir.Bucket İşlemleri
{ "buckets": [{ "name": "xray-arsiv", "created": "2024-01-15T10:00:00Z" }] }
Bucket adı: [a-z0-9][a-z0-9-]{1,61}[a-z0-9]
| Query | Açıklama |
|---|---|
| ?cors | CORS konfigürasyonu (body: JSON) |
| ?policy | Bucket policy (body: JSON) |
aws s3 mb s3://xray-arsiv --endpoint-url http://localhost:8080
(boş body)
| Query | Varsayılan | Açıklama |
|---|---|---|
| prefix | — | Ön ek filtresi: 2024/xray/ |
| delimiter | — | Klasör ayracı: / |
| max-keys | 1000 | Maks nesne sayısı |
| continuation-token | — | Sayfalama token'ı |
| ?uploads | — | Aktif multipart upload'ları listele |
{ "objects": [{ "key": "2024/hasta_001.dcm", "size": 2048576, "etag": "\"d41d...\"" }] }
{ "enabled": true, "mode": "COMPLIANCE", "retention_days": 2555 }
{ "rules": [{ "id": "7-yil", "expiration_days": 2555, "enabled": true }] }
Nesne İşlemleri
Anahtar slash içerebilir: 2024/xray/hasta.dcm. Tüm nesneler AES-256-GCM ile otomatik şifrelenir.
| Header | Zorunluluk | Açıklama |
|---|---|---|
| Content-Length | zorunlu | Body boyutu (byte) |
| Content-Type | opsiyonel | Örn: application/dicom |
| x-amz-meta-* | opsiyonel | Özel metadata: x-amz-meta-patient-id: 123 |
{ "etag": "\"d41d8cd...\"", "version_id": "v1710076920-abc123" }
aws s3 cp hasta.dcm s3://xray-arsiv/2024/hasta.dcm --endpoint-url http://localhost:8080
| Query | Açıklama |
|---|---|
| ?versionId=... | Belirli bir versiyonu indir |
| ?versions=true | Tüm versiyonları listele |
| Header | Açıklama |
|---|---|
| ETag | Nesne MD5 hash'i |
| x-amz-version-id | Version ID |
| x-amz-server-side-encryption | AES256 |
| x-amz-meta-* | Yüklenirken eklenen özel metadata |
Body olmadan tüm metadata header'larını döner. Büyük dosyaların varlık kontrolü için idealdir.
WORM/Object Lock aktif nesneler silinemez — ObjectLocked hatası döner.
| Query | Açıklama |
|---|---|
| ?versionId=... | Belirli bir versiyonu sil |
<Delete><Object><Key>2024/hasta_001.dcm</Key></Object></Delete>
| Query | Zorunluluk | Açıklama |
|---|---|---|
| bucket | zorunlu | Bucket adı |
| key | zorunlu | Nesne anahtarı |
| expires | opsiyonel | Saniye — varsayılan 300, maks 604800 |
{ "url": "http://host:8080/presign/eyJ...", "expires_at": "2024-03-10T15:30:00Z" }
Multipart Upload
5 GB üzeri dosyalar için. Her parça en az 5 MB olmalı (son parça hariç).
{ "upload_id": "mpu-abc123" }{ "etag": "\"abc123\"", "part_number": 1 }{ "parts": [{ "part_number": 1, "etag": "\"abc\"" }] }Upload'u iptal eder, yüklenen tüm parçaları siler.
Sistem Endpoint'leri
{ "status": "ok", "version": "v91.0", "is_leader": true,
"checks": { "redis": "ok", "postgres": "ok", "disk_used_pct": "34.2%", "nats": "ok" } }
Prometheus text formatında sistem metrikleri. Grafana ile doğrudan kullanılabilir.
Admin Endpoint'leri
{ "access_key": "admin", "role": "super_admin", "tenant_id": "system" }| Query | Varsayılan | Açıklama |
|---|---|---|
| format | csv | csv veya json |
| from | 30 gün önce | RFC3339: 2024-01-01T00:00:00Z |
| to | şimdi | RFC3339 |
| tenant | — | Tenant filtresi (super_admin) |
| action | — | PUT, GET, DELETE, LOGIN... |
| limit | 10000 | Maks 50000 |
{ "total_runs": 48, "deleted_bytes_human": "2.3 GB", "last_run_at": "2024-03-10T14:00:00Z" }{ "old_master_key_hex": "<64 karakter hex>" }
Tüm nesne DEK'lerini eski key ile çözer, yeni key ile şifreler. Yeni key sunucu yeniden başlatılmadan önce yerinde olmalıdır.
{ "tenant_id": "t1", "total_bytes": 1073741824, "object_count": 245, "bucket_count": 3, "quota_gb": 100 }Tenant (Kurum) Yönetimi
super_admin: tüm tenant'ları yönetir. tenant_admin: sadece kendi kurumunu görür.
[{ "id": "t1", "name": "Antalya Devlet", "slug": "antalya-devlet", "status": "active", "quota_gb": 100 }]{ "name": "Yeni Hastane", "slug": "yeni-hastane", "quota_gb": 50 }{ "name": "Güncel Ad", "quota_gb": 200 }{ "tenant_id": "t1", "status": "suspended" }{ "registration_id": "reg_abc123" }Self-servis kayıt (POST /api/register) sonrası admin onayı bekleyen başvurular.
Kullanıcı Yönetimi
Roller: super_admin → tenant_admin → operator → doctor → technician → backup_svc → viewer
[{ "access_key": "dr.ahmet", "name": "Dr. Ahmet", "role": "doctor", "is_active": true }]{ "tenant_id": "t1", "access_key": "dr.ahmet", "secret_key": "GucluSifre123456",
"role": "doctor", "name": "Dr. Ahmet Yılmaz", "email": "[email protected]" }
{ "name": "Dr. Ahmet Y.", "role": "operator", "is_active": true }Kullanıcı kalıcı olarak silinir. Silinmiş kullanıcının verileri tenant'ta kalır.
{ "user_id": "dr.ahmet", "new_secret": "YeniGucluSifre123" }{ "access_key": "dr.ahmet", "current_secret": "EskiSifre123456!", "new_secret": "YeniSifre789012!" }Güvenlik
2FA / TOTP
{ "qr_uri": "otpauth://totp/HospitalStorage:admin?secret=...", "secret": "JBSWY3DPEHPK3PXP" }{ "code": "123456" }
// Response: backup codes listesi (tek seferlik){ "access_key": "dr.ahmet" }{ "enabled": true, "verified": true, "backup_codes_remaining": 8 }Acil Durum Modu — super_admin
Tüm yazma işlemleri (PUT, DELETE) reddedilir. Okuma devam eder. Güvenlik ihlali şüphesinde kullanın.
Read-only veya lockdown modundan normal operasyona geçiş.
{ "mode": "normal" } // "normal", "read_only", "lockdown"IP Erişim Kuralları — super_admin
[{ "id": "r1", "tenant_id": "t1", "cidr": "10.0.0.0/24", "type": "whitelist" }]{ "tenant_id": "t1", "cidr": "192.168.1.0/24", "type": "whitelist" }
type: whitelist (sadece bu IP'ler erişir) veya blacklist (bu IP'ler engellenir).
Kural kalıcı olarak silinir.
KVKK Uyumluluk
Veri İhlali Bildirimi
{ "title": "Yetkisiz erişim tespiti", "severity": "high",
"detection_method": "anomaly", "description": "..." }
KVKK Madde 12: İhlal tespitinden itibaren 72 saat içinde bildirim zorunlu.
Tüm breach raporları: severity (low/medium/high/critical), status (open/investigating/contained/resolved/reported).
Raporu "reported" durumuna geçirir ve bildirim tarihini kaydeder.
Veri Silme Talebi (Right to Erasure)
{ "tenant_id": "t1", "requester": "Hasta Mehmet",
"data_subject": "TC:12345678901", "reason": "KVKK talep" }Durumlar: pending → approved → processing → completed
Prefix scan → obje silme → audit anonimleştirme → sonuç raporu. Geri alınamaz.
IAM & Erişim Kuralları
{ "user_id": "dr.ahmet", "policy": {
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::radyoloji/*"]
} }
AWS IAM uyumlu JSON politika. Deny her zaman Allow'u override eder.
Tenant'a ait tüm kullanıcı politikaları.
Politika kalıcı olarak silinir.
Hasta Erişim Kuralları
{ "user_id": "dr.ahmet", "patient_tc": "12345678901", "access_type": "read" }Hangi doktorun hangi hastanın dosyalarına erişebildiğini gösterir.
Hangi doktor, hangi hastanın dosyasına, ne zaman erişti — KVKK denetim raporu için.
Domain & Altyapı
[{ "domain": "dosya.hastane.com", "tenant_id": "t1", "is_verified": true }]{ "domain": "dosya.hastane.com", "tenant_id": "t1" }
DNS TXT kaydı ile doğrulama gerekir.
{ "domain": "dosya.hastane.com" }Multi-node yapıda hangi tenant'ın hangi node'da olduğunu gösterir.
Healthcare plugin framework — aktif eklentiler.
Başarısız event'ler (NATS publish hataları). Manuel retry veya silme.
Hasta Portalı
Hastalar kendi dosyalarını görebilir, paylaşım linki oluşturabilir.
{ "tc_no": "12345678901", "name": "Mehmet Yılmaz", "password": "..." }{ "tc_no": "12345678901", "password": "..." }TC no veya ad ile hasta arama. Doctor+ rolü gerekir.
Hasta ID veya TC no ile dosya listesi.
{ "patient_id": "p1", "tenant_id": "t1", "expires_secs": 86400, "max_views": 5 }
Token-based link: /patient/view/{token} — auth gerekmez, süreli erişim.
{ "hospital_name": "Yeni Hastane", "contact_name": "Ali", "email": "[email protected]", "phone": "05551234567" }
Başvuru admin onayı bekler (POST /admin/tenants/approve).
AWS CLI
# Konfigürasyon aws configure set aws_access_key_id ERISIM_ANAHTARINIZ aws configure set aws_secret_access_key GIZLI_ANAHTARINIZ aws configure set default.region tr # Kısayol alias s3h="aws s3 --endpoint-url http://localhost:8080" # Bucket s3h ls # listele s3h mb s3://xray-arsiv # oluştur # Nesne s3h cp hasta.dcm s3://xray-arsiv/2024/hasta.dcm # yükle s3h cp s3://xray-arsiv/2024/hasta.dcm lokal.dcm # indir s3h ls s3://xray-arsiv/2024/ # listele s3h rm s3://xray-arsiv/2024/hasta.dcm # sil s3h sync ./dosyalar/ s3://xray-arsiv/2024/ # senkronize et # Presign URL (5 dakika) aws s3 presign s3://xray-arsiv/2024/hasta.dcm \ --expires-in 300 --endpoint-url http://localhost:8080
Python — boto3
import boto3 from botocore.client import Config s3 = boto3.client( "s3", endpoint_url="http://localhost:8080", aws_access_key_id="ERISIM_ANAHTARINIZ", aws_secret_access_key="GIZLI_ANAHTARINIZ", region_name="tr", config=Config(signature_version="s3v4") ) # Yükle with open("hasta.dcm", "rb") as f: s3.put_object(Bucket="xray-arsiv", Key="2024/hasta.dcm", Body=f, ContentType="application/dicom", Metadata={"patient-id": "12345678901"}) # Listele resp = s3.list_objects_v2(Bucket="xray-arsiv", Prefix="2024/") for obj in resp.get("Contents", []): print(obj["Key"], obj["Size"]) # Presign URL url = s3.generate_presigned_url("get_object", Params={"Bucket": "xray-arsiv", "Key": "2024/hasta.dcm"}, ExpiresIn=3600) # Multipart (>5 GB) mpu = s3.create_multipart_upload(Bucket="xray-arsiv", Key="buyuk.dcm") parts = [] for i, chunk in enumerate(chunks, 1): p = s3.upload_part(Bucket="xray-arsiv", Key="buyuk.dcm", UploadId=mpu["UploadId"], PartNumber=i, Body=chunk) parts.append({"PartNumber": i, "ETag": p["ETag"]}) s3.complete_multipart_upload(Bucket="xray-arsiv", Key="buyuk.dcm", UploadId=mpu["UploadId"], MultipartUpload={"Parts": parts})
Go — aws-sdk-go-v2
package main import ( "context"; "os" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/s3" ) func newClient() *s3.Client { cfg, _ := config.LoadDefaultConfig(context.Background(), config.WithRegion("tr"), config.WithCredentialsProvider( credentials.NewStaticCredentialsProvider("ANAHTAR", "GIZLI", ""), ), ) return s3.NewFromConfig(cfg, func(o *s3.Options) { o.BaseEndpoint = aws.String("http://localhost:8080") o.UsePathStyle = true }) } func main() { client, ctx := newClient(), context.Background() // Yükle f, _ := os.Open("hasta.dcm"); defer f.Close() client.PutObject(ctx, &s3.PutObjectInput{ Bucket: aws.String("xray-arsiv"), Key: aws.String("2024/hasta.dcm"), Body: f, }) // Listele out, _ := client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{ Bucket: aws.String("xray-arsiv"), Prefix: aws.String("2024/"), }) for _, o := range out.Contents { println(*o.Key) } }