ssb: base de multicuenta, igual que en la version de movil

Se traen los mismos cambios: la cuenta sale de OASIS_ACCOUNT con el nombre validado,
statePath se separa por cuenta, y los doce sitios que construian la ruta como
os.homedir() + '/.ssb' pasan a usar la de la cuenta activa.

El mas importante sigue siendo panicmode_model: con varias identidades habria borrado
la que no era.

Tambien entra el gate que faltaba en renderMobileTopbar, sin el cual la cabecera de
movil se dibujaba encima de la de upstream en escritorio.

La configuracion de escritorio no se toca: sigue con tema Dark-SNH y todos los
modulos.

Verificado arrancando sin OASIS_MOBILE: ocho rutas OK, incluidas /peers y /stats que
son las que leen gossip.json, y cero rastro de la interfaz movil.
This commit is contained in:
SITO 2026-08-19 00:45:40 +02:00
parent ddd2787b73
commit f79573b317
7 changed files with 39 additions and 21 deletions

View file

@ -3507,7 +3507,7 @@ router
});
};
try {
const gossipPathLocal = path.join(os.homedir(), '.ssb', 'gossip.json');
const gossipPathLocal = path.join(require('../server/ssb_config').path, 'gossip.json');
let gossip = [];
try { gossip = JSON.parse(await promisesFs.readFile(gossipPathLocal, 'utf8')); } catch (_) {}
if (Array.isArray(gossip)) {
@ -8770,9 +8770,9 @@ router
if (pubKey) {
try {
const os = require('os'), fsx = require('fs'), px = require('path');
const gossip = JSON.parse(fsx.readFileSync(px.join(os.homedir(), '.ssb', 'gossip.json'), 'utf8') || '[]');
const gossip = JSON.parse(fsx.readFileSync(px.join(require('../server/ssb_config').path, 'gossip.json'), 'utf8') || '[]');
let unfollowed = [];
try { unfollowed = JSON.parse(fsx.readFileSync(px.join(os.homedir(), '.ssb', 'gossip_unfollowed.json'), 'utf8') || '[]'); } catch (_) {}
try { unfollowed = JSON.parse(fsx.readFileSync(px.join(require('../server/ssb_config').path, 'gossip_unfollowed.json'), 'utf8') || '[]'); } catch (_) {}
const activePub = Array.isArray(gossip) && gossip.some(p => p && p.key === pubKey) && !unfollowed.some(u => u && u.key === pubKey);
if (activePub) { ctx.redirect('/invites?flash=alreadyFederated'); return; }
} catch (_) {}

View file

@ -6,8 +6,8 @@ const archiver = require('../server/node_modules/archiver');
module.exports = {
exportSSB: async (outputPath) => {
try {
const homeDir = os.homedir();
const ssbPath = path.join(homeDir, '.ssb');
// se exporta la cuenta activa, no siempre la de por defecto
const ssbPath = require('../server/ssb_config').path;
const output = fs.createWriteStream(outputPath);
const archive = archiver('zip', {
zlib: { level: 9 }

View file

@ -49,8 +49,8 @@ module.exports = {
exportData: async (password) => {
try {
const pw = normalizePassword(password);
const homeDir = os.homedir();
const secretFilePath = path.join(homeDir, '.ssb', 'secret');
// identidad de la cuenta activa
const secretFilePath = path.join(require('../server/ssb_config').path, 'secret');
if (!fs.existsSync(secretFilePath)) {
throw new Error(".ssb/secret file doesn't exist");
}
@ -78,8 +78,8 @@ module.exports = {
} catch (_) {
throw new Error('Wrong password or corrupt backup file.');
}
const homeDir = os.homedir();
const ssbDir = path.join(homeDir, '.ssb');
// identidad de la cuenta activa
const ssbDir = require('../server/ssb_config').path;
fs.mkdirSync(ssbDir, { recursive: true });
const secretPath = path.join(ssbDir, 'secret');
if (fs.existsSync(secretPath)) {

View file

@ -66,8 +66,8 @@ const configure = (...customOptions) =>
Object.assign({}, defaultOptions, ...customOptions);
// PEERS
const ebtDir = path.join(os.homedir(), '.ssb', 'ebt');
const unfollowedPath = path.join(os.homedir(), '.ssb', 'gossip_unfollowed.json');
const ebtDir = path.join(require('../server/ssb_config').path, 'ebt');
const unfollowedPath = path.join(require('../server/ssb_config').path, 'gossip_unfollowed.json');
async function loadPeersFromEbt() {
let result = [];
@ -789,7 +789,7 @@ models.meta = {
discovered: async () => {
const ssb = await cooler.open();
const snapshot = await ssb.conn.dbPeers();
const gossipPath = path.join(os.homedir(), '.ssb', 'gossip.json');
const gossipPath = path.join(require('../server/ssb_config').path, 'gossip.json');
let gossipMap = new Map();
try {
const gossipData = JSON.parse(await fs.readFile(gossipPath, 'utf8'));

View file

@ -5,8 +5,8 @@ const path = require('path');
module.exports = {
removeSSB: async () => {
try {
const homeDir = os.homedir();
const ssbPath = path.join(homeDir, '.ssb');
// ruta de la cuenta activa: con varias identidades, ~/.ssb fijo borraria otra
const ssbPath = require('../server/ssb_config').path;
await fs.promises.rm(ssbPath, { recursive: true, force: true });
} catch (error) {
throw new Error("Error deleting data: " + error.message);

View file

@ -22,7 +22,7 @@ function readAddrMap() {
const listPubsFromEbt = () => {
try {
const ebtDir = path.join(os.homedir(), '.ssb', 'ebt');
const ebtDir = path.join(require('../server/ssb_config').path, 'ebt');
const files = fs.readdirSync(ebtDir);
return files.filter(f => f.endsWith('.ed25519'));
} catch {
@ -428,12 +428,12 @@ module.exports = ({ cooler, tribeCrypto, tribesModel }) => {
const inhabitants = new Set(allMsgs.map(m => m.value.author)).size;
const secretStat = fs.statSync(`${os.homedir()}/.ssb/secret`);
const secretStat = fs.statSync((require('../server/ssb_config').path + '/secret'));
const createdAt = secretStat.birthtime.toLocaleString();
const folderSize = getFolderSize(`${os.homedir()}/.ssb`);
const flumeSize = getFolderSize(`${os.homedir()}/.ssb/flume`);
const blobsSize = getFolderSize(`${os.homedir()}/.ssb/blobs`);
const folderSize = getFolderSize((require('../server/ssb_config').path + ''));
const flumeSize = getFolderSize((require('../server/ssb_config').path + '/flume'));
const blobsSize = getFolderSize((require('../server/ssb_config').path + '/blobs'));
const allTs = scopedMsgs.map(m => m.value.timestamp || 0).filter(Boolean);
const lastTs = allTs.length ? Math.max(...allTs) : 0;

View file

@ -11,7 +11,21 @@ const i = argv.indexOf('--');
const conf = argv.slice(i + 1);
const cliArgs = ~i ? argv.slice(0, i) : argv;
let config = Config('ssb', minimist(conf));
// Nombre de la cuenta: define donde vive todo (~/.<cuenta>) y de ahi cuelgan el
// secret, la base de datos, los blobs y el socket. Sin la variable se usa 'ssb',
// que es lo de siempre, asi que las instalaciones existentes no notan nada.
//
// Se valida a proposito: este nombre acaba en rutas de fichero y hay operaciones
// destructivas (el modo panico borra el directorio de la cuenta) que no deben poder
// apuntar fuera de sitio.
const ACCOUNT_RE = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/;
const rawAccount = process.env.OASIS_ACCOUNT || 'ssb';
const account = ACCOUNT_RE.test(rawAccount) ? rawAccount : 'ssb';
if (account !== rawAccount) {
console.error(`[oasis] OASIS_ACCOUNT no valido (${rawAccount}); se usa 'ssb'`);
}
let config = Config(account, minimist(conf));
config = { ...config, ...configData };
const debug = process.argv.includes('--debug') || process.env.OASIS_DEBUG === '1' || process.env.OASIS_DEBUG === 'true';
@ -23,8 +37,12 @@ const megabyte = Math.pow(2, 20);
config.blobs = config.blobs || {};
config.blobs.max = 50 * megabyte;
config.account = account;
config.statePath = (name) => {
if (process.env.OASIS_STATE_DIR) return path.join(process.env.OASIS_STATE_DIR, name);
// El directorio externo se separa por cuenta: sin esto dos identidades
// compartirian estado, que es justo lo que hay que evitar.
if (process.env.OASIS_STATE_DIR) return path.join(process.env.OASIS_STATE_DIR, account, name);
return config.path ? path.join(config.path, name) : null;
};