-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathserver.ts
172 lines (148 loc) · 4.97 KB
/
server.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import { createServer, IncomingMessage, ServerResponse } from 'http'
import { Server } from 'socket.io'
const allowedOrigins = [
process.env.FRONTEND_URL || 'http://localhost:3000',
'https://co-coding-c6wn.vercel.app',
]
const port = process.env.PORT || 3001
const server = createServer((req: IncomingMessage, res: ServerResponse) => {
const origin = req.headers.origin
if (req.url === '/health') {
res.writeHead(200, { 'Content-Type': 'text/plain' })
res.end('OK')
return
}
if (origin && allowedOrigins.indexOf(origin) !== -1) {
res.setHeader('Access-Control-Allow-Origin', origin)
res.setHeader('Access-Control-Allow-Methods', 'GET, POST')
res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
} else {
res.writeHead(403, { 'Content-Type': 'text/plain' })
res.end('Not allowed by CORS')
return
}
res.writeHead(404, { 'Content-Type': 'text/plain' })
res.end('Not Found')
})
// ws
const io = new Server(server, {
cors: {
origin: allowedOrigins,
methods: ['GET', 'POST'],
},
})
interface Settings {
switchTrigger: string
cursorAtEnd: boolean
}
interface Room {
users: number[]
maxUsers: number
content: string
editingUser: number | null
lastEditPosition: number
settings: Settings
lastSwitchTime: number
}
const rooms = new Map<string, Room>()
export function arrNext<T>(arr: T[], current: T): T {
// 获取当前元素的索引
const currentIndex = arr.indexOf(current)
// 如果元素不在数组中,返回数组第一个元素
if (currentIndex === -1) {
return arr[0]
}
// 如果是最后一个元素,返回第一个元素
if (currentIndex === arr.length - 1) {
return arr[0]
}
// 返回下一个元素
return arr[currentIndex + 1]
}
io.on('connection', (socket) => {
let currentRoom: string | null = null
let userId: number | null = null
socket.on('joinRoom', (roomId: string) => {
console.log('joinRoom', roomId)
if (!rooms.has(roomId)) {
rooms.set(roomId, {
users: [],
maxUsers: 999,
content: '',
editingUser: 1,
lastEditPosition: 0,
settings: {
switchTrigger: '[ \n]',
cursorAtEnd: true,
},
lastSwitchTime: Date.now(), // 初始化切换时间戳
})
}
const room = rooms.get(roomId)!
if (room.users.length < room.maxUsers) {
if (room.users.length === 0) {
userId = 1
room.editingUser = 1
} else {
userId = room.users[room.users.length - 1] + 1
}
room.users.push(userId)
socket.join(roomId)
currentRoom = roomId
socket.emit('userId', userId)
socket.emit('initialContent', room.content)
io.to(roomId).emit('setEditingUser', room.editingUser)
} else {
socket.emit('roomFull')
}
})
socket.on(
'contentChange',
({ roomId, content, cursorPosition, newChar }) => {
const room = rooms.get(roomId)
if (room && room.editingUser === userId) {
room.content = content
socket.to(roomId).emit('contentChange', content)
const now = Date.now()
// 确保距离上次切换至少200毫秒
if (
newChar &&
now - room.lastSwitchTime >= 200 &&
new RegExp(room.settings.switchTrigger).test(newChar)
) {
room.lastSwitchTime = now // 更新切换时间戳
room.editingUser = arrNext(room.users, userId)
io.to(roomId).emit('setEditingUser', room.editingUser)
}
}
},
)
socket.on('requestEditPermission', (roomId) => {
const room = rooms.get(roomId)
if (room && room.editingUser !== userId) {
room.editingUser = userId
room.lastEditPosition = room.content.length
io.to(roomId).emit('setEditingUser', userId)
}
})
socket.on('disconnect', () => {
if (currentRoom) {
const room = rooms.get(currentRoom)
if (room) {
const nextUser = arrNext(room.users, userId)
room.users = room.users.filter((i) => i !== userId)
if (room.users.length === 0) {
rooms.delete(currentRoom)
socket.leave(currentRoom)
} else {
room.editingUser = nextUser
room.lastEditPosition = 0
io.to(currentRoom).emit('setEditingUser', nextUser)
}
}
}
})
})
server.listen(port, () => {
console.log(`> Backend ready on http://localhost:${port}`)
})