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
+36
View File
@@ -0,0 +1,36 @@
{
"name": "chat-server",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "chat-server",
"version": "1.0.0",
"dependencies": {
"ws": "^8.16.0"
}
},
"node_modules/ws": {
"version": "8.21.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
"integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"name": "chat-server",
"version": "1.0.0",
"description": "WebSocket chat server with file sharing",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"ws": "^8.16.0"
}
}
+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);
}
});
}
+4
View File
@@ -0,0 +1,4 @@
[
{ "login": "alice", "password": "123" },
{ "login": "bob", "password": "456" }
]