mirror of
https://github.com/myronblair/jarvis
synced 2026-06-30 17:50:23 -05:00
9f92e4d5e4
- Mobile UI: 3-button bottom nav with panel switcher - Chat history search: search modal with keyword query - News filtering: category filter with localStorage persistence - Proactive reminders: planner/appointment alerts at login and every 5 min - Proactive alerts: polls every 60s, speaks new critical/warning alerts - Agent sparklines: 2h CPU+MEM sparkline on each online agent card - Tier source badge: KB/GROQ/CLAUDE/OLLAMA pill shown after each reply - VM suggestions: 24h resource analysis via voice command - HA scene control: fuzzy-match scene activation via voice - Jellyfin control: pause/stop/next/previous via voice and KB - Pattern suggestions: usage_patterns table + proactive chips every 30 min - Multi-step commands: compound "X and Y" command parsing (Tier 0.5) - Arc Reactor health: warning=amber/1.2s, critical=red/0.6s pulse encoding - Cross-session history: last 6 turns loaded from prior session - Restart agent: voice command to restart any JARVIS agent - New endpoints: history.php, metrics.php, suggestions.php, jellyfin.php Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
108 lines
4.0 KiB
PHP
108 lines
4.0 KiB
PHP
<?php
|
|
/**
|
|
* JARVIS API Router — fault-isolated per endpoint
|
|
* A ParseError or fatal in any endpoint file returns JSON 500 for that
|
|
* endpoint only; all other endpoints continue to work normally.
|
|
*/
|
|
require_once __DIR__ . '/../api/config.php';
|
|
require_once __DIR__ . '/../api/lib/db.php';
|
|
require_once __DIR__ . '/../api/lib/kb_engine.php';
|
|
|
|
session_start();
|
|
|
|
header('Content-Type: application/json');
|
|
header('Access-Control-Allow-Origin: *');
|
|
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
|
header('Access-Control-Allow-Headers: Content-Type, X-Session-Token');
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(204); exit; }
|
|
|
|
$uri = $_SERVER['REQUEST_URI'] ?? '/';
|
|
$method = $_SERVER['REQUEST_METHOD'];
|
|
$path = trim(parse_url($uri, PHP_URL_PATH), '/');
|
|
$parts = explode('/', $path);
|
|
|
|
if (($parts[0] ?? '') === 'api') array_shift($parts);
|
|
$endpoint = $parts[0] ?? '';
|
|
$action = $parts[1] ?? '';
|
|
|
|
// ── Auth check (skip for auth / agent / netscan) ──────────────────────
|
|
if (!\in_array($endpoint, ['auth', 'agent', 'netscan'], true)) {
|
|
$token = $_SESSION['jarvis_token'] ?? ($_SERVER['HTTP_X_SESSION_TOKEN'] ?? '');
|
|
$isValid = !empty($token) && $token === ($_SESSION['jarvis_token'] ?? '');
|
|
if (!$isValid) {
|
|
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
|
|
$isLocal = \in_array($ip, ['127.0.0.1', '::1', JARVIS_IP], true);
|
|
if (!$isLocal && $endpoint !== 'ping') {
|
|
http_response_code(401);
|
|
echo json_encode(['error' => 'Unauthorized', 'code' => 401]);
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($endpoint !== 'auth') session_write_close();
|
|
|
|
$body = file_get_contents('php://input');
|
|
$data = json_decode($body, true) ?? [];
|
|
|
|
// ── Fast ping (no file dispatch needed) ──────────────────────────────
|
|
if ($endpoint === 'ping') {
|
|
echo json_encode(['status' => 'online', 'time' => date('c'), 'codename' => JARVIS_CODENAME]);
|
|
exit;
|
|
}
|
|
|
|
// ── Endpoint → file map ───────────────────────────────────────────────
|
|
$endpoints = [
|
|
'auth' => 'auth.php',
|
|
'chat' => 'chat.php',
|
|
'system' => 'system.php',
|
|
'netscan' => 'netscan.php',
|
|
'network' => 'network.php',
|
|
'proxmox' => 'proxmox.php',
|
|
'ha' => 'ha.php',
|
|
'tts' => 'tts.php',
|
|
'email' => 'email.php',
|
|
'do' => 'do_server.php',
|
|
'alerts' => 'alerts.php',
|
|
'facts' => 'facts_collector.php',
|
|
'weather' => 'weather.php',
|
|
'news' => 'news.php',
|
|
'sites' => 'sites.php',
|
|
'agent' => 'agent.php',
|
|
'planner' => 'planner.php',
|
|
'jellyfin' => 'jellyfin.php',
|
|
'history' => 'history.php',
|
|
'metrics' => 'metrics.php',
|
|
'suggestions' => 'suggestions.php',
|
|
'arc' => 'arc.php',
|
|
'directives' => 'directives.php',
|
|
'memory' => 'memory.php',
|
|
'calendar' => 'calendar_sync.php',
|
|
];
|
|
|
|
if (!isset($endpoints[$endpoint])) {
|
|
http_response_code(404);
|
|
echo json_encode(['error' => 'Unknown endpoint: ' . $endpoint]);
|
|
exit;
|
|
}
|
|
|
|
$file = __DIR__ . '/../api/endpoints/' . $endpoints[$endpoint];
|
|
|
|
// ── Fault-isolated dispatch ───────────────────────────────────────────
|
|
// ob_start() buffers any partial output so a mid-execution fatal doesn't
|
|
// send a broken response. catch(Throwable) catches ParseError, TypeError,
|
|
// and all other Errors + Exceptions in PHP 7+.
|
|
ob_start();
|
|
try {
|
|
require $file;
|
|
ob_end_flush();
|
|
} catch (\Throwable $e) {
|
|
ob_end_clean();
|
|
http_response_code(500);
|
|
echo json_encode(['error' => 'Endpoint unavailable', 'endpoint' => $endpoint, 'code' => 500]);
|
|
error_log(sprintf('JARVIS API [%s] %s: %s in %s:%d',
|
|
$endpoint, get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()
|
|
));
|
|
}
|