Yasal

Yayın API’si (özel yazılımlar için)

Son güncelleme: 28 Eylül 2026 · glimana.com

WordPress kullanıyorsanız bu sayfaya ihtiyacınız yok: glimana Publisher eklentisini kurup glimana’daki bağlantı kodunu yapıştırmanız yeterli. Bu belge, kendi yazılımını kullanan siteler içindir: aşağıdaki iki uç noktayı siz uygularsınız, glimana onlara imzalı istek gönderir.

Akış

  1. glimana’da Ayarlar → Yayın bağlantıları → Özel API ile bağlantı oluşturun; uç nokta tabanını girin (ör. https://ornek.com/api/glimana). glimana size bir kez bir anahtar kimliği ve gizli anahtar gösterir.
  2. Sunucunuzda iki uç nokta açın: GET {taban}/status ve POST {taban}/posts.
  3. Her istekte imzayı aşağıdaki gibi doğrulayın; doğrulanamayan isteği hiçbir işlem yapmadan 401 ile reddedin.
  4. glimana’da “Bağlantıyı test et”e basın.

İstek başlıkları

BaşlıkDeğer
X-Glimana-KeyAnahtar kimliği (16 onaltılık karakter)
X-Glimana-TimestampUnix zamanı (saniye, UTC)
X-Glimana-Nonce32 onaltılık karakter, her istekte tek
X-Glimana-Actionstatus, posts.create, posts.get:{id} ya da posts.update:{id}
X-Glimana-Signaturev1= + 64 onaltılık karakter

İmza

Kanonik metin altı satırdır, satır sonu tek \n; gövde, isteğin ham baytlarıdır (JSON’u ayrıştırıp yeniden üretmeyin):

v1
{X-Glimana-Timestamp}
{X-Glimana-Nonce}
{HTTP YÖNTEMİ, büyük harf}
{X-Glimana-Action}
{sha256_hex(ham gövde)}       ← GET için boş gövdenin özeti

imza = "v1=" + hmac_sha256_hex(gizli_anahtar, kanonik_metin)

Adres değil eylem imzalanır; böylece ters vekil sunucu ya da yönlendirme adresi değiştirse de imza bozulmaz. Bu yüzden eylemi uç noktanızla eşleştirmek sizin işiniz: /status yalnız status; /posts POST'ta posts.create, GET /posts?id= için posts.get:{id}, güncellemede posts.update:{id} kabul etmeli (id = sizin yazı kimliğiniz). glimana arşiv sayfasını açınca ve yeniden göndermeden önce posts.get sorar; silinmiş yazı için 404 + {"error":"glimana_not_found"} döndürün — yoksa bu istekler "hatalı imza" sayılıp sunucunuz glimana'yı kilitleyebilir.

Zorunlu güvenlik kuralları

GET {taban}/status

Bağlantı testinde çağrılır. Gövde boştur. Yanıt:

200 OK
{
  "ok": true,
  "site_name": "Örnek Site",
  "plugin_version": "my-cms-1.0",
  "allow_publish": false,
  "categories": [ { "id": 3, "name": "Rehberler" } ],
  "authors": [ { "id": 2, "name": "Editör" } ],   ← glimana'nın makale bazlı seçebileceği yazarlar (sizin izin listeniz)
  "default_author": 2
}

POST {taban}/posts

{
  "idempotency_key": "0b6f6c0e-3c1a-4c55-9a51-2f1d5b7d9e10",
  "title": "Kira artış oranı nasıl hesaplanır",
  "content_html": "<h2>…</h2><p>…</p>",
  "excerpt": "140-155 karakterlik özet",
  "meta_description": "140-155 karakterlik özet",
  "slug": "kira-artis-orani-nasil-hesaplanir",
  "status": "draft",                     ← draft | future | publish
  "date_gmt": "2026-10-08T07:30:00Z",    ← yalnız status=future; her zaman UTC
  "categories": [3],
  "author_id": 2,                         ← isteğe bağlı; izin listenizde yoksa varsayılan yazarı kullanın
  "language": "tr"
}

Başarılı yanıt (yeni yazı 201, tekrar 200):

{ "id": 812, "status": "draft", "url": "https://ornek.com/?p=812", "edit_url": "https://ornek.com/admin/posts/812" }

Hata yanıtı: uygun durum kodu (400, 401, 403, 413, 422, 429, 500) ve {"code": "...", "message": "..."}. message kullanıcıya gösterilir; gizli bilgi koymayın. 401 ve 403’te glimana yeniden denemez; 429 ve 5xx’te iki kez daha dener.

Doğrulama örnekleri

PHP

function glimana_dogrula(string $gizli, array $h, string $yontem, string $beklenenEylem, string $govde): bool {
    $ts = (int) ($h['x-glimana-timestamp'] ?? 0);
    $nonce = (string) ($h['x-glimana-nonce'] ?? '');
    if (($h['x-glimana-action'] ?? '') !== $beklenenEylem) return false;
    if (abs(time() - $ts) > 300 || !preg_match('/^[a-f0-9]{32}$/', $nonce)) return false;
    $kanonik = implode("\n", ['v1', (string) $ts, $nonce, strtoupper($yontem), $beklenenEylem, hash('sha256', $govde)]);
    $beklenen = 'v1=' . hash_hmac('sha256', $kanonik, $gizli);
    if (!hash_equals($beklenen, (string) ($h['x-glimana-signature'] ?? ''))) return false;
    return nonce_ilk_kez_mi($nonce, 600);   // kendi deponuz: Redis SET NX EX 600 / veritabanı benzersiz satır
}
// $govde = file_get_contents('php://input');  ← ham gövde

Node.js

const crypto = require('crypto');
function dogrula(gizli, h, yontem, beklenenEylem, hamGovde /* Buffer */) {
  const ts = Number(h['x-glimana-timestamp']), nonce = String(h['x-glimana-nonce'] || '');
  if (h['x-glimana-action'] !== beklenenEylem) return false;
  if (Math.abs(Date.now() / 1000 - ts) > 300 || !/^[a-f0-9]{32}$/.test(nonce)) return false;
  const kanonik = ['v1', String(ts), nonce, yontem.toUpperCase(), beklenenEylem,
    crypto.createHash('sha256').update(hamGovde).digest('hex')].join('\n');
  const beklenen = Buffer.from('v1=' + crypto.createHmac('sha256', gizli).update(kanonik).digest('hex'));
  const gelen = Buffer.from(String(h['x-glimana-signature'] || ''));
  return gelen.length === beklenen.length && crypto.timingSafeEqual(gelen, beklenen) && nonceIlkKezMi(nonce);
}
// Express: app.post('/api/glimana/posts', express.raw({ type: 'application/json', limit: '2mb' }), ...)

Python

import hashlib, hmac, time, re
def dogrula(gizli: bytes, h: dict, yontem: str, beklenen_eylem: str, ham_govde: bytes) -> bool:
    ts, nonce = int(h.get('x-glimana-timestamp', 0)), h.get('x-glimana-nonce', '')
    if h.get('x-glimana-action') != beklenen_eylem: return False
    if abs(time.time() - ts) > 300 or not re.fullmatch(r'[a-f0-9]{32}', nonce): return False
    kanonik = '\n'.join(['v1', str(ts), nonce, yontem.upper(), beklenen_eylem, hashlib.sha256(ham_govde).hexdigest()])
    beklenen = 'v1=' + hmac.new(gizli, kanonik.encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(beklenen, h.get('x-glimana-signature', '')) and nonce_ilk_kez_mi(nonce)

Test vektörü

Uygulamanızı bununla sınayın (gerçek anahtar DEĞİLDİR):

gizli     : test_secret_do_not_use_0123456789abcdefABCDE
zaman     : 1767225600
nonce     : 4f6b1c2d3e4f5a6b7c8d9e0f1a2b3c4d
yöntem    : POST
eylem     : posts.create
gövde     : {"idempotency_key":"0b6f6c0e-3c1a-4c55-9a51-2f1d5b7d9e10","title":"Hello","content_html":"<p>Hi</p>","status":"draft"}
gövde sha : 1d30b224b239dc0093e24dcbdd7ce6fa0ddc12f8ad49892e1cce4ebf335ed72a
imza      : v1=ba6481f4574fb36b8e00ae74408bd54f81afc38fe446d8ab2c96ca0099af8abd

GET status (boş gövde, aynı zaman/nonce):
imza      : v1=ac5b10064ac28b51e173db45934e55781ea817b6d6c205a6f57faf30daa74eb8