mirror of
https://github.com/streamwall/streamwall.git
synced 2025-12-06 01:45:37 -05:00
Fix desyncs when multiple clients connected
This commit is contained in:
@@ -29,10 +29,19 @@ const base62 = baseX(
|
|||||||
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||||
)
|
)
|
||||||
|
|
||||||
function rand62(len: number) {
|
export function rand62(len: number) {
|
||||||
return base62.encode(randomBytes(len))
|
return base62.encode(randomBytes(len))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function uniqueRand62(len: number, map: Map<string, unknown>) {
|
||||||
|
let val = rand62(len)
|
||||||
|
while (map.has(val)) {
|
||||||
|
// Regenerate in case of a collision
|
||||||
|
val = rand62(len)
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
async function hashToken62(token: string, salt: string) {
|
async function hashToken62(token: string, salt: string) {
|
||||||
const hashBuffer = await scrypt(token, salt, 24)
|
const hashBuffer = await scrypt(token, salt, 24)
|
||||||
return base62.encode(hashBuffer as Buffer)
|
return base62.encode(hashBuffer as Buffer)
|
||||||
@@ -162,12 +171,7 @@ export class Auth extends EventEmitter<AuthEvents> {
|
|||||||
throw new Error(`invalid role: ${role}`)
|
throw new Error(`invalid role: ${role}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
let tokenId = rand62(8)
|
const tokenId = uniqueRand62(8, this.tokensById)
|
||||||
while (this.tokensById.has(tokenId)) {
|
|
||||||
// Regenerate in case of an id collision
|
|
||||||
tokenId = rand62(8)
|
|
||||||
}
|
|
||||||
|
|
||||||
const secret = rand62(24)
|
const secret = rand62(24)
|
||||||
const tokenHash = await hashToken62(secret, this.salt)
|
const tokenHash = await hashToken62(secret, this.salt)
|
||||||
const tokenData = {
|
const tokenData = {
|
||||||
|
|||||||
@@ -16,12 +16,13 @@ import {
|
|||||||
stateDiff,
|
stateDiff,
|
||||||
type StreamwallRole,
|
type StreamwallRole,
|
||||||
} from 'streamwall-shared'
|
} from 'streamwall-shared'
|
||||||
import { Auth, StateWrapper } from './auth.ts'
|
import { Auth, StateWrapper, uniqueRand62 } from './auth.ts'
|
||||||
import { loadStorage, type StorageDB } from './storage.ts'
|
import { loadStorage, type StorageDB } from './storage.ts'
|
||||||
|
|
||||||
export const SESSION_COOKIE_NAME = 's'
|
export const SESSION_COOKIE_NAME = 's'
|
||||||
|
|
||||||
interface Client {
|
interface Client {
|
||||||
|
clientId: string
|
||||||
ws: WebSocket
|
ws: WebSocket
|
||||||
lastStateSent: any
|
lastStateSent: any
|
||||||
identity: AuthTokenInfo
|
identity: AuthTokenInfo
|
||||||
@@ -248,7 +249,7 @@ async function initApp({ baseURL, clientStaticPath }: AppOptions) {
|
|||||||
console.error('Failed to send Streamwall doc update')
|
console.error('Failed to send Streamwall doc update')
|
||||||
}
|
}
|
||||||
for (const client of clients.values()) {
|
for (const client of clients.values()) {
|
||||||
if (client.identity.tokenId === origin) {
|
if (client.clientId === origin) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -299,25 +300,39 @@ async function initApp({ baseURL, clientStaticPath }: AppOptions) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clientId = uniqueRand62(8, clients)
|
||||||
const client: Client = {
|
const client: Client = {
|
||||||
|
clientId,
|
||||||
ws,
|
ws,
|
||||||
lastStateSent: null,
|
lastStateSent: null,
|
||||||
identity,
|
identity,
|
||||||
}
|
}
|
||||||
clients.set(identity.tokenId, client)
|
clients.set(clientId, client)
|
||||||
|
|
||||||
const pingInterval = setInterval(() => {
|
const pingInterval = setInterval(() => {
|
||||||
ws.ping()
|
ws.ping()
|
||||||
}, 20 * 1000)
|
}, 20 * 1000)
|
||||||
|
|
||||||
ws.on('close', () => {
|
ws.on('close', () => {
|
||||||
clients.delete(identity.tokenId)
|
clients.delete(clientId)
|
||||||
clearInterval(pingInterval)
|
clearInterval(pingInterval)
|
||||||
|
|
||||||
console.log('Client disconnected from', request.ip, client.identity)
|
console.log(
|
||||||
|
'Client',
|
||||||
|
clientId,
|
||||||
|
'disconnected from',
|
||||||
|
request.ip,
|
||||||
|
client.identity,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('Client connected from', request.ip, client.identity)
|
console.log(
|
||||||
|
'Client',
|
||||||
|
clientId,
|
||||||
|
'connected from',
|
||||||
|
request.ip,
|
||||||
|
client.identity,
|
||||||
|
)
|
||||||
|
|
||||||
handleMessage(async (rawData) => {
|
handleMessage(async (rawData) => {
|
||||||
let msg: ControlCommandMessage
|
let msg: ControlCommandMessage
|
||||||
@@ -350,7 +365,7 @@ async function initApp({ baseURL, clientStaticPath }: AppOptions) {
|
|||||||
Y.applyUpdate(
|
Y.applyUpdate(
|
||||||
streamwallConn.stateDoc,
|
streamwallConn.stateDoc,
|
||||||
new Uint8Array(rawData),
|
new Uint8Array(rawData),
|
||||||
identity.tokenId,
|
clientId,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user