collabcentral/routes/info.js
Joseph B. McQueen 90eb889523 Phase R3: split Express routes into routes/*.js
Every app.get/post/delete handler used to live directly in index.js,
which turned the file into the routing layer, the state store, and
the wiring root all at once. This peels the handlers out into five
per-concern modules and leaves index.js as a small composition root.

routes/info.js
- GET /status
- GET /CollabCentral/:app/info

routes/auth.js
- GET /CollabCentral/:app/authUrl
- GET /CollabCentral/:app/oauth (owns SESSION_COOKIE_OPTIONS now)

routes/user.js
- GET /CollabCentral/:app/user/:scope/:action (groups list / find)
- POST /CollabCentral/:app/user/groups/{add,remove}

routes/admin.js
- GET/POST /CollabCentral/:app/admin/users
- DELETE /CollabCentral/:app/admin/users/:id
- Owns requireAdmin + adminUserRow + adminUsersList (private to
  the module now that no other caller needs them).

routes/jobs.js
- POST /CollabCentral/:app/jobs/:action (edit / runNow / schedule /
  cancel)
- GET  /CollabCentral/:app/jobs/list/:scope
- GET  /CollabCentral/:app/jobs/detail/:jobId
- Owns buildJob (moved from index.js) since nothing outside the
  jobs routes ever called it.

Wiring pattern: each module exports registerXxxRoutes(app, deps)
and receives its dependencies through a dep-bag (isAuthorized,
isAdmin, webex, translator, jobsPipeline, saveConfig, helpers,
authorized, jobs, upload, sharp, uuid, fs, logger, ...). No route
module reaches into module-level state — that stays owned by
index.js.

index.js: 969 -> 457 lines (72% smaller than the original 1,642).
Now contains only imports, state loading (config, jobs, authorized,
tokens, botProfiles, groupsCache), the lib factory instantiations,
Express startup + cron schedules, the four register* calls, and a
handful of small helpers (saveConfig, cleanCompletedJobs,
isAuthorized, isAdmin, logger).

Tests still 53/53 green. Smoke-tested every route (auth matrix +
unknown bot + jobs detail 404 + signed-out 401 vs non-admin 403)
against a live process; every case matches pre-R3 behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 08:43:16 -04:00

39 lines
1.7 KiB
JavaScript

// Two "no state to speak of" endpoints:
// GET /status — bare liveness probe (no auth).
// GET /CollabCentral/:app/info — bot metadata + session flags used
// by the frontend bootstrap to decide
// which page shell to render.
//
// registerInfoRoutes(app, { getBotProfile, isAuthorized, isAdmin })
export function registerInfoRoutes(app, deps) {
var getBotProfile = deps.getBotProfile;
var isAuthorized = deps.isAuthorized;
var isAdmin = deps.isAdmin;
app.get('/status', function (req, res) {
res.status(200).send({ status: "I'm alive!'" });
});
// Returns the bot's display metadata + whether the current cookie is
// authorized. The front-end uses this to drive page labels, icons,
// and the "you don't have access" view, so adding a new bot doesn't
// require any code changes in the HTML/JS.
app.get('/CollabCentral/:app/info', function (req, res) {
var appName = req.params.app;
var botCfg = req.botConfig; // populated by requireBot
var profile = getBotProfile(appName) || {};
var iconBase = botCfg.iconBase || appName;
var personId = req.cookies && req.cookies.id;
res.status(200).send({
appName: appName,
label: botCfg.label || appName,
displayName: profile.displayName || botCfg.label || appName,
iconUrl: '/CollabCentral/' + appName + '/' + iconBase + '.png',
faviconUrl: '/CollabCentral/' + appName + '/' + iconBase + '.ico',
avatarUrl: profile.avatar || null,
authorized: isAuthorized(appName, personId),
isAdmin: isAdmin(personId)
});
});
}