tarihinde yayınlandı Yorum yapın

Airoxy: Claude Subscription’ınızı API Olarak Kullanın

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.

Client ──► Airoxy ──► Anthropic
          (auth)    (round-robin)

İstemci tarafında tek yapmanız gereken base URL’i ve x-api-key header’ını değiştirmek — başka hiçbir kod değişikliği gerekmez.

Neden Airoxy?

  • Abonelik ile API kullanımı: Claude subscription’ınızın OAuth token’larını API gibi kullanın
  • Token rotasyonu: Birden fazla token ekleyin, Airoxy bunları round-robin ile otomatik döndürsün
  • Rate limit yönetimi: 429/529 hatasında otomatik olarak sonraki token’a geçiş
  • Maliyet takibi: Her istek için tahmini maliyet hesaplama
  • Tam şeffaflık: İstek gövdesi byte-for-byte iletilir, Airoxy sadece model ve stream alanlarını okur
  • Gerçek zamanlı streaming: SSE (Server-Sent Events) desteği ile sıfır tamponlama

Kurulum

Airoxy’nin kurulumu tek komutla yapılır. Ubuntu sunucunuzda şu komutu çalıştırmanız yeterli:

curl -fsSL https://raw.githubusercontent.com/oralunal/airoxy/main/install.sh | sudo bash

Bu script otomatik olarak şunları yapar:

  • PHP 8.5+ kurulumu (Ondrej PPA üzerinden)
  • Git kurulumu
  • 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ış:

  1. 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
  2. airoxy token:auto komutuyla ~/.claude/.credentials.json dosyasından otomatik import edebilirsiniz
  3. 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ı
  4. OAuth token’ları için gerekli olan Claude Code sistem prompt’u otomatik enjekte edilir
  5. Token’lar her 6 saatte bir otomatik yenilenir (OAuth2 refresh flow)
  6. 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

KomutAçıklama
sudo airoxy startServisleri başlat
sudo airoxy stopServisleri durdur
sudo airoxy restartServisleri yeniden başlat
airoxy statusServis 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.

KomutAçıklama
airoxy token:add {token} {refresh_token} --name= --expires-in=28800Manuel token ekleme
airoxy token:autoClaude Code credential’larından otomatik import
airoxy token:auto --dry-runImport edilecekleri önizle
airoxy token:listTüm token’ları listele (maskelenmiş)
airoxy token:remove {id}Token sil
airoxy token:refreshTüm token’ları yenile
airoxy token:refresh --id=1Belirli bir token’ı yenile

API Key Yönetimi (İstemci Kimlik Doğrulama)

İstemcilerin Airoxy’ye bağlanırken kullandığı anahtarlar. Otomatik üretilir (ak- ön ekiyle).

KomutAçıklama
airoxy api-key:add --name="Uygulamam"Yeni API anahtarı oluştur (bir kez gösterilir)
airoxy api-key:listTüm anahtarları listele (maskelenmiş)
airoxy api-key:remove {id}Anahtar sil
airoxy api-key:toggle {id}Aktif/pasif yap

Log ve İstatistik

KomutAçıklama
airoxy logsSon 50 istek logunu göster
airoxy logs --limit=100Son 100 log
airoxy logs --todayBugünkü loglar
airoxy logs --date=2026-04-06Belirli bir tarihin logları
airoxy logs --token={id}Belirli bir token’a göre filtrele
airoxy statsGenel istatistikler
airoxy stats --todayBugünkü istatistikler
airoxy stats --monthBu ayki istatistikler

Sistem Yönetimi

KomutAçıklama
sudo airoxy updateSon kodu çek, bağımlılıkları güncelle, migrate et, yeniden başlat
sudo airoxy doctorSistem sağlık kontrolü ve otomatik düzeltme
sudo airoxy uninstallAiroxy’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:

ModelInput (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:

  • PHP sürümü (8.5+)
  • PHP eklentileri (curl, mbstring, sqlite3, openssl, tokenizer, xml)
  • Composer kurulumu
  • Supervisor kurulumu ve yapılandırması
  • .env dosyası ve APP_KEY
  • SQLite veritabanı varlığı ve izinleri
  • Bekleyen migration’lar
  • FrankenPHP kurulumu
  • Dosya sahipliği (www-data)
  • Airoxy ve scheduler süreç durumu
  • Global alias
  • Logrotate yapılandırması

Yapılandırma

/var/www/airoxy/.env dosyasını düzenleyerek ayarları değiştirebilirsiniz:

AIROXY_HOST=0.0.0.0              # Dinleme adresi
AIROXY_PORT=3800                  # Dinleme portu
AIROXY_TOKEN_REFRESH_INTERVAL=360 # Token yenileme aralığı (dakika)
AIROXY_LOG_RETENTION_DAYS=3       # Detaylı log saklama süresi (gün)

Kullanım Örneği

Standart istek:

curl http://sunucu-ip:3800/v1/messages 
  -H "content-type: application/json" 
  -H "x-api-key: ak-your-api-key-here" 
  -H "anthropic-version: 2023-06-01" 
  -d '{
    "model": "claude-sonnet-4-6",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "Merhaba!"}]
  }'

Streaming istek:

curl -N http://sunucu-ip:3800/v1/messages 
  -H "content-type: application/json" 
  -H "x-api-key: ak-your-api-key-here" 
  -H "anthropic-version: 2023-06-01" 
  -d '{
    "model": "claude-sonnet-4-6",
    "max_tokens": 1024,
    "stream": true,
    "messages": [{"role": "user", "content": "Merhaba!"}]
  }'

Laravel AI SDK ile kullanım — sadece environment variable’ları ayarlayın:

ANTHROPIC_BASE_URL=http://sunucu-ip:3800
ANTHROPIC_API_KEY=ak-your-api-key-here

Mimari

Airoxy şu teknoloji yığını üzerine inşa edilmiştir:

  • Sunucu: Laravel Octane + FrankenPHP (yüksek performanslı, kalıcı worker’lar)
  • Veritabanı: SQLite (hafif, kurulum gerektirmez)
  • Süreç Yönetimi: Supervisor (otomatik yeniden başlatma)
  • Log Rotasyonu: Logrotate (aylık, sıkıştırılmış, 3 ay saklama)
  • Framework: Laravel 13, PHP 8.5
airoxy/
  app/Services/
    ProxyService.php          # Çekirdek proxy mantığı (stream + non-stream)
    StreamHandler.php         # SSE chunk ayrıştırma, token sayma
    AccessTokenRotator.php    # Round-robin token seçimi
    TokenRefresher.php        # OAuth2 token yenileme
    CostCalculator.php        # Maliyet hesaplama
  app/Http/
    Middleware/AuthenticateToken.php  # API key doğrulama
    Controllers/ProxyController.php  # İstek yönlendirici
  app/Console/Commands/              # Tüm CLI komutları
  config/airoxy.php                  # Fiyatlandırma, OAuth2 ayarları

Sağlık Kontrolü

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:

GitHub: github.com/oralunal/airoxy

Lisans: MIT

tarihinde yayınlandı Yorum yapın

Data Transfer Objects Nedir? Güzellikleri Nelerdir? Gerçek Bir Uygulama Örneği Üzerinden

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.

PHP
$email_change_data = [
    'hash' => Str::random(64),
    'new_email' => $email,
    'created_at' => now(),
];

Cache::set('email_change', $email_change_data, 60 * 60 * 3);

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:

PHP
<?php

namespace App\DTOs;

use Illuminate\Support\Carbon;
use Illuminate\Support\Str;

readonly class EmailVerificationData
{
    public function __construct(
        public string $hash,
        public ?string $new_email,
        public Carbon $created_at,
    ) {}

    public static function create(?string $email = null): self
    {
        return new self(
            hash: Str::random(64),
            new_email: $email,
            created_at: now(),
        );
    }

    public function toArray(): array
    {
        return [
            'hash' => $this->hash,
            'new_email' => $this->new_email,
            'created_at' => $this->created_at,
        ];
    }

    public static function fromArray(array $data): self
    {
        return new self(
            hash: $data['hash'],
            new_email: $data['new_email'],
            created_at: $data['created_at'],
        );
    }
}

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.

Şimdi ilk hazırladığımız kodu güncelleyelim:

PHP
$email_verification_data = EmailVerificationData::create($email);
Cache::set('email_change', $email_verification_data->toArray(), 60 * 60 * 3);

Çok harika değil mi? Artık verimi DTO ile oluşturdum, diziye çevirip cache’e attık. Şimdi cache’den veriyi alıp kontrol işlemlerine geçelim:

PHP
$email_verification_data = EmailVerificationData::fromArray(Cache::get('email_change'));

İş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.

PHP
if($email_verification_data->hash === $incoming_hash) {
  echo $email_verification_data->created_at->toDateTimeString();
}

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.

tarihinde yayınlandı Yorum yapın

Fail2Ban’da Hazırladığım Tarama Botlarını Engelleyen Filtre

Herkese merhabalar arkadaşlar,

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:

Bash
[suspicious]
enabled  = true
filter   = suspicious
logpath  = /var/log/nginx/domains/domain.tld-ssl_log
           /var/log/apache2/domlogs/domain.tld-ssl_log
           /home/domain/access_logs/domain.tld-ssl_log
maxretry = 10
findtime = 1d
bantime  = 30d
port     = http,https

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.

tarihinde yayınlandı Yorum yapın

Vito’da Veritabanı Yedeklemelerinin Başarısız Olması

Harika bir günden herkese merhabalar,

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.

Umarım bir gün birilerinin işine yarar 🙂

tarihinde yayınlandı 1 Yorum

Brew ile PHP Memcached Kurulumu

Merhabalar arkadaşlar,

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
pecl install memcached

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
zlib directory [no] :

Bunu gördüğümüz anda zlib dizinimizi elle yazdırmamız gerekiyor:

Bash
zlib directory [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:

Bash
checking whether to use system zstd library... no
checking for ZLIB... yes, shared
checking for pkg-config... /opt/homebrew/bin/pkg-config
checking for zlib location... configure: error: memcached support requires ZLIB. Use --with-zlib-dir=<DIR> to specify the prefix where ZLIB headers and library are located
ERROR: `/private/tmp/pear/temp/memcached/configure --with-php-config=/opt/homebrew/opt/php/bin/php-config --with-libmemcached-dir=no --with-zlib-dir=no --with-system-fastlz=no --enable-memcached-igbinary=no --enable-memcached-msgpack=no --enable-memcached-json=no --enable-memcached-protocol=no --enable-memcached-sasl=yes --enable-memcached-session=yes' failed

Faydası olur mu bilmem, kendime not alıyorum 🙂

tarihinde yayınlandı Yorum yapın

MacOS’de Memcached Memory Değerini Artırma

Merhabalar,

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
brew services list

Bu komut şu çıktıyı veriyor:

Harika, mac’in arkaplan servis dosyası bu. Hemen o dosyayı açıp düzenlemeyi denedim:

Bash
vi ~/Library/LaunchAgents/homebrew.mxcl.memcached.plist

Dosyanın şöyle bir içeriği var:

XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>KeepAlive</key>
	<true/>
	<key>Label</key>
	<string>homebrew.mxcl.memcached</string>
	<key>LimitLoadToSessionType</key>
	<array>
		<string>Aqua</string>
		<string>Background</string>
		<string>LoginWindow</string>
		<string>StandardIO</string>
		<string>System</string>
	</array>
	<key>ProgramArguments</key>
	<array>
		<string>/opt/homebrew/opt/memcached/bin/memcached</string>
		<string>-m</string>
		<string>512</string>
		<string>-l</string>
		<string>localhost</string>
	</array>
	<key>RunAtLoad</key>
	<true/>
	<key>WorkingDirectory</key>
	<string>/opt/homebrew</string>
</dict>
</plist>

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.

Kolay gelsin,

tarihinde yayınlandı Yorum yapın

Scribe Laravel API Dökümantasyonu İçin Geliştirdiğim Auth Middleware Kütüphanesi

Merhaba arkadaşlar,

İ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 😀


tarihinde yayınlandı Yorum yapın

PHPStorm’un Route::prefix Metodu İçin Protected Uyarısı

Bu artık var olan bir sorun değil.

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:

PHP
    Route::prefix('/me')->name('me.')->middleware('auth:sanctum')->controller(MeController::class)->group(function () {
        Route::get('', 'show')->name('show');
    });

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.

tarihinde yayınlandı Yorum yapın

MySQL Kullanılan Projede ‘schema:dump –prune’ Sonrasında Test’lerin SQLite İle Çalışmaması

Merhaba arkadaşlar,

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.
php artisan schema: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:

PHP
<?php

use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

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:

Bash
./mysql2sqlite database/schema/mysql-schema.sql > database/schema/sqlite-schema.sql

Voila! sqlite-schema.sql dosyam schema klasörünün altında oluşmuştu. Tabii şimdi test etmeye geldi.

Bash
php artisan test

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.

tarihinde yayınlandı Yorum yapın

PhpStorm 2024.1 Güncellemesiyle Gelen Yapışkan Satırlar

Son bir kaç gündür IDE’de bir problem yaşıyorum. Tepede kapsayıcı elemanların önizlemesini gösteriyor. Böyle deyince tam anlaşılmadı ama PHP’de bir metot içerisinde kod yazarken hangi metotta olduğunuzu ve hatta hangi sınıf içerisinde yer aldığınızı görmenizi sağlayan bir önizleme. Görüntünün nasıl olduğunu daha detaylı aşağıdaki resimde görebilirsiniz:

Satır sayılarına bakarsanız ne demek istediğimi tam olarak görebilirsiniz.

Ayıptır söylemesi yakın zaman önce Macbook Pro aldım. Geçenlerde de Option tuşu ile farklı tuşlara basarak garip garip karakterler ortaya çıktığını farkettim. Örneğin option + j ikilisine basınca ∆ bu işaret geliyor, option + shift + v üçlüsüne basınca ◊ bu işaret geliyor gibi gibi. Bu simgeleri gözlemlemeye çalışırken istemeden bir kısayolu mu tetikledim acaba diye düşündüm. Fakat öyle değilmiş.

Sonra Settings’i açıp ayarları teker teker incelerken buldum bu özelliği. PhpStorm 2024.1 güncellemesi ile gelen ve default olarak açık olan bir özellikmiş. “Sticky Lines”. Settings -> Editor -> General -> Appearance altında bu ayarı bulabilirsiniz.

Benim gibi alışkanlarını hızlı terk edemeyenler için böyle özellikler çok fazla olabiliyor. jetbrains default olarak kapalı getirmeli böyle bir özelliği bence.

Jetbrains sticky lines tanımını şöyle yapıyor:

Sticky lines (yapışkan satırlar) veya sticky scroll özelliği, dosya içinde kaydırma yaparken üst elemanların görünürlüğünü koruyarak kodlama deneyiminizi geliştirir ve kodunuza hemen bağlam sağlar.

Ayrıca, herhangi bir yapışkan satıra tıklayarak düzenleyicide ilgili tanıma kaydırabilirsiniz.

jetbrains