sse: properly handle sse opening/closing

This commit is contained in:
axolotle 2023-06-13 14:20:55 +02:00
parent 0b1030a2fb
commit 2d78749c1d
4 changed files with 39 additions and 41 deletions

View file

@ -81,7 +81,6 @@
<script>
import { mapGetters } from 'vuex'
import { connectSSE } from '@/api/handlers'
import { HistoryConsole, ViewLockOverlay } from '@/views/_partials'
export default {
@ -118,7 +117,7 @@ export default {
// state will be automaticly reseted and user will be prompt with the login view.
if (this.connected) {
this.$store.dispatch('GET_YUNOHOST_INFOS')
connectSSE()
this.$store.dispatch('SSE_CONNECT')
}
},

View file

@ -4,7 +4,7 @@
*/
import store from '@/store'
import { openWebSocket, getResponseData, handleError } from './handlers'
import { getResponseData, handleError } from './handlers'
/**

View file

@ -24,40 +24,6 @@ export async function getResponseData (response) {
}
/**
* Opens a WebSocket connection to the server in case it sends messages.
* Currently, the connection is closed by the server right after an API call so
* we have to open it for every calls.
* Messages are dispatch to the store so it can handle them.
*
* @param {Object} request - Request info data.
* @return {Promise<Event>} Promise that resolve on websocket 'open' or 'error' event.
*/
export function openWebSocket (request) {
return new Promise(resolve => {
const ws = new WebSocket(`wss://${store.getters.host}/yunohost/api/messages`)
ws.onmessage = ({ data }) => {
store.dispatch('DISPATCH_MESSAGE', { request, messages: JSON.parse(data) })
}
// ws.onclose = (e) => {}
ws.onopen = resolve
// Resolve also on error so the actual fetch may be called.
ws.onerror = resolve
})
}
export function connectSSE () {
const host = store.getters.host.split(':')[0]
const evtSource = new EventSource(`https://${host}/yunohost/api/sse`)
evtSource.onmessage = (event) => {
store.dispatch('ON_SSE_MESSAGE', JSON.parse(atob(event.data)))
}
// FIXME handle 'onerror' hook
}
/**
* Handler for API errors.
*

View file

@ -2,13 +2,13 @@ import Vue from 'vue'
import router from '@/router'
import i18n from '@/i18n'
import api from '@/api'
import { connectSSE } from '@/api/handlers'
import { timeout, isEmptyValue, isObjectLiteral } from '@/helpers/commons'
export default {
state: {
host: window.location.host, // String
connected: localStorage.getItem('connected') === 'true', // Boolean
sse: null, // EventSource
yunohost: null, // Object { version, repo }
waiting: false, // Boolean
reconnecting: null, // null|Object { attemps, delay, initialDelay }
@ -28,6 +28,17 @@ export default {
state.connected = boolean
},
'SET_SSE_SOURCE' (state, sse) {
state.sse = sse
},
'CLOSE_SSE_SOURCE' (state) {
if (state.sse) {
state.sse.close()
state.sse = null
}
},
'SET_YUNOHOST_INFOS' (state, yunohost) {
state.yunohost = yunohost
},
@ -123,13 +134,30 @@ export default {
'CONNECT' ({ commit, dispatch }) {
commit('SET_CONNECTED', true)
connectSSE()
dispatch('GET_YUNOHOST_INFOS')
dispatch('SSE_CONNECT')
},
'SSE_CONNECT' ({ commit, dispatch }) {
const sse = new EventSource(`/yunohost/api/sse`, { withCredentials: true })
sse.onopen = () => {
commit('SET_SSE_SOURCE', sse)
console.log('connected')
};
sse.onmessage = (event) => {
dispatch('ON_SSE_MESSAGE', JSON.parse(atob(event.data)))
}
// sse.onerror = (event) => {
// }
},
'RESET_CONNECTED' ({ commit }) {
commit('SET_CONNECTED', false)
commit('SET_YUNOHOST_INFOS', null)
commit('CLOSE_SSE_SOURCE')
},
'DISCONNECT' ({ dispatch }, route = router.currentRoute) {
@ -249,7 +277,13 @@ export default {
},
async 'ON_SSE_MESSAGE' ({ state, commit, dispatch }, data) {
let action = state.history.findLast((action) => action.operationId === data.operation_id)
let action
if (data.type === 'start') {
action = state.requests.findLast((request) => request.status === 'pending')
} else {
action = state.history.findLast((action) => action.operationId === data.operation_id)
}
if (!action) {
action = await dispatch('START_EXTERNAL_ACTION', { operationId: data.operation_id, timestamp: data.timestamp })
}
@ -288,7 +322,6 @@ export default {
}
commit('ADD_TEMP_MESSAGE', { request: action, message, type })
}
action.messages.push(message)
}
},