#include "geri_bildirim.hpp" #include #include #include #include #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/cekirdek/surum.hpp" #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include namespace fiko { // ---- Ayar canlı değişkenleri (varsayılanlar; AyarSistemi sarar) ---- std::string& geriBildirimSunucu() { static std::string s = "https://fikoyt.com:9000"; return s; } std::string& geriBildirimKoleksiyon() { static std::string s = "geriBildirimler"; return s; } bool& geriBildirimEkranGoruntusuVarsayilan() { static bool b = true; return b; } int& geriBildirimZamanAsimiSn() { static int n = 20; return n; } int& geriBildirimMaksMb() { static int n = 8; return n; } namespace { std::wstring genislet(const std::string& s) { return std::wstring(s.begin(), s.end()); } // UTF-8 JSON gövdeyi URL'e POST et. HTTP 2xx → true. // TLS doğrulama GEVŞETİLMEZ: sunucu gerçek Let's Encrypt sertifikası taşır // (guncelleme_indir.cpp'deki self-signed gevşetmesi buraya KOPYALANMAMALI). bool jsonPost(const std::string& url, const std::string& govde, int zamanAsimiSn, std::string& hata) { const std::wstring wurl = genislet(url); URL_COMPONENTS uc{}; uc.dwStructSize = sizeof(uc); wchar_t host[256] = {}, yol[4096] = {}; uc.lpszHostName = host; uc.dwHostNameLength = 255; uc.lpszUrlPath = yol; uc.dwUrlPathLength = 4095; if (!WinHttpCrackUrl(wurl.c_str(), 0, 0, &uc)) { hata = "sunucu adresi çözümlenemedi"; return false; } host[uc.dwHostNameLength] = 0; const bool https = uc.nScheme == INTERNET_SCHEME_HTTPS; HINTERNET oturum = WinHttpOpen(L"FikoCAD-GeriBildirim/1.0", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (!oturum) { hata = "WinHttpOpen başarısız"; return false; } const int ms = zamanAsimiSn * 1000; WinHttpSetTimeouts(oturum, ms, ms, ms, ms); HINTERNET baglanti = WinHttpConnect(oturum, host, uc.nPort, 0); HINTERNET istek = baglanti ? WinHttpOpenRequest(baglanti, L"POST", yol, nullptr, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, https ? WINHTTP_FLAG_SECURE : 0) : nullptr; bool ok = false; if (istek) { static const wchar_t* kBaslik = L"Content-Type: application/json; charset=utf-8\r\n"; if (WinHttpSendRequest(istek, kBaslik, (DWORD)-1L, (LPVOID)govde.data(), (DWORD)govde.size(), (DWORD)govde.size(), 0) && WinHttpReceiveResponse(istek, nullptr)) { DWORD kod = 0, kb = sizeof(kod); WinHttpQueryHeaders(istek, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &kod, &kb, WINHTTP_NO_HEADER_INDEX); if (kod >= 200 && kod < 300) { ok = true; } else { hata = "sunucu HTTP " + std::to_string(kod) + " döndü"; } } else { hata = "sunucuya bağlanılamadı (ağ/DNS/sertifika)"; } WinHttpCloseHandle(istek); } else { hata = "istek açılamadı"; } if (baglanti) WinHttpCloseHandle(baglanti); WinHttpCloseHandle(oturum); return ok; } } // namespace std::string geriBildirimCihazMetni() { SYSTEM_INFO si{}; GetNativeSystemInfo(&si); return "Windows x64 · " + std::to_string((unsigned)si.dwNumberOfProcessors) + " çekirdek"; } std::string geriBildirimSimdiIso() { SYSTEMTIME t{}; GetSystemTime(&t); char buf[32]; std::snprintf(buf, sizeof(buf), "%04u-%02u-%02uT%02u:%02u:%02uZ", t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond); return buf; } void geriBildirimGonderBaslat(GeriBildirimServisi& svc, const std::string& mesaj, const std::string& kategori, bool ekranEkle) { Cekirdek* c = svc.cekirdek(); if (!c) return; if (mesaj.empty()) { svc.hataAyarla("Mesaj boş — ne olduğunu kısaca yazın."); return; } const std::string koleksiyon = geriBildirimKoleksiyon(); // Sözleşme kapısı: geçersiz koleksiyon → sunucuda 401 / yanlış route. İstek hiç kurulmaz. if (!geribildirim::koleksiyonGecerliMi(koleksiyon)) { svc.hataAyarla("Geçersiz koleksiyon adı ayarı: '" + koleksiyon + "' (harf/rakam/alt çizgi olmalı, 'hareketler' olamaz)."); return; } geribildirim::GeriBildirim gb; gb.mesaj = mesaj; gb.kategori = kategori; gb.surum = SURUM_METIN; gb.cihaz = geriBildirimCihazMetni(); gb.tarih = geriBildirimSimdiIso(); if (ekranEkle) gb.ekranPng = svc.ekran(); const std::string govde = geribildirim::govdeKur(gb); // MongoDB doküman sınırı 16MB; base64 ~%33 şişirir → ayarlanabilir üst sınır. const size_t maksBayt = (size_t)geriBildirimMaksMb() * 1024 * 1024; if (govde.size() > maksBayt) { svc.hataAyarla("Gönderi çok büyük (" + std::to_string(govde.size() / (1024 * 1024)) + " MB). Ekran görüntüsünü kaldırıp tekrar deneyin."); return; } const std::string url = geribildirim::urlKur(geriBildirimSunucu(), koleksiyon); const int zamanAsimi = geriBildirimZamanAsimiSn(); // Olay özeti (Olay Günlüğü / makro dinleyicileri): kategori + mesaj başlığı. const std::string ozet = kategori + ": " + geribildirim::utf8Kisalt(mesaj, 60); const u32 bayt = (u32)govde.size(); const bool ekranVar = !gb.ekranPng.empty(); svc.durumAyarla(GeriBildirimServisi::Durum::Gonderiliyor); struct Sonuc { bool ok = false; std::string hata; }; auto sonuc = std::make_shared(); c->gorevler().kuyrukla( "Geri bildirim gönderiliyor", [sonuc, url, govde, zamanAsimi](GorevBaglam&) { sonuc->ok = jsonPost(url, govde, zamanAsimi, sonuc->hata); }, [sonuc, &svc, c, ozet, bayt, ekranVar]() { // main-thread: durum + olay if (sonuc->ok) { svc.durumAyarla(GeriBildirimServisi::Durum::Gonderildi); svc.ekranTemizle(); c->olaylar().yayinla(GeriBildirimGonderildi{bayt, ekranVar}, ozet.c_str(), OlaySiddet::Bilgi, OlayKaynak::Kullanici); } else { const std::string h = sonuc->hata.empty() ? "gönderilemedi" : sonuc->hata; svc.hataAyarla(h); c->olaylar().yayinla(GeriBildirimHatasi{}, h.c_str(), OlaySiddet::Hata, OlayKaynak::Kullanici); } }); } } // namespace fiko