v62 Ana Sayfa

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.

Base URL
http://host:8080
Auth
AWS Sig V4
Region
tr
Service
s3
Her yanıt 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 Header
Authorization: AWS4-HMAC-SHA256
  Credential=<ACCESS_KEY>/<YYYYMMDD>/tr/s3/aws4_request,
  SignedHeaders=host;x-amz-content-sha256;x-amz-date,
  Signature=<hex_signature>
Zorunlu Header'lar
HeaderAçıklama
AuthorizationAWS4-HMAC-SHA256 formatında imza
X-Amz-DateISO8601 — örn: 20240115T120000Z
X-Amz-Content-Sha256Body SHA-256 hash'i
HostSunucu adresi
AWS CLI Kurulumu
# 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" } }
HTTPKodAçıklama
400InvalidRequestEksik veya hatalı parametre
403AccessDeniedGeçersiz imza veya yetersiz yetki
404NoSuchBucket / NoSuchKeyKaynak bulunamadı
409BucketAlreadyExists / ObjectLockedÇakışma veya WORM kilidi
429RateLimitExceeded100 req/s tenant limiti aşıldı
503StorageFull / ServiceUnavailableDisk >%95 veya bağımlılık hatası
503LeaderElectionInProgressCluster lider seçimi — 2s bekle, tekrar dene

Roller

super_admin
Tüm tenant ve sistem yönetimi
tenant_admin
Kendi tenant, kullanıcı, bucket
operator
Bucket & nesne CRUD, lifecycle
doctor
Tıbbi veri okuma ve yazma
backup_svc
Servis hesabı, yedekleme
technician
Salt okunur teknik destek
viewer
Yalnızca GET / HEAD
Rate limit: tenant başına 100 req/s, burst 200. Aşıldığında Retry-After header'ı gelir.

Bucket İşlemleri

GET/Bucket listele
Response 200
{ "buckets": [{ "name": "xray-arsiv", "created": "2024-01-15T10:00:00Z" }] }
PUT/{bucket}Bucket oluştur

Bucket adı: [a-z0-9][a-z0-9-]{1,61}[a-z0-9]

QueryAçıklama
?corsCORS konfigürasyonu (body: JSON)
?policyBucket policy (body: JSON)
AWS CLI
aws s3 mb s3://xray-arsiv --endpoint-url http://localhost:8080
DELETE/{bucket}Bucket sil (boş olmalı)
Response 204
(boş body)
GET/{bucket}Nesneleri listele (v2)
QueryVarsayılanAçıklama
prefixÖn ek filtresi: 2024/xray/
delimiterKlasör ayracı: /
max-keys1000Maks nesne sayısı
continuation-tokenSayfalama token'ı
?uploadsAktif multipart upload'ları listele
Response 200
{ "objects": [{ "key": "2024/hasta_001.dcm", "size": 2048576, "etag": "\"d41d...\"" }] }
PUT/{bucket}/wormWORM yapılandır
COMPLIANCE modunda etkinleştirilen WORM kapatılamaz.
Request Body
{ "enabled": true, "mode": "COMPLIANCE", "retention_days": 2555 }
PUT/{bucket}/lifecycleOtomatik silme kuralı
Request Body
{ "rules": [{ "id": "7-yil", "expiration_days": 2555, "enabled": true }] }

Nesne İşlemleri

PUT/{bucket}/{key}Nesne yükle

Anahtar slash içerebilir: 2024/xray/hasta.dcm. Tüm nesneler AES-256-GCM ile otomatik şifrelenir.

HeaderZorunlulukAçıklama
Content-LengthzorunluBody boyutu (byte)
Content-TypeopsiyonelÖrn: application/dicom
x-amz-meta-*opsiyonelÖzel metadata: x-amz-meta-patient-id: 123
Response 200
{ "etag": "\"d41d8cd...\"", "version_id": "v1710076920-abc123" }
AWS CLI
aws s3 cp hasta.dcm s3://xray-arsiv/2024/hasta.dcm --endpoint-url http://localhost:8080
GET/{bucket}/{key}Nesne indir
QueryAçıklama
?versionId=...Belirli bir versiyonu indir
?versions=trueTüm versiyonları listele
Response Headers
HeaderAçıklama
ETagNesne MD5 hash'i
x-amz-version-idVersion ID
x-amz-server-side-encryptionAES256
x-amz-meta-*Yüklenirken eklenen özel metadata
HEAD/{bucket}/{key}Nesne meta verisi (body yok)

Body olmadan tüm metadata header'larını döner. Büyük dosyaların varlık kontrolü için idealdir.

DELETE/{bucket}/{key}Nesne sil

WORM/Object Lock aktif nesneler silinemez — ObjectLocked hatası döner.

QueryAçıklama
?versionId=...Belirli bir versiyonu sil
POST/{bucket}?deleteToplu nesne silme
Request Body (XML)
<Delete><Object><Key>2024/hasta_001.dcm</Key></Object></Delete>
GET/admin/presignPresign URL oluştur
QueryZorunlulukAçıklama
bucketzorunluBucket adı
keyzorunluNesne anahtarı
expiresopsiyonelSaniye — varsayılan 300, maks 604800
Response 200
{ "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ç).

POST/{bucket}/{key}?uploads1 — Başlat
Response
{ "upload_id": "mpu-abc123" }
PUT/{bucket}/{key}?partNumber=N&uploadId=...2 — Parça yükle
Response
{ "etag": "\"abc123\"", "part_number": 1 }
POST/{bucket}/{key}?uploadId=...3 — Tamamla
Body
{ "parts": [{ "part_number": 1, "etag": "\"abc\"" }] }
DELETE/{bucket}/{key}?uploadId=...İptal et

Upload'u iptal eder, yüklenen tüm parçaları siler.

Sistem Endpoint'leri

GET/healthSistem durumu — auth gerekmez
Response 200 / 503
{ "status": "ok", "version": "v62", "is_leader": true,
  "checks": { "redis": "ok", "postgres": "ok", "disk_used_pct": "34.2%", "nats": "ok" } }
GET/metricsPrometheus metrikleri — auth gerekmez

Prometheus text formatında sistem metrikleri. Grafana ile doğrudan kullanılabilir.

Admin Endpoint'leri

GET/admin/whoamiDoğrulanan kullanıcı
{ "access_key": "admin", "role": "super_admin", "tenant_id": "system" }
GET/admin/audit/exportKVKK audit export (CSV / JSON)
QueryVarsayılanAçıklama
formatcsvcsv veya json
from30 gün önceRFC3339: 2024-01-01T00:00:00Z
toşimdiRFC3339
tenantTenant filtresi (super_admin)
actionPUT, GET, DELETE, LOGIN...
limit10000Maks 50000
GET/admin/gc/statusGC istatistikleri
{ "total_runs": 48, "deleted_bytes_human": "2.3 GB", "last_run_at": "2024-03-10T14:00:00Z" }
POST/admin/rebuildRedis'i yeniden oluştur — super_admin
Yalnızca Redis veri kaybında kullanın. Disk sidecar'larından metadata yeniden oluşturur.
POST/admin/rotate-keysMaster key rotasyonu — super_admin
Request Body
{ "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.

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) }
}