154 lines
7.3 KiB
PHP
154 lines
7.3 KiB
PHP
<?php
|
|
/**
|
|
* C-Relay-PG Admin — Followed Pubkeys (paginated).
|
|
* Replaces the NIP-44-encrypted caching_follows_status admin command.
|
|
*/
|
|
require_once __DIR__ . '/lib/helpers.php';
|
|
|
|
$pdo = db();
|
|
$page = current_page();
|
|
$per = cfg('follows_per_page', 50);
|
|
$offset = ($page - 1) * $per;
|
|
|
|
$filter = query_param('filter', 'all');
|
|
$search = query_param('search', '');
|
|
$sort = query_param('sort', 'events');
|
|
|
|
$where = [];
|
|
$params = [];
|
|
if ($filter === 'incomplete') {
|
|
$where[] = 'fp.backfill_complete = false';
|
|
} elseif ($filter === 'root') {
|
|
$where[] = 'fp.is_root = true';
|
|
} elseif ($filter === 'error') {
|
|
$where[] = "EXISTS (SELECT 1 FROM caching_backfill_relay_progress rp WHERE rp.author_pubkey = fp.pubkey AND rp.last_status LIKE 'error%')";
|
|
}
|
|
if ($search) {
|
|
$where[] = '(fp.pubkey ILIKE ? OR e.content::json->>\'name\' ILIKE ? OR e.content::json->>\'display_name\' ILIKE ?)';
|
|
$params[] = "%$search%"; $params[] = "%$search%"; $params[] = "%$search%";
|
|
}
|
|
$where_sql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
|
|
|
|
$sort_map = [
|
|
'events' => 'fp.events_fetched DESC',
|
|
'name' => "COALESCE(e.content::json->>'display_name', e.content::json->>'name') ASC NULLS LAST",
|
|
'recent' => 'fp.last_seen DESC',
|
|
'complete' => 'fp.backfill_complete ASC, fp.events_fetched DESC',
|
|
];
|
|
$sort_sql = $sort_map[$sort] ?? $sort_map['events'];
|
|
|
|
$stmt = $pdo->prepare("SELECT COUNT(*) FROM caching_followed_pubkeys fp LEFT JOIN LATERAL (SELECT content FROM events WHERE pubkey = fp.pubkey AND kind = 0 ORDER BY created_at DESC LIMIT 1) e ON true $where_sql");
|
|
$stmt->execute($params);
|
|
$total = intval($stmt->fetchColumn());
|
|
|
|
$sql = "
|
|
SELECT fp.pubkey, fp.is_root, fp.backfill_complete, fp.events_fetched,
|
|
fp.first_seen, fp.last_seen,
|
|
e.content::json->>'name' AS name,
|
|
e.content::json->>'display_name' AS display_name,
|
|
e.content::json->>'picture' AS picture,
|
|
e.content::json->>'nip05' AS nip05,
|
|
(SELECT COUNT(*) FROM events WHERE pubkey = fp.pubkey) AS total_events
|
|
FROM caching_followed_pubkeys fp
|
|
LEFT JOIN LATERAL (SELECT content FROM events WHERE pubkey = fp.pubkey AND kind = 0 ORDER BY created_at DESC LIMIT 1) e ON true
|
|
$where_sql
|
|
ORDER BY $sort_sql
|
|
LIMIT $per OFFSET $offset
|
|
";
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute($params);
|
|
$follows = $stmt->fetchAll();
|
|
|
|
admin_header('follows', 'C-Relay-PG Admin — Follows');
|
|
?>
|
|
|
|
<div class="section">
|
|
<div class="section-header">FOLLOWED PUBKEYS (<?= number_format($total) ?> total)</div>
|
|
|
|
<form class="filters" method="get">
|
|
<select name="filter" onchange="this.form.submit()">
|
|
<option value="all" <?= $filter==='all'?'selected':'' ?>>All</option>
|
|
<option value="incomplete" <?= $filter==='incomplete'?'selected':'' ?>>Incomplete only</option>
|
|
<option value="root" <?= $filter==='root'?'selected':'' ?>>Root only</option>
|
|
<option value="error" <?= $filter==='error'?'selected':'' ?>>Has error relays</option>
|
|
</select>
|
|
<select name="sort" onchange="this.form.submit()">
|
|
<option value="events" <?= $sort==='events'?'selected':'' ?>>Sort: Events fetched</option>
|
|
<option value="name" <?= $sort==='name'?'selected':'' ?>>Sort: Name</option>
|
|
<option value="recent" <?= $sort==='recent'?'selected':'' ?>>Sort: Recently seen</option>
|
|
<option value="complete" <?= $sort==='complete'?'selected':'' ?>>Sort: Backfill status</option>
|
|
</select>
|
|
<input type="text" name="search" value="<?= e($search) ?>" placeholder="Search name or pubkey…">
|
|
<button type="submit">Search</button>
|
|
<?php if ($filter !== 'all' || $search): ?>
|
|
<a href="follows.php" style="font-size:12px">Clear</a>
|
|
<?php endif; ?>
|
|
</form>
|
|
|
|
<div class="config-table-container">
|
|
<table class="config-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>npub</th>
|
|
<th>Root?</th>
|
|
<th>Events in DB</th>
|
|
<th>Backfill</th>
|
|
<th>Last Seen</th>
|
|
<th>Relays</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($follows as $f):
|
|
$npub = hex_to_npub($f['pubkey']);
|
|
$name = $f['display_name'] ?: $f['name'];
|
|
$rc = ['total' => 0, 'incomplete' => 0, 'errors' => 0];
|
|
try {
|
|
$relay_count = $pdo->prepare("SELECT COUNT(*) AS total, COUNT(*) FILTER (WHERE complete = false) AS incomplete, COUNT(*) FILTER (WHERE last_status LIKE 'error%') AS errors FROM caching_backfill_relay_progress WHERE author_pubkey = ?");
|
|
$relay_count->execute([$f['pubkey']]);
|
|
$rc = $relay_count->fetch() ?: $rc;
|
|
} catch (PDOException $ex) {}
|
|
?>
|
|
<tr>
|
|
<td>
|
|
<?php if ($name): ?>
|
|
<strong><?= e($name) ?></strong>
|
|
<?php if ($f['nip05']): ?>
|
|
<br><span style="color:var(--muted-color)">✓ <?= e($f['nip05']) ?></span>
|
|
<?php endif; ?>
|
|
<?php else: ?>
|
|
<span style="color:var(--muted-color);font-style:italic">unknown</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td><a href="https://njump.me/<?= e($npub) ?>" target="_blank" class="npub-link"><?= e(trunc($npub, 22)) ?></a></td>
|
|
<td><?= $f['is_root'] ? '<span class="badge badge-warning">root</span>' : '' ?></td>
|
|
<td><?= number_format(intval($f['total_events'])) ?></td>
|
|
<td><?= $f['backfill_complete']
|
|
? '<span class="badge badge-success">✓ complete</span>'
|
|
: '<span class="badge badge-warning">in progress</span>' ?></td>
|
|
<td><?= time_ago(intval($f['last_seen'])) ?></td>
|
|
<td>
|
|
<?php if (intval($rc['total']) > 0): ?>
|
|
<?= intval($rc['total']) ?> relays (<?= intval($rc['incomplete']) ?> incomplete
|
|
<?php if (intval($rc['errors']) > 0): ?>
|
|
, <span class="status-error"><?= intval($rc['errors']) ?> errors</span>
|
|
<?php endif; ?>)
|
|
<br><a href="relays.php?pubkey=<?= e($f['pubkey']) ?>" style="font-size:10px">View relay details →</a>
|
|
<?php else: ?>
|
|
<span style="color:var(--muted-color)">—</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php if (empty($follows)): ?>
|
|
<tr><td colspan="7" style="text-align:center;color:var(--muted-color);padding:20px">No followed pubkeys found.</td></tr>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<?= pagination($page, $per, $total, 'follows.php' . ($filter !== 'all' ? '?filter=' . e($filter) : '') . ($search ? '&search=' . e($search) : '') . ($sort !== 'events' ? '&sort=' . e($sort) : '')) ?>
|
|
</div>
|
|
|
|
<?php admin_footer(); ?>
|