diff --git a/src/backend/backend.js b/src/backend/backend.js index f5b56be..aebba6d 100644 --- a/src/backend/backend.js +++ b/src/backend/backend.js @@ -6162,19 +6162,6 @@ router ctx.redirect(safeReturnTo(ctx, '/forum', ['/forum'])); } }) - .get('/legacy/export', async (ctx) => { - if (!isLoopbackRequest(ctx)) { ctx.status = 403; ctx.body = ''; return; } - const pw = ctx.query && ctx.query.password; - if (!pw || pw.length < 32) return ctx.redirect('/legacy'); - try { - const { filename, data } = await legacyModel.exportData({ password: pw }); - ctx.set('Content-Type', 'application/octet-stream'); - ctx.set('Content-Disposition', `attachment; filename="${filename}"`); - ctx.set('Content-Length', String(data.length)); - ctx.body = data; - try { onboardingModel.markStep('backup'); } catch (_) {} - } catch (error) { sendErrorPage(ctx, error.message); } - }) .post('/legacy/import', koaBody({ multipart: true, formidable: { @@ -7975,28 +7962,6 @@ router console.log("oasis@version: running install.sh...", shOut, shErr); safeRefererRedirect(ctx, '/settings'); }) - .get("/settings/bottombar", async (ctx) => { - ctx.body = require("../views/main_views").bottomBarPickerView(); - }) - .post("/settings/bottombar", koaBody(), async (ctx) => { - const cfg = getConfig(); - const MAX = 4; - const body = ctx.request.body || {}; - const action = String(body.action || '').trim(); - const key = String(body.key || '').trim(); - const catalog = require("../views/main_views").PINNABLE_MODULES || []; - let pins = Array.isArray(cfg.bottomBarPins) ? cfg.bottomBarPins.slice() : []; - if (action === 'add') { - if (key && catalog.some(m => m.key === key) && !pins.includes(key) && pins.length < MAX) pins.push(key); - } else if (action === 'remove') { - pins = pins.filter(k => k !== key); - } else if (action === 'clear') { - pins = []; - } - cfg.bottomBarPins = pins.slice(0, MAX); - saveConfig(cfg); - ctx.redirect('/settings/bottombar'); - }) .post("/settings/theme", koaBody(), async (ctx) => { const theme = String(ctx.request.body.theme || "").trim(), cfg = getConfig(); cfg.themes.current = theme || "Dark-SNH"; @@ -8691,7 +8656,9 @@ const middleware = [ await next(); }, // rutas propias de la rama (Karvan): fuera de la cadena de upstream - require('./fork_routes')({ cooler, pull, pmModel, checkMod, getViewerId }), + require('./fork_routes')({ cooler, pull, pmModel, checkMod, getViewerId, + getConfig, saveConfig, legacyModel, onboardingModel, + isLoopbackRequest, sendErrorPage }), routes, ]; const app = http({ host, port, middleware, allowHost: config.allowHost }); diff --git a/src/backend/fork_routes.js b/src/backend/fork_routes.js index fe5288f..e2cd175 100644 --- a/src/backend/fork_routes.js +++ b/src/backend/fork_routes.js @@ -17,7 +17,9 @@ const { stripDangerousTags } = require("./sanitizeHtml"); 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"); -module.exports = ({ cooler, pull, pmModel, checkMod, getViewerId }) => { +module.exports = ({ cooler, pull, pmModel, checkMod, getViewerId, + getConfig, saveConfig, legacyModel, onboardingModel, + isLoopbackRequest, sendErrorPage }) => { // FORK_IA_UX Fase 2 (cross-device): relay de MENSAJES de Karvan por privados SSB. Funciona vía un pub // (el privado se replica al otro móvil); los SIGNAL de WebRTC NO se relayan (serían demasiados/lentos por el log). // No toca el arranque del SSB. Ingesta por stream EN VIVO (no polling) → eficiente. @@ -155,6 +157,41 @@ async function karvanInviteAuthor(roomId) { if (!checkMod(ctx, 'karvanMod')) { ctx.status = 403; ctx.body = { error: 'off' }; return; } const r = karvanModel.getSignals(ctx.params.id, { forId: ctx.query.for || '', since: ctx.query.since || 0 }); ctx.body = r || { signals: [], members: [] }; + }) + .get("/settings/bottombar", async (ctx) => { + ctx.body = require("../views/main_views").bottomBarPickerView(); + }) + .post("/settings/bottombar", koaBody(), async (ctx) => { + const cfg = getConfig(); + const MAX = 4; + const body = ctx.request.body || {}; + const action = String(body.action || '').trim(); + const key = String(body.key || '').trim(); + const catalog = require("../views/main_views").PINNABLE_MODULES || []; + let pins = Array.isArray(cfg.bottomBarPins) ? cfg.bottomBarPins.slice() : []; + if (action === 'add') { + if (key && catalog.some(m => m.key === key) && !pins.includes(key) && pins.length < MAX) pins.push(key); + } else if (action === 'remove') { + pins = pins.filter(k => k !== key); + } else if (action === 'clear') { + pins = []; + } + cfg.bottomBarPins = pins.slice(0, MAX); + saveConfig(cfg); + ctx.redirect('/settings/bottombar'); + }) + .get('/legacy/export', async (ctx) => { + if (!isLoopbackRequest(ctx)) { ctx.status = 403; ctx.body = ''; return; } + const pw = ctx.query && ctx.query.password; + if (!pw || pw.length < 32) return ctx.redirect('/legacy'); + try { + const { filename, data } = await legacyModel.exportData({ password: pw }); + ctx.set('Content-Type', 'application/octet-stream'); + ctx.set('Content-Disposition', `attachment; filename="${filename}"`); + ctx.set('Content-Length', String(data.length)); + ctx.body = data; + try { onboardingModel.markStep('backup'); } catch (_) {} + } catch (error) { sendErrorPage(ctx, error.message); } }); return forkRouter.routes();