Oasis 0.8.7 mobile (nodejs-project baseline)

This commit is contained in:
s1to 2026-07-09 09:56:30 +02:00
commit e563b86ede
226 changed files with 141968 additions and 0 deletions

191
src/server/SSB_server.js Normal file
View file

@ -0,0 +1,191 @@
#!/usr/bin/env node
const moduleAlias = require('module-alias');
moduleAlias.addAlias('punycode', 'punycode/');
const fs = require('fs');
const path = require('path');
const SecretStack = require('secret-stack');
const caps = require('ssb-caps');
const SSB = require('ssb-db');
const config = require('./ssb_config');
const { printMetadata } = require('./ssb_metadata');
(() => {
const realErr = console.error;
const SHS_NOISE = /shs\.server:|they dailed a wrong number|client hello invalid|invalid challenge|wrong application cap/i;
const EBT_NOISE = /stream ended with:\s*\d+\s+but wanted:\s*\d+/i;
const EBT_HANDSHAKE_NOISE = /does not support RPC ebt\./i;
const isEbtReplicateException = (args) =>
args.length >= 2 &&
typeof args[0] === 'string' &&
/rpc\.ebt\.replicate exception/i.test(args[0]) &&
args[1] && typeof args[1].message === 'string' && EBT_NOISE.test(args[1].message);
const parsePeer = (addr) => {
if (typeof addr !== 'string') return 'unknown';
const m = /net:(.+?):(\d+)(?:~|$)/.exec(addr);
if (!m) return addr;
return `${m[1].replace(/^::ffff:/, '')}:${m[2]}`;
};
const logRejection = (peer) => {
const ts = new Date().toISOString().replace('T', ' ').slice(0, 19);
realErr.call(console, `[${ts}] REJECTED ${peer} (wrong SHS cap)`);
};
console.error = function (...args) {
if (args.length >= 2 && args[0] === 'server error, from' && typeof args[1] === 'string' && args[1].includes('~shs:')) {
logRejection(parsePeer(args[1]));
return;
}
if (args.length >= 1 && args[0] && typeof args[0].message === 'string' && SHS_NOISE.test(args[0].message)) {
logRejection(parsePeer(args[0].address));
return;
}
if (args.length >= 1 && args[0] && typeof args[0].message === 'string' && EBT_NOISE.test(args[0].message)) return;
if (isEbtReplicateException(args)) return;
if (args.length >= 1 && typeof args[0] === 'string' && /rpc\.ebt\.replicate exception:.*stream ended with/i.test(args[0])) return;
return realErr.apply(console, args);
};
const realWarn = console.warn;
console.warn = function (...args) {
if (args.length >= 1 && typeof args[0] === 'string' && EBT_HANDSHAKE_NOISE.test(args[0])) return;
return realWarn.apply(console, args);
};
})();
require('ssb-plugins').loadUserPlugins(SecretStack({ caps }), config);
const Server = SecretStack({ caps })
.use(SSB)
.use(require('ssb-master'))
.use(require('ssb-gossip'))
.use(require('ssb-ebt'))
.use(require('ssb-friends'))
.use(require('ssb-blobs'))
.use(require('ssb-meme'))
.use(require('ssb-plugins'))
.use(require('ssb-conn'))
.use(require('ssb-box'))
.use(require('ssb-search'))
.use(require('ssb-private'))
.use(require('ssb-friend-pub'))
.use(config.pub ? require('ssb-invite') : require('ssb-invite-client'))
.use(require('ssb-logging'))
.use(require('ssb-replication-scheduler'))
.use(require('ssb-partial-replication'))
.use(require('ssb-about'))
.use(require('ssb-onion'))
.use(require('ssb-unix-socket'))
.use(require('ssb-no-auth'))
.use(require('ssb-backlinks'))
.use(require('ssb-links'))
.use(require('ssb-tangle'))
.use(require('ssb-query'));
if (config.autofollow && typeof config.autofollow === 'object' && !Array.isArray(config.autofollow)) {
if (config.autofollow.enabled === false) {
config.autofollow = null;
} else {
const feeds = Array.isArray(config.autofollow.feeds) ? config.autofollow.feeds : (Array.isArray(config.autofollow.suggestions) ? config.autofollow.suggestions : []);
config.autofollow = feeds.filter(f => typeof f === 'string' && f.length > 0);
}
}
if (config.autofollow && (Array.isArray(config.autofollow) ? config.autofollow.length > 0 : true)) {
Server.use(require('ssb-autofollow'));
}
const manifestFile = path.join(config.path, 'manifest.json');
let server;
const argv = process.argv.slice(2);
const isLockError = (err) => {
if (!err) return false;
if (err.name === 'OpenError') return true;
const msg = String(err.message || '');
return /Resource temporarily unavailable/i.test(msg) && /\.ssb\/.*LOCK/i.test(msg);
};
const handleFatal = (err) => {
if (isLockError(err)) {
console.log('');
console.log('Another Oasis instance is already running on this device. Close the other instance (or kill the process) and try again.');
console.log('');
process.exit(1);
}
throw err;
};
process.on('uncaughtException', handleFatal);
if (argv[0] === 'start') {
try {
server = Server(config);
} catch (err) {
handleFatal(err);
}
fs.writeFileSync(manifestFile, JSON.stringify(server.getManifest(), null, 2));
const { cmdAliases } = require('../client/cli-cmd-aliases');
const manifest = server.getManifest();
for (const k in cmdAliases) {
server[k] = server[cmdAliases[k]];
manifest[k] = manifest[cmdAliases[k]];
}
manifest.config = 'sync';
server.config = cb => {
console.log(JSON.stringify(config, null, 2));
cb();
};
if (process.stdout.isTTY && config.logging?.level !== 'info') {
const showProgress = () => {
let prog = -1;
const bar = r => '\r' + '*'.repeat(Math.floor(r * 50)) + '.'.repeat(50 - Math.floor(r * 50));
const percent = r => (Math.round(r * 10000) / 100).toFixed(2) + '%';
const rate = prog => prog.target === prog.current ? 1 : (prog.current - prog.start) / (prog.target - prog.start);
const interval = setInterval(() => {
const p = server.progress();
let r = 1;
const tasks = [];
for (const k in p) {
const pr = rate(p[k]);
if (pr < 1) tasks.push(`${k}:${percent(pr)}`);
r = Math.min(r, pr);
}
if (r !== prog) {
prog = r;
process.stdout.write(bar(r) + ` (${tasks.join(', ')})\x1b[K\r`);
}
}, 333);
interval.unref?.();
};
showProgress();
}
const { printMetadata, colors } = require('./ssb_metadata');
printMetadata('OASIS Server Only', colors.cyan, null);
setTimeout(() => {
try {
const pull = require('pull-stream');
const stream = server.conn && server.conn.hub && server.conn.hub().listen && server.conn.hub().listen();
if (!stream) return;
pull(stream, pull.drain((ev) => {
if (!ev || !ev.type) return;
const ts = new Date().toISOString().replace('T', ' ').slice(0, 19);
if (ev.type === 'connected') {
console.log(`[${ts}] CONNECTED ${ev.address || ''}`);
} else if (ev.type === 'disconnected') {
console.log(`[${ts}] DISCONNECTED ${ev.address || ''}`);
}
}, () => {}));
} catch (_) {}
}, 1000);
}
module.exports = {
config,
server: server || Server(config),
open: async () => server || Server(config)
};