207 lines
8.3 KiB
Markdown
207 lines
8.3 KiB
Markdown
# admin2: Full PHP Admin for C-Relay-PG
|
|
|
|
## Goal
|
|
|
|
Recreate the entire c-relay-pg admin interface as a PHP application in a new
|
|
`admin2/` directory, starting from the existing `api/` HTML/CSS/JS files and
|
|
replacing the Nostr admin command backend with direct PostgreSQL queries via
|
|
PHP PDO. This covers ALL admin sections, not just caching.
|
|
|
|
## Architecture
|
|
|
|
Same as `admin/` (PHP + nginx + PDO + 10s polling), but with the full
|
|
section set from the original `api/index.html`:
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
Browser -->|HTTPS /admin2/| Nginx
|
|
Nginx -->|*.php| PHP_FPM
|
|
Nginx -->|/relay/| Relay[C-Relay-PG]
|
|
PHP_FPM -->|PDO| PostgreSQL
|
|
Relay -->|libpq| PostgreSQL
|
|
```
|
|
|
|
## Sections (9 pages, matching original nav)
|
|
|
|
| # | Nav Label | Section ID | Data Source | PHP API Endpoint |
|
|
|---|-----------|------------|-------------|------------------|
|
|
| 1 | Statistics | databaseStatisticsSection | `pg_database_size()`, `events` count, `pg_stat_activity`, process stats | `api/stats.php` |
|
|
| 2 | Subscriptions | subscriptionDetailsSection | `subscriptions` + `subscription_metrics` tables | `api/subscriptions.php` |
|
|
| 3 | Configuration | div_config | `config` table (read/edit all keys) | `api/config.php` |
|
|
| 4 | Authorization | authRulesSection + wotSection | `auth_rules` table | `api/auth.php` |
|
|
| 5 | IP BAN | ipBansSection | `ip_bans` table | `api/ipbans.php` |
|
|
| 6 | Relay Events | relayEventsSection | `events` table (recent events, kind filter) | `api/events.php` |
|
|
| 7 | Caching | cachingSection | `caching_*` tables (already built in `admin/`) | `api/caching.php` |
|
|
| 8 | DM | nip17DMSection | `events` table (kind 4/14/15 DMs) | `api/dm.php` |
|
|
| 9 | Database Query | sqlQuerySection | Direct SQL execution (admin only) | `api/query.php` |
|
|
|
|
## Graph on Statistics Page
|
|
|
|
The original has an event-rate chart. For the PHP version:
|
|
- **X-axis:** one tick = 10 seconds (the refresh interval)
|
|
- **Y-axis:** events per interval
|
|
- **Data source:** `api/stats.php` returns `events_delta` (count of new events since last poll)
|
|
- **JS:** maintains a rolling array of the last 60 data points (10 minutes of history), appends a new point on each 10s refresh, renders as a simple canvas/SVG line chart
|
|
- **No external chart library** — use a lightweight inline canvas renderer to keep it dependency-free
|
|
|
|
## File Structure
|
|
|
|
```
|
|
admin2/
|
|
├── index.php ← Single-page app (all 9 sections in one page, show/hide via JS)
|
|
├── assets/
|
|
│ ├── index.css ← Copied from api/index.css (unchanged)
|
|
│ └── app.js ← Adapted from api/index.js (replace Nostr commands with fetch() to PHP APIs)
|
|
├── lib/
|
|
│ ├── config.php ← DB connection config (same as admin/)
|
|
│ ├── db.php ← PDO helper (same as admin/)
|
|
│ └── helpers.php ← Shared helpers (same as admin/)
|
|
├── api/
|
|
│ ├── stats.php ← Statistics: DB size, event counts, process info, event-rate delta
|
|
│ ├── subscriptions.php ← Subscription details + metrics
|
|
│ ├── config.php ← Config table read/edit (all keys, not just caching)
|
|
│ ├── auth.php ← Auth rules + WoT status
|
|
│ ├── ipbans.php ← IP ban list (read/add/remove)
|
|
│ ├── events.php ← Recent relay events (paginated, kind filter)
|
|
│ ├── caching.php ← Caching status (reuses admin/api/status.php logic)
|
|
│ ├── dm.php ← DM list (kind 4/14/15 events)
|
|
│ └── query.php ← Direct SQL query execution (admin only, read-only by default)
|
|
└── README.md
|
|
```
|
|
|
|
## Implementation Plan
|
|
|
|
### Step 1 — Scaffold admin2/ directory
|
|
- Create `admin2/` with `lib/` (copy from `admin/`), `assets/`, `api/`
|
|
- Copy `api/index.css` → `admin2/assets/index.css` (unchanged)
|
|
- Copy `api/index.html` → `admin2/index.php` (will be adapted)
|
|
|
|
### Step 2 — Adapt index.php from api/index.html
|
|
- Remove Nostr login/auth (NIP-07 extension, kind-23456 admin commands)
|
|
- Remove WebSocket connection logic (replaced by PHP AJAX polling)
|
|
- Keep all 9 section HTML structures exactly as-is
|
|
- Keep the side-nav, header, section show/hide JS
|
|
- Replace `sendAdminCommand()` calls with `fetch('api/*.php')` calls
|
|
- Add 10s auto-refresh via `setInterval` for statistics page
|
|
- Add event-rate chart canvas renderer
|
|
|
|
### Step 3 — Adapt app.js from api/index.js
|
|
- Keep: `switchPage()`, nav handling, section show/hide, config table rendering
|
|
- Remove: WebSocket connection, NIP-07 login, NIP-44 encryption, kind-23456 event sending
|
|
- Replace: each `sendAdminCommand(['system_command', '...'])` with `fetch('api/....php')`
|
|
- Add: `refreshStats()` function that polls `api/stats.php` every 10s
|
|
- Add: event-rate chart rendering on canvas (rolling 60-point window)
|
|
|
|
### Step 4 — Build PHP API endpoints (9 files)
|
|
Each endpoint returns JSON, queried directly from PostgreSQL via PDO:
|
|
|
|
**api/stats.php** — Statistics page data:
|
|
```sql
|
|
SELECT pg_database_size(current_database()) AS db_size;
|
|
SELECT COUNT(*) FROM events;
|
|
SELECT COUNT(*) FROM pg_stat_activity WHERE state = 'active';
|
|
-- Event rate: count events with first_seen in last 10s
|
|
SELECT COUNT(*) FROM events WHERE first_seen >= EXTRACT(EPOCH FROM NOW())::BIGINT - 10;
|
|
-- Kind distribution
|
|
SELECT kind, COUNT(*) FROM events GROUP BY kind ORDER BY count DESC;
|
|
```
|
|
|
|
**api/config.php** — Configuration (all config keys, not just caching):
|
|
```sql
|
|
SELECT key, value FROM config ORDER BY key;
|
|
-- POST: UPDATE config SET value = ? WHERE key = ?
|
|
```
|
|
|
|
**api/subscriptions.php** — Subscription details:
|
|
```sql
|
|
SELECT * FROM subscriptions ORDER BY created_at DESC LIMIT 100;
|
|
SELECT * FROM subscription_metrics ORDER BY id DESC LIMIT 100;
|
|
```
|
|
|
|
**api/auth.php** — Auth rules:
|
|
```sql
|
|
SELECT * FROM auth_rules ORDER BY id;
|
|
```
|
|
|
|
**api/ipbans.php** — IP bans:
|
|
```sql
|
|
SELECT * FROM ip_bans ORDER BY banned_at DESC;
|
|
-- POST: INSERT/DELETE
|
|
```
|
|
|
|
**api/events.php** — Recent relay events (paginated):
|
|
```sql
|
|
SELECT id, pubkey, kind, created_at, content, tags
|
|
FROM events ORDER BY created_at DESC LIMIT 50 OFFSET ?;
|
|
-- Filter by kind, pubkey
|
|
```
|
|
|
|
**api/caching.php** — Caching status (reuse from admin/):
|
|
```sql
|
|
SELECT * FROM caching_service_state;
|
|
SELECT * FROM caching_followed_pubkeys LIMIT 50 OFFSET ?;
|
|
SELECT * FROM caching_backfill_relay_progress LIMIT 50 OFFSET ?;
|
|
```
|
|
|
|
**api/dm.php** — Direct messages:
|
|
```sql
|
|
SELECT id, pubkey, kind, created_at, content
|
|
FROM events WHERE kind IN (4, 14, 15) ORDER BY created_at DESC LIMIT 50;
|
|
```
|
|
|
|
**api/query.php** — SQL query (admin only):
|
|
```sql
|
|
-- Execute arbitrary SELECT, return JSON
|
|
-- Read-only by default; write mode behind a flag
|
|
```
|
|
|
|
### Step 5 — Event-rate chart
|
|
- Canvas element in the statistics section
|
|
- JS maintains `eventRateHistory = []` (max 60 points)
|
|
- On each 10s refresh, push new event count, shift if > 60
|
|
- Render as line chart on canvas (no external library)
|
|
- X-axis labels: time (mm:ss), one tick per 10s
|
|
- Y-axis labels: event count per interval
|
|
|
|
### Step 6 — nginx config
|
|
Add `/admin2/` location block (same pattern as `/admin/`):
|
|
```nginx
|
|
location ^~ /admin2/ {
|
|
alias /opt/c-relay-pg/admin2/;
|
|
index index.php;
|
|
auth_basic "Relay Admin";
|
|
auth_basic_user_file /opt/c-relay-pg/admin/.htpasswd;
|
|
location ~ \.php$ {
|
|
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
|
|
include fastcgi_params;
|
|
fastcgi_param SCRIPT_FILENAME $request_filename;
|
|
}
|
|
location ^~ /admin2/lib/ { deny all; }
|
|
}
|
|
```
|
|
|
|
### Step 7 — Deploy and test
|
|
- Copy `admin2/` to server
|
|
- Add nginx location block, reload
|
|
- Test all 9 sections
|
|
- Verify 10s auto-refresh on statistics page
|
|
- Verify event-rate chart updates
|
|
|
|
## Key Differences from Original api/
|
|
|
|
| Aspect | Original (api/) | New (admin2/) |
|
|
|--------|-----------------|---------------|
|
|
| Auth | NIP-07 Nostr extension login | HTTP Basic Auth (nginx) |
|
|
| Data fetch | WebSocket + kind-23456 encrypted admin commands | PHP AJAX `fetch()` to `api/*.php` |
|
|
| Encryption | NIP-44 (64KB limit) | None (direct DB, no limit) |
|
|
| Real-time | WebSocket push | 10s polling |
|
|
| Backend | C relay binary (embedded JS) | PHP-FPM + PostgreSQL PDO |
|
|
| Sections | 9 (same) | 9 (same HTML, different data source) |
|
|
|
|
## Out of Scope
|
|
|
|
- NIP-07 Nostr login (use HTTP Basic Auth)
|
|
- WebSocket push (use 10s polling)
|
|
- Write operations that require relay-level logic (event publishing, NIP-42 auth) — those stay on the Nostr admin API
|
|
- The original `api/` page remains unchanged and functional
|