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,338
Folders
408
Scanned Size
3.84 GB
PHP Files
890
Editable Text Files
12,599
File viewer
guarded to /htdocs
/connlink/test1/ui/old/bootstrap.php
<?php declare(strict_types=1); const SIMON_FEED_VERSION = '2.5.0-phase5'; const SIMON_ROOT = __DIR__; const SIMON_CACHE_DIR = SIMON_ROOT . '/cache'; const SIMON_LOG_DIR = SIMON_ROOT . '/logs'; const SIMON_DATA_DIR = SIMON_ROOT . '/data'; const SIMON_BACKUP_DIR = SIMON_ROOT . '/backups'; const SIMON_FEEDS_FILE = SIMON_ROOT . '/feeds.json'; foreach ([SIMON_CACHE_DIR, SIMON_LOG_DIR, SIMON_DATA_DIR, SIMON_BACKUP_DIR] as $dir) { if (!is_dir($dir)) @mkdir($dir, 0775, true); } function simon_slug(string $value, string $fallback = 'feed'): string { $value = strtolower(trim($value)); $value = preg_replace('/[^a-z0-9_\-]+/', '_', $value) ?? ''; $value = trim($value, '_-'); return $value !== '' ? $value : $fallback; } function simon_backup_file(string $path, string $reason = 'save'): ?string { if (!is_file($path)) return null; if (!is_dir(SIMON_BACKUP_DIR)) @mkdir(SIMON_BACKUP_DIR, 0775, true); $name = pathinfo($path, PATHINFO_FILENAME); $stamp = gmdate('Ymd_His'); $safeReason = simon_slug($reason, 'backup'); $dest = SIMON_BACKUP_DIR . '/' . $name . '_' . $safeReason . '_' . $stamp . '.json'; return @copy($path, $dest) ? $dest : null; } function simon_normalize_feed(array $input): array { $id = simon_slug((string)($input['id'] ?? ''), 'feed_' . substr(sha1((string)microtime(true)), 0, 8)); $engine = simon_slug((string)($input['engine'] ?? 'rss'), 'rss'); $feed = [ 'id' => $id, 'name' => trim((string)($input['name'] ?? $id)) ?: $id, 'category' => simon_slug((string)($input['category'] ?? 'general'), 'general'), 'engine' => $engine, 'enabled' => !empty($input['enabled']), 'refresh' => max(30, (int)($input['refresh'] ?? 900)), 'priority' => max(0, min(100, (int)($input['priority'] ?? 70))), 'color' => preg_match('/^#[0-9a-fA-F]{6}$/', (string)($input['color'] ?? '')) ? (string)$input['color'] : '#4be3ff', ]; foreach (['url','stream','place'] as $k) { if (isset($input[$k]) && trim((string)$input[$k]) !== '') $feed[$k] = trim((string)$input[$k]); } foreach (['lat','lon'] as $k) { if (isset($input[$k]) && $input[$k] !== '') $feed[$k] = (float)$input[$k]; } if (isset($input['params']) && is_array($input['params'])) $feed['params'] = $input['params']; if (isset($input['map']) && is_array($input['map'])) $feed['map'] = $input['map']; return $feed; } function simon_json_response(array $payload, int $status = 200): void { http_response_code($status); header('Content-Type: application/json; charset=utf-8'); header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0'); echo json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); exit; } function simon_read_json_file(string $path, $default = []): mixed { if (!is_file($path)) return $default; $raw = file_get_contents($path); if ($raw === false || trim($raw) === '') return $default; $data = json_decode($raw, true); return json_last_error() === JSON_ERROR_NONE ? $data : $default; } function simon_write_json_file(string $path, mixed $data): bool { $tmp = $path . '.tmp'; $json = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); if ($json === false) return false; if (file_put_contents($tmp, $json, LOCK_EX) === false) return false; return rename($tmp, $path); } function simon_secrets(): array { $paths = [ SIMON_ROOT . '/private/secrets.php', SIMON_ROOT . '/secrets.php', dirname(SIMON_ROOT) . '/private/secure/secrets.php', dirname(dirname(SIMON_ROOT)) . '/private/secure/secrets.php', ]; foreach ($paths as $path) { if (is_file($path)) { $data = require $path; return is_array($data) ? $data : []; } } return []; } function simon_log(string $type, array $event): void { $event['type'] = $type; $event['at'] = gmdate('c'); $line = json_encode($event, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n"; @file_put_contents(SIMON_LOG_DIR . '/feed_events.ndjson', $line, FILE_APPEND | LOCK_EX); } function simon_cache_key(array $feed, array $extra = []): string { return preg_replace('/[^a-zA-Z0-9_\-]/', '_', ($feed['id'] ?? 'feed')) . '_' . substr(sha1(json_encode([$feed, $extra])), 0, 12) . '.json'; } function simon_cache_get(string $key, int $ttl): ?array { $path = SIMON_CACHE_DIR . '/' . $key; if (!is_file($path)) return null; if ($ttl > 0 && (time() - filemtime($path)) > $ttl) return null; $data = simon_read_json_file($path, null); return is_array($data) ? $data : null; } function simon_cache_set(string $key, array $data): void { @simon_write_json_file(SIMON_CACHE_DIR . '/' . $key, $data); } function simon_http_get(string $url, array $headers = [], int $timeout = 12): array { $ch = curl_init($url); $defaultHeaders = [ 'Accept: application/json, application/rss+xml, application/xml, text/xml, */*', 'User-Agent: SIMON-FeedEngine/2.0 (+https://www.gaylordsinclair.com/)' ]; curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 4, CURLOPT_TIMEOUT => $timeout, CURLOPT_CONNECTTIMEOUT => 6, CURLOPT_HTTPHEADER => array_merge($defaultHeaders, $headers), CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2, ]); $body = curl_exec($ch); $err = curl_error($ch); $code = (int)curl_getinfo($ch, CURLINFO_RESPONSE_CODE); $ctype = (string)curl_getinfo($ch, CURLINFO_CONTENT_TYPE); curl_close($ch); if ($body === false) $body = ''; return ['ok' => $code >= 200 && $code < 300, 'status' => $code, 'body' => $body, 'error' => $err, 'content_type' => $ctype]; } function simon_get_path(array $data, string $path, mixed $default = null): mixed { $cur = $data; foreach (explode('.', $path) as $part) { if (is_array($cur) && array_key_exists($part, $cur)) $cur = $cur[$part]; else return $default; } return $cur; } function simon_clean_text(mixed $value, int $max = 600): string { $text = trim(strip_tags(html_entity_decode((string)$value, ENT_QUOTES | ENT_HTML5, 'UTF-8'))); $text = preg_replace('/\s+/', ' ', $text) ?? $text; if (mb_strlen($text) > $max) $text = mb_substr($text, 0, $max - 1) . '…'; return $text; } function simon_event(array $feed, array $raw): array { $title = simon_clean_text($raw['title'] ?? $raw['name'] ?? $raw['headline'] ?? 'Untitled'); $summary = simon_clean_text($raw['summary'] ?? $raw['description'] ?? $raw['text'] ?? $raw['body'] ?? '', 900); $url = trim((string)($raw['url'] ?? $raw['link'] ?? $raw['href'] ?? '')); $published = (string)($raw['published'] ?? $raw['publishedAt'] ?? $raw['date'] ?? $raw['time'] ?? gmdate('c')); $timestamp = strtotime($published) ?: time(); $hash = sha1(($feed['id'] ?? '') . '|' . $title . '|' . $url . '|' . $timestamp); $priority = (float)($feed['priority'] ?? 50); $ageHours = max(0, (time() - $timestamp) / 3600); $fresh = max(0.15, 1 - min(1, $ageHours / 72)); $score = round(min(100, ($priority * 0.65) + ($fresh * 35)), 2); return [ 'id' => $hash, 'source_id' => (string)($feed['id'] ?? 'unknown'), 'source' => (string)($feed['name'] ?? ($feed['id'] ?? 'Unknown')), 'category' => (string)($feed['category'] ?? 'general'), 'engine' => (string)($feed['engine'] ?? 'unknown'), 'title' => $title, 'summary' => $summary, 'url' => $url, 'image' => (string)($raw['image'] ?? ''), 'published' => gmdate('c', $timestamp), 'score' => $score, 'color' => (string)($feed['color'] ?? '#4be3ff'), 'raw' => $raw, ]; } function simon_load_provider(string $engine): ?callable { $engine = preg_replace('/[^a-zA-Z0-9_\-]/', '', $engine); $path = SIMON_ROOT . '/providers/' . $engine . '.php'; if (!is_file($path)) return null; require_once $path; $fn = 'simon_provider_' . str_replace('-', '_', $engine); return function_exists($fn) ? $fn : null; } function simon_dedupe_events(array $events): array { $seen = []; $out = []; foreach ($events as $event) { $key = strtolower(trim((string)($event['url'] ?: $event['title']))); $key = preg_replace('/[?#].*$/', '', $key) ?: (string)$event['id']; if (isset($seen[$key])) continue; $seen[$key] = true; $out[] = $event; } return $out; }
Save file
Quick jump
open a path
Open