Skip to content

Commit ebb2ea7

Browse files
committed
fix: protocol compatibility fixes
1 parent 3cc8ba8 commit ebb2ea7

4 files changed

Lines changed: 70 additions & 22 deletions

File tree

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,5 +188,3 @@ bun run --filter=@apps/server deploy
188188
- **No event size limit:** Events larger than 10 KB are not rejected. Pusher returns HTTP `413` for oversized events. Large WebSocket messages may hit Cloudflare frame limits.
189189

190190
- **No `auth_timestamp` clock skew check:** The REST API authentication middleware does not validate that the `auth_timestamp` query parameter falls within ±600 seconds of the current time. This means signed requests can be replayed indefinitely.
191-
192-
- **`info` parameter not supported on event triggers:** The `POST /events` and `POST /batch_events` endpoints do not return channel attributes (`user_count`, `subscription_count`) when the `info` query parameter is provided.

apps/server/src/api/routes/apps.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,29 @@ app.post(
148148

149149
await stub.broadcast(payload)
150150

151+
if (payload.info) {
152+
const requested = payload.info.split(',').map((s) => s.trim())
153+
const includeUserCount = requested.includes('user_count')
154+
const includeSubscriptionCount = requested.includes('subscription_count')
155+
156+
const channels =
157+
payload.channels ?? (payload.channel ? [payload.channel] : [])
158+
const result: Record<string, Record<string, number>> = {}
159+
160+
for (const channel of channels) {
161+
const occ = await stub.getChannel(channel)
162+
const attrs: Record<string, number> = {}
163+
if (occ) {
164+
if (includeUserCount) attrs.user_count = occ.user_count
165+
if (includeSubscriptionCount)
166+
attrs.subscription_count = occ.subscription_count
167+
}
168+
result[channel] = attrs
169+
}
170+
171+
return c.json({ channels: result }, 200)
172+
}
173+
151174
return c.json({}, 200)
152175
},
153176
)
@@ -167,7 +190,27 @@ app.post(
167190

168191
await stub.broadcast(payload)
169192

170-
return c.json({ batch: payload.batch.map(() => ({})) }, 200)
193+
const batchResponses = await Promise.all(
194+
payload.batch.map(async (item) => {
195+
if (!item.info) return {}
196+
197+
const requested = item.info.split(',').map((s) => s.trim())
198+
const includeUserCount = requested.includes('user_count')
199+
const includeSubscriptionCount =
200+
requested.includes('subscription_count')
201+
202+
const occ = await stub.getChannel(item.channel)
203+
const attrs: Record<string, number> = {}
204+
if (occ) {
205+
if (includeUserCount) attrs.user_count = occ.user_count
206+
if (includeSubscriptionCount)
207+
attrs.subscription_count = occ.subscription_count
208+
}
209+
return attrs
210+
}),
211+
)
212+
213+
return c.json({ batch: batchResponses }, 200)
171214
},
172215
)
173216

apps/server/src/api/schemas/apps.ts

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,27 @@ export const querySchema = z.object({
88
body_md5: z.string().optional(),
99
})
1010

11-
export const eventSchema = z.object({
12-
name: z.string().nonempty(),
13-
channels: z.array(z.string()).nonempty().optional(),
14-
channel: z.string().nonempty().optional(),
15-
data: z.string(),
16-
socket_id: z.string().optional(),
17-
}).refine((data) => {
18-
if (!data.channels && !data.channel) {
19-
throw new z.ZodError([
20-
{
21-
code: z.ZodIssueCode.custom,
22-
message: 'Either channels or channel is required',
23-
path: ['channels'],
24-
},
25-
])
26-
}
27-
return true
28-
})
11+
export const eventSchema = z
12+
.object({
13+
name: z.string().nonempty(),
14+
channels: z.array(z.string()).nonempty().optional(),
15+
channel: z.string().nonempty().optional(),
16+
data: z.string(),
17+
socket_id: z.string().optional(),
18+
info: z.string().optional(),
19+
})
20+
.refine((data) => {
21+
if (!data.channels && !data.channel) {
22+
throw new z.ZodError([
23+
{
24+
code: 'custom',
25+
message: 'Either channels or channel is required',
26+
path: ['channels'],
27+
},
28+
])
29+
}
30+
return true
31+
})
2932

3033
export type Event = z.infer<typeof eventSchema>
3134

@@ -37,6 +40,7 @@ export const batchEventSchema = z.object({
3740
channel: z.string().nonempty(),
3841
data: z.string(),
3942
socket_id: z.string().optional(),
43+
info: z.string().optional(),
4044
}),
4145
)
4246
.nonempty()

apps/server/src/handlers/ws-handler.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ export class WebSocketHandler {
4848
} else if (this.isClientEvent(event)) {
4949
await this.handleClientEvent(ws, { event, channel, data })
5050
} else {
51-
console.log('Message event handler not implemented.', event)
51+
this.connections.sendTo(ws, {
52+
event: 'pusher:error',
53+
data: { code: 4009, message: 'Event not supported' },
54+
})
5255
}
5356
} catch (e) {
5457
console.error('Failed to handle WebSocket message:', e)

0 commit comments

Comments
 (0)