projectbin.

Sürüm takipli dosya deposu. Saf PHP, dış bağımlılık yok. Her yükleme sürüm numarasını artırır, her bayt sha256 ile mühürlenir.

https://bin.bariskeser.com

01Kimlik doğrulama

Okuma herkese açık, değiştirme anahtar ister. Bir updater istemcisi yeni sürüm var mı diye bakarken kimlik taşımak zorunda değil; depoya bir şey yazan veya silen her çağrı ise anahtarsız çalışmaz.

İşlemAnahtarKapsam
Proje / dosya / sürüm listelemegerekmez
İndirme, latest, verify, arama, changeloggerekmez
Yükleme (yeni sürüm)gerekirwrite
Proje oluşturmagerekirwrite
Dosya / sürüm silmegerekirdelete
Proje silmegerekiradmin
Anahtar yönetimigerekiradmin
Bunun anlamı

Adresi bilen herkes projeleri listeleyebilir ve artifaktları indirebilir. Depoya gizli bir şey koyma. Kapatmak istersen tek ayar yeter: config/config.php içinde public_read değerini false yap, ya da PB_PUBLIC_READ=0 ver — o zaman okuma da read kapsamlı bir anahtar ister.

Anahtar gönderme

Yazma çağrılarında üç taşıma biçiminden herhangi biri kabul edilir.

# başlık (önerilen)
curl -H "X-Api-Key: pb_xxx" https://bin.bariskeser.com/api/projects

# bearer
curl -H "Authorization: Bearer pb_xxx" https://bin.bariskeser.com/api/projects

# sorgu dizesi (log'lara düşer, dikkatli kullan)
curl "https://bin.bariskeser.com/api/projects?apikey=pb_xxx"
Anahtar saklama

Anahtarlar diskte yalnızca sha256 özeti olarak tutulur. Düz metin hâli üretildiği anda bir kez gösterilir ve sonradan geri alınamaz — kaybedilirse yenisi üretilir.

Kapsamlar

Dört kapsam var: read, write, delete ve admin — sonuncusu diğer üçünü de kapsar. Bir anahtar ayrıca belirli projelere kilitlenebilir; * tüm projeler demektir.

Okuma açıkken read kapsamı ve proje kilidi okumalarda uygulanmaz — aksi hâlde dar kapsamlı bir anahtar, anahtarsız bir çağrıdan daha az görürdü. Proje kilidi yazma ve silmede tam olarak geçerlidir: projects=myapp ile üretilmiş bir anahtar başka bir projeye yükleme yapamaz.

KoderrorAnlam
401unauthorizedYazma çağrısında anahtar gönderilmemiş
401invalid_keyAnahtar tanınmıyor — okuma açık olsa da geçersiz anahtar reddedilir, sessizce anonime düşmez
403forbiddenKapsam veya proje yetkisi yetersiz
403key_expiredAnahtarın süresi dolmuş
503not_initialisedHiç anahtar yok → php cli/pb.php init

02Kavramlar

Dört kavram var ve hepsi dosya sisteminde düz karşılığa sahip — özel bir araç olmadan okunabilir.

  • Projeprojectname= ile belirlenir, bir klasöre karşılık gelir. Ad sadeleştirilir: "MyApp Web"myapp-web. Proje yoksa otomatik oluşturulur.
  • Dosya — proje içindeki mantıksal ad, örneğin app.zip.
  • Sürüm — aynı dosyaya her yeni yükleme v1 → v2 → v3 diye otomatik artar. Numara flock altında ayrılır, eşzamanlı iki yükleme aynı numarayı almaz.
  • build — projedeki her kabul edilen yüklemede artan proje geneli sayaç.

Yanıtların tamamı JSON'dur. Başarılı yanıtlar "ok": true ve alanları düz biçimde döner; hatalar tek bir zarf paylaşır:

{ "ok": false, "error": "checksum_mismatch", "message": "...", "status": 422, "details": {} }

03Uç noktalar

Tümü /api altında. Kapsam sütunu, o çağrı için anahtarın taşıması gereken yetkiyi gösterir.

Sistem

MetotYolKapsamAçıklama
GET/api/healthCanlılık, PHP sürümü, limitler
GET/api/whoamiaçıkÇağıran anahtarın kimliği
GET/api/statsaçıkDepo geneli toplamlar

Projeler

MetotYolKapsam
GET/api/projectsaçık
POST/api/projects?projectname=Xwrite
GET/api/projects/{project}açık
DELETE/api/projects/{project}?confirm=1admin

Dosyalar ve sürümler

MetotYolKapsam
GET/api/files?projectname=Xaçık
GET/api/files/{project}/{file}açık
DELETE/api/files/{project}/{file}?confirm=1delete
DELETE/api/files/{project}/{file}/{version}delete

Yükleme, indirme, denetim

MetotYolKapsam
POST/api/upload?projectname=Xwrite
GET/api/download?projectname=X&file=Yaçık
GET/api/latest?projectname=X&file=Yaçık
GET/api/verify?projectname=Xaçık
GET/api/sha/{sha256}açık
GET/api/search?q=Xaçık
GET/api/changelog?projectname=Xaçık

Anahtar yönetimi

MetotYolKapsam
GET/api/keysadmin
POST/api/keys?label=ci&scopes=read,write&projects=*admin
DELETE/api/keys/{id|prefix}admin

04Yükleme

İki taşıma biçimi desteklenir. İkisi de aynı sonucu verir: yeni bir sürüm numarası.

multipart/form-data

curl -H "X-Api-Key: pb_xxx" \
     -F "file=@dist/app.zip" \
     "https://bin.bariskeser.com/api/upload?projectname=myapp&notes=nightly"

Ham gövde — script ve CI için en kolayı

SHA=$(sha256sum dist/app.zip | cut -d' ' -f1)

curl -H "X-Api-Key: pb_xxx" \
     -H "Content-Type: application/octet-stream" \
     --data-binary @dist/app.zip \
     "...?projectname=myapp&filename=app.zip&sha256=$SHA"

Parametreler

ParametreZorunluAçıklama
projectnameevetHedef proje; yoksa oluşturulur. project de kabul edilir
filenameham gövdedeDosya adı
sha256hayır, ama önerilirBeklenen özet. Tutmazsa 422 ve hiçbir şey yazılmaz
ashayırMantıksal ad. app-1.0.4.zipas=app.zip ile tek dosyanın sürümü olur
noteshayırSürüm notu; changelog'a ve sürüm listesine düşer
forcehayır1 ise aynı içerik olsa da yeni sürüm aç

Yanıt 201

{
  "ok": true,
  "deduplicated": false,
  "project": "myapp",
  "file": "app.zip",
  "version": 7,
  "latest_version": 7,
  "version_count": 7,
  "size": 4821903,
  "sha256": "2c44186f12b8aaca70da7a7dd5acc5cbc70db...",
  "md5": "...", "sha1": "...",
  "content_type": "application/zip",
  "build": 42,
  "uploaded_at": "2026-08-18T08:15:19Z",
  "uploaded_by": "ci (pb_8743bcb)",
  "notes": "nightly",
  "download_url": "https://bin.bariskeser.com/api/download?..."
}
Yinelenen içerik

Aynı baytlar tekrar yüklenirse yeni sürüm açılmaz; mevcut sürüm "deduplicated": true ile 200 döner. Bu, aynı CI işinin iki kez koşmasının sürüm numarasını şişirmesini engeller. Zorlamak için force=1.

05SHA kontrolleri

sha256 bu sistemdeki kimliktir. Bir baytın depoya girmesi ve orada kalması üç ayrı kapıdan geçer.

  1. Yükleme kapısı. sha256= gönderirsen, alınan baytlar hesaplanır ve karşılaştırılır. Tutmazsa 422 checksum_mismatch döner, geçici dosya silinir ve hiçbir sürüm oluşmaz.
  2. Yazma sonrası doğrulama. Dosya diske yazıldıktan sonra diskten yeniden hash'lenir. Tutmazsa sürüm geri alınır ve 500 write_corrupted döner — yarım yazılmış bir sürüm asla kayda geçmez.
  3. Sonradan denetim. /api/verify depodaki baytları istediğin zaman yeniden hash'ler ve kayıtla karşılaştırır.

Ayrıca her sürüm için md5 ve sha1 da kaydedilir, indirmede başlıkla döner. Bir özeti bilip dosyayı aramak için /api/sha/{sha256} tüm depoda arar.

06İndirme

version verilmezse latest gelir. version=3 veya version=first de geçerlidir.

curl -H "X-Api-Key: pb_xxx" -OJ \
  "https://bin.bariskeser.com/api/download?projectname=myapp&file=app.zip"

Dönen başlıklar transferi ikinci bir istek olmadan doğrulamanı sağlar:

Content-Type: application/zip
Content-Disposition: attachment; filename="app.zip"
ETag: "2c44186f12b8..."
X-Checksum-SHA256: 2c44186f12b8...
X-Checksum-MD5: b1f3ee2e57af...
X-Version: 7
Digest: sha-256=LEQYbxK4qspw2np91azFy8cNtjd4pcv/9JWqSU9CqDc=

If-None-Match ile koşullu istek 304 döner — CI'de aynı artifakt tekrar indirilmez.

07Bütünlük denetimi

Depodaki baytları yeniden hash'ler. Proje, tek dosya veya tek sürüm kapsamında çalışır.

curl -H "X-Api-Key: pb_xxx" \
  "https://bin.bariskeser.com/api/verify?projectname=myapp"

{ "ok": true, "checked": 12, "intact": 12,
  "failed": 0, "healthy": true, "failures": [] }

Bozulma varsa 500 ve dolu bir failures[] döner — problem alanı missing, checksum_mismatch veya size_mismatch olur. Böylece bir cron işi curl -f ile depo sağlığını sessizce izleyebilir.

08Hata kodları

Her hata sabit bir error kodu taşır; mesaj metni değişebilir, kod değişmez.

HTTPerrorAnlam
400no_file / missing_filename / empty_bodyGövde veya ad eksik
401unauthorized / invalid_keyKimlik
403forbiddenKapsam veya proje yetkisi
404project_not_found / file_not_found / version_not_foundBulunamadı
405method_not_allowedYanlış metot
413too_largeBoyut limiti aşıldı
415blocked_extensionUzantı reddedildi
422checksum_mismatchSHA tutmadı — kayıt yapılmadı
422invalid_project / missing_parameterParametre hatası
428confirmation_requiredSilme için confirm=1 gerekli
500write_corruptedDiske yazım bozuldu, sürüm geri alındı
503not_initialisedHiç anahtar yok

09CLI ve otonom kullanım

Sunucuda doğrudan depoya, uzaktan ise HTTP üzerinden çalışır. Uzak mod için --url + --key, ya da PB_URL / PB_KEY / PB_PROJECT ortam değişkenleri.

# kurulum ve anahtarlar
php cli/pb.php init
php cli/pb.php keygen --label=ci --scopes=read,write --projects=myapp
php cli/pb.php keys
php cli/pb.php revoke <prefix>

# gezinme
php cli/pb.php projects
php cli/pb.php ls        --project=myapp
php cli/pb.php versions  --project=myapp --file=app.zip
php cli/pb.php changelog --project=myapp
php cli/pb.php stats

# veri
php cli/pb.php upload --project=myapp dist/app.zip --notes="rc1"
php cli/pb.php push   --project=myapp dist/ --pattern="*.zip" --recursive
php cli/pb.php watch  --project=myapp dist/ --interval=5
php cli/pb.php pull   --project=myapp --file=app.zip --version=3 -o out.zip
php cli/pb.php verify --project=myapp

Her komuta --json eklenirse çıktı makine okunur olur.

watch nasıl karar veriyor

Bir dizini izler ve içerik hash'i değiştiğinde yükler — mtime'a değil sha256'ya bakar, yani aynı çıktıyı üreten bir rebuild yeni sürüm açmaz. Hâlâ yazılmakta olan dosyaları atlamak için boyutun iki okuma arasında sabit kalmasını bekler.

export PB_URL=https://bin.bariskeser.com
export PB_KEY=pb_xxx
php cli/pb.php watch --project=myapp ./dist --pattern="*.zip" --recursive

Updater entegrasyonu

Okuma açık olduğu için bir updater istemcisi anahtar taşımadan sürüm kontrolü yapabilir. Tek çağrı hem sürüm numarasını, hem sha256'yı, hem de indirme adresini verir:

curl -s "$PB/api/latest?projectname=myapp&file=app.zip"

{
  "ok": true,
  "version": 7,
  "latest_version": 7,
  "size": 4821903,
  "sha256": "2c44186f12b8...",
  "uploaded_at": "2026-08-18T08:15:19Z",
  "notes": "rc1",
  "download_url": "https://.../api/download?...&version=7"
}

Tipik bir updater döngüsü — kurulu sürümü karşılaştır, yeniyse indir, çalıştırmadan önce sha256'yı doğrula:

PB=https://bin.bariskeser.com
CURRENT=$(cat /opt/myapp/VERSION 2>/dev/null || echo 0)

META=$(curl -fsS "$PB/api/latest?projectname=myapp&file=app.zip")
LATEST=$(echo "$META" | grep -o '"version": [0-9]*' | grep -o '[0-9]*')
WANT=$(echo "$META" | grep -o '"sha256": "[a-f0-9]*"' | cut -d'"' -f4)

if [ "$LATEST" -le "$CURRENT" ]; then exit 0; fi

curl -fsS -o /tmp/app.zip \
  "$PB/api/download?projectname=myapp&file=app.zip&version=$LATEST"

GOT=$(sha256sum /tmp/app.zip | cut -d' ' -f1)
if [ "$GOT" != "$WANT" ]; then
  echo "checksum mismatch, aborting" >&2; exit 1
fi

# ... kur ...
echo "$LATEST" > /opt/myapp/VERSION
Bant genişliği

İndirme ETag döner. Updater aynı sürümü tekrar çekmesin diye If-None-Match gönderirse 304 alır ve gövde hiç aktarılmaz. Sürüm numarasını karşılaştırmak da aynı işi görür — ikisinden biri yeterli.

CI adımı olarak

SHA=$(sha256sum build/app.zip | cut -d' ' -f1)
curl -f -H "X-Api-Key: $PB_KEY" \
     -H "Content-Type: application/octet-stream" \
     --data-binary @build/app.zip \
     "$PB_URL/api/upload?projectname=myapp&filename=app.zip&sha256=$SHA&notes=$GIT_SHA"

10Kurulum notları

Bu sunucuya özgü ayarlar — taşınırsa gözden geçirilmesi gerekenler.

  • Yol: /home/DEPO/pi/SİTELER/bariskeser/projectbin. Birincil adres https://bin.bariskeser.com; eski https://bariskeser.com/projectbin yolu da çalışır ve ikisi aynı depoyu paylaşır — download_url istekte kullanılan alan adına göre üretilir.
  • Neden ayrı vhost: bin DNS kaydı Cloudflare üzerinden geçmiyor, yüklemeler doğrudan origin'e ulaşıyor — 100 MB gövde sınırı ve proxy zaman aşımı yok. Ölçüm: 4 MB Cloudflare üzerinden 81.9 sn (51 KB/s), doğrudan 0.27 sn (15.7 MB/s); 150 MB dosya 5.8 sn.
  • Vhost: /etc/apache2/sites-available/020-projectbin.conf (+ certbot'un ürettiği -le-ssl.conf). Kopyası repoda deploy/ altında.
  • DocumentRoot public/config/, data/, src/, cli/ fiziksel olarak web kökünün dışında.
  • AllowOverride None tüm yol boyunca. Apache üst dizinlerdeki .htaccess dosyalarını da okur; ana sitenin .htaccess'i DirectoryIndex disabled ve hataları bakım sayfasına yönlendiren ErrorDocument satırları içeriyor. Bunlar bir API'ye sızmamalı ve .htaccess vhost ayarını ezdiği için override'ları kapatmak tek gerçek çözüm.
  • TLS: Let's Encrypt. HTTP → HTTPS 301 yönlendirmesi vhost kapsamında RewriteEngine On ile birlikte tanımlı — certbot bu satırı eklemediği için yönlendirme başta etkisizdi.
  • Apache burada www-data:bariskeser olarak koşuyor (APACHE_RUN_GROUP=bariskeser), bu yüzden ağaç bariskeser:bariskeser; dizinler 2750, yazılabilir olan data/ ve config/ 2770, keys.json 0660.
  • Depo hem web sunucusu hem CLI tarafından yazıldığı için uygulama, oluşturduğu dizin ve dosyalara 0770 / 0660 uygular — kilit ve log dosyaları dâhil. Aksi hâlde ilk yazan kimlik diğerini kilitler.
  • config/, data/, src/, cli/ HTTP'den erişilemez: kök .htaccess yönlendirmesi, her dizinde Require all denied ve php_flag engine off.
  • Yükleme limitleri vhost içindeki php_value ile 2048M (sunucu mod_php kullanıyor). PHP-FPM'e geçilirse bunlar .user.ini'ye taşınmalı.
  • Okuma herkese açık (public_read = true). Kapatmak için config/config.php içinde false yap ya da PB_PUBLIC_READ=0 ver.
  • Yedek: data/ klasörünü kopyalamak tam yedektir — tüm sürümler, meta veriler ve changelog orada. config/keys.json ayrıca saklanmalı.