#pragma once // Geri bildirim gövde kurucu — SAF (Win32/ağ yok), bu yüzden birim test edilebilir. // Sunucu sözleşmesi (docker/fikoyt-sunucu, kokDB generic CRUD — SUNUCUDA DEĞİŞİKLİK YOK): // POST https://fikoyt.com:9000/fapiv1/fikoyt.com.FikoYT.sistem.DB..ekle // Content-Type: application/json → düz JSON gövde (multipart DEĞİL) // // ÜÇ SÖZLEŞME KURALI (ihlali sunucuyu düşürür / 401 verir — koleksiyonGecerliMi zorlar): // 1. Koleksiyon "hareketler" OLAMAZ → kokDB/guvenlik.go hareketYazmaKontrol 401 verir. // 2. Koleksiyon adı '.' veya '-' İÇEREMEZ → Fiber v3 route parser ':db' parametresini // bu ayraçlarda böler, istek yanlış route'a düşer. // 3. multipart DOSYA GÖNDERİLMEZ → kokDB/routes.go:295 oturumsuz istemcide // AktifKullanici.Id nil-deref yapar; goroutine içinde olduğu için recover // yakalayamaz ve SUNUCU PROCESS'İ ÖLÜR. Ekran görüntüsü base64 alan olarak gider. #include #include #include "fiko/core/temel.hpp" namespace fiko::geribildirim { // JSON string kaçışı (RFC 8259). Kontrol karakterleri \u00XX olur. inline std::string jsonKacis(const std::string& s) { std::string o; o.reserve(s.size() + 16); for (unsigned char c : s) { switch (c) { case '"': o += "\\\""; break; case '\\': o += "\\\\"; break; case '\n': o += "\\n"; break; case '\r': o += "\\r"; break; case '\t': o += "\\t"; break; case '\b': o += "\\b"; break; case '\f': o += "\\f"; break; default: if (c < 0x20) { static const char* h = "0123456789abcdef"; o += "\\u00"; o += h[c >> 4]; o += h[c & 0xF]; } else { o += (char)c; // UTF-8 baytları olduğu gibi geçer } } } return o; } // base64 kodla (standart alfabe, '=' dolgulu). inline std::string base64Kodla(const std::vector& v) { static const char* A = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; std::string o; o.reserve(((v.size() + 2) / 3) * 4); size_t i = 0; for (; i + 2 < v.size(); i += 3) { const u32 n = ((u32)v[i] << 16) | ((u32)v[i + 1] << 8) | v[i + 2]; o += A[(n >> 18) & 63]; o += A[(n >> 12) & 63]; o += A[(n >> 6) & 63]; o += A[n & 63]; } if (i + 1 == v.size()) { const u32 n = (u32)v[i] << 16; o += A[(n >> 18) & 63]; o += A[(n >> 12) & 63]; o += "=="; } else if (i + 2 == v.size()) { const u32 n = ((u32)v[i] << 16) | ((u32)v[i + 1] << 8); o += A[(n >> 18) & 63]; o += A[(n >> 12) & 63]; o += A[(n >> 6) & 63]; o += '='; } return o; } // Sözleşme kuralı 1+2 (yukarı bak). Ayar ekranından serbest metin girilebildiği için // gönderim yolunda ZORUNLU kapı — geçersizse istek hiç kurulmaz. inline bool koleksiyonGecerliMi(const std::string& k) { if (k.empty() || k == "hareketler") return false; for (char c : k) if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) return false; return true; } // Tam uç URL. taban sonundaki '/' tolere edilir. inline std::string urlKur(const std::string& taban, const std::string& koleksiyon) { std::string t = taban; while (!t.empty() && t.back() == '/') t.pop_back(); return t + "/fapiv1/fikoyt.com.FikoYT.sistem.DB." + koleksiyon + ".ekle"; } // UTF-8 güvenli kısaltma (olay özeti). Çok-baytlı karakterin ORTASINDAN bölmez — // naif substr Türkçe metinde bozuk bayt üretir ve olay günlüğünde çöp glif olur. inline std::string utf8Kisalt(const std::string& s, size_t maks) { if (s.size() <= maks) return s; size_t k = maks; while (k > 0 && ((u8)s[k] & 0xC0) == 0x80) --k; // devam baytı (10xxxxxx) → başa sar return s.substr(0, k); } struct GeriBildirim { std::string mesaj; std::string kategori; // "Hata" | "Öneri" | "Soru" std::string surum; std::string cihaz; std::string tarih; // ISO-8601 UTC std::vector ekranPng; // boş → alan hiç eklenmez }; // JSON gövde. ekranPng boşsa "ekranGoruntusu" alanı YAZILMAZ (boş string yerine yokluk). inline std::string govdeKur(const GeriBildirim& g) { std::string j = "{"; j += "\"mesaj\":\"" + jsonKacis(g.mesaj) + "\""; j += ",\"kategori\":\"" + jsonKacis(g.kategori) + "\""; j += ",\"surum\":\"" + jsonKacis(g.surum) + "\""; j += ",\"cihaz\":\"" + jsonKacis(g.cihaz) + "\""; j += ",\"tarih\":\"" + jsonKacis(g.tarih) + "\""; j += ",\"okundu\":false"; if (!g.ekranPng.empty()) j += ",\"ekranGoruntusu\":\"" + base64Kodla(g.ekranPng) + "\""; j += "}"; return j; } } // namespace fiko::geribildirim