This commit is contained in:
Комаров Данил Анатольевич 6
2026-05-29 04:59:58 +03:00
commit 83ef0d5ab3
28 changed files with 3027 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
const fs = require('fs');
const path = require('path');
const { WebSocketServer } = require('ws');
const PORT = parseInt(process.env.PORT, 10) || 8080;
const MAX_FILE_SIZE = parseInt(process.env.MAX_FILE_SIZE, 10) || 10 * 1024 * 1024;
const usersPath = path.join(__dirname, process.env.USERS_FILE || 'users.json');
let validUsers;
try {
validUsers = JSON.parse(fs.readFileSync(usersPath, 'utf-8'));
console.log(`Loaded ${validUsers.length} users from users.json`);
} catch (err) {
console.error('Failed to load users.json:', err.message);
process.exit(1);
}
function authenticate(login, password) {
return validUsers.find(u => u.login === login && u.password === password) || null;
}
const wss = new WebSocketServer({ port: PORT });
console.log(`WebSocket server running on ws://localhost:${PORT}`);
wss.on('connection', (ws, req) => {
const clientIp = req.socket.remoteAddress;
console.log(`New connection from ${clientIp}`);
let authenticated = false;
let userLogin = null;
ws.on('message', (data) => {
let parsed;
try {
parsed = JSON.parse(data.toString());
} catch (e) {
ws.send(JSON.stringify({ type: 'error', message: 'Invalid JSON' }));
return;
}
if (!authenticated) {
if (parsed.type === 'auth') {
const user = authenticate(parsed.login, parsed.password);
if (user) {
authenticated = true;
userLogin = parsed.login;
console.log(`User authenticated: ${userLogin}`);
ws.send(JSON.stringify({ type: 'auth_result', success: true }));
broadcast({
type: 'system',
text: `${userLogin} joined the chat`
}, null);
} else {
console.log(`Auth failed for login: ${parsed.login}`);
ws.send(JSON.stringify({
type: 'auth_result',
success: false,
reason: 'Invalid login or password'
}));
}
} else {
ws.send(JSON.stringify({ type: 'error', message: 'Authenticate first' }));
}
return;
}
if (parsed.type === 'text') {
if (!parsed.text || typeof parsed.text !== 'string') {
ws.send(JSON.stringify({ type: 'error', message: 'Invalid text message' }));
return;
}
console.log(`Text from ${userLogin}: ${parsed.text.substring(0, 50)}`);
broadcast({
type: 'text',
from: userLogin,
timestamp: Date.now(),
text: parsed.text
}, null);
return;
}
if (parsed.type === 'file') {
if (!parsed.filename || !parsed.mime || !parsed.data) {
ws.send(JSON.stringify({ type: 'error', message: 'Invalid file message' }));
return;
}
const size = Math.ceil((parsed.data.length * 3) / 4);
if (size > MAX_FILE_SIZE) {
ws.send(JSON.stringify({ type: 'error', message: 'File too large (max 10 MB)' }));
return;
}
console.log(`File from ${userLogin}: ${parsed.filename} (${parsed.mime}, ~${size} bytes)`);
broadcast({
type: 'file',
from: userLogin,
timestamp: Date.now(),
filename: parsed.filename,
mime: parsed.mime,
data: parsed.data
}, null);
return;
}
ws.send(JSON.stringify({ type: 'error', message: 'Unknown message type' }));
});
ws.on('close', (code, reason) => {
console.log(`Connection closed${userLogin ? ` for ${userLogin}` : ''} (code: ${code})`);
if (userLogin) {
broadcast({
type: 'system',
text: `${userLogin} left the chat`
}, null);
}
});
ws.on('error', (err) => {
console.error(`WebSocket error${userLogin ? ` for ${userLogin}` : ''}:`, err.message);
});
});
function broadcast(message, excludeWs) {
const json = JSON.stringify(message);
wss.clients.forEach(client => {
if (client !== excludeWs && client.readyState === 1) {
client.send(json);
}
});
}