SIMON Master Console Rebuild
True command center for SIMON + WEB360
This rebuild gives you a cleaner production spine: interactive tree, live viewer, guarded editor, dynamic graphics, architecture map, and note storage. It is designed to sit beside your existing
console.php
and become the stronger replacement path.
Dashboard
Tree
Viewer
System Map
File Registry
Notes
Files
15,282
Folders
406
Scanned Size
3.84 GB
PHP Files
876
Editable Text Files
12,545
File viewer
guarded to /htdocs
/connlink/test1/ui/old/job_engine.php
<?php declare(strict_types=1); require_once __DIR__ . '/bootstrap.php'; const SIMON_JOB_STATE = SIMON_DATA_DIR . '/jobs_state.json'; const SIMON_JOB_ITEMS_DIR = SIMON_DATA_DIR . '/job_items'; if (!is_dir(SIMON_JOB_ITEMS_DIR)) @mkdir(SIMON_JOB_ITEMS_DIR, 0775, true); function simon_jobs_state(): array { $state = simon_read_json_file(SIMON_JOB_STATE, ['version'=>SIMON_FEED_VERSION, 'updatedAt'=>null, 'feeds'=>[], 'runs'=>[]]); if (!is_array($state)) $state = ['version'=>SIMON_FEED_VERSION, 'updatedAt'=>null, 'feeds'=>[], 'runs'=>[]]; $state['feeds'] = is_array($state['feeds'] ?? null) ? $state['feeds'] : []; $state['runs'] = is_array($state['runs'] ?? null) ? $state['runs'] : []; return $state; } function simon_jobs_save_state(array $state): bool { $state['updatedAt'] = gmdate('c'); return simon_write_json_file(SIMON_JOB_STATE, $state); } function simon_job_item_path(string $feedId): string { return SIMON_JOB_ITEMS_DIR . '/' . simon_slug($feedId, 'feed') . '.json'; } function simon_job_delete_provider_cache(array $feed): void { $prefix = simon_slug((string)($feed['id'] ?? 'feed'), 'feed') . '_'; foreach (glob(SIMON_CACHE_DIR . '/' . $prefix . '*.json') ?: [] as $file) @unlink($file); } function simon_job_run_id(): string { return gmdate('Ymd_His') . '_' . substr(sha1((string)microtime(true)), 0, 7); } function simon_job_feed_due(array $feed, array $feedState, int $now = 0): bool { if ($now <= 0) $now = time(); if (empty($feed['enabled'])) return false; $last = strtotime((string)($feedState['lastRunAt'] ?? '')) ?: 0; $refresh = max(30, (int)($feed['refresh'] ?? 900)); return ($now - $last) >= $refresh; } function simon_job_run_feed(array $feed, array $ctx = [], bool $force = false, ?string $runId = null): array { $runId = $runId ?: simon_job_run_id(); $feedId = (string)($feed['id'] ?? 'unknown'); $engine = (string)($feed['engine'] ?? ''); $provider = simon_load_provider($engine); $start = microtime(true); $result = ['ok'=>false, 'items'=>[], 'error'=>'']; if (!$provider) { $result['error'] = 'Provider missing: ' . $engine; } else { try { if ($force) simon_job_delete_provider_cache($feed); $result = $provider($feed, $ctx); if (!is_array($result)) $result = ['ok'=>false, 'items'=>[], 'error'=>'Provider returned invalid result']; } catch (Throwable $e) { $result = ['ok'=>false, 'items'=>[], 'error'=>$e->getMessage()]; } } $items = []; foreach (($result['items'] ?? []) as $item) if (is_array($item)) $items[] = $item; $ms = round((microtime(true) - $start) * 1000); $record = [ 'runId' => $runId, 'feedId' => $feedId, 'name' => (string)($feed['name'] ?? $feedId), 'engine' => $engine, 'category' => (string)($feed['category'] ?? 'general'), 'ok' => (bool)($result['ok'] ?? false), 'error' => (string)($result['error'] ?? ''), 'count' => count($items), 'ms' => $ms, 'refresh' => max(30, (int)($feed['refresh'] ?? 900)), 'lastRunAt' => gmdate('c'), 'nextRunAt' => gmdate('c', time() + max(30, (int)($feed['refresh'] ?? 900))), ]; simon_write_json_file(simon_job_item_path($feedId), [ 'feed' => [ 'id'=>$feedId, 'name'=>(string)($feed['name'] ?? $feedId), 'category'=>(string)($feed['category'] ?? 'general'), 'engine'=>$engine, 'color'=>(string)($feed['color'] ?? '#4be3ff'), ], 'status' => $record, 'items' => $items, ]); simon_log('job_feed_run', $record); return $record; } function simon_jobs_run(array $options = []): array { $force = !empty($options['force']); $only = trim((string)($options['feed'] ?? '')); $limit = max(1, min(100, (int)($options['limit'] ?? 20))); $lat = isset($options['lat']) ? (float)$options['lat'] : null; $lon = isset($options['lon']) ? (float)$options['lon'] : null; $place = trim((string)($options['place'] ?? '')); $ctx = ['lat'=>$lat, 'lon'=>$lon, 'place'=>$place ?: null]; $feeds = simon_read_json_file(SIMON_FEEDS_FILE, []); if (!is_array($feeds)) $feeds = []; $state = simon_jobs_state(); $runId = simon_job_run_id(); $ran = []; $skipped = []; $now = time(); foreach ($feeds as $feed) { if (!is_array($feed)) continue; $feedId = (string)($feed['id'] ?? ''); if ($feedId === '') continue; if ($only !== '' && $feedId !== $only) continue; if (empty($feed['enabled'])) { $skipped[] = ['feedId'=>$feedId, 'reason'=>'disabled']; continue; } $feedState = is_array($state['feeds'][$feedId] ?? null) ? $state['feeds'][$feedId] : []; $due = $force || $only !== '' || simon_job_feed_due($feed, $feedState, $now); if (!$due) { $skipped[] = ['feedId'=>$feedId, 'reason'=>'not_due', 'nextRunAt'=>$feedState['nextRunAt'] ?? null]; continue; } if (count($ran) >= $limit) { $skipped[] = ['feedId'=>$feedId, 'reason'=>'limit_reached']; continue; } $record = simon_job_run_feed($feed, $ctx, $force, $runId); $state['feeds'][$feedId] = $record; $ran[] = $record; } $summary = [ 'runId' => $runId, 'at' => gmdate('c'), 'force' => $force, 'feed' => $only ?: null, 'ran' => count($ran), 'ok' => count(array_filter($ran, fn($r) => !empty($r['ok']))), 'failed' => count(array_filter($ran, fn($r) => empty($r['ok']))), 'skipped' => count($skipped), 'totalItems' => array_sum(array_map(fn($r) => (int)($r['count'] ?? 0), $ran)), ]; array_unshift($state['runs'], $summary); $state['runs'] = array_slice($state['runs'], 0, 80); simon_jobs_save_state($state); simon_log('job_run', $summary); return ['ok'=>true, 'summary'=>$summary, 'ran'=>$ran, 'skipped'=>$skipped, 'state'=>$state]; } function simon_jobs_cached_items(array $filters = []): array { $items = []; $health = []; $category = trim((string)($filters['category'] ?? '')); $source = trim((string)($filters['source'] ?? '')); $q = strtolower(trim((string)($filters['q'] ?? ''))); foreach (glob(SIMON_JOB_ITEMS_DIR . '/*.json') ?: [] as $file) { $snap = simon_read_json_file($file, []); if (!is_array($snap)) continue; $status = is_array($snap['status'] ?? null) ? $snap['status'] : []; $feed = is_array($snap['feed'] ?? null) ? $snap['feed'] : []; if ($source !== '' && ($feed['id'] ?? '') !== $source) continue; if ($category !== '' && ($feed['category'] ?? '') !== $category) continue; $health[] = $status; foreach (($snap['items'] ?? []) as $item) { if (!is_array($item)) continue; if ($q !== '') { $hay = strtolower(($item['title'] ?? '') . ' ' . ($item['summary'] ?? '') . ' ' . ($item['source'] ?? '') . ' ' . ($item['category'] ?? '')); if (!str_contains($hay, $q)) continue; } $items[] = $item; } } $items = simon_dedupe_events($items); usort($items, function($a, $b) { $s = ($b['score'] <=> $a['score']); if ($s !== 0) return $s; return strtotime($b['published'] ?? 'now') <=> strtotime($a['published'] ?? 'now'); }); return ['items'=>$items, 'health'=>$health]; } function simon_jobs_status(): array { $feeds = simon_read_json_file(SIMON_FEEDS_FILE, []); if (!is_array($feeds)) $feeds = []; $state = simon_jobs_state(); $rows = []; foreach ($feeds as $feed) { if (!is_array($feed)) continue; $id = (string)($feed['id'] ?? ''); if ($id === '') continue; $st = is_array($state['feeds'][$id] ?? null) ? $state['feeds'][$id] : []; $refresh = max(30, (int)($feed['refresh'] ?? 900)); $last = strtotime((string)($st['lastRunAt'] ?? '')) ?: 0; $next = $last ? $last + $refresh : time(); $rows[] = [ 'id'=>$id, 'name'=>(string)($feed['name'] ?? $id), 'category'=>(string)($feed['category'] ?? 'general'), 'engine'=>(string)($feed['engine'] ?? ''), 'enabled'=>!empty($feed['enabled']), 'refresh'=>$refresh, 'lastRunAt'=>$st['lastRunAt'] ?? null, 'nextRunAt'=>gmdate('c', $next), 'due'=>empty($feed['enabled']) ? false : time() >= $next, 'ok'=>$st['ok'] ?? null, 'count'=>$st['count'] ?? 0, 'ms'=>$st['ms'] ?? null, 'error'=>$st['error'] ?? '', ]; } return ['ok'=>true, 'version'=>SIMON_FEED_VERSION, 'checkedAt'=>gmdate('c'), 'feeds'=>$rows, 'runs'=>$state['runs'] ?? []]; }
Save file
Quick jump
open a path
Open