coherencia con la guia: lo de movil del dev vuelve a ser suyo
Comparando con el nodejs-project de la APK oficial (la referencia buena para el movil, no el git del dev) salia que parte de lo que se saco de backend.js era codigo de movil del propio dev: apagar los modulos que no van, cortar rutas, retocar el HTML, las descargas de la copia de seguridad y ocultar tema y workflow en la bienvenida. - backend.js vuelve a ser su version de movil mas dos lineas (montar nuestro router); modules_view.js, su version de movil - mobile_layer.js se queda con lo nuestro: avatar, filtro y lanzador - la guia lista las 17 diferencias que quedan con la APK oficial (561 lineas) y explica que hay que comparar con la APK, no con el git
This commit is contained in:
parent
b65dde1263
commit
6eafe5ee18
8 changed files with 124 additions and 147 deletions
|
|
@ -84,9 +84,8 @@ Aquí se puede hacer lo que haga falta.
|
||||||
- `src/views/hive_views.js`: lanzador y barras de arriba y abajo.
|
- `src/views/hive_views.js`: lanzador y barras de arriba y abajo.
|
||||||
- `src/views/mobile_icons.js`: los iconos.
|
- `src/views/mobile_icons.js`: los iconos.
|
||||||
- `src/backend/fork_routes.js`: nuestro router. Va justo delante del del dev.
|
- `src/backend/fork_routes.js`: nuestro router. Va justo delante del del dev.
|
||||||
- `src/backend/mobile_layer.js`: lo que el móvil necesita del backend (rutas que se
|
- `src/backend/mobile_layer.js`: lo que nuestra interfaz necesita del backend
|
||||||
cortan, módulos que no van, avatar y lanzador, retoques del HTML, descargas de la
|
(avatar, filtro y lanzador de la barra de arriba). La monta `fork_routes.js`.
|
||||||
copia de seguridad). La monta `fork_routes.js`.
|
|
||||||
- `src/models/fork_workflows.js`: nuestros workflows.
|
- `src/models/fork_workflows.js`: nuestros workflows.
|
||||||
- `src/client/assets/translations/i18n_fork.js`: nuestros textos.
|
- `src/client/assets/translations/i18n_fork.js`: nuestros textos.
|
||||||
- `src/client/assets/images/ux-*.svg`: nuestras imágenes.
|
- `src/client/assets/images/ux-*.svg`: nuestras imágenes.
|
||||||
|
|
@ -156,7 +155,8 @@ nombre es el de la ruta: `/tribes` es `tribes`.
|
||||||
|
|
||||||
## Cuando sale versión nueva del dev
|
## Cuando sale versión nueva del dev
|
||||||
|
|
||||||
1. Lo suyo se coge tal cual.
|
1. Lo suyo se coge tal cual, y para los ficheros con versión de móvil, la de su APK
|
||||||
|
oficial (`assets/nodejs-project.zip`), no la de su git.
|
||||||
2. En sus vistas se vuelven a poner los enganches. El `grep -n mobileUi` sobre la
|
2. En sus vistas se vuelven a poner los enganches. El `grep -n mobileUi` sobre la
|
||||||
versión anterior de la rama dice dónde iban.
|
versión anterior de la rama dice dónde iban.
|
||||||
3. Se repite la medición de `docs/FRONTERA_UPSTREAM.md`: qué ha tocado y si ha
|
3. Se repite la medición de `docs/FRONTERA_UPSTREAM.md`: qué ha tocado y si ha
|
||||||
|
|
@ -188,19 +188,25 @@ inspeccionar la WebView desde `chrome://inspect`.
|
||||||
|
|
||||||
## Lo que todavía está mal
|
## Lo que todavía está mal
|
||||||
|
|
||||||
Cosas de antes de estas normas que aún no las cumplen, por orden de lo que más
|
Para el móvil, la referencia es el `nodejs-project` de la APK oficial del dev, no su
|
||||||
duele:
|
git: él mete cambios de móvil (en `backend.js`, `SSB_server.js`, `phone_module.js`...)
|
||||||
|
que solo están en la APK. Comparado con eso, la rama cambia 17 ficheros suyos, 561
|
||||||
|
líneas (19/10/2026). Por tamaño:
|
||||||
|
|
||||||
1. En `backend.js` quedan 9 trozos pequeños (21 líneas) que no se pueden sacar desde
|
- `backend/blobHandler.js` (190): limpiar los metadatos de las fotos también en
|
||||||
fuera: tres expresiones regulares que con el Node 18 de Android tumbaban el
|
Android. Es una función nuestra metida en su fichero; habría que proponérsela al dev.
|
||||||
arranque (esto habría que contárselo al dev), no abrir el navegador al arrancar,
|
- `configs/oasis-config.json` (87) y `configs/config-manager.js` (18): la config de
|
||||||
no repetir fotos ya limpiadas, el modo chats y la línea que monta nuestro router.
|
serie del móvil (tema Acampada, workflow acampada, barra de abajo).
|
||||||
Lo demás ya está en `src/backend/mobile_layer.js`.
|
- `views/main_views.js` (62), `settings_view.js` (34), `welcome_view.js` (11),
|
||||||
2. En `workflows_model.js` queda la logica de skins del movil (`themeOf`, `sameTheme`):
|
`tribes_view.js` (9), `vote_view.js` (7): enganches a `mobile_ui.js` y el orden de
|
||||||
`settings_view.js` coge `currentWorkflow` al cargar y no se puede envolver desde
|
carga de las hojas. Es lo que la guía permite, pero hay que revisarlo en cada versión.
|
||||||
fuera sin tocar esa vista. Es poco, pero es del dev.
|
- `main.js` (45): DNS del sistema primero, para respetar la VPN.
|
||||||
3. `src/server/phone_module.js` escribe el estado de la llamada en
|
- `server/ssb_config.js` (30): varias identidades en el mismo móvil.
|
||||||
`.phone_state.json` para que la app Android enseñe la notificación (9 líneas). El
|
- `models/workflows_model.js` (17): la lógica de skins del móvil (`themeOf`,
|
||||||
módulo del dev no avisa hacia fuera de los cambios de estado; habría que pedírselo.
|
`sameTheme`). `settings_view.js` coge `currentWorkflow` al cargar y no se puede
|
||||||
4. `src/server/SSB_server.js` quita la publicación de la dirección del banco al
|
envolver desde fuera.
|
||||||
arrancar. Hay que mirar por qué se quitó y si sigue haciendo falta.
|
- `styles/mobile.css` (14), `bookmark_view.js` (13), `forum_view.js` (7),
|
||||||
|
`translations/i18n.js` (5, la mezcla con `i18n_fork.js`): arreglos sueltos de antes.
|
||||||
|
- `backend/backend.js` (2): la línea que monta nuestro router.
|
||||||
|
|
||||||
|
Cuando el dev saque versión, se repite esta comparación con su APK nueva.
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,11 @@ nada, pero son peso muerto y confunden.
|
||||||
`acampada` se registran desde `models/fork_workflows.js`. La config de serie del
|
`acampada` se registran desde `models/fork_workflows.js`. La config de serie del
|
||||||
movil coincide con `acampada`. Queda en `workflows_model.js` la logica de skins del
|
movil coincide con `acampada`. Queda en `workflows_model.js` la logica de skins del
|
||||||
movil.
|
movil.
|
||||||
|
- Correccion (19/10, noche): para el movil la referencia buena es el nodejs-project de
|
||||||
|
la APK oficial, no el git del dev. Parte de lo que la fase C saco de backend.js era
|
||||||
|
codigo de movil del propio dev; backend.js vuelve a ser su version de movil mas dos
|
||||||
|
lineas (montar nuestro router), y modules_view.js su version de movil. Contra la APK
|
||||||
|
oficial la rama cambia 17 ficheros y 561 lineas; la lista esta en la guia.
|
||||||
- Fuera Karvan (19/10): el dev ya trae Telefono y Salas, asi que se quita el modulo
|
- Fuera Karvan (19/10): el dev ya trae Telefono y Salas, asi que se quita el modulo
|
||||||
propio de salas y llamadas (modelo, vista, JS, CSS, router, pruebas, textos).
|
propio de salas y llamadas (modelo, vista, JS, CSS, router, pruebas, textos).
|
||||||
`Clear-SNH.css` y `modules_view.js` vuelven a ser identicos a los del dev y la linea
|
`Clear-SNH.css` y `modules_view.js` vuelven a ser identicos a los del dev y la linea
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 54 KiB |
|
|
@ -161,7 +161,7 @@ def esquema_rama():
|
||||||
((400, 210, 720, 400), "PANTALLAS",
|
((400, 210, 720, 400), "PANTALLAS",
|
||||||
["mobile_ui.js y las vistas", "mobile_*.js. En una vista", "del dev, como mucho una", "línea que las llama"], NEON),
|
["mobile_ui.js y las vistas", "mobile_*.js. En una vista", "del dev, como mucho una", "línea que las llama"], NEON),
|
||||||
((740, 210, 1060, 400), "RUTAS",
|
((740, 210, 1060, 400), "RUTAS",
|
||||||
["fork_routes.js y", "mobile_layer.js, delante", "del router del dev: lo que", "no atienden pasa al suyo"], NEON),
|
["fork_routes.js, delante", "del router del dev: lo", "que no atiende pasa al", "suyo"], NEON),
|
||||||
((1080, 210, 1340, 400), "ANDROID",
|
((1080, 210, 1340, 400), "ANDROID",
|
||||||
["la app del dev tal cual,", "con nuestro backend", "dentro y paquete", "propio"], NEON),
|
["la app del dev tal cual,", "con nuestro backend", "dentro y paquete", "propio"], NEON),
|
||||||
]
|
]
|
||||||
|
|
@ -173,7 +173,7 @@ def esquema_rama():
|
||||||
caja(d, (60, 500, 1340, 690), "OASIS DEL DEV: NO SE TOCA",
|
caja(d, (60, 500, 1340, 690), "OASIS DEL DEV: NO SE TOCA",
|
||||||
["backend.js y su middleware · los modelos (lo que se publica y cómo se cuenta) · SSB y su arranque",
|
["backend.js y su middleware · los modelos (lo que se publica y cómo se cuenta) · SSB y su arranque",
|
||||||
"las traducciones · style.css y mobile.css · los temas, también OasisMobile.css",
|
"las traducciones · style.css y mobile.css · los temas, también OasisMobile.css",
|
||||||
"De 6.400 líneas cambiadas en 43 ficheros suyos (octubre) a 1.045 en 40. Ver GUIA_DE_DESARROLLO.md"], BG)
|
"Comparado con su APK oficial, la rama cambia 17 ficheros suyos y 561 líneas. Ver GUIA_DE_DESARROLLO.md"], BG)
|
||||||
out.save(HERE / "esquema-rama.png", optimize=True)
|
out.save(HERE / "esquema-rama.png", optimize=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1644,6 +1644,17 @@ debug("Current configuration: %O", config);
|
||||||
debug(`You can save the above to ${defaultConfigFile} to make \
|
debug(`You can save the above to ${defaultConfigFile} to make \
|
||||||
these settings the default. See the readme for details.`);
|
these settings the default. See the readme for details.`);
|
||||||
const { saveConfig, getConfig } = require('../configs/config-manager');
|
const { saveConfig, getConfig } = require('../configs/config-manager');
|
||||||
|
if (process.env.OASIS_MOBILE === '1') {
|
||||||
|
try {
|
||||||
|
const cfg = getConfig();
|
||||||
|
const forced = ['ai', 'aiNav', 'dev', 'games', 'logs', 'maps', 'pixelia'].filter(m => cfg.modules && cfg.modules[`${m}Mod`] !== 'off');
|
||||||
|
let dirty = false;
|
||||||
|
if (forced.length) { forced.forEach(m => { cfg.modules[`${m}Mod`] = 'off'; }); dirty = true; }
|
||||||
|
const w = cfg.wallet || {};
|
||||||
|
if (w.url === 'http://127.0.0.1:17474' && w.user === 'oasis' && w.pass === 'testpass123') { cfg.wallet = { ...w, url: 'http://127.0.0.1:7474', user: '', pass: '' }; dirty = true; }
|
||||||
|
if (dirty) saveConfig(cfg);
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
const oasisCheckPath = "/.well-known/oasis";
|
const oasisCheckPath = "/.well-known/oasis";
|
||||||
process.on("uncaughtException", function (err) {
|
process.on("uncaughtException", function (err) {
|
||||||
if (err["code"] === "EADDRINUSE") {
|
if (err["code"] === "EADDRINUSE") {
|
||||||
|
|
@ -5103,6 +5114,7 @@ router
|
||||||
})
|
})
|
||||||
|
|
||||||
.get('/blockexplorer', async (ctx) => {
|
.get('/blockexplorer', async (ctx) => {
|
||||||
|
if (process.env.OASIS_MOBILE === '1') { ctx.redirect('/'); return; }
|
||||||
const userId = getViewerId();
|
const userId = getViewerId();
|
||||||
const query = ctx.query || {};
|
const query = ctx.query || {};
|
||||||
const search = {
|
const search = {
|
||||||
|
|
@ -5153,6 +5165,7 @@ router
|
||||||
ctx.body = renderBlockchainView(blockchainData, filter, userId, search, { inspect, censusBlocks });
|
ctx.body = renderBlockchainView(blockchainData, filter, userId, search, { inspect, censusBlocks });
|
||||||
})
|
})
|
||||||
.get('/blockexplorer/block/:id', async (ctx) => {
|
.get('/blockexplorer/block/:id', async (ctx) => {
|
||||||
|
if (process.env.OASIS_MOBILE === '1') { ctx.redirect('/'); return; }
|
||||||
const userId = getViewerId();
|
const userId = getViewerId();
|
||||||
const query = ctx.query || {};
|
const query = ctx.query || {};
|
||||||
const search = {
|
const search = {
|
||||||
|
|
@ -8473,8 +8486,8 @@ router
|
||||||
const available = {
|
const available = {
|
||||||
profile: true,
|
profile: true,
|
||||||
ux: true,
|
ux: true,
|
||||||
workflow: isLoopbackRequest(ctx),
|
workflow: process.env.OASIS_MOBILE !== '1' && isLoopbackRequest(ctx),
|
||||||
theme: true,
|
theme: process.env.OASIS_MOBILE !== '1',
|
||||||
community: checkMod(ctx, 'invitesMod'),
|
community: checkMod(ctx, 'invitesMod'),
|
||||||
greeting: checkMod(ctx, 'feedMod')
|
greeting: checkMod(ctx, 'feedMod')
|
||||||
};
|
};
|
||||||
|
|
@ -11509,6 +11522,40 @@ router
|
||||||
try { await forumModel.removeOpenInvite(ctx.params.id); } catch (_) { actionFail(ctx); return; }
|
try { await forumModel.removeOpenInvite(ctx.params.id); } catch (_) { actionFail(ctx); return; }
|
||||||
ctx.redirect(safeReturnTo(ctx, `/forum/${encodeURIComponent(ctx.params.id)}`, ['/forum']));
|
ctx.redirect(safeReturnTo(ctx, `/forum/${encodeURIComponent(ctx.params.id)}`, ['/forum']));
|
||||||
})
|
})
|
||||||
|
.get('/backup/keys/export', async (ctx) => {
|
||||||
|
if (!isLoopbackRequest(ctx)) { ctx.status = 403; ctx.body = ''; return; }
|
||||||
|
if (process.env.OASIS_MOBILE !== '1') { ctx.redirect('/backup'); return; }
|
||||||
|
const pw = String(ctx.query.password || '');
|
||||||
|
if (!pw || pw.length < 32) return ctx.redirect('/backup');
|
||||||
|
try {
|
||||||
|
const { filename, data } = await backupModel.exportKeys({ 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); }
|
||||||
|
})
|
||||||
|
.get('/backup/export', async (ctx) => {
|
||||||
|
if (!isLoopbackRequest(ctx)) { ctx.status = 403; ctx.body = ''; return; }
|
||||||
|
if (process.env.OASIS_MOBILE !== '1') { ctx.redirect('/backup'); return; }
|
||||||
|
if (!checkMod(ctx, 'backupMod')) return ctx.redirect('/modules');
|
||||||
|
const b = ctx.query || {};
|
||||||
|
const pw = String(b.password || '');
|
||||||
|
if (pw.length < 32) return ctx.redirect('/backup');
|
||||||
|
const rawMods = b.modules === undefined ? [] : (Array.isArray(b.modules) ? b.modules : [b.modules]);
|
||||||
|
const outPath = path.join(os.tmpdir(), `oasis-backup-${process.pid}-${Date.now()}.oasisbk`);
|
||||||
|
try {
|
||||||
|
const res = await backupModel.createBackup({ scope: b.scope, blobs: b.blobs, modules: rawMods, since: b.since }, pw, outPath);
|
||||||
|
ctx.set('Content-Type', 'application/octet-stream');
|
||||||
|
ctx.set('Content-Disposition', `attachment; filename="${res.filename}"`);
|
||||||
|
ctx.set('Content-Length', String(res.bytes));
|
||||||
|
const stream = fs.createReadStream(outPath);
|
||||||
|
stream.on('close', () => { try { fs.unlinkSync(outPath); } catch (_) {} });
|
||||||
|
ctx.body = stream;
|
||||||
|
try { onboardingModel.markStep('backup'); } catch (_) {}
|
||||||
|
} catch (error) { try { fs.unlinkSync(outPath); } catch (_) {} sendErrorPage(ctx, error.message); }
|
||||||
|
})
|
||||||
.post('/backup/keys/export', koaBody(), async (ctx) => {
|
.post('/backup/keys/export', koaBody(), async (ctx) => {
|
||||||
if (!isLoopbackRequest(ctx)) { ctx.status = 403; ctx.body = ''; return; }
|
if (!isLoopbackRequest(ctx)) { ctx.status = 403; ctx.body = ''; return; }
|
||||||
const pw = ctx.request.body.password;
|
const pw = ctx.request.body.password;
|
||||||
|
|
@ -14601,6 +14648,7 @@ router
|
||||||
})
|
})
|
||||||
.post("/update", koaBody(), async (ctx) => {
|
.post("/update", koaBody(), async (ctx) => {
|
||||||
if (!isLoopbackRequest(ctx)) { ctx.status = 403; ctx.body = ''; return; }
|
if (!isLoopbackRequest(ctx)) { ctx.status = 403; ctx.body = ''; return; }
|
||||||
|
if (process.env.OASIS_MOBILE === '1') { ctx.redirect('/settings'); return; }
|
||||||
const exec = require("node:util").promisify(require("node:child_process").exec);
|
const exec = require("node:util").promisify(require("node:child_process").exec);
|
||||||
const repoRoot = path.resolve(__dirname, '..', '..');
|
const repoRoot = path.resolve(__dirname, '..', '..');
|
||||||
const { stdout, stderr } = await exec("git reset --hard && git pull", { cwd: repoRoot });
|
const { stdout, stderr } = await exec("git reset --hard && git pull", { cwd: repoRoot });
|
||||||
|
|
@ -15127,6 +15175,7 @@ router
|
||||||
})
|
})
|
||||||
.post("/modules/preset", koaBody(), async (ctx) => {
|
.post("/modules/preset", koaBody(), async (ctx) => {
|
||||||
if (!isLoopbackRequest(ctx)) { ctx.status = 403; ctx.body = ''; return; }
|
if (!isLoopbackRequest(ctx)) { ctx.status = 403; ctx.body = ''; return; }
|
||||||
|
if (process.env.OASIS_MOBILE === '1') { ctx.redirect('/modules'); return; }
|
||||||
const ALL_MODULES = workflowsModel.ALL_MODULES;
|
const ALL_MODULES = workflowsModel.ALL_MODULES;
|
||||||
const PRESETS = workflowsModel.PRESETS;
|
const PRESETS = workflowsModel.PRESETS;
|
||||||
const preset = String(ctx.request.body.preset || '');
|
const preset = String(ctx.request.body.preset || '');
|
||||||
|
|
@ -15138,10 +15187,14 @@ router
|
||||||
ctx.redirect('/modules');
|
ctx.redirect('/modules');
|
||||||
})
|
})
|
||||||
.post("/save-modules", koaBody(), async (ctx) => {
|
.post("/save-modules", koaBody(), async (ctx) => {
|
||||||
|
const MOBILE_UNAVAILABLE_MODS = ['ai', 'aiNav', 'dev', 'games', 'logs', 'maps', 'pixelia'];
|
||||||
if (!isLoopbackRequest(ctx)) { ctx.status = 403; ctx.body = ''; return; }
|
if (!isLoopbackRequest(ctx)) { ctx.status = 403; ctx.body = ''; return; }
|
||||||
const modules = workflowsModel.ALL_MODULES;
|
const modules = workflowsModel.ALL_MODULES;
|
||||||
const cfg = getConfig();
|
const cfg = getConfig();
|
||||||
modules.forEach(mod => cfg.modules[`${mod}Mod`] = ctx.request.body[`${mod}Form`] === 'on' ? 'on' : 'off');
|
modules.forEach(mod => cfg.modules[`${mod}Mod`] = ctx.request.body[`${mod}Form`] === 'on' ? 'on' : 'off');
|
||||||
|
if (process.env.OASIS_MOBILE === '1') {
|
||||||
|
MOBILE_UNAVAILABLE_MODS.forEach(mod => cfg.modules[`${mod}Mod`] = 'off');
|
||||||
|
}
|
||||||
saveConfig(cfg);
|
saveConfig(cfg);
|
||||||
ctx.redirect(`/modules`);
|
ctx.redirect(`/modules`);
|
||||||
})
|
})
|
||||||
|
|
@ -15411,6 +15464,14 @@ const kickRefresh = () => {
|
||||||
};
|
};
|
||||||
setTimeout(() => { if (!sharedState.getLastRefresh()) kickRefresh(); }, 5000);
|
setTimeout(() => { if (!sharedState.getLastRefresh()) kickRefresh(); }, 5000);
|
||||||
const middleware = [
|
const middleware = [
|
||||||
|
async (ctx, next) => {
|
||||||
|
await next();
|
||||||
|
if (process.env.OASIS_MOBILE !== '1' || typeof ctx.body !== 'string' || !String(ctx.type || '').includes('html')) return;
|
||||||
|
ctx.body = ctx.body.replace(/<video\b[^>]*>/g, (tag) => tag.replace(/\bsrc="(\/blob\/[^"#]+)"/, 'src="$1#t=0.1"'));
|
||||||
|
const tab = ['/mentions', '/inbox', '/phone', '/data', '/tags', '/search', '/settings'].find((t) => ctx.path === t || ctx.path.startsWith(t + '/'));
|
||||||
|
const headerEnd = ctx.body.indexOf('<div class="main-content">');
|
||||||
|
if (tab && headerEnd > 0) ctx.body = ctx.body.slice(0, headerEnd).replace(`<a href="${tab}"`, `<a href="${tab}" aria-current="page"`) + ctx.body.slice(headerEnd);
|
||||||
|
},
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
if (ctx.method === 'GET' && /^recent$/i.test(String(ctx.query.filter || '')) && ['q', 'search'].some(k => String(ctx.query[k] || '').trim())) ctx.query = { ...ctx.query, filter: ctx.query.filter === 'RECENT' ? 'ALL' : 'all' };
|
if (ctx.method === 'GET' && /^recent$/i.test(String(ctx.query.filter || '')) && ['q', 'search'].some(k => String(ctx.query[k] || '').trim())) ctx.query = { ...ctx.query, filter: ctx.query.filter === 'RECENT' ? 'ALL' : 'all' };
|
||||||
await next();
|
await next();
|
||||||
|
|
@ -15515,8 +15576,7 @@ const middleware = [
|
||||||
await next();
|
await next();
|
||||||
},
|
},
|
||||||
// la rama movil: su router y su capa de backend (ver GUIA_DE_DESARROLLO.md)
|
// la rama movil: su router y su capa de backend (ver GUIA_DE_DESARROLLO.md)
|
||||||
require('./fork_routes')({ checkMod, getViewerId, getConfig, saveConfig, isLoopbackRequest, sendErrorPage,
|
require('./fork_routes')({ checkMod, getViewerId, getConfig, saveConfig, isLoopbackRequest, sendErrorPage, about }),
|
||||||
about, backupModel, onboardingModel }),
|
|
||||||
routes,
|
routes,
|
||||||
async (ctx) => {
|
async (ctx) => {
|
||||||
if (ctx.body != null || (ctx.method !== 'GET' && ctx.method !== 'HEAD')) return;
|
if (ctx.body != null || (ctx.method !== 'GET' && ctx.method !== 'HEAD')) return;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
//
|
//
|
||||||
// Va en la cadena justo delante de las rutas del dev (backend.js lo monta con una
|
// Va en la cadena justo delante de las rutas del dev (backend.js lo monta con una
|
||||||
// sola linea). Lo que no atiende pasa al del dev con next(). Aqui viven:
|
// sola linea). Lo que no atiende pasa al del dev con next(). Aqui viven:
|
||||||
// - mobile_layer.js: lo que el movil necesita del backend
|
// - mobile_layer.js: lanzador, filtro y avatar de la barra de arriba
|
||||||
// - wrapUpstream: mira lo que contesta el dev (seccion apagada, guardar modulos)
|
// - wrapUpstream: mira lo que contesta el dev (seccion apagada, guardar modulos)
|
||||||
// - las rutas propias: tema y Modulos en el movil, barra de abajo, identidades
|
// - las rutas propias: tema y Modulos en el movil, barra de abajo, identidades
|
||||||
//
|
//
|
||||||
|
|
@ -51,10 +51,8 @@ const wrapUpstream = async (ctx, next) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = ({ checkMod, getViewerId, getConfig, saveConfig, isLoopbackRequest, sendErrorPage,
|
module.exports = ({ checkMod, getViewerId, getConfig, saveConfig, isLoopbackRequest, sendErrorPage, about }) => {
|
||||||
about, backupModel, onboardingModel }) => {
|
const mobileLayer = require('./mobile_layer')({ getViewerId, about });
|
||||||
const mobileLayer = require('./mobile_layer')({ getConfig, saveConfig, getViewerId, about, backupModel, onboardingModel,
|
|
||||||
checkMod, isLoopbackRequest, sendErrorPage });
|
|
||||||
|
|
||||||
const forkRouter = new koaRouter();
|
const forkRouter = new koaRouter();
|
||||||
forkRouter
|
forkRouter
|
||||||
|
|
|
||||||
|
|
@ -1,49 +1,20 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
// Lo que el movil necesita del backend, fuera de backend.js.
|
// Lo que la interfaz de la rama necesita del backend, fuera de backend.js.
|
||||||
//
|
//
|
||||||
// Antes eran trozos metidos en backend.js (rutas, middleware y refreshAll), que es
|
// Prepara lo que leen la barra de arriba y el lanzador de hive_views.js: el filtro
|
||||||
// del dev y cambia en 34 de cada 39 versiones. Ahora va aqui y lo monta el router
|
// Personal/Social, si el lanzador se ensena en esta pagina y el avatar propio. Lo
|
||||||
// propio (fork_routes.js) delante de las rutas del dev. Nada de esto corre en
|
// monta el router propio (fork_routes.js) delante de las rutas del dev. Nada de
|
||||||
// escritorio. Ver GUIA_DE_DESARROLLO.md.
|
// esto corre en escritorio. Ver GUIA_DE_DESARROLLO.md.
|
||||||
//
|
//
|
||||||
// Hace cuatro cosas:
|
// Lo demas que el movil necesita del backend (modulos que no van, rutas que se
|
||||||
// - corta en el movil rutas que alli no tienen sentido (blockexplorer, actualizar
|
// cortan, retoques del HTML, descargas de la copia de seguridad) lo hace el propio
|
||||||
// con git, presets de modulos); el cambio de tema lo atiende fork_routes.js
|
// dev en la version de movil de su backend.js, la que va dentro de su APK.
|
||||||
// - mantiene apagados los modulos que no van en el movil, al arrancar y al guardar
|
|
||||||
// - prepara lo que leen la barra de arriba y el lanzador (avatar, filtro, lanzador)
|
|
||||||
// - retoca el HTML que sale (videos con fotograma, pestaña actual marcada)
|
|
||||||
// Y sirve las dos descargas de la copia de seguridad, que en el movil van por GET.
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const os = require('os');
|
|
||||||
const path = require('path');
|
|
||||||
const compose = require('../server/node_modules/koa-compose');
|
|
||||||
|
|
||||||
const MOBILE_UNAVAILABLE_MODS = ['ai', 'aiNav', 'dev', 'games', 'logs', 'maps', 'pixelia'];
|
|
||||||
const NULL_IMG = '&' + '0'.repeat(43) + '=.sha256';
|
const NULL_IMG = '&' + '0'.repeat(43) + '=.sha256';
|
||||||
const TAB_PATHS = ['/mentions', '/inbox', '/phone', '/data', '/tags', '/search', '/settings'];
|
|
||||||
const isMobile = () => process.env.OASIS_MOBILE === '1';
|
const isMobile = () => process.env.OASIS_MOBILE === '1';
|
||||||
const isPage = (p) => !/^\/(assets|image|blob|qr|c)(\/|$)/.test(p);
|
const isPage = (p) => !/^\/(assets|image|blob|qr|c)(\/|$)/.test(p);
|
||||||
|
|
||||||
module.exports = ({ getConfig, saveConfig, getViewerId, about, backupModel, onboardingModel,
|
module.exports = ({ getViewerId, about }) => {
|
||||||
checkMod, isLoopbackRequest, sendErrorPage }) => {
|
|
||||||
// --- al arrancar: modulos que no van en el movil, apagados; y la cartera de
|
|
||||||
// pruebas que traia la config de serie, fuera
|
|
||||||
if (isMobile()) {
|
|
||||||
try {
|
|
||||||
const cfg = getConfig();
|
|
||||||
let dirty = false;
|
|
||||||
MOBILE_UNAVAILABLE_MODS.forEach(m => { if (cfg.modules && cfg.modules[`${m}Mod`] !== 'off') { cfg.modules[`${m}Mod`] = 'off'; dirty = true; } });
|
|
||||||
const w = cfg.wallet || {};
|
|
||||||
if (w.url === 'http://127.0.0.1:17474' && w.user === 'oasis' && w.pass === 'testpass123') {
|
|
||||||
cfg.wallet = { ...w, url: 'http://127.0.0.1:7474', user: '', pass: '' };
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
if (dirty) saveConfig(cfg);
|
|
||||||
} catch (_) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- avatar propio para el circulo de la barra de arriba (lo lee hive_views)
|
|
||||||
let avatarAt = 0;
|
let avatarAt = 0;
|
||||||
const refreshAvatar = async () => {
|
const refreshAvatar = async () => {
|
||||||
avatarAt = Date.now();
|
avatarAt = Date.now();
|
||||||
|
|
@ -56,78 +27,12 @@ module.exports = ({ getConfig, saveConfig, getViewerId, about, backupModel, onbo
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- rutas que en el movil se cortan antes de llegar al dev
|
return async (ctx, next) => {
|
||||||
const guards = async (ctx, next) => {
|
if (!isMobile() || !isPage(ctx.path)) return next();
|
||||||
if (!isMobile()) return next();
|
global.__OASIS_HIVE_FILTER__ = (ctx.path === '/activity' && (ctx.query.hive === 'personal' || ctx.query.hive === 'community')) ? ctx.query.hive : '';
|
||||||
const p = ctx.path;
|
global.__OASIS_SHOW_HIVE__ = (ctx.path === '/activity' || ctx.path === '/');
|
||||||
if (ctx.method === 'GET' && (p === '/blockexplorer' || p.startsWith('/blockexplorer/block/'))) { ctx.redirect('/'); return; }
|
if (!avatarAt) await refreshAvatar();
|
||||||
if (ctx.method === 'POST' && p === '/update') { ctx.redirect('/settings'); return; }
|
else if (Date.now() - avatarAt > 60000) refreshAvatar();
|
||||||
if (ctx.method === 'POST' && p === '/modules/preset') { ctx.redirect('/modules'); return; }
|
|
||||||
return next();
|
return next();
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- antes y despues de las rutas del dev
|
|
||||||
const around = async (ctx, next) => {
|
|
||||||
if (!isMobile()) return next();
|
|
||||||
if (isPage(ctx.path)) {
|
|
||||||
// filtro de la home (Personal/Social) y lanzador solo en la portada
|
|
||||||
global.__OASIS_HIVE_FILTER__ = (ctx.path === '/activity' && (ctx.query.hive === 'personal' || ctx.query.hive === 'community')) ? ctx.query.hive : '';
|
|
||||||
global.__OASIS_SHOW_HIVE__ = (ctx.path === '/activity' || ctx.path === '/');
|
|
||||||
if (!avatarAt) await refreshAvatar();
|
|
||||||
else if (Date.now() - avatarAt > 60000) refreshAvatar();
|
|
||||||
}
|
|
||||||
await next();
|
|
||||||
// guardar modulos: los que no van en el movil siguen apagados
|
|
||||||
if (ctx.method === 'POST' && ctx.path === '/save-modules' && ctx.status >= 300 && ctx.status < 400) {
|
|
||||||
try {
|
|
||||||
const cfg = getConfig();
|
|
||||||
MOBILE_UNAVAILABLE_MODS.forEach(m => { cfg.modules[`${m}Mod`] = 'off'; });
|
|
||||||
saveConfig(cfg);
|
|
||||||
} catch (_) {}
|
|
||||||
}
|
|
||||||
if (typeof ctx.body !== 'string' || !String(ctx.type || '').includes('html')) return;
|
|
||||||
// los videos sin poster salen en negro en el WebView: se pide el fotograma 0.1
|
|
||||||
ctx.body = ctx.body.replace(/<video\b[^>]*>/g, (tag) => tag.replace(/\bsrc="(\/blob\/[^"#]+)"/, 'src="$1#t=0.1"'));
|
|
||||||
// la pestaña de la cabecera que corresponde a esta pagina
|
|
||||||
const tab = TAB_PATHS.find((t) => ctx.path === t || ctx.path.startsWith(t + '/'));
|
|
||||||
const headerEnd = ctx.body.indexOf('<div class="main-content">');
|
|
||||||
if (tab && headerEnd > 0) ctx.body = ctx.body.slice(0, headerEnd).replace(`<a href="${tab}"`, `<a href="${tab}" aria-current="page"`) + ctx.body.slice(headerEnd);
|
|
||||||
};
|
|
||||||
|
|
||||||
// --- copia de seguridad: en el movil se descarga por GET (el WebView no guarda
|
|
||||||
// la respuesta de un POST como fichero)
|
|
||||||
const backup = async (ctx, next) => {
|
|
||||||
if (ctx.method !== 'GET' || (ctx.path !== '/backup/keys/export' && ctx.path !== '/backup/export')) return next();
|
|
||||||
if (!isLoopbackRequest(ctx)) { ctx.status = 403; ctx.body = ''; return; }
|
|
||||||
if (!isMobile()) { ctx.redirect('/backup'); return; }
|
|
||||||
const q = ctx.query || {};
|
|
||||||
const pw = String(q.password || '');
|
|
||||||
if (!pw || pw.length < 32) { ctx.redirect('/backup'); return; }
|
|
||||||
if (ctx.path === '/backup/keys/export') {
|
|
||||||
try {
|
|
||||||
const { filename, data } = await backupModel.exportKeys({ 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;
|
|
||||||
}
|
|
||||||
if (!checkMod(ctx, 'backupMod')) { ctx.redirect('/modules'); return; }
|
|
||||||
const rawMods = q.modules === undefined ? [] : (Array.isArray(q.modules) ? q.modules : [q.modules]);
|
|
||||||
const outPath = path.join(os.tmpdir(), `oasis-backup-${process.pid}-${Date.now()}.oasisbk`);
|
|
||||||
try {
|
|
||||||
const res = await backupModel.createBackup({ scope: q.scope, blobs: q.blobs, modules: rawMods, since: q.since }, pw, outPath);
|
|
||||||
ctx.set('Content-Type', 'application/octet-stream');
|
|
||||||
ctx.set('Content-Disposition', `attachment; filename="${res.filename}"`);
|
|
||||||
ctx.set('Content-Length', String(res.bytes));
|
|
||||||
const stream = fs.createReadStream(outPath);
|
|
||||||
stream.on('close', () => { try { fs.unlinkSync(outPath); } catch (_) {} });
|
|
||||||
ctx.body = stream;
|
|
||||||
try { onboardingModel.markStep('backup'); } catch (_) {}
|
|
||||||
} catch (error) { try { fs.unlinkSync(outPath); } catch (_) {} sendErrorPage(ctx, error.message); }
|
|
||||||
};
|
|
||||||
|
|
||||||
return compose([around, guards, backup]);
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -66,13 +66,17 @@ const modulesView = () => {
|
||||||
{ name: 'wiki', label: i18n.modulesWikiLabel, description: i18n.modulesWikiDescription }
|
{ name: 'wiki', label: i18n.modulesWikiLabel, description: i18n.modulesWikiDescription }
|
||||||
].sort((x, y) => String(x.label || x.name).localeCompare(String(y.label || y.name), undefined, { sensitivity: 'base' }));
|
].sort((x, y) => String(x.label || x.name).localeCompare(String(y.label || y.name), undefined, { sensitivity: 'base' }));
|
||||||
|
|
||||||
const moduleStates = modules.reduce((acc, mod) => {
|
const MOBILE_UNAVAILABLE = ['ai', 'aiNav', 'dev', 'games', 'logs', 'maps', 'pixelia'];
|
||||||
|
const isMobile = process.env.OASIS_MOBILE === '1';
|
||||||
|
const visibleModules = isMobile ? modules.filter(mod => !MOBILE_UNAVAILABLE.includes(mod.name)) : modules;
|
||||||
|
|
||||||
|
const moduleStates = visibleModules.reduce((acc, mod) => {
|
||||||
acc[`${mod.name}Mod`] = config[`${mod.name}Mod`] === 'on' ? 'on' : 'off';
|
acc[`${mod.name}Mod`] = config[`${mod.name}Mod`] === 'on' ? 'on' : 'off';
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
const activeModulesCount = modules.filter(mod => moduleStates[`${mod.name}Mod`] === 'on').length;
|
const activeModulesCount = visibleModules.filter(mod => moduleStates[`${mod.name}Mod`] === 'on').length;
|
||||||
const totalModulesCount = modules.length;
|
const totalModulesCount = visibleModules.length;
|
||||||
const disabledModulesCount = totalModulesCount - activeModulesCount;
|
const disabledModulesCount = totalModulesCount - activeModulesCount;
|
||||||
|
|
||||||
const header = [div({ class: 'tags-header module-header-line' },
|
const header = [div({ class: 'tags-header module-header-line' },
|
||||||
|
|
@ -100,7 +104,7 @@ const modulesView = () => {
|
||||||
const presetButtons = div({ class: 'preset-group' },
|
const presetButtons = div({ class: 'preset-group' },
|
||||||
Object.entries(PRESETS).map(([key, mods]) => {
|
Object.entries(PRESETS).map(([key, mods]) => {
|
||||||
const presetLabel = (i18n[`modulesPreset_${key}`] || key).toUpperCase();
|
const presetLabel = (i18n[`modulesPreset_${key}`] || key).toUpperCase();
|
||||||
const isActive = modules.every(m => mods.includes(m.name) === (moduleStates[`${m.name}Mod`] === 'on'));
|
const isActive = visibleModules.every(m => mods.includes(m.name) === (moduleStates[`${m.name}Mod`] === 'on'));
|
||||||
return form({ action: "/modules/preset", method: "post", class: "modules-preset-form" },
|
return form({ action: "/modules/preset", method: "post", class: "modules-preset-form" },
|
||||||
input({ type: "hidden", name: "preset", value: key }),
|
input({ type: "hidden", name: "preset", value: key }),
|
||||||
button({
|
button({
|
||||||
|
|
@ -115,8 +119,7 @@ const modulesView = () => {
|
||||||
i18n.modules,
|
i18n.modules,
|
||||||
section(header),
|
section(header),
|
||||||
section(
|
section(
|
||||||
h2(i18n.modulesPresetTitle || "Common Configurations"),
|
...(isMobile ? [] : [h2(i18n.modulesPresetTitle || "Common Configurations"), presetButtons]),
|
||||||
presetButtons,
|
|
||||||
counts,
|
counts,
|
||||||
form(
|
form(
|
||||||
{ action: "/save-modules", method: "post" },
|
{ action: "/save-modules", method: "post" },
|
||||||
|
|
@ -127,7 +130,7 @@ const modulesView = () => {
|
||||||
td(i18n.modulesModuleDescription),
|
td(i18n.modulesModuleDescription),
|
||||||
td({ class: 'module-status-cell' }, i18n.modulesModuleStatus)
|
td({ class: 'module-status-cell' }, i18n.modulesModuleStatus)
|
||||||
),
|
),
|
||||||
modules.map(mod =>
|
visibleModules.map(mod =>
|
||||||
tr(
|
tr(
|
||||||
td(a({ href: `/${mod.name}` }, mod.label)),
|
td(a({ href: `/${mod.name}` }, mod.label)),
|
||||||
td(p(mod.description)),
|
td(p(mod.description)),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue