Plugin tabanlı · severity-aware · sıfır-config ayağa kalkan API izleme sistemi
⭐ İşine yaradıysa yıldız bırak — production'da yangın söndürme şansını arttırır.
Sürekli kullandığınız 3. parti API'ler — Iyzico, ParamPos, Twitter/X, Wakapi, Sentry, Slack, sizinki — bir günden ötekine user_id'yi userId yapar, amount'ı string'e çevirir, ya da bir alanı sessizce kaldırırlar. Production'da bunu kullanıcıyla aynı anda öğrenmek istemezsiniz.
API-Sentinel arka planda her endpoint'i belirlediğiniz aralıklarla çağırır, JSON response'unun schema'sını (alanlar, tipleri, nullable, enum, derinlik vs.) hashleyerek snapshot'lar, son snapshot ile karşılaştırır ve kırılma seviyesine göre (CRITICAL / WARNING / INFO) e-posta, SMS, webhook veya yazdığınız herhangi bir kanal üzerinden alert gönderir.
┌────────┐ scrape ┌──────────────┐ diff ┌──────────┐ severity ┌────────┐
│ 3rd ├─────────▶│ schema │────────▶│ differ │───────────▶│ alert │
│ API │ N dk │ extractor │ │ (deepdiff)│ │ router │
└────────┘ └──────┬───────┘ └──────────┘ └───┬────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ SQLite + JSON │ │ E-mail · SMS · │
│ schema arşivi │ │ Webhook · ... │
└─────────────────┘ └─────────────────┘
- Neden API-Sentinel?
- Mimari
- Servisler
- Severity Matrisi
- Hızlı Başlangıç
- Web UI Turu
- Plugin Yazımı
- Middleware Entegrasyonları
- API Referansı
- Güvenlik
- Konfigürasyon
- Sorun Giderme
- Geliştirme & Test
- Lisans
| Senaryo | API-Sentinel'siz | API-Sentinel ile |
|---|---|---|
3rd party data.user_id → data.userId rename |
İlk patlamayı son kullanıcı bildirir | 5 dk içinde CRITICAL alert + diff snapshot |
Auth header tipini değiştirir (Bearer → Basic) |
401 fırtınası, sessiz drop'lar | Auth değişimi CRITICAL kategorisinde, ayrı kanaldan alert |
Değer int iken artık string |
JSON parse / cast hataları, mobile crash | type_changed ile CRITICAL alert + eski/yeni schema diff |
| Yeni alan eklemiş, eski hala çalışıyor | Bilinmiyor, gözden kaçıyor | INFO seviyesinde haber, ekip backlog'a alıyor |
Bir alan nullable olmaktan çıktı |
Random NPE'ler | WARNING alert, kontrat netleştiriliyor |
Tasarım felsefesi: Hiçbir entegrasyon — alert kanalı, auth yöntemi, storage backend, provider — hardcoded değildir. Yeni bir kanal eklemek için bir Python dosyası bırakıp YAML'da etkinleştirmek yeterli. Sıfır plugin ile bile sistem ayağa kalkar; alert atmaz, sadece izler.
core/registry.py üzerinde merkezi PluginRegistry:
┌──────────────────── PluginRegistry ────────────────────┐
│ │
│ alert_channels: {email: EmailAlertChannel, │
│ sms: SmsAlertChannel, │
│ webhook: WebhookAlertChannel, ...} │
│ │
│ auth_handlers: {bearer: BearerAuth, │
│ basic: BasicAuth, │
│ api_key: ApiKeyAuth, │
│ oauth2: OAuth2Auth, ...} │
│ │
│ storage_backends:{sqlite: SqliteStorageBackend, │
│ file: FileStore, ...} │
│ │
└────────────────────────────────────────────────────────┘
auto_discover(package_path) ile her plugin dizinindeki .py dosyaları
import edilir, BaseAlertChannel / BaseAuthHandler türevleri otomatik
register edilir. Yeni dosya bırak, restart et, hazır.
┌────────────────┐
│ Scheduler │ APScheduler AsyncIOScheduler
│ (her endpoint │ endpoint başına bağımsız cron/interval
│ için ayrı job)│
└───────┬────────┘
▼
┌────────────────┐ httpx async client
│ Fetcher │ auth handler (bearer/basic/api_key/oauth2) inject eder
└───────┬────────┘ retry, timeout, status code tracking
▼
┌────────────────┐ JSON / XML / TEXT / RSS desteği
│ Schema │ pydantic-benzeri ağaç:
│ Extractor │ {field, type, nullable, enum, depth, format}
└───────┬────────┘
▼
┌────────────────┐ deepdiff ile previous vs current
│ Differ │ her diff item'ı için severity hesaplar
└───────┬────────┘
▼
┌────────────────┐ cooldown bastırma (alert flood koruması)
│ Alert Router │ her aktif kanala paralel async dispatch
└───────┬────────┘ başarısız kanalı log'lar, diğerlerini bekletmez
▼
┌────────────────┐ SQLite default · FileStore opsiyonel
│ Storage │ schemas, changes, alerts, providers, endpoints,
│ │ credentials (Fernet ile şifreli), settings
└────────────────┘
/app/data/
schemas.db ← SQLite (providers, endpoints, changes, alerts, ...)
/app/schemas/
iyzico/
payment_create/
2026-04-30T12:00:00Z.json ← snapshot
2026-04-30T12:05:00Z.json
...
twitter/
user_lookup/
...
- FastAPI + uvicorn + APScheduler (AsyncIOScheduler)
- Plugin tabanlı alert / auth / storage
- Web Dashboard (Tailwind CSS + Alpine.js, vanilla template)
- REST API (provider/endpoint/credential/changes/alerts CRUD)
- Auth Middleware (token bazlı)
- Rate Limiting (check endpoint'leri için 10 req/dk)
- Credential şifreleme (Fernet)
- Alert cooldown (default 300 sn, tekrar bastırma)
/health,/metricsendpoint'leri (Prometheus uyumlu)
- 3rd party API'lerin changelog sayfalarını scrape eder (HTML/Markdown/RSS)
- Yeni post tespit edilirse
schema-monitor'a webhook atarak proaktif alert üretir - Kullanım: API sağlayıcı duyurmadan önce siz yakalayın
- Hedefler
changelog-watcher/config/targets.yamlile tanımlanır
dotnet/ThirdPartyApiMiddleware.cs— ASP.NET Core middleware: outgoing HTTP isteklerini intercept edip API-Sentinel'e raporlar.laravel/ThirdPartyApiMonitor.php— Laravel HTTP client macro / middleware: aynı işi PHP tarafında yapar.
core/severity.py — diff'ten alert seviyesine deterministik eşleşme.
| Değişiklik | Severity | Neden |
|---|---|---|
field_removed |
🔴 CRITICAL | Mevcut tüketici breaks |
type_changed |
🔴 CRITICAL | Cast hataları, JSON parse fail |
array_item_schema_changed |
🔴 CRITICAL | Liste içindeki yapı değişti |
auth_changed |
🔴 CRITICAL | 401 fırtınası |
nullable_changed_to_false |
🟡 WARNING | Beklenmedik NOT NULL |
required_field_added |
🟡 WARNING | Eksik gönderim → 4xx |
enum_value_removed |
🟡 WARNING | Switch/case düşer |
depth_changed |
🟡 WARNING | Nesting değişti, parser etkilenir |
format_changed |
🟡 WARNING | date-time ↔ date, regex format |
field_added |
🔵 INFO | Tüketici geriye uyumlu |
enum_value_added |
🔵 INFO | Yeni state, izlenmeli |
status_code_changed_2xx |
🔵 INFO | 200 → 201 vb. |
Bu tablo
core/severity.py'da kanonik tanımlıdır — değiştirmeyin. Yeni diff türü eklemek istersek aynı dosyada genişletilir.
- Docker 24+ ve Docker Compose v2
- 512 MB RAM minimum (önerilen 1 GB)
- Linux/macOS/WSL2
git clone https://github.com/halilibrahimd27/api-sentinel.git
cd api-sentinelcp .env.example .env
# Üretimde MUTLAKA değiştirilmesi gereken alanlar:
# SECRET_KEY → openssl rand -base64 32
# AUTH_ENABLED=true ise ADMIN_PASSWORD belirleyindocker compose up -d
docker compose psTarayıcı → http://localhost:8080
Kullanıcı : admin
Şifre : <ADMIN_PASSWORD veya SECRET_KEY>
- Providers sekmesi → + Yeni Provider → ad:
iyzico - Endpoints → + Yeni Endpoint:
- URL:
https://api.iyzico.com/payment/auth - Interval:
5m - Auth:
bearer+ token
- URL:
- Manuel tetikle veya 5 dk bekle → ilk snapshot atılır.
- Alert kanalı ekle (Settings → Alert Channels → e-mail/SMS/webhook).
curl http://localhost:8080/health
curl http://localhost:8080/metrics # Prometheus formatı
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/providers| Sayfa | Ne yapar? |
|---|---|
| Dashboard | Stats grid (toplam provider, endpoint, son 24 saatteki değişimler), severity dağılım chart'ı, son alert'ler timeline. |
| Providers | Provider CRUD. Her provider'ın endpoint sayısı, son sağlık durumu, ortalama response time. |
| Endpoints | Endpoint CRUD. URL, interval, auth tipi, son fetch durumu, son schema diff'i (collapsible). |
| Changes | Tüm schema değişiklikleri (filter: severity, provider, tarih). Detayda eski vs yeni schema yan yana, diff highlight. |
| Alerts | Alert geçmişi, hangi kanaldan gönderildi, başarılı/başarısız. Tekrar gönder butonu. |
| Channels | Alert kanallarını aktif/pasif yapma, config doğrulama, "Test Mesajı Gönder". |
| Plugins | Discover edilmiş tüm plugin'leri listele (alerts/auth/store). Hangileri aktif, hangileri config eksik. |
| Settings | Cooldown süreleri, log seviyesi, schema retention, dump/restore. |
schema-monitor/alerts/slack.py:
from alerts.base import BaseAlertChannel
import httpx
class SlackAlertChannel(BaseAlertChannel):
"""Slack incoming webhook üzerinden alert gönderir."""
channel_name = "slack"
@classmethod
def required_env_vars(cls) -> list[str]:
return [] # config webhook_url üzerinden gelir
def validate_config(self, config: dict) -> bool:
return bool(config.get("webhook_url", "").startswith("https://hooks.slack.com/"))
async def send(self, severity, title, message, details) -> bool:
emoji = {"CRITICAL": "🔴", "WARNING": "🟡", "INFO": "🔵"}[severity]
payload = {
"text": f"{emoji} *{title}*\n{message}",
"attachments": [{"text": str(details), "color": severity.lower()}],
}
async with httpx.AsyncClient(timeout=10) as client:
resp = await client.post(self.config["webhook_url"], json=payload)
return resp.status_code == 200schema-monitor/config/alerts.yaml:
channels:
slack:
enabled: true
webhook_url: https://hooks.slack.com/services/T00/B00/XXX
min_severity: WARNINGContainer'ı restart et → Web UI > Plugins > slack görünür ve aktif.
schema-monitor/auth/hmac.py:
from auth.base import BaseAuthHandler
import hmac, hashlib, time
class HmacAuthHandler(BaseAuthHandler):
auth_name = "hmac"
def apply(self, request, credentials):
timestamp = str(int(time.time()))
signature = hmac.new(
credentials["secret"].encode(),
f"{timestamp}{request.url}".encode(),
hashlib.sha256,
).hexdigest()
request.headers["X-Timestamp"] = timestamp
request.headers["X-Signature"] = signature
return requeststore/base.py'da 25 metodlu abstract interface var. PostgreSQL, MongoDB, S3 backend'i yazmak için bu interface'i uygulayan bir sınıf yeterli.
- parti API'ye giden istekleri uygulama tarafından monitör etmek için. Schema-monitor zaten dışarıdan endpoint çağırıyor; middleware ekstra bir gerçek-zamanlı katmandır: prod trafiğindeki schema'yı da raporlar.
// Program.cs
app.UseMiddleware<ThirdPartyApiMiddleware>(new ThirdPartyApiMiddlewareOptions {
SentinelUrl = "http://api-sentinel-monitor:8080",
ApiToken = builder.Configuration["Sentinel:Token"],
Provider = "iyzico"
});middleware/dotnet/ThirdPartyApiMiddleware.cs referans implementasyondur.
// app/Providers/AppServiceProvider.php
Http::macro('monitored', fn($provider) =>
Http::withMiddleware(new ThirdPartyApiMonitor($provider))
);
// Kullanım:
Http::monitored('iyzico')->post('https://api.iyzico.com/payment/auth', $payload);Tüm
/api/*endpoint'leriAuthorization: Bearer <TOKEN>ister (AUTH_ENABLED=trueiken). Token/api/auth/loginile alınır.
GET /health Liveness probe
GET /metrics Prometheus-formatlı metrics
POST /api/auth/login {username, password} → {token}
POST /api/auth/logout Token'ı geçersiz kıl
GET /api/providers
POST /api/providers
PUT /api/providers/{name}
DELETE /api/providers/{name}
GET /api/providers/{name}/endpoints
POST /api/providers/{name}/endpoints
PUT /api/providers/{name}/endpoints/{ep}
DELETE /api/providers/{name}/endpoints/{ep}
GET /api/providers/{name}/credentials (key listesi — value asla dönmez)
POST /api/providers/{name}/credentials {key, value}
DELETE /api/providers/{name}/credentials/{key}
GET /api/changes?severity=CRITICAL&provider=iyzico&limit=50&offset=0
GET /api/changes/{id}
POST /api/changes/{id}/acknowledge
POST /api/check/{provider}/{endpoint} Tek endpoint
POST /api/check-all Tüm endpoint'ler (rate-limited)
GET /api/plugins Tüm plugin'ler
GET /api/plugins/{type} type: alerts | auth | store
GET /api/plugins/{type}/{name}/status
GET /api/alerts/channels
POST /api/alerts/channels {name, config}
PUT /api/alerts/channels/{name}
DELETE /api/alerts/channels/{name}
POST /api/alerts/test/{channel_name} Test mesajı
GET /api/alerts/history?limit=100
GET /api/settings
POST /api/settings
POST /api/settings/clear Tüm verileri sil (irreversible!)
GET /api/statuses Endpoint sağlık özeti
GET /api/stats Genel istatistikler
| Katman | Mekanizma |
|---|---|
| Auth | core/auth_middleware.py — token tabanlı, AUTH_ENABLED=true ile dashboard + tüm /api/* korumalı. Login → JWT-benzeri token, /api/auth/logout ile iptal. |
| Şifreleme | Provider credential'ları cryptography.Fernet ile at-rest şifreli (key = SECRET_KEY). DB dump'ı ele geçse de plain credential leak olmaz. |
| Rate Limit | _check_rate_limit() — IP başına, /api/check/* endpoint'leri için 10 req/dk in-memory bucket. |
| Cooldown | Aynı kanal × provider × endpoint için ALERT_COOLDOWN_SECONDS (default 300) içinde tekrar alert gönderilmez. Flood engellenir. |
| Non-root | Container sentinel user ile çalışır. |
| CORS | Default kapalı; reverse proxy katmanında açın. |
| Input validation | Tüm POST/PUT body'leri Pydantic model üzerinden geçer. |
SECRET_KEY=<openssl rand -base64 32 ile üretilmiş>
AUTH_ENABLED=true
ADMIN_PASSWORD=<güçlü, unique parola>
ALERT_COOLDOWN_SECONDS=600
LOG_LEVEL=WARNINGve docker-compose'da:
ports:
- "127.0.0.1:8080:8080" # Sadece localhost'a bind, reverse proxy önde olsunLOG_LEVEL=INFO
CHECK_INTERVAL_DEFAULT=5m # endpoint'te interval verilmediğinde
DB_PATH=/app/data/schemas.db
SCHEMA_DIR=/app/schemas
SECRET_KEY=change-me # Fernet key + auth secret
AUTH_ENABLED=true
ADMIN_PASSWORD= # boş ise SECRET_KEY parola olur
API_TOKEN= # opsiyonel sabit harici token
ALERT_COOLDOWN_SECONDS=300
IMAGE_TAG=latest # docker compose image tag
MONITOR_PORT=8080config/endpoints.yaml— ilk kurulumda DB boşsa provider/endpoint seed'ler. Çalışan sistemde Web UI üzerinden CRUD yapılır; YAML re-import etmez (çakışma önler).config/alerts.yaml— alert kanalı default'ları.changelog-watcher/config/targets.yaml— changelog sayfalarının URL'leri.
AUTH_ENABLED=true ve henüz login olmadınız. admin + ADMIN_PASSWORD (veya SECRET_KEY) ile login.
SECRET_KEY değişti, eski Fernet ile şifrelenmiş credential açılamıyor. Çözüm:
docker compose exec schema-monitor python -m core.crypto rotate \
--old <eski_key> --new <yeni_key>ya da credential'ları silip yeniden girin.
- Plugins sayfasında ilgili kanal aktif mi?
- Channels > "Test Mesajı Gönder" başarılı mı?
- Cooldown aktif olabilir —
/api/alerts/historyson alertleri göster. LOG_LEVEL=DEBUGyapın,docker compose logs schema-monitor.
- Endpoint'in
interval'ı çok uzun → kısaltın. - Response 200 dönmüyorsa snapshot atılmaz; Endpoints > Status sütununa bakın.
- 3rd party A/B test yapıyor olabilir →
Settings > Schema Stability Windowartırın.
Settings > Schema Retention Days (default 90) düşürün.
Manuel temizlik:
docker compose exec schema-monitor python -m core.store.cleanup --older-than 30dcd schema-monitor
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
export SECRET_KEY=dev-key-change-me
uvicorn main:app --reload --port 8080cd schema-monitor
pytest tests/ -v
pytest tests/ --cov=. --cov-report=term-missingKonvansiyon: 3rd party HTTP çağrıları test'lerde
httpx.MockTransportile mocklanır — gerçek istek atılmaz. Fixture'lartests/fixtures/altında JSON.
tests/test_alert_plugins.py— her alert kanalı için ayrı testtests/test_auth_plugins.py— auth handler'lartests/test_scheduler.py,tests/test_fetcher.py,tests/test_crypto.py— kritik modüller (≥%80 coverage zorunlu)
Kod kuralları (özet — detay CLAUDE.MD)
- Türkçe yorum, Google-style docstring
- Type hint zorunlu
print()yerineloggingasyncio.run()yerine uvicorn- Hardcoded credential / hardcoded plugin import yasak
- Bare
except:yasak - f-string >
.format()
MIT — LICENSE dosyasına bakın.
PR'lar memnuniyetle. Yeni bir entegrasyon (alert kanalı / auth / storage)
eklemek için schema-monitor/<plugin_dir>/ altında dosya oluşturmanız
yeterli — registry otomatik discover eder. Test eklemeyi unutmayın.
CONTRIBUTING.md ve SECURITY.md okunmalı.
İpucu: Issue açarken hangi 3. parti API'yi izlediğinizi ve hangi diff türünün düzgün severity'lendirilmediğini paylaşırsanız hızlı çözeriz.
| Süre | Yardım |
|---|---|
| 5 sn | ⭐ Star |
| 30 sn | Twitter/LinkedIn'de paylaş ("3rd party API schema değişikliklerini sessizce yakalayan tool") |
| 5 dk | Eksik bulduğun bir konu için issue aç |
| 30 dk | Yeni bir alert kanalı plugin'i yaz (Slack, Discord, Telegram, ntfy...) |
| 2 saat | Yeni bir auth handler veya storage backend ekle |