Refactor: Initial code split into includes directory for modularity
- Reorganized backend logic by moving API, authentication, Docker, status, and WebSocket handling into separate modules (api.js, auth.js, docker.js, status.js, websocket.js) within ./includes/ - Converted codebase to ES modules with import/export syntax for modern JavaScript - Updated index.js to serve as main entry point, importing from ./includes/ - Reduced code duplication and improved readability with modularized functions - Ensured full functionality preservation, including Docker stats and WebSocket communication - Updated README to reflect new folder structure and ES module setup
This commit is contained in:
667
includes/websocket.js
Normal file
667
includes/websocket.js
Normal file
@ -0,0 +1,667 @@
|
||||
import { URLSearchParams } from 'url';
|
||||
import { getContainerStats, streamContainerLogs, readServerProperties, writeServerProperties } from './docker.js';
|
||||
import { checkConnectionStatus, checkGeyserStatus, checkSftpStatus } from './status.js';
|
||||
import { apiRequest } from './api.js';
|
||||
|
||||
const clients = new Map();
|
||||
const staticEndpoints = ['log', 'website', 'map', 'my-link-cache', 'my-geyser-cache', 'my-sftp-cache', 'my-link', 'my-geyser-link', 'my-sftp'];
|
||||
const dynamicEndpoints = ['hello', 'time', 'mod-list'];
|
||||
|
||||
async function fetchAndSendUpdate(ws, endpoint, client, docker) {
|
||||
if (['mod-list', 'list-players'].includes(endpoint) && client.user !== 'Unknown') {
|
||||
try {
|
||||
const container = docker.getContainer(client.user);
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status !== 'running') {
|
||||
ws.send(JSON.stringify({ type: endpoint, error: `Container ${client.user} is not running` }));
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
ws.send(JSON.stringify({ type: endpoint, error: `Failed to check container status: ${error.message}` }));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (endpoint === 'time' && client.cache['time']) {
|
||||
ws.send(JSON.stringify({ type: endpoint, data: client.cache['time'] }));
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await apiRequest(`/${endpoint}`, client.apiKey);
|
||||
if (!response.error) {
|
||||
if (endpoint === 'time') client.cache['time'] = response;
|
||||
if (endpoint === 'my-link-cache') {
|
||||
client.cache['my-link-cache'] = response;
|
||||
if (client.subscriptions.has('my-link-cache') && client.user !== 'Unknown') {
|
||||
try {
|
||||
const container = docker.getContainer(client.user);
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status === 'running' && response.hostname && response.port) {
|
||||
const status = await checkConnectionStatus(response.hostname, response.port);
|
||||
ws.send(JSON.stringify({ type: 'connection-status', data: { isOnline: status.isOnline } }));
|
||||
} else {
|
||||
ws.send(JSON.stringify({ type: 'connection-status', error: `Container ${client.user} is not running` }));
|
||||
}
|
||||
} catch (error) {
|
||||
ws.send(JSON.stringify({ type: 'connection-status', error: `Failed to check container status: ${error.message}` }));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (endpoint === 'my-geyser-cache') {
|
||||
client.cache['my-geyser-cache'] = response;
|
||||
if (client.subscriptions.has('my-geyser-cache') && client.user !== 'Unknown') {
|
||||
try {
|
||||
const container = docker.getContainer(client.user);
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status === 'running' && response.hostname && response.port) {
|
||||
const status = await checkGeyserStatus(response.hostname, response.port);
|
||||
ws.send(JSON.stringify({ type: 'geyser-status', data: { isOnline: status.isOnline } }));
|
||||
} else {
|
||||
ws.send(JSON.stringify({ type: 'geyser-status', error: `Container ${client.user} is not running` }));
|
||||
}
|
||||
} catch (error) {
|
||||
ws.send(JSON.stringify({ type: 'geyser-status', error: `Failed to check container status: ${error.message}` }));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (endpoint === 'my-sftp-cache') {
|
||||
client.cache['my-sftp-cache'] = response;
|
||||
if (client.subscriptions.has('my-sftp-cache') && client.user !== 'Unknown') {
|
||||
try {
|
||||
const container = docker.getContainer(client.user);
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status === 'running' && response.hostname && response.port) {
|
||||
const status = await checkSftpStatus(response.hostname, response.port);
|
||||
ws.send(JSON.stringify({ type: 'sftp-status', data: { isOnline: status.isOnline } }));
|
||||
} else {
|
||||
ws.send(JSON.stringify({ type: 'sftp-status', error: `Container ${client.user} is not running` }));
|
||||
}
|
||||
} catch (error) {
|
||||
ws.send(JSON.stringify({ type: 'sftp-status', error: `Failed to check container status: ${error.message}` }));
|
||||
}
|
||||
}
|
||||
}
|
||||
ws.send(JSON.stringify({ type: endpoint, data: response }));
|
||||
} else {
|
||||
ws.send(JSON.stringify({ type: endpoint, error: response.error }));
|
||||
}
|
||||
}
|
||||
|
||||
async function manageStatusChecks(ws, client, user, docker) {
|
||||
try {
|
||||
const container = docker.getContainer(user);
|
||||
const inspect = await container.inspect();
|
||||
const isRunning = inspect.State.Status === 'running';
|
||||
|
||||
client.intervals.forEach(clearInterval);
|
||||
client.intervals = [];
|
||||
['connectionStatusInterval', 'geyserStatusInterval', 'sftpStatusInterval', 'statusCheckMonitorInterval'].forEach((key) => {
|
||||
if (client[key]) clearInterval(client[key]);
|
||||
client[key] = null;
|
||||
});
|
||||
|
||||
if (!isRunning || user === 'Unknown') {
|
||||
['my-link-cache', 'my-geyser-cache', 'my-sftp-cache'].forEach((sub) => {
|
||||
if (client.subscriptions.has(sub)) {
|
||||
ws.send(JSON.stringify({ type: sub.replace('-cache', '-status'), error: `Container ${user} is not running or user unknown` }));
|
||||
}
|
||||
});
|
||||
if (!isRunning && (client.subscriptions.has('my-link-cache') || client.subscriptions.has('my-geyser-cache') || client.subscriptions.has('my-sftp-cache')) && user !== 'Unknown') {
|
||||
console.log(`Starting container status monitor for ${user}`);
|
||||
client.statusCheckMonitorInterval = setInterval(async () => {
|
||||
try {
|
||||
const monitorContainer = docker.getContainer(user);
|
||||
const monitorInspect = await monitorContainer.inspect();
|
||||
if (monitorInspect.State.Status === 'running') {
|
||||
console.log(`Container ${user} is running, restarting status checks`);
|
||||
await manageStatusChecks(ws, client, user, docker);
|
||||
clearInterval(client.statusCheckMonitorInterval);
|
||||
client.statusCheckMonitorInterval = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error monitoring container ${user}:`, error.message);
|
||||
}
|
||||
}, parseInt(process.env.CONTAINER_STATUS_MONITOR_INTERVAL_MS, 10));
|
||||
client.intervals.push(client.statusCheckMonitorInterval);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const statusChecks = [
|
||||
{
|
||||
subscription: 'my-link-cache',
|
||||
intervalKey: 'connectionStatusInterval',
|
||||
intervalMs: process.env.CONNECTION_STATUS_INTERVAL_MS,
|
||||
checkFn: checkConnectionStatus,
|
||||
cacheKey: 'my-link-cache',
|
||||
statusType: 'connection-status'
|
||||
},
|
||||
{
|
||||
subscription: 'my-geyser-cache',
|
||||
intervalKey: 'geyserStatusInterval',
|
||||
intervalMs: process.env.GEYSER_STATUS_INTERVAL_MS,
|
||||
checkFn: checkGeyserStatus,
|
||||
cacheKey: 'my-geyser-cache',
|
||||
statusType: 'geyser-status'
|
||||
},
|
||||
{
|
||||
subscription: 'my-sftp-cache',
|
||||
intervalKey: 'sftpStatusInterval',
|
||||
intervalMs: process.env.SFTP_STATUS_INTERVAL_MS,
|
||||
checkFn: checkSftpStatus,
|
||||
cacheKey: 'my-sftp-cache',
|
||||
statusType: 'sftp-status'
|
||||
}
|
||||
];
|
||||
|
||||
for (const { subscription, intervalKey, intervalMs, checkFn, cacheKey, statusType } of statusChecks) {
|
||||
if (client.subscriptions.has(subscription)) {
|
||||
console.log(`Starting ${statusType} check for ${user}`);
|
||||
client[intervalKey] = setInterval(async () => {
|
||||
try {
|
||||
const containerCheck = docker.getContainer(user);
|
||||
const inspectCheck = await containerCheck.inspect();
|
||||
if (inspectCheck.State.Status !== 'running') {
|
||||
console.log(`Container ${user} stopped, clearing ${statusType} interval`);
|
||||
clearInterval(client[intervalKey]);
|
||||
client[intervalKey] = null;
|
||||
return;
|
||||
}
|
||||
const data = client.cache[cacheKey];
|
||||
if (data && data.hostname && data.port) {
|
||||
const status = await checkFn(data.hostname, data.port);
|
||||
ws.send(JSON.stringify({ type: statusType, data: { isOnline: status.isOnline } }));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error in ${statusType} check for ${user}:`, error.message);
|
||||
ws.send(JSON.stringify({ type: statusType, data: { isOnline: false, error: error.message } }));
|
||||
}
|
||||
}, parseInt(intervalMs, 10));
|
||||
client.intervals.push(client[intervalKey]);
|
||||
|
||||
const data = client.cache[cacheKey];
|
||||
if (data && data.hostname && data.port) {
|
||||
console.log(`Performing initial ${statusType} check for ${user}`);
|
||||
const status = await checkFn(data.hostname, data.port);
|
||||
ws.send(JSON.stringify({ type: statusType, data: { isOnline: status.isOnline } }));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error managing status checks for ${user}:`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
export function handleWebSocket(ws, req, docker) {
|
||||
const urlParams = new URLSearchParams(req.url.split('?')[1]);
|
||||
const apiKey = urlParams.get('apiKey');
|
||||
if (!apiKey) {
|
||||
console.error('WebSocket connection rejected: Missing API key');
|
||||
ws.send(JSON.stringify({ error: 'API key required' }));
|
||||
ws.close();
|
||||
return;
|
||||
}
|
||||
|
||||
const client = {
|
||||
apiKey,
|
||||
subscriptions: new Set(),
|
||||
user: null,
|
||||
intervals: [],
|
||||
logStream: null,
|
||||
cache: {},
|
||||
connectionStatusInterval: null,
|
||||
geyserStatusInterval: null,
|
||||
sftpStatusInterval: null,
|
||||
statusCheckMonitorInterval: null
|
||||
};
|
||||
clients.set(ws, client);
|
||||
console.log('WebSocket client registered with API key');
|
||||
|
||||
ws.on('message', async (message) => {
|
||||
try {
|
||||
const data = JSON.parse(message.toString());
|
||||
if (data.type === 'subscribe') {
|
||||
data.endpoints.forEach(endpoint => {
|
||||
client.subscriptions.add(endpoint);
|
||||
console.log(`Client subscribed to ${endpoint}`);
|
||||
});
|
||||
console.log(`Client subscriptions: ${Array.from(client.subscriptions)}`);
|
||||
|
||||
let hello = client.cache['hello'] || await apiRequest('/hello', client.apiKey);
|
||||
if (!client.cache['hello'] && !hello.error) client.cache['hello'] = hello;
|
||||
|
||||
if (hello.error) {
|
||||
console.error('Failed to fetch /hello:', hello.error);
|
||||
ws.send(JSON.stringify({ type: 'hello', error: hello.error }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (hello.message && typeof hello.message === 'string') {
|
||||
const user = hello.message.split(', ')[1]?.replace('!', '').trim() || 'Unknown';
|
||||
client.user = user;
|
||||
console.log(`User identified: ${user}`);
|
||||
ws.send(JSON.stringify({ type: 'hello', data: hello }));
|
||||
|
||||
if (client.subscriptions.has('docker') && user !== 'Unknown') {
|
||||
try {
|
||||
const container = docker.getContainer(user);
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status === 'running') {
|
||||
console.log(`Starting docker stats interval for ${user}`);
|
||||
client.intervals.push(setInterval(async () => {
|
||||
try {
|
||||
console.log(`Fetching docker stats for ${user}`);
|
||||
const stats = await getContainerStats(docker, user);
|
||||
if (stats.error) {
|
||||
console.error(`Error fetching stats for ${user}: ${stats.error}`);
|
||||
ws.send(JSON.stringify({ type: 'docker', error: stats.error }));
|
||||
} else {
|
||||
console.log(`Sending docker stats for ${user}:`, stats);
|
||||
ws.send(JSON.stringify({ type: 'docker', data: { ...stats, user } }));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error in docker stats interval for ${user}:`, error.message);
|
||||
ws.send(JSON.stringify({ type: 'docker', error: `Failed to fetch stats: ${error.message}` }));
|
||||
}
|
||||
}, parseInt(process.env.DOCKER_STATS_INTERVAL_MS, 10)));
|
||||
|
||||
// Send initial stats immediately
|
||||
console.log(`Sending initial docker stats for ${user}`);
|
||||
const initialStats = await getContainerStats(docker, user);
|
||||
ws.send(JSON.stringify({ type: 'docker', data: { ...initialStats, user } }));
|
||||
} else {
|
||||
console.log(`Container ${user} is not running, skipping docker stats interval`);
|
||||
ws.send(JSON.stringify({ type: 'docker', error: `Container ${user} is not running` }));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error checking container status for docker stats for ${user}:`, error.message);
|
||||
ws.send(JSON.stringify({ type: 'docker', error: `Failed to check container status: ${error.message}` }));
|
||||
}
|
||||
} else if (user === 'Unknown') {
|
||||
console.warn('Cannot start docker stats interval: User is Unknown');
|
||||
ws.send(JSON.stringify({ type: 'docker', error: 'User not identified' }));
|
||||
}
|
||||
|
||||
if (client.subscriptions.has('docker-logs') && user !== 'Unknown') {
|
||||
console.log(`Starting docker logs stream for ${user}`);
|
||||
await streamContainerLogs(docker, ws, user, client);
|
||||
} else if (user === 'Unknown') {
|
||||
console.warn('Cannot start docker logs stream: User is Unknown');
|
||||
ws.send(JSON.stringify({ type: 'docker-logs', error: 'User not identified' }));
|
||||
}
|
||||
|
||||
await manageStatusChecks(ws, client, user, docker);
|
||||
|
||||
await Promise.all([
|
||||
...staticEndpoints.filter(e => client.subscriptions.has(e)).map(e => fetchAndSendUpdate(ws, e, client, docker)),
|
||||
...dynamicEndpoints.filter(e => client.subscriptions.has(e)).map(async (e) => {
|
||||
if (e === 'hello' && client.cache['hello']) {
|
||||
ws.send(JSON.stringify({ type: 'hello', data: client.cache['hello'] }));
|
||||
return;
|
||||
}
|
||||
if (e === 'time' && client.cache['time']) {
|
||||
ws.send(JSON.stringify({ type: 'time', data: client.cache['time'] }));
|
||||
return;
|
||||
}
|
||||
await fetchAndSendUpdate(ws, e, client, docker);
|
||||
}),
|
||||
client.subscriptions.has('list-players') ? fetchAndSendUpdate(ws, 'list-players', client, docker) : null
|
||||
].filter(Boolean));
|
||||
|
||||
client.intervals.push(setInterval(async () => {
|
||||
try {
|
||||
for (const endpoint of dynamicEndpoints) {
|
||||
if (client.subscriptions.has(endpoint) && !(endpoint === 'hello' && client.cache['hello'] || endpoint === 'time' && client.cache['time'])) {
|
||||
await fetchAndSendUpdate(ws, endpoint, client, docker);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in dynamic endpoints interval:', error.message);
|
||||
}
|
||||
}, parseInt(process.env.DYNAMIC_ENDPOINTS_INTERVAL_MS, 10)));
|
||||
|
||||
client.intervals.push(setInterval(async () => {
|
||||
try {
|
||||
for (const endpoint of staticEndpoints) {
|
||||
if (client.subscriptions.has(endpoint)) {
|
||||
await fetchAndSendUpdate(ws, endpoint, client, docker);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in static endpoints interval:', error.message);
|
||||
}
|
||||
}, parseInt(process.env.STATIC_ENDPOINTS_INTERVAL_MS, 10)));
|
||||
|
||||
if (client.subscriptions.has('list-players') && user !== 'Unknown') {
|
||||
try {
|
||||
const container = docker.getContainer(user);
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status === 'running') {
|
||||
console.log(`Starting list-players interval for ${user}`);
|
||||
client.intervals.push(setInterval(() => fetchAndSendUpdate(ws, 'list-players', client, docker), parseInt(process.env.LIST_PLAYERS_INTERVAL_MS, 10)));
|
||||
} else {
|
||||
console.log(`Container ${user} is not running, skipping list-players interval`);
|
||||
ws.send(JSON.stringify({ type: 'list-players', error: `Container ${user} is not running` }));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error checking container status for list-players for ${user}:`, error.message);
|
||||
ws.send(JSON.stringify({ type: 'list-players', error: `Failed to check container status: ${error.message}` }));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error('Invalid /hello response:', hello);
|
||||
ws.send(JSON.stringify({ type: 'hello', error: 'Invalid hello response' }));
|
||||
}
|
||||
} else if (data.type === 'updateUser') {
|
||||
client.user = data.user;
|
||||
console.log(`Updated user to: ${client.user}`);
|
||||
if (client.user !== 'Unknown') {
|
||||
client.intervals.forEach(clearInterval);
|
||||
client.intervals = [];
|
||||
['connectionStatusInterval', 'geyserStatusInterval', 'sftpStatusInterval', 'statusCheckMonitorInterval'].forEach((key) => {
|
||||
if (client[key]) clearInterval(client[key]);
|
||||
client[key] = null;
|
||||
});
|
||||
|
||||
if (client.subscriptions.has('docker')) {
|
||||
try {
|
||||
const container = docker.getContainer(client.user);
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status === 'running') {
|
||||
console.log(`Starting docker stats interval for new user ${client.user}`);
|
||||
client.intervals.push(setInterval(async () => {
|
||||
try {
|
||||
console.log(`Fetching docker stats for ${client.user}`);
|
||||
const stats = await getContainerStats(docker, client.user);
|
||||
if (stats.error) {
|
||||
console.error(`Error fetching stats for ${client.user}: ${stats.error}`);
|
||||
ws.send(JSON.stringify({ type: 'docker', error: stats.error }));
|
||||
} else {
|
||||
console.log(`Sending docker stats for ${client.user}:`, stats);
|
||||
ws.send(JSON.stringify({ type: 'docker', data: { ...stats, user: client.user } }));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error in docker stats interval for ${client.user}:`, error.message);
|
||||
ws.send(JSON.stringify({ type: 'docker', error: `Failed to fetch stats: ${error.message}` }));
|
||||
}
|
||||
}, parseInt(process.env.DOCKER_STATS_INTERVAL_MS, 10)));
|
||||
|
||||
// Send initial stats immediately
|
||||
console.log(`Sending initial docker stats for ${client.user}`);
|
||||
const initialStats = await getContainerStats(docker, client.user);
|
||||
ws.send(JSON.stringify({ type: 'docker', data: { ...initialStats, user: client.user } }));
|
||||
} else {
|
||||
console.log(`Container ${client.user} is not running, skipping docker stats interval`);
|
||||
ws.send(JSON.stringify({ type: 'docker', error: `Container ${client.user} is not running` }));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error checking container status for docker stats for ${client.user}:`, error.message);
|
||||
ws.send(JSON.stringify({ type: 'docker', error: `Failed to check container status: ${error.message}` }));
|
||||
}
|
||||
}
|
||||
|
||||
if (client.subscriptions.has('list-players')) {
|
||||
try {
|
||||
const container = docker.getContainer(client.user);
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status === 'running') {
|
||||
console.log(`Starting list-players interval for new user ${client.user}`);
|
||||
client.intervals.push(setInterval(() => fetchAndSendUpdate(ws, 'list-players', client, docker), parseInt(process.env.LIST_PLAYERS_NEW_USER_INTERVAL_MS, 10)));
|
||||
} else {
|
||||
console.log(`Container ${client.user} is not running, skipping list-players interval`);
|
||||
ws.send(JSON.stringify({ type: 'list-players', error: `Container ${client.user} is not running` }));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error checking container status for list-players for ${client.user}:`, error.message);
|
||||
ws.send(JSON.stringify({ type: 'list-players', error: `Failed to check container status: ${error.message}` }));
|
||||
}
|
||||
}
|
||||
|
||||
await manageStatusChecks(ws, client, client.user, docker);
|
||||
|
||||
if (client.subscriptions.has('docker-logs')) {
|
||||
if (client.logStream) {
|
||||
client.logStream.destroy();
|
||||
client.logStream = null;
|
||||
}
|
||||
console.log(`Starting docker logs stream for new user ${client.user}`);
|
||||
await streamContainerLogs(docker, ws, client.user, client);
|
||||
}
|
||||
}
|
||||
} else if (data.type === 'request') {
|
||||
const { requestId, endpoint, method, body } = data;
|
||||
let response;
|
||||
if (endpoint.startsWith('/docker') || endpoint === '/docker') {
|
||||
response = client.user === 'Unknown' ? { error: 'User not identified' } : await getContainerStats(docker, client.user);
|
||||
console.log(`Docker stats request response for ${client.user}:`, response);
|
||||
} else if (endpoint === '/search' && method === 'POST' && body) {
|
||||
response = await apiRequest(endpoint, client.apiKey, method, body);
|
||||
response.totalResults = response.totalResults || (response.results ? response.results.length : 0);
|
||||
} else if (endpoint === '/server-properties' && method === 'GET') {
|
||||
response = client.user === 'Unknown' ? { error: 'User not identified' } : await readServerProperties(docker, client.user);
|
||||
} else if (endpoint === '/server-properties' && method === 'POST' && body && body.content) {
|
||||
response = client.user === 'Unknown' ? { error: 'User not identified' } : await writeServerProperties(docker, client.user, body.content);
|
||||
} else {
|
||||
response = await apiRequest(endpoint, client.apiKey, method, body);
|
||||
}
|
||||
ws.send(JSON.stringify({ requestId, ...response }));
|
||||
if (['my-link', 'my-geyser-link', 'my-sftp'].includes(endpoint) && !response.error) {
|
||||
await fetchAndSendUpdate(ws, endpoint, client, docker);
|
||||
if (endpoint === 'my-link') {
|
||||
const linkData = await apiRequest('/my-link-cache', client.apiKey);
|
||||
if (!linkData.error) {
|
||||
client.cache['my-link-cache'] = linkData;
|
||||
try {
|
||||
const container = docker.getContainer(client.user);
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status === 'running') {
|
||||
console.log(`Performing status check after my-link request for ${client.user}`);
|
||||
const status = await checkConnectionStatus(linkData.hostname, linkData.port);
|
||||
ws.send(JSON.stringify({ type: 'connection-status', data: { isOnline: status.isOnline } }));
|
||||
} else {
|
||||
ws.send(JSON.stringify({ type: 'connection-status', error: `Container ${client.user} is not running` }));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error checking container status for ${client.user}:`, error.message);
|
||||
ws.send(JSON.stringify({ type: 'connection-status', error: `Failed to check container status: ${error.message}` }));
|
||||
}
|
||||
}
|
||||
} else if (endpoint === 'my-geyser-link') {
|
||||
const geyserData = await apiRequest('/my-geyser-cache', client.apiKey);
|
||||
if (!geyserData.error) {
|
||||
client.cache['my-geyser-cache'] = geyserData;
|
||||
try {
|
||||
const container = docker.getContainer(client.user);
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status === 'running') {
|
||||
console.log(`Performing status check after my-geyser-link request for ${client.user}`);
|
||||
const status = await checkGeyserStatus(geyserData.hostname, geyserData.port);
|
||||
ws.send(JSON.stringify({ type: 'geyser-status', data: { isOnline: status.isOnline } }));
|
||||
} else {
|
||||
ws.send(JSON.stringify({ type: 'geyser-status', error: `Container ${client.user} is not running` }));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error checking container status for ${client.user}:`, error.message);
|
||||
ws.send(JSON.stringify({ type: 'geyser-status', error: `Failed to check container status: ${error.message}` }));
|
||||
}
|
||||
}
|
||||
} else if (endpoint === 'my-sftp') {
|
||||
const sftpData = await apiRequest('/my-sftp-cache', client.apiKey);
|
||||
if (!sftpData.error) {
|
||||
client.cache['my-sftp-cache'] = sftpData;
|
||||
try {
|
||||
const container = docker.getContainer(client.user);
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status === 'running') {
|
||||
console.log(`Performing status check after my-sftp request for ${client.user}`);
|
||||
const status = await checkSftpStatus(sftpData.hostname, sftpData.port);
|
||||
ws.send(JSON.stringify({ type: 'sftp-status', data: { isOnline: status.isOnline } }));
|
||||
} else {
|
||||
ws.send(JSON.stringify({ type: 'sftp-status', error: `Container ${client.user} is not running` }));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error checking container status for ${client.user}:`, error.message);
|
||||
ws.send(JSON.stringify({ type: 'sftp-status', error: `Failed to check container status: ${error.message}` }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (['kick-player', 'ban-player', 'op-player', 'deop-player'].includes(data.type)) {
|
||||
const { requestId, player } = data;
|
||||
if (!player) {
|
||||
ws.send(JSON.stringify({ requestId, error: 'Player name is required' }));
|
||||
return;
|
||||
}
|
||||
if (client.user === 'Unknown') {
|
||||
ws.send(JSON.stringify({ requestId, error: 'User not identified' }));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const container = docker.getContainer(client.user);
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status !== 'running') {
|
||||
ws.send(JSON.stringify({ requestId, error: `Container ${client.user} is not running` }));
|
||||
return;
|
||||
}
|
||||
const command = {
|
||||
'kick-player': `kick ${player}`,
|
||||
'ban-player': `ban ${player}`,
|
||||
'op-player': `op ${player}`,
|
||||
'deop-player': `deop ${player}`
|
||||
}[data.type];
|
||||
const response = await apiRequest('/console', client.apiKey, 'POST', { command });
|
||||
if (!response.error) {
|
||||
const playerListResponse = await apiRequest('/list-players', client.apiKey);
|
||||
if (!playerListResponse.error) {
|
||||
ws.send(JSON.stringify({ type: 'list-players', data: playerListResponse }));
|
||||
}
|
||||
}
|
||||
ws.send(JSON.stringify({ requestId, ...response }));
|
||||
} catch (error) {
|
||||
console.error(`Error processing ${data.type} for ${player}:`, error.message);
|
||||
ws.send(JSON.stringify({ requestId, error: `Failed to process command: ${error.message}` }));
|
||||
}
|
||||
} else if (data.type === 'tell-player') {
|
||||
const { requestId, player, message } = data;
|
||||
if (!player || !message) {
|
||||
ws.send(JSON.stringify({ requestId, error: 'Player name and message are required' }));
|
||||
return;
|
||||
}
|
||||
if (client.user === 'Unknown') {
|
||||
ws.send(JSON.stringify({ requestId, error: 'User not identified' }));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const container = docker.getContainer(client.user);
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status !== 'running') {
|
||||
ws.send(JSON.stringify({ requestId, error: `Container ${client.user} is not running` }));
|
||||
return;
|
||||
}
|
||||
const response = await apiRequest('/tell', client.apiKey, 'POST', { username: player, message });
|
||||
ws.send(JSON.stringify({ requestId, ...response }));
|
||||
} catch (error) {
|
||||
console.error(`Error sending message to ${player}:`, error.message);
|
||||
ws.send(JSON.stringify({ requestId, error: `Failed to send message: ${error.message}` }));
|
||||
}
|
||||
} else if (data.type === 'give-player') {
|
||||
const { requestId, player, item, amount } = data;
|
||||
if (!player || !item || !amount) {
|
||||
ws.send(JSON.stringify({ requestId, error: 'Player name, item, and amount are required' }));
|
||||
return;
|
||||
}
|
||||
if (client.user === 'Unknown') {
|
||||
ws.send(JSON.stringify({ requestId, error: 'User not identified' }));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const container = docker.getContainer(client.user);
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status !== 'running') {
|
||||
ws.send(JSON.stringify({ requestId, error: `Container ${client.user} is not running` }));
|
||||
return;
|
||||
}
|
||||
const response = await apiRequest('/give', client.apiKey, 'POST', { username: player, item, amount });
|
||||
ws.send(JSON.stringify({ requestId, ...response }));
|
||||
} catch (error) {
|
||||
console.error(`Error giving item to ${player}:`, error.message);
|
||||
ws.send(JSON.stringify({ requestId, error: `Failed to give item: ${error.message}` }));
|
||||
}
|
||||
} else if (data.type === 'refresh') {
|
||||
console.log('Processing refresh request');
|
||||
delete client.cache['hello'];
|
||||
delete client.cache['time'];
|
||||
await Promise.all([
|
||||
...staticEndpoints.filter(e => client.subscriptions.has(e)).map(e => fetchAndSendUpdate(ws, e, client, docker)),
|
||||
...dynamicEndpoints.filter(e => client.subscriptions.has(e)).map(e => fetchAndSendUpdate(ws, e, client, docker)),
|
||||
client.subscriptions.has('list-players') ? fetchAndSendUpdate(ws, 'list-players', client, docker) : null
|
||||
].filter(Boolean));
|
||||
if (client.user && client.user !== 'Unknown') {
|
||||
try {
|
||||
const stats = await getContainerStats(docker, client.user);
|
||||
console.log(`Sending refreshed docker stats for ${client.user}:`, stats);
|
||||
ws.send(JSON.stringify({ type: 'docker', data: { ...stats, user: client.user } }));
|
||||
const container = docker.getContainer(client.user);
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status === 'running') {
|
||||
const linkData = client.cache['my-link-cache'];
|
||||
if (linkData && linkData.hostname && linkData.port && client.subscriptions.has('my-link-cache')) {
|
||||
console.log(`Performing refresh connection status check for ${client.user}`);
|
||||
const status = await checkConnectionStatus(linkData.hostname, linkData.port);
|
||||
ws.send(JSON.stringify({ type: 'connection-status', data: { isOnline: status.isOnline } }));
|
||||
}
|
||||
const geyserData = client.cache['my-geyser-cache'];
|
||||
if (geyserData && geyserData.hostname && geyserData.port && client.subscriptions.has('my-geyser-cache')) {
|
||||
console.log(`Performing refresh Geyser status check for ${client.user}`);
|
||||
const status = await checkGeyserStatus(geyserData.hostname, geyserData.port);
|
||||
ws.send(JSON.stringify({ type: 'geyser-status', data: { isOnline: status.isOnline } }));
|
||||
}
|
||||
const sftpData = client.cache['my-sftp-cache'];
|
||||
if (sftpData && sftpData.hostname && sftpData.port && client.subscriptions.has('my-sftp-cache')) {
|
||||
console.log(`Performing refresh SFTP status check for ${client.user}`);
|
||||
const status = await checkSftpStatus(sftpData.hostname, sftpData.port);
|
||||
ws.send(JSON.stringify({ type: 'sftp-status', data: { isOnline: status.isOnline } }));
|
||||
}
|
||||
} else {
|
||||
if (client.subscriptions.has('my-link-cache')) {
|
||||
ws.send(JSON.stringify({ type: 'connection-status', error: `Container ${client.user} is not running` }));
|
||||
}
|
||||
if (client.subscriptions.has('my-geyser-cache')) {
|
||||
ws.send(JSON.stringify({ type: 'geyser-status', error: `Container ${client.user} is not running` }));
|
||||
}
|
||||
if (client.subscriptions.has('my-sftp-cache')) {
|
||||
ws.send(JSON.stringify({ type: 'sftp-status', error: `Container ${client.user} is not running` }));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error during refresh for ${client.user}:`, error.message);
|
||||
ws.send(JSON.stringify({ type: 'docker', error: `Failed to refresh stats: ${error.message}` }));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('WebSocket message error:', error.message);
|
||||
ws.send(JSON.stringify({ error: `Invalid message: ${error.message}` }));
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('close', () => {
|
||||
try {
|
||||
const client = clients.get(ws);
|
||||
client.intervals.forEach(clearInterval);
|
||||
if (client.logStream) {
|
||||
client.logStream.destroy();
|
||||
client.logStream = null;
|
||||
}
|
||||
['connectionStatusInterval', 'geyserStatusInterval', 'sftpStatusInterval', 'statusCheckMonitorInterval'].forEach((key) => {
|
||||
if (client[key]) clearInterval(client[key]);
|
||||
});
|
||||
clients.delete(ws);
|
||||
console.log('WebSocket client disconnected');
|
||||
} catch (error) {
|
||||
console.error('Error on WebSocket close:', error.message);
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('error', (error) => console.error('WebSocket error:', error.message));
|
||||
}
|
Reference in New Issue
Block a user