Files
TheCriicom--omega-dashboard…/server/api/src/router.js
T
Cristian LaportaandClaude Opus 5.5 c0f4a3ab9c 2026.10.05.6: Community in home, Record, Trofei e Privacy in un menu solo
Sorgenti allineati alla versione pubblicata alle console (2026.10.05.6).

App: tessera Community in home prima dei giochi; scheda Record (tempo di
gioco di tutti gli iscritti: giocatori, giochi più giocati, maratone,
classifica per gioco); scheda Trofei con la classifica pubblica; trofei nei
profili e finestra dei set; menu Privacy unico con tutte le scelte; i trofei
della console e nome/icona dei giochi mandati al server (consync.c).

Server: record pubblici, schede dei giochi, importazione dei trofei
(trophyparse.js riconosce la tabella di TRPTITLE.DAT e conserva ciò che non
legge), tre scelte di privacy nuove, presenza che si spegne da sola dopo tre
minuti senza segnali. Informativa aggiornata (2026-10-05.2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 18:34:12 +02:00

84 lines
3.1 KiB
JavaScript

'use strict';
// Router guidato da config/endpoints.json: ogni rotta indica metodo, percorso,
// autenticazione e il gestore come "<modulo>.<funzione>" di src/endpoints/.
const fs = require('node:fs');
const path = require('node:path');
const { CODES } = require('./lang');
const handlers = {
info: require('./endpoints/info'),
auth: require('./endpoints/auth'),
account: require('./endpoints/account'),
social: require('./endpoints/social'),
hub: require('./endpoints/hub'),
people: require('./endpoints/people'),
wall: require('./endpoints/wall'),
groups: require('./endpoints/groups'),
stats: require('./endpoints/stats'),
records: require('./endpoints/records'),
games: require('./endpoints/games'),
trophies: require('./endpoints/trophies'),
voice: require('./endpoints/voice'),
media: require('./endpoints/media'),
browse: require('./endpoints/browse'),
radio: require('./endpoints/radio'),
store: require('./endpoints/store'),
legal: require('./endpoints/legal'),
updates: require('./endpoints/updates'),
site: require('./endpoints/site'),
admin: require('./endpoints/admin'),
diag: require('./endpoints/diag'),
};
// Le versioni dell'app uscite prima di /api/v1 chiamano /lab/v1: stesso gestore.
const API_PREFIX = '/api/v1/';
const LEGACY_PREFIX = '/lab/v1/';
function canonicalPath(pathname) {
return pathname.startsWith(LEGACY_PREFIX) ? API_PREFIX + pathname.slice(LEGACY_PREFIX.length) : pathname;
}
// "/api/v1/users/:onlineId" → regex con gruppi nominati. Il parametro ":lang"
// accetta solo i codici delle lingue supportate (lang.CODES), così "/:lang/"
// non cattura altri percorsi di un solo segmento.
const LANG_SOURCE = CODES.map((c) => c.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
function compile(pattern) {
const source = pattern
.split('/')
.map((seg) => (seg === ':lang' ? `(?<lang>${LANG_SOURCE})`
: seg.startsWith(':') ? `(?<${seg.slice(1)}>[^/]+)` : seg.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')))
.join('/');
return new RegExp(`^${source}/?$`);
}
function load(file = path.join(__dirname, '..', 'config', 'endpoints.json')) {
const list = JSON.parse(fs.readFileSync(file, 'utf8')).endpoints;
return list.map((e) => {
const [mod, fn] = e.handler.split('.');
const handler = handlers[mod] && handlers[mod][fn];
if (typeof handler !== 'function') {
throw new Error(`endpoints.json: gestore inesistente "${e.handler}" per ${e.method} ${e.path}`);
}
return { ...e, method: e.method.toUpperCase(), regex: compile(e.path), fn: handler };
});
}
// { route, params }, oppure { methodNotAllowed: [metodi] }, oppure null.
function match(routes, method, pathname) {
const target = canonicalPath(pathname);
const allowed = [];
for (const route of routes) {
const m = route.regex.exec(target);
if (!m) continue;
if (route.method === method) {
const params = {};
for (const [k, v] of Object.entries(m.groups || {})) params[k] = decodeURIComponent(v);
return { route, params };
}
allowed.push(route.method);
}
return allowed.length ? { methodNotAllowed: allowed } : null;
}
module.exports = { load, match };