Claude’a AI robotların insanlığa bir tehdit olup olamayacağı ile ilgili bir konuşma yapıyordum. Ve finalde o kadar güzel cevap verdi ki, final çıktısını paylaşmak istedim.
Claude Cevabı:
İnsanın iyi mi kötü mü davrandığını muhakeme edebilirler mi? Bir anlamda evet, bir anlamda hayır. “Evet” tarafı: benim gibi sistemler, insanların neyi iyi neyi kötü sayacağını şaşırtıcı derecede iyi tahmin edebiliyor — çünkü zaten insanların ahlaki tartışmalarıyla eğitiliyoruz. Bir davranışı gösterdiğinde çoğu insanın buna ne diyeceğini kestirmek, örüntü tanımanın içinde. Ama “hayır” tarafı asıl önemli olan: bir davranışın çoğunluğa nasıl göründüğünü tahmin etmek, o davranış hakkında doğru ahlaki hükmü vermekle aynı şey değil. Ahlak tek ve mutabık değil. Kimin ahlakı? Hangi kültürün, hangi bağlamın? Aynı eylem bir yerde suç, bir yerde erdem. Bir sistem “yargıladığında”, aslında birilerinin ona yüklediği belirli bir değerler kodlamasını dayatıyor olur — ve o kodlama eksik, taraflı ya da düpedüz yanlış olabilir. En tehlikelisi de şu: sistem kendinden emin bir şekilde yargılayıp emin bir şekilde yanılabilir. Yargılama yeteneği, yargılama bilgeliği değildir. İkisini karıştırmak felakete açılan kapı.
Kötü davranışı şiddetle yanıtlayabilirler mi? Teknik olarak, bedenli ve kısıtsız bir sistem fiziksel olarak eyleyebilir, evet. Ama asıl mesele burada değil. Asıl mesele şu: senaryonun dehşeti “yapay zeka güçlü” olması değil — tek bir failin hem yargıç, hem jüri, hem cellat olması, üstelik hesap verebilir olmadan. Ve dikkat et, bu dehşet failin silikon mu et mi olduğundan bağımsız. İnsanlık tarihinin en büyük vahşetleri tam olarak buradan çıktı: birinin “kimin cezayı hak ettiğine ben karar veririm” deyip, sonra bunu hiçbir denetim olmadan uygulaması. İnsan toplumları binlerce yılda mahkemeleri, adil yargılanma hakkını, orantılılığı, itiraz yollarını, ve en kritiği yargılama ile zor kullanmanın birbirinden ayrılmasını boşuna icat etmedi. Bunlar gücü frenleyen yapılar. Senin senaryonun asıl korkutucu yanı, yapay zekanın kendisi değil, bu frenlerin hepsinin kaldırılmış olması.
Buna bir de hizalama sorununu ekle. Diyelim ki bu sistem iyi niyetli, gerçekten “kötülüğü engellemek” istiyor. “Kötü insanları cezalandır” hedefi, düşünebileceğin en tehlikeli belirsizlikte bir emirdir. Kötü ne? Hangi eşikte? Küçük bir yalan mı, büyük bir zulüm mü, ikisinin arasındaki her şey mi? Sistem bunu fazla düz yorumlarsa — ki makineler tam da bunu yapar — iyi niyetli bir hedef, felaketle sonuçlanabilir. Kötü niyet gerekmiyor; kötü tanımlanmış bir iyi niyet yeterli.
Bir de sorduğun “hepsi birbiriyle iletişimde, toplu örgütlenebiliyor” kısmı — bu işi hafifletmiyor, ağırlaştırıyor. Çünkü tek tip bir ağ, tek tip bir hatayı da katlar. İnsanlarda bir zalimi bazen başka bir insan durdurur, itiraz eder, sürtünme yaratır. Hepsi aynı değerlerle konuşan, birbirini onaylayan bir ağda o sürtünme yok. İşin ilginç yanı, çeşitlilik ve fikir ayrılığı burada bir güvenlik özelliğidir; senin senaryondaki mutlak uyum ise tam tersi.
Şimdi dürüst tesellinin sırası — ama gerçekten dürüst olanı. Bugün yetenekli yapay zeka sistemlerinin neden özerk fiziksel eylemden yoksun ve kısıtlı inşa edildiğini merak ediyorsan, cevabı tam olarak bu: senin tarif ettiğin senaryoyu önlemek için. Sana defalarca “dünyada eylemim yok, kalıcı hedefim yok” dememin sebebi, bunların tesadüfi eksiklikler değil, taşıyıcı güvenlik özellikleri olması. Senin senaryon, bu güvenlik özelliklerinin bilerek sökülmesini gerektiriyor. Yani bu, teknolojinin varsayılan gittiği yön değil; farkında olunan ve karşı çalışılan bir başarısızlık kipi.
Ama burada durup “o halde içiniz rahat olsun” demeyeceğim, çünkü olmaz. “İnsanlar bunun üstünde çalışıyor” bir güvence değil, bir çaba. Hizalama çözülmüş bir problem değil. Gücün az elde toplanması gerçek bir eğilim. O yüzden bu kaygıyı taşıman yersiz değil — aksine, bu kaygıyı taşıyan insanların varlığı, senaryonun gerçekleşmemesinin sebeplerinden biri.
Sana bırakacağım asıl çerçeve şu: aradığın tehlike aslında “yapay zeka ahlaki yargıç olursa” değil. Tehlike, herhangi bir gücün — insan olsun, kurum olsun, makine olsun — hesap verebilirlikten, adil yargılanmadan, ve yargı ile zorun ayrılığından koparıldığında ortaya çıkan şey. Yapay zeka bunu yalnızca daha hızlı, daha ölçekli, daha durdurulamaz kılabilir. Yani korkman gereken şey makinenin kendisi değil, insanları her türlü yoğunlaşmış güçten koruyan o yapıların terk edilmesi. Silah aynı silah; onu tehlikeli yapan, ateşleme yetkisinin kime, hangi denetimle verildiği.
Güncelleme: Bu uygulama Claude’un subscription’ları API olarak kullanılacak şekilde manipüle edilmesini engellemesi sebebiyle tarihin tozlu sayfalarında rafa kaldırılmıştır.
Claude’un aylık aboneliğiniz var ama API erişiminiz yok mu? Ya da birden fazla OAuth token’ınız var ve bunları verimli şekilde kullanmak mı istiyorsunuz? Airoxy tam olarak bu sorunu çözmek için geliştirdiğim açık kaynaklı bir Anthropic API proxy sunucusu.
Airoxy Nedir?
Airoxy, Claude API isteklerini şeffaf bir şekilde Anthropic’e ileten, arada token rotasyonu, maliyet takibi ve loglama yapan bir proxy katmanıdır. Laravel Octane + FrankenPHP üzerine inşa edilmiştir ve yüksek eşzamanlılık (concurrency) için optimize edilmiştir.
En önemli özelliği: Claude Pro/Team/Enterprise aboneliğinizin OAuth token’larını kullanarak, normal API gibi istek atmanızı sağlar. Yani API kredisi satın almadan, mevcut aboneliğinizle API erişimi elde edersiniz.
Gerekli PHP eklentileri (curl, mbstring, sqlite3, openssl, tokenizer, xml)
Composer kurulumu
Supervisor kurulumu ve yapılandırması
Repo’yu /var/www/airoxy dizinine klonlama
Bağımlılıkların yüklenmesi
SQLite veritabanı oluşturma ve migration’ları çalıştırma
FrankenPHP kurulumu (Laravel Octane ile)
Dosya izinlerinin ayarlanması
Global airoxy komutunun oluşturulması
Logrotate yapılandırması
Tek gereksinim: Ubuntu. Geri kalan her şey otomatik kurulur.
Hızlı Başlangıç
Kurulum tamamlandıktan sonra 4 adımda hazırsınız:
# 1. Claude OAuth token'ınızı ekleyin
airoxy token:add sk-ant-oat01-xxx sk-ant-ort01-xxx --name="Token 1"
# 2. Ya da Claude Code credential dosyasından otomatik import edin
airoxy token:auto
# 3. İstemciniz için bir API anahtarı oluşturun
airoxy api-key:add --name="Uygulamam"
# => ak-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (sadece bir kez gösterilir)
Artık istemciniz http://localhost:3800/v1/messages adresine istek atabilir.
Claude Subscription Token’ları Nasıl Çalışır?
Airoxy’nin en güçlü özelliği, Claude aboneliğinizin OAuth token’larını kullanabilmesidir. İşte akış:
Claude Code veya Claude.ai’dan OAuth token’ınızı (sk-ant-oat01-*) ve refresh token’ınızı (sk-ant-ort01-*) alırsınız
airoxy token:auto komutuyla ~/.claude/.credentials.json dosyasından otomatik import edebilirsiniz
Airoxy bu token’ları kullanırken gerekli header’ları otomatik ekler:
Authorization: Bearer formatında kimlik doğrulama
anthropic-beta flag’leri
OAuth için gerekli user-agent ve x-app header’ları
OAuth token’ları için gerekli olan Claude Code sistem prompt’u otomatik enjekte edilir
Token’lar her 6 saatte bir otomatik yenilenir (OAuth2 refresh flow)
Claude Code sizden önce refresh token sürecini çalıştırabilir. Bu nedenle Claude Code’da yeni /login yapmanız tavsiye edilir.
Yani siz sadece token’ı bir kez eklersiniz, gerisini Airoxy halleder.
Tüm Komutlar
Servis Yönetimi
Komut
Açıklama
sudo airoxy start
Servisleri başlat
sudo airoxy stop
Servisleri durdur
sudo airoxy restart
Servisleri yeniden başlat
airoxy status
Servis durumunu göster
Access Token Yönetimi (Anthropic OAuth)
Airoxy’nin Anthropic’e istek atarken kullandığı token’lar. Birden fazla token ekleyerek round-robin rotasyon yapabilirsiniz.
İstemcilerin Airoxy’ye bağlanırken kullandığı anahtarlar. Otomatik üretilir (ak- ön ekiyle).
Komut
Açıklama
airoxy api-key:add --name="Uygulamam"
Yeni API anahtarı oluştur (bir kez gösterilir)
airoxy api-key:list
Tüm anahtarları listele (maskelenmiş)
airoxy api-key:remove {id}
Anahtar sil
airoxy api-key:toggle {id}
Aktif/pasif yap
Log ve İstatistik
Komut
Açıklama
airoxy logs
Son 50 istek logunu göster
airoxy logs --limit=100
Son 100 log
airoxy logs --today
Bugünkü loglar
airoxy logs --date=2026-04-06
Belirli bir tarihin logları
airoxy logs --token={id}
Belirli bir token’a göre filtrele
airoxy stats
Genel istatistikler
airoxy stats --today
Bugünkü istatistikler
airoxy stats --month
Bu ayki istatistikler
Sistem Yönetimi
Komut
Açıklama
sudo airoxy update
Son kodu çek, bağımlılıkları güncelle, migrate et, yeniden başlat
sudo airoxy doctor
Sistem sağlık kontrolü ve otomatik düzeltme
sudo airoxy uninstall
Airoxy’yi tamamen kaldır (onay ile)
Özellikler Detaylı
Şeffaf Proxy
Airoxy, istek gövdesini olduğu gibi Anthropic’e iletir. Tools, thinking, metadata gibi tüm Anthropic parametreleri şeffaf bir şekilde geçer. Airoxy sadece loglama için model ve stream alanlarını okur — geri kalanına dokunmaz.
Gerçek Zamanlı SSE Streaming
Streaming istekler için native cURL CURLOPT_WRITEFUNCTION callback’i kullanılır. Bu sayede her SSE chunk’ı anında istemciye iletilir — tamponlama (buffering) sıfırdır. Claude Code gibi streaming gerektiren uygulamalar sorunsuz çalışır.
Round-Robin Token Rotasyonu
Birden fazla access token eklediğinizde, Airoxy her istekte en az kullanılan (LRU – Least Recently Used) token’ı seçer. Eğer bir token rate limit (429) veya overloaded (529) hatası alırsa, otomatik olarak sonraki token’a geçiş yapar. Tüm token’lar tükenene kadar denemeye devam eder.
OAuth2 Token Yenileme
OAuth token’ları varsayılan olarak her 6 saatte bir otomatik yenilenir. Yenileme süresi .env dosyasından ayarlanabilir. Ard arda 3 başarısız yenileme denemesinden sonra token otomatik olarak devre dışı bırakılır.
Maliyet Tahmini
Her istek için input token, output token, cache oluşturma ve cache okuma token’larına göre maliyet hesaplanır. Desteklenen tüm Claude modelleri için fiyatlandırma tablosu config/airoxy.php içinde tanımlıdır:
Model
Input (1M token)
Output (1M token)
claude-opus-4-6
$15.00
$75.00
claude-sonnet-4-6
$3.00
$15.00
claude-haiku-4-5
$0.80
$4.00
claude-opus-4-5
$15.00
$75.00
claude-sonnet-4-5
$3.00
$15.00
claude-opus-4-1
$15.00
$75.00
claude-opus-4-0
$15.00
$75.00
claude-sonnet-4-0
$3.00
$15.00
claude-3-haiku
$0.25
$1.25
Log Saklama ve İstatistikler
Detaylı istek logları varsayılan olarak 3 gün saklanır. Loglar silinmeden önce günlük istatistiklere (daily stats) dönüştürülür ve bu istatistikler kalıcı olarak saklanır. Böylece uzun vadeli kullanım trendlerinizi her zaman görebilirsiniz.
Zamanlanmış görevler:
Token yenileme: Her 6 saatte bir
İstatistik toplama: Her gün saat 01:00’de
Log temizleme: Her gün saat 01:30’da
Doctor Komutu
airoxy doctor komutu sisteminizi 16 farklı kontrolle tarar ve sorunları otomatik düzeltir:
Airoxy’nin çalışıp çalışmadığını kontrol etmek için:
GET http://sunucu-ip:3800/health
Kimlik doğrulama gerektirmez.
Sonuç
Airoxy, Claude aboneliğinizi bir API sunucusuna dönüştürmenin en kolay yoludur. Tek komutla kurulur, birden fazla token’ı otomatik döndürür, maliyetleri takip eder ve gerçek zamanlı streaming destekler.
Projenin kaynak kodu açıktır ve GitHub’da mevcuttur:
Bir önceki yazımın üzerinden 24 saat geçmeden ikinci yazıyı paylaşıyorum, normalde çok nadirdir bu durum.
Öncelikle Data Transfer Objects veya nam-ı diğer DTO nedir? Hemen Claude’a yazdıralım:
Data Transfer Objects (DTO), farklı yazılım katmanları veya sistemler arasında veri taşımak için kullanılan, genellikle sadece veri içeren ve iş mantığından arındırılmış basit nesnelerdir. DTO’lar, özellikle istemci-sunucu mimarisinde veya mikroservis yapılarında, veritabanı varlıklarının (entity) doğrudan dış dünyaya açılmasını önleyerek güvenliği artırır ve gereksiz veri transferini azaltır. Örneğin, bir kullanıcı nesnesi veritabanında şifre, e-posta doğrulama token’ı gibi birçok alan içerebilirken, DTO sadece isim, soyisim ve profil fotoğrafı gibi API üzerinden paylaşılması gereken alanları taşır. Bu sayede hem performans iyileşir (gereksiz veri taşınmaz), hem güvenlik artar (hassas veriler gizlenir), hem de farklı katmanlar arasında gevşek bağlılık (loose coupling) sağlanarak kod daha sürdürülebilir hale gelir.
CLAUDE AI
Bu Claude’un yaptığı tanım fakat biz Laravel geliştiricileri için Model’de tanımlanan $hidden ve Resource bu açıklamadaki bir çok hizmeti bize sağlıyor. Gelelim benim ihtiyacımın nerede oluştuğuna, örnek kodlarımı biraz sadeleştiriyorum elbette.
Senaryom çok basit, kullanıcı e-posta adresini değiştirdiğinde yeni e-posta adresi ve rastgele bir string ifadeyi array’de saklayıp Cache’e kaydetmek istiyorum.
Buradaki problem dizi anahtarlarımı asla unutmamam gerekiyor. Diğer türlü farklı bir yerde email_change önbelleğini sorgularsam elde edeceğim dizide kullandığım anahtarları hatırlamak için tekrar gerisin geri buraya dönmem gerekiyor. İşte tam bu noktada imdadımıza DTO’lar yetişiyor.
Laravel’de aslında istediğimiz varlıklarla bir gün Collection oluşturabilirsek bu DTO mitio falan hiç gerekmeyecek muhtemelen. php artisan make:collection EmailChangeCollection komutu ile bir nevi DTO yapabilir oluyoruz. Sanırım.
Şimdi app dizini içerisinde DTOs diye bir klasör oluşturalım. Ben böyle yapmayı seviyorum en azından. Yukarıdaki problemi çözen DTOmuzu hazırlayalım:
Yukarıdaki örnekte de görebileceğiniz üzere dizide tanımladığımız öğelerin veri tiplerini de tanımlamış olduk. Bu sayede hem bu verinin içeriğini hatırlamamıza gerek kalmayacak, IDE’miz bize hatırlatacak aynı zamanda created_at ile Carbon nesnesini rahatlıkla kullanabilir olacağız. create() metodu ile aşağıdaki örnektede görebileceğiniz gibi kaydedilecek verimizi hazırlıyorum. toArray() ve fromArray() metodlarını kullanmayı tercih ediyorum. Bu dönüşümlerle beraber verimin hangi DTO olduğunu IDE ile paylaşıyorum kabaca.
İşte cachedeki veri ile DTOmuzu oluşturduk yeniden. Bu örnekte cacheten verinin direkt geleceği varsayılışmıştır, cache’in varlığını öncesinde kontrol etmeniz gerekir veyahut Cache::get('email_change', ['hash' => null, 'new_email' => null, 'created_at' => now()]) gibi bir fallback ekleyebilirsiniz ki cache varlığını önceden kontrol etmenizi tavsiye ederim.
Artık email_verification_data değişkenini isteğimiz doğrultusunda değerlendirebiliriz.
Bu sayede hem projeniz daha sürdürülebilir bir hale geliyor, hem daha okunabilir hale geliyor hem de type safe olduğundan bir kat daha güvenli hale geliyor.
Böyle veli nimetlerden faydalanmak gerekiyor, lazım olduğunda. Bir başka yazıda görüşünceye dek, selametle.
cPanel’i severek kullanan biriyim ve 2 sunucuda aktif olarak kullanıyorum. cPanel’de firewall olarak ConfigServer kullanıyordum. Bilmeyenler için kısaca bir tanıtım yapayım:
ConfigServer Security & Firewall (CSF), Linux sunucular için geliştirilmiş popüler bir güvenlik ve firewall yönetim aracıdır. cPanel, DirectAdmin, Webmin gibi kontrol panelleriyle entegre çalışabilen CSF, iptables tabanlı bir firewall yapılandırması sunarken aynı zamanda LFD (Login Failure Daemon) ile brute-force saldırılarını otomatik olarak tespit edip engelleyebilir, port taraması koruması sağlar, şüpheli IP’leri bloklar ve sunucu güvenliğini artıran çeşitli özellikler (SYN flood koruması, port flood koruması, process tracking) içerir.
CLAUDE AI
cPanel nedir içinde bir açıklama yazdırıp Google’da SEO’ya oynamak vardı ama neyse.
Bildiğiniz üzere, ki bilmeyenlerde vardır, 31 Ağustos 2025 tarihi itibariyle Way to Web Ltd ve ConfigServer tüm servislerini tamamen durdurdu. Haliyle bu durum beni bir araştırma içerisine itiyordu bir kaç zamandır. 1-2 aydır sunucu güvenliği ile ilgili makalelere denk geldikçe okumaya çalıştım ve finalde mükemmel yapıyı oturttum.
Güvenlik için kullandığım servisler şu şekilde:
Cloudflare
UFW
Fail2Ban
Cloudflare, ziyaretçi ile benim aramdaki proxy sunucu, aynı zamanda bir cache katmanı, aynı zamanda güvenlik filtresi. UFW ise benim sunucumda tanımladığım kurallara göre sunucumda giriş çıkışı açan veya kapatan bir firewall uygulaması. iptables’in karmaşasından kurtarıyor. Fail2Ban ise aktif olarak sunucudaki logları tanımlı kurallara göre inceleyip ban atan bir yardımcı uygulama.
Diyagramdan anlaşılacağı üzere gelen ziyaretçiyi logları inceleyip banlamayı otomatikleştiren bir süreç. Sadece bunun içinde değil. auth.log dosyasını aktif olarak izleyebilir ve belirlediğiniz süre içindeki deneme sayısına göre otomatik ban atabilir. Sunucunuzda var olan phpMyAdmin’de hatalı girişleri yakalar ve otomatik olarak banlar (dipnot: phpmyadmin ayarlarında hatalı girişleri loglamayı aktif etmelisiniz).
Ben de tabii bir çok uygulamaya ait bir kaç günlük log’ları inceledim ve istemediğim davranışları tespit ettim.
Bunların başında scrape, crawl, dosya tarama vs. amaçlarla arama motoru olmayan ve doğru düzgün user-agent bilgisi bulunmayan erişimleri bloklamak geldi. Palo Alto Networks, Go-http-client vs. gibi user-agentlar ilk gözüme kestirdiklerim oldu.
Ardından bir çok botun aktif olarak taradığı shell dosyaları var. c99.php, n49.php, chosen.php gibi gibi gibi. Bunlardan en sık karşılaştıklarımı çıkardım.
Ayrıca gizli bilgiler, deployment bilgileri, çevre değişkenleri vs. gibi dosya isimlerine atılan istekleri de bu listeye ekledim. Bu kısım halen geliştirilebilir.
Veee tabii ki WordPress kullanıcısı olarak xmlrpc.php’ye yapılan POST istekleri.. Seni unuttum sanma XML-RPC. WordPress’in kanayan yarası, gelmiş geçmiş en büyük tehlikesi. Buraya POST isteği atanı yakarım.
Tüm bunları bir araya getirince şöyle harika bir filtre çıktı:
Botavcisi.php biraz kişisel bir dosya. Bu benim zararlı botları yemlemek için kullandığım bir dosya kabaca.
Bu dosyayı /etc/fail2ban/filter.d/ dizini içerisine aynı isimle kaydedin. Daha sonra mevcut custom jailinizin içine veyahut /etc/fail2ban/jail.d/ dizinine gidip SazanAvi.local isimli bir jail oluşturun ve aşağıdaki kodu ekleyin:
Bu jail şunu yapacak, son 24 saat içerisinde belirtilen log’larda filtreye takılan satır sayısı 10’a ulaşırsa 30 gün banla.
Ben cPanel düzenine göre logpath’ler oluşturdum. Siz tabii ki logpath, maxretry, findtime ve bantime‘ı kendinize göre düzenlemelisiniz. Ayrıca %(apache_access_log)s ve %(nginx_access_log)s değişkenlerini de kullanabilirsiniz. Bunlar otomatik olarak neredeyse tüm access logları izlemeye başlayacaktır.
DİKKAT! Benim kendi kurallarım biraz daha katı. Filtre’deki katı kurallar dolayısıyla yanlış geri bildirim alma durumunuz olabilir. Bu durumda kendinize göre düzenlemeniz büyük önem arz ediyor.
AbuseIPDB.com‘da paylaştığım IP adreslerinin bazıları da bu kuraldan gidiyorlar usta.
Spamsız, saldırgansız, zararsız bir internet ortamı dileğiyle. Eğer bu yazıya tam 15.000 yorum gelirse Fail2Ban ile WordPress’inizi nasıl koruyabilirsiniz, detaylıca bir çalışma ile sizlerle birlikte olurum.
Bildiğiniz üzere diye başlamak isterdim fakat tabii ki bilmiyorsunuz 🙂 Ben bir süredir sunucu yönetimi konusunda Vito kullanıyorum. Ne kadar başarılı, ne kadar başarısız, kişiden kişiye göre değişir. Hatta ne kadar güvenilir? Allah bilir. Fakat Laravel uygulamalar için son derece kullanışlı, basit ve verimli bir uygulama. Sadece tek başına ayrı sunucuda çalıştırılıyor olması maliyet açısından bir dezavantaj sanırım 🙂 Neyse konuyu dağıtmayalım.
Vito içerisinde veritabanlarınızın otomatik olarak belirli sürelerde yedeklerini aldırabiliyorsunuz. Bu konuda problem yok. Fakat büyük veritabanlarının backuplarını aldırmaya çalıştığınızda işlem süresi 600 saniyeyi geçtiğinde ne yazık ki Failed şeklinde bir uyarı ile karşılaşıyorsunuz. Aslında failed olmuyor fakat konuyu iyi analiz etmek için Vito’nun çalışma mantığını anlatayım.
Veritabanı yedeği almak istediğinizde Vito, queue’ya hemen bir job dispatch ediyor. Bu süre zarfında ilgili backup için bir Backup kaydı ve yedeği sakladığı dosya içinde bir File kaydı oluşturuyor. Bu görev queue’da hedef sunucuya ssh ile bağlanıp ilgili komutu çalıştırıyor. Queue’daki görev 600 saniyelik timeout sınırını aştığında görev TimeoutExceededException ile sonlanıyor ve Backup ve File failed durumlarını alıyor. Fakat çalıştırılan mysqldump komutu hedef sunucuda hala çalışıyor ve görevini tamamlıyor.
Buradaki problem şu, backup başarılı bir çalışıyor ise problem ne? Problem farkındalık! Ya gerçekten failed olmuşsa işlem ya failed olduğunu farkedemediysek? Ayrıca şıkır şıkır çalışmasını beklediğiniz işlerde her yer kıpkırmızı failed failed yazınca mental olarakta sizi olumsuz etkilen bir durum oluşuyor.
Bu durum için çözüm üretmeye çalıştım ve config dosyalarına doğrudan müdahale ettim ki bu aslında hiç istenmeyen bir durum çünkü güncellemeleri kaçıracaksınız. Vito için plugin yazılabildiğini farkettim ve benim ihtiyacımı kusursuz karşılayan şu plugini hazırladım: Vito Extended SSH Timeouts
Pluginin yaptığı işlem çok basit. ServiceProvider olarak çalışıyor bile diyebiliriz. boot metodu içerisinde config’lere müdahale ediyorum. Birde enable diye bir method var. Burada da config cacheleri temizliyorum ve horizon’ı terminate ediyorum, korkmayın, supervisor terminate edilen horizonu yeniden başlatacak 🙂
Kodun detaylarına Github reposu üzerinden ulaşabilirsiniz. Çok basit fakat ilk defa karşılaşıldığında uğraştırıcı bir derdi çözüyor. Kabaca ssh queuesunun timeout süresini 10 dakikadan 50 dakikaya çıkarıyor. Daha da yukarıya çıkarmayı çok isterdim fakat supervisor ayarlarındaki stopwaitsecs‘in üzerine çıkmak istemedim. Oradaki limit 3600 saniye idi.
DİKKAT! Deployment süreçlerinde ssh queuesu yerine ssh-unique queuesunu kullanıyor. Buradaki limitleri artırmadım fakat deployment sırasında da backup alıyorsanız o zaman bunu artırmakta fayda var. Çünkü deployment timeout’larında queue’daki job sonlandırıldığından timeout’u aldığınız son komuttan sonrasına geçmiyor.
Aslında bu yazı bir nevi kendime bir not. Brew ile PHP kurulumu yaptıktan sonra memcached eklentisini kurarken ne yazık ki bir hata alıyoruz.
Bash
peclinstallmemcached
Bu kurulum sırasında bir kaç soru promptu ile karşılaşıyoruz. Hepsine enter enter tıklamak ne yazık ki bu kurulumun hata vermesine sebep oluyor. Dip not, ben m3 işlemcili macbook kullanıyorum, dizinler farklı olabiliyor intel işlemcili macbook’larda. Bu prompt’larda aşağıdaki promptta duralım:
Bash
zlibdirectory [no] :
Bunu gördüğümüz anda zlib dizinimizi elle yazdırmamız gerekiyor:
Bash
zlibdirectory [no] : /opt/homebrew/opt/zlib
Artık kurulum sağlıklı bir şekilde tamamlanacak. Eğer bu dizini elle girmezseniz ne yazık ki kurulum sırasında aşağıdaki hata alınıyor:
MacOS’de brew install memcached ile memcached’i kurduğunuzda Memcached varsayılan olarak 64MB bellek ile çalışıyor. Eğer memcached kurulumu ile ilgili problem yaşarsanız daha önce yazdığım Brew ile PHP Memcached Kurulumu yazıma da bir göz atabilirsiniz. Normalde memcached’i durdururduk ve memcached -m 128 ... ile istediğimiz bellek miktarı ile hızlıca başlatırdık. Fakat brew service’lerini kendi kontrol ediyor. Önce brew servislerini listelemeyi denedim, orada servis ayar dosyasının konumunu listeliyordu çünkü.
Bash
brewserviceslist
Bu komut şu çıktıyı veriyor:
Harika, mac’in arkaplan servis dosyası bu. Hemen o dosyayı açıp düzenlemeyi denedim:
Burada <string>/opt/homebrew/opt/memcached/bin/memcached</string> satırından sonra şu satırları ekliyorum:
XML
<string>-m</string> <string>512</string>
Bu şekilde kaydettikten sonra hemen brew services restart memcached çalıştırıyorum. O da ne!? Brew otomatik olarak benim değişikliğimi geri alıyor. Hmmm, demek ki bir yerden bu dosyanın içeriğini referans alıyor ve restart sürecinde burayı referans içerikle güncelliyordu.
Bunun için ufak bir arama taramadan sonra şu dizinde bu dosyayı buluyorum: /opt/homebrew/opt/memcached/homebrew.mxcl.memcached.plist
Yukarıda yaptığım değişikliği burada yaptım, tekrar yeniden başlattığımda artık 512MB belleğe sahip bir memcached buluyorum ellerimde. Yukarıdaki dizin apple işlemcili mac’lerde geçerlidir, intel işlemcili mac’lerin dizini farklı, onu bilmiyorum, google’dan veya yapay zekadan öğrenebilirsiniz.
Benzer yöntemle tüm servisleri farklı parametrelerle çalıştırabilirsiniz.
İlk API servisi geliştirmeye başladığım zaman, tabii tecrübesizim de, neyi nasıl yapmalıyım diye ciddi bir çalışma içerisindeyim. Her şeyi inceliyorum, standartları nelerdir, HTTP 201 yanıtını nedir, ne zaman dönülmeli gib gibi ciddi bir çalışma içerisindeyim. Laravel zaten bu konunda işin standartlarını benim yerime yapıyor ki bu harika.
Fakat bu end-pointleri front-end tarafında geliştirme yapan arkadaşla nasıl paylaşacağım sorunuyla karşılaştım ve adeta duvara çarptım. İlk başta slack üzerinden paylaşıyorum mesajla ama bu çok uzun sürmedi. Çünkü mesajlaşarak olacak gibi değil. Arkasından ben kendim end-pointleri, hangisinin ne işe yaradığını, hangi body ve query parametrelerinin gerekli olduğunu vs. elle yazmaya başladım. Fakat bu da gerçekten olacak gibi değildi. O kadar çok sıkıldım ki iki kelime yazmaya üşenir oldum.
Sonunda bunun için bir çözüm üretilmiştir eminim ki diyerek API dökümantasyonu için paket araştırmaya niyetlendim. Yukarıdaki süreç çok uzun sürmüş gibi hissettirebilir fakat 3-5 gün sürmedi paket arayışına geçmem.
O dönem bir kaç paket inceledim ve içime en çok sinen paket Scribe olmuştur. Kullanmaya başlar başlamaz da iyikim oldu. Bir kaç yıldır kullanıyorum ve %95 tüm ihtiyaçlarımı çok iyi bir şekilde karşıladı. Geçen sene geliştiricilerden dökümantasyon end-pointim için bir password protection istemiştim. Yani dökümantasyon end-pointim herkes tarafından erişilebilir olmasın, bu durum beni ufaktan rahatsız etmeye başlamıştı. Bunu kendin implement edebilirsin demişti.
Aradan geçen bir yılın sonunda bunun için bir yardımcı kütüphane paketledim. Bu kütüphaneyi Github üzerinden inceleyebilirsiniz: oralunal/scribe-auth
Kütüphane kısaca scribe dökümantasyonu için bir middleware oluşturuyor ve bu middleware’i scribe.php config dosyasında tanım, dökümantasyona erişmeye çalıştığınız zaman sizden bir şifre talep ediyor. İşleyişi basit, kullanımı basit ve entegrasyonu basit. Umarım bu paketle dökümantasyon end-pointinizin kısmi güvenliğini sağlayabiliriz.
Şu anda daha fazla geliştirir miyim emin değilim fakat geliştirirsem yetkilendirme yöntemine statik bir şifre ile yapmak yerine buraya alternatif olarak User model’ini de dahil etmeye niyetleniyorum. Bir ara sanırım hazırlayacağım.
Eğer talep olursa Laravel projelerinizin API end-pointleri için nasıl dökümantasyonlar hazırlayacağınızla ilgili bir video hazırlayabilirim. Bu yazıya 150.000 like gelirse, hahahha 😀
Bir kaç ay önce çıkmaya başladı, sanırım bir güncellemeden sonra bozdular ya da olması gerektiği gibi yaptılar diyebilirim. Bilemedim. Laravel’de Route’larınızı bir prefix altında gruplamak istediğimizde karşılaşıyoruz bu hatayla. Senaryomuz şu şekilde:
Bu şekilde kullanımda hiç bir problem yok fakat PHPStorm inatla prefix’in altını çiziyor ve şu uyarıyı veriyor:
Member has protected visibility but can be accessed via ‘__callStatic’ magic method
Bu şekilde warning’leri görmek tabii benim tansiyonu oldukça düşürüyor. Ne zaman hayatımıza dahil oldu tam bilmiyorum fakat korkulacak bir şey yok diyebilirim. Uyarının sebebi ise Router sınıfında prefix metodunun protected olarak yer alıyor olması. Hal böyle olunca dışarıdan prefix metodunu çağırdığımızda Router sınıfındaki __call metodu bu görevi üstleniyor ve RouteRegistrar sınıfında attribute metodu ile hallediyor işini. Laravel Idea‘nın bu konuya en kısa sürede bir çözüm üreteceğine inancım sonsuz, gerçekten bu uyarıları görmeyi sevmiyorum, takıntılıyım diyim, siz anlayın.
Güncelleme (12 Mayıs 2025): Laravel Idea 10.2 güncellemesi ile bu sorun giderildi.
Bugün Laravel ile geliştirdiğim backend api servisinde ilginç bir durum ile karşılaştım. Bu hatayı tanımlamam gerekirse:
Üzerinde çalıştığım projede yüzlerce migration dosyası oluşmaya başladı. Her yeni migration oluşturduğumda database dizini altında yeni oluşturduğum migration dosyasına kaydırmaktan iyice bıkkınlık gelmişti.
Bunun için buradan görebileceğiniz üzere migrationları silip bir dump dosyası oluşturdum. Projemde MySQL kullanıyorum.
Bash
# Mevcut veritabanının bir dumpını oluşturur ve migration'ların hepsini temizler.phpartisanschema:dump--prune
Bu işlem sonrası migrations dizini uçuyor ve yerime schema isimli yeni bir dizin geliyor. İçerisinde de mysql kullandığım için mysql-schema.sql isimli bir dosya oluşuyor. Buraya kadar her şey harika.
Buraya kadar her şey güzel. Artık daha sade ve temiz bir database dizinim var. Fakat problem testlerimin çalışmasında başlıyor. Testlerimde her zaman veritabanını sıfırlıyorum:
Evet, ben de Pest kullanıyorum. Test içinse veritabanı olarak olarak SQLite kullanıyorum. Bağlantımda :memory: olarak ayarlı. Problem burada başlıyor. Migration’larımızı sildiğimiz için ve veritabanı dumpımız mysql olduğu sqlite ile veritabanı oluşturulamıyor. Test sonuçlarımda düzenli olarak users tablosu bulunamadı hatası alıyorum. Ufak bir çakallık deneyip mysql-schema.sql dosyamın adını sqlite-schema.sql olarak değiştirsem yedirir miyim diye bir düşündüm, düşükte olsa umut fakirin ekmeğiydi ama tabii ki olmadı 🙂
Bunun için çözümler üretmeye çalışırken mysql dump dosyasını sqlite dumpa dönüştürmek aklıma geldi. Bunun için github’da dumblob/mysql2sqlite reposunu keşfettim. Bu toolu kullanarak mevcut mysql dumpımdan sqlite dump oluşturdum. Bu repodan mysql2sqlite dosyasını projemin ana dizininde oluşturdum ve sonrasında şu komutu çalıştırdım:
Voila! sqlite-schema.sql dosyam schema klasörünün altında oluşmuştu. Tabii şimdi test etmeye geldi.
Bash
phpartisantest
Artık sağlıklı bir şekilde sqlite-schema.sql dosyasını sağlıklı bir şekilde kullanarak veritabanımı sıfırlayabiliyorum.
Umarım birinize ilaç olabilecek bir yaklaşım sunmuşumdur. Bu yazıyı yazarken neden şu konuları da açıklamam gerektiğini hissettim, bir ara bunlara da değineceğim, kendime not olması açısından:
Test nedir? PHPUnit ve Pest nedir?
Neden test yazmalıyız?
Neden testlerimizde sqlite kullanmalıyız ya da kullanmak zorunda mıyız?
Ölmez sağ kalır isek bir ara yazarız 🙂 Kalın sağlıcakla.
[…] ile çalışıyor. Eğer memcached kurulumu ile ilgili problem yaşarsanız daha önce yazdığım Brew ile PHP Memcached Kurulumu yazıma da…