Add Matrix credential upload route + local seed script

Closes the noVNC-unavailable gap: when Dokploy's reverse proxy strips
WebSocket upgrades for noVNC, there's no way to do a one-time
interactive Reddit login in the deployed container. Without that,
extractMatrixCredentials inside the container hits the /chat/ login
wall and never sniffs a bearer.

Add POST /api/debug/matrix-credentials that accepts a credentials JSON
body, persists it to data/matrix-credentials.json, and hot-swaps it
into the running ChatService via setMatrixClient. Also export
saveCredentials from the chat module so the route can reuse the same
cache-file logic as the in-container sniff.

scripts/seed-matrix-creds.ts opens a headed Chromium on the dev's
machine, waits for an interactive Reddit login (detected by polling
/api/me.json from inside the page), opens www.reddit.com/chat/, sniffs
the first authed matrix.redditspace.com request, resolves user_id via
/whoami, and POSTs the result to BOT_URL/api/debug/matrix-credentials.
End-to-end seeding without ever touching the container.

Usage:
  BOT_URL=https://red.apps.jbrunton.co.uk tsx scripts/seed-matrix-creds.ts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jjbruntonandClaude Opus 4.7 committed 2026-05-22 22:20:53 +01:00
1 parent e58bccf6b5
commit 0c7df6e1d3
3 files changed
+196

No files matched your search

+39
View File
@@ -2,6 +2,11 @@ import { Router } from 'express';
import path from 'path';
import fs from 'fs';
import type { WebContext } from '../context.js';
import {
MatrixClient,
saveCredentials,
type MatrixCredentials,
} from '../../services/chat/index.js';
export function createDebugRouter(ctx: WebContext): Router {
const router = Router();
@@ -60,6 +65,40 @@ export function createDebugRouter(ctx: WebContext): Router {
}
});
// Accepts a Matrix credentials JSON body, persists it to the cache file,
// and swaps it into the running ChatService. Lets us seed the deployed bot
// from a sniff run on a developer's machine when the in-container sniff
// can't reach an authed /chat/ surface and noVNC isn't an option.
//
// Body shape: { homeserver, accessToken, userId?, capturedAt? }
router.post('/debug/matrix-credentials', (req, res) => {
const body = (req.body ?? {}) as Partial<MatrixCredentials>;
if (!body.homeserver || !body.accessToken) {
res.status(400).json({ error: 'homeserver and accessToken are required' });
return;
}
const creds: MatrixCredentials = {
homeserver: body.homeserver,
accessToken: body.accessToken,
userId: body.userId ?? '',
capturedAt: body.capturedAt ?? Date.now(),
};
try {
saveCredentials(creds);
ctx.logger.info(
`[matrix] credentials uploaded via /api/debug/matrix-credentials (user=${creds.userId || 'unknown'})`,
);
if (ctx.services.chat) {
ctx.services.chat.setMatrixClient(new MatrixClient(creds, ctx.logger));
ctx.logger.info('[matrix] uploaded credentials swapped into running ChatService');
}
res.json({ success: true, homeserver: creds.homeserver, userId: creds.userId });
} catch (error) {
ctx.logger.error('Matrix credential upload failed:', error);
res.status(500).json({ error: String(error) });
}
});
router.post('/debug/inspect-dom', async (req, res) => {
if (!ctx.services.chat) {
res.status(500).json({ error: 'Chat service not initialized' });