uVersion
Türkçe
İndir →

Wiki

REST API

uVersion sunucusunun HTTP uç noktaları başvurusu: kimlik doğrulama, depolar, dosyalar, kilitler, yorumlar, izleme listesi, üretim panosu, derlemeler, yönetim.

uVersion sunucusu bir JSON HTTP API sunar. Çağrılar bir JWT (JSON Web Token) ile doğrulanır, yani sunucunun oturum açtığınızda size verdiği ve ardından her istekte geri gönderdiğiniz oturum belirteciyle. Bu sayfa, masaüstü istemcisi, editör eklentileri ve uversion CLI tarafından kullanılan uç noktaları belgeler. Bunları doğrudan çağırarak uVersion'ı kendi iç araçlarınıza entegre edebilirsiniz (kendi panonuz, denetim betikleri, webhook'lar vb.).

API'nin tamamını kapsamaz: burada açıklanmayan birkaç rota ailesi vardır. Liste Kapsanmayan bölümler bölümünde yer alır.

Kurallar

Temel URL

Belgelenen her yol, örneğinizin URL'sine görelidir. Örnekler https://uversion.mygamestudio.com kullanır. Onu kendinizinkiyle değiştirin.

Gerekli başlıklar

BaşlıkDeğer
AuthorizationBearer <jwt>, /api/auth/* ve /api/server-info (ve /health) dışındaki her /api/* rotasında
Content-TypeJSON gövdeli POST/PUT için application/json. İkili yüklemeler (derleme dosyaları) için application/octet-stream.
Acceptapplication/json önerilir (sunucu varsayılan olarak JSON döndürür)

İki yanıt biçimi, ve hangisini okuduğunuzu bilmeniz gerekir

Sunucunun tek bir yanıt biçimi değil ikisi vardır, ve bunları karıştırmak bir entegrasyona başlayan biri için en pahalı hatadır.

1. Kimliği doğrulanmış rotalar (/api/auth/* dışındaki tüm /api/*) bir zarfla yanıt verir:

{
  "success": true,
  "data": { /* payload */ }
}

Hata durumunda, aynı rotalar şu yanıtı verir:

{
  "success": false,
  "error": "No write permission on this repository"
}

success alanı her zaman bulunur. data başarı durumunda, error başarısızlık durumunda bulunur, asla ikisi birden değil.

2. Kimlik doğrulama rotaları (/api/auth/login, /refresh, /validate, /logout, /register, /change-password) bu zarfı kullanmaz. Çıplak nesneyi, success veya data olmadan döndürürler:

{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "user": { "id": 12, "username": "alice", ... },
  "must_change_password": false
}

Ve hataları, success içermeyen tek alanlı bir nesnedir:

{ "error": "Invalid credentials" }

Pratik sonuç: /api/auth/login üzerinde belirteç .token içinde okunur, .data.token içinde değil. .data.token sorgulayan bir betik hiçbir görünür hata olmadan null alır.

Son olarak, GET /api/files/{repo_id}/content ne birini ne ötekini döndürür: dosyanın ham ikili içeriğidir, olduğu gibi.

Sayfalama

Ortak bir sayfalama kuralı yoktur: her rotanın kendine özgü olanı vardır, ya da hiç yoktur. Ne limit, ne total, ne de has_more varsayın.

RotaKabul edilen parametrelerYanıtın biçimi
GET /api/files/{repo_id}/history limit (varsayılan 50), offset (varsayılan 0), path Düz bir commit dizisi. total yok, has_more yok: dizi limit değerinden az öğe içerdiğinde sona ulaşmışsınızdır.
GET /api/repositories yalnızca include_inactive (ve yalnızca bir süper yönetici için dikkate alınır) Düz dizi. Ne limit ne de offset okunur.
GET /api/locks/{repo_id}/status Hiçbiri Deponun tüm kilitlerinin düz dizisi.
GET /api/files/{repo_id}/snapshot commit_hash, zorunlu Düz dosya dizisi.
GET /api/admin/audit page ve per_page, limit/offset değil, ayrıca filtreler (user_id, action, entity_type, from, to) Süper yöneticiye ayrılmıştır. Ortak kuralın yokluğunu iyi gösterir: sayfa numarasıyla sayfalayan tek rotadır.

Burada listelenmeyen her rota için, sonucunun tamamını tek bir çağrıda döndürdüğünü varsayın.

Hız sınırlaması

Genel bir hız sınırlaması yoktur: bir Unreal projesi binlerce dosya içerir ve toplu işlemler (kilit alma, bırakma) sürekli olarak bir kısıtlamayı tetikler. Bu nedenle API'yi yoğun biçimde çağırabilirsiniz.

Yalnızca bir rota sınırlıdır, /api/auth/login: kullanıcı adı başına her 15 dakikada 5 başarısız deneme. Başarılı oturum açmalar sayılmaz.

Belirteçlerin iptali

Her oturum belirteci bir tv alanı (token version) taşır, ki bu, veritabanındaki users.token_version sütununu yansıtır. Sunucu her istekte ikisini karşılaştırır. POST /api/auth/logout çağrısı, bir yöneticinin parola sıfırlaması ya da bir hesabın devre dışı bırakılması bu değeri artırır, bu da o kullanıcının tüm mevcut belirteçlerini anında geçersiz kılar, tüm makinelerinde.

Curl

API'yi bir terminalden çağırmak için. .token öğesine dikkat edin: /api/auth/login yanıtı çıplak nesnedir, geçilecek bir .data yoktur.

TOKEN=$(curl -s -X POST https://uversion.mygamestudio.com/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"secret"}' | jq -r '.token')

# Kimliği doğrulanmış rotalar ise zarfı gayet kullanır:
curl -s -H "Authorization: Bearer $TOKEN" \
  https://uversion.mygamestudio.com/api/repositories | jq '.data'

Kimlik doğrulama

Hatırlatma: bu bölümdeki hiçbir rota {"success", "data"} zarfını kullanmaz. Çıplak nesneyi döndürürler, ve hataları {"error": "..."} biçimindedir.

POST /api/auth/login

Bir kullanıcının kimliğini doğrular ve 30 gün geçerli bir oturum belirteci döndürür.

Body:

{
  "username": "alice",
  "password": "secret"
}

Response 200:

{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": 12,
    "username": "alice",
    "email": "alice@mygamestudio.com",
    "role": "lead"
  },
  "must_change_password": false
}

Hiçbir expires_at alanı yoktur: geçerlilik süresi belirtecin kendisinden okunur, ya da sunucu yapılandırmasından çıkarılır (varsayılan 30 gün).

must_change_password alanını yok saymayın. Bu alan, hesap hâlâ geçici bir parolayla çalışırken true değerindedir: yükleyicinin ilk yönetici hesabı için ürettiği parola, ya da bir yöneticinin bir sıfırlama sırasında yeni koyduğu parola. Buna bakmayan bir istemci kullanıcıyı süresiz olarak bu geçici parolada bırakır. Beklenen davranış, başka herhangi bir eylemden önce hemen POST /api/auth/change-password adresine yönlendirmektir.

Hatalar:

  • 401: geçersiz kimlik bilgileri veya devre dışı bırakılmış hesap
  • 429: bu kullanıcı adı için 15 dakikalık pencerede 5 başarısız denemeye ulaşıldı

Sunucu, hesap mevcut olmadığında sahte bir özet karşısında bile, parolanın Argon2id doğrulamasını her zaman çalıştırır. Yanıt bu nedenle her iki durumda da aynı süreyi alır, bu da istekleri zamanlayarak bir hesabın var olup olmadığını tahmin etmeyi engeller.

POST /api/auth/refresh

Oturum belirtecini paroladan tekrar geçmeden yeniler.

Başlıklar: Authorization: Bearer <mevcut_belirteç>. Gövde yok.

Response 200: tam olarak /login ile aynı biçim (token, user, must_change_password), yeni bir belirteçle.

Hatalar:

  • 401: geçersiz, süresi dolmuş belirteç, devre dışı bırakılmış hesap veya eskimiş token_version

POST /api/auth/logout

Geçerli kullanıcının tüm belirteçlerini, tüm makinelerinde, users.token_version değerini artırarak geçersiz kılar. Her yerde yeniden oturum açması gerekir: masaüstü istemcisi, editör eklentisi ve CLI dahil.

Response 200: { "logged_out": true }.

POST /api/auth/validate

Bir belirtecin hâlâ geçerli olup olmadığını kontrol eder ve karşılık geldiği kullanıcıyı döndürür. Kontrol ayrıca is_active ve token_version üzerinde de yapılır, dolayısıyla iptal edilmiş bir belirteç, henüz süresi dolmamış olsa bile reddedilir.

Gövdeye dikkat: bu bir nesne değil, çıplak bir JSON dizesidir, yani çift tırnakla çevrelenmiş belirteç.

curl -X POST https://uversion.mygamestudio.com/api/auth/validate \
  -H "Content-Type: application/json" \
  -d '"eyJhbGciOiJIUzI1NiIs..."'

Response 200: çıplak bir kullanıcı nesnesi.

{
  "id": 12,
  "username": "alice",
  "email": "alice@mygamestudio.com",
  "role": "lead"
}

Ne valid, ne expires_at, ne de refreshed_token vardır: geçerlilik HTTP kodundan (200 veya 401) okunur, ve yenileme /api/auth/refresh üzerinden yapılır, asla bu rota üzerinden değil.

POST /api/auth/register

Bu rota varsayılan olarak reddeder. Açık kayıt, işletmeci sunucu yapılandırmasında açıkça etkinleştirmediği sürece devre dışıdır; aksi hâlde yanıt 403 ve {"error": "Open registration is disabled; contact your administrator"} olur.

Normal işleyişte, hesaplar yönetim yoluyla oluşturulur: POST /api/admin/users, ya da yönetim panelinin Kullanıcılar sekmesi. /register öğesine bağımlı bir entegrasyon kurmayın.

POST /api/auth/change-password

Geçerli kullanıcının parolasını değiştirir. token_version değerini artırır, bu da önceki tüm belirteçleri, çağrıyı yapmak için az önce kullanılan dahil geçersiz kılar.

Depolar

GET /api/repositories

İzinler tablosuna göre süzülmüş olarak, geçerli kullanıcının erişebildiği depoları listeler. Bir depoda hiçbir izin kuralı olmayan bir kullanıcı onu görmez. admin ve lead rolleri tüm depoları görür.

Sorgu parametreleri: yalnızca bir tane, include_inactive (boole, varsayılan false), ve yalnızca bir süper yönetici için dikkate alınır. Ne limit ne de offset vardır: rota tüm listeyi döndürür.

Response 200:

{
  "success": true,
  "data": [
    {
      "id": 1,
      "name": "hero-rpg",
      "description": "Main RPG project",
      "storage_path": "/var/lib/uversion/data/hero-rpg",
      "is_active": true,
      "created_at": "2026-01-15T10:00:00Z",
      "updated_at": "2026-05-15T08:30:00Z"
    },
    {
      "id": 2,
      "name": "shared-assets",
      "description": "Shared asset library",
      "storage_path": "/var/lib/uversion/data/shared-assets",
      "is_active": true,
      "created_at": "2026-02-01T09:00:00Z",
      "updated_at": "2026-05-02T11:12:00Z"
    }
  ]
}

Döndürülen tek alanlar bunlardır. Özellikle ne owner, ne current_revision, ne file_count, ne size_bytes, ne de last_commit_at vardır: bir deponun API anlamında bir sahibi yoktur, ve hacim verileri yönetim istatistik rotalarından elde edilir.

GET /api/repositories/{repo_id}

Bir deponun ayrıntısı. Listedekiyle aynı nesne biçimi, aynı alanlarla.

Hatalar:

  • 403: bu depoya erişim yok
  • 404: depo mevcut değil

POST /api/repositories

Bir depo oluşturur. Süper yöneticiye ayrılmıştır, yani yalnızca admin rolüne. Bu bir yetenek değildir: kontrol doğrudan rol üzerindedir, dolayısıyla bir project_admin ya da bir lead bir 403 alır. Üründe hiçbir create_repos yeteneği yoktur.

Body:

{
  "name": "new-project",
  "description": "İsteğe bağlı açıklama"
}

Doğrulama:

  • name: 1 ila 255 karakter. Sunucu hiçbir karakter kümesi kısıtlaması uygulamaz. Diskteki depolama yolu, ad normalleştirilerek türetilir.
  • description: isteğe bağlı.

Benzersizlik, sunucu ölçeğinde yalnızca ad üzerinedir. Sahip kavramı yoktur, dolayısıyla «sahip başına» benzersizlik de yoktur.

Hatalar:

  • 400: boş ad ya da 255 karakterin ötesinde
  • 403: çağıran admin rolüne sahip değil
  • 409: bir depo bu adı zaten taşıyor

Dosyalar

POST /api/files/{repo_id}/upload-chunks

Bir dosyanın tamamını gönderir, parça listesini değil. Rota adı yanıltıcıdır: dosyayı bloklara (chunks) bölen sunucudur, siz değil. Bir çağıran bu bölmeyi asla kendisi yapmak zorunda değildir.

SHA-256 özetleriyle tanınan özdeş bloklar tekilleştirilir: sunucuda halihazırda bulunan bir blok, hangi dosyadan ya da depodan gelirse gelsin ikinci kez saklanmaz. Kenarından değiştirilmiş büyük bir ikili dosyanın disk alanında neredeyse hiç yer kaplamamasını sağlayan budur.

Body: base64 ile kodlanmış tam dosyayı içeren, tek alanlı bir nesne.

{
  "data": "<base64 ile kodlanmış dosyanın tamamı>"
}

Başka herhangi bir biçim, özellikle bir chunks dizisi, sunucu tarafından reddedilir.

Response 200:

{
  "success": true,
  "data": {
    "chunks": [
      { "hash": "abc123...", "offset": 0,       "size": 1048576, "compressed_size": 423152 },
      { "hash": "def456...", "offset": 1048576, "size": 2097152, "compressed_size": 891204 }
    ],
    "chunks_stored": 1,
    "chunks_deduplicated": 1
  }
}

chunks dizisi olduğu gibi, tam nesneler dahil, ardından gelen POST /commit içinde yeniden kullanılmalıdır. chunks_stored gerçekten diske yazılan blokları, chunks_deduplicated ise zaten var olanları sayar.

İzin: depoya yazma gereklidir, aksi hâlde 403.

Sınırlar: istek gövdesi 1 GB ile sınırlıdır (security.max_body_size_files ile ayarlanabilir). Bu sınırdan daha büyük bir dosya bu rotadan geçemez.

POST /api/files/{repo_id}/commit

Bir dosya listesinden ve bloklarından atomik bir commit oluşturur. Ya tüm dosyalar geçer, ya da hiçbiri.

action için kabul edilen tek üç değer add, modify ve delete

added değil, modified değil, deleted değil.

Bu bir biçim ayrıntısı değildir. Sunucu tam anlamıyla action == "delete" testini yapar ve geri kalan her şeyi bir ekleme ya da değişiklik olarak ele alır. "deleted" göndermek bu yüzden hiçbir şeyi silmez: dosya boş bir chunks dizisiyle yazma dalına gider, ve sunucu boş içerikli bir revizyon kaydeder. Hiçbir hata yükseltilmez. Dosya mevcut kalır, son sürümü boşlukla üzerine yazılır, ve kayıp yalnızca başka birinin bir sonraki sync işleminde görünür.

Body:

{
  "message": "Updated main level + hero pose pass",
  "commit_hash": "isteğe bağlı: birden çok yığını tek bir commit altında toplamak için",
  "files": [
    {
      "path": "Content/Maps/MainLevel.umap",
      "action": "modify",
      "chunks": [
        { "hash": "abc123...", "offset": 0,       "size": 1048576, "compressed_size": 423152 },
        { "hash": "def456...", "offset": 1048576, "size": 2097152, "compressed_size": 891204 }
      ]
    },
    {
      "path": "Content/Characters/NewVillain.uasset",
      "action": "add",
      "chunks": [
        { "hash": "fed789...", "offset": 0, "size": 524288, "compressed_size": 201004 }
      ]
    },
    {
      "path": "Content/OldAsset.uasset",
      "action": "delete",
      "chunks": []
    }
  ]
}

chunks isteğe bağlı değildir, bir delete üzerinde bile. Alan bulunmalıdır: onu atlamak tüm isteğin ayrıştırılmasını başarısız kılar. Bir silme için, boş bir dizi gönderin.

Dizi nesneler içerir, dizeler değil: /upload-chunks tarafından döndürülen {hash, offset, size, compressed_size} girdilerini değiştirmeden yeniden kullanın. Her hash 64 karakterlik onaltılık bir özet olmalıdır, aksi hâlde commit'in tamamı 400 ile reddedilir.

Kökteki commit_hash alanı isteğe bağlıdır. Birden çok ardışık çağrının tek ve aynı commit tarafından taşınmasını sağlar, ki masaüstü istemcisi büyük bir gönderimi yığınlara böldüğünde bunu yapar. Atlanırsa, sunucu bir tane hesaplar.

Response 200:

{
  "success": true,
  "data": {
    "commit_hash": "7f3a9b1c2d3e4f...",
    "files_committed": 3,
    "revisions": [
      { "path": "Content/Maps/MainLevel.umap",             "revision_number": 12 },
      { "path": "Content/Characters/NewVillain.uasset",    "revision_number": 1  },
      { "path": "Content/OldAsset.uasset",                 "revision_number": 8  }
    ]
  }
}

Döndürülen tek alanlar bunlardır. Ne files_changed, ne bytes_uploaded, ne bytes_deduped, ne de revision vardır. revision_number değerinin bir depo sürüm numarası değil, dosya başına bir sayaç olduğuna dikkat edin: commit'i tanımlayan commit_hash değeridir, ve bir durumu referans göstermek için saklanması gereken odur.

Hatalar:

  • 400: bozuk gövde, geçersiz blok özeti (beklenen: 64 onaltılık karakter), eksik chunks alanı
  • 403: yollardan birinde yazma izni yok
  • 409: değiştirilmiş bir dosyada başka biri tarafından tutulan kilit, ya da aynı dosyada eşzamanlı commit

GET /api/files/{repo_id}/snapshot

Deponun belirli bir commit anındaki durumunu döndürür: mevcut dosyaların listesi, revizyon numaraları ve boyutlarıyla. Masaüstü istemcisi tarafından klonlama ve zorunlu senkronizasyon için kullanılır.

Sorgu parametreleri:

  • commit_hash: zorunlu. Referans noktası görevi gören commit'in özetidir. Bu parametre olmadan istek reddedilir, ve «son durum» için bir varsayılan değer yoktur.

Hiçbir revision parametresi yoktur. Anlık görüntü commit özetiyle istenir, asla numarayla değil. Bilinmeyen bir commit 404 yanıtı verir.

Response 200: saran nesne olmadan, düz bir dizi.

{
  "success": true,
  "data": [
    {
      "path": "Content/Maps/MainLevel.umap",
      "revision_number": 12,
      "file_size": 84934656
    },
    {
      "path": "Content/Characters/Hero.uasset",
      "revision_number": 3,
      "file_size": 5242880
    }
  ]
}

Yanıt blok listelerini içermez. Bir içeriği almak için, dosyayı sunucu tarafında yeniden birleştiren GET /api/files/{repo_id}/content üzerinden geçin.

GET /api/files/{repo_id}/content

Bir dosyanın içeriğini belirli bir revizyonda indirir (sunucu chunk'ları yeniden birleştirir). Klon ve sync tarafından kullanılır.

Sorgu parametreleri:

  • path: dosya yolu (repo köküne göreli)
  • revision (isteğe bağlı): revizyon numarası. Varsayılan: sonuncusu.

Response 200: dosyanın ikili içeriği.

GET /api/files/{repo_id}/history

Deponun commit geçmişi, en yeniden en eskiye.

Sorgu parametreleri:

  • limit: commit sayısı, varsayılan 50
  • offset: varsayılan 0
  • path (isteğe bağlı): yalnızca bu dosyaya dokunan commit'leri tutar

Okunan tek üç parametre bunlardır. Ne author ne de since vardır: bilinmeyen bir parametre sessizce yok sayılır, bu da makul ama süzülmemiş bir yanıt verir. Yazara ya da tarihe göre çağıran tarafında süzün.

Response 200: düz bir commit dizisi.

{
  "success": true,
  "data": [
    {
      "commit_hash": "7f3a9b1c...",
      "message": "Fixed lighting in main level",
      "author": "alice",
      "created_at": "2026-05-15T08:30:00Z",
      "files": [
        {
          "path": "Content/Maps/MainLevel.umap",
          "revision_number": 12,
          "file_size": 84934656
        }
      ]
    }
  ]
}

Ne total, ne has_more, ne de limit ve offset hatırlatması vardır. Tüm geçmişi dolaşmak için, limit değerinden az öğe alana kadar offset değerini artırın.

Her commit dokunulan dosyaların listesini doğrudan taşır. Revizyonu bir silme olan bir girdi bu şekilde işaretlenir ve indirilecek içeriği yoktur.

Artımlı sync

Masaüstü istemcisi tarafından verimli senkronizasyon için kullanılan uç noktalar:

  • GET /api/files/{repo_id}/sync: bir revizyondan bu yana değişen dosyalar, bir delta sync için.
  • GET /api/files/{repo_id}/deletions: sunucu tarafında silinen dosyalar, silmeleri yerelde yaymak için.
  • GET /api/files/{repo_id}/list: reponun dosyalarının listesi.
  • POST /api/files/{repo_id}/checkout: kilitleri alır ve düzenlemeyi hazırlar.

Kilitler

Kilitlerin süresi asla dolmaz

Bir kilit, açıkça bırakılana dek tutulur: bir checkin ile, bir revert ile, ya da bir yöneticinin zorunlu kilit açmasıyla. Hiçbir otomatik süre dolması yoktur, ne bir saat sonunda, ne bir ay sonunda.

expires_at alanı yalnızca, veritabanındaki karşılık gelen sütun boş bir değeri kabul etmediği için vardır. Sunucu oraya yüz yıllık bir gözcü değeri yazar: bugün alınan bir kilit 2126 dolaylarında bir son tarih gösterir. Bu alan üzerine hiçbir şey inşa etmeyin, ve bu tarihi bir kullanıcıya göstermeyin.

heartbeat bu yüzden hiçbir şeyi uzatmaz. Bir gözetim sinyalidir, tek amacı yöneticilere hangi kilitlerin hâlâ etkin biçimde kullanıldığını göstermektir.

POST /api/locks/{repo_id}/acquire

Bir yol listesi üzerinde kilitler alır. Almalar bağımsızdır: acquired listesi başarıları, failed listesi ise başarısızlıkları gerekçeleriyle içerir.

Body:

{
  "paths": [
    "Content/Maps/MainLevel.umap",
    "Content/Characters/Hero.uasset"
  ]
}

Response 200:

{
  "success": true,
  "data": {
    "acquired": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "file_id": 12345,
        "file_path": "Content/Maps/MainLevel.umap",
        "user_id": 12,
        "username": "alice",
        "acquired_at": "2026-05-15T14:30:00Z",
        "expires_at": "2126-05-15T14:30:00Z"
      }
    ],
    "failed": [
      {
        "path": "Content/Characters/Hero.uasset",
        "reason": "File is locked",
        "locked_by": "bob"
      }
    ]
  }
}

Bu örnekteki 2126 son tarihi bir dizgi hatası değildir: yukarıda açıklanan gözcü değeridir. Kilit kalıcıdır.

Başarısızlık gerekçeleri. reason alanı, gösterilmeye yönelik İngilizce bir cümledir, kararlı bir kod değil. Bu dizeyi karşılaştıran bir mantık yazmayın, ve önekini «ayrıştırmayın»: bir sürümden diğerine yeniden ifade edilebilir. Şu anda üretilen değerler şunlardır:

reasonAnlamılocked_by
File is lockedBaşka biri kilidi zaten tutuyorKişinin adı
No write permissionBu yolda yazma hakkı yoknull
Failed to create lockKilidin yerleştirilmesi veritabanında başarısız oldunull
Database errorBu yolda veritabanı hatasınull

Geçersiz bir yol (mutlak, .. içeren, boş, 4096 karakterin ötesinde ya da bir boş bayt taşıyan) failed içinde bir girdi üretmez: tüm isteği başarısız kılar.

POST /api/locks/{repo_id}/release

Tuttuğunuz kilitleri bırakır. Body:

{
  "paths": ["Content/Maps/MainLevel.umap"]
}

Bu rotada bir force alanı yoktur. Bir tane eklemek hiçbir etki yapmaz: sunucu tanımadığı alanları yok sayar, istek başarılı olur, ve başkasının kilidi yerinde kalır. Buna güvenen bir entegratör dosyayı bıraktığını sanır ama bırakmamıştır.

Başka birinin kilidini kaldırmak için tek rota POST /api/admin/locks/{lock_id}/force-release rotasıdır. İlgili deponun yönetimine ayrılmıştır, ve işlem denetim günlüğüne kaydedilir. Kilit tanımlayıcısını alır, ki bunu GET /api/locks/{repo_id}/status ile elde edersiniz.

POST /api/locks/{repo_id}/heartbeat

Kilitlerinizin etkinlik zaman damgasını günceller. Bu hiçbir şeyi uzatmaz, çünkü hiçbir şeyin süresi dolmaz: bir gözetim sinyalidir, bir yöneticinin hâlâ kullanılan bir kilidi unutulmuş bir kilitten ayırt etmesine olanak tanır.

Body: saran nesne olmadan, doğrudan bir JSON yol dizisi.

["Content/Maps/MainLevel.umap", "Content/Characters/Hero.uasset"]

Geçersiz yollar, eskimiş bir izleme kalıntısının diğerlerini engellememesi için tüm yığını başarısız kılmak yerine tek tek yok sayılır.

GET /api/locks/{repo_id}/status

Deponun tüm kilitlerini, dosya adı ve onu tutan kişinin adıyla listeler.

Sorgu parametreleri: hiçbiri. Ne user filtresi, ne limit, ne de offset vardır. Rota deponun kilitlerinin tamamını döndürür, çağıran tarafında süzülmek üzere.

Response 200: saran nesne ve total olmadan, düz bir dizi.

{
  "success": true,
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "file_id": 12345,
      "file_path": "Content/Maps/MainLevel.umap",
      "user_id": 12,
      "username": "alice",
      "acquired_at": "2026-05-15T14:30:00Z",
      "expires_at": "2126-05-15T14:30:00Z"
    }
  ]
}

id alanı, POST /api/admin/locks/{lock_id}/force-release rotasına geçirilecek olandır.

Yorumlar & incelemeler

GET /api/comments/{repo_id}/comments?commit_hash=<hash>

Bir commit'in yorumlarını, iş parçacığı halinde (üst → alt) listeler.

Response 200:

{
  "success": true,
  "data": [
    {
      "id": 42,
      "commit_hash": "7f3a9b1c...",
      "file_path": "Content/Maps/MainLevel.umap",
      "author_id": 12,
      "author_username": "alice",
      "body": "LGTM, ship it",
      "parent_id": null,
      "created_at": "2026-05-15T15:00:00Z",
      "replies": [
        {
          "id": 43,
          "author_username": "bob",
          "body": "Thanks!",
          "parent_id": 42,
          "created_at": "2026-05-15T15:05:00Z"
        }
      ]
    }
  ]
}

POST /api/comments/{repo_id}/comments

Bir yorum oluşturur. file_path isteğe bağlıdır (commit yorumu vs. dosya yorumu). Var olan bir iş parçacığına yanıt vermek için parent_id.

Body:

{
  "commit_hash": "7f3a9b1c...",
  "file_path": "Content/Maps/MainLevel.umap",
  "body": "LGTM, ship it",
  "parent_id": null
}

POST /api/comments/{repo_id}/reviews

Bir commit üzerinde bir inceleme gönderir. approve_changes yeteneği gereklidir.

Body:

{
  "commit_hash": "7f3a9b1c...",
  "status": "approved",
  "comment": "Lighting looks great, approving"
}

Kabul edilen durum: approved, changes_requested, pending.

İzleme listesi

GET /api/watchlist/{repo_id}/watchlist

Geçerli kullanıcının bu depodaki izleme desenlerini listeler.

Response 200:

{
  "success": true,
  "data": [
    {
      "id": 1,
      "user_id": 12,
      "pattern": "Content/Characters/Hero/**",
      "notify_on": ["commit", "lock"],
      "created_at": "2026-04-10T10:00:00Z"
    }
  ]
}

POST /api/watchlist/{repo_id}/watchlist

Bir izleme deseni ekler. Body:

{
  "pattern": "Content/Characters/Hero/**",
  "notify_on": ["commit", "lock"]
}

Olası olaylar: commit (bir commit eşleşen bir dosyayı değiştirdi), lock (bir kilit alındı), review (bir inceleme yayımlandı).

DELETE /api/watchlist/{repo_id}/watchlist/{watch_id}

Bir izleme desenini kaldırır. { "success": true } döndürür.

Üretim panosu (görevler)

Eski modification-requests API'si kaldırıldı: üretim panosu onu soğurur (istekler kartlara dönüşür, origin='request'). Uç noktalar /api/tasks/{repo_id} altında iç içedir.

RotaAçıklama
GET /api/tasks/{repo_id}/boardTam pano: sütunlar + kartlar
POST /api/tasks/{repo_id}/tasksBir kart oluşturur
PUT/DELETE /api/tasks/{repo_id}/tasks/{task_id}Bir kartı günceller / siler
PUT /api/tasks/{repo_id}/tasks/{task_id}/assigneesBir karta kullanıcılar atar
GET /api/tasks/{repo_id}/assignable-usersAtanabilir kullanıcıların listesi
POST /api/tasks/{repo_id}/columnsPanonun sütunlarını yapılandırır

Ayrıca sunulan alt rotalar: kart yorumları, asset ve commit bağlantıları, ve ekler (kapak görseliyle). Sütunların yapılandırılması manage_board yeteneğini gerektirir (admin / lead).

Derlemeler

POST /api/builds/{repo_id}/upload?hash=<sha256>

Bir derleme dosyasını <storage_path>/builds/<hash> konumuna yükler. Sunucu, sorgu parametresinde verilen SHA-256 değerini alınan içerikle doğrular. Hash sunucu tarafında zaten varsa, yeniden kopyalamadan hemen 200 döndürür (derleme deposu tarafında tekilleştirme).

Başlıklar: Content-Type: application/octet-stream.

Body: ham ikili (JSON sarma yok).

Sınırlar: dosya başına 8 GB.

Hatalar:

  • 400: hash sorgu parametresi eksik ya da bozuk, ya da hesaplanan SHA-256 != verilen hash
  • 413: dosya > 8 GB

POST /api/builds/{repo_id}/publish

Tüm dosyaları yüklendikten sonra bir derlemenin manifest'ini kaydeder. publish_builds yeteneği gereklidir.

Body:

{
  "version": "0.1.5-nightly",
  "config": "Development",
  "platform": "Win64",
  "executable_path": "HeroRPG/Binaries/Win64/HeroRPG.exe",
  "release_notes": "Nightly build of main branch, 2026-05-15",
  "files": [
    { "path": "HeroRPG.exe",                "hash": "abc123...", "size_bytes": 12345678 },
    { "path": "HeroRPG/Content/Paks/pak0.pak", "hash": "def456...", "size_bytes": 2147483648 }
  ]
}

GET /api/builds/{repo_id}

Bu projenin yayımlanmış derlemelerini listeler.

Derlemelere erişim proje proje verilir. download_builds yeteneği yeterli değildir: sunucu genelinde olduğundan, yalnızca hesabın sıradan bir izleyici olmadığını söyler. Ayrıca ya depoya erişim, ya da bu projenin derlemeleri üzerinde, yöneticisinin /api/admin/repositories/{repo_id}/build-access aracılığıyla verdiği açık bir yetki gerekir. Bir proje yöneticisi yönettiklerine kendiliğinden erişir, bir süper yönetici hepsine.

Response 200:

{
  "success": true,
  "data": [
    {
      "id": 5,
      "version": "0.1.5-nightly",
      "config": "Development",
      "platform": "Win64",
      "published_by": "ci-nightly",
      "published_at": "2026-05-15T03:00:00Z",
      "size_bytes": 2159829326,
      "file_count": 142,
      "release_notes": "Nightly build of main branch, 2026-05-15"
    }
  ]
}

GET /api/builds/{repo_id}/{build_id}/file

Bir derlemenin bir dosyasını indirir. download_builds yeteneği gereklidir. Bir derlemenin manifest'i GET /api/builds/{repo_id}/{build_id}/manifest aracılığıyla mevcuttur.

Yönetim

Bu rotaları gerçekte ne korur

Beklenebilecek olanın aksine, /api/admin/* rotalarının her biri bir yetenekle korunmaz. Neredeyse tümü rol üzerine olan dört denetimden birinden geçerler. Bir capability, bir role bağlı adlandırılmış bir haktır; önemli nokta şudur: tüm sunucu üzerinde geçerlidir, çünkü hiçbir depo referansı taşımaz. Bu yüzden birini bir projeye sınırlamak için asla kullanılamaz.

DenetimŞunun için geçerNe işe yarar
Süper yönetici admin rolü, ve yalnızca o Tüm sunucu için geçerli olan her şey: hesaplar, gruplar, denetim günlüğü, genel istatistikler, lisans, sunucu güncellemesi.
Bu deponun yöneticisi admin, ya da tam olarak bu depoyu yöneten bir project_admin Bir projeye özgü olan her şey: izinler, doğrulama kuralları, webhook'lar, derleme erişimi, zorunlu kilit açma, çöp toplama.
Sunucu geneli yetenek Adlandırılmış yeteneği elinde bulunduran her rol Birkaç enine rota. Dikkat: yetenek tüm depolarda geçerlidir, doğası budur. Hiçbirine sahip olmayan bir project_admin, onun yerine yalnızca yönettiği depolarda kabul edilir.
Yalnızca giriş admin ya da project_admin İçeri alır, ama hiçbir şeyi yetkilendirmez. Onu kullanan rota, sonrasında kendi sonuçlarını çağıranın yönettiği depolarla kendisi sınırlamalıdır.

Başka bir deyişle: manage_users ve manage_permissions yetenekleri yalnızca kâğıt üzerinde vardır. Kurulum anında veritabanında gerçekten oluşturulurlar, ama hiçbir kod satırı onları sorgulamaz. Bir role vermek hiçbir şeyi değiştirmez. admin olmayan bir hesabın, kendisine manage_users verildiği için kullanıcı yönetebileceğini varsayan bir entegrasyon yazmayın: bir 403 alacaktır.

RotaGerçek denetimAçıklama
GET /api/admin/usersYalnızca giriş, sonra süzmeBir project_admin yalnızca kendi kapsamındaki hesapları görür
POST /api/admin/usersYalnızca girişBir hesap oluşturur
PUT/DELETE /api/admin/users/{id}Süper yöneticiBir hesabı günceller ya da siler
POST /api/admin/users/{id}/reset-passwordSüper yöneticiParolayı sıfırlar ve hesabın tüm belirteçlerini iptal eder
GET/POST /api/admin/groupsSüper yöneticiGruplar geneldir, proje bazında devredilemezler
GET/POST /api/admin/permissions/{repo_id}Bu deponun yöneticisiYol desenine göre izin kuralları, bir gruba ya da bir kullanıcıya verilir
GET/POST /api/admin/repositories/{repo_id}/rulesBu deponun yöneticisiGönderimden önce uygulanan doğrulama kuralları
GET/POST /api/admin/repositories/{repo_id}/webhooksBu deponun yöneticisiDiscord, Slack, Teams, ya da genel webhook
GET /api/admin/locksYalnızca giriş, sonra süzmeKilitler, yönetilen depolarla sınırlı
POST /api/admin/locks/{lock_id}/force-releaseBu deponun yöneticisiBaşka birinin kilidini kaldırır. Denetim günlüğüne kaydedilir
GET /api/admin/auditSüper yöneticiDenetim günlüğü, süzülebilir ve sayfalanmış
GET /api/admin/statsSüper yöneticiDepolama hacmi, tekilleştirme oranı, büyüme
GET /api/admin/licenceSüper yöneticiLisans durumu ve tüketilen koltuklar
GET /api/admin/repositories/{repo_id}/gc/previewBu deponun yöneticisiHiçbir şey silmeden çöp toplamayı simüle eder
POST /api/admin/repositories/{repo_id}/gcBu deponun yöneticisiÇöp toplamayı başlatır

Rollerin, rütbelerinin ve her birinin ne yapabileceğinin tam ayrıntısı rollere ve izinlere ayrılmış sayfadadır.

Bu sayfada kapsanmayan API bölümleri

Aşağıdaki rota aileleri vardır, sunucu tarafından bağlanmış ve sunulmaktadır, ama burada açıklanmamıştır. Entegrasyonunuz bunlara ihtiyaç duyarsa, bugün en güvenilir yol, masaüstü istemcisinin yaptığı çağrıları gözlemlemek, ya da bize yazmaktır.

ÖnekNeyi kapsar
/api/advisor/*Project Health: Unreal proje denetimi, bulguları, sütun başına puan ve yok sayılacak öğelerin ayıklanması.
/api/distribution/*Projeler arası dağıtım: bir kaynak depo ile bir hedef depo arasındaki bağlantılar, bir projeden diğerine dosya yayımlama, geçmiş.
/api/binaries/*Bir commit'e bağlı, UnrealGameSync modeline göre önceden derlenmiş editör ikilileri.
/api/watchlist/*Yukarıda açıklanan izleme desenlerinin ötesinde: bildirimler ve gelen kutusu.
/api/profile/*Geçerli kullanıcının profili.
/api/admin/repositories/{repo_id}/adminsBir depoyu kim yönetir: proje yöneticilerinin atanması ve kaldırılması.
/api/admin/repositories/{repo_id}/build-accessBu projenin derlemelerini kim indirebilir, proje proje verilir.
/api/admin/licenceLisans durumu ve tüketilen koltuklar.
/api/admin/server/*Yönetim panelinden sunucu güncellemesi.

Sağlık

GET /health

Kimliği doğrulanmamış, sunucu veritabanına erişebiliyorsa 200 OK döndüren uç nokta. Yük dengeleyici ya da izleme sağlık kontrolleri için kullanılır (Prometheus blackbox, Datadog synthetic vb.).

Response 200: OK (text/plain).

Response 503: veritabanına ulaşılamıyorsa.

GET /api/server-info

Sunucu meta verilerini (sürüm vb.) döndüren herkese açık, kimliği doğrulanmamış uç nokta. /health gibi, kimlik doğrulama ara katmanıyla korunmaz.

Hata biçimi

Hata durumunda, kimliği doğrulanmış bir rota { "success": false, "error": "..." }, ve bir /api/auth/* rotası { "error": "..." } yanıtı verir, her iki durumda da uygun bir HTTP koduyla.

error alanı bir koda değil, bir insana yönelik bir iletidir. Ne gövdede ne de bir başlıkta, kararlı hata kodlarından oluşan bir katalog yoktur. Bu yüzden içeriği üzerine mantık kurmayın, ve önekini çözümlemeyin: bu cümleler bir sürümden diğerine yeniden ifade edilir, ve sessizce kırılan bir dize testi hiç test olmamasından beterdir.

Bir davranışı dallandıracak tek şey HTTP kodudur. Aşağıdaki tablo onun okunuşunu verir.

HTTPAnlamıNe zaman
400Bad requestGeçersiz parametre, bozuk JSON, gövde çok büyük (sert 413 sınırından önce)
401Not authenticatedBelirteç yok, süresi dolmuş, geçersiz imza, ya da token_version uyuşmazlığı
403Permission deniedEksik yetenek, ya da path / repo üzerinde izin yok
404Resource not foundRepo / dosya / commit / kullanıcı mevcut değil ya da erişilemez
409ConflictKilit zaten tutuluyor, eşzamanlı commit, benzersiz kısıtlama ihlali
413Payload too largeGövde sınırın ötesinde (/files üzerinde 1 GB, /builds/upload üzerinde 8 GB)
429Too many requestsYalnızca /auth/login üzerinde (kullanıcı başına hız sınırlaması)
500Internal server errorDB hatası, IO hatası. Sunucu tarafında günlüklenir, bug bildirirseniz timestamp ekleyin.
503Service unavailableVeritabanına ulaşılamıyor (health check) ya da geçiş sürüyor