mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-08-24 17:56:27 +00:00
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:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user