This commit is contained in:
Ty Clifford
2026-06-08 12:26:08 -04:00
commit 33a176f18e
19 changed files with 5342 additions and 0 deletions
+225
View File
@@ -0,0 +1,225 @@
<?php
// archive-viewer.php — Browse archived chat logs
require_once __DIR__ . '/bootstrap.php';
$archiveDir = ROOT_DIR . '/' . getConfigVal('archive.archive_dir', 'archive');
$days = [];
if (is_dir($archiveDir)) {
foreach (glob($archiveDir . '/*/*/*.sqlite') as $f) {
if (preg_match('/(\d{4})\/(\d{2})\/(\d{2})\.sqlite$/', $f, $m)) {
$days[] = "{$m[1]}-{$m[2]}-{$m[3]}";
}
}
rsort($days);
}
$selectedDay = $_GET['day'] ?? null;
$archiveMessages = [];
$archiveMeta = [];
if ($selectedDay && preg_match('/^\d{4}-\d{2}-\d{2}$/', $selectedDay)) {
[$y, $mo, $d] = explode('-', $selectedDay);
$dbPath = ROOT_DIR . '/' . str_replace(
['{year}', '{month}', '{day}'], [$y, $mo, $d],
getConfigVal('database.archive_path')
);
if (file_exists($dbPath)) {
$adb = getArchiveDB($y, $mo, $d);
$archiveMessages = $adb->query("SELECT * FROM messages ORDER BY created_at ASC")->fetchAll(PDO::FETCH_ASSOC);
$metaRows = $adb->query("SELECT key, value FROM archive_meta")->fetchAll(PDO::FETCH_ASSOC);
foreach ($metaRows as $mr) $archiveMeta[$mr['key']] = $mr['value'];
}
}
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CYBERCHAT // ARCHIVE</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Rajdhani:wght@400;600&family=Orbitron:wght@700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="assets/css/cyberchat.css">
<style>
html, body {
min-height: 100vh;
height: auto;
overflow: auto;
background: var(--bg0);
padding: 20px 16px 40px;
font-family: var(--ui);
}
.arc-wrap { max-width: 920px; margin: 0 auto; }
.arc-topbar {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid var(--bd);
}
.arc-back {
font-family: var(--mono);
font-size: 11px;
color: var(--b);
text-decoration: none;
letter-spacing: 1px;
transition: color 0.14s;
}
.arc-back:hover { color: var(--g); }
.arc-title {
font-family: var(--disp);
font-size: 11px;
letter-spacing: 3px;
color: var(--b);
}
.arc-grid {
display: grid;
grid-template-columns: 180px 1fr;
gap: 16px;
height: calc(100vh - 130px);
min-height: 300px;
}
.arc-sidebar, .arc-content {
background: var(--bg1);
border: 1px solid var(--bd);
border-radius: 6px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.arc-sidebar-head, .arc-content-head {
padding: 8px 12px;
border-bottom: 1px solid var(--bd);
font-family: var(--mono);
font-size: 9px;
letter-spacing: 2px;
color: var(--t1);
text-transform: uppercase;
flex-shrink: 0;
}
.arc-days-list { overflow-y: auto; flex: 1; }
.arc-day-item {
padding: 9px 14px;
font-family: var(--mono);
font-size: 12px;
color: var(--t1);
cursor: pointer;
border-left: 2px solid transparent;
transition: all 0.12s;
}
.arc-day-item:hover { color: var(--g); border-left-color: var(--g); background: rgba(0,255,159,0.04); }
.arc-day-item.active { color: var(--b); border-left-color: var(--b); background: rgba(0,184,255,0.06); }
.arc-messages-list { overflow-y: auto; flex: 1; padding: 4px 0; }
.arc-msg {
display: flex;
align-items: baseline;
padding: 3px 12px;
font-family: var(--mono);
font-size: 12px;
line-height: 1.65;
}
.arc-msg:hover { background: rgba(255,255,255,0.02); }
.arc-msg-time { color: var(--t2); font-size: 9px; min-width: 44px; flex-shrink: 0; margin-right: 7px; }
.arc-msg-user { font-weight: 600; font-size: 11px; flex-shrink: 0; min-width: 0; max-width: 90px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.arc-msg-sep { color: var(--t2); margin: 0 7px; flex-shrink: 0; }
.arc-msg-text { color: var(--t0); flex: 1; word-break: break-word; min-width: 0; }
.arc-msg-text audio { width: min(300px, 100%); height: 30px; vertical-align: middle; }
.arc-empty {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-family: var(--mono);
font-size: 10px;
color: var(--t2);
letter-spacing: 1.5px;
padding: 20px;
text-align: center;
}
.arc-meta {
font-family: var(--mono);
font-size: 9px;
color: var(--o);
padding: 6px 12px;
border-bottom: 1px solid var(--bd);
letter-spacing: 0.5px;
flex-shrink: 0;
}
@media (max-width: 600px) {
.arc-grid { grid-template-columns: 1fr; grid-template-rows: auto 1fr; height: auto; }
.arc-sidebar { height: 160px; }
}
</style>
</head>
<body>
<div class="arc-wrap">
<div class="arc-topbar">
<a href="index.html" class="arc-back">◂ BACK TO CHAT</a>
<span class="arc-title">// ARCHIVE VIEWER</span>
</div>
<div class="arc-grid">
<div class="arc-sidebar">
<div class="arc-sidebar-head">// DAYS</div>
<div class="arc-days-list">
<?php if (empty($days)): ?>
<div class="arc-empty">NO ARCHIVES YET</div>
<?php else: foreach ($days as $day): ?>
<div class="arc-day-item <?= $day === $selectedDay ? 'active' : '' ?>"
onclick="location.href='?day=<?= htmlspecialchars($day) ?>'">
<?= htmlspecialchars($day) ?>
</div>
<?php endforeach; endif; ?>
</div>
</div>
<div class="arc-content">
<div class="arc-content-head">// MESSAGES</div>
<?php if (!$selectedDay): ?>
<div class="arc-empty">SELECT A DAY FROM THE LIST</div>
<?php elseif (empty($archiveMessages)): ?>
<div class="arc-empty">NO MESSAGES FOR <?= htmlspecialchars($selectedDay) ?></div>
<?php else: ?>
<div class="arc-meta">
// <?= htmlspecialchars($selectedDay) ?>
&nbsp;|&nbsp; <?= count($archiveMessages) ?> MESSAGES
<?php if (!empty($archiveMeta['archived_at'])): ?>
&nbsp;|&nbsp; ARCHIVED <?= date('Y-m-d H:i', (int)$archiveMeta['archived_at']) ?> UTC
<?php endif; ?>
</div>
<div class="arc-messages-list">
<?php foreach ($archiveMessages as $msg): ?>
<div class="arc-msg">
<span class="arc-msg-time"><?= date('H:i', (int)$msg['created_at']) ?></span>
<span class="arc-msg-user" style="color:<?= htmlspecialchars($msg['color']) ?>"><?= htmlspecialchars($msg['username']) ?></span>
<span class="arc-msg-sep"></span>
<span class="arc-msg-text">
<?php if (($msg['message_type'] ?? 'text') === 'voice' && !empty($msg['voice_file'])): ?>
<audio controls preload="metadata"
src="<?= htmlspecialchars(trim((string)getConfigVal('voice.upload_dir', 'uploads/voice'), '/') . '/' . rawurlencode($msg['voice_file'])) ?>"></audio>
<?php else: ?>
<?= htmlspecialchars($msg['message']) ?>
<?php endif; ?>
</span>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
</body>
</html>