live-photo/server.js
callmeyan b01ce4d2c9 fix(file-upload): 优化文件上传逻辑并防止重复添加
- 更新服务器端文件上传处理逻辑,避免重复发送事件
- 客户端优化文件上传组件,防止重复添加文件
- 调整文件列表更新策略,支持文件更新
- 优化上传区域样式,提升用户体验
2025-08-01 18:13:16 +08:00

137 lines
4.2 KiB
JavaScript

const { createServer } = require('http');
const { parse } = require('url');
const next = require('next');
const { Server } = require('socket.io');
const dev = process.env.NODE_ENV !== 'production';
const hostname = 'localhost';
const port = parseInt(process.env.PORT || '3000', 10);
const socketPort = parseInt(process.env.SOCKET_PORT || '3001', 10);
// Create Next.js app
const app = next({ dev, hostname, port });
const handler = app.getRequestHandler();
// Create HTTP server for socket events
const httpServer = createServer((req, res) => {
const url = parse(req.url, true);
if (url.pathname === '/file:uploaded' && req.method === 'POST') {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
try {
const fileData = JSON.parse(body);
io.emit('file:uploaded', fileData);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true }));
} catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, error: 'Invalid data' }));
}
});
} else if (url.pathname === '/file:deleted' && req.method === 'POST') {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
try {
const data = JSON.parse(body);
io.emit('file:deleted', data);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true }));
} catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, error: 'Invalid data' }));
}
});
} else if (url.pathname === '/file:visibility:changed' && req.method === 'POST') {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
try {
const data = JSON.parse(body);
io.emit('file:visibility:changed', data);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true }));
} catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, error: 'Invalid data' }));
}
});
} else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, error: 'Not found' }));
}
});
// Create Socket.IO server
const io = new Server(httpServer, {
cors: {
origin: process.env.NEXTAUTH_URL || `http://${hostname}:${port}`,
methods: ['GET', 'POST'],
},
});
let userCount = 0;
io.on('connection', (socket) => {
userCount++;
io.emit('user:count', userCount);
console.log(`User connected. Total users: ${userCount}`);
// Handle file upload events (from client)
socket.on('file:upload', (data) => {
socket.broadcast.emit('file:uploaded', data);
console.log('File uploaded:', data.filename);
});
// Handle file delete events (from client)
socket.on('file:delete', (data) => {
socket.broadcast.emit('file:deleted', data);
console.log('File deleted:', data.id);
});
// Handle file visibility toggle events (from client)
socket.on('file:toggle:visibility', (data) => {
socket.broadcast.emit('file:visibility:changed', data);
console.log('File visibility changed:', data.id, data.isVisible);
});
socket.on('disconnect', () => {
userCount--;
io.emit('user:count', userCount);
console.log(`User disconnected. Total users: ${userCount}`);
});
});
app.prepare().then(() => {
// Start Next.js server
createServer(async (req, res) => {
try {
const parsedUrl = parse(req.url, true);
await handler(req, res, parsedUrl);
} catch (err) {
console.error('Error occurred handling', req.url, err);
res.statusCode = 500;
res.end('internal server error');
}
})
.once('error', (err) => {
console.error(err);
process.exit(1);
})
.listen(port, () => {
console.log(`> Ready on http://${hostname}:${port}`);
});
// Start Socket.IO server
httpServer.listen(socketPort, () => {
console.log(`> Socket.IO server ready on http://${hostname}:${socketPort}`);
});
});