Files
weval-consulting/api/blade-api.php

146 lines
5.6 KiB
PHP

<?php
// === WEVAL SECRETS LOADER ===
$_WEVAL_SECRETS = [];
if (file_exists('/etc/weval/secrets.env')) {
foreach (file('/etc/weval/secrets.env', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
if (strpos($line, '#') === 0) continue;
if (strpos($line, '=') !== false) {
list($k, $v) = explode('=', $line, 2);
$_WEVAL_SECRETS[trim($k)] = trim($v);
}
}
}
function weval_secret($key, $default='') {
global $_WEVAL_SECRETS;
return $_WEVAL_SECRETS[$key] ?? getenv($key) ?: $default;
}
// === INPUT SANITIZATION ===
function weval_input($key, $type='string', $method='GET') {
$src = $method === 'POST' ? INPUT_POST : INPUT_GET;
$val = filter_input($src, $key, FILTER_SANITIZE_FULL_SPECIAL_CHARS);
if ($val === null || $val === false) {
$val = ($method === 'POST') ? ($_POST[$key] ?? '') : ($_GET[$key] ?? '');
$val = htmlspecialchars(strip_tags(trim($val)), ENT_QUOTES, 'UTF-8');
}
if ($type === 'int') return intval($val);
if ($type === 'email') return filter_var($val, FILTER_SANITIZE_EMAIL);
return $val;
}
header("Content-Type: application/json");
$KEY = weval_secret('BLADE_KEY','BLADE2026');
$TASKS_DIR = "/var/www/html/api/blade-tasks";
$HEARTBEAT = "/var/www/html/api/blade-tasks/heartbeat.json";
if (!is_dir($TASKS_DIR)) mkdir($TASKS_DIR, 0775, true);
$k = $_REQUEST["k"] ?? "";
$action = $_REQUEST["action"] ?? "status";
// Public status (no auth needed)
if ($action === "status") {
$hb = file_exists($HEARTBEAT) ? json_decode(file_get_contents($HEARTBEAT), true) : null;
$tasks = glob($TASKS_DIR . "/task_*.json");
$pending = $done = $failed = 0;
foreach ($tasks as $t) {
$d = json_decode(file_get_contents($t), true);
if ($d["status"] === "pending") $pending++;
elseif ($d["status"] === "done") $done++;
elseif ($d["status"] === "failed") $failed++;
}
$online = $hb && (time() - strtotime($hb["ts"] ?? "2000-01-01")) < 120;
echo json_encode(["ok" => true, "blade" => [
"online" => $online,
"heartbeat" => $hb,
"tasks" => ["pending" => $pending, "done" => $done, "failed" => $failed, "total" => count($tasks)]
]]);
exit;
}
// Auth required for all other actions
if ($k !== $KEY) { echo json_encode(["error" => "auth"]); exit; }
switch ($action) {
case "push":
$type = $_REQUEST["type"] ?? "powershell";
$cmd = $_REQUEST["cmd"] ?? "";
$label = $_REQUEST["label"] ?? "task";
$priority = intval($_REQUEST["priority"] ?? 5);
if (!$cmd) { echo json_encode(["error" => "no cmd"]); exit; }
$id = "task_" . date("Ymd_His") . "_" . substr(md5(uniqid()), 0, 6);
$task = [
"id" => $id, "type" => $type, "cmd" => $cmd, "label" => $label,
"priority" => $priority, "status" => "pending",
"created" => date("c"), "started" => null, "completed" => null,
"result" => null, "error" => null, "source" => $_REQUEST["source"] ?? "api"
];
file_put_contents($TASKS_DIR . "/" . $id . ".json", json_encode($task, JSON_PRETTY_PRINT));
echo json_encode(["ok" => true, "task" => $task]);
break;
case "poll":
// Blade polls for pending tasks
$tasks = glob($TASKS_DIR . "/task_*.json");
$pending = [];
foreach ($tasks as $t) {
$d = json_decode(file_get_contents($t), true);
if ($d["status"] === "pending") $pending[] = $d;
}
usort($pending, fn($a, $b) => $b["priority"] - $a["priority"]);
echo json_encode(["ok" => true, "tasks" => array_slice($pending, 0, 5)]);
break;
case "report":
$id = $_REQUEST["id"] ?? "";
$f = $TASKS_DIR . "/" . $id . ".json";
if (!file_exists($f)) { echo json_encode(["error" => "not found"]); exit; }
$task = json_decode(file_get_contents($f), true);
$task["status"] = $_REQUEST["status"] ?? "done";
$task["result"] = $_REQUEST["result"] ?? "";
$task["error"] = $_REQUEST["error"] ?? null;
$task["completed"] = date("c");
file_put_contents($f, json_encode($task, JSON_PRETTY_PRINT));
echo json_encode(["ok" => true, "task" => $task]);
break;
case "heartbeat":
$data = [
"ts" => date("c"),
"hostname" => $_REQUEST["hostname"] ?? "blade",
"cpu" => $_REQUEST["cpu"] ?? "?",
"ram" => $_REQUEST["ram"] ?? "?",
"disk" => $_REQUEST["disk"] ?? "?",
"uptime" => $_REQUEST["uptime"] ?? "?",
"user" => $_REQUEST["user"] ?? "?",
"ip" => $_SERVER["REMOTE_ADDR"],
"agent_version" => $_REQUEST["version"] ?? "2.0"
];
file_put_contents($HEARTBEAT, json_encode($data, JSON_PRETTY_PRINT));
echo json_encode(["ok" => true]);
break;
case "list":
$tasks = glob($TASKS_DIR . "/task_*.json");
$all = [];
foreach (array_slice(array_reverse($tasks), 0, 50) as $t) {
$all[] = json_decode(file_get_contents($t), true);
}
echo json_encode(["ok" => true, "tasks" => $all]);
break;
case "clear":
$tasks = glob($TASKS_DIR . "/task_*.json");
$cleared = 0;
foreach ($tasks as $t) {
$d = json_decode(file_get_contents($t), true);
if ($d["status"] !== "pending") { unlink($t); $cleared++; }
}
echo json_encode(["ok" => true, "cleared" => $cleared]);
break;
default:
echo json_encode(["error" => "unknown action", "actions" => ["status","push","poll","report","heartbeat","list","clear"]]);
}