assets: Use new websocket client

This commit is contained in:
Tom Wiesing 2023-11-13 11:04:03 +01:00
parent e91f1f2413
commit 6a739df24b
No known key found for this signature in database
67 changed files with 437 additions and 562 deletions

View file

@ -1,5 +1,6 @@
import './index.css'
import callServerAction, { ResultMessage } from './proto'
import { Result } from '../apiclient/websocket'
import LocalCall from './local'
type Print = ((text: string, flush?: boolean) => void) & {
paintedFrames: number
@ -166,7 +167,7 @@ export function createModal (action: string, params: string[], opts: Partial<Mod
finishButton.className = 'pure-button pure-button-success'
finishButton.append(typeof opts?.onClose === 'function' ? 'Close & Finish' : 'Close')
let result: ResultMessage = { success: false, message: 'Nothing happened' }
let result: Result = { success: false, message: 'Nothing happened' }
finishButton.addEventListener('click', (event) => {
event.preventDefault()
@ -194,7 +195,7 @@ export function createModal (action: string, params: string[], opts: Partial<Mod
window.onbeforeunload = () => 'A remote session is in progress. Are you sure you want to leave?'
// when closing, add a button to the modal!
const close = (message: ResultMessage): void => {
const close = (message: Result): void => {
result = message
if (result.success) {
@ -214,32 +215,25 @@ export function createModal (action: string, params: string[], opts: Partial<Mod
print('Connecting ...', true)
// backendURL is the backend url to connect to
const backendURL = location.protocol.replace('http', 'ws') + '//' + location.host + '/api/v1/ws'
// connect to the socket and send the action
callServerAction(
backendURL,
{
call: action,
params
},
(
send: (text: string) => void,
cancel: () => void
) => {
cancelButton.removeAttribute('disabled')
const call = new LocalCall({
call: action,
params
});
call.beforeCall = function() {
cancelButton.removeAttribute('disabled')
cancelButton.addEventListener('click', (event) => {
event.preventDefault()
print('^C\n', true)
cancel()
this.cancel()
})
print(' Connected.\n', true)
},
print
).then(close)
.catch(() => {
close({ success: false, message: 'connection closed unexpectedly' })
})
}
call.onLogLine = print;
call.connect()
.then((result) => close(result))
.catch(() => close({ success: false, message: 'connection closed unexpectedly' }));
}

View file

@ -0,0 +1,8 @@
import { default as Call, CallSpec } from '../apiclient/websocket';
/** LocalCall is like Call, but uses the current page */
export default class LocalCall extends Call {
constructor(spec: CallSpec) {
super({ url: location.protocol.replace('http', 'ws') + '//' + location.host + '/api/v1/ws'}, spec);
}
}

View file

@ -1,83 +0,0 @@
import { Mutex } from 'async-mutex'
import { runMutexExclusive } from '~/src/lib/discard'
export interface CallMessage { call: string, params?: string[] | null }
export type ResultMessage = { success: true } | { success: false, message: string }
export interface SignalMessage { signal: string }
function isResultMessage (value: any): value is ResultMessage {
return typeof value === 'object' &&
Object.prototype.hasOwnProperty.call(value, 'success') &&
(
(value.success === true) ||
(value.success === false && Object.prototype.hasOwnProperty.call(value, 'message') && (typeof value.message === 'string'))
)
}
/**
* Opens a WebSocket connection and calls a server action
* @param endpoint Endpoint to call
* @param call Function to call
* @param onOpen callback for once the connection is opened. The send function can be used to send additional text to the server. It should include newlines.
* @param onText called when the connection receives some text, including newlines.
* @returns a promise that is resolved once the conneciton is closed. Rejected if the connection errors.
*/
export default async function callServerAction (
endpoint: string,
call: CallMessage,
onOpen: (send: (text: string) => void, cancel: () => void) => void,
onText: (text: string) => void
): Promise<ResultMessage> {
return await new Promise((resolve, reject) => {
const mutex = new Mutex()
const socket = new WebSocket(endpoint)
let result: ResultMessage
socket.onmessage = (msg) => {
runMutexExclusive(mutex, async () => {
if (typeof msg.data === 'string') {
onText(msg.data)
return
}
if (msg.data instanceof Blob) {
const object = JSON.parse(await msg.data.text())
if (isResultMessage(object)) {
if (object.success) {
result = { success: true }
} else {
result = { success: false, message: object.message }
}
return
}
}
console.warn('Unknown message', msg)
})
}
socket.onclose = () => {
mutex.runExclusive(() => resolve(result)).then(() => {}).catch(console.error.bind(console))
}
socket.onerror = reject // if an error occurs, close the socket
socket.onopen = () => {
const blob = new Blob([JSON.stringify(call)])
socket.send(blob)
onOpen(
(text: string) => {
if (typeof text !== 'string') {
console.warn('Ignoring send() call with unknown type', text)
return
}
socket.send(text)
},
() => {
const blob = new Blob([JSON.stringify({ signal: 'cancel' })])
socket.send(blob)
}
)
}
})
}