upstream: integrar Oasis 1.3.1
Base del merge a tres bandas: git 0.9.5 de epsylon, esta rama y el src de la
APK oficial 1.3.1 (la variante movil: OASIS_MOBILE, regex sin \p{}, audio y
video de Android). Corre en el mismo nodejs-mobile 18.20.4 que ya usabamos.
Se conserva de la rama: Karvan, identidades y multicuenta, topbar y barra
inferior, borrado de EXIF sin sharp, filtro hot del foro, RTC sin STUN y los
modulos apagados en movil (wallet, banking, torrents).
Se toma de upstream lo que ya hace de serie: respuesta citada en chats, ssb-lan
en movil, configuracion en ~/.ssb/oasis (sobrevive a reinstalar el backend).
El asistente de bienvenida deja elegir workflow tambien en movil.
This commit is contained in:
parent
8d838dd8b1
commit
c332afd6ea
265 changed files with 80970 additions and 29167 deletions
|
|
@ -1,12 +1,33 @@
|
|||
const peersView = async ({ onlinePeers, discoveredPeers, unknownPeers, lanBroadcastActive = false, technicalPeers = [] }) => {
|
||||
const { form, button, div, h2, p, section, a, hr, input, label, br, span, table, tr, td, textarea } = require("../server/node_modules/hyperaxe");
|
||||
const { template, i18n } = require('./main_views');
|
||||
const peersView = async ({ onlinePeers, discoveredPeers, unknownPeers, lanBroadcastActive = false, technicalPeers = [], versions = {}, staleKeys = [], onionKeys = [], ownVersion = '', lastErrors = {}, paused = false }) => {
|
||||
const { form, button, div, h2, p, section, a, hr, input, label, br, span, table, tr, td, textarea, wbr } = require("../server/node_modules/hyperaxe");
|
||||
const { template, i18n, renderStateChip } = require('./main_views');
|
||||
const { NET_REASON_KEYS, canonicalKey } = require('../models/peer_health');
|
||||
const majorMinor = (v) => String(v || '').split('.').slice(0, 2).map(n => parseInt(n, 10) || 0);
|
||||
const isOutdated = (v) => {
|
||||
if (!v || !ownVersion) return false;
|
||||
const [a1, b1] = majorMinor(v), [a2, b2] = majorMinor(ownVersion);
|
||||
return a1 < a2 || (a1 === a2 && b1 < b2);
|
||||
};
|
||||
const stale = new Set(staleKeys || []);
|
||||
const outdatedKey = (k) => isOutdated((versions || {})[k]) || (!(versions || {})[k] && stale.has(k));
|
||||
const versionCell = (k) => {
|
||||
const v = (versions || {})[k];
|
||||
if (!v) return [stale.has(k) ? span({ title: i18n.peerVersionUnannounced }, renderStateChip('closed', null, String(i18n.peerOutdated).toUpperCase())) : '—'];
|
||||
const old = isOutdated(v);
|
||||
return [span({ title: old ? i18n.peerOutdated : '' }, renderStateChip(old ? 'closed' : 'mutuals', null, String(v)))];
|
||||
};
|
||||
const stateCell = (tp) => {
|
||||
const st = String(tp.state || '');
|
||||
if (st === 'connected') return renderStateChip('mutuals', null, String(i18n.peerStateConnected).toUpperCase());
|
||||
if (st === 'connecting') return renderStateChip('whole', null, String(i18n.peerStateConnecting).toUpperCase());
|
||||
if (st === 'staged') return renderStateChip(outdatedKey(tp.key) ? 'whole' : 'mutuals', null, String(i18n.peerStateStaged).toUpperCase());
|
||||
const err = (lastErrors || {})[canonicalKey(tp.key)];
|
||||
return renderStateChip('hidden', null, String(i18n[err ? NET_REASON_KEYS[err.reason] : 'peerErrSilent'] || i18n.peerErrOther).toUpperCase());
|
||||
};
|
||||
|
||||
const startButton = form({ action: "/settings/conn/start", method: "post" }, button({ type: "submit" }, i18n.startNetworking));
|
||||
const restartButton = form({ action: "/settings/conn/restart", method: "post" }, button({ type: "submit" }, i18n.restartNetworking));
|
||||
const stopButton = form({ action: "/settings/conn/stop", method: "post" }, button({ type: "submit" }, i18n.stopNetworking));
|
||||
const syncButton = form({ action: "/settings/conn/sync", method: "post" }, button({ type: "submit" }, i18n.sync));
|
||||
const connButtons = [startButton, restartButton, stopButton, syncButton];
|
||||
const pauseButton = paused
|
||||
? form({ action: "/peers/resume", method: "post" }, button({ type: "submit", class: "tribe-action-btn success-btn" }, String(i18n.peersResume || 'Resume').toUpperCase()))
|
||||
: form({ action: "/peers/pause", method: "post" }, button({ type: "submit", class: "tribe-action-btn danger-btn" }, String(i18n.peersPause || 'Pause').toUpperCase()));
|
||||
|
||||
const deduplicatePeers = (peers) => {
|
||||
const seen = new Set();
|
||||
|
|
@ -28,16 +49,28 @@ const peersView = async ({ onlinePeers, discoveredPeers, unknownPeers, lanBroadc
|
|||
return null;
|
||||
};
|
||||
|
||||
const shortHost = (h) => {
|
||||
const s = String(h || '');
|
||||
const m = s.match(/^(?:onion:)?([a-z2-7]{16,56})\.onion(.*)$/i);
|
||||
return m ? `${m[1].slice(0, 10)}….onion${m[2]}` : s;
|
||||
};
|
||||
const onion = new Set(onionKeys || []);
|
||||
const overTor = (peerData) => /^onion:/i.test(String(peerData[0] || '')) || /\.onion$/i.test(String((peerData[1] || {}).host || '')) || onion.has(canonicalKey((peerData[1] || {}).key));
|
||||
const renderPeerRow = (peerData) => {
|
||||
const peer = peerData[1];
|
||||
const { name, users, key } = peer;
|
||||
const tor = overTor(peerData);
|
||||
const peerUrl = `/author/${encodeURIComponent(key)}`;
|
||||
const filteredUsers = (users || []).filter(u => u.id !== key);
|
||||
const userCount = filteredUsers.length;
|
||||
const userCount = Array.isArray(users) ? filteredUsers.length : null;
|
||||
return tr(
|
||||
td({ 'data-label': i18n.peerHost || 'Pub' }, a({ href: peerUrl, class: "user-link" }, name || key.slice(0, 20) + '…')),
|
||||
td({ 'data-label': 'Key' }, a({ href: peerUrl, class: 'user-link peer-key' }, key)),
|
||||
td({ 'data-label': i18n.peersReplicatedFeeds || 'Feeds' }, String(userCount))
|
||||
td({ 'data-label': i18n.peerHost || 'Pub' }, a({ href: peerUrl, class: "user-link", title: name || key }, name ? shortHost(name) : key.slice(0, 20) + '…')),
|
||||
td({ 'data-label': i18n.peersOasisId || 'Oasis ID' }, a({ href: peerUrl, class: 'user-link peer-key' }, key)),
|
||||
td({ 'data-label': i18n.peersOasisVersion || 'Version' }, ...versionCell(key)),
|
||||
td({ 'data-label': i18n.peersTorColumn }, tor
|
||||
? span({ class: 'ubi-tick-ok', title: i18n.peersTorYes }, '✓')
|
||||
: span({ class: 'peer-tor-no', title: i18n.peersTorNo }, '—')),
|
||||
td({ 'data-label': i18n.peersReplicatedFeeds || 'Replicated' }, userCount == null ? '—' : String(userCount))
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -54,8 +87,10 @@ const peersView = async ({ onlinePeers, discoveredPeers, unknownPeers, lanBroadc
|
|||
return table({ class: 'block-info-table' },
|
||||
tr(
|
||||
td({ class: 'card-label' }, i18n.peerHost || 'Pub'),
|
||||
td({ class: 'card-label' }, 'Key'),
|
||||
td({ class: 'card-label' }, i18n.peersReplicatedFeeds || 'Replicated feeds')
|
||||
td({ class: 'card-label' }, i18n.peersOasisId || 'Oasis ID'),
|
||||
td({ class: 'card-label' }, i18n.peersOasisVersion || 'Version'),
|
||||
td({ class: 'card-label' }, i18n.peersTorColumn),
|
||||
td({ class: 'card-label' }, i18n.peersReplicatedFeeds || 'Replicated')
|
||||
),
|
||||
...peers.map(renderPeerRow)
|
||||
);
|
||||
|
|
@ -67,12 +102,15 @@ const peersView = async ({ onlinePeers, discoveredPeers, unknownPeers, lanBroadc
|
|||
const action = connected ? 'disconnect' : 'connect';
|
||||
const btnLabel = connected ? (i18n.peerDisconnect || 'Disconnect') : (i18n.peerConnect || 'Connect');
|
||||
return tr(
|
||||
td({ 'data-label': 'Key' }, a({ href: `/author/${encodeURIComponent(k)}`, class: 'user-link peer-key' }, k ? k.slice(0, 20) + '…' : '—')),
|
||||
td({ 'data-label': 'Host' }, String(tp.host || '—')),
|
||||
td({ 'data-label': i18n.peerPort || 'Port' }, String(tp.port || '—')),
|
||||
td({ 'data-label': 'State' }, String(tp.state || tp.source || '—')),
|
||||
td({ 'data-label': i18n.peerLastChange || 'Last change' }, String(tp.stateChange ? new Date(tp.stateChange).toISOString().slice(0, 16).replace('T', ' ') : '—')),
|
||||
td({ 'data-label': '' },
|
||||
td({ 'data-label': i18n.peersOasisId }, a({ href: `/author/${encodeURIComponent(k)}`, class: 'user-link peer-key' }, k ? k.slice(0, 20) + '…' : '—')),
|
||||
onion.has(canonicalKey(tp.key)) || /\.onion$/i.test(String(tp.host || ''))
|
||||
? td({ 'data-label': i18n.peerAddressLabel, class: 'peer-host' }, renderStateChip('fediverse', null, 'TOR'))
|
||||
: td({ 'data-label': i18n.peerAddressLabel, title: String(tp.host || ''), class: 'peer-host' }, tp.host ? shortHost(tp.host).split(/(?<=\.)/).reduce((acc, part, i) => i ? [...acc, wbr(), part] : [part], []) : '—'),
|
||||
td({ 'data-label': i18n.peerPort, class: 'peer-port' }, String(tp.port || '—')),
|
||||
td({ 'data-label': i18n.peersOasisVersion || 'Version' }, ...versionCell(k)),
|
||||
td({ 'data-label': i18n.peerStateLabel }, stateCell(tp)),
|
||||
td({ 'data-label': i18n.peerLastChangeLabel }, String(tp.stateChange ? new Date(tp.stateChange).toISOString().slice(0, 16).replace('T', ' ') : '—')),
|
||||
td(
|
||||
form({ method: "POST", action: `/peers/${action}`, class: "inline-form" },
|
||||
input({ type: "hidden", name: "key", value: k }),
|
||||
input({ type: "hidden", name: "host", value: String(tp.host || '') }),
|
||||
|
|
@ -82,9 +120,9 @@ const peersView = async ({ onlinePeers, discoveredPeers, unknownPeers, lanBroadc
|
|||
)
|
||||
);
|
||||
});
|
||||
const refreshButton = form({ action: "/peers/refresh", method: "post" }, button({ type: "submit" }, i18n.peerRefresh || 'Refresh'));
|
||||
const pruneButton = form({ action: "/peers/prune", method: "post" }, button({ type: "submit" }, i18n.peerPruneIdle || 'Remove idle'));
|
||||
const exportButton = form({ action: "/peers/export", method: "get" }, button({ type: "submit" }, i18n.peerExport || 'Export'));
|
||||
const refreshButton = form({ action: "/peers/refresh", method: "post" }, button({ type: "submit", class: "filter-btn" }, i18n.peerRefresh || 'Refresh'));
|
||||
const pruneButton = form({ action: "/peers/prune", method: "post" }, button({ type: "submit", class: "filter-btn" }, i18n.peerPruneIdle || 'Remove idle'));
|
||||
const exportButton = form({ action: "/peers/export", method: "get" }, button({ type: "submit", class: "filter-btn" }, i18n.peerExport || 'Export'));
|
||||
const importForm = form(
|
||||
{ action: "/peers/import", method: "post", enctype: "multipart/form-data", class: "peers-import-form" },
|
||||
label({ class: 'peers-import-label' }, i18n.peerImportTitle || 'Import peer list'),
|
||||
|
|
@ -98,15 +136,17 @@ const peersView = async ({ onlinePeers, discoveredPeers, unknownPeers, lanBroadc
|
|||
|
||||
const peersTechnicalBlock = div({ class: 'tags-header peers-technical-block' },
|
||||
h2(i18n.peerConnectionsTitle || 'Connections'),
|
||||
div({ class: "conn-actions peers-conn-actions" }, refreshButton, pruneButton, exportButton),
|
||||
div({ class: "peers-pause-row" }, pauseButton, p({ class: paused ? "peers-paused-note" : "peers-pause-hint" }, paused ? i18n.peersPausedNote : i18n.peersPauseHint)),
|
||||
div({ class: "conn-actions peers-conn-actions filters" }, refreshButton, pruneButton, exportButton),
|
||||
technicalPeers.length
|
||||
? table({ class: 'block-info-table' },
|
||||
tr(
|
||||
td({ class: 'card-label' }, 'Key'),
|
||||
td({ class: 'card-label' }, 'Host'),
|
||||
td({ class: 'card-label' }, i18n.peerPort || 'Port'),
|
||||
td({ class: 'card-label' }, 'State'),
|
||||
td({ class: 'card-label' }, 'Last change'),
|
||||
td({ class: 'card-label' }, i18n.peersOasisId),
|
||||
td({ class: 'card-label' }, i18n.peerAddressLabel),
|
||||
td({ class: 'card-label peer-port' }, i18n.peerPort),
|
||||
td({ class: 'card-label' }, i18n.peersOasisVersion || 'Version'),
|
||||
td({ class: 'card-label' }, i18n.peerStateLabel),
|
||||
td({ class: 'card-label' }, i18n.peerLastChangeLabel),
|
||||
td({ class: 'card-label' }, '')
|
||||
),
|
||||
...technicalRows
|
||||
|
|
@ -118,11 +158,10 @@ const peersView = async ({ onlinePeers, discoveredPeers, unknownPeers, lanBroadc
|
|||
return template(
|
||||
i18n.peers,
|
||||
section(
|
||||
div({ class: 'tags-header' },
|
||||
div({ class: 'tags-header module-header-line' },
|
||||
h2(i18n.peers),
|
||||
p(i18n.peerConnectionsIntro)
|
||||
),
|
||||
div({ class: "conn-actions" }, ...connButtons),
|
||||
(onlineCount + discoveredCount + unknownCount) > 0
|
||||
? div({ class: "peers-list" },
|
||||
div({ class: "tags-header" }, h2(`${i18n.online} (${onlineCount})`)),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue