false, 'error' => $msg]); exit; } if ($_SERVER['REQUEST_METHOD'] !== 'POST') { fail(405, 'POST only'); } $cfg = require __DIR__ . '/config.php'; $raw = file_get_contents('php://input'); if ($raw === false || strlen($raw) > 512 * 1024) { fail(400, 'bad body'); } $msg = json_decode($raw, true); if (!is_array($msg)) { fail(400, 'invalid JSON'); } $token = (string)($msg['token'] ?? ''); if ($token === '' || !hash_equals((string)$cfg['ingest_token'], $token)) { fail(403, 'bad token'); } $symbol = (string)($msg['symbol'] ?? ''); if ($symbol === '' || !in_array($symbol, $cfg['symbols'], true)) { fail(400, 'unknown symbol'); } try { $pdo = new PDO($cfg['db_dsn'], $cfg['db_user'], $cfg['db_pass'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, ]); } catch (PDOException $e) { fail(500, 'db unavailable'); } $type = (string)($msg['type'] ?? ''); try { if ($type === 'tick') { $price = filter_var($msg['price'] ?? null, FILTER_VALIDATE_FLOAT); if ($price === false || $price === null) { fail(400, 'bad price'); } $prev = filter_var($msg['prev'] ?? null, FILTER_VALIDATE_FLOAT); $tickAt = (string)($msg['tick_at'] ?? gmdate('Y-m-d H:i:s')); $st = $pdo->prepare( 'INSERT INTO live_price (symbol, price, prev_price, tick_at) VALUES (:s, :p, :pp, :t) ON DUPLICATE KEY UPDATE prev_price = VALUES(prev_price), price = VALUES(price), tick_at = VALUES(tick_at)' ); $st->execute([ ':s' => $symbol, ':p' => $price, ':pp' => ($prev === false || $prev === null) ? null : $prev, ':t' => substr($tickAt, 0, 19), ]); echo json_encode(['ok' => true]); exit; } if ($type === 'report') { $tf = (string)($msg['timeframe'] ?? ''); $barTs = substr((string)($msg['bar_ts'] ?? ''), 0, 19); $signal = (string)($msg['signal'] ?? ''); $quality = (string)($msg['quality'] ?? 'F'); $score = filter_var($msg['score'] ?? null, FILTER_VALIDATE_FLOAT); $conf = filter_var($msg['confidence'] ?? null, FILTER_VALIDATE_FLOAT); $payload = $msg['payload'] ?? null; if ($tf === '' || strlen($tf) > 8 || $barTs === '' || $signal === '' || $score === false || $score === null || $conf === false || $conf === null || !is_array($payload)) { fail(400, 'bad report fields'); } $st = $pdo->prepare( 'INSERT INTO signal_reports (symbol, timeframe, bar_ts, signal_name, quality, score, confidence, payload) VALUES (:s, :tf, :bt, :sig, :q, :sc, :c, :pl) ON DUPLICATE KEY UPDATE signal_name = VALUES(signal_name), quality = VALUES(quality), score = VALUES(score), confidence = VALUES(confidence), payload = VALUES(payload)' ); $st->execute([ ':s' => $symbol, ':tf' => $tf, ':bt' => $barTs, ':sig' => substr($signal, 0, 12), ':q' => substr($quality, 0, 2), ':sc' => $score, ':c' => $conf, ':pl' => json_encode($payload, JSON_UNESCAPED_UNICODE), ]); echo json_encode(['ok' => true]); exit; } fail(400, 'unknown type'); } catch (PDOException $e) { error_log('ingest.php: ' . $e->getMessage()); fail(500, 'db error'); }