veritabani.sqlite(db, "CREATE TABLE IF NOT EXISTS baglanti(kod TEXT PRIMARY KEY, adres TEXT NOT NULL, olusturma TEXT, tiklama INTEGER DEFAULT 0); CREATE TABLE IF NOT EXISTS tiklama(kod TEXT, zaman TEXT);")
# Satırları `|` ile ayrılmış sütunlara böler. Adres son sütundur ve
# kendi içinde `|` taşıyabilir → kalan parçalar geri birleştirilir.
fonksiyon satirlar(ham):
sonuc = []
eger ham == "":
don sonuc
dongu s icinde ham.bol("\n"):
sonuc.ekle(s)
don sonuc
fonksiyon son_sutun(parcalar, bas):
don parcalar.dilim(bas, parcalar.uzunluk() - bas).birlestir("|")
# ── Kısa kod ────────────────────────────────────────────────────────
fonksiyon yeni_kod():
kod = ""
dongu _ icinde 1..KOD_UZUNLUGU:
kod = kod + ALFABE.dilim(rastgele.sayi(0, ALFABE.uzunluk() - 1), 1)
don kod
fonksiyon kod_gecerli(k):
eger k.uzunluk() != KOD_UZUNLUGU:
don yanlis
dongu i icinde 0..k.uzunluk() - 1:
eger degil ALFABE.icerir(k.dilim(i, 1)):
don yanlis
don dogru
# Hedef adres yalnız http(s) olabilir: `javascript:` ya da `file:` ile
# başlayan bir "bağlantı" kısaltıcıyı saldırı aracına çevirirdi.
fonksiyon adres_dogrula(a):
eger a.uzunluk() > 2000:
don "adres çok uzun"
eger degil (a.ile_baslar("https://") veya a.ile_baslar("http://")):
don "adres http:// ya da https:// ile başlamalı"
eger a.icerir(" ") veya a.icerir("\n") veya a.icerir("\r") veya a.icerir("\t"):
don "adres boşluk içeremez"
eger a.uzunluk() < 12:
don "adres eksik görünüyor"
don ""
fonksiyon kaydet(db, adres):
kod = yeni_kod()
# Çakışma olasılığı çok düşük ama sıfır değil: boş kod bulana dek dene.
iken veritabani.sqlite(db, "SELECT count(*) FROM baglanti WHERE kod = ?", [kod]) != "0":
toplam = veritabani.sqlite(db, "SELECT count(*) FROM baglanti")
tik = veritabani.sqlite(db, "SELECT coalesce(sum(tiklama), 0) FROM baglanti")
bugun = veritabani.sqlite(db, "SELECT count(*) FROM tiklama WHERE date(zaman) = date('now', 'localtime')")
g = "<section class='kahraman'><h1>Uzun bağlantılar,<br><em>kısa ve ölçülebilir.</em></h1>"
g = g + "<p>Bir adres yapıştırın; KabaLang ona kısa bir kod versin ve her tıklamayı saysın.</p>"
g = g + "<form id='f'><input id='adres' placeholder='https://…' autocomplete='off' required><button>Kısalt</button></form>"
g = g + "<div id='sonuc'></div></section>"
g = g + "<div class='kutular'>"
g = g + "<div class='kutu'><small>Bağlantı</small><div>" + toplam + "</div></div>"
g = g + "<div class='kutu'><small>Toplam tıklama</small><div>" + tik + "</div></div>"
g = g + "<div class='kutu'><small>Bugün</small><div>" + bugun + "</div></div>"
g = g + "</div>"
g = g + "<table><tr><th>Kısa kod</th><th class='hedef'>Hedef</th><th>Tıklama</th><th></th></tr>"
dongu s icinde satirlar(veritabani.sqlite(db, "SELECT kod, tiklama, adres FROM baglanti ORDER BY olusturma DESC LIMIT 12")):
p = s.bol("|")
kod = p[0]
# Kod yalnız ALFABE harflerinden oluşur → href içinde güvenli.
# Hedef adres kullanıcıdan geldi → yalnız METİN olarak, kaçırılarak basılır.
g = g + "<tr><td class='kod'><a href='/k/" + kod + "'>/k/" + kod + "</a></td>"
g = g + "<td class='hedef'>" + web.kacir(son_sutun(p, 2)) + "</td>"
g = g + "<td class='sayac'>" + p[1] + "</td>"
g = g + "<td><a href='/istatistik/" + kod + "'>istatistik →</a></td></tr>"
g = g + "</table>"
# Küçük bir istemci betiği: formu JSON API'ye gönderir.
g = g + "<script>document.getElementById('f').onsubmit=async e=>{e.preventDefault();const s=document.getElementById('sonuc');"
g = g + "const y=await fetch('/api/kisalt',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({adres:document.getElementById('adres').value})});"
g = g + "const d=await y.json();s.replaceChildren();if(!y.ok){s.textContent='⚠ '+d.hata;return;}"
g = g + "const a=document.createElement('a');a.className='kisa';a.href=d.kisa;a.textContent=location.host+d.kisa;s.append('✓ Hazır: ',a);setTimeout(()=>location.reload(),1600);};</script>"
don web.html(sayfa("Kısalt · KabaLang", g))
fonksiyon kisalt_api(istek):
govde = istek.govde()
eger degil json.gecerli_mi(govde):
don web.json(json.uret({"hata": "gövde geçerli JSON değil"}), 400)
adres = json.al(govde, "adres").kirp()
sorun = adres_dogrula(adres)
eger sorun != "":
don web.json(json.uret({"hata": sorun}), 422)
kod = kaydet(db, adres)
don web.json(json.uret({"kod": kod, "kisa": "/k/" + kod, "adres": adres}), 201)
fonksiyon bulunamadi_sayfasi():
g = "<section class='kahraman'><h1>Bu kod <em>bulunamadı</em></h1><p>Bağlantı silinmiş ya da hiç var olmamış olabilir.</p><p><a href='/'>← ana sayfa</a></p></section>"
don web.html(sayfa("Bulunamadı · Kısalt", g), 404)
fonksiyon yonlendir(istek):
kod = istek.parametre("kod")
eger degil kod_gecerli(kod):
don bulunamadi_sayfasi()
adres = veritabani.sqlite(db, "SELECT adres FROM baglanti WHERE kod = ?", [kod])
eger adres == "":
don bulunamadi_sayfasi()
veritabani.sqlite(db, "INSERT INTO tiklama(kod, zaman) VALUES (?, datetime('now', 'localtime'))", [kod])
veritabani.sqlite(db, "UPDATE baglanti SET tiklama = tiklama + 1 WHERE kod = ?", [kod])
# Gerçek HTTP yönlendirmesi: 302 + Location başlığı.
don web.yanit("", 302).baslik("Location", adres)
# Son 7 günün tıklamalarını SVG çubuk grafiğe çevirir — tamamen sunucuda.
adet = sayi(veritabani.sqlite(db, "SELECT count(*) FROM tiklama WHERE kod = ? AND date(zaman) = date('now', 'localtime', ?)", [kod, "-" + metin(geri) + " days"]))