Add admin shard control: config, status, town crier (phase 5)
- admin/uoLink.controller.js: GET /admin/uo-link/config (masked config + live health + ingestion stats from the socket/broadcaster); PUT to save base/ws URL + write-only token + protocol + enabled, which (re)starts or stops the WS ingest client and activity-logs the change; POST/DELETE /uo-link/towncrier to publish/remove town-crier messages; GET /uo-link/stream (admin SSE channel, full feed incl. audit/cheat). Mounted adminOnly with express-validator guards + #swagger annotations (new "Admin · Shard" tag, TownCrierRequest schema). - server.js: startup probe (checkUoLink) that logs reachability and warns loudly on a protocol mismatch when the integration is enabled. - client: api.admin uo-link methods; ShardAdmin.jsx control panel (status panel with ingestion stats, config form, town crier) modeled on DiscordBotAdmin; wired into AdminLayout nav/titles + the /admin/shard route. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011qPmpmVH1xGCiZoz9m9vW3
This commit is contained in:
@@ -5,6 +5,8 @@ const app = require('./app')
|
||||
const internalApp = require('./internalApp')
|
||||
const botScore = require('./middleware/botScore')
|
||||
const uoLinkSocket = require('./utils/uoLinkSocket')
|
||||
const uoLinkClient = require('./utils/uoLinkClient')
|
||||
const uoLinkConfig = require('./model/uoLinkConfig/uoLinkConfig.model')
|
||||
const shardBroadcast = require('./utils/shardBroadcast')
|
||||
const { ensureSchema, close } = require('./utils/db')
|
||||
const { seedDefaults, createInitialAdminFromEnv } = require('../db/seed')
|
||||
@@ -85,6 +87,7 @@ async function start() {
|
||||
// sidecar problem block server startup.
|
||||
try {
|
||||
await uoLinkSocket.start()
|
||||
await checkUoLink()
|
||||
} catch (err) {
|
||||
log.warn('uo-link socket failed to start (continuing)', { error: err.message })
|
||||
}
|
||||
@@ -92,6 +95,33 @@ async function start() {
|
||||
setupShutdown(server, internalServer)
|
||||
}
|
||||
|
||||
// Best-effort startup probe of the uo-link sidecar: if the integration is
|
||||
// enabled, log whether it is reachable and warn loudly on a protocol mismatch
|
||||
// (fail-fast visibility rather than silently mis-parsing a newer wire format).
|
||||
async function checkUoLink() {
|
||||
const config = await uoLinkConfig.getSafe()
|
||||
if (!config.enabled) return
|
||||
const health = await uoLinkClient.health()
|
||||
if (!health.ok) {
|
||||
log.warn('uo-link is enabled but the sidecar is unreachable at startup', {
|
||||
baseUrl: config.baseUrl,
|
||||
error: health.error || `status ${health.status}`,
|
||||
})
|
||||
return
|
||||
}
|
||||
if (health.data && health.data.protocol && health.data.protocol !== config.protocol) {
|
||||
log.error('uo-link PROTOCOL MISMATCH — pinned vs sidecar', {
|
||||
pinned: config.protocol,
|
||||
sidecar: health.data.protocol,
|
||||
})
|
||||
} else {
|
||||
log.info('uo-link sidecar reachable', {
|
||||
pluginConnected: health.data && health.data.plugin_connected,
|
||||
protocol: health.data && health.data.protocol,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function setupShutdown(server, internalServer) {
|
||||
let closing = false
|
||||
const shutdown = async (signal) => {
|
||||
|
||||
Reference in New Issue
Block a user