frontera con upstream: lo del movil sale de los ficheros del dev

docs/FRONTERA_UPSTREAM.md mide como cambia upstream (39 releases de la 1.0.1 a
la 1.3.3; backend.js en 34, main_views en 31, OasisMobile.css en 21) y fija que
se toca: backend, middleware, modelos, server y traducciones de upstream, no;
vistas de upstream, solo una linea de gancho; hojas, vistas, router y modelos
propios, si.

Fase A, lo del 19/10:
- backend.js, workflows_model.js y modules_view.js vuelven a como estaban
- seccion apagada, /modules/enable y ?saved=1: en el router propio, que envuelve
  las redirecciones de upstream en vez de cambiar checkMod
- Modulos del movil en views/mobile_modules_view.js, servida por el router propio
- workflow propio "acampada" registrado desde models/fork_workflows.js en vez de
  redefinir "activists"
- votaciones, tribus, ajustes, borrar, pie y bienvenida: la logica en
  views/mobile_ui.js; en cada vista de upstream queda la llamada
This commit is contained in:
SITO 2026-10-19 13:04:38 +02:00
parent 9b391cfc3d
commit d04d99ece2
14 changed files with 571 additions and 267 deletions

View file

@ -20,6 +20,44 @@ const { stripDangerousTags } = require("./sanitizeHtml");
const { buildIceServers } = require("./turnCredentials");
const karvanModel = require('../models/karvan_model')({}); // FORK_IA_UX: salas efímeras en RAM (mensajes temporales + señalización WebRTC)
const { karvanView, karvanRoomView } = require("../views/karvan_view");
const compose = require("../server/node_modules/koa-compose");
const { isLocalPath } = require("./request_guards");
const { ALL_MODULES } = require("../models/workflows_model");
const { mobileModulesView, STEMS } = require("../views/mobile_modules_view");
require("../models/fork_workflows").register();
const isMobile = () => process.env.OASIS_MOBILE === '1';
// Seccion apagada: upstream responde con una redireccion a /modules sin decir de
// donde venia. Este envoltorio la ve pasar y anota que modulo era, para que la
// pantalla de Modulos del movil lo diga y ofrezca activarlo. La app es de una
// persona, asi que basta con recordar el ultimo unos segundos.
const SEGMENT_MOD = { tribe: 'tribes', vote: 'votes', document: 'docs', documents: 'docs', blog: 'blogs', event: 'events',
task: 'tasks', calendar: 'calendars', image: 'images', video: 'videos', audio: 'audios', bookmark: 'bookmarks',
file: 'files', poll: 'polls', report: 'reports', project: 'projects', shop: 'shops', job: 'jobs', campaign: 'campaigns',
podcast: 'podcasts', room: 'rooms', map: 'maps', transfer: 'transfers', pad: 'pads', chat: 'chats', torrent: 'torrents' };
const modOfPath = (p) => {
const seg = String(p || '').split('/')[1] || '';
const mod = STEMS[seg] ? seg : SEGMENT_MOD[seg];
return mod && ALL_MODULES.includes(mod) ? mod : null;
};
let lastBlocked = null;
const takeBlocked = () => {
const b = lastBlocked && Date.now() - lastBlocked.at < 15000 ? lastBlocked : null;
lastBlocked = null;
return b;
};
const wrapUpstream = async (ctx, next) => {
await next();
if (!isMobile() || ctx.status < 300 || ctx.status >= 400) return;
const to = String(ctx.response.get('Location') || '');
// guardar en Modulos: volver con el aviso de guardado
if (ctx.method === 'POST' && ctx.path === '/save-modules') { ctx.redirect('/modules?saved=1'); return; }
if (ctx.method === 'GET' && ctx.path !== '/modules' && /^(https?:\/\/[^/]+)?\/modules$/.test(to)) {
const mod = modOfPath(ctx.path);
if (mod) lastBlocked = { mod, path: ctx.path, at: Date.now() };
}
};
module.exports = ({ cooler, pull, pmModel, checkMod, getViewerId,
getConfig, saveConfig, isLoopbackRequest, sendErrorPage }) => {
@ -78,6 +116,22 @@ async function karvanInviteAuthor(roomId) {
const forkRouter = new koaRouter();
forkRouter
// Modulos en el movil: la vista propia (mobile_modules_view.js); en escritorio, la de upstream
.get('/modules', async (ctx, next) => {
if (!isMobile()) return next();
ctx.body = mobileModulesView({ blocked: takeBlocked(), saved: ctx.query.saved === '1' });
})
// activar un solo modulo desde el aviso de Modulos y volver a donde se queria ir
.post('/modules/enable', koaBody(), async (ctx) => {
if (!isLoopbackRequest(ctx)) { ctx.status = 403; ctx.body = ''; return; }
const mod = String((ctx.request.body || {}).mod || '').replace(/Mod$/, '');
if (!ALL_MODULES.includes(mod)) { ctx.redirect('/modules'); return; }
const cfg = getConfig();
cfg.modules[`${mod}Mod`] = 'on';
saveConfig(cfg);
const back = String((ctx.request.body || {}).returnTo || '');
ctx.redirect(isLocalPath(back) ? back : `/${mod}`);
})
// Configuracion ICE para las llamadas. Solo loopback: la credencial TURN va
// firmada con el secreto del nodo y no debe salir de la maquina.
.get('/karvan/ice', async (ctx) => {
@ -233,5 +287,5 @@ async function karvanInviteAuthor(roomId) {
setTimeout(() => process.exit(0), 500);
});
return forkRouter.routes();
return compose([wrapUpstream, forkRouter.routes()]);
};