websocket.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { buttonResults } from '../resources/vue/stores/buttonResults.js'
  2. import { rateLimits } from '../resources/vue/stores/rateLimits.js'
  3. import { connectionState } from '../resources/vue/stores/connectionState.js'
  4. const RECONNECT_DELAY_MS = 10000
  5. export function initWebsocket () {
  6. window.addEventListener('EventOutputChunk', onOutputChunk)
  7. window.addEventListener('EventExecutionStarted', onExecutionChanged)
  8. window.addEventListener('EventExecutionFinished', onExecutionChanged)
  9. reconnectWebsocket()
  10. }
  11. window.websocketAvailable = false
  12. async function reconnectWebsocket () {
  13. if (window.websocketAvailable) {
  14. return
  15. }
  16. connectionState.reconnecting = true
  17. connectionState.connected = false
  18. if (connectionState.disconnectedAt == null) {
  19. connectionState.disconnectedAt = Date.now()
  20. }
  21. connectionState.nextReconnectAt = null
  22. try {
  23. window.websocketAvailable = true
  24. const stream = window.client.eventStream()
  25. connectionState.connected = true
  26. connectionState.reconnecting = false
  27. connectionState.nextReconnectAt = null
  28. for await (const e of stream) {
  29. connectionState.disconnectedAt = null
  30. handleEvent(e)
  31. }
  32. } catch (err) {
  33. console.error('Websocket connection failed: ', err)
  34. }
  35. window.websocketAvailable = false
  36. connectionState.connected = false
  37. connectionState.disconnectedAt = connectionState.disconnectedAt ?? Date.now()
  38. connectionState.nextReconnectAt = Date.now() + RECONNECT_DELAY_MS
  39. console.log('Reconnecting websocket in ' + RECONNECT_DELAY_MS + 'ms...')
  40. setTimeout(() => {
  41. reconnectWebsocket()
  42. }, RECONNECT_DELAY_MS)
  43. }
  44. function handleEvent (msg) {
  45. const typeName = msg.event.value.$typeName.replace('olivetin.api.v1.', '')
  46. const j = new Event(typeName)
  47. j.payload = msg.event.value
  48. switch (typeName) {
  49. case 'EventOutputChunk':
  50. case 'EventConfigChanged':
  51. case 'EventEntityChanged':
  52. window.dispatchEvent(j)
  53. break
  54. case 'EventExecutionFinished':
  55. case 'EventExecutionStarted':
  56. window.dispatchEvent(j)
  57. break
  58. default:
  59. console.warn('Unhandled websocket message type from server: ', typeName)
  60. window.showBigError('ws-unhandled-message', 'handling websocket message', 'Unhandled websocket message type from server: ' + typeName, true)
  61. }
  62. }
  63. function onOutputChunk (evt) {
  64. const chunk = evt.payload
  65. if (window.terminal) {
  66. if (chunk.executionTrackingId === window.terminal.executionTrackingId) {
  67. window.terminal.write(chunk.output)
  68. }
  69. }
  70. }
  71. function onExecutionChanged (evt) {
  72. buttonResults[evt.payload.logEntry.executionTrackingId] = evt.payload.logEntry
  73. const logEntry = evt.payload.logEntry
  74. // Update rate limit store from logEntry if rate limit expiry datetime is provided
  75. if (logEntry && logEntry.datetimeRateLimitExpires && logEntry.bindingId) {
  76. // Parse datetime string "2006-01-02 15:04:05" and convert to Unix timestamp
  77. const date = new Date(logEntry.datetimeRateLimitExpires.replace(' ', 'T') + 'Z')
  78. rateLimits[logEntry.bindingId] = date.getTime() / 1000
  79. } else if (logEntry && logEntry.bindingId) {
  80. // Clear rate limit if not set
  81. rateLimits[logEntry.bindingId] = 0
  82. }
  83. }