most-box 0.2.9 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/out/admin/index.html +0 -0
  2. package/out/app/index.html +0 -0
  3. package/out/assets/{FilePreviewOverlay-BYZIAMJ-.js → FilePreviewOverlay-CM5IqnZD.js} +1 -1
  4. package/out/assets/{MarketingHeader-ceWdRCpe.js → MarketingHeader-B45AaM29.js} +1 -1
  5. package/out/assets/{MarketingLayout-Bun1d2wL.js → MarketingLayout-A00pEOa2.js} +1 -1
  6. package/out/assets/{MilkdownEditor-CT6SHYKG.js → MilkdownEditor-BTq9bDAf.js} +1 -1
  7. package/out/assets/{OpenSidebarButton-CLSCB7CG.js → OpenSidebarButton-D8qfUGzU.js} +1 -1
  8. package/out/assets/{SidebarAccount-CNh0kvtB.js → SidebarAccount-_RBE5K_o.js} +1 -1
  9. package/out/assets/{SidebarHomeLink-DXv0c9Ob.js → SidebarHomeLink-qMFEGjZt.js} +1 -1
  10. package/out/assets/{downloadValidation-Bzl7cci9.js → downloadValidation-CSXwoQKW.js} +1 -1
  11. package/out/assets/index-BRhsWNj1.css +1 -0
  12. package/out/assets/{index-BI8oNSVC.js → index-C-_ZwxuN.js} +2 -2
  13. package/out/assets/index-CYVRmppz.css +1 -0
  14. package/out/assets/index.lazy-0YYV0CR3.js +1 -0
  15. package/out/assets/index.lazy-BTYGr9Vb.js +1 -0
  16. package/out/assets/{index.lazy-BOMHingE.js → index.lazy-BVAtQtTa.js} +1 -1
  17. package/out/assets/{index.lazy-ur0joIFl.js → index.lazy-BbdLA2L-.js} +1 -1
  18. package/out/assets/{index.lazy-bAp_Hgck.js → index.lazy-CbiLtziY.js} +1 -1
  19. package/out/assets/{index.lazy-D4zNn3D6.js → index.lazy-CcEdBg3i.js} +2 -2
  20. package/out/assets/index.lazy-CePHsLXG.js +1 -0
  21. package/out/assets/{index.lazy-Ch4Y17pJ.js → index.lazy-DG_3_VdF.js} +1 -1
  22. package/out/assets/{index.lazy-7omTRykl.js → index.lazy-DR0rWEWZ.js} +1 -1
  23. package/out/assets/{index.lazy-BSY5_gby.js → index.lazy-DSyTSCJD.js} +1 -1
  24. package/out/assets/{index.lazy-C4ipXKZ7.js → index.lazy-DUhsuPLv.js} +1 -1
  25. package/out/assets/{index.lazy-_mEd0Q_u.js → index.lazy-DVnhWbzH.js} +1 -1
  26. package/out/assets/{index.lazy-CPZbEnJZ.js → index.lazy-DZpkg1DJ.js} +1 -1
  27. package/out/assets/useChannelMessages-DOaWHdAP.js +4 -0
  28. package/out/assets/{useGameRoom-cs9YpYt7.js → useGameRoom-hHkV65ii.js} +1 -1
  29. package/out/assets/{userProfile-qcc60tHK.js → userProfile-DwZip0Dt.js} +1 -1
  30. package/out/chat/index.html +0 -0
  31. package/out/chat/join/index.html +0 -0
  32. package/out/download/index.html +2 -2
  33. package/out/game/gandengyan/index.html +0 -0
  34. package/out/game/index.html +0 -0
  35. package/out/game/zhajinhua/index.html +0 -0
  36. package/out/index.html +2 -2
  37. package/out/note/index.html +0 -0
  38. package/out/ping/index.html +2 -2
  39. package/out/profile/index.html +0 -0
  40. package/out/web3/index.html +0 -0
  41. package/package.json +1 -1
  42. package/server/index.js +59 -2
  43. package/server/src/http/app.js +7 -1
  44. package/server/src/http/nodeStatus.js +6 -0
  45. package/server/src/http/routes/channelRoutes.js +12 -0
  46. package/server/src/index.js +476 -3
  47. package/out/assets/index-3OD3Chi9.css +0 -1
  48. package/out/assets/index-BSOvFG3o.css +0 -1
  49. package/out/assets/index.lazy-CvBIIYbm.js +0 -1
  50. package/out/assets/index.lazy-Cz-OZwYL.js +0 -1
  51. package/out/assets/index.lazy-DRY3xoQp.js +0 -1
  52. package/out/assets/useChannelMessages-BeKF0gwo.js +0 -3
package/server/index.js CHANGED
@@ -137,6 +137,9 @@ function bindEngineEvents({
137
137
  engine.on('channel:peer:offline', data =>
138
138
  wsBroadcast('channel:peer:offline', data)
139
139
  )
140
+ engine.on('channel:presence', data =>
141
+ wsSendToChannel(data.channelKey, 'channel:presence', data)
142
+ )
140
143
  engine.on('channel:joined', data => wsBroadcast('channel:joined', data))
141
144
  engine.on('channel:left', data => wsBroadcast('channel:left', data))
142
145
  engine.on('user:metadata:updated', data => {
@@ -157,23 +160,33 @@ function bindEngineEvents({
157
160
  }
158
161
 
159
162
  function createWebSocketServer({
163
+ engine,
160
164
  serverInstance,
161
165
  validateWebSocketRequest,
166
+ getWebSocketUserAddress,
162
167
  subscribeToChannel,
163
168
  unsubscribeFromChannel,
164
169
  cleanupWsSubscriptions,
165
170
  }) {
166
171
  const wss = new WebSocketServer({ noServer: true })
172
+ let wsConnectionCounter = 0
167
173
 
168
- wss.on('connection', ws => {
174
+ wss.on('connection', (ws, req) => {
175
+ ws.userAddress = getWebSocketUserAddress(req)
176
+ ws.presenceSourceId = `ws:${++wsConnectionCounter}`
169
177
  ws.on('error', () => {})
170
178
  ws.on('close', () => {
171
179
  cleanupWsSubscriptions(ws)
180
+ try {
181
+ engine.clearChannelPresenceSource(ws.presenceSourceId, {
182
+ broadcast: true,
183
+ })
184
+ } catch {}
172
185
  })
173
186
  ws.on('message', raw => {
174
187
  try {
175
188
  const msg = JSON.parse(raw)
176
- const { event, data } = msg
189
+ const { event, data = {} } = msg
177
190
 
178
191
  switch (event) {
179
192
  case 'register':
@@ -189,6 +202,48 @@ function createWebSocketServer({
189
202
  unsubscribeFromChannel(ws, data.channel)
190
203
  }
191
204
  break
205
+ case 'channel:presence:join':
206
+ if (data.channel && ws.userAddress) {
207
+ engine.joinChannelPresence(data.channel, {
208
+ ownerAddress: ws.userAddress,
209
+ sourceId: ws.presenceSourceId,
210
+ sessionId: data.sessionId,
211
+ displayName: data.displayName,
212
+ avatar: data.avatar,
213
+ profileUpdatedAt: data.profileUpdatedAt,
214
+ })
215
+ }
216
+ break
217
+ case 'channel:presence:heartbeat':
218
+ if (data.channel && ws.userAddress) {
219
+ engine.heartbeatChannelPresence(data.channel, {
220
+ ownerAddress: ws.userAddress,
221
+ sourceId: ws.presenceSourceId,
222
+ sessionId: data.sessionId,
223
+ })
224
+ }
225
+ break
226
+ case 'channel:presence:profile':
227
+ if (data.channel && ws.userAddress) {
228
+ engine.updateChannelPresenceProfile(data.channel, {
229
+ ownerAddress: ws.userAddress,
230
+ sourceId: ws.presenceSourceId,
231
+ sessionId: data.sessionId,
232
+ displayName: data.displayName,
233
+ avatar: data.avatar,
234
+ profileUpdatedAt: data.profileUpdatedAt,
235
+ })
236
+ }
237
+ break
238
+ case 'channel:presence:leave':
239
+ if (data.channel && ws.userAddress) {
240
+ engine.leaveChannelPresence(data.channel, {
241
+ ownerAddress: ws.userAddress,
242
+ sourceId: ws.presenceSourceId,
243
+ sessionId: data.sessionId,
244
+ })
245
+ }
246
+ break
192
247
  }
193
248
  } catch (err) {
194
249
  console.error('[WS Message Error]', err.message)
@@ -295,8 +350,10 @@ export async function main() {
295
350
  )
296
351
 
297
352
  wssRef.current = createWebSocketServer({
353
+ engine,
298
354
  serverInstance: serverInstanceRef.current,
299
355
  validateWebSocketRequest: appRuntime.validateWebSocketRequest,
356
+ getWebSocketUserAddress: appRuntime.getWebSocketUserAddress,
300
357
  subscribeToChannel: appRuntime.subscribeToChannel,
301
358
  unsubscribeFromChannel: appRuntime.unsubscribeFromChannel,
302
359
  cleanupWsSubscriptions: appRuntime.cleanupWsSubscriptions,
@@ -225,6 +225,11 @@ export function createApp(engine, options = {}) {
225
225
  return false
226
226
  }
227
227
 
228
+ return Boolean(getWebSocketUserAddress(req))
229
+ }
230
+
231
+ function getWebSocketUserAddress(req) {
232
+ const url = new URL(req.url, `http://localhost:${appPort}`)
228
233
  const address = url.searchParams.get('address') || ''
229
234
  const timestamp = url.searchParams.get('timestamp') || ''
230
235
  const signature = url.searchParams.get('signature') || ''
@@ -233,7 +238,7 @@ export function createApp(engine, options = {}) {
233
238
  'GET',
234
239
  '/ws'
235
240
  )
236
- return auth.ok
241
+ return auth.ok ? auth.address : ''
237
242
  }
238
243
 
239
244
  // 将广播函数挂载到 engine 上供外部测试使用
@@ -310,5 +315,6 @@ export function createApp(engine, options = {}) {
310
315
  unsubscribeFromChannel,
311
316
  cleanupWsSubscriptions,
312
317
  validateWebSocketRequest,
318
+ getWebSocketUserAddress,
313
319
  }
314
320
  }
@@ -266,6 +266,12 @@ export function buildOpenApiSpec(appPort) {
266
266
  responses: { 200: { description: 'Channel peers' } },
267
267
  },
268
268
  },
269
+ '/api/channels/{name}/presence': {
270
+ get: {
271
+ summary: 'List active user presence for a channel',
272
+ responses: { 200: { description: 'Channel presence' } },
273
+ },
274
+ },
269
275
  '/api/channels/{name}/remark': {
270
276
  put: {
271
277
  summary: 'Set an authenticated user channel remark',
@@ -123,6 +123,18 @@ export function registerChannelRoutes(app, { engine }) {
123
123
  }
124
124
  })
125
125
 
126
+ app.get('/api/channels/:name/presence', c => {
127
+ try {
128
+ return c.json(
129
+ engine.getChannelPresence(c.req.param('name'), {
130
+ ownerAddress: c.get('userAddress'),
131
+ })
132
+ )
133
+ } catch (err) {
134
+ return badRequestOrAppError(c, err)
135
+ }
136
+ })
137
+
126
138
  app.put('/api/channels/:name/remark', async c => {
127
139
  const name = c.req.param('name')
128
140
  const body = await c.req.json()