This commit is contained in:
Raven Scott
2025-12-17 20:05:50 -05:00
commit 742e27d3f7
276 changed files with 89838 additions and 0 deletions
+159
View File
@@ -0,0 +1,159 @@
/**
* Global Profile Plugin - WebSocket Management
*
* Handles WebSocket connections, broadcasting, and real-time updates
*/
const sdk = require('../../includes/plugins/sdk');
const { getProfileFromDB, getAllProfilesFromDB } = require('./database');
/**
* Broadcast profile update to WebSocket clients and all peers
*/
function broadcastProfileUpdate(data) {
const message = {
...data,
timestamp: Date.now()
};
sdk.log.debug('global.profile', `Broadcasting profile update: ${data.type} for peer ${data.profile?.peerId || data.peerId}`);
// Broadcast to local WebSocket clients
const localClients = sdk.websocket.broadcast(message);
sdk.log.debug('global.profile', `Sent to ${localClients} local WebSocket client(s)`);
// Broadcast to all connected P2P peers via channels
// Peers will forward to their local WebSocket clients
try {
const peerCount = sdk.channels.broadcast('profile-updates', message);
if (peerCount > 0) {
sdk.log.debug('global.profile', `Sent to ${peerCount} peer(s) via P2P channels`);
}
} catch (err) {
sdk.log.debug('global.profile', `Error broadcasting to peers: ${err.message}`);
}
}
/**
* Get replication status for HyperDB (avatars are stored in HyperDB)
*/
function getReplicationStatus() {
const status = {
hyperdb: { active: false, peers: 0 }
};
try {
// Get HyperDB replication status
const dbReplication = sdk.db.replication;
if (dbReplication) {
const dbStatus = dbReplication.getStatus();
if (dbStatus) {
status.hyperdb = {
active: dbStatus.active,
peers: dbStatus.peers || 0
};
}
}
} catch (err) {
sdk.log.debug('global.profile', `Error getting HyperDB replication status: ${err.message}`);
}
// Avatars are now stored in HyperDB, so no separate Hyperdrive status needed
return status;
}
/**
* Setup WebSocket handlers
*/
function setupWebSocketHandlers() {
// Initialize WebSocket server
if (!sdk.websocket.initialize()) {
sdk.log.warn('global.profile', 'Failed to initialize WebSocket server');
return;
}
// Track previous replication status to detect changes
let previousReplicationStatus = JSON.stringify(getReplicationStatus());
sdk.websocket.on('connection', async (ws) => {
const clientCount = sdk.websocket.getClientCount();
sdk.log.info('global.profile', `WebSocket client connected (${clientCount} total)`);
// Initial profile data is now loaded via REST API
// WebSocket is only used for real-time updates (profile-update, profile-deleted)
// The request-profiles handler below is kept for backward compatibility
});
sdk.websocket.on('message', async (ws, message) => {
try {
if (message.type === 'request-profiles') {
try {
const profiles = await getAllProfilesFromDB();
sdk.websocket.send(ws, {
type: 'profiles',
profiles: profiles.map(p => {
try {
p.customFields = p.customFields ? JSON.parse(p.customFields) : {};
p.tags = p.tags ? JSON.parse(p.tags) : [];
} catch (err) {
p.customFields = {};
p.tags = [];
}
p.online = sdk.state.peerIds.includes(p.peerId);
return p;
}),
timestamp: Date.now()
});
} catch (err) {
sdk.log.error('global.profile', `Error getting profiles for WebSocket: ${err.message}`);
sdk.websocket.send(ws, {
type: 'profiles',
profiles: [],
timestamp: Date.now(),
error: 'Database not ready'
});
}
} else if (message.type === 'request-replication-status') {
// Send current replication status
const replicationStatus = getReplicationStatus();
sdk.websocket.send(ws, {
type: 'replication-status',
data: replicationStatus,
timestamp: Date.now()
});
}
} catch (err) {
sdk.log.error('global.profile', `Error handling WebSocket message: ${err.message}`);
}
});
// Broadcast replication status updates periodically
setInterval(() => {
const clientCount = sdk.websocket.getClientCount();
if (clientCount > 0) {
const currentStatus = getReplicationStatus();
const currentStatusStr = JSON.stringify(currentStatus);
// Only broadcast if status changed
if (currentStatusStr !== previousReplicationStatus) {
const sent = sdk.websocket.broadcast({
type: 'replication-status',
data: currentStatus,
timestamp: Date.now()
});
previousReplicationStatus = currentStatusStr;
sdk.log.debug('global.profile', `Broadcasted replication status update to ${sent} client(s)`);
}
}
}, 2000); // Check every 2 seconds
}
module.exports = {
broadcastProfileUpdate,
getReplicationStatus,
setupWebSocketHandlers
};