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 +0,0 @@
|
|||
[]
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"discardedItems": []
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
[]
|
||||
|
|
@ -1 +0,0 @@
|
|||
[]
|
||||
|
|
@ -1,7 +1,58 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const configFilePath = path.join(__dirname, 'oasis-config.json');
|
||||
const os = require('os');
|
||||
const legacyConfigPath = path.join(__dirname, 'oasis-config.json');
|
||||
const stateDir = process.env.OASIS_STATE_DIR || process.env.ssb_path || path.join(os.homedir(), '.ssb');
|
||||
const oasisDir = path.join(stateDir, 'oasis');
|
||||
const configFilePath = path.join(oasisDir, 'oasis-config.json');
|
||||
const defaultServerConfigPath = path.join(__dirname, 'server-config.json');
|
||||
const serverConfigFilePath = path.join(oasisDir, 'oasis-server-config.json');
|
||||
for (const [from, to] of [[path.join(stateDir, 'oasis-config.json'), configFilePath], [path.join(stateDir, 'oasis-server-config.json'), serverConfigFilePath]]) {
|
||||
try {
|
||||
if (!fs.existsSync(to) && fs.existsSync(from)) {
|
||||
fs.mkdirSync(oasisDir, { recursive: true, mode: 0o700 });
|
||||
fs.renameSync(from, to);
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
const writeStateFile = (file, data) => {
|
||||
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
|
||||
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
||||
fs.writeFileSync(tmp, data, { encoding: 'utf8', mode: 0o600 });
|
||||
fs.renameSync(tmp, file);
|
||||
try { fs.chmodSync(file, 0o600); } catch (_) {}
|
||||
};
|
||||
let configCache = { at: 0, cfg: null };
|
||||
const CONFIG_TTL_MS = 2000;
|
||||
const writeConfigFile = (data) => { configCache = { at: 0, cfg: null }; return writeStateFile(configFilePath, data); };
|
||||
const readJsonFile = (file) => { try { return JSON.parse(fs.readFileSync(file, 'utf8')); } catch (_) { return null; } };
|
||||
const isPlainObject = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
||||
const mergeDeep = (base, over) => {
|
||||
if (!isPlainObject(base) || !isPlainObject(over)) return over === undefined ? base : over;
|
||||
const out = { ...base };
|
||||
for (const [k, v] of Object.entries(over)) out[k] = isPlainObject(v) && isPlainObject(base[k]) ? mergeDeep(base[k], v) : v;
|
||||
return out;
|
||||
};
|
||||
if (!fs.existsSync(serverConfigFilePath)) {
|
||||
const shipped = readJsonFile(defaultServerConfigPath);
|
||||
const hops = shipped && shipped.friends ? shipped.friends.hops : undefined;
|
||||
try {
|
||||
if (shipped && shipped.pub === true) writeStateFile(serverConfigFilePath, JSON.stringify(shipped, null, 2));
|
||||
else if (Number.isFinite(hops) && hops !== 2) writeStateFile(serverConfigFilePath, JSON.stringify({ friends: { hops } }, null, 2));
|
||||
} catch (_) {}
|
||||
}
|
||||
const readServerConfig = () => mergeDeep(readJsonFile(defaultServerConfigPath) || {}, readJsonFile(serverConfigFilePath) || {});
|
||||
const saveServerConfig = (patch) => writeStateFile(serverConfigFilePath, JSON.stringify(mergeDeep(readJsonFile(serverConfigFilePath) || {}, patch), null, 2));
|
||||
const inviteCodesPath = path.join(__dirname, 'invite-codes.json');
|
||||
const readInviteCodes = () => {
|
||||
const raw = readJsonFile(inviteCodesPath);
|
||||
return (Array.isArray(raw) ? raw : raw ? [raw] : []).filter((e) => e && typeof e.code === 'string' && e.code.trim());
|
||||
};
|
||||
const defaultInvite = () => readInviteCodes()[0] || null;
|
||||
if (!fs.existsSync(configFilePath) && fs.existsSync(legacyConfigPath)) {
|
||||
try { writeConfigFile(fs.readFileSync(legacyConfigPath, 'utf8')); } catch (_) {}
|
||||
}
|
||||
|
||||
if (!fs.existsSync(configFilePath)) {
|
||||
const defaultConfig = {
|
||||
|
|
@ -22,10 +73,10 @@ if (!fs.existsSync(configFilePath)) {
|
|||
"threadsMod": "on",
|
||||
"multiverseMod": "on",
|
||||
"pollsMod": "on",
|
||||
"fediverseMod": "off",
|
||||
"fediverseMod": "on",
|
||||
"invitesMod": "on",
|
||||
"walletMod": "on",
|
||||
"legacyMod": "on",
|
||||
"backupMod": "on",
|
||||
"devMod": "on",
|
||||
"cipherMod": "on",
|
||||
"bookmarksMod": "on",
|
||||
|
|
@ -43,6 +94,14 @@ if (!fs.existsSync(configFilePath)) {
|
|||
"reportsMod": "on",
|
||||
"opinionsMod": "on",
|
||||
"padsMod": "on",
|
||||
"wikiMod": "on",
|
||||
"emergenciesMod": "on",
|
||||
"mailingMod": "on",
|
||||
"roomsMod": "on",
|
||||
"phoneMod": "on",
|
||||
"logisticsMod": "on",
|
||||
"podcastsMod": "on",
|
||||
"campaignsMod": "on",
|
||||
"calendarsMod": "on",
|
||||
"transfersMod": "on",
|
||||
"feedMod": "on",
|
||||
|
|
@ -58,6 +117,7 @@ if (!fs.existsSync(configFilePath)) {
|
|||
"projectsMod": "on",
|
||||
"industryMod": "on",
|
||||
"bankingMod": "on",
|
||||
"schoolMod": "on",
|
||||
"parliamentMod": "on",
|
||||
"courtsMod": "on",
|
||||
"favoritesMod": "on",
|
||||
|
|
@ -65,6 +125,7 @@ if (!fs.existsSync(configFilePath)) {
|
|||
"mapsMod": "on",
|
||||
"chatsMod": "on",
|
||||
"torrentsMod": "on",
|
||||
"filesMod": "on",
|
||||
"graphosMod": "on",
|
||||
"larpMod": "on"
|
||||
},
|
||||
|
|
@ -74,9 +135,6 @@ if (!fs.existsSync(configFilePath)) {
|
|||
"pass": "",
|
||||
"fee": "5"
|
||||
},
|
||||
"walletPub": {
|
||||
"pubId": ""
|
||||
},
|
||||
"ai": {
|
||||
"prompt": "Provide an informative and precise response."
|
||||
},
|
||||
|
|
@ -87,20 +145,29 @@ if (!fs.existsSync(configFilePath)) {
|
|||
"language": "en",
|
||||
"wish": "whole",
|
||||
"pmVisibility": "whole",
|
||||
"phone": { "visibility": "mutuals", "dnd": false, "relay": true },
|
||||
"lanBroadcasting": true
|
||||
};
|
||||
fs.writeFileSync(configFilePath, JSON.stringify(defaultConfig, null, 2));
|
||||
writeConfigFile(JSON.stringify(defaultConfig, null, 2));
|
||||
}
|
||||
|
||||
const getConfig = () => {
|
||||
const now = Date.now();
|
||||
if (!configCache.cfg || now - configCache.at > CONFIG_TTL_MS) configCache = { at: now, cfg: readConfig() };
|
||||
return structuredClone(configCache.cfg);
|
||||
};
|
||||
|
||||
const readConfig = () => {
|
||||
const configData = fs.readFileSync(configFilePath);
|
||||
const cfg = JSON.parse(configData);
|
||||
if (!['whole', 'mutuals', 'only-lan'].includes(cfg.wish)) cfg.wish = 'whole';
|
||||
if (!['whole', 'mutuals', 'only-lan', 'local'].includes(cfg.wish)) cfg.wish = 'whole';
|
||||
if (cfg.pmVisibility !== 'whole' && cfg.pmVisibility !== 'mutuals') cfg.pmVisibility = 'whole';
|
||||
if (!cfg.phone || typeof cfg.phone !== 'object') cfg.phone = {};
|
||||
cfg.phone = { visibility: cfg.phone.visibility === 'whole' ? 'whole' : 'mutuals', dnd: cfg.phone.dnd === true, relay: cfg.phone.relay !== false, video: cfg.phone.video !== false, ...(typeof cfg.phone.videoDevice === 'string' && cfg.phone.videoDevice.trim() ? { videoDevice: cfg.phone.videoDevice.trim() } : {}) };
|
||||
if (typeof cfg.ux === 'string') cfg.ux = { current: cfg.ux };
|
||||
if (!cfg.ux || typeof cfg.ux !== 'object') cfg.ux = { current: 'blocks' };
|
||||
if (cfg.ux.current === 'menus') cfg.ux.current = 'blocks';
|
||||
if (cfg.ux.current !== 'blocks' && cfg.ux.current !== 'ainav' && cfg.ux.current !== 'chats') cfg.ux.current = 'blocks';
|
||||
if (cfg.ux.current !== 'blocks' && cfg.ux.current !== 'ainav' && cfg.ux.current !== 'chats' && cfg.ux.current !== 'feed' && cfg.ux.current !== 'phone') cfg.ux.current = 'blocks';
|
||||
if (cfg.ux.current === 'ainav' && cfg.modules && cfg.modules.aiNavMod !== 'on') cfg.ux.current = 'blocks';
|
||||
if (cfg.ux.current === 'chats' && cfg.modules && cfg.modules.chatsMod !== 'on') cfg.ux.current = 'blocks';
|
||||
|
||||
|
|
@ -113,14 +180,30 @@ const getConfig = () => {
|
|||
pins = pins.filter((k, i) => pins.indexOf(k) === i).slice(0, 4);
|
||||
cfg.bottomBarPins = pins;
|
||||
delete cfg.bottomBarPin;
|
||||
if (cfg.ux.current === 'phone' && cfg.modules && cfg.modules.phoneMod === 'off') cfg.ux.current = 'blocks';
|
||||
if (cfg.ux.current === 'feed' && cfg.modules && cfg.modules.feedMod === 'off') cfg.ux.current = 'blocks';
|
||||
if (!cfg.blobCache || typeof cfg.blobCache !== 'object' || !Number.isFinite(Number(cfg.blobCache.maxMB))) cfg.blobCache = { maxMB: 0 };
|
||||
if (!Number.isFinite(Number(cfg.blobCache.pubMaxMB))) cfg.blobCache.pubMaxMB = 0;
|
||||
if (cfg.modules && typeof cfg.modules === 'object') {
|
||||
if (cfg.modules.backupMod === undefined) cfg.modules.backupMod = cfg.modules.legacyMod === 'off' ? 'off' : 'on';
|
||||
for (const mod of ['wikiMod', 'emergenciesMod', 'mailingMod', 'logisticsMod', 'podcastsMod', 'campaignsMod', 'filesMod', 'phoneMod', 'roomsMod']) {
|
||||
if (cfg.modules[mod] === undefined) cfg.modules[mod] = 'on';
|
||||
}
|
||||
}
|
||||
return cfg;
|
||||
};
|
||||
|
||||
const saveConfig = (newConfig) => {
|
||||
fs.writeFileSync(configFilePath, JSON.stringify(newConfig, null, 2));
|
||||
writeConfigFile(JSON.stringify(newConfig, null, 2));
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
configFilePath,
|
||||
serverConfigFilePath,
|
||||
getConfig,
|
||||
saveConfig,
|
||||
readServerConfig,
|
||||
saveServerConfig,
|
||||
readInviteCodes,
|
||||
defaultInvite,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
{
|
||||
"audios": [],
|
||||
"blogs": [],
|
||||
"bookmarks": [],
|
||||
"calendars": [],
|
||||
"chats": [],
|
||||
"documents": [],
|
||||
"events": [],
|
||||
"forum": [],
|
||||
"housing": [],
|
||||
"images": [],
|
||||
"jobs": [],
|
||||
"logs": [],
|
||||
"maps": [],
|
||||
"market": [],
|
||||
"pads": [],
|
||||
"polls": [],
|
||||
"projects": [],
|
||||
"reports": [],
|
||||
"shopProducts": [],
|
||||
"shops": [],
|
||||
"tasks": [],
|
||||
"torrents": [],
|
||||
"transfers": [],
|
||||
"videos": [],
|
||||
"votes": []
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
{"pending":[],"accepted":[],"lastAcceptMs":0}
|
||||
23
src/configs/invite-codes.json
Normal file
23
src/configs/invite-codes.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
[
|
||||
{
|
||||
"name": "SNH \"La Plaza\"",
|
||||
"description": "A shared place to begin an experimental utopia ...",
|
||||
"url": "https://solarnethub.com",
|
||||
"code": "pub.solarnethub.com:8008:@0qSCyK3xyL71X4qKkmf84Cb2riP6OeUqxCvbP2Z6HWs=.ed25519~7mC1c1gBJe8pmnWI9eIUYpFtEPUOiuHAxbzfxP/f0Ak=",
|
||||
"createdAt": "2026-05-24T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"name": "t0k10 h4x0rs",
|
||||
"description": "n0 l0gin. n0 m41l. n0 cl0ud...",
|
||||
"url": "https://pub.elhack.net",
|
||||
"code": "pub.elhack.net:8008:@R8WWz72Pz66Jw0SObjxe9Boio5WzskGpTK7wsPzeUEc=.ed25519~HaBhhmtYnYYfBA2j+JoYnckWXhPSzFGn0TCY1I9rP98=",
|
||||
"createdAt": "2026-08-11T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"name": "Acampada 26s",
|
||||
"description": "Red de la acampada en Madrid del #26s",
|
||||
"url": "https://acampada26s.net",
|
||||
"code": "madrid.acampada26s.net:8008:@Wt9tL9XtJCb7sZjX0+FtDlzziFLumWo1DAbou4Q5m3o=.ed25519~tkAoj1gE6dX8AcAOLQXs22VWFWKfOeJdT+7pBPfgbDA=",
|
||||
"createdAt": "2026-09-26T00:00:00.000Z"
|
||||
}
|
||||
]
|
||||
|
|
@ -11,9 +11,7 @@
|
|||
"fediverseMod": "on",
|
||||
"invitesMod": "on",
|
||||
"walletMod": "off",
|
||||
"legacyMod": "on",
|
||||
"devMod": "off",
|
||||
"industryMod": "on",
|
||||
"cipherMod": "on",
|
||||
"bookmarksMod": "on",
|
||||
"videosMod": "on",
|
||||
|
|
@ -34,17 +32,20 @@
|
|||
"calendarsMod": "on",
|
||||
"transfersMod": "off",
|
||||
"feedMod": "on",
|
||||
"pixeliaMod": "on",
|
||||
"pixeliaMod": "off",
|
||||
"melodyMod": "on",
|
||||
"agendaMod": "on",
|
||||
"aiMod": "off",
|
||||
"aiNavMod": "off",
|
||||
"forumMod": "on",
|
||||
"gamesMod": "off",
|
||||
"housingMod": "on",
|
||||
"jobsMod": "on",
|
||||
"shopsMod": "on",
|
||||
"projectsMod": "on",
|
||||
"industryMod": "on",
|
||||
"bankingMod": "off",
|
||||
"schoolMod": "on",
|
||||
"parliamentMod": "on",
|
||||
"courtsMod": "off",
|
||||
"favoritesMod": "on",
|
||||
|
|
@ -52,15 +53,24 @@
|
|||
"mapsMod": "off",
|
||||
"chatsMod": "on",
|
||||
"torrentsMod": "off",
|
||||
"filesMod": "on",
|
||||
"phoneMod": "on",
|
||||
"roomsMod": "on",
|
||||
"graphosMod": "on",
|
||||
"larpMod": "on",
|
||||
"housingMod": "off"
|
||||
"backupMod": "on",
|
||||
"campaignsMod": "on",
|
||||
"emergenciesMod": "on",
|
||||
"logisticsMod": "on",
|
||||
"mailingMod": "on",
|
||||
"podcastsMod": "on",
|
||||
"wikiMod": "on"
|
||||
},
|
||||
"wallet": {
|
||||
"url": "http://localhost:7474",
|
||||
"url": "http://127.0.0.1:7474",
|
||||
"user": "",
|
||||
"pass": "",
|
||||
"fee": "5"
|
||||
"fee": "0.01"
|
||||
},
|
||||
"walletPub": {
|
||||
"pubId": ""
|
||||
|
|
@ -76,6 +86,15 @@
|
|||
"wish": "whole",
|
||||
"pmVisibility": "whole",
|
||||
"lanBroadcasting": true,
|
||||
"phone": {
|
||||
"visibility": "whole",
|
||||
"dnd": false,
|
||||
"relay": true
|
||||
},
|
||||
"blobCache": {
|
||||
"maxMB": 0,
|
||||
"pubMaxMB": 0
|
||||
},
|
||||
"bottomBarPins": [
|
||||
"search",
|
||||
"inbox",
|
||||
|
|
@ -91,4 +110,4 @@
|
|||
},
|
||||
"relayOnly": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,13 +11,6 @@
|
|||
"dunbar": 300,
|
||||
"hops": 2
|
||||
},
|
||||
"gossip": {
|
||||
"connections": 20,
|
||||
"local": true,
|
||||
"friends": true,
|
||||
"seed": true,
|
||||
"global": true
|
||||
},
|
||||
"replicationScheduler": {
|
||||
"autostart": true,
|
||||
"partialReplication": null
|
||||
|
|
@ -54,7 +47,11 @@
|
|||
}
|
||||
],
|
||||
"tunnel": [],
|
||||
"onion": [],
|
||||
"onion": [
|
||||
{
|
||||
"transform": "shs"
|
||||
}
|
||||
],
|
||||
"ws": []
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,65 @@
|
|||
let _inboxCount = 0;
|
||||
let _phoneCount = 0;
|
||||
let _phoneCall = null;
|
||||
let _phoneRoom = null;
|
||||
let _pubIds = new Set();
|
||||
let _inboxPmCount = 0;
|
||||
let _inboxNotifCount = 0;
|
||||
let _carbonHcT = 0;
|
||||
let _walletReady = false;
|
||||
let _donatableAuthors = new Set();
|
||||
let _carbonHcH = 0;
|
||||
let _lastRefresh = 0;
|
||||
let _onlinePeers = null;
|
||||
let _syncedPeers = null;
|
||||
let _inboxUnread = null;
|
||||
let _lastSyncTs = null;
|
||||
let _ecoValue = null;
|
||||
let _lastActivity = null;
|
||||
let _maxBlockBytes = 0;
|
||||
let _inhabitantCount = 0;
|
||||
let _tribesCount = 0;
|
||||
let _mentionsCount = 0;
|
||||
let _mentionsTotal = 0;
|
||||
let _bestMatch = null;
|
||||
let _dismissedSuggestion = null;
|
||||
let _matchPool = [];
|
||||
let _matchIdx = -1;
|
||||
let _sectionMatches = new Map();
|
||||
const _dismissedSuggestions = new Set();
|
||||
let _suggestionsQuiet = false;
|
||||
let _featuredEmergency = null;
|
||||
let _dismissedEmergency = null;
|
||||
|
||||
module.exports = {
|
||||
getInboxCount: () => _inboxCount,
|
||||
setInboxCount: (n) => { _inboxCount = n; },
|
||||
getPhoneCount: () => _phoneCount,
|
||||
setPhoneCount: (n) => { _phoneCount = Number(n) || 0; },
|
||||
getPhoneCall: () => _phoneCall,
|
||||
setPhoneCall: (st) => { _phoneCall = st || null; },
|
||||
getPhoneRoom: () => _phoneRoom,
|
||||
setPhoneRoom: (st) => { _phoneRoom = st || null; },
|
||||
getPubIds: () => _pubIds,
|
||||
setPubIds: (set) => { _pubIds = set instanceof Set ? set : new Set(); },
|
||||
isPubId: (id) => _pubIds.has(String(id || '')),
|
||||
getInboxPmCount: () => _inboxPmCount,
|
||||
setInboxPmCount: (n) => { _inboxPmCount = n; },
|
||||
getInboxNotifCount: () => _inboxNotifCount,
|
||||
setInboxNotifCount: (n) => { _inboxNotifCount = n; },
|
||||
getCarbonHcT: () => _carbonHcT,
|
||||
setCarbonHcT: (n) => { _carbonHcT = n; },
|
||||
getDonatableAuthors: () => _donatableAuthors,
|
||||
setDonatableAuthors: (set) => { _donatableAuthors = set instanceof Set ? set : new Set(); },
|
||||
getWalletReady: () => _walletReady,
|
||||
setWalletReady: (v) => { _walletReady = !!v; },
|
||||
getCarbonHcH: () => _carbonHcH,
|
||||
setCarbonHcH: (n) => { _carbonHcH = n; },
|
||||
getLastRefresh: () => _lastRefresh,
|
||||
setLastRefresh: (t) => { _lastRefresh = t; },
|
||||
getOnlinePeerCount: () => _onlinePeers,
|
||||
setOnlinePeerCount: (n) => { _onlinePeers = n; },
|
||||
getSyncedPeerCount: () => _syncedPeers,
|
||||
setSyncedPeerCount: (n) => { _syncedPeers = Math.max(0, Number(n) || 0); },
|
||||
getInboxUnreadCount: () => _inboxUnread,
|
||||
setInboxUnreadCount: (n) => { _inboxUnread = n; },
|
||||
getLastSyncTs: () => _lastSyncTs,
|
||||
|
|
@ -33,12 +70,36 @@ module.exports = {
|
|||
setLastActivity: (a) => { _lastActivity = a; },
|
||||
getMaxBlockBytes: () => _maxBlockBytes,
|
||||
setMaxBlockBytes: (n) => { if (Number(n) > _maxBlockBytes) _maxBlockBytes = Number(n); },
|
||||
getMentionsTotal: () => _mentionsTotal,
|
||||
setMentionsTotal: (n) => { _mentionsTotal = Math.max(0, Number(n) || 0); },
|
||||
getMentionsCount: () => _mentionsCount,
|
||||
setMentionsCount: (n) => { _mentionsCount = Math.max(0, Number(n) || 0); },
|
||||
getInhabitantCount: () => _inhabitantCount,
|
||||
setInhabitantCount: (n) => { _inhabitantCount = Math.max(0, Number(n) || 0); },
|
||||
getTribesCount: () => _tribesCount,
|
||||
setTribesCount: (n) => { _tribesCount = Math.max(0, Number(n) || 0); },
|
||||
getFeaturedEmergency: () => _featuredEmergency,
|
||||
setFeaturedEmergency: (a) => { _featuredEmergency = a || null; },
|
||||
getDismissedEmergency: () => _dismissedEmergency,
|
||||
setDismissedEmergency: (id) => { _dismissedEmergency = id || null; },
|
||||
getBestMatch: () => _bestMatch,
|
||||
setBestMatch: (m) => { _bestMatch = m || null; },
|
||||
getDismissedSuggestion: () => _dismissedSuggestion,
|
||||
setDismissedSuggestion: (href) => { _dismissedSuggestion = href || null; }
|
||||
setMatchPool: (pool) => {
|
||||
const before = new Set(_matchPool.map(m => m.href));
|
||||
_matchPool = Array.isArray(pool) ? pool.filter(m => m && m.href) : [];
|
||||
if (_matchPool.some(m => !before.has(m.href) && !_dismissedSuggestions.has(m.href))) _suggestionsQuiet = false;
|
||||
},
|
||||
nextBestMatch: () => {
|
||||
const avail = _matchPool.filter(m => !_dismissedSuggestions.has(m.href));
|
||||
if (!avail.length) { _bestMatch = null; return null; }
|
||||
_matchIdx = (_matchIdx + 1) % avail.length;
|
||||
_bestMatch = avail[_matchIdx];
|
||||
return _bestMatch;
|
||||
},
|
||||
setSectionMatches: (map) => { _sectionMatches = map instanceof Map ? map : new Map(); },
|
||||
getSectionMatch: (section) => (_sectionMatches.get(String(section || '')) || []).find(m => !_dismissedSuggestions.has(m.href)) || null,
|
||||
isKnownSuggestion: (href) => !!href && (_matchPool.some(m => m.href === href) || [..._sectionMatches.values()].some(list => list.some(m => m.href === href))),
|
||||
dismissSuggestion: (href) => { if (href) { _dismissedSuggestions.add(href); _suggestionsQuiet = true; } },
|
||||
areSuggestionsQuiet: () => _suggestionsQuiet,
|
||||
isSuggestionDismissed: (href) => _dismissedSuggestions.has(href)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"name": "SNH \"La Plaza\"",
|
||||
"description": "A shared place to begin a utopia ...",
|
||||
"url": "https://pub.solarnethub.com",
|
||||
"code": "pub.solarnethub.com:8008:@0qSCyK3xyL71X4qKkmf84Cb2riP6OeUqxCvbP2Z6HWs=.ed25519~7mC1c1gBJe8pmnWI9eIUYpFtEPUOiuHAxbzfxP/f0Ak=",
|
||||
"createdAt": "2026-05-24T00:00:00.000Z"
|
||||
}
|
||||
112
src/configs/state-manager.js
Normal file
112
src/configs/state-manager.js
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const LEGACY_CONFIG_DIR = __dirname;
|
||||
const ROOT = 'oasis';
|
||||
|
||||
const FOLDERS = {
|
||||
'wallet-addresses.json': 'banking',
|
||||
'banking-address-book.json': 'banking',
|
||||
'banking-allocations.json': 'banking',
|
||||
'banking-eco-history.json': 'banking',
|
||||
'banking-epochs.json': 'banking',
|
||||
'banking-ubi-notice.json': 'banking',
|
||||
'banking-confirm-notice.json': 'banking',
|
||||
'banking-ubi-paid.json': 'banking',
|
||||
'banking-funds-history.json': 'banking',
|
||||
'content_favorites.json': 'content',
|
||||
'blob-access.json': 'content',
|
||||
'follow_state.json': 'content',
|
||||
'invites-used.json': 'content',
|
||||
'agenda-config.json': 'content',
|
||||
'AI-history.json': 'ai',
|
||||
'AI-vectors.json': 'ai',
|
||||
'AI-search-vectors.json': 'ai',
|
||||
'fediverse-accounts.json': 'multiverse',
|
||||
'nextcloud-account.json': 'multiverse',
|
||||
'oasis-backup.json': 'backup',
|
||||
'oasis-first-contact': 'flags',
|
||||
'oasis-mentions-seen': 'flags',
|
||||
'oasis-inbox-read': 'flags',
|
||||
'oasis-inbox-archived': 'flags',
|
||||
'oasis-political-seen': 'flags',
|
||||
'gossip_unfollowed.json': 'peers',
|
||||
'lan-peers.json': 'peers',
|
||||
'peer-health.json': 'peers',
|
||||
'phone-history.json': 'phone',
|
||||
'phone-seen.json': 'phone',
|
||||
'snapshot.oasissn': 'content',
|
||||
'snapshot-recent.oasissn': 'content'
|
||||
};
|
||||
|
||||
const ALIASES = {
|
||||
'oasis-backup.json': ['backup.json']
|
||||
};
|
||||
|
||||
const ssbDir = () => {
|
||||
try {
|
||||
const resolved = require('../server/ssb_config').statePath('.');
|
||||
if (resolved) return path.resolve(resolved);
|
||||
} catch (_) {}
|
||||
if (process.env.OASIS_STATE_DIR) return process.env.OASIS_STATE_DIR;
|
||||
return path.join(os.homedir(), '.ssb');
|
||||
};
|
||||
|
||||
const stateDir = (name) => path.join(ssbDir(), ROOT, FOLDERS[name] || '');
|
||||
|
||||
const statePath = (name) => {
|
||||
const dir = stateDir(name);
|
||||
const target = path.join(dir, name);
|
||||
try {
|
||||
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
||||
if (!fs.existsSync(target)) {
|
||||
const candidates = [name, ...(ALIASES[name] || [])]
|
||||
.flatMap(n => [path.join(ssbDir(), n), path.join(LEGACY_CONFIG_DIR, n)]);
|
||||
const legacy = candidates.find(p => fs.existsSync(p));
|
||||
if (legacy) {
|
||||
try { fs.renameSync(legacy, target); }
|
||||
catch (_) { fs.copyFileSync(legacy, target); try { fs.unlinkSync(legacy); } catch (_) {} }
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
return target;
|
||||
};
|
||||
|
||||
const keysDir = (base = null) => {
|
||||
const root = ssbDir();
|
||||
if (base && path.resolve(base) !== root) return path.join(base, 'keys');
|
||||
const target = path.join(root, ROOT, 'keys');
|
||||
try {
|
||||
if (!fs.existsSync(target)) {
|
||||
fs.mkdirSync(path.dirname(target), { recursive: true });
|
||||
const legacy = path.join(root, 'keys');
|
||||
if (fs.existsSync(legacy)) fs.renameSync(legacy, target);
|
||||
else fs.mkdirSync(target, { recursive: true, mode: 0o700 });
|
||||
}
|
||||
} catch (_) {}
|
||||
return target;
|
||||
};
|
||||
|
||||
const migrateAll = () => {
|
||||
for (const name of Object.keys(FOLDERS)) {
|
||||
try {
|
||||
const legacy = [name, ...(ALIASES[name] || [])]
|
||||
.flatMap(n => [path.join(ssbDir(), n), path.join(LEGACY_CONFIG_DIR, n)])
|
||||
.find(p => fs.existsSync(p));
|
||||
if (legacy) statePath(name);
|
||||
} catch (_) {}
|
||||
}
|
||||
for (const name of Object.keys(FOLDERS)) {
|
||||
if (FOLDERS[name] !== 'flags' || name.endsWith('.json')) continue;
|
||||
const legacy = path.join(ssbDir(), name);
|
||||
const target = path.join(stateDir(name), name);
|
||||
try { if (fs.existsSync(legacy) && fs.existsSync(target)) fs.unlinkSync(legacy); } catch (_) {}
|
||||
}
|
||||
for (const leftover of ['keys', 'node_modules']) {
|
||||
const dir = path.join(ssbDir(), leftover);
|
||||
try { if (fs.statSync(dir).isDirectory() && fs.readdirSync(dir).length === 0) fs.rmdirSync(dir); } catch (_) {}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { statePath, stateDir, ssbDir, keysDir, migrateAll };
|
||||
|
|
@ -1 +0,0 @@
|
|||
{}
|
||||
Loading…
Add table
Add a link
Reference in a new issue