feat: production-ready release v1.0.0

- Integrated 'Koala-Bridge' for seamless web-to-extension communication.
- Implemented dedicated, minimalist invitation page (join.html).
- Added brute-force protection and RAM-cleanup to the relay server.
- Removed external fonts and trackers from the landing page (Privacy First).
- Unified header/footer design across all website pages.
- Added MIT License and comprehensive legal documentation (Impressum/Datenschutz).
This commit is contained in:
MacBook
2026-04-21 08:46:28 +02:00
parent 8b02593f40
commit 3e2e9ed7a5
15 changed files with 757 additions and 12 deletions
+31
View File
@@ -191,9 +191,30 @@ function handleServerEvent(event, data) {
currentRoom = data;
addLog(`Joined Room: ${data.roomId}`, 'success');
chrome.runtime.sendMessage({ type: 'PEER_UPDATE', peers: data.peers }).catch(() => {});
// Inform Website Bridge
chrome.tabs.query({}, (tabs) => {
tabs.forEach(tab => {
chrome.tabs.sendMessage(tab.id, { type: 'JOIN_STATUS', success: true, message: 'Joined' }).catch(() => {});
});
});
break;
case EVENTS.ROOM_LIST:
chrome.runtime.sendMessage({ type: 'ROOM_LIST', rooms: data.rooms }).catch(() => {});
break;
case EVENTS.ERROR:
addLog(`Server Error: ${data.message}`, 'error');
chrome.notifications.create({
type: 'basic',
iconUrl: 'icons/icon128.png',
title: 'KoalaSync Error',
message: data.message
});
// Inform Website Bridge
chrome.tabs.query({}, (tabs) => {
tabs.forEach(tab => {
chrome.tabs.sendMessage(tab.id, { type: 'JOIN_STATUS', success: false, message: data.message }).catch(() => {});
});
});
break;
case EVENTS.PLAY:
case EVENTS.PAUSE:
@@ -327,6 +348,16 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
sendResponse(logs);
} else if (message.type === 'GET_HISTORY') {
sendResponse(history);
} else if (message.type === 'GET_ROOM_LIST') {
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(`42${JSON.stringify([EVENTS.GET_ROOMS])}`);
}
} else if (message.type === 'WEB_JOIN_REQUEST') {
const { roomId, password } = message;
chrome.storage.sync.set({ roomId, password }, () => {
connect();
// We wait for status update in handleServerEvent
});
} else if (message.type === 'CONTENT_EVENT') {
if (sender.tab) {
currentTabId = sender.tab.id;
+31
View File
@@ -0,0 +1,31 @@
/**
* KoalaSync Bridge Script
* Injected into koalasync.shik3i.net to facilitate communication between
* the landing page and the extension.
*/
// 1. Signal presence to the website
document.documentElement.dataset.koalasyncInstalled = 'true';
// 2. Listen for Join Requests from the Website
window.addEventListener('KOALASYNC_JOIN_REQUEST', (e) => {
const { roomId, password } = e.detail;
chrome.runtime.sendMessage({
type: 'WEB_JOIN_REQUEST',
roomId,
password
});
});
// 3. Listen for Status Updates from the Extension and relay to Website
chrome.runtime.onMessage.addListener((msg) => {
if (msg.type === 'JOIN_STATUS') {
const event = new CustomEvent('KOALASYNC_STATUS', {
detail: {
success: msg.success,
message: msg.message
}
});
window.dispatchEvent(event);
}
});
+5
View File
@@ -35,6 +35,11 @@
],
"run_at": "document_idle",
"all_frames": false
},
{
"matches": ["https://koalasync.shik3i.net/*"],
"js": ["bridge.js"],
"run_at": "document_start"
}
],
"icons": {
+11 -1
View File
@@ -222,7 +222,17 @@
<label>Password (Optional)</label>
<input type="password" id="password" placeholder="Room password">
</div>
<button id="joinBtn" class="primary">Join / Create Room</button>
<div id="roomError" style="display:none; color:var(--error); font-size:11px; margin-bottom:8px; text-align:center;"></div>
<button id="joinBtn" class="primary">Join Room</button>
<button id="createRoomBtn" class="secondary" style="border: 1px solid var(--accent); color: var(--accent);">Create Random Room</button>
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 1.5rem; margin-bottom: 8px;">
<label style="margin:0;">Public Rooms</label>
<button id="refreshRooms" style="background:transparent; border:none; color:var(--accent); font-size:10px; cursor:pointer;">REFRESH</button>
</div>
<div id="publicRooms" class="info-card" style="max-height: 120px; overflow-y: auto; padding: 4px;">
<div style="text-align:center; color: var(--text-muted); font-size: 11px; padding: 10px;">Refreshing...</div>
</div>
<div id="roomInfo" style="display:none; margin-top: 20px;">
<label>Invite Link</label>
+93 -3
View File
@@ -1,4 +1,4 @@
import { EVENTS } from './shared/constants.js';
import { EVENTS, OFFICIAL_LANDING_PAGE_URL } from './shared/constants.js';
import { BLACKLIST_DOMAINS } from './shared/blacklist.js';
const elements = {
@@ -23,7 +23,11 @@ const elements = {
inviteLink: document.getElementById('inviteLink'),
filterNoise: document.getElementById('filterNoise'),
historyList: document.getElementById('historyList'),
copyLogs: document.getElementById('copyLogs')
copyLogs: document.getElementById('copyLogs'),
createRoomBtn: document.getElementById('createRoomBtn'),
publicRooms: document.getElementById('publicRooms'),
refreshRooms: document.getElementById('refreshRooms'),
roomError: document.getElementById('roomError')
};
// --- Initialization ---
@@ -55,6 +59,12 @@ async function init() {
updatePeerList(res.peers);
}
});
// Check for invite link on landing page
checkInviteLink();
// Initial room list fetch
chrome.runtime.sendMessage({ type: 'GET_ROOM_LIST' });
}
// --- UI Logic ---
@@ -62,7 +72,8 @@ function updateUI(roomId, password) {
const inRoom = !!roomId;
elements.roomInfo.style.display = inRoom ? 'block' : 'none';
if (inRoom) {
elements.inviteLink.value = `${roomId}${password ? '#' + password : ''}`;
const invite = `${OFFICIAL_LANDING_PAGE_URL}/join.html#join:${roomId}:${password}`;
elements.inviteLink.value = invite;
}
}
@@ -168,6 +179,43 @@ function refreshHistory() {
});
}
function updateRoomList(rooms) {
if (!rooms || rooms.length === 0) {
elements.publicRooms.innerHTML = '<div style="text-align:center; padding: 10px; color:var(--text-muted);">No active rooms</div>';
return;
}
elements.publicRooms.innerHTML = rooms.map(r => `
<div class="room-item" style="display:flex; justify-content:space-between; align-items:center; padding: 8px; border-bottom: 1px solid rgba(255,255,255,0.05); cursor:pointer;" data-id="${r.id}">
<span style="font-weight:600;">${r.id}</span>
<span style="font-size:11px; color:var(--accent)">${r.peerCount} peers</span>
</div>
`).join('');
elements.publicRooms.querySelectorAll('.room-item').forEach(item => {
item.addEventListener('click', () => {
elements.roomId.value = item.dataset.id;
elements.password.value = '';
elements.password.focus();
});
});
}
function checkInviteLink() {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const tab = tabs[0];
if (tab && tab.url && tab.url.includes(OFFICIAL_LANDING_PAGE_URL) && tab.url.includes('#join:')) {
const parts = tab.url.split('#join:')[1].split(':');
if (parts.length >= 2) {
elements.roomId.value = parts[0];
elements.password.value = parts[1];
// Visual feedback
elements.joinBtn.style.boxShadow = '0 0 15px var(--accent)';
setTimeout(() => elements.joinBtn.style.boxShadow = '', 2000);
}
}
});
}
function setServerMode(custom) {
elements.serverOfficial.classList.toggle('active', !custom);
elements.serverCustom.classList.toggle('active', custom);
@@ -194,6 +242,28 @@ elements.tabs.forEach(btn => {
});
});
function showError(msg) {
if (!elements.roomError) return;
elements.roomError.textContent = msg;
elements.roomError.style.display = 'block';
elements.roomId.style.borderColor = 'var(--error)';
elements.password.style.borderColor = 'var(--error)';
// Shake effect
document.getElementById('tab-room').animate([
{ transform: 'translateX(0)' },
{ transform: 'translateX(-5px)' },
{ transform: 'translateX(5px)' },
{ transform: 'translateX(0)' }
], { duration: 200, iterations: 2 });
setTimeout(() => {
if (elements.roomError) elements.roomError.style.display = 'none';
elements.roomId.style.borderColor = '';
elements.password.style.borderColor = '';
}, 5000);
}
// --- Action Handlers ---
elements.joinBtn.addEventListener('click', async () => {
const serverUrl = elements.serverUrl.value;
@@ -216,6 +286,22 @@ elements.leaveBtn.addEventListener('click', async () => {
updateUI(null, null);
});
elements.createRoomBtn.addEventListener('click', () => {
const animals = ['koala', 'panda', 'tiger', 'eagle', 'fox', 'bear'];
const adj = ['happy', 'cool', 'fast', 'smart', 'brave', 'calm'];
const id = `${adj[Math.floor(Math.random() * adj.length)]}-${animals[Math.floor(Math.random() * animals.length)]}-${Math.floor(Math.random() * 100)}`;
const pass = Math.random().toString(36).substring(2, 8);
elements.roomId.value = id;
elements.password.value = pass;
elements.joinBtn.click();
});
elements.refreshRooms.addEventListener('click', () => {
elements.publicRooms.innerHTML = '<div style="text-align:center; padding: 10px; color:var(--text-muted);">Refreshing...</div>';
chrome.runtime.sendMessage({ type: 'GET_ROOM_LIST' });
});
elements.targetTab.addEventListener('change', async () => {
await chrome.storage.sync.set({ targetTabId: elements.targetTab.value });
});
@@ -270,6 +356,10 @@ chrome.runtime.onMessage.addListener((msg) => {
applyConnectionStatus(msg.status);
} else if (msg.type === 'HISTORY_UPDATE') {
updateHistory(msg.history);
} else if (msg.type === 'ROOM_LIST') {
updateRoomList(msg.rooms);
} else if (msg.type === 'LOG_UPDATE' && msg.log && msg.log.type === 'error') {
showError(msg.log.message);
}
});