mirror of
https://github.com/nocobase/nocobase.git
synced 2026-09-21 05:44:51 +08:00
* chore: export xlsx command * chore: container service * chore: report export progress * chore: export * chore: performInsert * chore: export command * chore: base exporter * chore: limit option * chore: export * chore: export * chore: types * chore: ws socket with auth * refactor: refactor exporter, change addRows method to handleRow method to support row-by-row processing * chore: xlsx exporter * chore: base exporter * chore: export limit * chore: import action * chore: load websocket * chore: import action * chore: object to cli args * chore: import options * chore: import action * chore: import runner * chore: import action * chore: import options * chore: import options * chore: plugin load event * chore: test * chore: i18n * chore: i18n * chore: i18n * fix: ws auth status * chore: cache in data source * chore: load datasource * chore: import with field alias * fix: build * fix: test * fix: build * fix: import schema settings * fix: import action * fix: import progress * chore: template creator * fix: import error message * fix: import error message * chore: test * chore: workflow dispatch event * chore: export alias * fix: typo * chore: error render * fix: event error * chore: send message to tags * fix: test * fix: import action setting
203 lines
5.3 KiB
TypeScript
203 lines
5.3 KiB
TypeScript
/**
|
|
* This file is part of the NocoBase (R) project.
|
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
* Authors: NocoBase Team.
|
|
*
|
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
*/
|
|
|
|
import { define, observable } from '@formily/reactive';
|
|
import { getSubAppName } from '@nocobase/sdk';
|
|
import { Application } from './Application';
|
|
|
|
export type WebSocketClientOptions = {
|
|
reconnectInterval?: number;
|
|
reconnectAttempts?: number;
|
|
pingInterval?: number;
|
|
url?: string;
|
|
basename?: string;
|
|
protocols?: string | string[];
|
|
onServerDown?: any;
|
|
};
|
|
|
|
export class WebSocketClient {
|
|
protected _ws: WebSocket;
|
|
protected _reconnectTimes = 0;
|
|
protected events = [];
|
|
protected options: WebSocketClientOptions;
|
|
app: Application;
|
|
enabled: boolean;
|
|
connected = false;
|
|
serverDown = false;
|
|
lastMessage = {};
|
|
|
|
constructor(options: WebSocketClientOptions | boolean) {
|
|
if (!options) {
|
|
this.enabled = false;
|
|
return;
|
|
}
|
|
this.options = options === true ? {} : options;
|
|
this.enabled = true;
|
|
define(this, {
|
|
serverDown: observable.ref,
|
|
connected: observable.ref,
|
|
lastMessage: observable.ref,
|
|
});
|
|
}
|
|
|
|
getURL() {
|
|
if (!this.app) {
|
|
return;
|
|
}
|
|
const apiBaseURL = this.app.getApiUrl();
|
|
if (!apiBaseURL) {
|
|
return;
|
|
}
|
|
const subApp = getSubAppName(this.app.getPublicPath());
|
|
const queryString = subApp ? `?__appName=${subApp}` : '';
|
|
const wsPath = this.options.basename || '/ws';
|
|
if (this.options.url) {
|
|
const url = new URL(this.options.url);
|
|
if (url.hostname === 'localhost') {
|
|
const protocol = location.protocol === 'https:' ? 'wss' : 'ws';
|
|
return `${protocol}://${location.hostname}:${url.port}${wsPath}${queryString}`;
|
|
}
|
|
return `${this.options.url}${queryString}`;
|
|
}
|
|
try {
|
|
const url = new URL(apiBaseURL);
|
|
return `${url.protocol === 'https:' ? 'wss' : 'ws'}://${url.host}${wsPath}${queryString}`;
|
|
} catch (error) {
|
|
return `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}${wsPath}${queryString}`;
|
|
}
|
|
}
|
|
|
|
get reconnectAttempts() {
|
|
return this.options?.reconnectAttempts || 30;
|
|
}
|
|
|
|
get reconnectInterval() {
|
|
return this.options?.reconnectInterval || 1000;
|
|
}
|
|
|
|
get pingInterval() {
|
|
return this.options?.pingInterval || 30000;
|
|
}
|
|
|
|
get readyState() {
|
|
if (!this._ws) {
|
|
return -1;
|
|
}
|
|
return this._ws.readyState;
|
|
}
|
|
|
|
connect() {
|
|
if (!this.enabled) {
|
|
return;
|
|
}
|
|
if (this._reconnectTimes === 0) {
|
|
console.log('[nocobase-ws]: connecting...');
|
|
}
|
|
if (this._reconnectTimes >= this.reconnectAttempts) {
|
|
return;
|
|
}
|
|
if (this.readyState === WebSocket.OPEN) {
|
|
return;
|
|
}
|
|
this._reconnectTimes++;
|
|
const ws = new WebSocket(this.getURL(), this.options.protocols);
|
|
let pingIntervalTimer: any;
|
|
|
|
ws.onopen = () => {
|
|
console.log('[nocobase-ws]: connected.');
|
|
this.serverDown = false;
|
|
if (this._ws) {
|
|
this.removeAllListeners();
|
|
}
|
|
this._reconnectTimes = 0;
|
|
this._ws = ws;
|
|
for (const { type, listener, options } of this.events) {
|
|
this._ws.addEventListener(type, listener, options);
|
|
}
|
|
pingIntervalTimer = setInterval(() => this.send('ping'), this.pingInterval);
|
|
this.connected = true;
|
|
this.emit('open', {});
|
|
};
|
|
ws.onerror = async () => {
|
|
// setTimeout(() => this.connect(), this.reconnectInterval);
|
|
console.log('onerror', this.readyState, this._reconnectTimes);
|
|
};
|
|
ws.onclose = async (event) => {
|
|
setTimeout(() => this.connect(), this.reconnectInterval);
|
|
console.log('onclose', this.readyState, this._reconnectTimes, this.serverDown);
|
|
this.connected = false;
|
|
clearInterval(pingIntervalTimer);
|
|
// if (this._reconnectTimes >= Math.min(this.reconnectAttempts, 5)) {
|
|
// this.serverDown = true;
|
|
// this.emit('serverDown', event);
|
|
// }
|
|
};
|
|
}
|
|
|
|
reconnect() {
|
|
this._reconnectTimes = 0;
|
|
this.connect();
|
|
}
|
|
|
|
close() {
|
|
if (!this._ws) {
|
|
return;
|
|
}
|
|
this._reconnectTimes = this.reconnectAttempts;
|
|
return this._ws.close();
|
|
}
|
|
|
|
send(data: string | ArrayBufferLike | Blob | ArrayBufferView) {
|
|
if (!this._ws) {
|
|
return;
|
|
}
|
|
return this._ws.send(data);
|
|
}
|
|
|
|
on(type: string, listener: any, options?: boolean | AddEventListenerOptions) {
|
|
this.events.push({ type, listener, options });
|
|
if (!this._ws) {
|
|
return;
|
|
}
|
|
this._ws.addEventListener(type, listener, options);
|
|
}
|
|
|
|
emit(type: string, args: any) {
|
|
for (const event of this.events) {
|
|
if (event.type === type) {
|
|
event.listener(args);
|
|
}
|
|
}
|
|
}
|
|
|
|
off(type: string, listener: any, options?: boolean | EventListenerOptions) {
|
|
let index = 0;
|
|
for (const event of this.events) {
|
|
if (event.type === type && event.listener === listener) {
|
|
this.events.splice(index, 1);
|
|
break;
|
|
}
|
|
index++;
|
|
}
|
|
if (!this._ws) {
|
|
return;
|
|
}
|
|
this._ws.removeEventListener(type, listener, options);
|
|
}
|
|
|
|
removeAllListeners() {
|
|
if (!this._ws) {
|
|
return;
|
|
}
|
|
for (const { type, listener, options } of this.events) {
|
|
this._ws.removeEventListener(type, listener, options);
|
|
}
|
|
}
|
|
}
|