UNPKG

@doreamonjs/gate

Version:
252 lines (251 loc) 11.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const react_1 = __importStar(require("react")); const dva_1 = require("dva"); const doreamon_1 = __importDefault(require("@zodash/doreamon")); const antd_1 = require("antd"); const icons_1 = require("@ant-design/icons"); const components_1 = require("@doreamonjs/components"); const log = __importStar(require("@doreamonjs/plugin-log")); const fetch_event_source_1 = require("@microsoft/fetch-event-source"); class EventSourceX { constructor(url, options) { this._event = new doreamon_1.default.event.Event(); this._ctrl = new AbortController(); const method = (options === null || options === void 0 ? void 0 : options.method) || 'GET'; fetch_event_source_1.fetchEventSource(url, { method: method, headers: options === null || options === void 0 ? void 0 : options.headers, body: options === null || options === void 0 ? void 0 : options.body, signal: this._ctrl.signal, onopen: (response) => { var _a, _b; if (response.status >= 400 && response.status < 500 && response.status !== 429) { const error = new Error(`HTTP ${response.status} ${response.statusText}`); this._event.emit('error', error); (_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error); return Promise.reject(); } this._event.emit('open', response); (_b = this.onopen) === null || _b === void 0 ? void 0 : _b.call(this, response); return Promise.resolve(); }, onmessage: (event) => { var _a; this._event.emit('message', event); (_a = this.onmessage) === null || _a === void 0 ? void 0 : _a.call(this, event); return Promise.resolve(); }, onclose: () => { this._event.emit('close'); // this._ctrl.abort(); return Promise.resolve(); }, onerror: (error) => { var _a; (_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error); }, }); } close() { this._ctrl.abort(); } addEventListener(type, listener) { this._event.on(type, listener); } } const Log = (props) => { var _a; const xtermRef = react_1.useRef(null); // const source = useRef<EventSource | null>(null); const source = react_1.useRef(null); const [isWaitingForLog, setIsWaitingForLog] = react_1.useState(false); const staticLogRef = react_1.useRef(undefined); const drawerProps = { // className: styles.drawerform, width: props.width || 800, title: props.title || '日志', // placement: 'right', visible: props.visible, onClose: props.onClose, // closable: false, maskClosable: false, }; const onLogDownload = react_1.useCallback(async () => { try { await doreamon_1.default.fs.download(props.downloadURL, props.downloadFilename, { headers: { 'Accept': 'text/plain,text/html,application/octet-stream', }, }); } catch (err) { console.error(err); antd_1.message.error(`下载失败:${err.message}`); } }, [props.downloadURL, props.downloadFilename]); react_1.useEffect(() => { var _a, _b; const terminal = (_a = xtermRef.current) === null || _a === void 0 ? void 0 : _a.terminal; if (!terminal) { return; } terminal.clear(); if (!!props.sse) { // source.current = new EventSource(props.sse); source.current = new EventSourceX(props.sse, props.sseOptions); // terminal.write('实时日志,等待中 ...\r\n'); setIsWaitingForLog(true); let isAlreadySet = false; source.current.onmessage = (event) => { if (!isAlreadySet) { isAlreadySet = true; setIsWaitingForLog(false); // setTimeout(() => { // setIsWaitingForLog(false); // }, 1000); } let data; let log; try { data = JSON.parse(event.data); log = data.log; } catch (err) { console.error(err); return; } // @TODO ignore empty log if (!log) { return; } if (log == '[DONE]') { source.current.close(); source.current = null; return; } // const lines = log // .split('\n') // // .map(e => { // // if (!e.trim()) return e; // // return `[${data.id}][${doreamon.date(data.ts).format('YYYY-MM-DD HH:mm:ss')}] ${e}`; // // }) // .join('\r\n'); // terminal.write(lines); // terminal.write(log); // @TODO why must use '\r\n' ? terminal.write(log.split('\n').join('\r\n')); terminal.scrollToBottom(); }; return () => { if (!!source.current) { source.current.close(); source.current = null; } }; } (_b = props.log) === null || _b === void 0 ? void 0 : _b.split('\n').forEach((e) => terminal.write(e + '\r\n')); setTimeout(() => { terminal.scrollToBottom(); }, 100); }, [props.log, props.sse]); react_1.useEffect(() => { if (!staticLogRef.current) { return; } const div = staticLogRef.current; div.scrollTop = div.scrollHeight; }, [props.log, props.sse]); react_1.useEffect(() => { // if close drawer, close sse if (!props.visible && !!source.current) { source.current.close(); source.current = null; } }, [props.visible]); if (!props.sse) { return (react_1.default.createElement(antd_1.Drawer, { ...drawerProps, bodyStyle: { backgroundColor: '#000', padding: 0, }, placement: "right" }, react_1.default.createElement("div", { style: { position: 'relative', width: '100%', height: '100%', overflow: 'hidden', overflowY: 'auto', }, ref: staticLogRef }, props.downloadURL && (react_1.default.createElement(antd_1.Button, { style: { position: 'fixed', top: 70, right: 20, zIndex: 100, }, icon: react_1.default.createElement(icons_1.CloudDownloadOutlined, null), onClick: onLogDownload }, (_a = props.downloadBtnText) !== null && _a !== void 0 ? _a : '下载')), react_1.default.createElement("pre", { style: { color: '#fff', paddingTop: 10, paddingLeft: 10, whiteSpace: 'pre-wrap', } }, props.log || '无日志')))); } return (react_1.default.createElement(antd_1.Drawer, { ...drawerProps, bodyStyle: { backgroundColor: '#000', padding: 0, position: 'relative', } }, react_1.default.createElement(components_1.Loading.Cover, { style: { backgroundColor: '#000', }, loading: isWaitingForLog }), react_1.default.createElement(components_1.Terminal, { style: { width: '100%', // height: 'calc(100vh - 56px)', height: '100%', }, ref: xtermRef }))); }; const mapState = (states) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t; const state = states[log.NAMESPACE]; const loading = states.loading; const { nextEffect } = state; const btnLoading = loading.effects[nextEffect]; return { loading: btnLoading, visible: state.visible, ...state.attributes, values: state.datasets.values, // log: (_b = (_a = state.attributes) === null || _a === void 0 ? void 0 : _a.log) !== null && _b !== void 0 ? _b : (_c = state.datasets.values) === null || _c === void 0 ? void 0 : _c.log, sse: (_e = (_d = state.attributes) === null || _d === void 0 ? void 0 : _d.sse) !== null && _e !== void 0 ? _e : (_f = state.datasets.values) === null || _f === void 0 ? void 0 : _f.sse, sseOptions: (_h = (_g = state.attributes) === null || _g === void 0 ? void 0 : _g.sseOptions) !== null && _h !== void 0 ? _h : (_j = state.datasets.values) === null || _j === void 0 ? void 0 : _j.sseOptions, // downloadURL: (_l = (_k = state.attributes) === null || _k === void 0 ? void 0 : _k.downloadURL) !== null && _l !== void 0 ? _l : (_m = state.datasets.values) === null || _m === void 0 ? void 0 : _m.downloadURL, downloadFilename: (_p = (_o = state.attributes) === null || _o === void 0 ? void 0 : _o.downloadFilename) !== null && _p !== void 0 ? _p : (_q = state.datasets.values) === null || _q === void 0 ? void 0 : _q.downloadFilename, downloadBtnText: (_s = (_r = state.attributes) === null || _r === void 0 ? void 0 : _r.downloadBtnText) !== null && _s !== void 0 ? _s : (_t = state.datasets.values) === null || _t === void 0 ? void 0 : _t.downloadBtnText, }; }; const mapActions = (dispatch) => ({ onClose: () => dispatch({ type: `${log.NAMESPACE}/close` }), }); exports.default = dva_1.connect(mapState, mapActions)(Log);