Oasis 0.9.5 con Karvan para escritorio
Rama de desarrollo experimental sobre el Oasis de epsylon, sin afiliacion con el proyecto original. Base: 0.9.5. Trae el modulo Karvan —salas efimeras en memoria con autodestruccion por TTL, chat que funciona sin JavaScript y llamadas de audio y video sobre WebRTC— con el enlace en el menu lateral por el mismo patron que el resto de modulos: renderKarvanLink calcado de renderPollsLink, entrada en modules_view y en la lista de /modules. Los estilos del modulo van en su propia hoja, styles/karvan.css, siguiendo el patron de highlight.css: asi se ve igual con cualquier tema, sin depender del tema movil. Sin servidores ICE de terceros: no hay STUN de fabrica, porque un STUN aprende la IP publica y el momento de cada llamada. Solo candidatos host salvo que se configure un TURN propio en oasis-config.json, con credenciales efimeras por el mecanismo REST de coturn. El codigo propio vive separado —views/fork/, backend/fork_routes.js, models/karvan_model.js y translations/fork/— de modo que sobre los ficheros de upstream solo hay enganches de una linea y cada version nueva se integra sin arrastrar nada. Respecto al arbol de Android: fuera el wrapper y main.js, que es su arranque; el tema vuelve a Dark-SNH y se restauran los modulos que en movil se recortan por peso. Verificado arrancando sin OASIS_MOBILE: nueve rutas OK, menu lateral de upstream con 27 grupos y el enlace de Karvan, cero rastro de la interfaz movil (ni hexagonos, ni topbar, ni barra inferior), y una sala creada y servida.
This commit is contained in:
commit
ddd2787b73
286 changed files with 157145 additions and 0 deletions
192
scripts/build-deb.sh
Executable file
192
scripts/build-deb.sh
Executable file
|
|
@ -0,0 +1,192 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
VERSION="0.7.8"
|
||||
PKG_NAME="oasis"
|
||||
ARCH=$(dpkg --print-architecture)
|
||||
SRC_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
BUILD_DIR="/tmp/oasis-deb-build"
|
||||
DEB_ROOT="${BUILD_DIR}/${PKG_NAME}_${VERSION}_${ARCH}"
|
||||
INSTALL_DIR="/opt/oasis"
|
||||
|
||||
if [ "$1" = "--arm64" ]; then
|
||||
ARCH="arm64"
|
||||
DEB_ROOT="${BUILD_DIR}/${PKG_NAME}_${VERSION}_${ARCH}"
|
||||
fi
|
||||
|
||||
echo "=== Building Oasis ${VERSION} .deb (${ARCH}) ==="
|
||||
|
||||
rm -rf "${DEB_ROOT}"
|
||||
mkdir -p "${DEB_ROOT}/DEBIAN"
|
||||
mkdir -p "${DEB_ROOT}${INSTALL_DIR}/src/server"
|
||||
mkdir -p "${DEB_ROOT}${INSTALL_DIR}/src/backend"
|
||||
mkdir -p "${DEB_ROOT}${INSTALL_DIR}/src/views"
|
||||
mkdir -p "${DEB_ROOT}${INSTALL_DIR}/src/models"
|
||||
mkdir -p "${DEB_ROOT}${INSTALL_DIR}/src/client"
|
||||
mkdir -p "${DEB_ROOT}${INSTALL_DIR}/src/configs"
|
||||
mkdir -p "${DEB_ROOT}${INSTALL_DIR}/src/AI"
|
||||
mkdir -p "${DEB_ROOT}${INSTALL_DIR}/scripts"
|
||||
mkdir -p "${DEB_ROOT}${INSTALL_DIR}/docs"
|
||||
mkdir -p "${DEB_ROOT}/usr/bin"
|
||||
mkdir -p "${DEB_ROOT}/usr/share/applications"
|
||||
mkdir -p "${DEB_ROOT}/usr/share/doc/${PKG_NAME}"
|
||||
mkdir -p "${DEB_ROOT}/lib/systemd/system"
|
||||
|
||||
echo "Copying application files..."
|
||||
|
||||
cp -r "${SRC_DIR}/src/server/package.json" "${DEB_ROOT}${INSTALL_DIR}/src/server/"
|
||||
cp -r "${SRC_DIR}/src/server/package-lock.json" "${DEB_ROOT}${INSTALL_DIR}/src/server/" 2>/dev/null || true
|
||||
cp "${SRC_DIR}/src/server/"*.js "${DEB_ROOT}${INSTALL_DIR}/src/server/"
|
||||
|
||||
if [ -d "${SRC_DIR}/src/server/packages" ]; then
|
||||
cp -r "${SRC_DIR}/src/server/packages" "${DEB_ROOT}${INSTALL_DIR}/src/server/"
|
||||
find "${DEB_ROOT}${INSTALL_DIR}/src/server/packages" -name "node_modules" -type d -exec rm -rf {} + 2>/dev/null || true
|
||||
fi
|
||||
|
||||
cp "${SRC_DIR}/src/backend/"*.js "${DEB_ROOT}${INSTALL_DIR}/src/backend/"
|
||||
cp -r "${SRC_DIR}/src/views/"*.js "${DEB_ROOT}${INSTALL_DIR}/src/views/"
|
||||
cp -r "${SRC_DIR}/src/models/"*.js "${DEB_ROOT}${INSTALL_DIR}/src/models/"
|
||||
cp -r "${SRC_DIR}/src/client" "${DEB_ROOT}${INSTALL_DIR}/src/"
|
||||
find "${DEB_ROOT}${INSTALL_DIR}/src/client" -name "*.py" -delete 2>/dev/null
|
||||
find "${DEB_ROOT}${INSTALL_DIR}/src/client" -name ".ruff_cache" -type d -exec rm -rf {} + 2>/dev/null || true
|
||||
for f in oasis-config.json server-config.json snh-invite-code.json config-manager.js shared-state.js; do
|
||||
cp "${SRC_DIR}/src/configs/${f}" "${DEB_ROOT}${INSTALL_DIR}/src/configs/"
|
||||
done
|
||||
cp "${SRC_DIR}/src/AI/"*.js "${DEB_ROOT}${INSTALL_DIR}/src/AI/" 2>/dev/null || true
|
||||
cp "${SRC_DIR}/src/AI/"*.mjs "${DEB_ROOT}${INSTALL_DIR}/src/AI/" 2>/dev/null || true
|
||||
if [ -d "${SRC_DIR}/src/AI/embeddings" ]; then
|
||||
cp -r "${SRC_DIR}/src/AI/embeddings" "${DEB_ROOT}${INSTALL_DIR}/src/AI/"
|
||||
fi
|
||||
cp -r "${SRC_DIR}/scripts" "${DEB_ROOT}${INSTALL_DIR}/"
|
||||
if [ -d "${SRC_DIR}/docs/PUB" ]; then
|
||||
mkdir -p "${DEB_ROOT}${INSTALL_DIR}/docs/PUB"
|
||||
cp "${SRC_DIR}/docs/PUB/"* "${DEB_ROOT}${INSTALL_DIR}/docs/PUB/" 2>/dev/null || true
|
||||
fi
|
||||
cp "${SRC_DIR}/oasis.sh" "${DEB_ROOT}${INSTALL_DIR}/"
|
||||
cp "${SRC_DIR}/install.sh" "${DEB_ROOT}${INSTALL_DIR}/" 2>/dev/null || true
|
||||
cp "${SRC_DIR}/LICENSE" "${DEB_ROOT}${INSTALL_DIR}/"
|
||||
cp "${SRC_DIR}/README.md" "${DEB_ROOT}${INSTALL_DIR}/" 2>/dev/null || true
|
||||
|
||||
cat > "${DEB_ROOT}/DEBIAN/control" << EOF
|
||||
Package: ${PKG_NAME}
|
||||
Version: ${VERSION}
|
||||
Architecture: ${ARCH}
|
||||
Maintainer: SolarNET.HuB <solarnethub@riseup.net>
|
||||
Depends: nodejs (>= 18)
|
||||
Recommends: npm
|
||||
Installed-Size: $(du -sk "${DEB_ROOT}${INSTALL_DIR}" | cut -f1)
|
||||
Section: net
|
||||
Priority: optional
|
||||
Homepage: https://solarnethub.com
|
||||
Description: Oasis P2P Social Network
|
||||
Oasis is a P2P encrypted social network built on Secure Scuttlebutt (SSB).
|
||||
Zero browser JavaScript — all rendering is server-side HTML+CSS.
|
||||
Part of the SolarNET.HuB ecosystem.
|
||||
EOF
|
||||
|
||||
cat > "${DEB_ROOT}/DEBIAN/postinst" << 'POSTINST'
|
||||
#!/bin/bash
|
||||
|
||||
INSTALL_DIR="/opt/oasis"
|
||||
|
||||
echo "Installing Node.js dependencies..."
|
||||
cd "${INSTALL_DIR}/src/server"
|
||||
npm install --production 2>&1 | tail -10
|
||||
node ../../scripts/patch-node-modules.js 2>/dev/null || true
|
||||
|
||||
if ! id -u oasis >/dev/null 2>&1; then
|
||||
useradd --system --home-dir /var/lib/oasis --create-home --shell /usr/sbin/nologin oasis
|
||||
fi
|
||||
|
||||
mkdir -p /var/lib/oasis/.ssb
|
||||
chown -R oasis:oasis /var/lib/oasis
|
||||
chown -R oasis:oasis "${INSTALL_DIR}"
|
||||
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
echo ""
|
||||
echo "=== Oasis installed ==="
|
||||
echo "Start: systemctl start oasis"
|
||||
echo "Enable: systemctl enable oasis"
|
||||
echo "Open: http://localhost:3000"
|
||||
echo ""
|
||||
POSTINST
|
||||
chmod 755 "${DEB_ROOT}/DEBIAN/postinst"
|
||||
|
||||
cat > "${DEB_ROOT}/DEBIAN/prerm" << 'PRERM'
|
||||
#!/bin/bash
|
||||
set -e
|
||||
systemctl stop oasis 2>/dev/null || true
|
||||
systemctl disable oasis 2>/dev/null || true
|
||||
PRERM
|
||||
chmod 755 "${DEB_ROOT}/DEBIAN/prerm"
|
||||
|
||||
cat > "${DEB_ROOT}/DEBIAN/postrm" << 'POSTRM'
|
||||
#!/bin/bash
|
||||
set -e
|
||||
if [ "$1" = "purge" ]; then
|
||||
rm -rf /opt/oasis
|
||||
userdel oasis 2>/dev/null || true
|
||||
rm -rf /var/lib/oasis
|
||||
fi
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
POSTRM
|
||||
chmod 755 "${DEB_ROOT}/DEBIAN/postrm"
|
||||
|
||||
cat > "${DEB_ROOT}/lib/systemd/system/oasis.service" << EOF
|
||||
[Unit]
|
||||
Description=Oasis P2P Social Network
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=oasis
|
||||
Group=oasis
|
||||
WorkingDirectory=${INSTALL_DIR}
|
||||
ExecStart=/bin/sh ${INSTALL_DIR}/oasis.sh
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
Environment=HOME=/var/lib/oasis
|
||||
Environment=NODE_ENV=production
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
cat > "${DEB_ROOT}/usr/bin/oasis" << 'LAUNCHER'
|
||||
#!/bin/sh
|
||||
exec /bin/sh /opt/oasis/oasis.sh "$@"
|
||||
LAUNCHER
|
||||
chmod 755 "${DEB_ROOT}/usr/bin/oasis"
|
||||
|
||||
cat > "${DEB_ROOT}/usr/share/applications/oasis.desktop" << EOF
|
||||
[Desktop Entry]
|
||||
Name=Oasis
|
||||
GenericName=P2P Social Network
|
||||
Exec=xdg-open http://localhost:3000
|
||||
Icon=applications-internet
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Network;Chat;InstantMessaging;
|
||||
EOF
|
||||
|
||||
cat > "${DEB_ROOT}/usr/share/doc/${PKG_NAME}/copyright" << EOF
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: Oasis
|
||||
Source: https://code.03c8.net/KrakensLab/snh-oasis
|
||||
|
||||
Files: *
|
||||
Copyright: 2022-2026 SolarNET.HuB / psy <epsylon@riseup.net>
|
||||
License: AGPL-3.0
|
||||
EOF
|
||||
|
||||
echo "Building .deb package..."
|
||||
DEB_FILE="${BUILD_DIR}/${PKG_NAME}_${VERSION}_${ARCH}.deb"
|
||||
dpkg-deb --build "${DEB_ROOT}" "${DEB_FILE}"
|
||||
|
||||
echo ""
|
||||
echo "=== Package built: ${DEB_FILE} ==="
|
||||
echo "Size: $(du -h "${DEB_FILE}" | cut -f1)"
|
||||
echo ""
|
||||
echo "Install: sudo dpkg -i ${DEB_FILE}"
|
||||
echo " sudo apt-get install -f"
|
||||
4
scripts/generate_shs.js
Normal file
4
scripts/generate_shs.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
const crypto = require('crypto');
|
||||
|
||||
const cap = crypto.randomBytes(32).toString('base64');
|
||||
console.log('New SHS cap:', cap);
|
||||
91
scripts/oasis-pub.js
Normal file
91
scripts/oasis-pub.js
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
#!/usr/bin/env node
|
||||
const path = require('path');
|
||||
const ssbConfig = require(path.join(__dirname, '..', 'src', 'server', 'ssb_config'));
|
||||
const ssbClient = require(path.join(__dirname, '..', 'src', 'server', 'node_modules', 'ssb-client'));
|
||||
|
||||
const socketPath = path.join(ssbConfig.path, 'socket');
|
||||
const publicInteger = (ssbConfig.keys.public || '').replace('.ed25519', '');
|
||||
const remote = `unix:${socketPath}~noauth:${publicInteger}`;
|
||||
|
||||
const cmd = process.argv[2];
|
||||
const args = process.argv.slice(3);
|
||||
|
||||
const usage = () => {
|
||||
console.error('Usage: sh oasis.sh <command> [args]');
|
||||
console.error('');
|
||||
console.error('PUB admin commands (sbot must be running: sh oasis.sh server):');
|
||||
console.error(' whoami Print this PUB id');
|
||||
console.error(' invite [N] Create an invite code (default uses=1)');
|
||||
console.error(' name <text> Set PUB display name');
|
||||
console.error(' announce <host> [port] Publish a pub address (default port=8008)');
|
||||
console.error(' follow <feedId> Follow another PUB');
|
||||
console.error(' status Show peer / replication status');
|
||||
console.error(' gossip List known gossip peers');
|
||||
process.exit(1);
|
||||
};
|
||||
|
||||
if (!cmd) usage();
|
||||
|
||||
const call = (fn, ...a) => new Promise((res, rej) => fn(...a, (e, r) => e ? rej(e) : res(r)));
|
||||
|
||||
ssbClient(ssbConfig.keys, { remote, caps: ssbConfig.caps }).then(async (ssb) => {
|
||||
try {
|
||||
switch (cmd) {
|
||||
case 'whoami': {
|
||||
const r = await call(ssb.whoami.bind(ssb));
|
||||
console.log(JSON.stringify(r, null, 2));
|
||||
break;
|
||||
}
|
||||
case 'invite': {
|
||||
const uses = Math.max(1, parseInt(args[0] || '1', 10));
|
||||
const code = await call(ssb.invite.create.bind(ssb.invite), uses);
|
||||
console.log(code);
|
||||
break;
|
||||
}
|
||||
case 'name': {
|
||||
const text = String(args[0] || '');
|
||||
if (!text) { console.error('Missing name'); process.exit(1); }
|
||||
const me = await call(ssb.whoami.bind(ssb));
|
||||
const r = await call(ssb.publish.bind(ssb), { type: 'about', about: me.id, name: text });
|
||||
console.log(JSON.stringify(r, null, 2));
|
||||
break;
|
||||
}
|
||||
case 'announce': {
|
||||
const host = args[0];
|
||||
const port = parseInt(args[1] || '8008', 10);
|
||||
if (!host) { console.error('Missing host'); process.exit(1); }
|
||||
const me = await call(ssb.whoami.bind(ssb));
|
||||
const r = await call(ssb.publish.bind(ssb), { type: 'pub', address: { key: me.id, host, port } });
|
||||
console.log(JSON.stringify(r, null, 2));
|
||||
break;
|
||||
}
|
||||
case 'follow': {
|
||||
const feedId = args[0];
|
||||
if (!feedId) { console.error('Missing feedId'); process.exit(1); }
|
||||
const r = await call(ssb.publish.bind(ssb), { type: 'contact', contact: feedId, following: true });
|
||||
console.log(JSON.stringify(r, null, 2));
|
||||
break;
|
||||
}
|
||||
case 'status': {
|
||||
const r = await call(ssb.status.bind(ssb));
|
||||
console.log(JSON.stringify(r, null, 2));
|
||||
break;
|
||||
}
|
||||
case 'gossip': {
|
||||
const r = await call(ssb.conn.dbPeers.bind(ssb.conn));
|
||||
console.log(JSON.stringify(r, null, 2));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
ssb.close();
|
||||
} catch (e) {
|
||||
console.error('Error:', e.message || e);
|
||||
process.exit(1);
|
||||
}
|
||||
}).catch((e) => {
|
||||
console.error('Connection error:', e.message || e);
|
||||
console.error('Is the Oasis sbot running? (sh oasis.sh server)');
|
||||
process.exit(1);
|
||||
});
|
||||
79
scripts/patch-node-modules.js
Normal file
79
scripts/patch-node-modules.js
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const log = (msg) => console.log(`[OASIS] [PATCH] ${msg}`);
|
||||
|
||||
// === Patch ssb-ref ===
|
||||
const ssbRefPath = path.resolve(__dirname, '../src/server/node_modules/ssb-ref/index.js');
|
||||
if (fs.existsSync(ssbRefPath)) {
|
||||
const data = fs.readFileSync(ssbRefPath, 'utf8');
|
||||
const alreadyClean = /exports\.parseAddress\s*=\s*parseAddress/.test(data);
|
||||
if (!alreadyClean) {
|
||||
const patched = data.replace(
|
||||
/exports\.parseAddress\s*=\s*deprecate\(\s*['"][^'"]*['"]\s*,\s*parseAddress\s*\)/,
|
||||
'exports.parseAddress = parseAddress'
|
||||
);
|
||||
if (patched !== data) {
|
||||
fs.writeFileSync(ssbRefPath, patched);
|
||||
log('Patched ssb-ref to remove deprecated usage of parseAddress');
|
||||
} else {
|
||||
log('ssb-ref patch skipped: unexpected parseAddress export format');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log('ssb-ref patch skipped: file not found at ' + ssbRefPath);
|
||||
}
|
||||
|
||||
// === Patch ssb-blobs ===
|
||||
const ssbBlobsPath = path.resolve(__dirname, '../src/server/node_modules/ssb-blobs/inject.js');
|
||||
if (fs.existsSync(ssbBlobsPath)) {
|
||||
let data = fs.readFileSync(ssbBlobsPath, 'utf8');
|
||||
|
||||
const marker = 'want: function (id, cb)';
|
||||
const startIndex = data.indexOf(marker);
|
||||
if (startIndex !== -1) {
|
||||
const endIndex = data.indexOf('},', startIndex);
|
||||
if (endIndex !== -1) {
|
||||
const before = data.slice(0, startIndex);
|
||||
const after = data.slice(endIndex + 2);
|
||||
|
||||
const replacement = `
|
||||
want: function (id, cb) {
|
||||
id = toBlobId(id);
|
||||
if (!isBlobId(id)) return cb(new Error('invalid id:' + id));
|
||||
|
||||
if (blobStore.isEmptyHash(id)) return cb(null, true);
|
||||
|
||||
if (wantCallbacks[id]) {
|
||||
if (!Array.isArray(wantCallbacks[id])) wantCallbacks[id] = [];
|
||||
wantCallbacks[id].push(cb);
|
||||
} else {
|
||||
wantCallbacks[id] = [cb];
|
||||
blobStore.size(id, function (err, size) {
|
||||
if (err) return cb(err);
|
||||
if (size != null) {
|
||||
while (wantCallbacks[id].length) {
|
||||
const fn = wantCallbacks[id].shift();
|
||||
if (typeof fn === 'function') fn(null, true);
|
||||
}
|
||||
delete wantCallbacks[id];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const peerId = findPeerWithBlob(id);
|
||||
if (peerId) get(peerId, id);
|
||||
|
||||
if (wantCallbacks[id]) registerWant(id);
|
||||
},`;
|
||||
|
||||
const finalData = before + replacement + after;
|
||||
fs.writeFileSync(ssbBlobsPath, finalData);
|
||||
log('Patched ssb-blobs to fix wantCallbacks handling');
|
||||
} else {
|
||||
log('ssb-blobs patch skipped: end of want function not found');
|
||||
}
|
||||
} else {
|
||||
log('ssb-blobs patch skipped: want function not found');
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue