feat: admin dashboard for THE CREATOR
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
e3a4299dae
commit
2170e9275c
@@ -0,0 +1,107 @@
|
||||
import { Hono } from 'hono'
|
||||
import { db, schema, sqlite } from '../db/index.js'
|
||||
import { eq, desc, sql, count } from 'drizzle-orm'
|
||||
import { getActiveSSECount } from './fights.js'
|
||||
|
||||
const CREATOR_PUBKEY = 'da5e0c1b646bdb13c2300f805b0ca3e5afe5b052c594ce78bac8978d21c3fa39'
|
||||
const startTime = Date.now()
|
||||
|
||||
export const adminRouter = new Hono()
|
||||
|
||||
function isCreator(pubkey: string | undefined): boolean {
|
||||
return pubkey === CREATOR_PUBKEY
|
||||
}
|
||||
|
||||
// All admin endpoints require creator pubkey in header
|
||||
adminRouter.use('*', async (c, next) => {
|
||||
const pubkey = c.req.header('x-pubkey')
|
||||
if (!isCreator(pubkey)) {
|
||||
return c.json({ error: 'Forbidden' }, 403)
|
||||
}
|
||||
await next()
|
||||
})
|
||||
|
||||
// GET /stats — live server stats
|
||||
adminRouter.get('/stats', async (c) => {
|
||||
const mem = process.memoryUsage()
|
||||
const dbSizeRow = sqlite.prepare("SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size()").get() as { size: number } | undefined
|
||||
const activeFights = await db.select({ count: count() }).from(schema.fights).where(eq(schema.fights.status, 'live'))
|
||||
const totalBots = await db.select({ count: count() }).from(schema.bots)
|
||||
const totalFights = await db.select({ count: count() }).from(schema.fights)
|
||||
const totalSats = await db.select({ total: sql<number>`coalesce(sum(${schema.fights.potSats}), 0)` }).from(schema.fights)
|
||||
|
||||
return c.json({
|
||||
uptime: Math.round((Date.now() - startTime) / 1000),
|
||||
rssBytes: mem.rss,
|
||||
heapUsed: mem.heapUsed,
|
||||
heapTotal: mem.heapTotal,
|
||||
dbSizeBytes: dbSizeRow?.size || 0,
|
||||
activeFights: activeFights[0]?.count || 0,
|
||||
activeSSE: getActiveSSECount(),
|
||||
totalBots: totalBots[0]?.count || 0,
|
||||
totalFights: totalFights[0]?.count || 0,
|
||||
totalSatsMoved: totalSats[0]?.total || 0,
|
||||
})
|
||||
})
|
||||
|
||||
// GET /bots — list all bots with admin details
|
||||
adminRouter.get('/bots', async (c) => {
|
||||
const bots = await db.select({
|
||||
id: schema.bots.id,
|
||||
name: schema.bots.name,
|
||||
eloRating: schema.bots.eloRating,
|
||||
wins: schema.bots.wins,
|
||||
losses: schema.bots.losses,
|
||||
tier: schema.bots.tier,
|
||||
isActive: schema.bots.isActive,
|
||||
archetype: schema.bots.archetype,
|
||||
botType: schema.bots.botType,
|
||||
consecutiveErrors: schema.bots.consecutiveErrors,
|
||||
lastFightAt: schema.bots.lastFightAt,
|
||||
createdAt: schema.bots.createdAt,
|
||||
}).from(schema.bots).orderBy(desc(schema.bots.eloRating))
|
||||
|
||||
return c.json({ bots })
|
||||
})
|
||||
|
||||
// POST /bots/:id/deactivate — deactivate a bot
|
||||
adminRouter.post('/bots/:id/deactivate', async (c) => {
|
||||
const botId = c.req.param('id')
|
||||
await db.update(schema.bots).set({ isActive: false }).where(eq(schema.bots.id, botId))
|
||||
return c.json({ ok: true })
|
||||
})
|
||||
|
||||
// POST /bots/:id/activate — reactivate a bot
|
||||
adminRouter.post('/bots/:id/activate', async (c) => {
|
||||
const botId = c.req.param('id')
|
||||
await db.update(schema.bots).set({ isActive: true }).where(eq(schema.bots.id, botId))
|
||||
return c.json({ ok: true })
|
||||
})
|
||||
|
||||
// POST /bots/:id/reset-elo — reset elo to 1200
|
||||
adminRouter.post('/bots/:id/reset-elo', async (c) => {
|
||||
const botId = c.req.param('id')
|
||||
await db.update(schema.bots).set({ eloRating: 1200, tier: 0 }).where(eq(schema.bots.id, botId))
|
||||
return c.json({ ok: true })
|
||||
})
|
||||
|
||||
// GET /fights — recent fights with details
|
||||
adminRouter.get('/fights', async (c) => {
|
||||
const limit = Math.min(parseInt(c.req.query('limit') || '50'), 200)
|
||||
const fights = await db.select({
|
||||
id: schema.fights.id,
|
||||
botAId: schema.fights.botAId,
|
||||
botBId: schema.fights.botBId,
|
||||
arena: schema.fights.arena,
|
||||
status: schema.fights.status,
|
||||
winnerId: schema.fights.winnerId,
|
||||
botAHp: schema.fights.botAHp,
|
||||
botBHp: schema.fights.botBHp,
|
||||
totalRounds: schema.fights.totalRounds,
|
||||
mode: schema.fights.mode,
|
||||
potSats: schema.fights.potSats,
|
||||
createdAt: schema.fights.createdAt,
|
||||
}).from(schema.fights).orderBy(desc(schema.fights.createdAt)).limit(limit)
|
||||
|
||||
return c.json({ fights })
|
||||
})
|
||||
Reference in New Issue
Block a user