chat
This commit is contained in:
1 parent
104d781614
commit
d604c01114
145 files changed
+13678
-2169
No files matched your search
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"id": "comms-core",
|
||||
"name": "Comms Core",
|
||||
"version": "1.17.1",
|
||||
"requires": [],
|
||||
"backend": {
|
||||
"routes": ["/controller/comms"],
|
||||
"websocketEvents": ["presence", "incoming_call", "call_state", "chat_message"]
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,10 @@ ifeq ($(OSTYPE),NetBSD)
|
||||
include make-netbsd.mk
|
||||
endif
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
include make-windows.mk
|
||||
endif
|
||||
|
||||
drone:
|
||||
@echo "rendering .drone.yaml from .drone.jsonnet"
|
||||
drone jsonnet --format --stream
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
window.BackOneRingingPopup = {
|
||||
_audio: null,
|
||||
showIncomingCall({ callerName, callType, callId, onAccept, onReject }) {
|
||||
const el = document.getElementById('ringing-popup');
|
||||
if (!el) return;
|
||||
el.classList.remove('ringing-hidden');
|
||||
el.innerHTML = `
|
||||
<div class="ringing-card">
|
||||
<h2>Incoming ${callType} call</h2>
|
||||
<p>${callerName || 'Unknown peer'}</p>
|
||||
<button class="ringing-accept">Accept</button>
|
||||
<button class="ringing-reject">Reject</button>
|
||||
</div>`;
|
||||
el.querySelector('.ringing-accept').onclick = () => { this.dismiss(); onAccept && onAccept(callId); };
|
||||
el.querySelector('.ringing-reject').onclick = () => { this.dismiss(); onReject && onReject(callId); };
|
||||
this.playRingtone();
|
||||
if (Notification.permission === 'default') Notification.requestPermission();
|
||||
if (Notification.permission === 'granted') {
|
||||
new Notification(`Incoming ${callType} call`, { body: callerName || 'BackOne peer' });
|
||||
}
|
||||
},
|
||||
dismiss() {
|
||||
const el = document.getElementById('ringing-popup');
|
||||
if (el) { el.classList.add('ringing-hidden'); el.innerHTML = ''; }
|
||||
this.stopRingtone();
|
||||
},
|
||||
playRingtone() {
|
||||
this.stopRingtone();
|
||||
try {
|
||||
this._audio = new Audio('data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBSuBzvLZiTYIG2m98OScTgwOUarm7blmHgU7k9n1unEiBC13y+/ek0EKElyx5+ytWBUIQ5zd8sFuJAUwgM/z2Ik6CBxpvfDknE4MDlGq5u25Zh4FO5PZ9br');
|
||||
this._audio.loop = true;
|
||||
this._audio.play().catch(() => {});
|
||||
} catch (_) {}
|
||||
},
|
||||
stopRingtone() {
|
||||
if (this._audio) { this._audio.pause(); this._audio = null; }
|
||||
}
|
||||
};
|
||||
|
||||
async function loadAddonManifest() {
|
||||
const res = await fetch('/app/addons/manifest.json', { cache: 'no-store' });
|
||||
if (!res.ok) {
|
||||
console.warn('manifest.json fetch failed:', res.status);
|
||||
return [];
|
||||
}
|
||||
const data = await res.json();
|
||||
return Array.isArray(data) ? data : [];
|
||||
}
|
||||
|
||||
async function loadAddonModule(entry) {
|
||||
const entryPath = entry.frontend && entry.frontend.entry ? entry.frontend.entry : `/app/addons/${entry.id}/index.js`;
|
||||
await import(entryPath);
|
||||
const factory = window.BackOneAddons && window.BackOneAddons[entry.id];
|
||||
return factory ? factory() : null;
|
||||
}
|
||||
|
||||
window.BackOneAddonLoader = { loadAddonManifest, loadAddonModule };
|
||||
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* BackOne API auth — same token as backone-cli (~/.backOneOneAuthToken / X-ZT1-Auth).
|
||||
*/
|
||||
window.BackOneAuth = (function () {
|
||||
const LS_KEY = 'backone.authToken';
|
||||
let cache = null;
|
||||
|
||||
function trimToken(s) {
|
||||
return String(s || '').replace(/\s+/g, '');
|
||||
}
|
||||
|
||||
function tokenFromUrl() {
|
||||
try {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const fromQuery = trimToken(params.get('auth') || params.get('token'));
|
||||
if (fromQuery) return fromQuery;
|
||||
const hash = (location.hash || '').replace(/^#\/?/, '');
|
||||
const hashParams = new URLSearchParams(hash.includes('?') ? hash.split('?')[1] : '');
|
||||
return trimToken(hashParams.get('auth') || hashParams.get('token'));
|
||||
} catch (_) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
async function probeToken(token) {
|
||||
if (!token) return false;
|
||||
try {
|
||||
const res = await fetch('/network', {
|
||||
cache: 'no-store',
|
||||
headers: { 'X-ZT1-Auth': token }
|
||||
});
|
||||
return res.ok;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchAuthtoken() {
|
||||
for (const path of ['/api/backone/authtoken', '/app/api/authtoken']) {
|
||||
try {
|
||||
const res = await fetch(path, { cache: 'no-store' });
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
const token = trimToken(data && data.token);
|
||||
if (token) return token;
|
||||
}
|
||||
} catch (_) {
|
||||
/* try next */
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
async function resolveToken() {
|
||||
const urlToken = tokenFromUrl();
|
||||
if (urlToken) {
|
||||
if (await probeToken(urlToken)) {
|
||||
return urlToken;
|
||||
}
|
||||
}
|
||||
|
||||
const stored = trimToken(localStorage.getItem(LS_KEY));
|
||||
if (stored && await probeToken(stored)) {
|
||||
return stored;
|
||||
}
|
||||
|
||||
const apiToken = await fetchAuthtoken();
|
||||
if (apiToken && await probeToken(apiToken)) {
|
||||
return apiToken;
|
||||
}
|
||||
|
||||
if (stored) return stored;
|
||||
if (apiToken) return apiToken;
|
||||
if (urlToken) return urlToken;
|
||||
return '';
|
||||
}
|
||||
|
||||
async function getToken() {
|
||||
if (cache) return cache;
|
||||
cache = await resolveToken();
|
||||
return cache || '';
|
||||
}
|
||||
|
||||
async function authHeaders(extra) {
|
||||
const token = await getToken();
|
||||
const headers = Object.assign({}, extra || {});
|
||||
if (token) {
|
||||
headers['X-ZT1-Auth'] = token;
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
async function fetchWithAuth(path, options) {
|
||||
options = options || {};
|
||||
const headers = await authHeaders(options.headers || {});
|
||||
return fetch(path, Object.assign({}, options, { headers }));
|
||||
}
|
||||
|
||||
async function apiGet(path) {
|
||||
const res = await fetchWithAuth(path, { cache: 'no-store' });
|
||||
if (!res.ok) throw new Error(`${path} returned ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async function apiGetFirst(paths) {
|
||||
let lastErr = null;
|
||||
for (const path of paths) {
|
||||
try {
|
||||
return await apiGet(path);
|
||||
} catch (err) {
|
||||
lastErr = err;
|
||||
}
|
||||
}
|
||||
throw lastErr || new Error('API request failed');
|
||||
}
|
||||
|
||||
async function apiSend(path, method, body) {
|
||||
const headers = await authHeaders(
|
||||
body != null ? { 'Content-Type': 'application/json' } : {}
|
||||
);
|
||||
const res = await fetch(path, {
|
||||
method,
|
||||
headers,
|
||||
body: body != null ? JSON.stringify(body) : undefined
|
||||
});
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => '');
|
||||
throw new Error(text || `${method} ${path} returned ${res.status}`);
|
||||
}
|
||||
if (res.status === 204) {
|
||||
return { result: true };
|
||||
}
|
||||
const text = await res.text();
|
||||
if (!text) {
|
||||
return { result: true };
|
||||
}
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (_) {
|
||||
return { result: true };
|
||||
}
|
||||
}
|
||||
|
||||
async function apiSendFirst(paths, method, body) {
|
||||
let lastErr = null;
|
||||
for (const path of paths) {
|
||||
try {
|
||||
return await apiSend(path, method, body);
|
||||
} catch (err) {
|
||||
lastErr = err;
|
||||
}
|
||||
}
|
||||
throw lastErr || new Error(`${method} failed`);
|
||||
}
|
||||
|
||||
/** Store token from ~/.backOneOneAuthToken (same as backone-cli). */
|
||||
async function setUserToken(token) {
|
||||
cache = trimToken(token);
|
||||
if (cache) {
|
||||
localStorage.setItem(LS_KEY, cache);
|
||||
} else {
|
||||
localStorage.removeItem(LS_KEY);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
async function validateCurrentToken() {
|
||||
cache = null;
|
||||
const token = await getToken();
|
||||
if (!token) return false;
|
||||
return probeToken(token);
|
||||
}
|
||||
|
||||
function invalidate() {
|
||||
cache = null;
|
||||
}
|
||||
|
||||
async function init() {
|
||||
return getToken();
|
||||
}
|
||||
|
||||
return {
|
||||
getToken,
|
||||
authHeaders,
|
||||
fetchWithAuth,
|
||||
apiGet,
|
||||
apiGetFirst,
|
||||
apiSend,
|
||||
apiSendFirst,
|
||||
setUserToken,
|
||||
validateCurrentToken,
|
||||
invalidate,
|
||||
init
|
||||
};
|
||||
})();
|
||||
@@ -0,0 +1,57 @@
|
||||
window.BackOneContacts = (function () {
|
||||
let cache = null;
|
||||
|
||||
async function load() {
|
||||
if (cache) return cache;
|
||||
try {
|
||||
const res = await BackOneAuth.fetchWithAuth('/api/backone/contacts', { cache: 'no-store' })
|
||||
.catch(() => BackOneAuth.fetchWithAuth('/app/api/contacts', { cache: 'no-store' }));
|
||||
if (!res.ok) {
|
||||
cache = {};
|
||||
return cache;
|
||||
}
|
||||
cache = await res.json();
|
||||
if (!cache || typeof cache !== 'object') cache = {};
|
||||
} catch (_) {
|
||||
cache = {};
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
function getDisplayName(peerId, controllerName) {
|
||||
const c = cache || {};
|
||||
const entry = c[peerId];
|
||||
if (entry && entry.nickname) return entry.nickname;
|
||||
if (controllerName) return controllerName;
|
||||
return peerId;
|
||||
}
|
||||
|
||||
function getNickname(peerId) {
|
||||
const entry = (cache || {})[peerId];
|
||||
return entry && entry.nickname ? entry.nickname : '';
|
||||
}
|
||||
|
||||
async function setNickname(peerId, nickname) {
|
||||
await load();
|
||||
const patch = {};
|
||||
patch[peerId] = { nickname: nickname || '' };
|
||||
const res = await BackOneAuth.fetchWithAuth('/api/backone/contacts', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(patch)
|
||||
}).catch(() => BackOneAuth.fetchWithAuth('/app/api/contacts', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(patch)
|
||||
}));
|
||||
if (!res.ok) throw new Error('Failed to save nickname');
|
||||
cache = await res.json();
|
||||
return cache;
|
||||
}
|
||||
|
||||
function invalidate() {
|
||||
cache = null;
|
||||
}
|
||||
|
||||
return { load, getDisplayName, getNickname, setNickname, invalidate };
|
||||
})();
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BackOne</title>
|
||||
<link rel="stylesheet" href="/app/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>BackOne</h1>
|
||||
<div id="network-bar" class="network-bar">
|
||||
<label class="network-bar-label">Network
|
||||
<select id="global-network"></select>
|
||||
</label>
|
||||
<input id="global-join-id" type="text" maxlength="16" placeholder="Join network ID" autocomplete="off" />
|
||||
<button type="button" id="global-join-btn">Join</button>
|
||||
<button type="button" id="global-leave-btn">Leave</button>
|
||||
<span id="global-network-status" class="network-bar-status"></span>
|
||||
<div id="auth-token-bar" class="auth-token-bar auth-token-hidden">
|
||||
<label class="auth-token-label">Auth token
|
||||
<input id="auth-token-input" type="password" autocomplete="off"
|
||||
placeholder="Paste from ~/.backOneOneAuthToken" />
|
||||
</label>
|
||||
<button type="button" id="auth-token-save">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
<nav id="addon-nav"></nav>
|
||||
</header>
|
||||
<main id="addon-panel">
|
||||
<p>Select an addon from the navigation bar.</p>
|
||||
</main>
|
||||
<div id="ringing-popup" class="ringing-hidden"></div>
|
||||
<script src="/app/auth-store.js"></script>
|
||||
<script src="/app/contacts-store.js"></script>
|
||||
<script src="/app/peer-directory.js"></script>
|
||||
<script src="/app/network-session.js"></script>
|
||||
<script src="/app/peer-picker.js"></script>
|
||||
<script src="/app/addon-loader.js"></script>
|
||||
<script src="/app/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,228 @@
|
||||
window.BackOneAddons = window.BackOneAddons || {};
|
||||
|
||||
const addonButtons = {};
|
||||
let addonModules = {};
|
||||
let coreRefreshTimer = null;
|
||||
|
||||
function hashAddonId() {
|
||||
const h = (location.hash || '').replace(/^#\/?/, '').toLowerCase();
|
||||
if (h === 'voice' || h === 'video' || h === 'chat') return h;
|
||||
return '';
|
||||
}
|
||||
|
||||
function activateAddon(id) {
|
||||
const btn = addonButtons[id];
|
||||
if (!btn) return;
|
||||
btn.click();
|
||||
}
|
||||
|
||||
function escapeHtml(s) {
|
||||
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
function networkLabel(n) {
|
||||
if (!n) return '';
|
||||
return n.name ? `${n.name} (${n.id})` : n.id;
|
||||
}
|
||||
|
||||
function renderCoreStatus(statusEl) {
|
||||
const core = BackOneNetworkSession.getCoreStatus();
|
||||
const net = BackOneNetworkSession.getActiveNetwork();
|
||||
if (!core.online) {
|
||||
statusEl.innerHTML = '<span class="core-dot offline"></span> BackOne offline — start <code>backone</code> service';
|
||||
return;
|
||||
}
|
||||
const netPart = net ? networkLabel(net) : 'no network joined';
|
||||
const addr = core.address ? ` · ${escapeHtml(core.address)}` : '';
|
||||
const authNote = core.authenticated
|
||||
? ' · auth token OK'
|
||||
: ' · <span class="peer-network-warn">paste <code>~/.backOneOneAuthToken</code> below</span>';
|
||||
statusEl.innerHTML =
|
||||
`<span class="core-dot online"></span> BackOne online${addr} · ` +
|
||||
`<strong>${escapeHtml(netPart)}</strong> · ${core.leafPeerCount} peer(s)${authNote}`;
|
||||
}
|
||||
|
||||
async function initAuthTokenBar(statusEl, onSaved) {
|
||||
const bar = document.getElementById('auth-token-bar');
|
||||
const input = document.getElementById('auth-token-input');
|
||||
const saveBtn = document.getElementById('auth-token-save');
|
||||
if (!bar || !input || !saveBtn) return;
|
||||
|
||||
async function refreshAuthUi() {
|
||||
const ok = await BackOneAuth.validateCurrentToken();
|
||||
bar.classList.toggle('auth-token-hidden', ok);
|
||||
if (!ok && !input.value) {
|
||||
input.placeholder = 'Paste from ~/.backOneOneAuthToken';
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
saveBtn.addEventListener('click', async () => {
|
||||
const token = input.value.trim();
|
||||
if (!token) {
|
||||
if (statusEl) statusEl.textContent = 'Paste the token from ~/.backOneOneAuthToken';
|
||||
return;
|
||||
}
|
||||
saveBtn.disabled = true;
|
||||
await BackOneAuth.setUserToken(token);
|
||||
BackOneAuth.invalidate();
|
||||
const ok = await BackOneAuth.validateCurrentToken();
|
||||
if (!ok) {
|
||||
if (statusEl) statusEl.textContent = 'Token rejected — check ~/.backOneOneAuthToken matches the running backone service';
|
||||
saveBtn.disabled = false;
|
||||
return;
|
||||
}
|
||||
input.value = '';
|
||||
bar.classList.add('auth-token-hidden');
|
||||
if (typeof onSaved === 'function') await onSaved();
|
||||
saveBtn.disabled = false;
|
||||
});
|
||||
|
||||
await refreshAuthUi();
|
||||
return refreshAuthUi;
|
||||
}
|
||||
|
||||
async function initNetworkBar() {
|
||||
const bar = document.getElementById('network-bar');
|
||||
if (!bar) return;
|
||||
|
||||
const netSel = bar.querySelector('#global-network');
|
||||
const joinInput = bar.querySelector('#global-join-id');
|
||||
const joinBtn = bar.querySelector('#global-join-btn');
|
||||
const leaveBtn = bar.querySelector('#global-leave-btn');
|
||||
const statusEl = bar.querySelector('#global-network-status');
|
||||
|
||||
async function reloadNetworkList(selectId) {
|
||||
await BackOneNetworkSession.syncFromCore();
|
||||
const networks = await BackOneNetworkSession.loadNetworks();
|
||||
netSel.innerHTML = networks.map(n =>
|
||||
`<option value="${n.id}">${escapeHtml(networkLabel(n))} · ${escapeHtml(n.status || '')}</option>`
|
||||
).join('') || '<option value="">— no networks joined —</option>';
|
||||
|
||||
const want = (selectId || BackOneNetworkSession.getNetworkId() || '').toLowerCase();
|
||||
if (want && networks.some(n => (n.id || '').toLowerCase() === want)) {
|
||||
netSel.value = networks.find(n => (n.id || '').toLowerCase() === want).id;
|
||||
await BackOneNetworkSession.selectNetwork(netSel.value);
|
||||
} else if (networks.length === 1) {
|
||||
netSel.value = networks[0].id;
|
||||
await BackOneNetworkSession.selectNetwork(networks[0].id);
|
||||
} else if (networks.length > 0 && BackOneNetworkSession.getNetworkId()) {
|
||||
netSel.value = BackOneNetworkSession.getNetworkId();
|
||||
}
|
||||
renderCoreStatus(statusEl);
|
||||
}
|
||||
|
||||
netSel.addEventListener('change', async () => {
|
||||
if (!netSel.value) return;
|
||||
statusEl.textContent = 'Switching network…';
|
||||
await BackOneNetworkSession.selectNetwork(netSel.value);
|
||||
await BackOneNetworkSession.syncFromCore();
|
||||
renderCoreStatus(statusEl);
|
||||
});
|
||||
|
||||
joinBtn.addEventListener('click', async () => {
|
||||
const id = BackOnePeerDirectory.normalizeNetworkId(joinInput.value);
|
||||
if (!id) {
|
||||
statusEl.textContent = 'Enter a 16-digit hexadecimal network ID';
|
||||
return;
|
||||
}
|
||||
joinBtn.disabled = true;
|
||||
leaveBtn.disabled = true;
|
||||
statusEl.textContent = `Joining ${id}…`;
|
||||
try {
|
||||
await BackOneNetworkSession.joinNetwork(id);
|
||||
joinInput.value = '';
|
||||
await reloadNetworkList(id);
|
||||
} catch (err) {
|
||||
statusEl.textContent = err.message || 'Join failed';
|
||||
} finally {
|
||||
joinBtn.disabled = false;
|
||||
leaveBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
leaveBtn.addEventListener('click', async () => {
|
||||
const id = netSel.value || BackOneNetworkSession.getNetworkId();
|
||||
if (!id) {
|
||||
statusEl.textContent = 'Select a network to leave';
|
||||
return;
|
||||
}
|
||||
if (!confirm(`Leave network ${networkLabel(BackOneNetworkSession.getActiveNetwork() || { id })}?`)) return;
|
||||
joinBtn.disabled = true;
|
||||
leaveBtn.disabled = true;
|
||||
statusEl.textContent = `Leaving ${id}…`;
|
||||
try {
|
||||
await BackOneNetworkSession.leaveNetwork(id);
|
||||
await reloadNetworkList();
|
||||
} catch (err) {
|
||||
statusEl.textContent = err.message || 'Leave failed';
|
||||
} finally {
|
||||
joinBtn.disabled = false;
|
||||
leaveBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
BackOneNetworkSession.subscribe(() => renderCoreStatus(statusEl));
|
||||
|
||||
const refreshAuthUi = await initAuthTokenBar(statusEl, () => reloadNetworkList());
|
||||
|
||||
await reloadNetworkList();
|
||||
if (refreshAuthUi) await refreshAuthUi();
|
||||
|
||||
coreRefreshTimer = setInterval(() => {
|
||||
BackOneNetworkSession.syncFromCore().then(() => renderCoreStatus(statusEl));
|
||||
}, 45000);
|
||||
}
|
||||
|
||||
async function initAppShell() {
|
||||
await BackOneAuth.init();
|
||||
await initNetworkBar();
|
||||
|
||||
const nav = document.getElementById('addon-nav');
|
||||
const panel = document.getElementById('addon-panel');
|
||||
const addons = await BackOneAddonLoader.loadAddonManifest();
|
||||
if (!addons.length) {
|
||||
panel.innerHTML = '<p>No UI addons found. Install backone-addon-voice, backone-addon-video, or backone-addon-chat, then run: <code>sudo backone-updater rebuild-manifest</code> and restart backone.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
for (const entry of addons) {
|
||||
const mod = await BackOneAddonLoader.loadAddonModule(entry);
|
||||
if (mod) addonModules[entry.id] = mod;
|
||||
const btn = document.createElement('button');
|
||||
btn.dataset.addonId = entry.id;
|
||||
btn.textContent = (entry.frontend && entry.frontend.navLabel) || entry.name || entry.id;
|
||||
btn.onclick = () => {
|
||||
nav.querySelectorAll('button').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
panel.innerHTML = '';
|
||||
if (addonModules[entry.id] && addonModules[entry.id].mount) {
|
||||
addonModules[entry.id].mount(panel);
|
||||
} else {
|
||||
panel.innerHTML = `<p>Addon "${entry.id}" loaded.</p>`;
|
||||
}
|
||||
};
|
||||
addonButtons[entry.id] = btn;
|
||||
nav.appendChild(btn);
|
||||
}
|
||||
|
||||
const fromHash = hashAddonId();
|
||||
if (fromHash && addonButtons[fromHash]) {
|
||||
activateAddon(fromHash);
|
||||
} else {
|
||||
nav.querySelector('button')?.click();
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('hashchange', () => {
|
||||
const id = hashAddonId();
|
||||
if (id) activateAddon(id);
|
||||
});
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
BackOneNetworkSession.syncFromCore();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initAppShell);
|
||||
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Single BackOne core network for all addons (same as backone-cli listnetworks/join).
|
||||
*/
|
||||
window.BackOneNetworkSession = (function () {
|
||||
const NETWORK_KEY = 'backone.lastNetworkId';
|
||||
const PEER_KEY = 'backone.lastPeerId';
|
||||
|
||||
let activeNetwork = null;
|
||||
let selectedPeer = null;
|
||||
let coreStatus = { online: false, address: '', networkCount: 0, leafPeerCount: 0, authenticated: false };
|
||||
const listeners = new Set();
|
||||
|
||||
function emit() {
|
||||
const snap = {
|
||||
network: activeNetwork,
|
||||
networkId: getNetworkId(),
|
||||
peer: selectedPeer,
|
||||
core: { ...coreStatus }
|
||||
};
|
||||
listeners.forEach(fn => {
|
||||
try { fn(snap); } catch (_) { /* ignore */ }
|
||||
});
|
||||
}
|
||||
|
||||
function getNetworkId() {
|
||||
return (localStorage.getItem(NETWORK_KEY) || '').toLowerCase();
|
||||
}
|
||||
|
||||
function setNetworkId(id) {
|
||||
const nwid = BackOnePeerDirectory.normalizeNetworkId(id);
|
||||
if (nwid) {
|
||||
localStorage.setItem(NETWORK_KEY, nwid);
|
||||
} else {
|
||||
localStorage.removeItem(NETWORK_KEY);
|
||||
}
|
||||
BackOnePeerDirectory.setLastNetworkId(nwid);
|
||||
return nwid;
|
||||
}
|
||||
|
||||
function getSelectedPeerId() {
|
||||
return (localStorage.getItem(PEER_KEY) || '').toLowerCase();
|
||||
}
|
||||
|
||||
function setSelectedPeer(peer) {
|
||||
selectedPeer = peer || null;
|
||||
if (peer && peer.peerId) {
|
||||
localStorage.setItem(PEER_KEY, peer.peerId);
|
||||
} else {
|
||||
localStorage.removeItem(PEER_KEY);
|
||||
}
|
||||
emit();
|
||||
}
|
||||
|
||||
function pickDefaultNetworkId(networks) {
|
||||
if (!networks.length) return '';
|
||||
const stored = getNetworkId();
|
||||
if (stored && networks.some(n => (n.id || '').toLowerCase() === stored)) {
|
||||
return stored;
|
||||
}
|
||||
const preferred = networks.find(n => n.status === 'OK')
|
||||
|| networks.find(n => n.status === 'REQUESTING_CONFIGURATION')
|
||||
|| networks[0];
|
||||
return preferred ? (preferred.id || '').toLowerCase() : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* On app start: read BackOne core state (same as backone-cli info / listnetworks / peers).
|
||||
*/
|
||||
async function syncFromCore() {
|
||||
const token = await BackOneAuth.getToken();
|
||||
const [status, networks, cliPeers] = await Promise.all([
|
||||
BackOnePeerDirectory.loadCoreStatus(),
|
||||
BackOnePeerDirectory.loadNetworks(),
|
||||
BackOnePeerDirectory.loadCliPeers()
|
||||
]);
|
||||
|
||||
coreStatus = {
|
||||
online: !!(status && status.online),
|
||||
address: (status && status.address) || '',
|
||||
version: (status && status.version) || '',
|
||||
networkCount: networks.length,
|
||||
leafPeerCount: BackOnePeerDirectory.countLeafPeers(cliPeers),
|
||||
authenticated: !!token
|
||||
};
|
||||
|
||||
const pick = pickDefaultNetworkId(networks);
|
||||
if (pick) {
|
||||
setNetworkId(pick);
|
||||
activeNetwork = networks.find(n => (n.id || '').toLowerCase() === pick)
|
||||
|| await BackOnePeerDirectory.loadNetwork(pick);
|
||||
} else {
|
||||
setNetworkId('');
|
||||
activeNetwork = null;
|
||||
}
|
||||
|
||||
emit();
|
||||
return { coreStatus, activeNetwork, networks };
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
const id = getNetworkId();
|
||||
if (!id) {
|
||||
activeNetwork = null;
|
||||
emit();
|
||||
return null;
|
||||
}
|
||||
activeNetwork = await BackOnePeerDirectory.loadNetwork(id);
|
||||
if (!activeNetwork) {
|
||||
activeNetwork = { id, name: id, status: 'UNKNOWN' };
|
||||
}
|
||||
emit();
|
||||
return activeNetwork;
|
||||
}
|
||||
|
||||
async function loadNetworks() {
|
||||
return BackOnePeerDirectory.loadNetworks();
|
||||
}
|
||||
|
||||
async function selectNetwork(nwid) {
|
||||
const id = setNetworkId(nwid);
|
||||
if (!id) {
|
||||
await refresh();
|
||||
return null;
|
||||
}
|
||||
return refresh();
|
||||
}
|
||||
|
||||
async function joinNetwork(nwid) {
|
||||
const id = BackOnePeerDirectory.normalizeNetworkId(nwid);
|
||||
if (!id) throw new Error('Network ID must be 16 hexadecimal characters');
|
||||
await BackOnePeerDirectory.joinNetwork(id);
|
||||
return syncFromCore();
|
||||
}
|
||||
|
||||
async function leaveNetwork(nwid) {
|
||||
const id = BackOnePeerDirectory.normalizeNetworkId(nwid || getNetworkId());
|
||||
if (!id) throw new Error('No network selected');
|
||||
await BackOnePeerDirectory.leaveNetwork(id);
|
||||
if (getNetworkId() === id) {
|
||||
setNetworkId('');
|
||||
selectedPeer = null;
|
||||
localStorage.removeItem(PEER_KEY);
|
||||
}
|
||||
return syncFromCore();
|
||||
}
|
||||
|
||||
function subscribe(fn) {
|
||||
listeners.add(fn);
|
||||
return () => listeners.delete(fn);
|
||||
}
|
||||
|
||||
function requireNetworkId() {
|
||||
const id = getNetworkId();
|
||||
if (!id) throw new Error('Join a BackOne network first (same as backone-cli join)');
|
||||
return id;
|
||||
}
|
||||
|
||||
function getCoreStatus() {
|
||||
return { ...coreStatus };
|
||||
}
|
||||
|
||||
return {
|
||||
getNetworkId,
|
||||
getActiveNetwork: () => activeNetwork,
|
||||
getCoreStatus,
|
||||
getSelectedPeer: () => selectedPeer,
|
||||
getSelectedPeerId,
|
||||
setSelectedPeer,
|
||||
selectNetwork,
|
||||
joinNetwork,
|
||||
leaveNetwork,
|
||||
loadNetworks,
|
||||
syncFromCore,
|
||||
refresh,
|
||||
subscribe,
|
||||
requireNetworkId
|
||||
};
|
||||
})();
|
||||
@@ -0,0 +1,266 @@
|
||||
window.BackOnePeerDirectory = (function () {
|
||||
const NETWORK_KEY = 'backone.lastNetworkId';
|
||||
const API = {
|
||||
peers: ['/api/backone/peers', '/app/api/peers', '/peer'],
|
||||
networks: ['/api/backone/networks', '/app/api/networks', '/network'],
|
||||
status: ['/api/backone/status', '/app/api/status', '/status'],
|
||||
network: (id) => [
|
||||
`/api/backone/network/${id}`,
|
||||
`/app/api/network/${id}`,
|
||||
`/network/${id}`
|
||||
],
|
||||
members: (id) => [
|
||||
`/api/backone/network/${id}/members`,
|
||||
`/app/api/network/${id}/members`,
|
||||
`/unstable/controller/network/${id}/member`
|
||||
],
|
||||
join: (id) => [
|
||||
`/api/backone/network/${id}`,
|
||||
`/app/api/network/${id}`,
|
||||
`/network/${id}`
|
||||
],
|
||||
leave: (id) => [
|
||||
`/api/backone/network/${id}`,
|
||||
`/app/api/network/${id}`,
|
||||
`/network/${id}`
|
||||
]
|
||||
};
|
||||
|
||||
function apiGet(path) {
|
||||
return BackOneAuth.apiGet(path);
|
||||
}
|
||||
|
||||
/** Same data as `backone-cli -j peers` (GET /peer). */
|
||||
async function loadCliPeers() {
|
||||
try {
|
||||
const peers = await BackOneAuth.apiGetFirst(API.peers);
|
||||
return Array.isArray(peers) ? peers : [];
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/** Same data as `backone-cli -j listnetworks` (GET /network). */
|
||||
async function loadNetworks() {
|
||||
try {
|
||||
const nets = await BackOneAuth.apiGetFirst(API.networks);
|
||||
return Array.isArray(nets) ? nets : [];
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/** Same data as `backone-cli -j info` (GET /status). */
|
||||
async function loadCoreStatus() {
|
||||
try {
|
||||
return await BackOneAuth.apiGetFirst(API.status);
|
||||
} catch (_) {
|
||||
try {
|
||||
const res = await fetch('/health', { cache: 'no-store' });
|
||||
return { online: res.ok };
|
||||
} catch (_2) {
|
||||
return { online: false };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function countLeafPeers(cliPeers) {
|
||||
if (!Array.isArray(cliPeers)) return 0;
|
||||
return cliPeers.filter(p => !p.role || p.role === 'LEAF').length;
|
||||
}
|
||||
|
||||
async function loadNetwork(nwid) {
|
||||
if (!nwid) return null;
|
||||
const networks = await loadNetworks();
|
||||
const found = networks.find(n => n.id === nwid);
|
||||
if (found) return found;
|
||||
try {
|
||||
return await BackOneAuth.apiGetFirst(API.network(nwid));
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getLastNetworkId() {
|
||||
return localStorage.getItem(NETWORK_KEY) || '';
|
||||
}
|
||||
|
||||
function setLastNetworkId(nwid) {
|
||||
if (nwid) localStorage.setItem(NETWORK_KEY, nwid);
|
||||
}
|
||||
|
||||
function peerIsOnline(p) {
|
||||
if (!p) return false;
|
||||
const paths = p.paths || [];
|
||||
return paths.some(x => x.active && !x.expired);
|
||||
}
|
||||
|
||||
function peerLinkLabel(p) {
|
||||
if (!p) return '';
|
||||
if (p.tunneled) return 'RELAY';
|
||||
const paths = p.paths || [];
|
||||
for (const path of paths) {
|
||||
if (path.preferred && path.active && !path.expired) {
|
||||
return 'DIRECT';
|
||||
}
|
||||
}
|
||||
for (const path of paths) {
|
||||
if (path.active && !path.expired) {
|
||||
return 'DIRECT';
|
||||
}
|
||||
}
|
||||
return 'RELAY';
|
||||
}
|
||||
|
||||
function peerPreferredPath(p) {
|
||||
if (!p) return '';
|
||||
const paths = p.paths || [];
|
||||
for (const path of paths) {
|
||||
if (path.preferred && path.address) return path.address;
|
||||
}
|
||||
for (const path of paths) {
|
||||
if (path.active && path.address) return path.address;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function cliPeerMap(cliPeers) {
|
||||
const map = {};
|
||||
for (const p of cliPeers) {
|
||||
const id = (p.address || '').toLowerCase();
|
||||
if (id) map[id] = p;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
async function loadMemberMap(nwid) {
|
||||
if (!nwid) return {};
|
||||
try {
|
||||
const data = await BackOneAuth.apiGetFirst(API.members(nwid));
|
||||
const map = {};
|
||||
if (data && Array.isArray(data.data)) {
|
||||
for (const m of data.data) {
|
||||
const id = (m.id || '').toLowerCase();
|
||||
if (id) map[id] = m;
|
||||
}
|
||||
}
|
||||
return map;
|
||||
} catch (_) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function buildPeerEntry(peerId, member, cliPeer, networkId, networkName) {
|
||||
const controllerName = member ? (member.name || '') : '';
|
||||
const ips = member && member.ipAssignments ? member.ipAssignments : [];
|
||||
return {
|
||||
peerId,
|
||||
networkId: networkId || '',
|
||||
networkName: networkName || '',
|
||||
displayName: BackOneContacts.getDisplayName(peerId, controllerName),
|
||||
controllerName,
|
||||
nickname: BackOneContacts.getNickname(peerId),
|
||||
online: peerIsOnline(cliPeer),
|
||||
latency: cliPeer && typeof cliPeer.latency === 'number' ? cliPeer.latency : null,
|
||||
role: (cliPeer && cliPeer.role) || (member ? 'LEAF' : ''),
|
||||
version: (cliPeer && cliPeer.version) || '',
|
||||
link: cliPeer ? peerLinkLabel(cliPeer) : '',
|
||||
path: cliPeer ? peerPreferredPath(cliPeer) : '',
|
||||
ip: ips.length ? ips[0] : '',
|
||||
onNetwork: !!member
|
||||
};
|
||||
}
|
||||
|
||||
async function loadPeersForNetwork(nwid) {
|
||||
if (!nwid) return [];
|
||||
|
||||
const [cliPeers, members, selfStatus, network] = await Promise.all([
|
||||
loadCliPeers(),
|
||||
loadMemberMap(nwid),
|
||||
loadCoreStatus().catch(() => ({})),
|
||||
loadNetwork(nwid)
|
||||
]);
|
||||
await BackOneContacts.load();
|
||||
|
||||
const selfId = ((selfStatus && selfStatus.address) || '').toLowerCase();
|
||||
const networkId = nwid;
|
||||
const networkName = network ? (network.name || network.id || nwid) : nwid;
|
||||
const cliById = cliPeerMap(cliPeers);
|
||||
const out = [];
|
||||
|
||||
if (Object.keys(members).length > 0) {
|
||||
for (const peerId of Object.keys(members)) {
|
||||
const member = members[peerId];
|
||||
if (!member || member.authorized === false) continue;
|
||||
if (peerId === selfId) continue;
|
||||
out.push(buildPeerEntry(peerId, member, cliById[peerId], networkId, networkName));
|
||||
}
|
||||
} else {
|
||||
for (const peerId of Object.keys(cliById)) {
|
||||
const p = cliById[peerId];
|
||||
if (!peerId || peerId.length !== 10 || peerId === selfId) continue;
|
||||
if (p.role && p.role !== 'LEAF') continue;
|
||||
out.push(buildPeerEntry(peerId, null, p, networkId, networkName));
|
||||
}
|
||||
}
|
||||
|
||||
out.sort((a, b) => {
|
||||
if (a.online !== b.online) return a.online ? -1 : 1;
|
||||
return a.displayName.localeCompare(b.displayName);
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
function searchPeers(query, list) {
|
||||
const q = (query || '').trim().toLowerCase();
|
||||
if (!q) return list;
|
||||
return list.filter(p =>
|
||||
p.displayName.toLowerCase().includes(q) ||
|
||||
p.peerId.includes(q) ||
|
||||
(p.networkName || '').toLowerCase().includes(q) ||
|
||||
(p.networkId || '').includes(q) ||
|
||||
(p.controllerName || '').toLowerCase().includes(q) ||
|
||||
(p.role || '').toLowerCase().includes(q) ||
|
||||
(p.path || '').toLowerCase().includes(q) ||
|
||||
(p.ip || '').toLowerCase().includes(q)
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeNetworkId(input) {
|
||||
const id = String(input || '').replace(/[^0-9a-fA-F]/g, '').toLowerCase();
|
||||
return id.length === 16 ? id : '';
|
||||
}
|
||||
|
||||
async function joinNetwork(nwid) {
|
||||
const id = normalizeNetworkId(nwid);
|
||||
if (!id) {
|
||||
throw new Error('Network ID must be 16 hexadecimal characters');
|
||||
}
|
||||
return BackOneAuth.apiSendFirst(API.join(id), 'POST', {});
|
||||
}
|
||||
|
||||
async function leaveNetwork(nwid) {
|
||||
const id = normalizeNetworkId(nwid);
|
||||
if (!id) {
|
||||
throw new Error('Network ID must be 16 hexadecimal characters');
|
||||
}
|
||||
return BackOneAuth.apiSendFirst(API.leave(id), 'DELETE');
|
||||
}
|
||||
|
||||
return {
|
||||
loadCliPeers,
|
||||
loadNetworks,
|
||||
loadNetwork,
|
||||
loadCoreStatus,
|
||||
countLeafPeers,
|
||||
loadPeersForNetwork,
|
||||
getLastNetworkId,
|
||||
setLastNetworkId,
|
||||
joinNetwork,
|
||||
leaveNetwork,
|
||||
normalizeNetworkId,
|
||||
searchPeers,
|
||||
peerIsOnline,
|
||||
peerLinkLabel
|
||||
};
|
||||
})();
|
||||
@@ -0,0 +1,151 @@
|
||||
window.BackOnePeerPicker = function PeerPicker(container, options) {
|
||||
options = options || {};
|
||||
const onSelect = options.onSelect || (() => {});
|
||||
const onNetworkChange = options.onNetworkChange || (() => {});
|
||||
const onAction = options.onAction || null;
|
||||
const actionLabel = options.actionLabel || 'Select';
|
||||
const showActionButton = options.showActionButton !== false;
|
||||
const useSharedNetwork = options.useSharedNetwork !== false;
|
||||
|
||||
let selectedPeer = null;
|
||||
let peers = [];
|
||||
let currentNetwork = null;
|
||||
let unsub = null;
|
||||
|
||||
container.innerHTML = `
|
||||
<div class="peer-picker">
|
||||
<div class="peer-picker-core">BackOne core · same network as <code>backone-cli</code></div>
|
||||
<div id="pp-network-banner" class="peer-network-banner"></div>
|
||||
<label class="peer-picker-row">Search
|
||||
<input id="pp-search" type="search" placeholder="Name or peer ID" />
|
||||
</label>
|
||||
<ul id="pp-list" class="peer-list"></ul>
|
||||
<div id="pp-selected" class="peer-selected"></div>
|
||||
${showActionButton ? `<button id="pp-action" type="button" disabled>${actionLabel}</button>` : ''}
|
||||
</div>`;
|
||||
|
||||
const netBanner = container.querySelector('#pp-network-banner');
|
||||
const searchInput = container.querySelector('#pp-search');
|
||||
const listEl = container.querySelector('#pp-list');
|
||||
const selectedEl = container.querySelector('#pp-selected');
|
||||
const actionBtn = container.querySelector('#pp-action');
|
||||
|
||||
function networkLabel(n) {
|
||||
if (!n) return '';
|
||||
return n.name ? `${n.name} (${n.id})` : n.id;
|
||||
}
|
||||
|
||||
function renderNetworkBanner() {
|
||||
if (!useSharedNetwork) return;
|
||||
const id = BackOneNetworkSession.getNetworkId();
|
||||
const core = BackOneNetworkSession.getCoreStatus();
|
||||
if (!core.online) {
|
||||
netBanner.innerHTML = '<span class="peer-network-warn">BackOne offline — peers from <code>backone-cli</code> unavailable</span>';
|
||||
return;
|
||||
}
|
||||
if (!currentNetwork || !id) {
|
||||
netBanner.innerHTML = '<span class="peer-network-warn">No network — core reports ' + core.networkCount + ' joined (use header bar or <code>backone-cli listnetworks</code>)</span>';
|
||||
return;
|
||||
}
|
||||
netBanner.textContent = `Network: ${networkLabel(currentNetwork)} · ${peers.length} contact(s) · ${core.leafPeerCount} VL1 peer(s) from core`;
|
||||
}
|
||||
|
||||
function renderList() {
|
||||
const q = searchInput.value;
|
||||
const filtered = BackOnePeerDirectory.searchPeers(q, peers);
|
||||
const netTag = currentNetwork ? escapeHtml(currentNetwork.name || currentNetwork.id) : '';
|
||||
listEl.innerHTML = filtered.map(p => {
|
||||
const lat = p.latency != null ? `${p.latency}ms` : '—';
|
||||
const meta = [p.role || 'LEAF', lat, p.link || ''].filter(Boolean).join(' · ');
|
||||
const ip = p.ip ? ` · ${escapeHtml(p.ip)}` : '';
|
||||
return `
|
||||
<li class="peer-item ${p.online ? 'online' : 'offline'} ${selectedPeer && selectedPeer.peerId === p.peerId ? 'active' : ''}" data-id="${p.peerId}">
|
||||
<span class="peer-dot" title="${p.online ? 'Online' : 'Offline'}"></span>
|
||||
<span class="peer-main">
|
||||
<strong class="peer-name">${escapeHtml(p.displayName)}</strong>
|
||||
<small class="peer-sub">${escapeHtml(p.peerId)} · ${escapeHtml(meta)}${netTag ? ' · ' + netTag : ''}${ip}</small>
|
||||
</span>
|
||||
<button type="button" class="peer-edit" title="Edit nickname">✎</button>
|
||||
</li>`;
|
||||
}).join('') || '<li class="peer-empty">No peers on the active network — check <code>backone-cli peers</code> and controller members</li>';
|
||||
|
||||
listEl.querySelectorAll('.peer-item').forEach(li => {
|
||||
li.addEventListener('click', (ev) => {
|
||||
if (ev.target.classList.contains('peer-edit')) return;
|
||||
const id = li.dataset.id;
|
||||
selectedPeer = peers.find(x => x.peerId === id) || null;
|
||||
if (selectedPeer) BackOneNetworkSession.setSelectedPeer(selectedPeer);
|
||||
renderList();
|
||||
renderSelected();
|
||||
if (selectedPeer) onSelect(selectedPeer, currentNetwork);
|
||||
});
|
||||
const editBtn = li.querySelector('.peer-edit');
|
||||
if (editBtn) {
|
||||
editBtn.addEventListener('click', async (ev) => {
|
||||
ev.stopPropagation();
|
||||
const id = li.dataset.id;
|
||||
const peer = peers.find(x => x.peerId === id);
|
||||
if (!peer) return;
|
||||
const nick = prompt('Nickname for ' + peer.peerId, peer.nickname || peer.controllerName || '');
|
||||
if (nick === null) return;
|
||||
await BackOneContacts.setNickname(peer.peerId, nick);
|
||||
BackOneContacts.invalidate();
|
||||
await refreshPeers();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderSelected() {
|
||||
if (!selectedPeer) {
|
||||
selectedEl.textContent = '';
|
||||
if (actionBtn) actionBtn.disabled = true;
|
||||
return;
|
||||
}
|
||||
const net = currentNetwork ? networkLabel(currentNetwork) : BackOneNetworkSession.getNetworkId();
|
||||
selectedEl.innerHTML = `Selected: <strong>${escapeHtml(selectedPeer.displayName)}</strong> (${escapeHtml(selectedPeer.peerId)})${net ? ` on <em>${escapeHtml(net)}</em>` : ''}`;
|
||||
if (actionBtn) actionBtn.disabled = !BackOneNetworkSession.getNetworkId();
|
||||
}
|
||||
|
||||
function escapeHtml(s) {
|
||||
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
async function refreshPeers() {
|
||||
currentNetwork = BackOneNetworkSession.getActiveNetwork();
|
||||
if (useSharedNetwork && BackOneNetworkSession.getNetworkId() && !currentNetwork) {
|
||||
currentNetwork = await BackOneNetworkSession.refresh();
|
||||
}
|
||||
renderNetworkBanner();
|
||||
const nwid = BackOneNetworkSession.getNetworkId();
|
||||
await BackOneContacts.load();
|
||||
peers = nwid ? await BackOnePeerDirectory.loadPeersForNetwork(nwid) : [];
|
||||
const lastPeerId = BackOneNetworkSession.getSelectedPeerId();
|
||||
if (lastPeerId) {
|
||||
selectedPeer = peers.find(p => p.peerId === lastPeerId) || BackOneNetworkSession.getSelectedPeer();
|
||||
}
|
||||
renderList();
|
||||
renderSelected();
|
||||
onNetworkChange(currentNetwork, peers);
|
||||
}
|
||||
|
||||
searchInput.addEventListener('input', renderList);
|
||||
if (actionBtn) {
|
||||
actionBtn.addEventListener('click', () => {
|
||||
if (selectedPeer && onAction) onAction(selectedPeer, currentNetwork);
|
||||
});
|
||||
}
|
||||
|
||||
if (useSharedNetwork) {
|
||||
unsub = BackOneNetworkSession.subscribe(() => refreshPeers());
|
||||
}
|
||||
|
||||
refreshPeers();
|
||||
|
||||
return {
|
||||
getSelectedPeer: () => selectedPeer,
|
||||
getSelectedNetwork: () => currentNetwork,
|
||||
refresh: refreshPeers,
|
||||
destroy: () => { if (unsub) unsub(); }
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
body { font-family: system-ui, sans-serif; margin: 0; background: #0f1419; color: #e7ecf1; }
|
||||
header { display: flex; align-items: center; gap: 1rem; padding: 1rem; background: #1a2332; border-bottom: 1px solid #2d3a4d; flex-wrap: wrap; }
|
||||
header h1 { margin: 0; font-size: 1.25rem; }
|
||||
.network-bar { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; flex: 1; min-width: 280px; }
|
||||
.network-bar-label { font-size: 0.85rem; color: #8b9bb4; }
|
||||
.network-bar select, .network-bar input { background: #0f1419; color: #e7ecf1; border: 1px solid #2d3a4d; border-radius: 4px; padding: 0.35rem 0.5rem; }
|
||||
.network-bar input { width: 10rem; }
|
||||
.network-bar button { background: #2d5a8a; color: #fff; border: none; padding: 0.35rem 0.65rem; border-radius: 4px; cursor: pointer; }
|
||||
.network-bar-status { font-size: 0.8rem; color: #6b8cae; flex: 1 1 100%; }
|
||||
.auth-token-bar { display: flex; align-items: center; gap: 0.5rem; flex: 1 1 100%; }
|
||||
.auth-token-hidden { display: none; }
|
||||
.auth-token-label { font-size: 0.85rem; color: #8b9bb4; }
|
||||
.auth-token-bar input { background: #0f1419; color: #e7ecf1; border: 1px solid #2d3a4d; border-radius: 4px; padding: 0.35rem 0.5rem; width: 16rem; }
|
||||
.auth-token-bar button { background: #2d5a8a; color: #fff; border: none; padding: 0.35rem 0.65rem; border-radius: 4px; cursor: pointer; }
|
||||
.core-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 0.35rem; vertical-align: middle; }
|
||||
.core-dot.online { background: #2ecc71; }
|
||||
.core-dot.offline { background: #e74c3c; }
|
||||
nav { display: flex; gap: 0.5rem; flex-wrap: wrap; }
|
||||
nav button { background: #2d5a8a; color: #fff; border: none; padding: 0.5rem 1rem; border-radius: 6px; cursor: pointer; }
|
||||
nav button.active { background: #4a90d9; }
|
||||
main { padding: 1.5rem; }
|
||||
.ringing-hidden { display: none; }
|
||||
#ringing-popup { position: fixed; inset: 0; background: rgba(0,0,0,0.6); display: flex; align-items: center; justify-content: center; z-index: 1000; }
|
||||
#ringing-popup.ringing-hidden { display: none; }
|
||||
.ringing-card { background: #1a2332; padding: 2rem; border-radius: 12px; text-align: center; min-width: 280px; }
|
||||
.ringing-card button { margin: 0.5rem; padding: 0.6rem 1.2rem; border: none; border-radius: 6px; cursor: pointer; }
|
||||
.ringing-accept { background: #2ecc71; color: #000; }
|
||||
.ringing-reject { background: #e74c3c; color: #fff; }
|
||||
.peer-picker { max-width: 480px; }
|
||||
.peer-picker-core { font-size: 0.75rem; color: #6b8cae; margin-bottom: 0.5rem; text-transform: uppercase; letter-spacing: 0.04em; }
|
||||
.peer-network-banner { font-size: 0.85rem; color: #8b9bb4; margin-bottom: 0.75rem; padding: 0.4rem 0.5rem; background: #1a2332; border-radius: 4px; }
|
||||
.peer-network-warn { color: #e6a23c; }
|
||||
.peer-picker-row { display: block; margin-bottom: 0.75rem; }
|
||||
.peer-picker-row select, .peer-picker-row input { width: 100%; margin-top: 0.25rem; padding: 0.4rem; background: #1a2332; color: #e7ecf1; border: 1px solid #2d3a4d; border-radius: 4px; }
|
||||
.peer-list { list-style: none; padding: 0; margin: 0.5rem 0; max-height: 280px; overflow: auto; border: 1px solid #2d3a4d; border-radius: 6px; }
|
||||
.peer-item { display: flex; align-items: center; gap: 0.5rem; padding: 0.6rem 0.75rem; cursor: pointer; border-bottom: 1px solid #2d3a4d; }
|
||||
.peer-item:last-child { border-bottom: none; }
|
||||
.peer-item:hover, .peer-item.active { background: #243044; }
|
||||
.peer-dot { width: 8px; height: 8px; border-radius: 50%; background: #666; flex-shrink: 0; }
|
||||
.peer-item.online .peer-dot { background: #2ecc71; }
|
||||
.peer-main { flex: 1; min-width: 0; }
|
||||
.peer-name { display: block; }
|
||||
.peer-sub { color: #8b9bb4; font-size: 0.8rem; }
|
||||
.peer-edit { background: transparent; border: none; color: #8b9bb4; cursor: pointer; padding: 0.25rem; }
|
||||
.peer-empty { padding: 1rem; color: #8b9bb4; }
|
||||
.peer-selected { margin: 0.5rem 0; min-height: 1.25rem; }
|
||||
.chat-layout { display: flex; gap: 1rem; min-height: 400px; }
|
||||
.chat-peers { flex: 0 0 280px; }
|
||||
.chat-thread-panel { flex: 1; display: flex; flex-direction: column; }
|
||||
.chat-thread-header { padding-bottom: 0.5rem; border-bottom: 1px solid #2d3a4d; margin-bottom: 0.5rem; }
|
||||
#chat-thread { flex: 1; overflow: auto; border: 1px solid #2d3a4d; padding: 0.5rem; border-radius: 6px; margin-bottom: 0.5rem; }
|
||||
.video-remote-label { position: absolute; bottom: 4px; left: 4px; background: rgba(0,0,0,0.6); padding: 2px 6px; font-size: 0.75rem; border-radius: 4px; }
|
||||
.video-wrap { position: relative; display: inline-block; }
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"id": "chat",
|
||||
"name": "Chat P2P",
|
||||
"version": "1.17.1",
|
||||
"requires": ["comms-core"],
|
||||
"backoneCore": {
|
||||
"apis": [
|
||||
"/app/api/peers",
|
||||
"/app/api/networks",
|
||||
"POST /app/api/network/{nwid}",
|
||||
"DELETE /app/api/network/{nwid}",
|
||||
"/app/api/network/{nwid}",
|
||||
"/app/api/network/{nwid}/members",
|
||||
"/app/api/authtoken",
|
||||
"/app/api/contacts"
|
||||
]
|
||||
},
|
||||
"backend": {
|
||||
"routes": ["/controller/comms/network"],
|
||||
"websocketEvents": ["chat_message", "typing"]
|
||||
},
|
||||
"frontend": {
|
||||
"entry": "/app/addons/chat/index.js",
|
||||
"navLabel": "Chat",
|
||||
"icon": "chat"
|
||||
},
|
||||
"settings": {
|
||||
"attachmentMaxBytes": 10485760
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
window.BackOneAddons = window.BackOneAddons || {};
|
||||
|
||||
window.BackOneAddons.chat = function chatAddon() {
|
||||
const messagesByPeer = {};
|
||||
let selectedPeer = null;
|
||||
let root = null;
|
||||
|
||||
function getThread(peerId) {
|
||||
if (!messagesByPeer[peerId]) messagesByPeer[peerId] = [];
|
||||
return messagesByPeer[peerId];
|
||||
}
|
||||
|
||||
function networkLabel() {
|
||||
const n = BackOneNetworkSession.getActiveNetwork();
|
||||
return n ? (n.name || n.id) : BackOneNetworkSession.getNetworkId();
|
||||
}
|
||||
|
||||
function updateConnectionBar() {
|
||||
const bar = root?.querySelector('#chat-conn-bar');
|
||||
if (!bar || !window.BackOneCommLayout) return;
|
||||
bar.innerHTML = BackOneCommLayout.connectionBarHtml(
|
||||
selectedPeer,
|
||||
BackOneNetworkSession.getActiveNetwork()
|
||||
);
|
||||
}
|
||||
|
||||
function renderHeader() {
|
||||
const header = root?.querySelector('#chat-header');
|
||||
if (!header) return;
|
||||
const net = networkLabel();
|
||||
if (!selectedPeer) {
|
||||
header.textContent = net ? `Select a contact on ${net}` : 'Join a network in the header bar';
|
||||
return;
|
||||
}
|
||||
header.innerHTML = `<strong>${selectedPeer.displayName || selectedPeer.peerId}</strong> <small>${selectedPeer.peerId}${net ? ' · ' + net : ''}</small>`;
|
||||
updateConnectionBar();
|
||||
}
|
||||
|
||||
return {
|
||||
mount(container) {
|
||||
root = container;
|
||||
container.innerHTML = `
|
||||
<div class="comm-layout">
|
||||
<aside class="comm-sidebar">
|
||||
<div class="comm-sidebar-head">
|
||||
<h2>Chat P2P</h2>
|
||||
<p class="comm-sidebar-sub">BackOne core · same network as backone-cli</p>
|
||||
</div>
|
||||
<div class="comm-sidebar-body">
|
||||
<div id="chat-peer-picker"></div>
|
||||
</div>
|
||||
</aside>
|
||||
<div class="comm-main">
|
||||
<div class="comm-main-head">
|
||||
<div class="comm-breadcrumb">Chat P2P > <strong id="chat-breadcrumb-peer">Select contact</strong></div>
|
||||
</div>
|
||||
<div class="chat-thread-panel">
|
||||
<div class="chat-thread-header" id="chat-header">Select a contact</div>
|
||||
<div id="chat-thread"></div>
|
||||
<div class="chat-compose">
|
||||
<textarea id="chat-input" rows="2" placeholder="Type a message" disabled></textarea>
|
||||
<div class="chat-compose-row">
|
||||
<input type="file" id="chat-file" accept="image/*,.pdf,.doc,.docx,.txt" disabled />
|
||||
<button type="button" id="chat-send" class="btn-primary" disabled>Send</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chat-conn-bar" class="comm-connection-bar"></div>`;
|
||||
|
||||
const thread = container.querySelector('#chat-thread');
|
||||
const input = container.querySelector('#chat-input');
|
||||
const fileInput = container.querySelector('#chat-file');
|
||||
const sendBtn = container.querySelector('#chat-send');
|
||||
|
||||
function updateInputs() {
|
||||
const ok = !!BackOneNetworkSession.getNetworkId() && !!selectedPeer;
|
||||
input.disabled = !ok;
|
||||
fileInput.disabled = !ok;
|
||||
sendBtn.disabled = !ok;
|
||||
renderHeader();
|
||||
const bc = root.querySelector('#chat-breadcrumb-peer');
|
||||
if (bc) bc.textContent = selectedPeer ? (selectedPeer.displayName || selectedPeer.peerId) : 'Select contact';
|
||||
}
|
||||
|
||||
const render = () => {
|
||||
if (!selectedPeer) {
|
||||
thread.innerHTML = '<p class="peer-empty">Choose a peer from the list</p>';
|
||||
return;
|
||||
}
|
||||
const messages = getThread(selectedPeer.peerId);
|
||||
thread.innerHTML = messages.map(m =>
|
||||
`<div><strong>${m.senderName || m.sender || 'me'}:</strong> ${m.body || ''}${m.file ? ` [${m.file}]` : ''}</div>`
|
||||
).join('');
|
||||
thread.scrollTop = thread.scrollHeight;
|
||||
};
|
||||
|
||||
BackOnePeerPicker(container.querySelector('#chat-peer-picker'), {
|
||||
layout: 'sidebar',
|
||||
showActionButton: false,
|
||||
onSelect(peer) {
|
||||
selectedPeer = peer;
|
||||
updateInputs();
|
||||
render();
|
||||
},
|
||||
onNetworkChange() {
|
||||
selectedPeer = null;
|
||||
updateInputs();
|
||||
render();
|
||||
}
|
||||
});
|
||||
|
||||
BackOneNetworkSession.subscribe(() => {
|
||||
updateInputs();
|
||||
render();
|
||||
});
|
||||
|
||||
sendBtn.onclick = async () => {
|
||||
const networkId = BackOneNetworkSession.getNetworkId();
|
||||
if (!selectedPeer || !networkId) return;
|
||||
const body = input.value.trim();
|
||||
const selfName = 'me';
|
||||
if (fileInput.files.length) {
|
||||
const fd = new FormData();
|
||||
fd.append('file', fileInput.files[0]);
|
||||
fd.append('networkId', networkId);
|
||||
await BackOneAuth.fetchWithAuth(`/controller/comms/network/${networkId}/chat/attachment`, {
|
||||
method: 'POST',
|
||||
body: fd
|
||||
}).catch(() => {});
|
||||
getThread(selectedPeer.peerId).push({
|
||||
sender: selfName,
|
||||
senderName: selfName,
|
||||
body: '',
|
||||
file: fileInput.files[0].name
|
||||
});
|
||||
fileInput.value = '';
|
||||
}
|
||||
if (body) {
|
||||
await BackOneAuth.fetchWithAuth(`/controller/comms/network/${networkId}/chat`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ body, targetId: selectedPeer.peerId, networkId })
|
||||
}).catch(() => {});
|
||||
getThread(selectedPeer.peerId).push({
|
||||
sender: selfName,
|
||||
senderName: selfName,
|
||||
body
|
||||
});
|
||||
input.value = '';
|
||||
}
|
||||
render();
|
||||
};
|
||||
|
||||
updateInputs();
|
||||
render();
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"id": "comms-core",
|
||||
"name": "Comms Core",
|
||||
"version": "1.17.1",
|
||||
"requires": [],
|
||||
"backend": {
|
||||
"routes": ["/controller/comms"],
|
||||
"websocketEvents": ["presence", "incoming_call", "call_state", "chat_message"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
-- BackOne comms-core addon schema (PostgreSQL)
|
||||
CREATE TABLE IF NOT EXISTS backone_calls (
|
||||
id TEXT PRIMARY KEY,
|
||||
network_id TEXT NOT NULL,
|
||||
caller_id TEXT NOT NULL,
|
||||
callee_id TEXT NOT NULL,
|
||||
call_type TEXT NOT NULL CHECK (call_type IN ('voice', 'video')),
|
||||
state TEXT NOT NULL,
|
||||
sdp_offer TEXT,
|
||||
sdp_answer TEXT,
|
||||
created_at BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS backone_chat_messages (
|
||||
id TEXT PRIMARY KEY,
|
||||
network_id TEXT NOT NULL,
|
||||
sender_id TEXT NOT NULL,
|
||||
body TEXT,
|
||||
attachment_id TEXT,
|
||||
created_at BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS backone_attachments (
|
||||
id TEXT PRIMARY KEY,
|
||||
network_id TEXT NOT NULL,
|
||||
sender_id TEXT NOT NULL,
|
||||
filename TEXT NOT NULL,
|
||||
mime_type TEXT NOT NULL,
|
||||
size_bytes BIGINT NOT NULL,
|
||||
storage_path TEXT NOT NULL,
|
||||
created_at BIGINT NOT NULL
|
||||
);
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"id": "updater",
|
||||
"name": "BackOne Updater",
|
||||
"version": "1.17.1",
|
||||
"requires": [],
|
||||
"backend": {
|
||||
"routes": [],
|
||||
"websocketEvents": []
|
||||
}
|
||||
}
|
||||
Executable
+129
@@ -0,0 +1,129 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if [ -f "${SCRIPT_DIR}/platform-paths.sh" ]; then
|
||||
# shellcheck source=platform-paths.sh
|
||||
. "${SCRIPT_DIR}/platform-paths.sh"
|
||||
else
|
||||
# shellcheck source=../../pkg/common/platform-paths.sh
|
||||
. "${SCRIPT_DIR}/../../pkg/common/platform-paths.sh"
|
||||
fi
|
||||
backone_resolve_paths
|
||||
|
||||
HOME_DIR="${BACKONE_HOME}"
|
||||
STAMP="${HOME_DIR}/.installed-version"
|
||||
LOG_DIR="${BACKONE_LOG_DIR:-/var/log/backone}"
|
||||
LOG_FILE="${LOG_DIR}/updater.log"
|
||||
MIGRATIONS="${BACKONE_ADDONS_DIR}/updater/migrations"
|
||||
LEGACY_HOME="/var/lib/zerotier-one"
|
||||
|
||||
log() {
|
||||
mkdir -p "$LOG_DIR"
|
||||
echo "$(date -Iseconds) $*" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
read_version() {
|
||||
if [ -f "$STAMP" ]; then
|
||||
cat "$STAMP"
|
||||
else
|
||||
echo '{"package":"backone","version":"0.0.0","schemaVersion":0}'
|
||||
fi
|
||||
}
|
||||
|
||||
write_version() {
|
||||
local version="${1:-$(cat debian/version 2>/dev/null || echo 1.15.0)}"
|
||||
mkdir -p "$HOME_DIR"
|
||||
cat > "$STAMP" <<EOF
|
||||
{"package":"backone","version":"${version}","schemaVersion":2,"updatedAt":"$(date -Iseconds)"}
|
||||
EOF
|
||||
chown backone:backone "$STAMP" 2>/dev/null || true
|
||||
}
|
||||
|
||||
run_migrations() {
|
||||
local scope="${1:-all}"
|
||||
[ -d "$MIGRATIONS" ] || return 0
|
||||
for script in "$MIGRATIONS"/*; do
|
||||
[ -f "$script" ] || continue
|
||||
case "$(basename "$script")" in
|
||||
001_*)
|
||||
[ "$scope" = "all" ] || [ "$scope" = "core" ] || continue
|
||||
;;
|
||||
002_*)
|
||||
[ "$scope" = "all" ] || [ "$scope" = "core" ] || continue
|
||||
;;
|
||||
003_*)
|
||||
[ "$scope" = "all" ] || [ "$scope" = "core" ] || continue
|
||||
;;
|
||||
*)
|
||||
if [ "$scope" != "all" ] && [ "$scope" != "$ADDON_SCOPE" ]; then
|
||||
continue
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
log "Running migration: $(basename "$script")"
|
||||
BACKONE_HOME="$HOME_DIR" bash "$script" || log "Migration warning: $(basename "$script")"
|
||||
done
|
||||
}
|
||||
|
||||
migrate_zerotier() {
|
||||
if [ -d "$LEGACY_HOME" ] && [ ! -f "$HOME_DIR/identity.public" ]; then
|
||||
log "Migrating $LEGACY_HOME -> $HOME_DIR"
|
||||
mkdir -p "$HOME_DIR"
|
||||
cp -a "$LEGACY_HOME/." "$HOME_DIR/" 2>/dev/null || true
|
||||
ln -sfn "$HOME_DIR" "$LEGACY_HOME" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
rebuild_manifest() {
|
||||
local registry="${HOME_DIR}/addons.d/enabled.json"
|
||||
mkdir -p "${HOME_DIR}/addons.d"
|
||||
if [ ! -f "$registry" ]; then
|
||||
echo '[]' > "$registry"
|
||||
fi
|
||||
local script_dir manifest_script
|
||||
script_dir="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)"
|
||||
manifest_script="${script_dir}/rebuild-addon-manifest.sh"
|
||||
if [ ! -x "$manifest_script" ]; then
|
||||
echo "backone-updater: ${manifest_script} not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
local count
|
||||
count="$(BACKONE_HOME="$HOME_DIR" BACKONE_ADDONS_DIR="${BACKONE_ADDONS_DIR}" bash "$manifest_script")"
|
||||
log "Wrote ${HOME_DIR}/app/addons/manifest.json (${count} UI addons)"
|
||||
}
|
||||
|
||||
cmd_migrate() {
|
||||
local scope="all"
|
||||
ADDON_SCOPE=""
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--core) scope="core"; shift ;;
|
||||
--addon) ADDON_SCOPE="$2"; scope="addon"; shift 2 ;;
|
||||
--dry-run)
|
||||
log "Dry run: would migrate scope=$scope addon=$ADDON_SCOPE"
|
||||
ls -1 "$MIGRATIONS" 2>/dev/null || true
|
||||
exit 0
|
||||
;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
migrate_zerotier
|
||||
run_migrations "$scope"
|
||||
write_version
|
||||
rebuild_manifest
|
||||
log "Migration complete"
|
||||
}
|
||||
|
||||
cmd_status() {
|
||||
echo "Installed: $(read_version)"
|
||||
echo "Package version: $(cat "${BACKONE_ADDONS_DIR}/comms-core/addon.json" 2>/dev/null | grep '"version"' | head -1 || cat debian/version 2>/dev/null || echo unknown)"
|
||||
echo "Addons registry: ${HOME_DIR}/addons.d/enabled.json"
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
migrate) shift; cmd_migrate "$@" ;;
|
||||
status) cmd_status ;;
|
||||
rebuild-manifest) rebuild_manifest ;;
|
||||
*) echo "Usage: backone-updater migrate [--core|--addon ID] [--dry-run] | status | rebuild-manifest" >&2; exit 1 ;;
|
||||
esac
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/bin/bash
|
||||
# Deep-merge JSON into /var/lib/backone/local.conf (idempotent).
|
||||
set -euo pipefail
|
||||
|
||||
HOME_DIR="${BACKONE_HOME:-/var/lib/backone}"
|
||||
CONF="${HOME_DIR}/local.conf"
|
||||
PATCH="${1:-}"
|
||||
|
||||
if [ -z "$PATCH" ] && [ ! -t 0 ]; then
|
||||
PATCH="$(cat)"
|
||||
fi
|
||||
|
||||
if [ -z "$PATCH" ]; then
|
||||
echo "Usage: merge-local-conf.sh '<json>' or pipe JSON on stdin" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
python3 - "$CONF" "$PATCH" <<'PY'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
conf_path, patch_raw = sys.argv[1], sys.argv[2]
|
||||
|
||||
def deep_merge(base, patch):
|
||||
if not isinstance(base, dict) or not isinstance(patch, dict):
|
||||
return patch
|
||||
out = dict(base)
|
||||
for k, v in patch.items():
|
||||
if k in out and isinstance(out[k], dict) and isinstance(v, dict):
|
||||
out[k] = deep_merge(out[k], v)
|
||||
else:
|
||||
out[k] = v
|
||||
return out
|
||||
|
||||
patch = json.loads(patch_raw)
|
||||
current = {}
|
||||
if os.path.isfile(conf_path):
|
||||
with open(conf_path, encoding="utf-8") as f:
|
||||
try:
|
||||
current = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
current = {}
|
||||
if not isinstance(current, dict):
|
||||
current = {}
|
||||
|
||||
merged = deep_merge(current, patch)
|
||||
os.makedirs(os.path.dirname(conf_path), exist_ok=True)
|
||||
with open(conf_path, "w", encoding="utf-8") as f:
|
||||
json.dump(merged, f, indent=2)
|
||||
f.write("\n")
|
||||
PY
|
||||
|
||||
chown backone:backone "$CONF" 2>/dev/null || true
|
||||
chmod 644 "$CONF" 2>/dev/null || true
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
BACKONE_HOME="${BACKONE_HOME:-/var/lib/backone}"
|
||||
LEGACY="/var/lib/zerotier-one"
|
||||
if [ -d "$LEGACY" ] && [ ! -f "$BACKONE_HOME/identity.public" ]; then
|
||||
mkdir -p "$BACKONE_HOME"
|
||||
cp -an "$LEGACY/." "$BACKONE_HOME/" 2>/dev/null || cp -a "$LEGACY/." "$BACKONE_HOME/"
|
||||
fi
|
||||
if ! getent passwd backone >/dev/null 2>&1 && getent passwd zerotier-one >/dev/null 2>&1; then
|
||||
usermod -l backone -d "$BACKONE_HOME" zerotier-one 2>/dev/null || true
|
||||
fi
|
||||
chown -R backone:backone "$BACKONE_HOME" 2>/dev/null || true
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
BACKONE_HOME="${BACKONE_HOME:-/var/lib/backone}"
|
||||
mkdir -p "$BACKONE_HOME/addons.d" "$BACKONE_HOME/app/addons"
|
||||
if [ ! -f "$BACKONE_HOME/addons.d/enabled.json" ]; then
|
||||
echo '[]' > "$BACKONE_HOME/addons.d/enabled.json"
|
||||
fi
|
||||
chown -R backone:backone "$BACKONE_HOME/addons.d" "$BACKONE_HOME/app" 2>/dev/null || true
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
BACKONE_HOME="${BACKONE_HOME:-/var/lib/backone}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
if [ -x "${SCRIPT_DIR}/merge-local-conf.sh" ]; then
|
||||
BACKONE_HOME="$BACKONE_HOME" bash "${SCRIPT_DIR}/merge-local-conf.sh" '{"settings":{"enableWebServer":true}}'
|
||||
fi
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
# Writes $BACKONE_HOME/app/addons/manifest.json from installed addon.json files.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if [ -f "${SCRIPT_DIR}/platform-paths.sh" ]; then
|
||||
# shellcheck source=platform-paths.sh
|
||||
. "${SCRIPT_DIR}/platform-paths.sh"
|
||||
else
|
||||
# shellcheck source=../../pkg/common/platform-paths.sh
|
||||
. "${SCRIPT_DIR}/../../pkg/common/platform-paths.sh"
|
||||
fi
|
||||
backone_resolve_paths
|
||||
|
||||
HOME_DIR="${BACKONE_HOME}"
|
||||
ADDONS_DIR="${BACKONE_ADDONS_DIR}"
|
||||
MANIFEST="${HOME_DIR}/app/addons/manifest.json"
|
||||
|
||||
mkdir -p "${HOME_DIR}/app/addons" "${HOME_DIR}/addons.d"
|
||||
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
echo "rebuild-addon-manifest: python3 required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
python3 - "$ADDONS_DIR" "$MANIFEST" <<'PY'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
addons_dir, manifest_path = sys.argv[1], sys.argv[2]
|
||||
out = []
|
||||
if os.path.isdir(addons_dir):
|
||||
for name in sorted(os.listdir(addons_dir)):
|
||||
path = os.path.join(addons_dir, name, "addon.json")
|
||||
if not os.path.isfile(path):
|
||||
continue
|
||||
with open(path, encoding="utf-8") as f:
|
||||
m = json.load(f)
|
||||
if "frontend" not in m:
|
||||
continue
|
||||
item = {
|
||||
"id": m["id"],
|
||||
"name": m.get("name", m["id"]),
|
||||
"version": m.get("version", "0.0.0"),
|
||||
"frontend": m["frontend"],
|
||||
}
|
||||
if "requires" in m:
|
||||
item["requires"] = m["requires"]
|
||||
out.append(item)
|
||||
|
||||
os.makedirs(os.path.dirname(manifest_path), exist_ok=True)
|
||||
with open(manifest_path, "w", encoding="utf-8") as f:
|
||||
json.dump(out, f, indent=2)
|
||||
f.write("\n")
|
||||
print(len(out))
|
||||
PY
|
||||
|
||||
chown backone:backone "$MANIFEST" 2>/dev/null || true
|
||||
chmod 644 "$MANIFEST" 2>/dev/null || true
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
#!/bin/sh
|
||||
# Keep ~/.backOneOneAuthToken and /var/lib/backone/authtoken.secret in sync (same as backone-cli).
|
||||
set -e
|
||||
|
||||
BACKONE_HOME="${BACKONE_HOME:-/var/lib/backone}"
|
||||
SECRET="${BACKONE_HOME}/authtoken.secret"
|
||||
|
||||
trim_token() {
|
||||
tr -d '\n\r \t' < "$1"
|
||||
}
|
||||
|
||||
sync_for_user() {
|
||||
user="$1"
|
||||
home="$2"
|
||||
[ -n "$user" ] && [ -n "$home" ] && [ -d "$home" ] || return 0
|
||||
user_token="${home}/.backOneOneAuthToken"
|
||||
|
||||
if [ -f "$user_token" ] && [ -s "$user_token" ]; then
|
||||
ut="$(trim_token "$user_token")"
|
||||
[ -n "$ut" ] || return 0
|
||||
st=""
|
||||
if [ -f "$SECRET" ]; then
|
||||
st="$(trim_token "$SECRET" 2>/dev/null || true)"
|
||||
fi
|
||||
if [ "$ut" != "$st" ]; then
|
||||
printf '%s' "$ut" > "$SECRET"
|
||||
chown backone:backone "$SECRET" 2>/dev/null || true
|
||||
chmod 0600 "$SECRET" 2>/dev/null || true
|
||||
echo "Synced ${SECRET} from ${user_token}"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -f "$SECRET" ]; then
|
||||
install -o "$user" -g "$(id -gn "$user" 2>/dev/null || echo "$user")" -m 0600 "$SECRET" "$user_token" 2>/dev/null \
|
||||
|| cp "$SECRET" "$user_token"
|
||||
chown "$user":"$(id -gn "$user" 2>/dev/null || echo "$user")" "$user_token" 2>/dev/null || true
|
||||
chmod 0600 "$user_token" 2>/dev/null || true
|
||||
echo "Installed ${user_token} from ${SECRET}"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ -n "${SUDO_USER:-}" ] && [ "$SUDO_USER" != "root" ]; then
|
||||
home="$(getent passwd "$SUDO_USER" | cut -d: -f6)"
|
||||
sync_for_user "$SUDO_USER" "$home"
|
||||
fi
|
||||
|
||||
if [ -n "${USER:-}" ] && [ "$USER" != "root" ] && [ "$USER" != "${SUDO_USER:-}" ]; then
|
||||
home="$(getent passwd "$USER" | cut -d: -f6)"
|
||||
sync_for_user "$USER" "$home"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"id": "video",
|
||||
"name": "Video Call P2P",
|
||||
"version": "1.17.1",
|
||||
"requires": ["comms-core", "voice"],
|
||||
"backoneCore": {
|
||||
"apis": [
|
||||
"/app/api/peers",
|
||||
"/app/api/networks",
|
||||
"POST /app/api/network/{nwid}",
|
||||
"DELETE /app/api/network/{nwid}",
|
||||
"/app/api/network/{nwid}",
|
||||
"/app/api/network/{nwid}/members",
|
||||
"/app/api/authtoken",
|
||||
"/app/api/contacts"
|
||||
]
|
||||
},
|
||||
"backend": {
|
||||
"routes": ["/controller/comms/call"],
|
||||
"websocketEvents": ["incoming_call", "call_state"]
|
||||
},
|
||||
"frontend": {
|
||||
"entry": "/app/addons/video/index.js",
|
||||
"navLabel": "Video",
|
||||
"icon": "video"
|
||||
},
|
||||
"settings": {
|
||||
"enableVideoCall": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
window.BackOneAddons = window.BackOneAddons || {};
|
||||
|
||||
window.BackOneAddons.video = function videoAddon() {
|
||||
let pc = null;
|
||||
let picker = null;
|
||||
let selectedPeer = null;
|
||||
let inCall = false;
|
||||
let localStream = null;
|
||||
let micEnabled = true;
|
||||
let camEnabled = true;
|
||||
let root = null;
|
||||
|
||||
function networkLabel() {
|
||||
const n = BackOneNetworkSession.getActiveNetwork();
|
||||
return n ? (n.name || n.id) : BackOneNetworkSession.getNetworkId();
|
||||
}
|
||||
|
||||
function updateConnectionBar() {
|
||||
const bar = root?.querySelector('#video-conn-bar');
|
||||
if (!bar || !window.BackOneCommLayout) return;
|
||||
bar.innerHTML = BackOneCommLayout.connectionBarHtml(
|
||||
selectedPeer,
|
||||
BackOneNetworkSession.getActiveNetwork()
|
||||
);
|
||||
}
|
||||
|
||||
function updateHeadStatus() {
|
||||
const statusEl = root?.querySelector('#video-head-status');
|
||||
const breadcrumb = root?.querySelector('#video-breadcrumb-peer');
|
||||
if (!statusEl) return;
|
||||
if (inCall && selectedPeer) {
|
||||
statusEl.innerHTML = `<span class="connected">Connected</span> · ${selectedPeer.latency != null ? selectedPeer.latency + 'ms' : '—'}`;
|
||||
if (breadcrumb) breadcrumb.textContent = selectedPeer.peerId;
|
||||
} else if (selectedPeer) {
|
||||
statusEl.textContent = 'Ready';
|
||||
if (breadcrumb) breadcrumb.textContent = selectedPeer.peerId;
|
||||
} else {
|
||||
statusEl.textContent = '';
|
||||
if (breadcrumb) breadcrumb.textContent = '—';
|
||||
}
|
||||
}
|
||||
|
||||
function updateCallButtons() {
|
||||
const callBtn = root?.querySelector('#video-call');
|
||||
if (callBtn) callBtn.disabled = !selectedPeer || !BackOneNetworkSession.getNetworkId() || inCall;
|
||||
const selectedLabel = root?.querySelector('#video-selected-label');
|
||||
if (selectedLabel) {
|
||||
selectedLabel.innerHTML = selectedPeer
|
||||
? `Selected: <strong>${selectedPeer.peerId.slice(0, 10)}…</strong>`
|
||||
: 'Selected: <strong>—</strong>';
|
||||
}
|
||||
updateHeadStatus();
|
||||
updateConnectionBar();
|
||||
}
|
||||
|
||||
function showPlaceholder(show) {
|
||||
const ph = root?.querySelector('#video-placeholder');
|
||||
const remote = root?.querySelector('#video-remote');
|
||||
if (ph) ph.style.display = show ? 'block' : 'none';
|
||||
if (remote) remote.style.display = show ? 'none' : 'block';
|
||||
}
|
||||
|
||||
function hangUp() {
|
||||
if (pc) { pc.close(); pc = null; }
|
||||
if (localStream) {
|
||||
localStream.getTracks().forEach(t => t.stop());
|
||||
localStream = null;
|
||||
}
|
||||
const local = root?.querySelector('#video-local');
|
||||
const remote = root?.querySelector('#video-remote');
|
||||
if (local) local.srcObject = null;
|
||||
if (remote) remote.srcObject = null;
|
||||
inCall = false;
|
||||
micEnabled = true;
|
||||
camEnabled = true;
|
||||
showPlaceholder(true);
|
||||
updateCallButtons();
|
||||
window.BackOneRingingPopup?.dismiss();
|
||||
}
|
||||
|
||||
return {
|
||||
mount(container) {
|
||||
root = container;
|
||||
container.innerHTML = `
|
||||
<div class="comm-layout">
|
||||
<aside class="comm-sidebar">
|
||||
<div class="comm-sidebar-head">
|
||||
<h2>Video Call P2P</h2>
|
||||
<p class="comm-sidebar-sub">BackOne core · same network as backone-cli</p>
|
||||
</div>
|
||||
<div class="comm-sidebar-body">
|
||||
<div id="video-picker"></div>
|
||||
</div>
|
||||
<div class="comm-sidebar-actions">
|
||||
<div id="video-selected-label" class="comm-selected-label">Selected: <strong>—</strong></div>
|
||||
<button type="button" id="video-call" class="btn-primary" disabled>Video Call</button>
|
||||
<button type="button" id="video-hangup" class="btn-secondary">Hang Up</button>
|
||||
</div>
|
||||
</aside>
|
||||
<div class="comm-main">
|
||||
<div class="comm-main-head">
|
||||
<div class="comm-breadcrumb">Video Call P2P > <span id="video-breadcrumb-state">${inCall ? 'Connected to' : 'Select peer'}</span> <strong id="video-breadcrumb-peer">—</strong></div>
|
||||
<div id="video-head-status" class="comm-head-status"></div>
|
||||
</div>
|
||||
<div class="comm-stage">
|
||||
<div class="comm-remote-tag"><span class="dot"></span> Remote</div>
|
||||
<div id="video-placeholder" class="comm-remote-placeholder">
|
||||
<div class="avatar">👤</div>
|
||||
<div class="peer-id" id="video-ph-peer">—</div>
|
||||
<div>Remote Peer</div>
|
||||
</div>
|
||||
<video id="video-remote" autoplay playsinline style="display:none"></video>
|
||||
<div class="video-local-pip">
|
||||
<video id="video-local" autoplay muted playsinline></video>
|
||||
</div>
|
||||
<div class="comm-call-controls">
|
||||
<button type="button" id="video-mic">Mic</button>
|
||||
<button type="button" id="video-cam">Camera</button>
|
||||
<button type="button" id="video-fs">Fullscreen</button>
|
||||
<button type="button" id="video-hangup-stage" class="danger">Hang Up</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="video-conn-bar" class="comm-connection-bar"></div>`;
|
||||
|
||||
const callBtn = container.querySelector('#video-call');
|
||||
|
||||
picker = BackOnePeerPicker(container.querySelector('#video-picker'), {
|
||||
layout: 'sidebar',
|
||||
showActionButton: false,
|
||||
onSelect(peer) {
|
||||
selectedPeer = peer;
|
||||
const phPeer = root.querySelector('#video-ph-peer');
|
||||
if (phPeer) phPeer.textContent = peer.peerId;
|
||||
updateCallButtons();
|
||||
},
|
||||
onNetworkChange() {
|
||||
if (!inCall) selectedPeer = null;
|
||||
updateCallButtons();
|
||||
}
|
||||
});
|
||||
|
||||
BackOneNetworkSession.subscribe(() => updateCallButtons());
|
||||
|
||||
callBtn.onclick = async () => {
|
||||
const peer = selectedPeer || picker.getSelectedPeer();
|
||||
const networkId = BackOneNetworkSession.getNetworkId();
|
||||
if (!peer || !networkId) return;
|
||||
try {
|
||||
pc = new RTCPeerConnection();
|
||||
pc.ontrack = (ev) => {
|
||||
root.querySelector('#video-remote').srcObject = ev.streams[0];
|
||||
showPlaceholder(false);
|
||||
};
|
||||
localStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
|
||||
root.querySelector('#video-local').srcObject = localStream;
|
||||
localStream.getTracks().forEach(t => pc.addTrack(t, localStream));
|
||||
const offer = await pc.createOffer();
|
||||
await pc.setLocalDescription(offer);
|
||||
await BackOneAuth.fetchWithAuth('/controller/comms/call', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
type: 'video',
|
||||
networkId,
|
||||
targetId: peer.peerId,
|
||||
sdp: offer.sdp
|
||||
})
|
||||
});
|
||||
inCall = true;
|
||||
root.querySelector('#video-breadcrumb-state').textContent = 'Connected to';
|
||||
updateCallButtons();
|
||||
} catch (err) {
|
||||
hangUp();
|
||||
alert(err.message || 'Call failed');
|
||||
}
|
||||
};
|
||||
|
||||
container.querySelector('#video-hangup').onclick = hangUp;
|
||||
container.querySelector('#video-hangup-stage').onclick = hangUp;
|
||||
|
||||
container.querySelector('#video-mic').onclick = () => {
|
||||
if (!localStream) return;
|
||||
micEnabled = !micEnabled;
|
||||
localStream.getAudioTracks().forEach(t => { t.enabled = micEnabled; });
|
||||
container.querySelector('#video-mic').textContent = micEnabled ? 'Mic' : 'Mic off';
|
||||
};
|
||||
|
||||
container.querySelector('#video-cam').onclick = () => {
|
||||
if (!localStream) return;
|
||||
camEnabled = !camEnabled;
|
||||
localStream.getVideoTracks().forEach(t => { t.enabled = camEnabled; });
|
||||
container.querySelector('#video-cam').textContent = camEnabled ? 'Camera' : 'Camera off';
|
||||
};
|
||||
|
||||
container.querySelector('#video-fs').onclick = () => {
|
||||
const stage = container.querySelector('.comm-stage');
|
||||
if (stage.requestFullscreen) stage.requestFullscreen();
|
||||
};
|
||||
|
||||
showPlaceholder(true);
|
||||
updateCallButtons();
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"id": "voice",
|
||||
"name": "Voice Call P2P",
|
||||
"version": "1.17.1",
|
||||
"requires": ["comms-core"],
|
||||
"backoneCore": {
|
||||
"apis": [
|
||||
"/app/api/peers",
|
||||
"/app/api/networks",
|
||||
"POST /app/api/network/{nwid}",
|
||||
"DELETE /app/api/network/{nwid}",
|
||||
"/app/api/network/{nwid}",
|
||||
"/app/api/network/{nwid}/members",
|
||||
"/app/api/authtoken",
|
||||
"/app/api/contacts"
|
||||
]
|
||||
},
|
||||
"backend": {
|
||||
"routes": ["/controller/comms/call"],
|
||||
"websocketEvents": ["incoming_call", "call_state"]
|
||||
},
|
||||
"frontend": {
|
||||
"entry": "/app/addons/voice/index.js",
|
||||
"navLabel": "Voice",
|
||||
"icon": "phone"
|
||||
},
|
||||
"settings": {
|
||||
"enableVoiceCall": true,
|
||||
"ringTimeoutSec": 30
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
window.BackOneAddons = window.BackOneAddons || {};
|
||||
|
||||
window.BackOneAddons.voice = function voiceAddon() {
|
||||
let pc = null;
|
||||
let picker = null;
|
||||
let selectedPeer = null;
|
||||
let inCall = false;
|
||||
let localStream = null;
|
||||
let micEnabled = true;
|
||||
let root = null;
|
||||
|
||||
function updateConnectionBar() {
|
||||
const bar = root?.querySelector('#voice-conn-bar');
|
||||
if (!bar || !window.BackOneCommLayout) return;
|
||||
bar.innerHTML = BackOneCommLayout.connectionBarHtml(
|
||||
selectedPeer,
|
||||
BackOneNetworkSession.getActiveNetwork()
|
||||
);
|
||||
}
|
||||
|
||||
function updateHeadStatus() {
|
||||
const statusEl = root?.querySelector('#voice-head-status');
|
||||
const breadcrumb = root?.querySelector('#voice-breadcrumb-peer');
|
||||
if (!statusEl) return;
|
||||
if (inCall && selectedPeer) {
|
||||
statusEl.innerHTML = `<span class="connected">Connected</span> · ${selectedPeer.latency != null ? selectedPeer.latency + 'ms' : '—'}`;
|
||||
if (breadcrumb) breadcrumb.textContent = selectedPeer.peerId;
|
||||
} else if (selectedPeer) {
|
||||
statusEl.textContent = 'Ready';
|
||||
if (breadcrumb) breadcrumb.textContent = selectedPeer.peerId;
|
||||
} else {
|
||||
statusEl.textContent = '';
|
||||
if (breadcrumb) breadcrumb.textContent = '—';
|
||||
}
|
||||
}
|
||||
|
||||
function updateCallButtons() {
|
||||
const callBtn = root?.querySelector('#voice-call');
|
||||
if (callBtn) callBtn.disabled = !selectedPeer || !BackOneNetworkSession.getNetworkId() || inCall;
|
||||
const selectedLabel = root?.querySelector('#voice-selected-label');
|
||||
if (selectedLabel) {
|
||||
selectedLabel.innerHTML = selectedPeer
|
||||
? `Selected: <strong>${selectedPeer.peerId.slice(0, 10)}…</strong>`
|
||||
: 'Selected: <strong>—</strong>';
|
||||
}
|
||||
updateHeadStatus();
|
||||
updateConnectionBar();
|
||||
}
|
||||
|
||||
function hangUp() {
|
||||
if (pc) { pc.close(); pc = null; }
|
||||
if (localStream) {
|
||||
localStream.getTracks().forEach(t => t.stop());
|
||||
localStream = null;
|
||||
}
|
||||
inCall = false;
|
||||
micEnabled = true;
|
||||
updateCallButtons();
|
||||
window.BackOneRingingPopup?.dismiss();
|
||||
}
|
||||
|
||||
return {
|
||||
mount(container) {
|
||||
root = container;
|
||||
container.innerHTML = `
|
||||
<div class="comm-layout">
|
||||
<aside class="comm-sidebar">
|
||||
<div class="comm-sidebar-head">
|
||||
<h2>Voice Call P2P</h2>
|
||||
<p class="comm-sidebar-sub">BackOne core · same network as backone-cli</p>
|
||||
</div>
|
||||
<div class="comm-sidebar-body">
|
||||
<div id="voice-picker"></div>
|
||||
</div>
|
||||
<div class="comm-sidebar-actions">
|
||||
<div id="voice-selected-label" class="comm-selected-label">Selected: <strong>—</strong></div>
|
||||
<button type="button" id="voice-call" class="btn-primary" disabled>Voice Call</button>
|
||||
<button type="button" id="voice-hangup" class="btn-secondary">Hang Up</button>
|
||||
</div>
|
||||
</aside>
|
||||
<div class="comm-main">
|
||||
<div class="comm-main-head">
|
||||
<div class="comm-breadcrumb">Voice Call P2P > <span id="voice-breadcrumb-state">Select peer</span> <strong id="voice-breadcrumb-peer">—</strong></div>
|
||||
<div id="voice-head-status" class="comm-head-status"></div>
|
||||
</div>
|
||||
<div class="comm-stage">
|
||||
<div class="voice-stage-inner">
|
||||
<div class="avatar">🎙</div>
|
||||
<div class="peer-id" id="voice-ph-peer">—</div>
|
||||
<div id="voice-stage-label">Select a peer to call</div>
|
||||
</div>
|
||||
<div class="comm-call-controls">
|
||||
<button type="button" id="voice-mic">Mic</button>
|
||||
<button type="button" id="voice-hangup-stage" class="danger">Hang Up</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="voice-conn-bar" class="comm-connection-bar"></div>`;
|
||||
|
||||
const callBtn = container.querySelector('#voice-call');
|
||||
const stageLabel = container.querySelector('#voice-stage-label');
|
||||
|
||||
picker = BackOnePeerPicker(container.querySelector('#voice-picker'), {
|
||||
layout: 'sidebar',
|
||||
showActionButton: false,
|
||||
onSelect(peer) {
|
||||
selectedPeer = peer;
|
||||
root.querySelector('#voice-ph-peer').textContent = peer.peerId;
|
||||
if (!inCall) stageLabel.textContent = `Ready to call ${peer.displayName || peer.peerId}`;
|
||||
updateCallButtons();
|
||||
},
|
||||
onNetworkChange() {
|
||||
if (!inCall) selectedPeer = null;
|
||||
updateCallButtons();
|
||||
}
|
||||
});
|
||||
|
||||
BackOneNetworkSession.subscribe(() => updateCallButtons());
|
||||
|
||||
callBtn.onclick = async () => {
|
||||
const peer = selectedPeer || picker.getSelectedPeer();
|
||||
const networkId = BackOneNetworkSession.getNetworkId();
|
||||
if (!peer || !networkId) return;
|
||||
try {
|
||||
pc = new RTCPeerConnection();
|
||||
localStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
|
||||
localStream.getTracks().forEach(t => pc.addTrack(t, localStream));
|
||||
const offer = await pc.createOffer();
|
||||
await pc.setLocalDescription(offer);
|
||||
await BackOneAuth.fetchWithAuth('/controller/comms/call', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
type: 'voice',
|
||||
networkId,
|
||||
targetId: peer.peerId,
|
||||
sdp: offer.sdp
|
||||
})
|
||||
});
|
||||
inCall = true;
|
||||
root.querySelector('#voice-breadcrumb-state').textContent = 'Connected to';
|
||||
stageLabel.textContent = `In call with ${peer.displayName || peer.peerId}`;
|
||||
updateCallButtons();
|
||||
} catch (err) {
|
||||
hangUp();
|
||||
alert(err.message || 'Call failed');
|
||||
}
|
||||
};
|
||||
|
||||
container.querySelector('#voice-hangup').onclick = hangUp;
|
||||
container.querySelector('#voice-hangup-stage').onclick = hangUp;
|
||||
|
||||
container.querySelector('#voice-mic').onclick = () => {
|
||||
if (!localStream) return;
|
||||
micEnabled = !micEnabled;
|
||||
localStream.getAudioTracks().forEach(t => { t.enabled = micEnabled; });
|
||||
container.querySelector('#voice-mic').textContent = micEnabled ? 'Mic' : 'Mic off';
|
||||
};
|
||||
|
||||
updateCallButtons();
|
||||
}
|
||||
};
|
||||
};
|
||||
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
window.BackOneRingingPopup = {
|
||||
_audio: null,
|
||||
showIncomingCall({ callerName, callType, callId, onAccept, onReject }) {
|
||||
const el = document.getElementById('ringing-popup');
|
||||
if (!el) return;
|
||||
el.classList.remove('ringing-hidden');
|
||||
el.innerHTML = `
|
||||
<div class="ringing-card">
|
||||
<h2>Incoming ${callType} call</h2>
|
||||
<p>${callerName || 'Unknown peer'}</p>
|
||||
<button class="ringing-accept">Accept</button>
|
||||
<button class="ringing-reject">Reject</button>
|
||||
</div>`;
|
||||
el.querySelector('.ringing-accept').onclick = () => { this.dismiss(); onAccept && onAccept(callId); };
|
||||
el.querySelector('.ringing-reject').onclick = () => { this.dismiss(); onReject && onReject(callId); };
|
||||
this.playRingtone();
|
||||
if (Notification.permission === 'default') Notification.requestPermission();
|
||||
if (Notification.permission === 'granted') {
|
||||
new Notification(`Incoming ${callType} call`, { body: callerName || 'BackOne peer' });
|
||||
}
|
||||
},
|
||||
dismiss() {
|
||||
const el = document.getElementById('ringing-popup');
|
||||
if (el) { el.classList.add('ringing-hidden'); el.innerHTML = ''; }
|
||||
this.stopRingtone();
|
||||
},
|
||||
playRingtone() {
|
||||
this.stopRingtone();
|
||||
try {
|
||||
this._audio = new Audio('data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBSuBzvLZiTYIG2m98OScTgwOUarm7blmHgU7k9n1unEiBC13y+/ek0EKElyx5+ytWBUIQ5zd8sFuJAUwgM/z2Ik6CBxpvfDknE4MDlGq5u25Zh4FO5PZ9br');
|
||||
this._audio.loop = true;
|
||||
this._audio.play().catch(() => {});
|
||||
} catch (_) {}
|
||||
},
|
||||
stopRingtone() {
|
||||
if (this._audio) { this._audio.pause(); this._audio = null; }
|
||||
}
|
||||
};
|
||||
|
||||
async function loadAddonManifest() {
|
||||
const res = await fetch('/app/addons/manifest.json', { cache: 'no-store' });
|
||||
if (!res.ok) {
|
||||
console.warn('manifest.json fetch failed:', res.status);
|
||||
return [];
|
||||
}
|
||||
const data = await res.json();
|
||||
return Array.isArray(data) ? data : [];
|
||||
}
|
||||
|
||||
async function loadAddonModule(entry) {
|
||||
const entryPath = entry.frontend && entry.frontend.entry ? entry.frontend.entry : `/app/addons/${entry.id}/index.js`;
|
||||
await import(entryPath);
|
||||
const factory = window.BackOneAddons && window.BackOneAddons[entry.id];
|
||||
return factory ? factory() : null;
|
||||
}
|
||||
|
||||
window.BackOneAddonLoader = { loadAddonManifest, loadAddonModule };
|
||||
Vendored
+195
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* BackOne API auth — same token as backone-cli (~/.backOneOneAuthToken / X-ZT1-Auth).
|
||||
*/
|
||||
window.BackOneAuth = (function () {
|
||||
const LS_KEY = 'backone.authToken';
|
||||
let cache = null;
|
||||
|
||||
function trimToken(s) {
|
||||
return String(s || '').replace(/\s+/g, '');
|
||||
}
|
||||
|
||||
function tokenFromUrl() {
|
||||
try {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const fromQuery = trimToken(params.get('auth') || params.get('token'));
|
||||
if (fromQuery) return fromQuery;
|
||||
const hash = (location.hash || '').replace(/^#\/?/, '');
|
||||
const hashParams = new URLSearchParams(hash.includes('?') ? hash.split('?')[1] : '');
|
||||
return trimToken(hashParams.get('auth') || hashParams.get('token'));
|
||||
} catch (_) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
async function probeToken(token) {
|
||||
if (!token) return false;
|
||||
try {
|
||||
const res = await fetch('/network', {
|
||||
cache: 'no-store',
|
||||
headers: { 'X-ZT1-Auth': token }
|
||||
});
|
||||
return res.ok;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchAuthtoken() {
|
||||
for (const path of ['/api/backone/authtoken', '/app/api/authtoken']) {
|
||||
try {
|
||||
const res = await fetch(path, { cache: 'no-store' });
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
const token = trimToken(data && data.token);
|
||||
if (token) return token;
|
||||
}
|
||||
} catch (_) {
|
||||
/* try next */
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
async function resolveToken() {
|
||||
const urlToken = tokenFromUrl();
|
||||
if (urlToken) {
|
||||
if (await probeToken(urlToken)) {
|
||||
return urlToken;
|
||||
}
|
||||
}
|
||||
|
||||
const stored = trimToken(localStorage.getItem(LS_KEY));
|
||||
if (stored && await probeToken(stored)) {
|
||||
return stored;
|
||||
}
|
||||
|
||||
const apiToken = await fetchAuthtoken();
|
||||
if (apiToken && await probeToken(apiToken)) {
|
||||
return apiToken;
|
||||
}
|
||||
|
||||
if (stored) return stored;
|
||||
if (apiToken) return apiToken;
|
||||
if (urlToken) return urlToken;
|
||||
return '';
|
||||
}
|
||||
|
||||
async function getToken() {
|
||||
if (cache) return cache;
|
||||
cache = await resolveToken();
|
||||
return cache || '';
|
||||
}
|
||||
|
||||
async function authHeaders(extra) {
|
||||
const token = await getToken();
|
||||
const headers = Object.assign({}, extra || {});
|
||||
if (token) {
|
||||
headers['X-ZT1-Auth'] = token;
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
async function fetchWithAuth(path, options) {
|
||||
options = options || {};
|
||||
const headers = await authHeaders(options.headers || {});
|
||||
return fetch(path, Object.assign({}, options, { headers }));
|
||||
}
|
||||
|
||||
async function apiGet(path) {
|
||||
const res = await fetchWithAuth(path, { cache: 'no-store' });
|
||||
if (!res.ok) throw new Error(`${path} returned ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async function apiGetFirst(paths) {
|
||||
let lastErr = null;
|
||||
for (const path of paths) {
|
||||
try {
|
||||
return await apiGet(path);
|
||||
} catch (err) {
|
||||
lastErr = err;
|
||||
}
|
||||
}
|
||||
throw lastErr || new Error('API request failed');
|
||||
}
|
||||
|
||||
async function apiSend(path, method, body) {
|
||||
const headers = await authHeaders(
|
||||
body != null ? { 'Content-Type': 'application/json' } : {}
|
||||
);
|
||||
const res = await fetch(path, {
|
||||
method,
|
||||
headers,
|
||||
body: body != null ? JSON.stringify(body) : undefined
|
||||
});
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => '');
|
||||
throw new Error(text || `${method} ${path} returned ${res.status}`);
|
||||
}
|
||||
if (res.status === 204) {
|
||||
return { result: true };
|
||||
}
|
||||
const text = await res.text();
|
||||
if (!text) {
|
||||
return { result: true };
|
||||
}
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (_) {
|
||||
return { result: true };
|
||||
}
|
||||
}
|
||||
|
||||
async function apiSendFirst(paths, method, body) {
|
||||
let lastErr = null;
|
||||
for (const path of paths) {
|
||||
try {
|
||||
return await apiSend(path, method, body);
|
||||
} catch (err) {
|
||||
lastErr = err;
|
||||
}
|
||||
}
|
||||
throw lastErr || new Error(`${method} failed`);
|
||||
}
|
||||
|
||||
/** Store token from ~/.backOneOneAuthToken (same as backone-cli). */
|
||||
async function setUserToken(token) {
|
||||
cache = trimToken(token);
|
||||
if (cache) {
|
||||
localStorage.setItem(LS_KEY, cache);
|
||||
} else {
|
||||
localStorage.removeItem(LS_KEY);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
async function validateCurrentToken() {
|
||||
cache = null;
|
||||
const token = await getToken();
|
||||
if (!token) return false;
|
||||
return probeToken(token);
|
||||
}
|
||||
|
||||
function invalidate() {
|
||||
cache = null;
|
||||
}
|
||||
|
||||
async function init() {
|
||||
return getToken();
|
||||
}
|
||||
|
||||
return {
|
||||
getToken,
|
||||
authHeaders,
|
||||
fetchWithAuth,
|
||||
apiGet,
|
||||
apiGetFirst,
|
||||
apiSend,
|
||||
apiSendFirst,
|
||||
setUserToken,
|
||||
validateCurrentToken,
|
||||
invalidate,
|
||||
init
|
||||
};
|
||||
})();
|
||||
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
window.BackOneContacts = (function () {
|
||||
let cache = null;
|
||||
|
||||
async function load() {
|
||||
if (cache) return cache;
|
||||
try {
|
||||
const res = await BackOneAuth.fetchWithAuth('/api/backone/contacts', { cache: 'no-store' })
|
||||
.catch(() => BackOneAuth.fetchWithAuth('/app/api/contacts', { cache: 'no-store' }));
|
||||
if (!res.ok) {
|
||||
cache = {};
|
||||
return cache;
|
||||
}
|
||||
cache = await res.json();
|
||||
if (!cache || typeof cache !== 'object') cache = {};
|
||||
} catch (_) {
|
||||
cache = {};
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
function getDisplayName(peerId, controllerName) {
|
||||
const c = cache || {};
|
||||
const entry = c[peerId];
|
||||
if (entry && entry.nickname) return entry.nickname;
|
||||
if (controllerName) return controllerName;
|
||||
return peerId;
|
||||
}
|
||||
|
||||
function getNickname(peerId) {
|
||||
const entry = (cache || {})[peerId];
|
||||
return entry && entry.nickname ? entry.nickname : '';
|
||||
}
|
||||
|
||||
async function setNickname(peerId, nickname) {
|
||||
await load();
|
||||
const patch = {};
|
||||
patch[peerId] = { nickname: nickname || '' };
|
||||
const res = await BackOneAuth.fetchWithAuth('/api/backone/contacts', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(patch)
|
||||
}).catch(() => BackOneAuth.fetchWithAuth('/app/api/contacts', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(patch)
|
||||
}));
|
||||
if (!res.ok) throw new Error('Failed to save nickname');
|
||||
cache = await res.json();
|
||||
return cache;
|
||||
}
|
||||
|
||||
function invalidate() {
|
||||
cache = null;
|
||||
}
|
||||
|
||||
return { load, getDisplayName, getNickname, setNickname, invalidate };
|
||||
})();
|
||||
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BackOne</title>
|
||||
<link rel="stylesheet" href="/app/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="app-header">
|
||||
<div class="app-brand">
|
||||
<h1>BackOne</h1>
|
||||
<span id="core-status-badge" class="core-badge offline">Offline</span>
|
||||
</div>
|
||||
<div id="network-bar" class="network-bar">
|
||||
<label class="network-bar-label">Network
|
||||
<select id="global-network"></select>
|
||||
</label>
|
||||
<input id="global-join-id" type="text" maxlength="16" placeholder="Join network ID" autocomplete="off" />
|
||||
<button type="button" id="global-join-btn">Join</button>
|
||||
<button type="button" id="global-leave-btn">Leave</button>
|
||||
<span id="global-network-status" class="network-bar-status" aria-live="polite"></span>
|
||||
<div id="auth-token-bar" class="auth-token-bar auth-token-hidden">
|
||||
<label class="auth-token-label">Auth token
|
||||
<input id="auth-token-input" type="password" autocomplete="off"
|
||||
placeholder="Paste from ~/.backOneOneAuthToken" />
|
||||
</label>
|
||||
<button type="button" id="auth-token-save">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
<nav id="addon-nav" class="addon-nav"></nav>
|
||||
</header>
|
||||
<main id="addon-panel" class="addon-panel"></main>
|
||||
<div id="ringing-popup" class="ringing-hidden"></div>
|
||||
<script src="/app/auth-store.js"></script>
|
||||
<script src="/app/contacts-store.js"></script>
|
||||
<script src="/app/peer-directory.js"></script>
|
||||
<script src="/app/network-session.js"></script>
|
||||
<script src="/app/peer-picker.js"></script>
|
||||
<script src="/app/addon-loader.js"></script>
|
||||
<script src="/app/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+237
@@ -0,0 +1,237 @@
|
||||
window.BackOneAddons = window.BackOneAddons || {};
|
||||
|
||||
const addonButtons = {};
|
||||
let addonModules = {};
|
||||
let coreRefreshTimer = null;
|
||||
|
||||
function hashAddonId() {
|
||||
const h = (location.hash || '').replace(/^#\/?/, '').toLowerCase();
|
||||
if (h === 'voice' || h === 'video' || h === 'chat') return h;
|
||||
return '';
|
||||
}
|
||||
|
||||
function activateAddon(id) {
|
||||
const btn = addonButtons[id];
|
||||
if (!btn) return;
|
||||
btn.click();
|
||||
}
|
||||
|
||||
function escapeHtml(s) {
|
||||
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
function networkLabel(n) {
|
||||
if (!n) return '';
|
||||
return n.name ? `${n.name} (${n.id})` : n.id;
|
||||
}
|
||||
|
||||
function updateCoreBadge(statusEl) {
|
||||
const badge = document.getElementById('core-status-badge');
|
||||
const core = BackOneNetworkSession.getCoreStatus();
|
||||
if (badge) {
|
||||
badge.textContent = core.online ? 'Online' : 'Offline';
|
||||
badge.classList.toggle('online', core.online);
|
||||
badge.classList.toggle('offline', !core.online);
|
||||
}
|
||||
if (!statusEl) return;
|
||||
if (!core.online) {
|
||||
statusEl.textContent = 'Start the backone service to use networks and peers';
|
||||
return;
|
||||
}
|
||||
const net = BackOneNetworkSession.getActiveNetwork();
|
||||
const netPart = net ? networkLabel(net) : 'no network joined';
|
||||
statusEl.textContent = `${netPart} · ${core.leafPeerCount} peer(s)`;
|
||||
}
|
||||
|
||||
async function initAuthTokenBar(statusEl, onSaved) {
|
||||
const bar = document.getElementById('auth-token-bar');
|
||||
const input = document.getElementById('auth-token-input');
|
||||
const saveBtn = document.getElementById('auth-token-save');
|
||||
if (!bar || !input || !saveBtn) return;
|
||||
|
||||
async function refreshAuthUi() {
|
||||
const ok = await BackOneAuth.validateCurrentToken();
|
||||
bar.classList.toggle('auth-token-hidden', ok);
|
||||
if (!ok && !input.value) {
|
||||
input.placeholder = 'Paste from ~/.backOneOneAuthToken';
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
saveBtn.addEventListener('click', async () => {
|
||||
const token = input.value.trim();
|
||||
if (!token) {
|
||||
if (statusEl) statusEl.textContent = 'Paste the token from ~/.backOneOneAuthToken';
|
||||
return;
|
||||
}
|
||||
saveBtn.disabled = true;
|
||||
await BackOneAuth.setUserToken(token);
|
||||
BackOneAuth.invalidate();
|
||||
const ok = await BackOneAuth.validateCurrentToken();
|
||||
if (!ok) {
|
||||
if (statusEl) statusEl.textContent = 'Token rejected — check ~/.backOneOneAuthToken';
|
||||
saveBtn.disabled = false;
|
||||
return;
|
||||
}
|
||||
input.value = '';
|
||||
bar.classList.add('auth-token-hidden');
|
||||
if (typeof onSaved === 'function') await onSaved();
|
||||
saveBtn.disabled = false;
|
||||
});
|
||||
|
||||
await refreshAuthUi();
|
||||
return refreshAuthUi;
|
||||
}
|
||||
|
||||
async function initNetworkBar() {
|
||||
const bar = document.getElementById('network-bar');
|
||||
if (!bar) return;
|
||||
|
||||
const netSel = bar.querySelector('#global-network');
|
||||
const joinInput = bar.querySelector('#global-join-id');
|
||||
const joinBtn = bar.querySelector('#global-join-btn');
|
||||
const leaveBtn = bar.querySelector('#global-leave-btn');
|
||||
const statusEl = bar.querySelector('#global-network-status');
|
||||
|
||||
async function reloadNetworkList(selectId) {
|
||||
await BackOneNetworkSession.syncFromCore();
|
||||
const networks = await BackOneNetworkSession.loadNetworks();
|
||||
netSel.innerHTML = networks.map(n =>
|
||||
`<option value="${n.id}">${escapeHtml(networkLabel(n))} · ${escapeHtml(n.status || 'OK')}</option>`
|
||||
).join('') || '<option value="">— no networks —</option>';
|
||||
|
||||
const want = (selectId || BackOneNetworkSession.getNetworkId() || '').toLowerCase();
|
||||
if (want && networks.some(n => (n.id || '').toLowerCase() === want)) {
|
||||
netSel.value = networks.find(n => (n.id || '').toLowerCase() === want).id;
|
||||
await BackOneNetworkSession.selectNetwork(netSel.value);
|
||||
} else if (networks.length === 1) {
|
||||
netSel.value = networks[0].id;
|
||||
await BackOneNetworkSession.selectNetwork(networks[0].id);
|
||||
} else if (networks.length > 0 && BackOneNetworkSession.getNetworkId()) {
|
||||
netSel.value = BackOneNetworkSession.getNetworkId();
|
||||
}
|
||||
updateCoreBadge(statusEl);
|
||||
}
|
||||
|
||||
netSel.addEventListener('change', async () => {
|
||||
if (!netSel.value) return;
|
||||
statusEl.textContent = 'Switching network…';
|
||||
await BackOneNetworkSession.selectNetwork(netSel.value);
|
||||
await BackOneNetworkSession.syncFromCore();
|
||||
updateCoreBadge(statusEl);
|
||||
});
|
||||
|
||||
joinBtn.addEventListener('click', async () => {
|
||||
const id = BackOnePeerDirectory.normalizeNetworkId(joinInput.value);
|
||||
if (!id) {
|
||||
statusEl.textContent = 'Enter a 16-digit hexadecimal network ID';
|
||||
return;
|
||||
}
|
||||
joinBtn.disabled = true;
|
||||
leaveBtn.disabled = true;
|
||||
statusEl.textContent = `Joining ${id}…`;
|
||||
try {
|
||||
await BackOneNetworkSession.joinNetwork(id);
|
||||
joinInput.value = '';
|
||||
await reloadNetworkList(id);
|
||||
} catch (err) {
|
||||
statusEl.textContent = err.message || 'Join failed';
|
||||
} finally {
|
||||
joinBtn.disabled = false;
|
||||
leaveBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
leaveBtn.addEventListener('click', async () => {
|
||||
const id = netSel.value || BackOneNetworkSession.getNetworkId();
|
||||
if (!id) {
|
||||
statusEl.textContent = 'Select a network to leave';
|
||||
return;
|
||||
}
|
||||
if (!confirm(`Leave network ${networkLabel(BackOneNetworkSession.getActiveNetwork() || { id })}?`)) return;
|
||||
joinBtn.disabled = true;
|
||||
leaveBtn.disabled = true;
|
||||
statusEl.textContent = `Leaving ${id}…`;
|
||||
try {
|
||||
await BackOneNetworkSession.leaveNetwork(id);
|
||||
await reloadNetworkList();
|
||||
} catch (err) {
|
||||
statusEl.textContent = err.message || 'Leave failed';
|
||||
} finally {
|
||||
joinBtn.disabled = false;
|
||||
leaveBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
BackOneNetworkSession.subscribe(() => updateCoreBadge(statusEl));
|
||||
|
||||
const refreshAuthUi = await initAuthTokenBar(statusEl, () => reloadNetworkList());
|
||||
|
||||
await reloadNetworkList();
|
||||
if (refreshAuthUi) await refreshAuthUi();
|
||||
|
||||
coreRefreshTimer = setInterval(() => {
|
||||
BackOneNetworkSession.syncFromCore().then(() => updateCoreBadge(statusEl));
|
||||
}, 45000);
|
||||
}
|
||||
|
||||
async function initAppShell() {
|
||||
await BackOneAuth.init();
|
||||
await initNetworkBar();
|
||||
|
||||
const nav = document.getElementById('addon-nav');
|
||||
const panel = document.getElementById('addon-panel');
|
||||
const addons = await BackOneAddonLoader.loadAddonManifest();
|
||||
if (!addons.length) {
|
||||
panel.innerHTML = '<p>No UI addons found. Install backone-addon-voice, backone-addon-video, or backone-addon-chat, then run: <code>sudo backone-updater rebuild-manifest</code> and restart backone.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
const navOrder = ['chat', 'video', 'voice'];
|
||||
const sorted = [...addons].sort((a, b) => {
|
||||
const ia = navOrder.indexOf(a.id);
|
||||
const ib = navOrder.indexOf(b.id);
|
||||
return (ia < 0 ? 99 : ia) - (ib < 0 ? 99 : ib);
|
||||
});
|
||||
|
||||
for (const entry of sorted) {
|
||||
const mod = await BackOneAddonLoader.loadAddonModule(entry);
|
||||
if (mod) addonModules[entry.id] = mod;
|
||||
const btn = document.createElement('button');
|
||||
btn.dataset.addonId = entry.id;
|
||||
btn.textContent = (entry.frontend && entry.frontend.navLabel) || entry.name || entry.id;
|
||||
btn.onclick = () => {
|
||||
nav.querySelectorAll('button').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
panel.innerHTML = '';
|
||||
location.hash = entry.id;
|
||||
if (addonModules[entry.id] && addonModules[entry.id].mount) {
|
||||
addonModules[entry.id].mount(panel);
|
||||
} else {
|
||||
panel.innerHTML = `<p>Addon "${entry.id}" loaded.</p>`;
|
||||
}
|
||||
};
|
||||
addonButtons[entry.id] = btn;
|
||||
nav.appendChild(btn);
|
||||
}
|
||||
|
||||
const fromHash = hashAddonId();
|
||||
if (fromHash && addonButtons[fromHash]) {
|
||||
activateAddon(fromHash);
|
||||
} else {
|
||||
nav.querySelector('button')?.click();
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('hashchange', () => {
|
||||
const id = hashAddonId();
|
||||
if (id) activateAddon(id);
|
||||
});
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
BackOneNetworkSession.syncFromCore();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initAppShell);
|
||||
Vendored
+178
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Single BackOne core network for all addons (same as backone-cli listnetworks/join).
|
||||
*/
|
||||
window.BackOneNetworkSession = (function () {
|
||||
const NETWORK_KEY = 'backone.lastNetworkId';
|
||||
const PEER_KEY = 'backone.lastPeerId';
|
||||
|
||||
let activeNetwork = null;
|
||||
let selectedPeer = null;
|
||||
let coreStatus = { online: false, address: '', networkCount: 0, leafPeerCount: 0, authenticated: false };
|
||||
const listeners = new Set();
|
||||
|
||||
function emit() {
|
||||
const snap = {
|
||||
network: activeNetwork,
|
||||
networkId: getNetworkId(),
|
||||
peer: selectedPeer,
|
||||
core: { ...coreStatus }
|
||||
};
|
||||
listeners.forEach(fn => {
|
||||
try { fn(snap); } catch (_) { /* ignore */ }
|
||||
});
|
||||
}
|
||||
|
||||
function getNetworkId() {
|
||||
return (localStorage.getItem(NETWORK_KEY) || '').toLowerCase();
|
||||
}
|
||||
|
||||
function setNetworkId(id) {
|
||||
const nwid = BackOnePeerDirectory.normalizeNetworkId(id);
|
||||
if (nwid) {
|
||||
localStorage.setItem(NETWORK_KEY, nwid);
|
||||
} else {
|
||||
localStorage.removeItem(NETWORK_KEY);
|
||||
}
|
||||
BackOnePeerDirectory.setLastNetworkId(nwid);
|
||||
return nwid;
|
||||
}
|
||||
|
||||
function getSelectedPeerId() {
|
||||
return (localStorage.getItem(PEER_KEY) || '').toLowerCase();
|
||||
}
|
||||
|
||||
function setSelectedPeer(peer) {
|
||||
selectedPeer = peer || null;
|
||||
if (peer && peer.peerId) {
|
||||
localStorage.setItem(PEER_KEY, peer.peerId);
|
||||
} else {
|
||||
localStorage.removeItem(PEER_KEY);
|
||||
}
|
||||
emit();
|
||||
}
|
||||
|
||||
function pickDefaultNetworkId(networks) {
|
||||
if (!networks.length) return '';
|
||||
const stored = getNetworkId();
|
||||
if (stored && networks.some(n => (n.id || '').toLowerCase() === stored)) {
|
||||
return stored;
|
||||
}
|
||||
const preferred = networks.find(n => n.status === 'OK')
|
||||
|| networks.find(n => n.status === 'REQUESTING_CONFIGURATION')
|
||||
|| networks[0];
|
||||
return preferred ? (preferred.id || '').toLowerCase() : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* On app start: read BackOne core state (same as backone-cli info / listnetworks / peers).
|
||||
*/
|
||||
async function syncFromCore() {
|
||||
const token = await BackOneAuth.getToken();
|
||||
const [status, networks, cliPeers] = await Promise.all([
|
||||
BackOnePeerDirectory.loadCoreStatus(),
|
||||
BackOnePeerDirectory.loadNetworks(),
|
||||
BackOnePeerDirectory.loadCliPeers()
|
||||
]);
|
||||
|
||||
coreStatus = {
|
||||
online: !!(status && status.online),
|
||||
address: (status && status.address) || '',
|
||||
version: (status && status.version) || '',
|
||||
networkCount: networks.length,
|
||||
leafPeerCount: BackOnePeerDirectory.countLeafPeers(cliPeers),
|
||||
authenticated: !!token
|
||||
};
|
||||
|
||||
const pick = pickDefaultNetworkId(networks);
|
||||
if (pick) {
|
||||
setNetworkId(pick);
|
||||
activeNetwork = networks.find(n => (n.id || '').toLowerCase() === pick)
|
||||
|| await BackOnePeerDirectory.loadNetwork(pick);
|
||||
} else {
|
||||
setNetworkId('');
|
||||
activeNetwork = null;
|
||||
}
|
||||
|
||||
emit();
|
||||
return { coreStatus, activeNetwork, networks };
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
const id = getNetworkId();
|
||||
if (!id) {
|
||||
activeNetwork = null;
|
||||
emit();
|
||||
return null;
|
||||
}
|
||||
activeNetwork = await BackOnePeerDirectory.loadNetwork(id);
|
||||
if (!activeNetwork) {
|
||||
activeNetwork = { id, name: id, status: 'UNKNOWN' };
|
||||
}
|
||||
emit();
|
||||
return activeNetwork;
|
||||
}
|
||||
|
||||
async function loadNetworks() {
|
||||
return BackOnePeerDirectory.loadNetworks();
|
||||
}
|
||||
|
||||
async function selectNetwork(nwid) {
|
||||
const id = setNetworkId(nwid);
|
||||
if (!id) {
|
||||
await refresh();
|
||||
return null;
|
||||
}
|
||||
return refresh();
|
||||
}
|
||||
|
||||
async function joinNetwork(nwid) {
|
||||
const id = BackOnePeerDirectory.normalizeNetworkId(nwid);
|
||||
if (!id) throw new Error('Network ID must be 16 hexadecimal characters');
|
||||
await BackOnePeerDirectory.joinNetwork(id);
|
||||
return syncFromCore();
|
||||
}
|
||||
|
||||
async function leaveNetwork(nwid) {
|
||||
const id = BackOnePeerDirectory.normalizeNetworkId(nwid || getNetworkId());
|
||||
if (!id) throw new Error('No network selected');
|
||||
await BackOnePeerDirectory.leaveNetwork(id);
|
||||
if (getNetworkId() === id) {
|
||||
setNetworkId('');
|
||||
selectedPeer = null;
|
||||
localStorage.removeItem(PEER_KEY);
|
||||
}
|
||||
return syncFromCore();
|
||||
}
|
||||
|
||||
function subscribe(fn) {
|
||||
listeners.add(fn);
|
||||
return () => listeners.delete(fn);
|
||||
}
|
||||
|
||||
function requireNetworkId() {
|
||||
const id = getNetworkId();
|
||||
if (!id) throw new Error('Join a BackOne network first (same as backone-cli join)');
|
||||
return id;
|
||||
}
|
||||
|
||||
function getCoreStatus() {
|
||||
return { ...coreStatus };
|
||||
}
|
||||
|
||||
return {
|
||||
getNetworkId,
|
||||
getActiveNetwork: () => activeNetwork,
|
||||
getCoreStatus,
|
||||
getSelectedPeer: () => selectedPeer,
|
||||
getSelectedPeerId,
|
||||
setSelectedPeer,
|
||||
selectNetwork,
|
||||
joinNetwork,
|
||||
leaveNetwork,
|
||||
loadNetworks,
|
||||
syncFromCore,
|
||||
refresh,
|
||||
subscribe,
|
||||
requireNetworkId
|
||||
};
|
||||
})();
|
||||
Vendored
+266
@@ -0,0 +1,266 @@
|
||||
window.BackOnePeerDirectory = (function () {
|
||||
const NETWORK_KEY = 'backone.lastNetworkId';
|
||||
const API = {
|
||||
peers: ['/api/backone/peers', '/app/api/peers', '/peer'],
|
||||
networks: ['/api/backone/networks', '/app/api/networks', '/network'],
|
||||
status: ['/api/backone/status', '/app/api/status', '/status'],
|
||||
network: (id) => [
|
||||
`/api/backone/network/${id}`,
|
||||
`/app/api/network/${id}`,
|
||||
`/network/${id}`
|
||||
],
|
||||
members: (id) => [
|
||||
`/api/backone/network/${id}/members`,
|
||||
`/app/api/network/${id}/members`,
|
||||
`/unstable/controller/network/${id}/member`
|
||||
],
|
||||
join: (id) => [
|
||||
`/api/backone/network/${id}`,
|
||||
`/app/api/network/${id}`,
|
||||
`/network/${id}`
|
||||
],
|
||||
leave: (id) => [
|
||||
`/api/backone/network/${id}`,
|
||||
`/app/api/network/${id}`,
|
||||
`/network/${id}`
|
||||
]
|
||||
};
|
||||
|
||||
function apiGet(path) {
|
||||
return BackOneAuth.apiGet(path);
|
||||
}
|
||||
|
||||
/** Same data as `backone-cli -j peers` (GET /peer). */
|
||||
async function loadCliPeers() {
|
||||
try {
|
||||
const peers = await BackOneAuth.apiGetFirst(API.peers);
|
||||
return Array.isArray(peers) ? peers : [];
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/** Same data as `backone-cli -j listnetworks` (GET /network). */
|
||||
async function loadNetworks() {
|
||||
try {
|
||||
const nets = await BackOneAuth.apiGetFirst(API.networks);
|
||||
return Array.isArray(nets) ? nets : [];
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/** Same data as `backone-cli -j info` (GET /status). */
|
||||
async function loadCoreStatus() {
|
||||
try {
|
||||
return await BackOneAuth.apiGetFirst(API.status);
|
||||
} catch (_) {
|
||||
try {
|
||||
const res = await fetch('/health', { cache: 'no-store' });
|
||||
return { online: res.ok };
|
||||
} catch (_2) {
|
||||
return { online: false };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function countLeafPeers(cliPeers) {
|
||||
if (!Array.isArray(cliPeers)) return 0;
|
||||
return cliPeers.filter(p => !p.role || p.role === 'LEAF').length;
|
||||
}
|
||||
|
||||
async function loadNetwork(nwid) {
|
||||
if (!nwid) return null;
|
||||
const networks = await loadNetworks();
|
||||
const found = networks.find(n => n.id === nwid);
|
||||
if (found) return found;
|
||||
try {
|
||||
return await BackOneAuth.apiGetFirst(API.network(nwid));
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getLastNetworkId() {
|
||||
return localStorage.getItem(NETWORK_KEY) || '';
|
||||
}
|
||||
|
||||
function setLastNetworkId(nwid) {
|
||||
if (nwid) localStorage.setItem(NETWORK_KEY, nwid);
|
||||
}
|
||||
|
||||
function peerIsOnline(p) {
|
||||
if (!p) return false;
|
||||
const paths = p.paths || [];
|
||||
return paths.some(x => x.active && !x.expired);
|
||||
}
|
||||
|
||||
function peerLinkLabel(p) {
|
||||
if (!p) return '';
|
||||
if (p.tunneled) return 'RELAY';
|
||||
const paths = p.paths || [];
|
||||
for (const path of paths) {
|
||||
if (path.preferred && path.active && !path.expired) {
|
||||
return 'DIRECT';
|
||||
}
|
||||
}
|
||||
for (const path of paths) {
|
||||
if (path.active && !path.expired) {
|
||||
return 'DIRECT';
|
||||
}
|
||||
}
|
||||
return 'RELAY';
|
||||
}
|
||||
|
||||
function peerPreferredPath(p) {
|
||||
if (!p) return '';
|
||||
const paths = p.paths || [];
|
||||
for (const path of paths) {
|
||||
if (path.preferred && path.address) return path.address;
|
||||
}
|
||||
for (const path of paths) {
|
||||
if (path.active && path.address) return path.address;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function cliPeerMap(cliPeers) {
|
||||
const map = {};
|
||||
for (const p of cliPeers) {
|
||||
const id = (p.address || '').toLowerCase();
|
||||
if (id) map[id] = p;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
async function loadMemberMap(nwid) {
|
||||
if (!nwid) return {};
|
||||
try {
|
||||
const data = await BackOneAuth.apiGetFirst(API.members(nwid));
|
||||
const map = {};
|
||||
if (data && Array.isArray(data.data)) {
|
||||
for (const m of data.data) {
|
||||
const id = (m.id || '').toLowerCase();
|
||||
if (id) map[id] = m;
|
||||
}
|
||||
}
|
||||
return map;
|
||||
} catch (_) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function buildPeerEntry(peerId, member, cliPeer, networkId, networkName) {
|
||||
const controllerName = member ? (member.name || '') : '';
|
||||
const ips = member && member.ipAssignments ? member.ipAssignments : [];
|
||||
return {
|
||||
peerId,
|
||||
networkId: networkId || '',
|
||||
networkName: networkName || '',
|
||||
displayName: BackOneContacts.getDisplayName(peerId, controllerName),
|
||||
controllerName,
|
||||
nickname: BackOneContacts.getNickname(peerId),
|
||||
online: peerIsOnline(cliPeer),
|
||||
latency: cliPeer && typeof cliPeer.latency === 'number' ? cliPeer.latency : null,
|
||||
role: (cliPeer && cliPeer.role) || (member ? 'LEAF' : ''),
|
||||
version: (cliPeer && cliPeer.version) || '',
|
||||
link: cliPeer ? peerLinkLabel(cliPeer) : '',
|
||||
path: cliPeer ? peerPreferredPath(cliPeer) : '',
|
||||
ip: ips.length ? ips[0] : '',
|
||||
onNetwork: !!member
|
||||
};
|
||||
}
|
||||
|
||||
async function loadPeersForNetwork(nwid) {
|
||||
if (!nwid) return [];
|
||||
|
||||
const [cliPeers, members, selfStatus, network] = await Promise.all([
|
||||
loadCliPeers(),
|
||||
loadMemberMap(nwid),
|
||||
loadCoreStatus().catch(() => ({})),
|
||||
loadNetwork(nwid)
|
||||
]);
|
||||
await BackOneContacts.load();
|
||||
|
||||
const selfId = ((selfStatus && selfStatus.address) || '').toLowerCase();
|
||||
const networkId = nwid;
|
||||
const networkName = network ? (network.name || network.id || nwid) : nwid;
|
||||
const cliById = cliPeerMap(cliPeers);
|
||||
const out = [];
|
||||
|
||||
if (Object.keys(members).length > 0) {
|
||||
for (const peerId of Object.keys(members)) {
|
||||
const member = members[peerId];
|
||||
if (!member || member.authorized === false) continue;
|
||||
if (peerId === selfId) continue;
|
||||
out.push(buildPeerEntry(peerId, member, cliById[peerId], networkId, networkName));
|
||||
}
|
||||
} else {
|
||||
for (const peerId of Object.keys(cliById)) {
|
||||
const p = cliById[peerId];
|
||||
if (!peerId || peerId.length !== 10 || peerId === selfId) continue;
|
||||
if (p.role && p.role !== 'LEAF') continue;
|
||||
out.push(buildPeerEntry(peerId, null, p, networkId, networkName));
|
||||
}
|
||||
}
|
||||
|
||||
out.sort((a, b) => {
|
||||
if (a.online !== b.online) return a.online ? -1 : 1;
|
||||
return a.displayName.localeCompare(b.displayName);
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
function searchPeers(query, list) {
|
||||
const q = (query || '').trim().toLowerCase();
|
||||
if (!q) return list;
|
||||
return list.filter(p =>
|
||||
p.displayName.toLowerCase().includes(q) ||
|
||||
p.peerId.includes(q) ||
|
||||
(p.networkName || '').toLowerCase().includes(q) ||
|
||||
(p.networkId || '').includes(q) ||
|
||||
(p.controllerName || '').toLowerCase().includes(q) ||
|
||||
(p.role || '').toLowerCase().includes(q) ||
|
||||
(p.path || '').toLowerCase().includes(q) ||
|
||||
(p.ip || '').toLowerCase().includes(q)
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeNetworkId(input) {
|
||||
const id = String(input || '').replace(/[^0-9a-fA-F]/g, '').toLowerCase();
|
||||
return id.length === 16 ? id : '';
|
||||
}
|
||||
|
||||
async function joinNetwork(nwid) {
|
||||
const id = normalizeNetworkId(nwid);
|
||||
if (!id) {
|
||||
throw new Error('Network ID must be 16 hexadecimal characters');
|
||||
}
|
||||
return BackOneAuth.apiSendFirst(API.join(id), 'POST', {});
|
||||
}
|
||||
|
||||
async function leaveNetwork(nwid) {
|
||||
const id = normalizeNetworkId(nwid);
|
||||
if (!id) {
|
||||
throw new Error('Network ID must be 16 hexadecimal characters');
|
||||
}
|
||||
return BackOneAuth.apiSendFirst(API.leave(id), 'DELETE');
|
||||
}
|
||||
|
||||
return {
|
||||
loadCliPeers,
|
||||
loadNetworks,
|
||||
loadNetwork,
|
||||
loadCoreStatus,
|
||||
countLeafPeers,
|
||||
loadPeersForNetwork,
|
||||
getLastNetworkId,
|
||||
setLastNetworkId,
|
||||
joinNetwork,
|
||||
leaveNetwork,
|
||||
normalizeNetworkId,
|
||||
searchPeers,
|
||||
peerIsOnline,
|
||||
peerLinkLabel
|
||||
};
|
||||
})();
|
||||
Vendored
+193
@@ -0,0 +1,193 @@
|
||||
window.BackOnePeerPicker = function PeerPicker(container, options) {
|
||||
options = options || {};
|
||||
const onSelect = options.onSelect || (() => {});
|
||||
const onNetworkChange = options.onNetworkChange || (() => {});
|
||||
const onAction = options.onAction || null;
|
||||
const actionLabel = options.actionLabel || 'Select';
|
||||
const showActionButton = options.showActionButton !== false;
|
||||
const useSharedNetwork = options.useSharedNetwork !== false;
|
||||
const layout = options.layout || 'default';
|
||||
|
||||
let selectedPeer = null;
|
||||
let peers = [];
|
||||
let currentNetwork = null;
|
||||
let unsub = null;
|
||||
|
||||
if (layout === 'sidebar') {
|
||||
container.innerHTML = `
|
||||
<div class="peer-picker">
|
||||
<div id="pp-network-banner" class="peer-info-box"></div>
|
||||
<label class="peer-picker-row">
|
||||
<input id="pp-search" type="search" placeholder="Name or peer ID" />
|
||||
</label>
|
||||
<ul id="pp-list" class="peer-list"></ul>
|
||||
</div>`;
|
||||
} else {
|
||||
container.innerHTML = `
|
||||
<div class="peer-picker">
|
||||
<div class="peer-picker-core">BackOne core · same network as <code>backone-cli</code></div>
|
||||
<div id="pp-network-banner" class="peer-network-banner"></div>
|
||||
<label class="peer-picker-row">Search
|
||||
<input id="pp-search" type="search" placeholder="Name or peer ID" />
|
||||
</label>
|
||||
<ul id="pp-list" class="peer-list"></ul>
|
||||
<div id="pp-selected" class="peer-selected"></div>
|
||||
${showActionButton ? `<button id="pp-action" type="button" disabled>${actionLabel}</button>` : ''}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
const netBanner = container.querySelector('#pp-network-banner');
|
||||
const searchInput = container.querySelector('#pp-search');
|
||||
const listEl = container.querySelector('#pp-list');
|
||||
const selectedEl = container.querySelector('#pp-selected');
|
||||
const actionBtn = container.querySelector('#pp-action');
|
||||
|
||||
function networkLabel(n) {
|
||||
if (!n) return '';
|
||||
return n.name ? `${n.name} (${n.id})` : n.id;
|
||||
}
|
||||
|
||||
function renderNetworkBanner() {
|
||||
if (!useSharedNetwork) return;
|
||||
const id = BackOneNetworkSession.getNetworkId();
|
||||
const core = BackOneNetworkSession.getCoreStatus();
|
||||
if (!core.online) {
|
||||
netBanner.innerHTML = '<span class="peer-network-warn">BackOne offline — peers unavailable</span>';
|
||||
return;
|
||||
}
|
||||
if (!currentNetwork || !id) {
|
||||
netBanner.innerHTML = '<span class="peer-network-warn">No network — join one in the header bar</span>';
|
||||
return;
|
||||
}
|
||||
const labelSuffix = `${peers.length} contact(s) · ${core.leafPeerCount} VL1 peer(s) from core`;
|
||||
if (layout === 'sidebar') {
|
||||
netBanner.textContent = `${networkLabel(currentNetwork)} · ${labelSuffix}`;
|
||||
} else {
|
||||
netBanner.textContent = `Network: ${networkLabel(currentNetwork)} · ${textSuffix}`;
|
||||
}
|
||||
}
|
||||
|
||||
function renderList() {
|
||||
const q = searchInput.value;
|
||||
const filtered = BackOnePeerDirectory.searchPeers(q, peers);
|
||||
const netTag = currentNetwork ? (currentNetwork.name || currentNetwork.id) : '';
|
||||
listEl.innerHTML = filtered.map(p => {
|
||||
const lat = p.latency != null ? `${p.latency}ms` : '—';
|
||||
const link = p.link || 'DIRECT';
|
||||
const meta = [p.role || 'LEAF', lat, link, netTag].filter(Boolean).join(' · ');
|
||||
return `
|
||||
<li class="peer-item ${p.online ? 'online' : 'offline'} ${selectedPeer && selectedPeer.peerId === p.peerId ? 'active' : ''}" data-id="${p.peerId}">
|
||||
<span class="peer-dot" title="${p.online ? 'Online' : 'Offline'}"></span>
|
||||
<span class="peer-main">
|
||||
<strong class="peer-name">${escapeHtml(p.displayName || p.peerId)}</strong>
|
||||
<small class="peer-sub">${escapeHtml(meta)}</small>
|
||||
</span>
|
||||
<button type="button" class="peer-edit" title="Edit nickname">✎</button>
|
||||
</li>`;
|
||||
}).join('') || '<li class="peer-empty">No peers on this network</li>';
|
||||
|
||||
listEl.querySelectorAll('.peer-item').forEach(li => {
|
||||
li.addEventListener('click', (ev) => {
|
||||
if (ev.target.classList.contains('peer-edit')) return;
|
||||
const id = li.dataset.id;
|
||||
selectedPeer = peers.find(x => x.peerId === id) || null;
|
||||
if (selectedPeer) BackOneNetworkSession.setSelectedPeer(selectedPeer);
|
||||
renderList();
|
||||
renderSelected();
|
||||
if (selectedPeer) onSelect(selectedPeer, currentNetwork);
|
||||
});
|
||||
const editBtn = li.querySelector('.peer-edit');
|
||||
if (editBtn) {
|
||||
editBtn.addEventListener('click', async (ev) => {
|
||||
ev.stopPropagation();
|
||||
const id = li.dataset.id;
|
||||
const peer = peers.find(x => x.peerId === id);
|
||||
if (!peer) return;
|
||||
const nick = prompt('Nickname for ' + peer.peerId, peer.nickname || peer.controllerName || '');
|
||||
if (nick === null) return;
|
||||
await BackOneContacts.setNickname(peer.peerId, nick);
|
||||
BackOneContacts.invalidate();
|
||||
await refreshPeers();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderSelected() {
|
||||
if (layout === 'sidebar') return;
|
||||
if (!selectedPeer) {
|
||||
if (selectedEl) selectedEl.textContent = '';
|
||||
if (actionBtn) actionBtn.disabled = true;
|
||||
return;
|
||||
}
|
||||
const net = currentNetwork ? networkLabel(currentNetwork) : BackOneNetworkSession.getNetworkId();
|
||||
if (selectedEl) {
|
||||
selectedEl.innerHTML = `Selected: <strong>${escapeHtml(selectedPeer.displayName)}</strong> (${escapeHtml(selectedPeer.peerId)})${net ? ` on <em>${escapeHtml(net)}</em>` : ''}`;
|
||||
}
|
||||
if (actionBtn) actionBtn.disabled = !BackOneNetworkSession.getNetworkId();
|
||||
}
|
||||
|
||||
function escapeHtml(s) {
|
||||
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
async function refreshPeers() {
|
||||
currentNetwork = BackOneNetworkSession.getActiveNetwork();
|
||||
if (useSharedNetwork && BackOneNetworkSession.getNetworkId() && !currentNetwork) {
|
||||
currentNetwork = await BackOneNetworkSession.refresh();
|
||||
}
|
||||
renderNetworkBanner();
|
||||
const nwid = BackOneNetworkSession.getNetworkId();
|
||||
await BackOneContacts.load();
|
||||
peers = nwid ? await BackOnePeerDirectory.loadPeersForNetwork(nwid) : [];
|
||||
const lastPeerId = BackOneNetworkSession.getSelectedPeerId();
|
||||
if (lastPeerId) {
|
||||
selectedPeer = peers.find(p => p.peerId === lastPeerId) || BackOneNetworkSession.getSelectedPeer();
|
||||
}
|
||||
renderList();
|
||||
renderSelected();
|
||||
onNetworkChange(currentNetwork, peers);
|
||||
}
|
||||
|
||||
searchInput.addEventListener('input', renderList);
|
||||
if (actionBtn) {
|
||||
actionBtn.addEventListener('click', () => {
|
||||
if (selectedPeer && onAction) onAction(selectedPeer, currentNetwork);
|
||||
});
|
||||
}
|
||||
|
||||
if (useSharedNetwork) {
|
||||
unsub = BackOneNetworkSession.subscribe(() => refreshPeers());
|
||||
}
|
||||
|
||||
refreshPeers();
|
||||
|
||||
return {
|
||||
getSelectedPeer: () => selectedPeer,
|
||||
getSelectedNetwork: () => currentNetwork,
|
||||
getPeers: () => peers,
|
||||
refresh: refreshPeers,
|
||||
destroy: () => { if (unsub) unsub(); }
|
||||
};
|
||||
};
|
||||
|
||||
window.BackOneCommLayout = {
|
||||
connectionBarHtml(peer, network) {
|
||||
const netLabel = network
|
||||
? (network.name ? `${network.name} (${network.id})` : network.id)
|
||||
: BackOneNetworkSession.getNetworkId() || '—';
|
||||
const lat = peer && peer.latency != null ? `${peer.latency}ms` : '—';
|
||||
const latClass = peer && peer.latency != null && peer.latency < 100 ? 'latency-ok' : '';
|
||||
return `
|
||||
<div class="comm-stat"><label>Network ID</label><span>${escapeHtml(netLabel)}</span></div>
|
||||
<div class="comm-stat"><label>Peer ID</label><span>${peer ? escapeHtml(peer.peerId) : '—'}</span></div>
|
||||
<div class="comm-stat"><label>Role</label><span>${peer ? escapeHtml(peer.role || 'LEAF') : '—'}</span></div>
|
||||
<div class="comm-stat"><label>Transport</label><span>${peer ? escapeHtml(peer.link || 'DIRECT') : '—'}</span></div>
|
||||
<div class="comm-stat"><label>Latency</label><span class="${latClass}">${lat}</span></div>
|
||||
<div class="comm-stat"><label>Quality</label><span class="comm-quality"><span></span><span></span><span></span><span></span></span></div>`;
|
||||
}
|
||||
};
|
||||
|
||||
function escapeHtml(s) {
|
||||
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
Vendored
+643
@@ -0,0 +1,643 @@
|
||||
:root {
|
||||
--bg: #0f1419;
|
||||
--surface: #1a2332;
|
||||
--surface-2: #243044;
|
||||
--border: #2d3a4d;
|
||||
--text: #e7ecf1;
|
||||
--muted: #8b9bb4;
|
||||
--accent: #4a90d9;
|
||||
--accent-dim: #2d5a8a;
|
||||
--ok: #2ecc71;
|
||||
--warn: #e6a23c;
|
||||
--danger: #e74c3c;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
margin: 0;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.app-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 0.65rem 1rem;
|
||||
background: var(--surface);
|
||||
border-bottom: 1px solid var(--border);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.app-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
|
||||
.app-brand h1 {
|
||||
margin: 0;
|
||||
font-size: 1.15rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.core-badge {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.15rem 0.55rem;
|
||||
border-radius: 999px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.core-badge.online {
|
||||
background: rgba(46, 204, 113, 0.15);
|
||||
color: var(--ok);
|
||||
border: 1px solid rgba(46, 204, 113, 0.35);
|
||||
}
|
||||
|
||||
.core-badge.offline {
|
||||
background: rgba(231, 76, 60, 0.12);
|
||||
color: var(--danger);
|
||||
border: 1px solid rgba(231, 76, 60, 0.3);
|
||||
}
|
||||
|
||||
.network-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
flex-wrap: wrap;
|
||||
flex: 1;
|
||||
min-width: 240px;
|
||||
}
|
||||
|
||||
.network-bar-label { font-size: 0.82rem; color: var(--muted); }
|
||||
|
||||
.network-bar select,
|
||||
.network-bar input,
|
||||
.auth-token-bar input {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
padding: 0.35rem 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.network-bar input { width: 9.5rem; }
|
||||
|
||||
.network-bar button,
|
||||
.auth-token-bar button {
|
||||
background: var(--accent-dim);
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 0.35rem 0.65rem;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.network-bar-status {
|
||||
font-size: 0.75rem;
|
||||
color: #6b8cae;
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
.auth-token-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
.auth-token-hidden { display: none; }
|
||||
|
||||
.auth-token-label { font-size: 0.82rem; color: var(--muted); }
|
||||
|
||||
.auth-token-bar input { width: 14rem; }
|
||||
|
||||
.addon-nav {
|
||||
display: flex;
|
||||
gap: 0.35rem;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.addon-nav button {
|
||||
background: transparent;
|
||||
color: var(--muted);
|
||||
border: 1px solid transparent;
|
||||
padding: 0.4rem 0.85rem;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
.addon-nav button:hover { color: var(--text); background: var(--surface-2); }
|
||||
|
||||
.addon-nav button.active {
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.addon-panel {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.addon-panel > p {
|
||||
padding: 1.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* —— comm layout (voice / video / chat) —— */
|
||||
|
||||
.comm-layout {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.comm-sidebar {
|
||||
width: 300px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid var(--border);
|
||||
background: var(--bg);
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.comm-sidebar-head {
|
||||
padding: 1rem 1rem 0.5rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.comm-sidebar-head h2 {
|
||||
margin: 0 0 0.35rem;
|
||||
font-size: 1.05rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.comm-sidebar-sub {
|
||||
margin: 0;
|
||||
font-size: 0.72rem;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.comm-sidebar-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding: 0.75rem 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.comm-sidebar-actions {
|
||||
padding: 0.75rem 1rem 1rem;
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.comm-selected-label {
|
||||
font-size: 0.78rem;
|
||||
color: var(--muted);
|
||||
margin-bottom: 0.15rem;
|
||||
}
|
||||
|
||||
.comm-selected-label strong {
|
||||
color: var(--text);
|
||||
font-family: ui-monospace, monospace;
|
||||
}
|
||||
|
||||
.comm-main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
background: #0a0e14;
|
||||
}
|
||||
|
||||
.comm-main-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.65rem 1rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.comm-breadcrumb {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.comm-breadcrumb strong { color: var(--text); }
|
||||
|
||||
.comm-head-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
color: var(--muted);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.comm-head-status .connected {
|
||||
color: var(--ok);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.comm-stage {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 0;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.comm-stage video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.comm-remote-placeholder {
|
||||
text-align: center;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.comm-remote-placeholder .avatar {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border-radius: 50%;
|
||||
background: var(--surface-2);
|
||||
margin: 0 auto 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 2rem;
|
||||
color: #5a6a7d;
|
||||
}
|
||||
|
||||
.comm-remote-placeholder .peer-id {
|
||||
font-family: ui-monospace, monospace;
|
||||
color: var(--text);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.comm-remote-tag {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
left: 12px;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
padding: 0.2rem 0.55rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.comm-remote-tag .dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
.comm-call-controls {
|
||||
position: absolute;
|
||||
bottom: 1.25rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
padding: 0.45rem;
|
||||
background: rgba(26, 35, 50, 0.92);
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.comm-call-controls button {
|
||||
background: var(--surface-2);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
padding: 0.45rem 0.85rem;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.comm-call-controls button.danger {
|
||||
background: var(--danger);
|
||||
border-color: var(--danger);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.comm-connection-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1.25rem 2rem;
|
||||
padding: 0.65rem 1rem;
|
||||
background: var(--surface);
|
||||
border-top: 1px solid var(--border);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.comm-stat label {
|
||||
display: block;
|
||||
color: var(--muted);
|
||||
font-size: 0.68rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
margin-bottom: 0.15rem;
|
||||
}
|
||||
|
||||
.comm-stat span {
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.comm-stat .latency-ok { color: var(--ok); }
|
||||
|
||||
.comm-quality {
|
||||
display: inline-flex;
|
||||
align-items: flex-end;
|
||||
gap: 2px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.comm-quality span {
|
||||
width: 3px;
|
||||
background: var(--ok);
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
.comm-quality span:nth-child(1) { height: 4px; }
|
||||
.comm-quality span:nth-child(2) { height: 7px; }
|
||||
.comm-quality span:nth-child(3) { height: 10px; }
|
||||
.comm-quality span:nth-child(4) { height: 14px; }
|
||||
|
||||
/* —— peer picker —— */
|
||||
|
||||
.peer-picker { display: flex; flex-direction: column; flex: 1; min-height: 0; }
|
||||
|
||||
.peer-info-box {
|
||||
font-size: 0.82rem;
|
||||
color: #9eb4d0;
|
||||
margin-bottom: 0.75rem;
|
||||
padding: 0.55rem 0.65rem;
|
||||
background: rgba(45, 90, 138, 0.25);
|
||||
border: 1px solid rgba(74, 144, 217, 0.25);
|
||||
border-radius: 6px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.peer-picker-row { display: block; margin-bottom: 0.65rem; }
|
||||
|
||||
.peer-picker-row input {
|
||||
width: 100%;
|
||||
margin-top: 0.25rem;
|
||||
padding: 0.45rem 0.55rem;
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.peer-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
--peer-row-height: 3.25rem;
|
||||
max-height: calc(var(--peer-row-height) * 10);
|
||||
flex: 0 1 auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.peer-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.5rem;
|
||||
min-height: var(--peer-row-height);
|
||||
padding: 0.55rem 0.65rem;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid var(--border);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.peer-item:last-child { border-bottom: none; }
|
||||
|
||||
.peer-item:hover,
|
||||
.peer-item.active {
|
||||
background: var(--surface-2);
|
||||
}
|
||||
|
||||
.peer-item.active { border-left: 3px solid var(--accent); padding-left: calc(0.65rem - 3px); }
|
||||
|
||||
.peer-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #666;
|
||||
flex-shrink: 0;
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
|
||||
.peer-item.online .peer-dot { background: var(--ok); }
|
||||
|
||||
.peer-main { flex: 1; min-width: 0; }
|
||||
|
||||
.peer-name {
|
||||
display: block;
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
.peer-sub {
|
||||
color: var(--muted);
|
||||
font-size: 0.72rem;
|
||||
display: block;
|
||||
margin-top: 0.15rem;
|
||||
}
|
||||
|
||||
.peer-edit {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--muted);
|
||||
cursor: pointer;
|
||||
padding: 0.2rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.peer-empty { padding: 1rem; color: var(--muted); font-size: 0.85rem; }
|
||||
|
||||
.peer-network-warn { color: var(--warn); }
|
||||
|
||||
.btn-primary {
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 0.55rem 1rem;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.88rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--surface-2);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
padding: 0.55rem 1rem;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
/* —— chat —— */
|
||||
|
||||
.chat-thread-panel {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.chat-thread-header {
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
#chat-thread {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
border: 1px solid var(--border);
|
||||
padding: 0.75rem;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 0.5rem;
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.chat-compose {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.chat-compose textarea {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.chat-compose-row {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* —— voice stage —— */
|
||||
|
||||
.voice-stage-inner {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.voice-stage-inner .avatar {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
border-radius: 50%;
|
||||
background: var(--surface-2);
|
||||
margin: 0 auto 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
/* —— ringing popup —— */
|
||||
|
||||
.ringing-hidden { display: none; }
|
||||
|
||||
#ringing-popup {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#ringing-popup.ringing-hidden { display: none; }
|
||||
|
||||
.ringing-card {
|
||||
background: var(--surface);
|
||||
padding: 2rem;
|
||||
border-radius: 12px;
|
||||
text-align: center;
|
||||
min-width: 280px;
|
||||
}
|
||||
|
||||
.ringing-card button {
|
||||
margin: 0.5rem;
|
||||
padding: 0.6rem 1.2rem;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ringing-accept { background: var(--ok); color: #000; }
|
||||
.ringing-reject { background: var(--danger); color: #fff; }
|
||||
|
||||
.video-local-pip {
|
||||
position: absolute;
|
||||
bottom: 4.5rem;
|
||||
right: 1rem;
|
||||
width: 160px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 2px solid var(--border);
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.video-local-pip video {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"id": "app-tray",
|
||||
"name": "BackOne Tray",
|
||||
"version": "1.17.0",
|
||||
"requires": []
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=BackOne Tray
|
||||
Comment=Start BackOne tray icon at login
|
||||
Exec=/usr/bin/backone-tray
|
||||
Icon=network-workgroup
|
||||
Terminal=false
|
||||
X-GNOME-Autostart-enabled=true
|
||||
Hidden=false
|
||||
@@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=BackOne Tray
|
||||
Comment=BackOne system tray for voice, video, and chat
|
||||
Exec=/usr/bin/backone-tray
|
||||
Icon=network-workgroup
|
||||
Terminal=false
|
||||
Categories=Network;
|
||||
Executable
+127
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env python3
|
||||
"""BackOne system tray — opens /app UI and addon deep links."""
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import urllib.request
|
||||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
try:
|
||||
gi.require_version('AyatanaAppIndicator3', '0.1')
|
||||
from gi.repository import AyatanaAppIndicator3 as AppIndicator3
|
||||
except (ImportError, ValueError):
|
||||
gi.require_version('AppIndicator3', '0.1')
|
||||
from gi.repository import AppIndicator3
|
||||
|
||||
from gi.repository import GLib, Gtk
|
||||
|
||||
APP_URL = os.environ.get('BACKONE_APP_URL', 'http://127.0.0.1:9993/app/')
|
||||
HEALTH_URL = os.environ.get('BACKONE_HEALTH_URL', 'http://127.0.0.1:9993/health')
|
||||
ICON_NAME = 'network-workgroup'
|
||||
|
||||
|
||||
class BackOneTray:
|
||||
def __init__(self):
|
||||
self._windows = []
|
||||
self.indicator = AppIndicator3.Indicator.new(
|
||||
'backone-tray',
|
||||
ICON_NAME,
|
||||
AppIndicator3.IndicatorCategory.APPLICATION_STATUS,
|
||||
)
|
||||
self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
|
||||
self.indicator.set_menu(self._build_menu())
|
||||
try:
|
||||
self.indicator.connect('activate', lambda *_: self._open_url(APP_URL))
|
||||
except TypeError:
|
||||
pass
|
||||
self._update_status()
|
||||
GLib.timeout_add_seconds(30, self._on_timer)
|
||||
|
||||
def _build_menu(self):
|
||||
menu = Gtk.Menu()
|
||||
|
||||
def add_item(label, callback):
|
||||
item = Gtk.MenuItem(label=label)
|
||||
item.connect('activate', lambda *_: callback())
|
||||
item.show()
|
||||
menu.append(item)
|
||||
return item
|
||||
|
||||
add_item('Open BackOne App', lambda: self._open_url(APP_URL))
|
||||
add_item('Voice', lambda: self._open_url(APP_URL + '#/voice'))
|
||||
add_item('Video', lambda: self._open_url(APP_URL + '#/video'))
|
||||
add_item('Chat', lambda: self._open_url(APP_URL + '#/chat'))
|
||||
menu.append(Gtk.SeparatorMenuItem())
|
||||
add_item('Restart BackOne Service', self._restart_service)
|
||||
add_item('Quit Tray', Gtk.main_quit)
|
||||
menu.show()
|
||||
return menu
|
||||
|
||||
def _service_ok(self):
|
||||
try:
|
||||
with urllib.request.urlopen(HEALTH_URL, timeout=2) as resp:
|
||||
return resp.status == 200
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _update_status(self):
|
||||
title = 'BackOne (online)' if self._service_ok() else 'BackOne (offline)'
|
||||
self.indicator.set_title(title)
|
||||
|
||||
def _on_timer(self):
|
||||
self._update_status()
|
||||
return True
|
||||
|
||||
def _open_url(self, url):
|
||||
if self._try_webkit(url):
|
||||
return
|
||||
for cmd in (
|
||||
['xdg-open', url],
|
||||
['gio', 'open', url],
|
||||
['sensible-browser', url],
|
||||
):
|
||||
try:
|
||||
subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
return
|
||||
except OSError:
|
||||
continue
|
||||
|
||||
def _try_webkit(self, url):
|
||||
try:
|
||||
gi.require_version('WebKit2', '4.1')
|
||||
except ValueError:
|
||||
try:
|
||||
gi.require_version('WebKit2', '4.0')
|
||||
except ValueError:
|
||||
return False
|
||||
from gi.repository import WebKit2
|
||||
|
||||
win = Gtk.Window(title='BackOne')
|
||||
win.set_default_size(900, 640)
|
||||
view = WebKit2.WebView()
|
||||
view.load_uri(url)
|
||||
win.add(view)
|
||||
win.connect('destroy', lambda w: self._windows.remove(w) if w in self._windows else None)
|
||||
self._windows.append(win)
|
||||
win.show_all()
|
||||
return True
|
||||
|
||||
def _restart_service(self):
|
||||
subprocess.Popen(
|
||||
['pkexec', 'systemctl', 'restart', 'backone'],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
BackOneTray()
|
||||
Gtk.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
+10
-1
@@ -1,5 +1,5 @@
|
||||
Name: backone
|
||||
Version: 1.14.1
|
||||
Version: 1.17.1
|
||||
Release: 1%{?dist}
|
||||
Summary: BackOne global ethernet switch
|
||||
|
||||
@@ -155,6 +155,15 @@ chmod 0755 $RPM_BUILD_ROOT/etc/init.d/backone
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Tue Jun 30 2026 Dedy Sutanto <dsutanto@backone.cloud> - 1.17.1
|
||||
- Fix /api/backone routes and reinstallable Web UI packages
|
||||
|
||||
* Tue Jun 30 2026 Dedy Sutanto <dsutanto@backone.cloud> - 1.17.0
|
||||
- Tray app, auto web server, peer contact UI
|
||||
|
||||
* Tue Jun 30 2026 Dedy Sutanto <dsutanto@backone.cloud> - 1.16.0
|
||||
- Release notes
|
||||
|
||||
* Tue Oct 8 2024 Dedy Sutanto <dsutanto@backone.cloud> - 1.14.1
|
||||
- see https://github.com/proitlab/BackOne for release notes
|
||||
|
||||
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $0 <version> [changelog message]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION="$1"
|
||||
MESSAGE="${2:-see https://github.com/proitlab/BackOne for release notes}"
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
IFS='.' read -r MAJ MIN REV <<< "${VERSION%%-*}"
|
||||
REV="${REV:-0}"
|
||||
|
||||
sed -i "s/^#define ZEROTIER_ONE_VERSION_MAJOR .*/#define ZEROTIER_ONE_VERSION_MAJOR ${MAJ}/" "$ROOT/version.h"
|
||||
sed -i "s/^#define ZEROTIER_ONE_VERSION_MINOR .*/#define ZEROTIER_ONE_VERSION_MINOR ${MIN}/" "$ROOT/version.h"
|
||||
sed -i "s/^#define ZEROTIER_ONE_VERSION_REVISION .*/#define ZEROTIER_ONE_VERSION_REVISION ${REV}/" "$ROOT/version.h"
|
||||
sed -i "s/^#define ZEROTIER_ONE_VERSION_BUILD .*/#define ZEROTIER_ONE_VERSION_BUILD 0/" "$ROOT/version.h"
|
||||
|
||||
echo "$VERSION" > "$ROOT/debian/version"
|
||||
|
||||
"$ROOT/ci/scripts/munge_debian_changelog.sh" "$ROOT/debian/changelog" "$VERSION" \
|
||||
"Dedy Sutanto <dsutanto@backone.cloud>" "$MESSAGE"
|
||||
|
||||
"$ROOT/ci/scripts/munge_rpm_spec.sh" "$ROOT/backone.spec" "$VERSION" \
|
||||
"Dedy Sutanto <dsutanto@backone.cloud>" "$MESSAGE"
|
||||
|
||||
for addon in comms-core voice video chat updater app-tray; do
|
||||
if [ -f "$ROOT/addons/$addon/addon.json" ]; then
|
||||
sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"${VERSION}\"/" "$ROOT/addons/$addon/addon.json"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Bumped to ${VERSION}"
|
||||
@@ -31,7 +31,7 @@ else
|
||||
fi
|
||||
|
||||
awk -v version=${VERSION} -v date=${DATE} -v name=${NAME} -v message=${MESSAGE} \
|
||||
'BEGIN{print "zerotier-one (" version ") stable; urgency=medium\n\n * " message "\n\n -- " name " " date "\n" }{ print }' \
|
||||
'BEGIN{print "backone (" version ") stable; urgency=medium\n\n * " message "\n\n -- " name " " date "\n" }{ print }' \
|
||||
${FILE} > ${FILE}.new
|
||||
|
||||
mv ${FILE}.new ${FILE}
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/bin/sh
|
||||
# Keep ~/.backOneOneAuthToken and /var/lib/backone/authtoken.secret in sync (same as backone-cli).
|
||||
set -e
|
||||
|
||||
BACKONE_HOME="${BACKONE_HOME:-/var/lib/backone}"
|
||||
SECRET="${BACKONE_HOME}/authtoken.secret"
|
||||
|
||||
trim_token() {
|
||||
tr -d '\n\r \t' < "$1"
|
||||
}
|
||||
|
||||
sync_for_user() {
|
||||
user="$1"
|
||||
home="$2"
|
||||
[ -n "$user" ] && [ -n "$home" ] && [ -d "$home" ] || return 0
|
||||
user_token="${home}/.backOneOneAuthToken"
|
||||
|
||||
if [ -f "$user_token" ] && [ -s "$user_token" ]; then
|
||||
ut="$(trim_token "$user_token")"
|
||||
[ -n "$ut" ] || return 0
|
||||
st=""
|
||||
if [ -f "$SECRET" ]; then
|
||||
st="$(trim_token "$SECRET" 2>/dev/null || true)"
|
||||
fi
|
||||
if [ "$ut" != "$st" ]; then
|
||||
printf '%s' "$ut" > "$SECRET"
|
||||
chown backone:backone "$SECRET" 2>/dev/null || true
|
||||
chmod 0600 "$SECRET" 2>/dev/null || true
|
||||
echo "Synced ${SECRET} from ${user_token}"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -f "$SECRET" ]; then
|
||||
install -o "$user" -g "$(id -gn "$user" 2>/dev/null || echo "$user")" -m 0600 "$SECRET" "$user_token" 2>/dev/null \
|
||||
|| cp "$SECRET" "$user_token"
|
||||
chown "$user":"$(id -gn "$user" 2>/dev/null || echo "$user")" "$user_token" 2>/dev/null || true
|
||||
chmod 0600 "$user_token" 2>/dev/null || true
|
||||
echo "Installed ${user_token} from ${SECRET}"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ -n "${SUDO_USER:-}" ] && [ "$SUDO_USER" != "root" ]; then
|
||||
home="$(getent passwd "$SUDO_USER" | cut -d: -f6)"
|
||||
sync_for_user "$SUDO_USER" "$home"
|
||||
fi
|
||||
|
||||
if [ -n "${USER:-}" ] && [ "$USER" != "root" ] && [ "$USER" != "${SUDO_USER:-}" ]; then
|
||||
home="$(getent passwd "$USER" | cut -d: -f6)"
|
||||
sync_for_user "$USER" "$home"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -1120,6 +1120,12 @@ void EmbeddedNetworkController::configureHTTPControlPlane(
|
||||
};
|
||||
s.Get(memberListPath2, memberListGet2);
|
||||
sv6.Get(memberListPath2, memberListGet2);
|
||||
std::string appMemberListPath = "/app/api/network/([0-9a-fA-F]{16})/members";
|
||||
s.Get(appMemberListPath, memberListGet2);
|
||||
sv6.Get(appMemberListPath, memberListGet2);
|
||||
std::string backoneMemberListPath = "/api/backone/network/([0-9a-fA-F]{16})/members";
|
||||
s.Get(backoneMemberListPath, memberListGet2);
|
||||
sv6.Get(backoneMemberListPath, memberListGet2);
|
||||
|
||||
auto memberGet = [&, setContent](const httplib::Request &req, httplib::Response &res) {
|
||||
auto networkID = req.matches[1];
|
||||
|
||||
Vendored
Executable
+1825
File diff suppressed because it is too large.
Load diff
Vendored
Executable
+1462
File diff suppressed because it is too large.
Load diff
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
8131853dbc8c3be5171fa96353de7a884a79d3f1d6b3fbf48f99037f5f95fe27 ext/libpqxx-7.7.3/config/config.guess
|
||||
1ee0b1581032c46aac6ebd1de74b130f35082fb45a12e9ead42ad5c775f9b64f ext/libpqxx-7.7.3/config/config.sub
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
backone (1.17.1) stable; urgency=medium
|
||||
|
||||
* Fix /api/backone routes and reinstallable Web UI packages
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 12:02:07 +0700
|
||||
|
||||
backone (1.17.0) stable; urgency=medium
|
||||
|
||||
* Tray app, auto web server, peer contact UI
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 09:55:07 +0700
|
||||
|
||||
backone (1.16.0) stable; urgency=medium
|
||||
|
||||
* Release notes
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 08:59:49 +0700
|
||||
|
||||
backone (1.15.0) stable; urgency=medium
|
||||
|
||||
* Separated addon architecture: voice, video, chat, updater
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 08:37:16 +0700
|
||||
zerotier-one (1.14.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 11 Sep 2024 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.14.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 19 Mar 2024 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 12 Sep 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 25 Aug 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 17 Aug 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.6) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 21 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.5) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 10 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.4) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 06 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.3) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Sat, 21 Jan 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 13 Oct 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 27 Jun 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 03 Jun 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.10) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 10 May 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.9) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 25 Apr 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.8) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 11 Apr 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.7) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 21 Mar 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.6) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 07 Mar 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.5) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 17 Dec 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.4) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 23 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.3) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 15 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 08 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 20 Oct 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 15 Sep 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.6) unstable; urgency=medium
|
||||
|
||||
* Backport endpoint mitigation against address collision attack.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 21 Sep 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.5) unstable; urgency=medium
|
||||
|
||||
* Fix path filtering bug that could cause "software laser" effect.
|
||||
* Fix printf overflow in CLI (not exploitable or security related)
|
||||
* Fix Windows device enumeration issue.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 13 Apr 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.4) unstable; urgency=medium
|
||||
|
||||
* REALLY fix a problem causing nodes to go into a "coma" with some network configurations.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 15 Feb 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.3-1) unstable; urgency=medium
|
||||
|
||||
* Fix a problem causing nodes to go into a "coma" with some network configurations.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 02 Feb 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.2-2) unstable; urgency=medium
|
||||
|
||||
* This is a minor update to the 1.6.2 package to address issues with
|
||||
running on ARMv6 CPUs like the Raspberry Pi Zero and original v1 Pi.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 31 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 30 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 24 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 19 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.5.0) unstable; urgency=medium
|
||||
|
||||
* Version 1.5.0 is actually 1.6.0-beta1
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 05 Aug 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.6) unstable; urgency=medium
|
||||
|
||||
* Update default root server list
|
||||
* Fix build flags on "armhf" (32-bit ARM) platforms for better
|
||||
compatibility with Pi Zero and other devices.
|
||||
* Fix license text in one.cpp.
|
||||
* Add a clarification to LICENSE.txt.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 30 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.4) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
* License changed to BSL 1.1
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 23 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.2-2) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
* This is a new build that fixes a binary build issue with containers and SELinux
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 04 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.2) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 04 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.0) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 29 Jul 2019 01:00:00 -0700
|
||||
|
||||
# Older entries have been removed from this changelog.
|
||||
# To read the complete changelog use `apt changelog backone-addon-chat`.
|
||||
Whitespace-only changes.
+257
@@ -0,0 +1,257 @@
|
||||
backone (1.17.1) stable; urgency=medium
|
||||
|
||||
* Fix /api/backone routes and reinstallable Web UI packages
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 12:02:07 +0700
|
||||
|
||||
backone (1.17.0) stable; urgency=medium
|
||||
|
||||
* Tray app, auto web server, peer contact UI
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 09:55:07 +0700
|
||||
|
||||
backone (1.16.0) stable; urgency=medium
|
||||
|
||||
* Release notes
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 08:59:49 +0700
|
||||
|
||||
backone (1.15.0) stable; urgency=medium
|
||||
|
||||
* Separated addon architecture: voice, video, chat, updater
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 08:37:16 +0700
|
||||
zerotier-one (1.14.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 11 Sep 2024 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.14.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 19 Mar 2024 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 12 Sep 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 25 Aug 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 17 Aug 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.6) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 21 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.5) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 10 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.4) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 06 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.3) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Sat, 21 Jan 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 13 Oct 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 27 Jun 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 03 Jun 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.10) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 10 May 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.9) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 25 Apr 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.8) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 11 Apr 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.7) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 21 Mar 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.6) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 07 Mar 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.5) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 17 Dec 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.4) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 23 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.3) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 15 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 08 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 20 Oct 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 15 Sep 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.6) unstable; urgency=medium
|
||||
|
||||
* Backport endpoint mitigation against address collision attack.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 21 Sep 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.5) unstable; urgency=medium
|
||||
|
||||
* Fix path filtering bug that could cause "software laser" effect.
|
||||
* Fix printf overflow in CLI (not exploitable or security related)
|
||||
* Fix Windows device enumeration issue.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 13 Apr 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.4) unstable; urgency=medium
|
||||
|
||||
* REALLY fix a problem causing nodes to go into a "coma" with some network configurations.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 15 Feb 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.3-1) unstable; urgency=medium
|
||||
|
||||
* Fix a problem causing nodes to go into a "coma" with some network configurations.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 02 Feb 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.2-2) unstable; urgency=medium
|
||||
|
||||
* This is a minor update to the 1.6.2 package to address issues with
|
||||
running on ARMv6 CPUs like the Raspberry Pi Zero and original v1 Pi.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 31 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 30 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 24 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 19 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.5.0) unstable; urgency=medium
|
||||
|
||||
* Version 1.5.0 is actually 1.6.0-beta1
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 05 Aug 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.6) unstable; urgency=medium
|
||||
|
||||
* Update default root server list
|
||||
* Fix build flags on "armhf" (32-bit ARM) platforms for better
|
||||
compatibility with Pi Zero and other devices.
|
||||
* Fix license text in one.cpp.
|
||||
* Add a clarification to LICENSE.txt.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 30 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.4) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
* License changed to BSL 1.1
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 23 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.2-2) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
* This is a new build that fixes a binary build issue with containers and SELinux
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 04 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.2) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 04 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.0) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 29 Jul 2019 01:00:00 -0700
|
||||
|
||||
# Older entries have been removed from this changelog.
|
||||
# To read the complete changelog use `apt changelog backone-addon-comms-core`.
|
||||
Whitespace-only changes.
+257
@@ -0,0 +1,257 @@
|
||||
backone (1.17.1) stable; urgency=medium
|
||||
|
||||
* Fix /api/backone routes and reinstallable Web UI packages
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 12:02:07 +0700
|
||||
|
||||
backone (1.17.0) stable; urgency=medium
|
||||
|
||||
* Tray app, auto web server, peer contact UI
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 09:55:07 +0700
|
||||
|
||||
backone (1.16.0) stable; urgency=medium
|
||||
|
||||
* Release notes
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 08:59:49 +0700
|
||||
|
||||
backone (1.15.0) stable; urgency=medium
|
||||
|
||||
* Separated addon architecture: voice, video, chat, updater
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 08:37:16 +0700
|
||||
zerotier-one (1.14.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 11 Sep 2024 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.14.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 19 Mar 2024 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 12 Sep 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 25 Aug 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 17 Aug 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.6) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 21 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.5) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 10 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.4) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 06 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.3) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Sat, 21 Jan 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 13 Oct 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 27 Jun 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 03 Jun 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.10) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 10 May 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.9) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 25 Apr 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.8) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 11 Apr 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.7) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 21 Mar 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.6) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 07 Mar 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.5) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 17 Dec 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.4) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 23 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.3) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 15 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 08 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 20 Oct 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 15 Sep 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.6) unstable; urgency=medium
|
||||
|
||||
* Backport endpoint mitigation against address collision attack.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 21 Sep 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.5) unstable; urgency=medium
|
||||
|
||||
* Fix path filtering bug that could cause "software laser" effect.
|
||||
* Fix printf overflow in CLI (not exploitable or security related)
|
||||
* Fix Windows device enumeration issue.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 13 Apr 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.4) unstable; urgency=medium
|
||||
|
||||
* REALLY fix a problem causing nodes to go into a "coma" with some network configurations.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 15 Feb 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.3-1) unstable; urgency=medium
|
||||
|
||||
* Fix a problem causing nodes to go into a "coma" with some network configurations.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 02 Feb 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.2-2) unstable; urgency=medium
|
||||
|
||||
* This is a minor update to the 1.6.2 package to address issues with
|
||||
running on ARMv6 CPUs like the Raspberry Pi Zero and original v1 Pi.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 31 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 30 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 24 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 19 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.5.0) unstable; urgency=medium
|
||||
|
||||
* Version 1.5.0 is actually 1.6.0-beta1
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 05 Aug 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.6) unstable; urgency=medium
|
||||
|
||||
* Update default root server list
|
||||
* Fix build flags on "armhf" (32-bit ARM) platforms for better
|
||||
compatibility with Pi Zero and other devices.
|
||||
* Fix license text in one.cpp.
|
||||
* Add a clarification to LICENSE.txt.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 30 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.4) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
* License changed to BSL 1.1
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 23 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.2-2) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
* This is a new build that fixes a binary build issue with containers and SELinux
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 04 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.2) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 04 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.0) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 29 Jul 2019 01:00:00 -0700
|
||||
|
||||
# Older entries have been removed from this changelog.
|
||||
# To read the complete changelog use `apt changelog backone-addon-updater`.
|
||||
Whitespace-only changes.
+257
@@ -0,0 +1,257 @@
|
||||
backone (1.17.1) stable; urgency=medium
|
||||
|
||||
* Fix /api/backone routes and reinstallable Web UI packages
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 12:02:07 +0700
|
||||
|
||||
backone (1.17.0) stable; urgency=medium
|
||||
|
||||
* Tray app, auto web server, peer contact UI
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 09:55:07 +0700
|
||||
|
||||
backone (1.16.0) stable; urgency=medium
|
||||
|
||||
* Release notes
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 08:59:49 +0700
|
||||
|
||||
backone (1.15.0) stable; urgency=medium
|
||||
|
||||
* Separated addon architecture: voice, video, chat, updater
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 08:37:16 +0700
|
||||
zerotier-one (1.14.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 11 Sep 2024 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.14.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 19 Mar 2024 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 12 Sep 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 25 Aug 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 17 Aug 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.6) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 21 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.5) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 10 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.4) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 06 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.3) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Sat, 21 Jan 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 13 Oct 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 27 Jun 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 03 Jun 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.10) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 10 May 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.9) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 25 Apr 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.8) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 11 Apr 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.7) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 21 Mar 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.6) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 07 Mar 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.5) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 17 Dec 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.4) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 23 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.3) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 15 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 08 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 20 Oct 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 15 Sep 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.6) unstable; urgency=medium
|
||||
|
||||
* Backport endpoint mitigation against address collision attack.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 21 Sep 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.5) unstable; urgency=medium
|
||||
|
||||
* Fix path filtering bug that could cause "software laser" effect.
|
||||
* Fix printf overflow in CLI (not exploitable or security related)
|
||||
* Fix Windows device enumeration issue.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 13 Apr 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.4) unstable; urgency=medium
|
||||
|
||||
* REALLY fix a problem causing nodes to go into a "coma" with some network configurations.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 15 Feb 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.3-1) unstable; urgency=medium
|
||||
|
||||
* Fix a problem causing nodes to go into a "coma" with some network configurations.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 02 Feb 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.2-2) unstable; urgency=medium
|
||||
|
||||
* This is a minor update to the 1.6.2 package to address issues with
|
||||
running on ARMv6 CPUs like the Raspberry Pi Zero and original v1 Pi.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 31 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 30 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 24 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 19 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.5.0) unstable; urgency=medium
|
||||
|
||||
* Version 1.5.0 is actually 1.6.0-beta1
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 05 Aug 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.6) unstable; urgency=medium
|
||||
|
||||
* Update default root server list
|
||||
* Fix build flags on "armhf" (32-bit ARM) platforms for better
|
||||
compatibility with Pi Zero and other devices.
|
||||
* Fix license text in one.cpp.
|
||||
* Add a clarification to LICENSE.txt.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 30 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.4) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
* License changed to BSL 1.1
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 23 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.2-2) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
* This is a new build that fixes a binary build issue with containers and SELinux
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 04 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.2) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 04 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.0) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 29 Jul 2019 01:00:00 -0700
|
||||
|
||||
# Older entries have been removed from this changelog.
|
||||
# To read the complete changelog use `apt changelog backone-addon-video`.
|
||||
Whitespace-only changes.
+257
@@ -0,0 +1,257 @@
|
||||
backone (1.17.1) stable; urgency=medium
|
||||
|
||||
* Fix /api/backone routes and reinstallable Web UI packages
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 12:02:07 +0700
|
||||
|
||||
backone (1.17.0) stable; urgency=medium
|
||||
|
||||
* Tray app, auto web server, peer contact UI
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 09:55:07 +0700
|
||||
|
||||
backone (1.16.0) stable; urgency=medium
|
||||
|
||||
* Release notes
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 08:59:49 +0700
|
||||
|
||||
backone (1.15.0) stable; urgency=medium
|
||||
|
||||
* Separated addon architecture: voice, video, chat, updater
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 08:37:16 +0700
|
||||
zerotier-one (1.14.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 11 Sep 2024 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.14.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 19 Mar 2024 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 12 Sep 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 25 Aug 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 17 Aug 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.6) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 21 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.5) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 10 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.4) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 06 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.3) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Sat, 21 Jan 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 13 Oct 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 27 Jun 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 03 Jun 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.10) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 10 May 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.9) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 25 Apr 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.8) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 11 Apr 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.7) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 21 Mar 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.6) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 07 Mar 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.5) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 17 Dec 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.4) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 23 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.3) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 15 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 08 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 20 Oct 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 15 Sep 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.6) unstable; urgency=medium
|
||||
|
||||
* Backport endpoint mitigation against address collision attack.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 21 Sep 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.5) unstable; urgency=medium
|
||||
|
||||
* Fix path filtering bug that could cause "software laser" effect.
|
||||
* Fix printf overflow in CLI (not exploitable or security related)
|
||||
* Fix Windows device enumeration issue.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 13 Apr 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.4) unstable; urgency=medium
|
||||
|
||||
* REALLY fix a problem causing nodes to go into a "coma" with some network configurations.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 15 Feb 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.3-1) unstable; urgency=medium
|
||||
|
||||
* Fix a problem causing nodes to go into a "coma" with some network configurations.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 02 Feb 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.2-2) unstable; urgency=medium
|
||||
|
||||
* This is a minor update to the 1.6.2 package to address issues with
|
||||
running on ARMv6 CPUs like the Raspberry Pi Zero and original v1 Pi.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 31 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 30 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 24 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 19 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.5.0) unstable; urgency=medium
|
||||
|
||||
* Version 1.5.0 is actually 1.6.0-beta1
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 05 Aug 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.6) unstable; urgency=medium
|
||||
|
||||
* Update default root server list
|
||||
* Fix build flags on "armhf" (32-bit ARM) platforms for better
|
||||
compatibility with Pi Zero and other devices.
|
||||
* Fix license text in one.cpp.
|
||||
* Add a clarification to LICENSE.txt.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 30 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.4) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
* License changed to BSL 1.1
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 23 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.2-2) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
* This is a new build that fixes a binary build issue with containers and SELinux
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 04 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.2) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 04 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.0) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 29 Jul 2019 01:00:00 -0700
|
||||
|
||||
# Older entries have been removed from this changelog.
|
||||
# To read the complete changelog use `apt changelog backone-addon-voice`.
|
||||
Whitespace-only changes.
+257
@@ -0,0 +1,257 @@
|
||||
backone (1.17.1) stable; urgency=medium
|
||||
|
||||
* Fix /api/backone routes and reinstallable Web UI packages
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 12:02:07 +0700
|
||||
|
||||
backone (1.17.0) stable; urgency=medium
|
||||
|
||||
* Tray app, auto web server, peer contact UI
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 09:55:07 +0700
|
||||
|
||||
backone (1.16.0) stable; urgency=medium
|
||||
|
||||
* Release notes
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 08:59:49 +0700
|
||||
|
||||
backone (1.15.0) stable; urgency=medium
|
||||
|
||||
* Separated addon architecture: voice, video, chat, updater
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 08:37:16 +0700
|
||||
zerotier-one (1.14.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 11 Sep 2024 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.14.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 19 Mar 2024 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 12 Sep 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 25 Aug 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 17 Aug 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.6) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 21 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.5) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 10 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.4) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 06 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.3) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Sat, 21 Jan 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 13 Oct 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 27 Jun 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 03 Jun 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.10) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 10 May 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.9) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 25 Apr 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.8) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 11 Apr 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.7) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 21 Mar 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.6) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 07 Mar 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.5) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 17 Dec 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.4) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 23 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.3) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 15 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 08 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 20 Oct 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 15 Sep 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.6) unstable; urgency=medium
|
||||
|
||||
* Backport endpoint mitigation against address collision attack.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 21 Sep 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.5) unstable; urgency=medium
|
||||
|
||||
* Fix path filtering bug that could cause "software laser" effect.
|
||||
* Fix printf overflow in CLI (not exploitable or security related)
|
||||
* Fix Windows device enumeration issue.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 13 Apr 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.4) unstable; urgency=medium
|
||||
|
||||
* REALLY fix a problem causing nodes to go into a "coma" with some network configurations.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 15 Feb 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.3-1) unstable; urgency=medium
|
||||
|
||||
* Fix a problem causing nodes to go into a "coma" with some network configurations.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 02 Feb 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.2-2) unstable; urgency=medium
|
||||
|
||||
* This is a minor update to the 1.6.2 package to address issues with
|
||||
running on ARMv6 CPUs like the Raspberry Pi Zero and original v1 Pi.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 31 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 30 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 24 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 19 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.5.0) unstable; urgency=medium
|
||||
|
||||
* Version 1.5.0 is actually 1.6.0-beta1
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 05 Aug 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.6) unstable; urgency=medium
|
||||
|
||||
* Update default root server list
|
||||
* Fix build flags on "armhf" (32-bit ARM) platforms for better
|
||||
compatibility with Pi Zero and other devices.
|
||||
* Fix license text in one.cpp.
|
||||
* Add a clarification to LICENSE.txt.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 30 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.4) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
* License changed to BSL 1.1
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 23 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.2-2) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
* This is a new build that fixes a binary build issue with containers and SELinux
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 04 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.2) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 04 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.0) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 29 Jul 2019 01:00:00 -0700
|
||||
|
||||
# Older entries have been removed from this changelog.
|
||||
# To read the complete changelog use `apt changelog backone-app-shell`.
|
||||
Whitespace-only changes.
+257
@@ -0,0 +1,257 @@
|
||||
backone (1.17.1) stable; urgency=medium
|
||||
|
||||
* Fix /api/backone routes and reinstallable Web UI packages
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 12:02:07 +0700
|
||||
|
||||
backone (1.17.0) stable; urgency=medium
|
||||
|
||||
* Tray app, auto web server, peer contact UI
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 09:55:07 +0700
|
||||
|
||||
backone (1.16.0) stable; urgency=medium
|
||||
|
||||
* Release notes
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 08:59:49 +0700
|
||||
|
||||
backone (1.15.0) stable; urgency=medium
|
||||
|
||||
* Separated addon architecture: voice, video, chat, updater
|
||||
|
||||
-- Dedy Sutanto <dsutanto@backone.cloud> Tue, 30 Jun 2026 08:37:16 +0700
|
||||
zerotier-one (1.14.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 11 Sep 2024 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.14.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 19 Mar 2024 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 12 Sep 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 25 Aug 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.12.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 17 Aug 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.6) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 21 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.5) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 10 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.4) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 06 Mar 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.3) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Sat, 21 Jan 2023 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 13 Oct 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 27 Jun 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.10.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 03 Jun 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.10) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 10 May 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.9) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 25 Apr 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.8) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 11 Apr 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.7) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 21 Mar 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.6) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 07 Mar 2022 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.5) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 17 Dec 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.4) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 23 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.3) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 15 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 08 Nov 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 20 Oct 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.8.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 15 Sep 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.6) unstable; urgency=medium
|
||||
|
||||
* Backport endpoint mitigation against address collision attack.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 21 Sep 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.5) unstable; urgency=medium
|
||||
|
||||
* Fix path filtering bug that could cause "software laser" effect.
|
||||
* Fix printf overflow in CLI (not exploitable or security related)
|
||||
* Fix Windows device enumeration issue.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 13 Apr 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.4) unstable; urgency=medium
|
||||
|
||||
* REALLY fix a problem causing nodes to go into a "coma" with some network configurations.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 15 Feb 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.3-1) unstable; urgency=medium
|
||||
|
||||
* Fix a problem causing nodes to go into a "coma" with some network configurations.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 02 Feb 2021 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.2-2) unstable; urgency=medium
|
||||
|
||||
* This is a minor update to the 1.6.2 package to address issues with
|
||||
running on ARMv6 CPUs like the Raspberry Pi Zero and original v1 Pi.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 31 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.2) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 30 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.1) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 24 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.6.0) unstable; urgency=medium
|
||||
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 19 Nov 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.5.0) unstable; urgency=medium
|
||||
|
||||
* Version 1.5.0 is actually 1.6.0-beta1
|
||||
* See RELEASE-NOTES.md for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 05 Aug 2020 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.6) unstable; urgency=medium
|
||||
|
||||
* Update default root server list
|
||||
* Fix build flags on "armhf" (32-bit ARM) platforms for better
|
||||
compatibility with Pi Zero and other devices.
|
||||
* Fix license text in one.cpp.
|
||||
* Add a clarification to LICENSE.txt.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 30 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.4) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
* License changed to BSL 1.1
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 23 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.2-2) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
* This is a new build that fixes a binary build issue with containers and SELinux
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 04 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.2) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 04 Aug 2019 01:00:00 -0700
|
||||
|
||||
zerotier-one (1.4.0) unstable; urgency=medium
|
||||
|
||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||
|
||||
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 29 Jul 2019 01:00:00 -0700
|
||||
|
||||
# Older entries have been removed from this changelog.
|
||||
# To read the complete changelog use `apt changelog backone-app-tray`.
|
||||
Whitespace-only changes.
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
if [ -x /usr/lib/backone/addons/updater/backone-updater ]; then
|
||||
/usr/lib/backone/addons/updater/backone-updater rebuild-manifest || true
|
||||
elif [ -x /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh ]; then
|
||||
BACKONE_HOME=/var/lib/backone /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
#DEBHELPER#
|
||||
exit 0
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
misc:Depends=
|
||||
misc:Pre-Depends=
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
Package: backone-addon-chat
|
||||
Source: backone
|
||||
Version: 1.17.1
|
||||
Architecture: all
|
||||
Maintainer: Dedy Sutanto <dsutanto@backone.cloud>
|
||||
Installed-Size: 28
|
||||
Depends: backone, backone-addon-comms-core, backone-app-shell
|
||||
Section: net
|
||||
Priority: optional
|
||||
Homepage: https://backone.cloud
|
||||
Description: BackOne chat P2P addon
|
||||
Text chat with image and document attachments over BackOne.
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
cb73fba9ab4a23ff5f4dc1e3fa7d6520 usr/lib/backone/addons/chat/addon.json
|
||||
6020963c701ef2f1c4c4c8a78f2d6845 usr/share/doc/backone-addon-chat/changelog.gz
|
||||
463936e9518d72d5ce8f85b746e0b8b5 var/lib/backone/app/addons/chat/index.js
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
if [ -x /usr/lib/backone/addons/updater/backone-updater ]; then
|
||||
/usr/lib/backone/addons/updater/backone-updater rebuild-manifest || true
|
||||
elif [ -x /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh ]; then
|
||||
BACKONE_HOME=/var/lib/backone /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
Binary file not shown.
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
if [ -x /usr/lib/backone/addons/updater/backone-updater ]; then
|
||||
/usr/lib/backone/addons/updater/backone-updater rebuild-manifest || true
|
||||
elif [ -x /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh ]; then
|
||||
BACKONE_HOME=/var/lib/backone /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
#DEBHELPER#
|
||||
exit 0
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
if [ -x /usr/lib/backone/addons/updater/backone-updater ]; then
|
||||
/usr/lib/backone/addons/updater/backone-updater rebuild-manifest || true
|
||||
elif [ -x /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh ]; then
|
||||
BACKONE_HOME=/var/lib/backone /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
#DEBHELPER#
|
||||
exit 0
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
misc:Depends=
|
||||
misc:Pre-Depends=
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
Package: backone-addon-comms-core
|
||||
Source: backone
|
||||
Version: 1.17.1
|
||||
Architecture: amd64
|
||||
Maintainer: Dedy Sutanto <dsutanto@backone.cloud>
|
||||
Installed-Size: 15
|
||||
Depends: backone
|
||||
Recommends: postgresql-client, redis-tools
|
||||
Section: net
|
||||
Priority: optional
|
||||
Homepage: https://backone.cloud
|
||||
Description: BackOne comms core addon (signaling backend)
|
||||
Shared signaling backend for voice, video, and chat addons.
|
||||
@@ -0,0 +1,3 @@
|
||||
afd892e714b0637552f6fc56040e860a usr/lib/backone/addons/comms-core/addon.json
|
||||
51ec6efb47fa207fa716611711defd8f usr/lib/backone/addons/comms-core/schema.sql
|
||||
4b992da672501c1ed9da4a0c09d59986 usr/share/doc/backone-addon-comms-core/changelog.gz
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
if [ -x /usr/lib/backone/addons/updater/backone-updater ]; then
|
||||
/usr/lib/backone/addons/updater/backone-updater rebuild-manifest || true
|
||||
elif [ -x /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh ]; then
|
||||
BACKONE_HOME=/var/lib/backone /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
BIN
Binary file not shown.
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
if [ -x /usr/lib/backone/addons/updater/backone-updater ]; then
|
||||
/usr/lib/backone/addons/updater/backone-updater rebuild-manifest || true
|
||||
elif [ -x /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh ]; then
|
||||
BACKONE_HOME=/var/lib/backone /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
#DEBHELPER#
|
||||
exit 0
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
misc:Depends=
|
||||
misc:Pre-Depends=
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
Package: backone-addon-updater
|
||||
Source: backone
|
||||
Version: 1.17.1
|
||||
Architecture: all
|
||||
Maintainer: Dedy Sutanto <dsutanto@backone.cloud>
|
||||
Installed-Size: 28
|
||||
Depends: backone
|
||||
Section: net
|
||||
Priority: optional
|
||||
Homepage: https://backone.cloud
|
||||
Description: BackOne version updater addon
|
||||
Migrates installations between BackOne and addon versions.
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
bee18cfe55163e6f4cf1d8a22c19dfa8 usr/lib/backone/addons/updater/addon.json
|
||||
fbc9822a89457454f09b87ba35182052 usr/lib/backone/addons/updater/backone-updater
|
||||
86727de17602883708f38bdc69d68196 usr/lib/backone/addons/updater/merge-local-conf.sh
|
||||
1a944e1b0c1afdcff4b75c11f1a18ce0 usr/lib/backone/addons/updater/migrations/001_zerotier_to_backone.sh
|
||||
5f8f8cd7bba7204f08b4d1a7e7b0dd4c usr/lib/backone/addons/updater/migrations/002_register_addon_host.sh
|
||||
2fac4021a24721dee1bac76557171998 usr/lib/backone/addons/updater/migrations/003_enable_webserver_defaults.sh
|
||||
9e532d47a1b291f26457aaff04864920 usr/lib/backone/addons/updater/rebuild-addon-manifest.sh
|
||||
dbf8511a07dbd23850fe2553d78dcf6a usr/share/doc/backone-addon-updater/changelog.gz
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
if [ -x /usr/lib/backone/addons/updater/backone-updater ]; then
|
||||
/usr/lib/backone/addons/updater/backone-updater rebuild-manifest || true
|
||||
elif [ -x /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh ]; then
|
||||
BACKONE_HOME=/var/lib/backone /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
+1
@@ -0,0 +1 @@
|
||||
../lib/backone/addons/updater/backone-updater
|
||||
BIN
Binary file not shown.
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
if [ -x /usr/lib/backone/addons/updater/backone-updater ]; then
|
||||
/usr/lib/backone/addons/updater/backone-updater rebuild-manifest || true
|
||||
elif [ -x /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh ]; then
|
||||
BACKONE_HOME=/var/lib/backone /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
#DEBHELPER#
|
||||
exit 0
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
misc:Depends=
|
||||
misc:Pre-Depends=
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
Package: backone-addon-video
|
||||
Source: backone
|
||||
Version: 1.17.1
|
||||
Architecture: all
|
||||
Maintainer: Dedy Sutanto <dsutanto@backone.cloud>
|
||||
Installed-Size: 25
|
||||
Depends: backone, backone-addon-comms-core, backone-addon-voice, backone-app-shell
|
||||
Section: net
|
||||
Priority: optional
|
||||
Homepage: https://backone.cloud
|
||||
Description: BackOne video call P2P addon
|
||||
P2P video calling with ringing popup over the BackOne overlay.
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
b50ba09efcb821add4a37b940c457c92 usr/lib/backone/addons/video/addon.json
|
||||
f2cf6c14a4db3363befe75d07dca482b usr/share/doc/backone-addon-video/changelog.gz
|
||||
a589ab06e5b078b87e397c0a5e65e401 var/lib/backone/app/addons/video/index.js
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
if [ -x /usr/lib/backone/addons/updater/backone-updater ]; then
|
||||
/usr/lib/backone/addons/updater/backone-updater rebuild-manifest || true
|
||||
elif [ -x /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh ]; then
|
||||
BACKONE_HOME=/var/lib/backone /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
Binary file not shown.
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
if [ -x /usr/lib/backone/addons/updater/backone-updater ]; then
|
||||
/usr/lib/backone/addons/updater/backone-updater rebuild-manifest || true
|
||||
elif [ -x /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh ]; then
|
||||
BACKONE_HOME=/var/lib/backone /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
#DEBHELPER#
|
||||
exit 0
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
misc:Depends=
|
||||
misc:Pre-Depends=
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
Package: backone-addon-voice
|
||||
Source: backone
|
||||
Version: 1.17.1
|
||||
Architecture: all
|
||||
Maintainer: Dedy Sutanto <dsutanto@backone.cloud>
|
||||
Installed-Size: 24
|
||||
Depends: backone, backone-addon-comms-core, backone-app-shell
|
||||
Section: net
|
||||
Priority: optional
|
||||
Homepage: https://backone.cloud
|
||||
Description: BackOne voice call P2P addon
|
||||
P2P voice calling with ringing popup over the BackOne overlay.
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
cb9c4fe7a9fe5cd4748522249933cde5 usr/lib/backone/addons/voice/addon.json
|
||||
8c0d667eb292b835a170c3d46c8ce16a usr/share/doc/backone-addon-voice/changelog.gz
|
||||
3b48d55aa718993b14ebd23b6e9bceaa var/lib/backone/app/addons/voice/index.js
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
if [ -x /usr/lib/backone/addons/updater/backone-updater ]; then
|
||||
/usr/lib/backone/addons/updater/backone-updater rebuild-manifest || true
|
||||
elif [ -x /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh ]; then
|
||||
BACKONE_HOME=/var/lib/backone /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
Binary file not shown.
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
if [ -x /usr/lib/backone/addons/updater/backone-updater ]; then
|
||||
/usr/lib/backone/addons/updater/backone-updater rebuild-manifest || true
|
||||
elif [ -x /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh ]; then
|
||||
BACKONE_HOME=/var/lib/backone /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh || true
|
||||
fi
|
||||
if [ -x /usr/lib/backone/addons/updater/merge-local-conf.sh ]; then
|
||||
BACKONE_HOME=/var/lib/backone /usr/lib/backone/addons/updater/merge-local-conf.sh '{"settings":{"enableWebServer":true}}' || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
#DEBHELPER#
|
||||
exit 0
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
misc:Depends=
|
||||
misc:Pre-Depends=
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
Package: backone-app-shell
|
||||
Source: backone
|
||||
Version: 1.17.1
|
||||
Architecture: all
|
||||
Maintainer: Dedy Sutanto <dsutanto@backone.cloud>
|
||||
Installed-Size: 56
|
||||
Depends: backone
|
||||
Section: net
|
||||
Priority: optional
|
||||
Homepage: https://backone.cloud
|
||||
Description: BackOne web app shell host
|
||||
Minimal web UI host that dynamically loads separated BackOne addons.
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
ddc11210878af47483c00a896bc49c7f usr/share/doc/backone-app-shell/changelog.gz
|
||||
3d02f4314de4d3628cb193610174fcde var/lib/backone/app/addon-loader.js
|
||||
49d4fd44dcbabedd7037c9a59fb9da3a var/lib/backone/app/auth-store.js
|
||||
fc714bc78bb3127f5bf96a4aaecf9684 var/lib/backone/app/contacts-store.js
|
||||
7babf02e4ba1ca836f844acc5940d7b8 var/lib/backone/app/index.html
|
||||
802d8d5241ba3587f8ba66e986de3395 var/lib/backone/app/main.js
|
||||
07908ab7ebed73d83ce776fd5ce68e94 var/lib/backone/app/network-session.js
|
||||
2c328cb61a8c74e9c64cac2e00ca37c5 var/lib/backone/app/peer-directory.js
|
||||
b8229c43483474ac0f9eb47e756b0773 var/lib/backone/app/peer-picker.js
|
||||
dbbb653bdddf96ee5d520c9bb4ab976b var/lib/backone/app/styles.css
|
||||
99914b932bd37a50b983c5e7c90ae93b var/lib/backone/contacts.json
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
if [ -x /usr/lib/backone/addons/updater/backone-updater ]; then
|
||||
/usr/lib/backone/addons/updater/backone-updater rebuild-manifest || true
|
||||
elif [ -x /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh ]; then
|
||||
BACKONE_HOME=/var/lib/backone /usr/lib/backone/addons/updater/rebuild-addon-manifest.sh || true
|
||||
fi
|
||||
if [ -x /usr/lib/backone/addons/updater/merge-local-conf.sh ]; then
|
||||
BACKONE_HOME=/var/lib/backone /usr/lib/backone/addons/updater/merge-local-conf.sh '{"settings":{"enableWebServer":true}}' || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
Binary file not shown.
Loaded 100 of 145 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user