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ık | Değer |
|---|---|
Authorization | Bearer <jwt>, /api/auth/* ve /api/server-info (ve /health) dışındaki her /api/* rotasında |
Content-Type | JSON gövdeli POST/PUT için application/json. İkili yüklemeler (derleme dosyaları) için application/octet-stream. |
Accept | application/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.
| Rota | Kabul edilen parametreler | Yanı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ış hesap429: 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 yok404: 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 ötesinde403: çağıranadminrolüne sahip değil409: 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), eksikchunksalanı403: yollardan birinde yazma izni yok409: 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 50offset: varsayılan 0path(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
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:
reason | Anlamı | locked_by |
|---|---|---|
File is locked | Başka biri kilidi zaten tutuyor | Kişinin adı |
No write permission | Bu yolda yazma hakkı yok | null |
Failed to create lock | Kilidin yerleştirilmesi veritabanında başarısız oldu | null |
Database error | Bu 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.
| Rota | Açıklama |
|---|---|
GET /api/tasks/{repo_id}/board | Tam pano: sütunlar + kartlar |
POST /api/tasks/{repo_id}/tasks | Bir 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}/assignees | Bir karta kullanıcılar atar |
GET /api/tasks/{repo_id}/assignable-users | Atanabilir kullanıcıların listesi |
POST /api/tasks/{repo_id}/columns | Panonun 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 hash413: 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çer | Ne 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.
| Rota | Gerçek denetim | Açıklama |
|---|---|---|
GET /api/admin/users | Yalnızca giriş, sonra süzme | Bir project_admin yalnızca kendi kapsamındaki hesapları görür |
POST /api/admin/users | Yalnızca giriş | Bir hesap oluşturur |
PUT/DELETE /api/admin/users/{id} | Süper yönetici | Bir hesabı günceller ya da siler |
POST /api/admin/users/{id}/reset-password | Süper yönetici | Parolayı sıfırlar ve hesabın tüm belirteçlerini iptal eder |
GET/POST /api/admin/groups | Süper yönetici | Gruplar geneldir, proje bazında devredilemezler |
GET/POST /api/admin/permissions/{repo_id} | Bu deponun yöneticisi | Yol desenine göre izin kuralları, bir gruba ya da bir kullanıcıya verilir |
GET/POST /api/admin/repositories/{repo_id}/rules | Bu deponun yöneticisi | Gönderimden önce uygulanan doğrulama kuralları |
GET/POST /api/admin/repositories/{repo_id}/webhooks | Bu deponun yöneticisi | Discord, Slack, Teams, ya da genel webhook |
GET /api/admin/locks | Yalnızca giriş, sonra süzme | Kilitler, yönetilen depolarla sınırlı |
POST /api/admin/locks/{lock_id}/force-release | Bu deponun yöneticisi | Başka birinin kilidini kaldırır. Denetim günlüğüne kaydedilir |
GET /api/admin/audit | Süper yönetici | Denetim günlüğü, süzülebilir ve sayfalanmış |
GET /api/admin/stats | Süper yönetici | Depolama hacmi, tekilleştirme oranı, büyüme |
GET /api/admin/licence | Süper yönetici | Lisans durumu ve tüketilen koltuklar |
GET /api/admin/repositories/{repo_id}/gc/preview | Bu deponun yöneticisi | Hiçbir şey silmeden çöp toplamayı simüle eder |
POST /api/admin/repositories/{repo_id}/gc | Bu 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.
| Önek | Neyi 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}/admins | Bir depoyu kim yönetir: proje yöneticilerinin atanması ve kaldırılması. |
/api/admin/repositories/{repo_id}/build-access | Bu projenin derlemelerini kim indirebilir, proje proje verilir. |
/api/admin/licence | Lisans 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.
| HTTP | Anlamı | Ne zaman |
|---|---|---|
| 400 | Bad request | Geçersiz parametre, bozuk JSON, gövde çok büyük (sert 413 sınırından önce) |
| 401 | Not authenticated | Belirteç yok, süresi dolmuş, geçersiz imza, ya da token_version uyuşmazlığı |
| 403 | Permission denied | Eksik yetenek, ya da path / repo üzerinde izin yok |
| 404 | Resource not found | Repo / dosya / commit / kullanıcı mevcut değil ya da erişilemez |
| 409 | Conflict | Kilit zaten tutuluyor, eşzamanlı commit, benzersiz kısıtlama ihlali |
| 413 | Payload too large | Gövde sınırın ötesinde (/files üzerinde 1 GB, /builds/upload üzerinde 8 GB) |
| 429 | Too many requests | Yalnızca /auth/login üzerinde (kullanıcı başına hız sınırlaması) |
| 500 | Internal server error | DB hatası, IO hatası. Sunucu tarafında günlüklenir, bug bildirirseniz timestamp ekleyin. |
| 503 | Service unavailable | Veritabanına ulaşılamıyor (health check) ya da geçiş sürüyor |