| |
| |
| |
| |
| |
|
|
| #include <iostream> |
| #include <fstream> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| #include <map> |
| #include <chrono> |
| #include <ctime> |
| #include <cstring> |
| #include <cstdlib> |
| #include <cstdio> |
| #include <sys/stat.h> |
| #include <sys/socket.h> |
| #include <sys/wait.h> |
| #include <netinet/in.h> |
| #include <unistd.h> |
| #include <dirent.h> |
|
|
| static const int PORT = 7860; |
| static std::string GH_TOKEN = std::getenv("GH_TOKEN") ? std::getenv("GH_TOKEN") : ""; |
| static std::string GH_REPO = std::getenv("GH_REPO") ? std::getenv("GH_REPO") : "overandor/rentmasseur-extension"; |
| static std::string ADMIN_TOKEN = std::getenv("ADMIN_TOKEN") ? std::getenv("ADMIN_TOKEN") : ""; |
| static const std::string CONTENT_DIR = "./content"; |
| static const std::string RECEIPTS_DIR = "./receipts"; |
| static const std::string AVAILABILITY_FILE = "./availability.json"; |
| static const std::string KPI_DIR = "./content/kpis"; |
| static const std::string KPI_PATH = "./content/kpis/hourly_kpis.jsonl"; |
|
|
| static void ensure_dir(const std::string& path) { |
| mkdir(path.c_str(), 0755); |
| } |
|
|
| static std::string iso_timestamp() { |
| auto now = std::chrono::system_clock::now(); |
| std::time_t t = std::chrono::system_clock::to_time_t(now); |
| char buf[32]; |
| std::strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", std::gmtime(&t)); |
| return std::string(buf); |
| } |
|
|
| static std::string compact_timestamp() { |
| auto now = std::chrono::system_clock::now(); |
| std::time_t t = std::chrono::system_clock::to_time_t(now); |
| char buf[32]; |
| std::strftime(buf, sizeof(buf), "%Y%m%d_%H%M%S", std::gmtime(&t)); |
| return std::string(buf); |
| } |
|
|
| static bool file_exists(const std::string& path) { |
| std::ifstream f(path); |
| return static_cast<bool>(f); |
| } |
|
|
| static std::string read_file(const std::string& path) { |
| std::ifstream f(path); |
| if (!f) return ""; |
| std::stringstream ss; |
| ss << f.rdbuf(); |
| return ss.str(); |
| } |
|
|
| static std::string json_escape(const std::string& s) { |
| std::string out; |
| for (char c : s) { |
| if (c == '"') out += "\\\"";else if (c == '\\') out += "\\\\"; |
| else if (c == '\n') out += "\\n"; |
| else if (c == '\r') out += "\\r"; |
| else if (c == '\t') out += "\\t"; |
| else if ((unsigned char)c < 0x20) { |
| char buf[8]; |
| std::snprintf(buf, sizeof(buf), "\\u%04x", (unsigned char)c); |
| out += buf; |
| } else out += c; |
| } |
| return out; |
| } |
|
|
| static std::string http_response(int code, const std::string& content_type, const std::string& body) { |
| std::string reason = code == 200 ? "OK" : (code == 202 ? "Accepted" : (code == 403 ? "Forbidden" : (code == 404 ? "Not Found" : "Error"))); |
| std::ostringstream ss; |
| ss << "HTTP/1.1 " << code << " " << reason << "\r\n"; |
| ss << "Content-Type: " << content_type << "\r\n"; |
| ss << "Content-Length: " << body.size() << "\r\n"; |
| ss << "Access-Control-Allow-Origin: *\r\n"; |
| ss << "Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"; |
| ss << "Access-Control-Allow-Headers: Content-Type\r\n"; |
| ss << "Connection: close\r\n\r\n"; |
| ss << body; |
| return ss.str(); |
| } |
|
|
| static std::string get_method(const std::string& request) { |
| size_t space = request.find(' '); |
| return space == std::string::npos ? "GET" : request.substr(0, space); |
| } |
|
|
| static std::string get_path(const std::string& request) { |
| size_t first = request.find(' '); |
| if (first == std::string::npos) return "/"; |
| size_t second = request.find(' ', first + 1); |
| if (second == std::string::npos) return "/"; |
| return request.substr(first + 1, second - first - 1); |
| } |
|
|
| static std::string get_body(const std::string& request) { |
| size_t pos = request.find("\r\n\r\n"); |
| return pos == std::string::npos ? "" : request.substr(pos + 4); |
| } |
|
|
| struct CommandResult { |
| int exit_code; |
| std::string output; |
| }; |
|
|
| static CommandResult run_command_evidence(const std::string& cmd) { |
| std::string wrapped = cmd + " 2>&1"; |
| FILE* pipe = popen(wrapped.c_str(), "r"); |
| if (!pipe) return {127, "popen failed"}; |
| char buffer[512]; |
| std::string output; |
| while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { |
| output += buffer; |
| if (output.size() > 20000) { |
| output = output.substr(output.size() - 20000); |
| } |
| } |
| int status = pclose(pipe); |
| int code = status; |
| if (WIFEXITED(status)) code = WEXITSTATUS(status); |
| return {code, output}; |
| } |
|
|
| static std::string write_receipt(const std::string& action, const std::string& status, int exit_code, const std::string& output, const std::string& extra_json = "") { |
| ensure_dir(RECEIPTS_DIR); |
| std::string path = RECEIPTS_DIR + "/hf_action_" + compact_timestamp() + "_" + action + ".json"; |
| std::ofstream f(path); |
| if (f) { |
| f << "{\n"; |
| f << " \"action\": \"" << json_escape(action) << "\",\n"; |
| f << " \"status\": \"" << json_escape(status) << "\",\n"; |
| f << " \"exit_code\": " << exit_code << ",\n"; |
| f << " \"timestamp\": \"" << iso_timestamp() << "\",\n"; |
| f << " \"stdout_stderr_tail\": \"" << json_escape(output) << "\""; |
| if (!extra_json.empty()) f << ",\n " << extra_json; |
| f << "\n}\n"; |
| } |
| return path; |
| } |
|
|
| static int count_files(const std::string& dir) { |
| int count = 0; |
| DIR* d = opendir(dir.c_str()); |
| if (!d) return 0; |
| struct dirent* entry; |
| while ((entry = readdir(d)) != nullptr) { |
| if (entry->d_type == DT_REG) count++; |
| } |
| closedir(d); |
| return count; |
| } |
|
|
| static std::string read_json_or_block(const std::string& path, const std::string& name) { |
| std::string c = read_file(path); |
| if (!c.empty()) return c; |
| return "{\"status\":\"missing_real_data\",\"name\":\"" + json_escape(name) + "\",\"path\":\"" + json_escape(path) + "\"}"; |
| } |
|
|
| static std::string bios_json() { |
| std::string dir = CONTENT_DIR + "/bios"; |
| DIR* d = opendir(dir.c_str()); |
| if (!d) return "{\"status\":\"missing_real_data\",\"bios\":[],\"reason\":\"content/bios directory does not exist\"}"; |
| std::ostringstream ss; |
| ss << "{\"status\":\"ok\",\"bios\":["; |
| bool first = true; |
| struct dirent* entry; |
| while ((entry = readdir(d)) != nullptr) { |
| if (entry->d_type != DT_REG) continue; |
| std::string name = entry->d_name; |
| std::string content = read_file(dir + "/" + name); |
| if (!first) ss << ","; |
| first = false; |
| ss << "{\"file\":\"" << json_escape(name) << "\",\"content\":\"" << json_escape(content) << "\"}"; |
| } |
| closedir(d); |
| ss << "]}"; |
| return ss.str(); |
| } |
|
|
| static std::string gh_api(const std::string& method, const std::string& endpoint, const std::string& body = "") { |
| if (GH_TOKEN.empty()) return "{\"status\":\"blocked\",\"reason\":\"GH_TOKEN not set\"}"; |
| std::string cmd = "curl -sS -X " + method; |
| cmd += " -H \"Authorization: Bearer " + GH_TOKEN + "\""; |
| cmd += " -H \"Accept: application/vnd.github+json\""; |
| cmd += " -H \"X-GitHub-Api-Version: 2022-11-28\""; |
| if (!body.empty()) cmd += " -d '" + body + "'"; |
| cmd += " https://api.github.com/repos/" + GH_REPO + "/" + endpoint; |
| CommandResult r = run_command_evidence(cmd); |
| return r.output.empty() ? "{\"status\":\"dispatched_or_empty_response\",\"exit_code\":" + std::to_string(r.exit_code) + "}" : r.output; |
| } |
|
|
| static std::string url_encode_workflow(const std::string& s) { |
| std::string out; |
| for (char c : s) { |
| if (c == '/') out += "%2F"; |
| else if (c == ' ') out += "%20"; |
| else out += c; |
| } |
| return out; |
| } |
|
|
| static std::string action_response(const std::string& action, const std::string& cmd) { |
| CommandResult r = run_command_evidence(cmd); |
| std::string status = r.exit_code == 0 ? "success" : "failed"; |
| std::string label = r.exit_code == 0 ? (!r.output.empty() ? "GREEN_REAL" : "GRAY_NO_DATA") : "RED_FAILED"; |
| std::string receipt = write_receipt(action, status, r.exit_code, r.output, "\"command\": \"" + json_escape(cmd) + "\""); |
| std::ostringstream ss; |
| ss << "{\"status\":\"" << status << "\",\"label\":\"" << label << "\",\"action\":\"" << json_escape(action) << "\",\"exit_code\":" << r.exit_code; |
| ss << ",\"receipt\":\"" << json_escape(receipt) << "\",\"stdout_stderr_tail\":\"" << json_escape(r.output) << "\"}"; |
| return ss.str(); |
| } |
|
|
| |
|
|
| struct MetricSnapshot { |
| long profile_views = 0; |
| long contact_clicks = 0; |
| long new_visits = 0; |
| long new_emails = 0; |
| long online_bookmarks = 0; |
| bool profile_visible = false; |
| bool available = false; |
| long public_visits = 0; |
| long days_online = 0; |
| double views_per_day = 0.0; |
| }; |
|
|
| static long extract_long(const std::string& json, const std::string& key) { |
| std::string needle = "\"" + key + "\""; |
| size_t pos = json.find(needle); |
| if (pos == std::string::npos) return 0; |
| pos = json.find(":", pos); |
| if (pos == std::string::npos) return 0; |
| pos++; |
| while (pos < json.size() && (json[pos] == ' ' || json[pos] == '\t')) pos++; |
| return std::atol(json.c_str() + pos); |
| } |
|
|
| static double extract_double(const std::string& json, const std::string& key) { |
| std::string needle = "\"" + key + "\""; |
| size_t pos = json.find(needle); |
| if (pos == std::string::npos) return 0.0; |
| pos = json.find(":", pos); |
| if (pos == std::string::npos) return 0.0; |
| pos++; |
| while (pos < json.size() && (json[pos] == ' ' || json[pos] == '\t')) pos++; |
| return std::atof(json.c_str() + pos); |
| } |
|
|
| static bool extract_bool(const std::string& json, const std::string& key) { |
| std::string needle = "\"" + key + "\""; |
| size_t pos = json.find(needle); |
| if (pos == std::string::npos) return false; |
| pos = json.find(":", pos); |
| if (pos == std::string::npos) return false; |
| pos++; |
| while (pos < json.size() && (json[pos] == ' ' || json[pos] == '\t')) pos++; |
| return json.substr(pos, 4) == "true"; |
| } |
|
|
| static std::vector<MetricSnapshot> load_metric_snapshots() { |
| std::vector<MetricSnapshot> snaps; |
| std::string content = read_file(CONTENT_DIR + "/metrics_ingest.jsonl"); |
| if (content.empty()) return snaps; |
| std::istringstream stream(content); |
| std::string line; |
| while (std::getline(stream, line)) { |
| if (line.empty()) continue; |
| MetricSnapshot s; |
| |
| s.profile_views = extract_long(line, "profile_views"); |
| s.contact_clicks = extract_long(line, "contact_clicks"); |
| s.new_visits = extract_long(line, "new_visits"); |
| s.new_emails = extract_long(line, "new_emails"); |
| s.online_bookmarks = extract_long(line, "online_bookmarks"); |
| s.profile_visible = extract_bool(line, "profile_visible"); |
| s.available = extract_bool(line, "available"); |
| |
| s.public_visits = extract_long(line, "public_visits"); |
| s.days_online = extract_long(line, "days_online"); |
| s.views_per_day = extract_double(line, "views_per_day"); |
| |
| if (s.profile_views > 0 || s.public_visits > 0 || s.days_online > 0) { |
| snaps.push_back(s); |
| } |
| } |
| return snaps; |
| } |
|
|
| static std::string compute_immortality(const std::vector<MetricSnapshot>& snaps) { |
| if (snaps.empty()) { |
| return "{\"score\":0.0,\"grade\":\"NO_DATA\",\"components\":{}}"; |
| } |
| const auto& latest = snaps.back(); |
| double days_online = (double)latest.days_online; |
| double vpd = latest.views_per_day; |
|
|
| double profile_age_score = std::min(days_online / 1000.0, 1.0); |
|
|
| |
| double vpd_trend = 0.5; |
| if (snaps.size() >= 2) { |
| double recent = 0, older = 0; int rc = 0, oc = 0; |
| for (size_t i = 0; i < snaps.size(); i++) { |
| if (i >= snaps.size() - 2) { recent += snaps[i].views_per_day; rc++; } |
| else { older += snaps[i].views_per_day; oc++; } |
| } |
| if (oc > 0 && older > 0) vpd_trend = std::min((recent / rc) / (older / oc), 2.0) / 2.0; |
| else if (rc > 0 && recent > 0) vpd_trend = 0.6; |
| } |
|
|
| double vis_count = 0, avail_count = 0; |
| for (const auto& s : snaps) { if (s.profile_visible) vis_count++; if (s.available) avail_count++; } |
| double vis_persist = vis_count / (double)snaps.size(); |
| double avail_stab = avail_count / (double)snaps.size(); |
| double retention = vpd > 0 ? std::min(vpd / 100.0, 1.0) : 0.0; |
|
|
| double score = profile_age_score * 0.25 + vpd_trend * 0.25 + vis_persist * 0.20 + avail_stab * 0.15 + retention * 0.15; |
|
|
| const char* grade = score >= 0.80 ? "IMMORTAL" : score >= 0.60 ? "RESILIENT" : score >= 0.40 ? "STABLE" : score >= 0.20 ? "FRAGILE" : "DECLINING"; |
|
|
| char buf[512]; |
| std::snprintf(buf, sizeof(buf), |
| "{\"score\":%.4f,\"grade\":\"%s\",\"components\":{" |
| "\"days_online\":%ld,\"views_per_day\":%.1f,\"profile_age_score\":%.4f," |
| "\"views_per_day_trend\":%.4f,\"visibility_persistence\":%.4f," |
| "\"availability_stability\":%.4f,\"retention_score\":%.4f}}", |
| score, grade, latest.days_online, vpd, profile_age_score, vpd_trend, vis_persist, avail_stab, retention); |
| return std::string(buf); |
| } |
|
|
| static std::string compute_virality(const std::vector<MetricSnapshot>& snaps) { |
| if (snaps.empty()) { |
| return "{\"score\":0.0,\"grade\":\"NO_DATA\",\"components\":{}}"; |
| } |
| const auto& latest = snaps.back(); |
| long pv = latest.profile_views; |
| long cc = latest.contact_clicks; |
| long nv = latest.new_visits; |
| long ne = latest.new_emails; |
| long ob = latest.online_bookmarks; |
|
|
| long views_velocity = 0, contact_velocity = 0; |
| double views_accel = 0.0; |
| if (snaps.size() >= 2) { |
| views_velocity = std::max(pv - snaps[snaps.size()-2].profile_views, 0L); |
| contact_velocity = std::max(cc - snaps[snaps.size()-2].contact_clicks, 0L); |
| } |
| if (snaps.size() >= 3) { |
| long v1 = std::max(snaps[snaps.size()-2].profile_views - snaps[snaps.size()-3].profile_views, 0L); |
| long v2 = views_velocity; |
| if (v1 > 0) views_accel = (double)(v2 - v1) / (double)v1; |
| else if (v2 > 0) views_accel = 1.0; |
| } |
|
|
| double new_visitor_rate = pv > 0 ? (double)nv / pv : 0; |
| double bookmark_rate = pv > 0 ? (double)ob / pv : 0; |
| double email_rate = pv > 0 ? (double)ne / pv : 0; |
| double ctr = pv > 0 ? (double)cc / pv : 0; |
|
|
| double vv_norm = std::min(views_velocity / 50.0, 1.0); |
| double va_norm = std::min(std::max(views_accel, 0.0) / 0.5, 1.0); |
| double cv_norm = std::min(contact_velocity / 10.0, 1.0); |
| double nv_norm = std::min(new_visitor_rate / 0.05, 1.0); |
| double bm_norm = std::min(bookmark_rate / 0.01, 1.0); |
| double em_norm = std::min(email_rate / 0.01, 1.0); |
| double ctr_norm = std::min(ctr / 0.10, 1.0); |
|
|
| double score = vv_norm*0.25 + va_norm*0.15 + cv_norm*0.20 + nv_norm*0.15 + bm_norm*0.05 + em_norm*0.05 + ctr_norm*0.15; |
|
|
| const char* grade = score >= 0.70 ? "VIRAL" : score >= 0.50 ? "ACCELERATING" : score >= 0.30 ? "STEADY" : score >= 0.15 ? "SLOW" : "STAGNANT"; |
|
|
| char buf[800]; |
| std::snprintf(buf, sizeof(buf), |
| "{\"score\":%.4f,\"grade\":\"%s\",\"components\":{" |
| "\"profile_views\":%ld,\"views_velocity\":%ld,\"views_acceleration\":%.4f," |
| "\"contact_clicks\":%ld,\"contact_click_velocity\":%ld," |
| "\"new_visits\":%ld,\"new_visitor_rate\":%.4f," |
| "\"online_bookmarks\":%ld,\"bookmark_rate\":%.4f," |
| "\"new_emails\":%ld,\"email_rate\":%.4f," |
| "\"contact_click_rate\":%.4f}}", |
| score, grade, pv, views_velocity, views_accel, cc, contact_velocity, |
| nv, new_visitor_rate, ob, bookmark_rate, ne, email_rate, ctr); |
| return std::string(buf); |
| } |
|
|
| static std::string kpi_response() { |
| auto snaps = load_metric_snapshots(); |
| std::string imm = compute_immortality(snaps); |
| std::string vir = compute_virality(snaps); |
| std::ostringstream ss; |
| ss << "{\"packet_type\":\"rm_wargrade_kpis\",\"timestamp\":\"" << iso_timestamp() << "\"," |
| << "\"snapshots_analyzed\":" << snaps.size() << "," |
| << "\"immortality\":" << imm << "," |
| << "\"virality\":" << vir << "}"; |
| |
| write_receipt("kpi_computation", "success", 0, "war-grade KPI computed in C++", |
| "\"snapshots\":" + std::to_string(snaps.size())); |
| return ss.str(); |
| } |
|
|
| static std::string blocked_response(const std::string& action, const std::string& reason) { |
| std::string receipt = write_receipt(action, "blocked", 0, reason); |
| return "{\"status\":\"blocked\",\"action\":\"" + json_escape(action) + "\",\"reason\":\"" + json_escape(reason) + "\",\"receipt\":\"" + json_escape(receipt) + "\"}"; |
| } |
|
|
| static bool is_mutation(const std::string& m, const std::string& p) { |
| if (m == "POST" && p != "/api/metrics/ingest") return true; |
| if (p.rfind("/api/cicd/trigger/", 0) == 0) return true; |
| if (p.rfind("/api/rotate/", 0) == 0) return true; |
| if (p == "/api/run/ga-rl" || p == "/api/run/orchestrator" || p == "/api/run/availability" || p == "/api/rotator/report") return true; |
| return false; |
| } |
|
|
| static std::string check_admin(const std::string& req, const std::string& path, const std::string& method) { |
| if (!is_mutation(method, path)) return ""; |
| if (ADMIN_TOKEN.empty()) return blocked_response("auth", "ADMIN_TOKEN required; mutation endpoints disabled until ADMIN_TOKEN is set."); |
| size_t ap = req.find("Authorization: Bearer "); |
| if (ap != std::string::npos) { |
| size_t vs = ap + 21; |
| size_t ve = req.find("\r\n", vs); |
| if (ve != std::string::npos && req.substr(vs, ve - vs) == ADMIN_TOKEN) return ""; |
| } |
| return blocked_response("auth", "Admin token required for mutation endpoints. Use Authorization: Bearer <ADMIN_TOKEN> header."); |
| } |
|
|
| static std::string landing_page() { |
| return R"HTML(<!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width,initial-scale=1.0"> |
| <title>RentMasseur RevenueOps Control Plane</title> |
| <style> |
| *{box-sizing:border-box;margin:0;padding:0} |
| body{font-family:'SF Mono',Monaco,Consolas,monospace;background:#0a0b14;color:#e0e0e8;padding:20px} |
| h1{font-size:24px;font-weight:600;letter-spacing:-0.5px} |
| h2{font-size:13px;text-transform:uppercase;letter-spacing:1px;color:#6b7280;margin-bottom:12px} |
| .subtitle{color:#6b7280;font-size:13px;margin-top:4px} |
| .header{padding:24px 0;border-bottom:1px solid #1e2030;margin-bottom:24px} |
| .header h1{color:#fff} |
| .status-bar{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap} |
| .tag{padding:4px 10px;border-radius:4px;font-size:11px;font-weight:600} |
| .tag-green{background:#0d3320;color:#39ff88;border:1px solid #1a5c3a} |
| .tag-gray{background:#1a1b2e;color:#6b7280;border:1px solid #2a2b3e} |
| .tag-red{background:#330d0d;color:#ff5370;border:1px solid #5c1a1a} |
| .tag-yellow{background:#332b0d;color:#ffd166;border:1px solid #5c4a1a} |
| .tag-black{background:#0d0d0d;color:#444;border:1px solid #222} |
| .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(340px,1fr));gap:16px;margin-top:16px} |
| .panel{background:#11121f;border:1px solid #1e2030;border-radius:8px;padding:20px;min-height:200px} |
| .panel pre{white-space:pre-wrap;word-break:break-all;font-size:12px;line-height:1.5;color:#9ca3af;max-height:400px;overflow:auto} |
| .metric-row{display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px solid #1a1b2e;font-size:13px} |
| .metric-label{color:#6b7280} |
| .metric-value{color:#e0e0e8;font-weight:600} |
| .metric-zero{color:#444} |
| .truth{font-size:12px;color:#ff5370;padding:8px;background:#1a0a0a;border-radius:4px;margin-top:8px} |
| .actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px} |
| .btn{padding:8px 14px;border-radius:6px;border:1px solid #2a2b3e;background:#161728;color:#9ca3af;text-decoration:none;font-size:12px;cursor:pointer;transition:all 0.15s} |
| .btn:hover{border-color:#3a3b4e;color:#fff} |
| .btn-danger{border-color:#3a1a1a;color:#ff5370} |
| .btn-danger:hover{background:#1a0a0a} |
| .btn-ok{border-color:#1a3a2a;color:#39ff88} |
| .btn-ok:hover{background:#0a1a0a} |
| .footer{margin-top:32px;padding-top:16px;border-top:1px solid #1e2030;color:#444;font-size:11px;text-align:center} |
| #daily-proof{font-size:12px;line-height:1.8;color:#9ca3af} |
| .proof-q{color:#6b7280} |
| .proof-a{color:#e0e0e8} |
| </style> |
| </head> |
| <body> |
| |
| <div class="header"> |
| <h1>RentMasseur RevenueOps Control Plane</h1> |
| <p class="subtitle">Mission: one paying client per day, or prove exactly why it failed today.</p> |
| <div class="status-bar"> |
| <span class="tag tag-gray" id="tag-health">HEALTH: checking...</span> |
| <span class="tag tag-gray" id="tag-metrics">METRICS: checking...</span> |
| <span class="tag tag-gray" id="tag-candidates">CANDIDATES: checking...</span> |
| <span class="tag tag-gray" id="tag-decision">DECISION: checking...</span> |
| <span class="tag tag-gray" id="tag-immortality">IMMORTALITY: checking...</span> |
| <span class="tag tag-gray" id="tag-virality">VIRALITY: checking...</span> |
| <span class="tag tag-black">AVAILABILITY: BLACK_DISABLED</span> |
| </div> |
| </div> |
| |
| <div class="grid"> |
| |
| <div class="panel"> |
| <h2>Mission Control</h2> |
| <div class="metric-row"><span class="metric-label">Today target</span><span class="metric-value">1 paid client</span></div> |
| <div class="metric-row"><span class="metric-label">Current status</span><span class="metric-value" id="mission-status">loading...</span></div> |
| <div class="metric-row"><span class="metric-label">Prospects today</span><span class="metric-value metric-zero" id="prospects-count">0</span></div> |
| <div class="metric-row"><span class="metric-label">Leads active</span><span class="metric-value metric-zero" id="leads-active">0</span></div> |
| <div class="metric-row"><span class="metric-label">Bookings confirmed</span><span class="metric-value metric-zero" id="bookings-confirmed">0</span></div> |
| <div class="metric-row"><span class="metric-label">Revenue verified</span><span class="metric-value metric-zero">$0</span></div> |
| <div class="truth" id="mission-truth">No mock success. No simulation labels. Every number must be backed by a receipt.</div> |
| </div> |
| |
| <div class="panel"> |
| <h2>Live Metrics</h2> |
| <div id="metrics-panel"><pre>loading...</pre></div> |
| <div class="actions"> |
| <a class="btn" href="/api/funnel/daily">Funnel Daily</a> |
| <a class="btn" href="/api/leads">Leads</a> |
| <a class="btn btn-ok" href="/api/metrics/ingest" onclick="return false">Ingest (POST)</a> |
| </div> |
| </div> |
| |
| <div class="panel"> |
| <h2>Candidate Queue</h2> |
| <div id="candidates-panel"><pre>loading...</pre></div> |
| <div class="actions"> |
| <a class="btn" href="/api/candidates">Refresh</a> |
| <a class="btn" href="/api/bios">Bios</a> |
| </div> |
| </div> |
| |
| <div class="panel"> |
| <h2>Decision Gate</h2> |
| <div id="decision-panel"><pre>loading...</pre></div> |
| <div class="actions"> |
| <a class="btn" href="/api/decision/latest">Latest Decision</a> |
| <a class="btn btn-danger" href="/api/run/availability">Availability (BLOCKED)</a> |
| </div> |
| </div> |
| |
| <div class="panel"> |
| <h2>Job Ledger</h2> |
| <div id="jobs-panel"><pre>loading...</pre></div> |
| <div class="actions"> |
| <a class="btn" href="/api/jobs">All Jobs</a> |
| <a class="btn" href="/api/receipts">Receipts</a> |
| <a class="btn" href="/api/audit/files">File Audit</a> |
| </div> |
| </div> |
| |
| <div class="panel"> |
| <h2>Revenue Proof</h2> |
| <div class="metric-row"><span class="metric-label">Verified revenue</span><span class="metric-value metric-zero">$0</span></div> |
| <div class="metric-row"><span class="metric-label">Confirmed bookings</span><span class="metric-value metric-zero">0</span></div> |
| <div class="metric-row"><span class="metric-label">Target</span><span class="metric-value">1 client/day</span></div> |
| <div class="metric-row"><span class="metric-label">Client probability</span><span class="metric-value metric-zero" id="prob">unverified</span></div> |
| <div class="truth">No estimates pretending to be money. Only confirmed bookings count.</div> |
| </div> |
| |
| </div> |
| |
| <div class="grid"> |
| <div class="panel" style="border-color:#1a3a2a"> |
| <h2 style="color:#39ff88">IMMORTALITY SCORE</h2> |
| <div class="metric-row"><span class="metric-label">Score</span><span class="metric-value" id="imm-score" style="font-size:18px">--</span></div> |
| <div class="metric-row"><span class="metric-label">Grade</span><span class="metric-value" id="imm-grade" style="font-size:16px">--</span></div> |
| <div class="metric-row"><span class="metric-label">Days online</span><span class="metric-value" id="imm-days">--</span></div> |
| <div class="metric-row"><span class="metric-label">Views/day</span><span class="metric-value" id="imm-vpd">--</span></div> |
| <div class="metric-row"><span class="metric-label">Profile age score</span><span class="metric-value" id="imm-age">--</span></div> |
| <div class="metric-row"><span class="metric-label">VPD trend</span><span class="metric-value" id="imm-trend">--</span></div> |
| <div class="metric-row"><span class="metric-label">Visibility persistence</span><span class="metric-value" id="imm-vis">--</span></div> |
| <div class="metric-row"><span class="metric-label">Availability stability</span><span class="metric-value" id="imm-avail">--</span></div> |
| <div class="metric-row"><span class="metric-label">Retention score</span><span class="metric-value" id="imm-retention">--</span></div> |
| <div class="actions"><a class="btn btn-ok" href="/api/kpis">Raw KPI JSON</a></div> |
| </div> |
| |
| <div class="panel" style="border-color:#3a1a1a"> |
| <h2 style="color:#ff5370">VIRALITY SCORE</h2> |
| <div class="metric-row"><span class="metric-label">Score</span><span class="metric-value" id="vir-score" style="font-size:18px">--</span></div> |
| <div class="metric-row"><span class="metric-label">Grade</span><span class="metric-value" id="vir-grade" style="font-size:16px">--</span></div> |
| <div class="metric-row"><span class="metric-label">Profile views</span><span class="metric-value" id="vir-views">--</span></div> |
| <div class="metric-row"><span class="metric-label">Views velocity</span><span class="metric-value" id="vir-velocity">--</span></div> |
| <div class="metric-row"><span class="metric-label">Views acceleration</span><span class="metric-value" id="vir-accel">--</span></div> |
| <div class="metric-row"><span class="metric-label">Contact clicks</span><span class="metric-value" id="vir-clicks">--</span></div> |
| <div class="metric-row"><span class="metric-label">Click velocity</span><span class="metric-value" id="vir-clickvel">--</span></div> |
| <div class="metric-row"><span class="metric-label">New visitor rate</span><span class="metric-value" id="vir-newvisitor">--</span></div> |
| <div class="metric-row"><span class="metric-label">Contact click rate</span><span class="metric-value" id="vir-ctr">--</span></div> |
| <div class="metric-row"><span class="metric-label">Snapshots analyzed</span><span class="metric-value" id="vir-snapshots">--</span></div> |
| <div class="actions"><a class="btn" href="/api/kpis/history">KPI History</a></div> |
| </div> |
| </div> |
| |
| <div class="grid"> |
| <div class="panel" style="min-height:auto"> |
| <h2>Daily Revenue Proof</h2> |
| <div id="daily-proof"> |
| <span class="proof-q">What did the system observe?</span> <span class="proof-a" id="dp-observe">--</span><br> |
| <span class="proof-q">How many prospects existed?</span> <span class="proof-a" id="dp-prospects">--</span><br> |
| <span class="proof-q">How many were qualified?</span> <span class="proof-a" id="dp-qualified">--</span><br> |
| <span class="proof-q">How many clicked?</span> <span class="proof-a" id="dp-clicked">--</span><br> |
| <span class="proof-q">How many messaged?</span> <span class="proof-a" id="dp-messaged">--</span><br> |
| <span class="proof-q">How many appointments?</span> <span class="proof-a" id="dp-appts">--</span><br> |
| <span class="proof-q">How many paid?</span> <span class="proof-a" id="dp-paid">--</span><br> |
| <span class="proof-q">Which experiment was live?</span> <span class="proof-a" id="dp-exp">--</span><br> |
| <span class="proof-q">What won?</span> <span class="proof-a" id="dp-won">--</span><br> |
| <span class="proof-q">What failed?</span> <span class="proof-a" id="dp-failed">--</span><br> |
| <span class="proof-q">Tomorrow next best action?</span> <span class="proof-a" id="dp-next">--</span> |
| </div> |
| </div> |
| </div> |
| |
| <div class="grid"> |
| <div class="panel" style="min-height:auto"> |
| <h2>CI/CD Control</h2> |
| <div class="actions"> |
| <a class="btn" href="/api/cicd/list">List Workflows</a> |
| <a class="btn" href="/api/cicd/runs">Recent Runs</a> |
| <a class="btn btn-ok" href="/api/cicd/trigger/deploy-hf-space.yml">Deploy HF</a> |
| <a class="btn" href="/api/cicd/trigger/master-rotator.yml">Master Rotator</a> |
| </div> |
| <pre id="cicd-panel" style="margin-top:12px">loading...</pre> |
| </div> |
| <div class="panel" style="min-height:auto"> |
| <h2>System State</h2> |
| <pre id="state">loading...</pre> |
| </div> |
| </div> |
| |
| <div class="footer"> |
| RentMasseur RevenueOps Control Plane · No receipt, no reality. No metric, no optimization. No lead, no client claim. |
| </div> |
| |
| <script> |
| function setTag(id, text, cls) { |
| const el = document.getElementById(id); |
| el.textContent = text; |
| el.className = 'tag ' + cls; |
| } |
| |
| const state = document.getElementById('state'); |
| const metricsPanel = document.getElementById('metrics-panel'); |
| const candidatesPanel = document.getElementById('candidates-panel'); |
| const decisionPanel = document.getElementById('decision-panel'); |
| const jobsPanel = document.getElementById('jobs-panel'); |
| const cicdPanel = document.getElementById('cicd-panel'); |
| |
| fetch('/api/health').then(r=>r.json()).then(j=>{ |
| setTag('tag-health', 'HEALTH: ' + (j.status||'?'), j.status==='GREEN_REAL' ? 'tag-green' : 'tag-red'); |
| }).catch(()=>setTag('tag-health','HEALTH: OFFLINE','tag-red')); |
| |
| fetch('/api/report').then(r=>r.json()).then(j=>{ |
| state.textContent = JSON.stringify(j, null, 2); |
| const hasMetrics = j.status === 'real_data_present'; |
| setTag('tag-metrics', 'METRICS: ' + (hasMetrics ? 'REAL' : 'NO_DATA'), hasMetrics ? 'tag-green' : 'tag-gray'); |
| const hasBios = (j.content_counts?.bios || 0) > 0; |
| setTag('tag-candidates', 'CANDIDATES: ' + (hasBios ? j.content_counts.bios : 'NONE'), hasBios ? 'tag-green' : 'tag-gray'); |
| setTag('tag-decision', 'DECISION: ' + (j.latest_decision?.status || 'NONE'), j.latest_decision?.status === 'accepted' ? 'tag-green' : 'tag-gray'); |
| document.getElementById('mission-status').textContent = j.status || 'unknown'; |
| document.getElementById('mission-status').className = 'metric-value' + (j.status?.includes('blocked') ? ' metric-zero' : ''); |
| }).catch(e=>state.textContent=String(e)); |
| |
| fetch('/api/funnel/daily').then(r=>r.json()).then(j=>{ |
| metricsPanel.innerHTML = ''; |
| const rows = [ |
| ['Metric entries', j.metric_entries||0], |
| ['Profile views', j.profile_views||0], |
| ['Contact clicks', j.contact_clicks||0], |
| ['Email clicks', j.email_clicks||0], |
| ['Phone clicks', j.phone_clicks||0], |
| ['Booking requests', j.booking_requests||0], |
| ['Confirmed bookings', j.confirmed_bookings||0], |
| ]; |
| rows.forEach(([k,v])=>{ |
| const d=document.createElement('div'); |
| d.className='metric-row'; |
| d.innerHTML='<span class="metric-label">'+k+'</span><span class="metric-value'+(v===0?' metric-zero':'')+'">'+v+'</span>'; |
| metricsPanel.appendChild(d); |
| }); |
| if((j.metric_entries||0)===0){ |
| const t=document.createElement('div'); |
| t.className='truth'; |
| t.textContent='NO REAL METRICS. Funnel requires first-party data from extension or manual capture.'; |
| metricsPanel.appendChild(t); |
| } |
| document.getElementById('prob').textContent = j.client_probability || 'unverified'; |
| }).catch(e=>metricsPanel.innerHTML='<pre>'+String(e)+'</pre>'); |
| |
| fetch('/api/candidates').then(r=>r.json()).then(j=>{ |
| candidatesPanel.innerHTML='<pre>'+JSON.stringify(j,null,2)+'</pre>'; |
| }).catch(e=>candidatesPanel.innerHTML='<pre>'+String(e)+'</pre>'); |
| |
| fetch('/api/decision/latest').then(r=>r.json()).then(j=>{ |
| decisionPanel.innerHTML='<pre>'+JSON.stringify(j,null,2)+'</pre>'; |
| }).catch(e=>decisionPanel.innerHTML='<pre>'+String(e)+'</pre>'); |
| |
| fetch('/api/jobs').then(r=>r.json()).then(j=>{ |
| const count = j.jobs?.length || 0; |
| jobsPanel.innerHTML='<pre>Jobs: '+count+'\n'+JSON.stringify(j.jobs?.slice(-3),null,2)+'</pre>'; |
| }).catch(e=>jobsPanel.innerHTML='<pre>'+String(e)+'</pre>'); |
| |
| fetch('/api/cicd/runs').then(r=>r.json()).then(j=>{ |
| const runs = j.workflow_runs||[]; |
| cicdPanel.textContent = runs.slice(0,5).map(r=>r.name+': '+(r.conclusion||r.status)+' ('+r.created_at+')').join('\n'); |
| }).catch(e=>cicdPanel.textContent=String(e)); |
| |
| // War-grade KPI fetch |
| fetch('/api/kpis').then(r=>r.json()).then(j=>{ |
| const imm = j.immortality||{}; |
| const vir = j.virality||{}; |
| const ic = imm.components||{}; |
| const vc = vir.components||{}; |
| |
| // Immortality tags + panel |
| const immGrade = imm.grade||'NO_DATA'; |
| const immCls = immGrade==='IMMORTAL'?'tag-green':immGrade==='RESILIENT'?'tag-green':immGrade==='STABLE'?'tag-yellow':immGrade==='FRAGILE'?'tag-red':'tag-gray'; |
| setTag('tag-immortality','IMMORTALITY: '+(imm.score||0).toFixed(2)+' '+immGrade, immCls); |
| |
| document.getElementById('imm-score').textContent = (imm.score||0).toFixed(4); |
| document.getElementById('imm-grade').textContent = immGrade; |
| document.getElementById('imm-days').textContent = ic.days_online||0; |
| document.getElementById('imm-vpd').textContent = (ic.views_per_day||0).toFixed(1); |
| document.getElementById('imm-age').textContent = (ic.profile_age_score||0).toFixed(4); |
| document.getElementById('imm-trend').textContent = (ic.views_per_day_trend||0).toFixed(4); |
| document.getElementById('imm-vis').textContent = (ic.visibility_persistence||0).toFixed(4); |
| document.getElementById('imm-avail').textContent = (ic.availability_stability||0).toFixed(4); |
| document.getElementById('imm-retention').textContent = (ic.retention_score||0).toFixed(4); |
| |
| // Virality tags + panel |
| const virGrade = vir.grade||'NO_DATA'; |
| const virCls = virGrade==='VIRAL'?'tag-green':virGrade==='ACCELERATING'?'tag-green':virGrade==='STEADY'?'tag-yellow':virGrade==='SLOW'?'tag-red':'tag-gray'; |
| setTag('tag-virality','VIRALITY: '+(vir.score||0).toFixed(2)+' '+virGrade, virCls); |
| |
| document.getElementById('vir-score').textContent = (vir.score||0).toFixed(4); |
| document.getElementById('vir-grade').textContent = virGrade; |
| document.getElementById('vir-views').textContent = vc.profile_views||0; |
| document.getElementById('vir-velocity').textContent = vc.views_velocity||0; |
| document.getElementById('vir-accel').textContent = (vc.views_acceleration||0).toFixed(4); |
| document.getElementById('vir-clicks').textContent = vc.contact_clicks||0; |
| document.getElementById('vir-clickvel').textContent = vc.contact_click_velocity||0; |
| document.getElementById('vir-newvisitor').textContent = ((vc.new_visitor_rate||0)*100).toFixed(2)+'%'; |
| document.getElementById('vir-ctr').textContent = ((vc.contact_click_rate||0)*100).toFixed(2)+'%'; |
| document.getElementById('vir-snapshots').textContent = j.snapshots_analyzed||0; |
| }).catch(e=>{ |
| setTag('tag-immortality','IMMORTALITY: OFFLINE','tag-red'); |
| setTag('tag-virality','VIRALITY: OFFLINE','tag-red'); |
| }); |
| </script> |
| </body></html>)HTML"; |
| } |
| static void handle_client(int client_socket) { |
| char buffer[16384]; |
| int received = recv(client_socket, buffer, sizeof(buffer) - 1, 0); |
| if (received <= 0) { close(client_socket); return; } |
| buffer[received] = '\0'; |
| std::string request(buffer); |
| std::string method = get_method(request); |
| std::string path = get_path(request); |
| std::string body = get_body(request); |
| std::string response; |
| std::string content_type = "application/json"; |
| int code = 200; |
|
|
| ensure_dir(CONTENT_DIR); |
| ensure_dir(RECEIPTS_DIR); |
|
|
| std::string auth_err = check_admin(request, path, method); |
| if (!auth_err.empty()) { |
| code = 403; |
| response = auth_err; |
| std::string w = http_response(code, content_type, response); |
| send(client_socket, w.c_str(), w.size(), 0); |
| close(client_socket); |
| return; |
| } |
|
|
| if (method == "OPTIONS") { |
| response = "{}"; |
| } else if (path == "/" || path == "/index.html") { |
| response = landing_page(); content_type = "text/html"; |
| } else if (path == "/health" || path == "/api/health") { |
| response = "{\"status\":\"GREEN_REAL\",\"service\":\"rentmasseur-cpp-os\",\"mode\":\"evidence_only\",\"grade\":\"MILITARY\",\"timestamp\":\"" + iso_timestamp() + "\"}"; |
| } else if (path == "/api/report") { |
| int bios_count = count_files(CONTENT_DIR + "/bios"); |
| bool metrics = file_exists(CONTENT_DIR + "/live_metrics.json") || file_exists(CONTENT_DIR + "/metrics_ingest.jsonl"); |
| std::ostringstream ss; |
| ss << "{\"status\":\"" << (metrics ? "real_data_present" : "blocked_missing_metrics") << "\","; |
| ss << "\"rl_state\":" << read_json_or_block(CONTENT_DIR + "/rl_state.json", "rl_state") << ","; |
| ss << "\"ga_state\":" << read_json_or_block(CONTENT_DIR + "/ga_rl_state.json", "ga_state") << ","; |
| ss << "\"availability\":" << read_json_or_block(AVAILABILITY_FILE, "availability") << ","; |
| ss << "\"content_counts\":{\"bios\":" << bios_count << ",\"receipts\":" << count_files(RECEIPTS_DIR) << "},"; |
| ss << "\"live_profile_update_allowed\":false,"; |
| ss << "\"timestamp\":\"" << iso_timestamp() << "\"}"; |
| response = ss.str(); |
| } else if (path == "/api/bios") { |
| response = bios_json(); |
| } else if (path == "/api/competitors") { |
| response = read_json_or_block(CONTENT_DIR + "/competitor_bios.json", "competitor_bios"); |
| } else if (path == "/api/jobs") { |
| std::ostringstream ss; |
| ss << "{\"status\":\"ok\",\"jobs\":["; |
| DIR* d = opendir(RECEIPTS_DIR.c_str()); |
| if (d) { |
| struct dirent* entry; |
| bool first = true; |
| while ((entry = readdir(d)) != nullptr) { |
| if (entry->d_type != DT_REG) continue; |
| std::string fname = entry->d_name; |
| if (!first) ss << ","; |
| first = false; |
| std::string fcontent = read_file(RECEIPTS_DIR + "/" + fname); |
| ss << fcontent; |
| } |
| closedir(d); |
| } |
| ss << "]}"; |
| response = ss.str(); |
| } else if (path.rfind("/api/jobs/", 0) == 0) { |
| std::string job_id = path.substr(10); |
| std::string job_path = RECEIPTS_DIR + "/" + job_id; |
| std::string fcontent = read_file(job_path); |
| if (fcontent.empty()) { |
| code = 404; |
| response = "{\"status\":\"failed\",\"reason\":\"job not found\",\"job_id\":\"" + json_escape(job_id) + "\"}"; |
| } else { |
| response = fcontent; |
| } |
| } else if (path == "/api/audit/files") { |
| std::ostringstream ss; |
| ss << "{\"status\":\"ok\",\"files\":["; |
| const char* files_to_audit[] = { |
| "cpp_os_server.cpp", "rotator_engine.cpp", "ga_rl_optimizer.cpp", |
| "production_control_loop.cpp", "Dockerfile", "requirements.txt", |
| "orchestrator.py", "content_generator.py", "rl_feedback.py", |
| "interview_rotator.py", "blog_rotator.py", "metrics_collector.py", |
| "rentmasseur_availability.py", "hf_app.py", nullptr |
| }; |
| bool first = true; |
| for (int i = 0; files_to_audit[i]; i++) { |
| std::string fname = files_to_audit[i]; |
| bool exists = file_exists(fname); |
| std::string state; |
| if (!exists) state = "dead"; |
| else if (fname.find(".cpp") != std::string::npos) state = "compiled"; |
| else if (fname.find(".py") != std::string::npos) state = "imported"; |
| else state = "present"; |
| bool has_receipt = file_exists(std::string(RECEIPTS_DIR + "/hf_action_") + fname); |
| if (!first) ss << ","; |
| first = false; |
| ss << "{\"file\":\"" << json_escape(fname) << "\",\"state\":\"" << state << "\",\"exists\":" << (exists ? "true" : "false") << ",\"has_receipt\":" << (has_receipt ? "true" : "false") << "}"; |
| } |
| ss << "]}"; |
| response = ss.str(); |
| } else if (path == "/api/receipts") { |
| response = "{\"status\":\"ok\",\"receipt_count\":" + std::to_string(count_files(RECEIPTS_DIR)) + "}"; |
| } else if (path == "/api/ingest" && method == "POST") { |
| std::string ingest_path = CONTENT_DIR + "/metrics_ingest.jsonl"; |
| std::ofstream f(ingest_path, std::ios::app); |
| if (!f) { code = 500; response = "{\"status\":\"failed\",\"reason\":\"could not open metrics_ingest.jsonl\"}"; } |
| else { |
| f << "{\"timestamp\":\"" << iso_timestamp() << "\",\"body\":\"" << json_escape(body) << "\"}\n"; |
| std::string receipt = write_receipt("ingest", "success", 0, "metrics accepted", "\"output_file\": \"" + ingest_path + "\""); |
| response = "{\"status\":\"success\",\"output_file\":\"" + ingest_path + "\",\"receipt\":\"" + receipt + "\"}"; |
| } |
| } else if (path == "/api/run/ga-rl") { |
| response = action_response("ga-rl", "./ga_rl_optimizer --population 12 --generations 5 --target 300 && ./ga_rl_optimizer --apply-winner"); |
| } else if (path == "/api/run/orchestrator") { |
| response = action_response("orchestrator", "python3 orchestrator.py --all --dry-run"); |
| } else if (path == "/api/run/availability") { |
| code = 403; |
| response = blocked_response("availability", "Live login automation is disabled because prior runs hit captcha/anti-bot. Use first-party metrics ingestion or a manually approved platform path."); |
| } else if (path.rfind("/api/rotate/", 0) == 0) { |
| std::string rotate_type = path.substr(12); |
| if (!(rotate_type == "bio" || rotate_type == "photo" || rotate_type == "price" || rotate_type == "interview" || rotate_type == "blog")) { |
| code = 404; response = "{\"status\":\"failed\",\"reason\":\"unknown rotate type\"}"; |
| } else { |
| response = action_response("rotate_" + rotate_type, "./rotator_engine --rotate " + rotate_type); |
| } |
| } else if (path == "/api/rotator/report") { |
| response = action_response("rotator_report", "./rotator_engine --report"); |
| } else if (path == "/api/cicd/list") { |
| response = gh_api("GET", "actions/workflows"); |
| } else if (path == "/api/cicd/runs") { |
| { |
| std::string cmd = "curl -sS -X GET -H \"Authorization: Bearer " + GH_TOKEN + "\" -H \"Accept: application/vnd.github+json\" -H \"X-GitHub-Api-Version: 2022-11-28\" \"https://api.github.com/repos/" + GH_REPO + "/actions/runs?per_page=5\" | python3 -c \"import sys,json; d=json.load(sys.stdin); print(json.dumps({'workflow_runs':[{'name':r['name'],'status':r['status'],'conclusion':r.get('conclusion'),'created_at':r['created_at']} for r in d.get('workflow_runs',[])]}))\""; |
| CommandResult r = run_command_evidence(cmd); |
| response = r.output.empty() ? "{\"status\":\"error\",\"exit_code\":" + std::to_string(r.exit_code) + "}" : r.output; |
| } |
| } else if (path.rfind("/api/cicd/trigger/", 0) == 0) { |
| std::string wf = path.substr(18); |
| std::string raw = gh_api("POST", "actions/workflows/" + url_encode_workflow(wf) + "/dispatches", "{\"ref\":\"main\"}"); |
| std::string receipt = write_receipt("cicd_trigger_" + wf, "dispatched", 0, raw); |
| response = "{\"status\":\"dispatched\",\"workflow\":\"" + json_escape(wf) + "\",\"github_response\":" + raw + ",\"receipt\":\"" + json_escape(receipt) + "\"}"; |
| } else if (path.rfind("/api/cicd/status/", 0) == 0) { |
| response = gh_api("GET", "actions/runs/" + path.substr(16)); |
| } else if (path == "/api/funnel/daily") { |
| std::string metrics = read_file(CONTENT_DIR + "/metrics_ingest.jsonl"); |
| int metric_count = 0; |
| long total_views = 0, total_clicks = 0, total_emails = 0, total_visits = 0; |
| if (!metrics.empty()) { |
| std::istringstream ms(metrics); |
| std::string line; |
| while (std::getline(ms, line)) { |
| if (line.empty()) continue; |
| metric_count++; |
| size_t vp = line.find("\"profile_views\""); |
| if (vp != std::string::npos) { size_t vn = line.find(":", vp); if (vn != std::string::npos) total_views += std::atol(line.c_str() + vn + 1); } |
| size_t cp = line.find("\"contact_clicks\""); |
| if (cp != std::string::npos) { size_t cn = line.find(":", cp); if (cn != std::string::npos) total_clicks += std::atol(line.c_str() + cn + 1); } |
| size_t ep = line.find("\"new_emails\""); |
| if (ep != std::string::npos) { size_t en = line.find(":", ep); if (en != std::string::npos) total_emails += std::atol(line.c_str() + en + 1); } |
| size_t np = line.find("\"new_visits\""); |
| if (np != std::string::npos) { size_t nn = line.find(":", np); if (nn != std::string::npos) total_visits += std::atol(line.c_str() + nn + 1); } |
| } |
| } |
| double ctr = total_views > 0 ? (double)total_clicks / total_views : 0.0; |
| char ctr_buf[16]; std::snprintf(ctr_buf, sizeof(ctr_buf), "%.4f", ctr); |
| const char* status = metric_count == 0 ? "gray_no_data" : (total_views < 100 ? "no_signal" : "real_data"); |
| std::ostringstream ss; |
| ss << "{\"status\":\"" << status << "\",\"metric_entries\":" << metric_count |
| << ",\"profile_views\":" << total_views |
| << ",\"contact_clicks\":" << total_clicks |
| << ",\"new_visits\":" << total_visits |
| << ",\"new_emails\":" << total_emails |
| << ",\"contact_click_rate\":" << ctr_buf |
| << ",\"email_clicks\":0,\"phone_clicks\":0,\"booking_requests\":0,\"confirmed_bookings\":0,\"gross_revenue\":0" |
| << ",\"client_target\":1,\"client_probability\":\"unverified_no_real_metrics\"" |
| << ",\"note\":\"Funnel requires first-party metrics from extension or manual dashboard capture\"" |
| << ",\"timestamp\":\"" << iso_timestamp() << "\"}"; |
| response = ss.str(); |
| } else if (path == "/api/leads") { |
| std::string leads = read_file(CONTENT_DIR + "/leads.jsonl"); |
| int lead_count = 0; |
| if (!leads.empty()) { |
| for (size_t i = 0; i < leads.size(); i++) if (leads[i] == '\n') lead_count++; |
| } |
| response = "{\"status\":\"" + std::string(lead_count > 0 ? "ok" : "gray_no_data") + "\",\"lead_count\":" + std::to_string(lead_count) + ",\"note\":\"Leads are tracked from first-party contact events only\"}"; |
| } else if (path == "/api/kpis") { |
| response = kpi_response(); |
| } else if (path == "/api/kpis/history") { |
| response = read_file(KPI_PATH); |
| if (response.empty()) { response = "{\"status\":\"no_kpi_history\"}"; } |
| } else if (path == "/api/decision/latest") { |
| std::string decision = read_file(CONTENT_DIR + "/decisions/latest_decision.json"); |
| if (decision.empty()) { |
| response = "{\"status\":\"gray_no_data\",\"reason\":\"no production gate decision has been made yet\",\"note\":\"Run master-rotator with production_control_loop --evaluate to generate a decision\"}"; |
| } else { |
| response = decision; |
| } |
| } else if (path == "/api/candidates") { |
| std::ostringstream ss; |
| ss << "{\"status\":\"ok\",\"candidates\":{"; |
| const char* types[] = {"bios", "interviews", "blogs", "photos", "prices", nullptr}; |
| bool first = true; |
| for (int i = 0; types[i]; i++) { |
| if (!first) ss << ","; |
| first = false; |
| ss << "\"" << types[i] << "\":" << count_files(CONTENT_DIR + "/" + types[i]); |
| } |
| ss << "}}"; |
| response = ss.str(); |
| } else if (path == "/api/metrics/ingest" && method == "POST") { |
| std::string lower_body = body; |
| for (char& c : lower_body) c = (char)tolower(c); |
| const char* secret_keys[] = {"cookie", "cookies", "token", "accesstoken", "refreshtoken", "authorization", "password", "session", "bearer", nullptr}; |
| bool has_secret = false; |
| std::string secret_found = ""; |
| for (int i = 0; secret_keys[i]; i++) { |
| if (lower_body.find(secret_keys[i]) != std::string::npos) { |
| has_secret = true; |
| secret_found = secret_keys[i]; |
| break; |
| } |
| } |
| if (has_secret) { |
| code = 400; |
| std::string receipt = write_receipt("metrics_ingest_rejected", "rejected", 0, "payload contains secret-bearing key", "\"rejected_key\": \"" + secret_found + "\""); |
| response = "{\"status\":\"rejected\",\"reason\":\"payload contains secret-bearing field\",\"field\":\"" + secret_found + "\",\"receipt\":\"" + json_escape(receipt) + "\"}"; |
| } else { |
| std::string ingest_path = CONTENT_DIR + "/metrics_ingest.jsonl"; |
| std::ofstream f(ingest_path, std::ios::app); |
| if (!f) { code = 500; response = "{\"status\":\"failed\",\"reason\":\"could not write metrics file\"}"; } |
| else { |
| f << body << "\n"; |
| f.close(); |
| std::string latest_path = CONTENT_DIR + "/metrics/latest_metrics.json"; |
| ensure_dir(CONTENT_DIR + "/metrics"); |
| std::ofstream lf(latest_path); |
| if (lf) { lf << body; lf.close(); } |
| std::string receipt = write_receipt("metrics_ingest", "success", 0, "first-party metrics accepted", |
| "\"output_file\": \"" + ingest_path + "\", \"latest_file\": \"" + latest_path + "\""); |
| response = "{\"status\":\"success\",\"output_file\":\"" + ingest_path + "\",\"latest_file\":\"" + latest_path + "\",\"receipt\":\"" + json_escape(receipt) + "\",\"note\":\"first-party metrics only, no automated login\"}"; |
| } |
| } |
| } else if (path == "/api/config" && method == "POST") { |
| std::string config_path = CONTENT_DIR + "/system_config.json"; |
| std::ofstream f(config_path); |
| if (!f) { code = 500; response = "{\"status\":\"failed\",\"reason\":\"could not write config\"}"; } |
| else { |
| f << body; |
| std::string receipt = write_receipt("config", "success", 0, "config saved", "\"output_file\": \"" + config_path + "\""); |
| response = "{\"status\":\"success\",\"output_file\":\"" + config_path + "\",\"receipt\":\"" + receipt + "\"}"; |
| } |
| } else { |
| code = 404; |
| response = "{\"status\":\"failed\",\"error\":\"not found\"}"; |
| } |
|
|
| std::string wire = http_response(code, content_type, response); |
| send(client_socket, wire.c_str(), wire.size(), 0); |
| close(client_socket); |
| } |
|
|
| static int start_server(int port) { |
| int server_fd = socket(AF_INET, SOCK_STREAM, 0); |
| if (server_fd < 0) { std::cerr << "Socket creation failed\n"; return 1; } |
| int opt = 1; |
| setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); |
| sockaddr_in address{}; |
| address.sin_family = AF_INET; |
| address.sin_addr.s_addr = INADDR_ANY; |
| address.sin_port = htons(port); |
| if (bind(server_fd, (sockaddr*)&address, sizeof(address)) < 0) { std::cerr << "Bind failed\n"; close(server_fd); return 1; } |
| if (listen(server_fd, 10) < 0) { std::cerr << "Listen failed\n"; close(server_fd); return 1; } |
| std::cout << "RentMasseur C++ OS listening on port " << port << std::endl; |
| while (true) { |
| sockaddr_in client_addr{}; |
| socklen_t addr_len = sizeof(client_addr); |
| int client_socket = accept(server_fd, (sockaddr*)&client_addr, &addr_len); |
| if (client_socket < 0) continue; |
| handle_client(client_socket); |
| } |
| close(server_fd); |
| return 0; |
| } |
|
|
| int main(int argc, char* argv[]) { |
| int port = PORT; |
| if (argc > 1) port = std::atoi(argv[1]); |
| std::cout << "Starting RentMasseur C++ OS evidence-only mode on port " << port << std::endl; |
| return start_server(port); |
| } |
|
|