console.log('[Oasis Mobile] Node.js backend starting...'); process.env.HOME = process.env.HOME || require('path').resolve(__dirname, '..', '..'); process.env.ssb_appname = process.env.ssb_appname || 'ssb'; process.env.OASIS_MOBILE = '1'; process.env.CHLORIDE_JS = '1'; const path = require('path'); const fs = require('fs'); const tmpDir = path.join(process.env.HOME, 'tmp'); try { fs.mkdirSync(tmpDir, { recursive: true }); } catch (e) {} process.env.TMPDIR = tmpDir; process.env.TMP = tmpDir; process.env.TEMP = tmpDir; try { require('os').tmpdir = () => tmpDir; } catch (e) {} try { const dns = require('dns'); const net = require('net'); const servers = ['8.8.8.8', '1.1.1.1', '8.8.4.4']; try { dns.setServers(servers); } catch (e) {} try { if (dns.promises && dns.promises.setServers) dns.promises.setServers(servers); } catch (e) {} const localResolve = (hostname, family) => { if (hostname === 'localhost' || hostname === 'localhost.localdomain' || hostname === 'ip6-localhost') { return family === 6 ? { address: '::1', family: 6 } : { address: '127.0.0.1', family: 4 }; } const ipv = net.isIP(hostname); if (ipv) return { address: hostname, family: ipv }; return null; }; const safeLookup = function (hostname, options, callback) { if (typeof options === 'function') { callback = options; options = {}; } options = options || {}; const family = options.family === 6 ? 6 : 4; const direct = localResolve(hostname, family); if (direct) { return process.nextTick(() => options.all ? callback(null, [direct]) : callback(null, direct.address, direct.family)); } const resolver = family === 6 ? dns.resolve6 : dns.resolve4; resolver.call(dns, hostname, (err, addrs) => { if (err || !addrs || !addrs.length) return callback(err || new Error('ENOTFOUND ' + hostname)); if (options.all) return callback(null, addrs.map((a) => ({ address: a, family }))); return callback(null, addrs[0], family); }); }; dns.lookup = safeLookup; if (dns.promises) { dns.promises.lookup = (hostname, options) => new Promise((resolve, reject) => { safeLookup(hostname, options || {}, (err, address, family) => { if (err) return reject(err); if (options && options.all) return resolve(address); resolve({ address, family }); }); }); } } catch (e) {} const backendPath = path.join(__dirname, 'src', 'backend', 'backend.js'); console.log('[Oasis] Launching Oasis Social Network Utopia...'); console.log('[Oasis] Backend path:', backendPath); console.log('[Oasis] HOME:', process.env.HOME); const http = require('http'); const keylock = require('./src/models/keylock'); const configPath = path.join(process.env.HOME, '.ssb'); let backendStarted = false; function startBackend() { if (backendStarted) return; backendStarted = true; try { require(backendPath); console.log('[Oasis] Backend started on port 3000'); } catch (error) { console.error('[Oasis] Error starting backend:', error); } } function unlockAndStart(keys) { global.__OASIS_KEYS__ = keys; for (const s of sockets) { try { s.destroy(); } catch (_) {} } try { lockServer.close(() => startBackend()); } catch (_) { startBackend(); } } const MODE = keylock.isConfigured(configPath) ? 'unlock' : 'setup'; const LOCK_LANGS = ['en','es','fr','eu','de','it','pt','zh','ar','hi','ru']; const LOCK_I18N = { en: { name:"English", t_setup:"Create your password", t_unlock:"Unlock Oasis", s_setup:"It encrypts your identity and keys on this device. If you forget it, you lose the account (no recovery).", s_unlock:"Enter your password to decrypt your keys and start Oasis.", ph:"Password", ph2:"Repeat password", b_setup:"Create and enter", b_unlock:"Enter", e_min:"Minimum 6 characters", e_mism:"Passwords do not match", e_wrong:"Incorrect password", e_pre:"Error: ", m_unlock:"Unlocking…" }, es: { name:"Español", t_setup:"Crea tu contraseña", t_unlock:"Desbloquea Oasis", s_setup:"Cifrará tu identidad y tus claves en este dispositivo. Si la olvidas, pierdes la cuenta (no hay recuperación).", s_unlock:"Introduce tu contraseña para descifrar tus claves y arrancar Oasis.", ph:"Contraseña", ph2:"Repite la contraseña", b_setup:"Crear y entrar", b_unlock:"Entrar", e_min:"Mínimo 6 caracteres", e_mism:"Las contraseñas no coinciden", e_wrong:"Contraseña incorrecta", e_pre:"Error: ", m_unlock:"Desbloqueando…" }, fr: { name:"Français", t_setup:"Créez votre mot de passe", t_unlock:"Déverrouiller Oasis", s_setup:"Il chiffre votre identité et vos clés sur cet appareil. Si vous l'oubliez, vous perdez le compte (aucune récupération).", s_unlock:"Saisissez votre mot de passe pour déchiffrer vos clés et démarrer Oasis.", ph:"Mot de passe", ph2:"Répétez le mot de passe", b_setup:"Créer et entrer", b_unlock:"Entrer", e_min:"6 caractères minimum", e_mism:"Les mots de passe ne correspondent pas", e_wrong:"Mot de passe incorrect", e_pre:"Erreur : ", m_unlock:"Déverrouillage…" }, eu: { name:"Euskara", t_setup:"Sortu zure pasahitza", t_unlock:"Desblokeatu Oasis", s_setup:"Zure identitatea eta gakoak gailu honetan zifratzen ditu. Ahazten baduzu, kontua galtzen duzu (ez dago berreskuratzerik).", s_unlock:"Sartu zure pasahitza gakoak deszifratu eta Oasis abiarazteko.", ph:"Pasahitza", ph2:"Errepikatu pasahitza", b_setup:"Sortu eta sartu", b_unlock:"Sartu", e_min:"Gutxienez 6 karaktere", e_mism:"Pasahitzak ez datoz bat", e_wrong:"Pasahitz okerra", e_pre:"Errorea: ", m_unlock:"Desblokeatzen…" }, de: { name:"Deutsch", t_setup:"Erstelle dein Passwort", t_unlock:"Oasis entsperren", s_setup:"Es verschlüsselt deine Identität und Schlüssel auf diesem Gerät. Wenn du es vergisst, verlierst du das Konto (keine Wiederherstellung).", s_unlock:"Gib dein Passwort ein, um deine Schlüssel zu entschlüsseln und Oasis zu starten.", ph:"Passwort", ph2:"Passwort wiederholen", b_setup:"Erstellen und eintreten", b_unlock:"Eintreten", e_min:"Mindestens 6 Zeichen", e_mism:"Passwörter stimmen nicht überein", e_wrong:"Falsches Passwort", e_pre:"Fehler: ", m_unlock:"Entsperren…" }, it: { name:"Italiano", t_setup:"Crea la tua password", t_unlock:"Sblocca Oasis", s_setup:"Cifra la tua identità e le tue chiavi su questo dispositivo. Se la dimentichi, perdi l'account (nessun recupero).", s_unlock:"Inserisci la password per decifrare le chiavi e avviare Oasis.", ph:"Password", ph2:"Ripeti la password", b_setup:"Crea ed entra", b_unlock:"Entra", e_min:"Minimo 6 caratteri", e_mism:"Le password non coincidono", e_wrong:"Password errata", e_pre:"Errore: ", m_unlock:"Sblocco…" }, pt: { name:"Português", t_setup:"Crie a sua palavra-passe", t_unlock:"Desbloquear o Oasis", s_setup:"Cifra a sua identidade e chaves neste dispositivo. Se a esquecer, perde a conta (sem recuperação).", s_unlock:"Introduza a sua palavra-passe para decifrar as chaves e iniciar o Oasis.", ph:"Palavra-passe", ph2:"Repita a palavra-passe", b_setup:"Criar e entrar", b_unlock:"Entrar", e_min:"Mínimo 6 caracteres", e_mism:"As palavras-passe não coincidem", e_wrong:"Palavra-passe incorreta", e_pre:"Erro: ", m_unlock:"A desbloquear…" }, zh: { name:"中文", t_setup:"创建你的密码", t_unlock:"解锁 Oasis", s_setup:"它会在此设备上加密你的身份和密钥。如果忘记,你将失去账户(无法恢复)。", s_unlock:"输入密码以解密你的密钥并启动 Oasis。", ph:"密码", ph2:"重复密码", b_setup:"创建并进入", b_unlock:"进入", e_min:"至少 6 个字符", e_mism:"密码不一致", e_wrong:"密码错误", e_pre:"错误:", m_unlock:"正在解锁…" }, ar: { name:"العربية", t_setup:"أنشئ كلمة المرور", t_unlock:"فتح Oasis", s_setup:"يشفّر هويتك ومفاتيحك على هذا الجهاز. إذا نسيتها، تفقد الحساب (لا استعادة).", s_unlock:"أدخل كلمة المرور لفك تشفير مفاتيحك وتشغيل Oasis.", ph:"كلمة المرور", ph2:"أعد كلمة المرور", b_setup:"إنشاء ودخول", b_unlock:"دخول", e_min:"6 أحرف على الأقل", e_mism:"كلمتا المرور غير متطابقتين", e_wrong:"كلمة مرور غير صحيحة", e_pre:"خطأ: ", m_unlock:"جارٍ الفتح…" }, hi: { name:"हिन्दी", t_setup:"अपना पासवर्ड बनाएं", t_unlock:"Oasis अनलॉक करें", s_setup:"यह इस डिवाइस पर आपकी पहचान और कुंजियाँ एन्क्रिप्ट करता है। भूल गए तो खाता खो देंगे (कोई रिकवरी नहीं)।", s_unlock:"अपनी कुंजियाँ डिक्रिप्ट करने और Oasis शुरू करने के लिए पासवर्ड दर्ज करें।", ph:"पासवर्ड", ph2:"पासवर्ड दोहराएँ", b_setup:"बनाएँ और प्रवेश करें", b_unlock:"प्रवेश करें", e_min:"कम से कम 6 अक्षर", e_mism:"पासवर्ड मेल नहीं खाते", e_wrong:"गलत पासवर्ड", e_pre:"त्रुटि: ", m_unlock:"अनलॉक हो रहा है…" }, ru: { name:"Русский", t_setup:"Создайте пароль", t_unlock:"Разблокировать Oasis", s_setup:"Он шифрует вашу личность и ключи на этом устройстве. Если забудете, потеряете аккаунт (без восстановления).", s_unlock:"Введите пароль, чтобы расшифровать ключи и запустить Oasis.", ph:"Пароль", ph2:"Повторите пароль", b_setup:"Создать и войти", b_unlock:"Войти", e_min:"Минимум 6 символов", e_mism:"Пароли не совпадают", e_wrong:"Неверный пароль", e_pre:"Ошибка: ", m_unlock:"Разблокировка…" }, }; function lockPage(mode) { const confirmField = mode === 'setup' ? '' : ''; return '' + '' + 'Oasis' + '' + '' + '

' + '
' + confirmField + '' + '
' + ''; } function readBody(req) { return new Promise((resolve) => { let b = ''; req.on('data', (d) => { b += d; if (b.length > 1e5) req.destroy(); }); req.on('end', () => { try { resolve(JSON.parse(b || '{}')); } catch (_) { resolve({}); } }); req.on('error', () => resolve({})); }); } const sockets = new Set(); const lockServer = http.createServer(async (req, res) => { try { if (req.method === 'POST' && req.url === '/__keylock/setup') { const { password } = await readBody(req); if (!password || String(password).length < 6) { res.writeHead(400, { 'Connection': 'close' }); return res.end('{"ok":false}'); } keylock.setPassword(configPath, password); const keys = keylock.loadOrCreateSecret(configPath); res.writeHead(200, { 'Content-Type': 'application/json', 'Connection': 'close' }); res.end('{"ok":true}'); return unlockAndStart(keys); } if (req.method === 'POST' && req.url === '/__keylock/unlock') { const { password } = await readBody(req); const kek = password ? keylock.verifyPassword(configPath, password) : null; if (!kek) { res.writeHead(401, { 'Connection': 'close' }); return res.end('{"ok":false}'); } const keys = keylock.loadOrCreateSecret(configPath); res.writeHead(200, { 'Content-Type': 'application/json', 'Connection': 'close' }); res.end('{"ok":true}'); return unlockAndStart(keys); } res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Connection': 'close' }); res.end(lockPage(MODE)); } catch (e) { res.writeHead(500, { 'Connection': 'close' }); res.end('{"ok":false,"error":"' + String(e && e.message).replace(/"/g, '') + '"}'); } }); lockServer.on('connection', (s) => { sockets.add(s); s.on('close', () => sockets.delete(s)); }); lockServer.listen(3000, '127.0.0.1', () => { console.log('[Oasis] keylock gate escuchando en 127.0.0.1:3000 (modo: ' + MODE + ')'); });