Show Telegram status errors and keep setup instructions admin-only
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"date": "2026-09-06",
|
||||
"environment": "localhost:5183 normal user, injected GET 503 then real GET",
|
||||
"checks": {
|
||||
"errorVisible": true,
|
||||
"retryVisible": true,
|
||||
"recovered": true,
|
||||
"adminInstructionsHidden": true,
|
||||
"oldErrorCleared": true
|
||||
},
|
||||
"limits": [
|
||||
"No Telegram message or link request sent",
|
||||
"Only status error recovery and normal-user configuration view tested",
|
||||
"Bot pairing challenge and external delivery remain open"
|
||||
]
|
||||
}
|
||||
@@ -924,3 +924,10 @@ Taslak türü seçicisine erişilebilir ad eklendi. Üretim derlemesi geçti. Ö
|
||||
- Usul alanı explicit usul/ihaleUsulu/alimTuru değerlerinden eşleşir; Mal Alımı değerinden Açık İhale tahmini yapılmaz. Kaynakta usul yoksa usule göre filtrelenmiş sonuçta gösterilmez.
|
||||
- 209 otomatik test ve derleme geçti. 81 il kapsamı, Türkçe normalizasyon, beş ölçütün taşınması, geçersiz bölge ve usul/kategori ayrımı test edildi. Yerel 5 sentetik ihalede bölge filtresi 1 Giresun kaydına daralttı; bölgeyi kaldırınca 5 kayıt geri geldi; yenileme sonrası sol panelden tekrar uygulandı. 4 kontrol qa-saved-filter-application.json içinde.
|
||||
- İl bazında bölgeleme coğrafi sınırın ilçelerdeki ayrıntısını temsil etmez. Gerçek kaynak usul/şehir doğruluğu ve bütün ölçütlerin birleşik tarayıcı senaryosu ayrıca kabul edilmelidir. Bu işlem bildirim aboneliği oluşturmaz.
|
||||
|
||||
### Telegram durum ekranında hata ve rol görünümü — 6 Eylül 2026
|
||||
- İlk GET hatasında d=null olduğu için bölüm tamamen kayboluyordu. Yükleme/hata/yeniden deneme ekranı eklendi; bağlantı durumunun boolean alanları doğrulanır. Sonradan durum yenileme düğmesi de vardır.
|
||||
- Hesap/token anahtarıyla panel ayrılır; kapalı/eski panelin yanıtı UI'ye uygulanmaz. Tek işlem kilidi, duruma göre başarı/hata mesajları ve erişilebilir status/alert rolleri eklendi. Yenileme başarısızlığı yutulup başarı gibi sunulmaz.
|
||||
- Bot kurulum talimatları yalnız yöneticide gösterilir. Normal kullanıcı hizmetin etkin olmadığını ve yöneticiyle görüşmesi gerektiğini görür. Bağlama başarısı mesajının dış onay mesajını gönderildi varsayması kaldırıldı.
|
||||
- 5183 yeni izole QA örneğinde ilk GET 503, ardından gerçek API yanıtıyla kurtarma sınandı. 5 kontrol geçti: hata, retry, kurtarma, normal kullanıcıda kurulum ayrıntılarının gizli olması ve hatanın temizlenmesi. Kanıt qa-telegram-status.json. Dış mesaj veya bağlama isteği gönderilmedi.
|
||||
- Derleme geçti. Sunucu iş mantığı değişmediği için önceki 209 otomatik test tekrar çalıştırılmadı. Bot eşleme kodunun yaşam döngüsü ve gerçek teslim kabulü açık.
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {useApp} from '../../context/AppContext';
|
||||
import {getToken} from '../../services/auth';
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Send, CheckCircle2, AlertTriangle, Loader2, Unplug } from 'lucide-react';
|
||||
import { telegramDurum, telegramBagla, telegramTest, telegramSil } from '../../services/account';
|
||||
|
||||
@@ -10,22 +12,36 @@ import { telegramDurum, telegramBagla, telegramTest, telegramSil } from '../../s
|
||||
const kodUret = () => String(Math.floor(100000 + Math.random() * 900000));
|
||||
|
||||
export const TelegramBaglanti = () => {
|
||||
const {authUser,isAdmin}=useApp();
|
||||
return <TelegramPanel key={`${authUser?.email}:${getToken()}`} isAdmin={isAdmin}/>;
|
||||
};
|
||||
const TelegramPanel = ({isAdmin}) => {
|
||||
const [d, setD] = useState(null);
|
||||
const [kod] = useState(kodUret);
|
||||
const [busy, setBusy] = useState(null);
|
||||
const [mesaj, setMesaj] = useState(null);
|
||||
|
||||
const yenile = () => telegramDurum().then(setD).catch((e) => setMesaj({ tur: 'hata', metin: e.message }));
|
||||
useEffect(() => { yenile(); }, []);
|
||||
|
||||
const calistir = async (ad, fn, basari) => {
|
||||
setBusy(ad); setMesaj(null);
|
||||
try { await fn(); setMesaj({ tur: 'ok', metin: basari }); await yenile(); }
|
||||
catch (e) { setMesaj({ tur: 'hata', metin: e.message }); }
|
||||
finally { setBusy(null); }
|
||||
const alive=useRef(true),locked=useRef(false);
|
||||
const yenile = async () => {
|
||||
const data=await telegramDurum();
|
||||
if(typeof data.bagli!=='boolean'||typeof data.yapilandirilmis!=='boolean')throw new Error('Telegram bağlantı durumu doğrulanamadı.');
|
||||
if(alive.current)setD(data);
|
||||
};
|
||||
const calistir = async (ad, fn, basari) => {
|
||||
if(locked.current)return;locked.current=true;
|
||||
setBusy(ad);setMesaj(null);
|
||||
try {await fn();if(!alive.current)return;if(ad!=='load')await yenile();if(alive.current&&basari)setMesaj({tur:'ok',metin:basari});}
|
||||
catch(e){if(alive.current)setMesaj({tur:'hata',metin:e.message});}
|
||||
finally{locked.current=false;if(alive.current)setBusy(null);}
|
||||
};
|
||||
useEffect(()=>{alive.current=true;void calistir('load',yenile);return()=>{alive.current=false;};},[]);
|
||||
|
||||
if (!d) return null;
|
||||
if(!d)return <section className="bg-white border rounded-xl p-5 space-y-3" aria-label="Telegram bağlantı durumu">
|
||||
<h3 className="font-bold text-sm">Telegram Bildirimi</h3>
|
||||
{busy&&<p role="status" className="text-sm">Bağlantı durumu yükleniyor…</p>}
|
||||
{mesaj&&<p role="alert" className="text-red-700 text-sm">{mesaj.metin}</p>}
|
||||
<button disabled={!!busy} onClick={()=>calistir('load',yenile)} className="border rounded p-2 text-sm">Telegram durumunu yeniden dene</button>
|
||||
</section>;
|
||||
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-2xl border border-slate-200 shadow-xs space-y-4">
|
||||
@@ -39,19 +55,18 @@ export const TelegramBaglanti = () => {
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-slate-600 leading-relaxed">
|
||||
Yeni ihale özetleri, takipteki ihalede <strong>zeyilname/düzeltme ilanı</strong> ve <strong>sonuç</strong>
|
||||
("X firması Y'ye aldı, %Z kırım") e-postanın yanında Telegram'a da gelir.
|
||||
Telegram bağlantısı ve ilgili bildirim hizmetleri etkinse ihale özetleri bu kanaldan gönderilebilir. Gönderim, okunma veya her kaynak değişikliğinin yakalanması garanti edilmez.
|
||||
</p>
|
||||
|
||||
{!d.yapilandirilmis ? (
|
||||
<div className="bg-amber-50 border border-amber-200 rounded-xl p-4 space-y-2 text-xs text-amber-900 leading-relaxed">
|
||||
<p className="font-bold">Sunucuda Telegram botu henüz tanımlı değil. Kurulum (yönetici, bir kez):</p>
|
||||
{isAdmin ? <><p className="font-bold">Sunucuda Telegram botu henüz tanımlı değil. Kurulum (yönetici, bir kez):</p>
|
||||
<ol className="list-decimal pl-5 space-y-1">
|
||||
<li>Telegram'da <strong>@BotFather</strong>'a yazın: <code className="bg-amber-100 px-1 rounded">/newbot</code> → bir ad ve <code className="bg-amber-100 px-1 rounded">…bot</code> ile biten kullanıcı adı verin.</li>
|
||||
<li>BotFather'ın verdiği tokenı Coolify → ihaleist → Environment'a <code className="bg-amber-100 px-1 rounded">TELEGRAM_BOT_TOKEN</code> olarak ekleyin, Redeploy.</li>
|
||||
<li>Bu sayfa yeniden açıldığında aşağıdaki bağlama adımları görünür.</li>
|
||||
</ol>
|
||||
<p className="text-[11px] text-amber-800">Token yalnızca sunucuda durur; uygulamaya girilmez.</p>
|
||||
<p className="text-[11px] text-amber-800">Token yalnızca sunucuda durur; uygulamaya girilmez.</p></> : <p>Telegram bildirim hizmeti henüz etkin değil. Kurulum için yöneticinizle görüşün.</p>}
|
||||
</div>
|
||||
) : d.bagli ? (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
@@ -78,15 +93,16 @@ export const TelegramBaglanti = () => {
|
||||
<li>Bota şu kodu yazıp gönderin: <code className="bg-white border border-sky-300 px-2 py-0.5 rounded font-mono text-sm font-black tracking-widest">{kod}</code></li>
|
||||
<li>Sonra buraya dönüp <strong>Doğrula</strong>'ya basın.</li>
|
||||
</ol>
|
||||
<button onClick={() => calistir('bagla', () => telegramBagla(kod), 'Bağlandı! Telegram\'a onay mesajı gönderildi.')} disabled={!!busy}
|
||||
<button onClick={() => calistir('bagla', () => telegramBagla(kod), 'Telegram bağlantısı kaydedildi.')} disabled={!!busy}
|
||||
className="bg-sky-600 hover:bg-sky-500 disabled:opacity-40 text-white font-bold text-xs px-5 py-2 rounded-lg flex items-center gap-1.5">
|
||||
{busy === 'bagla' ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <CheckCircle2 className="w-3.5 h-3.5" />} Doğrula
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button disabled={!!busy} onClick={()=>calistir('load',yenile)} className="border rounded p-2 text-xs">Telegram durumunu yenile</button>
|
||||
{mesaj && (
|
||||
<div className={`rounded-lg p-3 flex gap-2 text-xs ${mesaj.tur === 'ok' ? 'bg-emerald-50 border border-emerald-200 text-emerald-900' : 'bg-red-50 border border-red-200 text-red-800'}`}>
|
||||
<div role={mesaj.tur === 'ok' ? 'status' : 'alert'} className={`rounded-lg p-3 flex gap-2 text-xs ${mesaj.tur === 'ok' ? 'bg-emerald-50 border border-emerald-200 text-emerald-900' : 'bg-red-50 border border-red-200 text-red-800'}`}>
|
||||
{mesaj.tur === 'ok' ? <CheckCircle2 className="w-4 h-4 shrink-0" /> : <AlertTriangle className="w-4 h-4 shrink-0" />}
|
||||
<span>{mesaj.metin}</span>
|
||||
</div>
|
||||
|
||||
@@ -39,7 +39,11 @@ const qaDelivery=qaSubscriptionStore.reserveDelivery(qaReviewSub.id,{to:user.ema
|
||||
qaSubscriptionStore.failDelivery(qaDelivery,'Sentetik belirsiz sağlayıcı sonucu.');
|
||||
qaSubscriptionStore.reserveDelivery(qaReviewSub.id,null,[]);
|
||||
let subscriptionFailure='read';
|
||||
let telegramFailure=true;
|
||||
const ui=await createServer({server:{host:'127.0.0.1',port:0,proxy:{'/api':{target:'http://127.0.0.1:'+port}}},plugins:[{name:'qa-fixtures',configureServer(server){server.middlewares.use(async(req,res,next)=>{
|
||||
if(req.url==='/qa-telegram-controls'){res.setHeader('Content-Type','text/html; charset=utf-8');res.end(`<html lang="tr"><h1>Yerel Telegram testi</h1><button id="recover">Telegram durum hatasını kapat</button><p role="status"></p><script>document.getElementById('recover').onclick=async()=>{await fetch('/qa-telegram-recover',{method:'POST'});document.querySelector('[role=status]').textContent='Hata kapatıldı';};</script></html>`);return;}
|
||||
if(req.url==='/qa-telegram-recover'&&req.method==='POST'){telegramFailure=false;res.end('ok');return;}
|
||||
if(req.url==='/api/me/telegram'&&req.method==='GET'&&telegramFailure){res.writeHead(503,{'Content-Type':'application/json'});res.end(JSON.stringify({success:false,error:'QA: Telegram durumu alınamadı.'}));return;}
|
||||
if(req.url==='/qa-subscription-controls'){res.setHeader('Content-Type','text/html; charset=utf-8');res.end(`<html lang="tr"><h1>Yerel abonelik testi</h1><button id="read">Abonelik liste hatası</button><button id="delete">Abonelik silme hatası</button><button id="none">Abonelik hatalarını kapat</button><p role="status"></p><script>for(const mode of ['read','delete','none'])document.getElementById(mode).onclick=async()=>{const r=await fetch('/qa-subscription-failure?mode='+mode,{method:'POST'});document.querySelector('[role=status]').textContent=await r.text();};</script></html>`);return;}
|
||||
if(req.url?.startsWith('/qa-subscription-failure?')&&req.method==='POST'){subscriptionFailure=new URL(req.url,'http://localhost').searchParams.get('mode');res.end('Abonelik test durumu: '+subscriptionFailure);return;}
|
||||
if(req.url?.startsWith('/api/notify/subscriptions')&&((subscriptionFailure==='read'&&req.method==='GET')||(subscriptionFailure==='delete'&&req.method==='DELETE'))){res.writeHead(503,{'Content-Type':'application/json'});res.end(JSON.stringify({success:false,error:'QA: Abonelik deposuna erişilemiyor.'}));return;}
|
||||
|
||||
Reference in New Issue
Block a user