Session 18: Admin dashboard + Tank01 prefetch endpoint (1443 tests)
This commit is contained in:
@@ -22,6 +22,7 @@ const shareCardRoutes = require('./routes/shareCard');
|
||||
const pushRoutes = require('./routes/push');
|
||||
const gradingRoutes = require('./routes/grading');
|
||||
const correctionRoutes = require('./routes/corrections');
|
||||
const internalRoutes = require('./routes/internal');
|
||||
const { missionHeader } = require('./middleware/mission');
|
||||
|
||||
const app = express();
|
||||
@@ -137,6 +138,11 @@ app.use('/api/grading', express.json({ limit: '10mb' }), gradingRoutes);
|
||||
app.use('/api/grading', express.json({ limit: '256kb' }), correctionRoutes);
|
||||
const widgetRoutes = require('./routes/widget');
|
||||
app.use('/api/widget', widgetRoutes);
|
||||
// Session 18 — internal ops endpoints (admin dashboard triggers,
|
||||
// shared-key auth via `VYNDR_INTERNAL_KEY`). Never reachable from
|
||||
// the public surface; the Next.js admin route proxies through with
|
||||
// the key kept server-side.
|
||||
app.use('/api/internal', internalRoutes);
|
||||
|
||||
// Session 10 — Sentry's Express error handler catches uncaught
|
||||
// errors from every route mounted above. Must come AFTER routes but
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Internal ops endpoints (Session 18).
|
||||
*
|
||||
* Reachable only with the shared `VYNDR_INTERNAL_KEY` — never
|
||||
* exposed to end users. The admin dashboard wires the Tank01
|
||||
* prefetch button to POST here through the Next.js server (the
|
||||
* key never touches a browser).
|
||||
*
|
||||
* Deviation from spec: the spec suggested `execSync('node scripts/tank01-prefetch.js')`.
|
||||
* We import the module instead — same behavior, but in-process and
|
||||
* testable. The module already exposes `main(argv)` which returns
|
||||
* the same summary object the spec expected to parse out of stdout.
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const { requireInternalAuth } = require('../middleware/internalAuth');
|
||||
const tank01Prefetch = require('../../scripts/tank01-prefetch');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.use(requireInternalAuth({ loopbackOnly: false }));
|
||||
|
||||
/**
|
||||
* POST /api/internal/prefetch/tank01
|
||||
*
|
||||
* Body (all optional):
|
||||
* { max?: number, sports?: string[]|string, dryRun?: boolean }
|
||||
*
|
||||
* Builds an argv array equivalent to the CLI form and hands it to
|
||||
* the prefetch module. Returns the module's summary on success.
|
||||
*/
|
||||
router.post('/prefetch/tank01', async (req, res) => {
|
||||
const body = (req.body && typeof req.body === 'object') ? req.body : {};
|
||||
|
||||
// Build argv. `main()` parses its own args, so all the validation
|
||||
// (numeric bounds, allowed sports) stays in one place — we just
|
||||
// translate JSON shapes into CLI flags.
|
||||
const argv = ['node', 'scripts/tank01-prefetch.js'];
|
||||
|
||||
if (Number.isFinite(body.max) && body.max > 0) {
|
||||
argv.push(`--max=${Math.floor(body.max)}`);
|
||||
}
|
||||
if (body.dryRun === true) {
|
||||
argv.push('--dry-run');
|
||||
}
|
||||
if (body.sports) {
|
||||
const sportsList = Array.isArray(body.sports)
|
||||
? body.sports.join(',')
|
||||
: String(body.sports);
|
||||
argv.push(`--sports=${sportsList}`);
|
||||
}
|
||||
|
||||
try {
|
||||
const summary = await tank01Prefetch.main(argv);
|
||||
return res.json({ ok: true, summary });
|
||||
} catch (err) {
|
||||
const message = err && err.message ? err.message : String(err);
|
||||
console.error('[internal/prefetch/tank01] failed:', message);
|
||||
return res.status(500).json({ ok: false, error: message });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user