Initial Commit
This commit is contained in:
110
server/socket.ts
Normal file
110
server/socket.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { Server as SocketIOServer, Socket } from 'socket.io';
|
||||
import { upsertPixels, getAllPixels, db } from './database';
|
||||
import { Cursor, Pixel } from '../shared/types';
|
||||
import passport from 'passport';
|
||||
|
||||
function generateRandomColor(): string {
|
||||
const colors = [
|
||||
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7',
|
||||
'#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E2'
|
||||
];
|
||||
return colors[Math.floor(Math.random() * colors.length)];
|
||||
}
|
||||
|
||||
interface UserSocket extends Socket {
|
||||
userId: string;
|
||||
username: string;
|
||||
color: string;
|
||||
user?: any;
|
||||
}
|
||||
|
||||
// Helper function to get user data from db
|
||||
function getUserById(userId: number) {
|
||||
try {
|
||||
const stmt = db.prepare('SELECT * FROM users WHERE id = ?');
|
||||
return stmt.get(userId);
|
||||
} catch (error) {
|
||||
console.error('Error fetching user:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function setupSocketIO(io: SocketIOServer): void {
|
||||
const connectedUsers = new Map<string, { x: number; y: number; color: string; username: string; userId: number }>();
|
||||
|
||||
io.on('connection', (socket: Socket) => {
|
||||
const userSocket = socket as UserSocket;
|
||||
|
||||
socket.on('join-canvas', (token: string) => {
|
||||
try {
|
||||
const decoded = JSON.parse(Buffer.from(token, 'base64').toString());
|
||||
|
||||
if (Date.now() - decoded.timestamp > 5 * 60 * 1000) {
|
||||
socket.emit('error', 'Token expired');
|
||||
socket.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
const userData = getUserById(decoded.userId);
|
||||
if (userData) {
|
||||
userSocket.user = userData;
|
||||
const userId = `auth_${userData.id}`;
|
||||
const username = userData.username || userData.display_name;
|
||||
const color = generateRandomColor();
|
||||
|
||||
connectedUsers.set(userId, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
color: color,
|
||||
username: username,
|
||||
userId: userData.id.toString()
|
||||
});
|
||||
|
||||
socket.emit('canvas-state', getAllPixels());
|
||||
socket.emit('cursor-update', Array.from(connectedUsers.values()));
|
||||
socket.emit('auth-success', { user: userData });
|
||||
|
||||
socket.broadcast.emit('cursor-update', Array.from(connectedUsers.values()));
|
||||
|
||||
socket.on('cursor-move', (cursor: Cursor) => {
|
||||
const user = connectedUsers.get(userId);
|
||||
if (user) {
|
||||
user.x = cursor.x;
|
||||
user.y = cursor.y;
|
||||
socket.broadcast.emit('cursor-update', Array.from(connectedUsers.values()));
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('paint-pixels', async (pixels: Pixel[]) => {
|
||||
try {
|
||||
if (!userSocket.user) {
|
||||
socket.emit('error', 'Authentication required to paint pixels');
|
||||
return;
|
||||
}
|
||||
|
||||
upsertPixels(pixels);
|
||||
socket.broadcast.emit('canvas-update', { pixels });
|
||||
} catch (error) {
|
||||
console.error('Error saving pixels:', error);
|
||||
socket.emit('error', 'Failed to save pixels');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
connectedUsers.delete(userId);
|
||||
socket.broadcast.emit('cursor-update', Array.from(connectedUsers.values()));
|
||||
});
|
||||
|
||||
} else {
|
||||
console.error('User not found for token:', decoded.userId);
|
||||
socket.emit('error', 'Invalid user');
|
||||
socket.disconnect();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Token verification failed:', error);
|
||||
socket.emit('error', 'Invalid token');
|
||||
socket.disconnect();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user