mirror of
https://github.com/whyour/qinglong.git
synced 2025-07-07 03:46:07 +08:00
修复调试脚本日志丢失
This commit is contained in:
parent
ab3fc9b5f1
commit
a864a56917
|
@ -34,7 +34,7 @@ export default (app: Router) => {
|
||||||
const logger: Logger = Container.get('logger');
|
const logger: Logger = Container.get('logger');
|
||||||
try {
|
try {
|
||||||
let result = [];
|
let result = [];
|
||||||
const blacklist = ['node_modules', '.git'];
|
const blacklist = ['node_modules', '.git', '.pnpm'];
|
||||||
if (req.query.path) {
|
if (req.query.path) {
|
||||||
const targetPath = path.join(
|
const targetPath = path.join(
|
||||||
config.scriptPath,
|
config.scriptPath,
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { SockMessage } from '../data/sock';
|
||||||
export default class SockService {
|
export default class SockService {
|
||||||
private clients: Connection[] = [];
|
private clients: Connection[] = [];
|
||||||
|
|
||||||
constructor(@Inject('logger') private logger: winston.Logger) {}
|
constructor(@Inject('logger') private logger: winston.Logger) { }
|
||||||
|
|
||||||
public getClients() {
|
public getClients() {
|
||||||
return this.clients;
|
return this.clients;
|
||||||
|
|
|
@ -273,13 +273,13 @@ update_qinglong() {
|
||||||
exit_status=$?
|
exit_status=$?
|
||||||
|
|
||||||
if [[ $exit_status -eq 0 ]]; then
|
if [[ $exit_status -eq 0 ]]; then
|
||||||
echo -e "\n更新青龙源文件成功...\n"
|
echo -e "更新青龙源文件成功...\n"
|
||||||
|
|
||||||
unzip -oq ${dir_tmp}/ql.zip -d ${dir_tmp}
|
unzip -oq ${dir_tmp}/ql.zip -d ${dir_tmp}
|
||||||
|
|
||||||
update_qinglong_static
|
update_qinglong_static
|
||||||
else
|
else
|
||||||
echo -e "\n更新青龙源文件失败,请检查网络...\n"
|
echo -e "更新青龙源文件失败,请检查网络...\n"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,28 +288,29 @@ update_qinglong_static() {
|
||||||
exit_status=$?
|
exit_status=$?
|
||||||
|
|
||||||
if [[ $exit_status -eq 0 ]]; then
|
if [[ $exit_status -eq 0 ]]; then
|
||||||
echo -e "\n更新青龙静态资源成功...\n"
|
echo -e "更新青龙静态资源成功...\n"
|
||||||
unzip -oq ${dir_tmp}/static.zip -d ${dir_tmp}
|
unzip -oq ${dir_tmp}/static.zip -d ${dir_tmp}
|
||||||
|
|
||||||
check_update_dep
|
check_update_dep
|
||||||
else
|
else
|
||||||
echo -e "\n更新青龙静态资源失败,请检查网络...\n"
|
echo -e "更新青龙静态资源失败,请检查网络...\n"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
check_update_dep() {
|
check_update_dep() {
|
||||||
echo -e "\n开始检测依赖...\n"
|
echo -e "\n开始检测依赖...\n"
|
||||||
if [[ $(diff $dir_sample/package.json $dir_scripts/package.json) ]]; then
|
if [[ ! -s $dir_scripts/package.json ]] || [[ $(diff $dir_sample/package.json $dir_scripts/package.json) ]]; then
|
||||||
cp -f $dir_sample/package.json $dir_scripts/package.json
|
cp -f $dir_sample/package.json $dir_scripts/package.json
|
||||||
npm_install_2 $dir_scripts
|
npm_install_2 $dir_scripts
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $(diff $dir_root/package.json ${dir_tmp}/qinglong-${primary_branch}/package.json) ]]; then
|
if [[ $(diff $dir_root/package.json ${dir_tmp}/qinglong-${primary_branch}/package.json) ]]; then
|
||||||
npm_install_2 "${dir_tmp}/qinglong-${primary_branch}"
|
npm_install_2 "${dir_tmp}/qinglong-${primary_branch}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $exit_status -eq 0 ]]; then
|
if [[ $exit_status -eq 0 ]]; then
|
||||||
echo -e "\n依赖检测安装成功...\n"
|
echo -e "\n依赖检测安装成功...\n"
|
||||||
echo -e "\n更新包下载成功...\n"
|
echo -e "更新包下载成功..."
|
||||||
|
|
||||||
if [[ "$needRestart" == 'true' ]]; then
|
if [[ "$needRestart" == 'true' ]]; then
|
||||||
cp -rf ${dir_tmp}/qinglong-${primary_branch}/* ${dir_root}/
|
cp -rf ${dir_tmp}/qinglong-${primary_branch}/* ${dir_root}/
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import React, { useEffect, useState, useRef } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import ProLayout, { PageLoading } from '@ant-design/pro-layout';
|
import ProLayout, { PageLoading } from '@ant-design/pro-layout';
|
||||||
import * as DarkReader from '@umijs/ssr-darkreader';
|
import * as DarkReader from '@umijs/ssr-darkreader';
|
||||||
import defaultProps from './defaultProps';
|
import defaultProps from './defaultProps';
|
||||||
|
@ -29,9 +29,9 @@ import {
|
||||||
MenuProps,
|
MenuProps,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import SockJS from 'sockjs-client';
|
|
||||||
import * as Sentry from '@sentry/react';
|
import * as Sentry from '@sentry/react';
|
||||||
import { init } from '../utils/init';
|
import { init } from '../utils/init';
|
||||||
|
import WebSocketManager from '../utils/websocket';
|
||||||
|
|
||||||
export interface SharedContext {
|
export interface SharedContext {
|
||||||
headerStyle: React.CSSProperties;
|
headerStyle: React.CSSProperties;
|
||||||
|
@ -40,7 +40,6 @@ export interface SharedContext {
|
||||||
user: any;
|
user: any;
|
||||||
reloadUser: (needLoading?: boolean) => void;
|
reloadUser: (needLoading?: boolean) => void;
|
||||||
reloadTheme: () => void;
|
reloadTheme: () => void;
|
||||||
socketMessage: any;
|
|
||||||
systemInfo: TSystemInfo;
|
systemInfo: TSystemInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,8 +59,6 @@ export default function () {
|
||||||
const [user, setUser] = useState<any>({});
|
const [user, setUser] = useState<any>({});
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
const [systemInfo, setSystemInfo] = useState<TSystemInfo>();
|
const [systemInfo, setSystemInfo] = useState<TSystemInfo>();
|
||||||
const ws = useRef<any>(null);
|
|
||||||
const [socketMessage, setSocketMessage] = useState<any>();
|
|
||||||
const [collapsed, setCollapsed] = useState(false);
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
const [initLoading, setInitLoading] = useState<boolean>(true);
|
const [initLoading, setInitLoading] = useState<boolean>(true);
|
||||||
const {
|
const {
|
||||||
|
@ -180,32 +177,14 @@ export default function () {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!user || !user.username) return;
|
if (!user || !user.username) return;
|
||||||
ws.current = new SockJS(
|
const ws = WebSocketManager.getInstance(
|
||||||
`${window.location.origin}/api/ws?token=${localStorage.getItem(
|
`${window.location.origin}/api/ws?token=${localStorage.getItem(
|
||||||
config.authKey,
|
config.authKey,
|
||||||
)}`,
|
)}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
ws.current.onmessage = (e: any) => {
|
|
||||||
try {
|
|
||||||
const data = JSON.parse(e.data);
|
|
||||||
if (data.type === 'ping') {
|
|
||||||
if (data && data.message === 'hanhh') {
|
|
||||||
console.log('WS connection succeeded !!!');
|
|
||||||
} else {
|
|
||||||
console.log('WS connection Failed !!!', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setSocketMessage(data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log('websocket连接失败', e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const wsCurrent = ws.current;
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
wsCurrent.close();
|
ws.close();
|
||||||
};
|
};
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
|
@ -387,7 +366,6 @@ export default function () {
|
||||||
user,
|
user,
|
||||||
reloadUser,
|
reloadUser,
|
||||||
reloadTheme,
|
reloadTheme,
|
||||||
socketMessage,
|
|
||||||
systemInfo,
|
systemInfo,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -35,6 +35,7 @@ import { useOutletContext } from '@umijs/max';
|
||||||
import { SharedContext } from '@/layouts';
|
import { SharedContext } from '@/layouts';
|
||||||
import useTableScrollHeight from '@/hooks/useTableScrollHeight';
|
import useTableScrollHeight from '@/hooks/useTableScrollHeight';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import WebSocketManager from '@/utils/websocket';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
const { Search } = Input;
|
const { Search } = Input;
|
||||||
|
@ -87,8 +88,7 @@ const StatusMap: Record<number, { icon: React.ReactNode; color: string }> = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Dependence = () => {
|
const Dependence = () => {
|
||||||
const { headerStyle, isPhone, socketMessage } =
|
const { headerStyle, isPhone } = useOutletContext<SharedContext>();
|
||||||
useOutletContext<SharedContext>();
|
|
||||||
const columns: any = [
|
const columns: any = [
|
||||||
{
|
{
|
||||||
title: intl.get('序号'),
|
title: intl.get('序号'),
|
||||||
|
@ -109,11 +109,12 @@ const Dependence = () => {
|
||||||
width: 120,
|
width: 120,
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
render: (text: string, record: any, index: number) => {
|
render: (text: string, record: any, index: number) => {
|
||||||
|
console.log(record.status);
|
||||||
return (
|
return (
|
||||||
<Space size="middle" style={{ cursor: 'text' }}>
|
<Space size="middle" style={{ cursor: 'text' }}>
|
||||||
<Tag
|
<Tag
|
||||||
color={StatusMap[record.status].color}
|
color={StatusMap[record.status]?.color}
|
||||||
icon={StatusMap[record.status].icon}
|
icon={StatusMap[record.status]?.icon}
|
||||||
style={{ marginRight: 0 }}
|
style={{ marginRight: 0 }}
|
||||||
>
|
>
|
||||||
{intl.get(Status[record.status])}
|
{intl.get(Status[record.status])}
|
||||||
|
@ -395,63 +396,62 @@ const Dependence = () => {
|
||||||
}
|
}
|
||||||
}, [logDependence]);
|
}, [logDependence]);
|
||||||
|
|
||||||
useEffect(() => {
|
const handleMessage = useCallback((payload: any) => {
|
||||||
if (!socketMessage) return;
|
const { message, references } = payload;
|
||||||
const { type, message, references } = socketMessage;
|
let status: number | undefined = undefined;
|
||||||
if (
|
if (message.includes('开始时间') && references.length > 0) {
|
||||||
type === 'installDependence' &&
|
status = message.includes('安装') ? Status.安装中 : Status.删除中;
|
||||||
message.includes('开始时间') &&
|
|
||||||
references.length > 0
|
|
||||||
) {
|
|
||||||
const result = [...value];
|
|
||||||
for (let i = 0; i < references.length; i++) {
|
|
||||||
const index = value.findIndex((x) => x.id === references[i]);
|
|
||||||
if (index !== -1) {
|
|
||||||
result.splice(index, 1, {
|
|
||||||
...value[index],
|
|
||||||
status: message.includes('安装') ? Status.安装中 : Status.删除中,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setValue(result);
|
|
||||||
}
|
}
|
||||||
if (
|
if (message.includes('结束时间') && references.length > 0) {
|
||||||
type === 'installDependence' &&
|
|
||||||
message.includes('结束时间') &&
|
|
||||||
references.length > 0
|
|
||||||
) {
|
|
||||||
let status;
|
|
||||||
if (message.includes('安装')) {
|
if (message.includes('安装')) {
|
||||||
status = message.includes('成功') ? Status.已安装 : Status.安装失败;
|
status = message.includes('成功') ? Status.已安装 : Status.安装失败;
|
||||||
} else {
|
} else {
|
||||||
status = message.includes('成功') ? Status.已删除 : Status.删除失败;
|
status = message.includes('成功') ? Status.已删除 : Status.删除失败;
|
||||||
}
|
}
|
||||||
const result = [...value];
|
|
||||||
for (let i = 0; i < references.length; i++) {
|
|
||||||
const index = value.findIndex((x) => x.id === references[i]);
|
|
||||||
if (index !== -1) {
|
|
||||||
result.splice(index, 1, {
|
|
||||||
...value[index],
|
|
||||||
status,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setValue(result);
|
|
||||||
|
|
||||||
if (status === Status.已删除) {
|
if (status === Status.已删除) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const _result = [...value];
|
setValue((p) => {
|
||||||
for (let i = 0; i < references.length; i++) {
|
const _result = [...p];
|
||||||
const index = value.findIndex((x) => x.id === references[i]);
|
for (let i = 0; i < references.length; i++) {
|
||||||
if (index !== -1) {
|
const index = p.findIndex((x) => x.id === references[i]);
|
||||||
_result.splice(index, 1);
|
if (index !== -1) {
|
||||||
|
_result.splice(index, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return _result;
|
||||||
setValue(_result);
|
});
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [socketMessage]);
|
if (typeof status === 'number') {
|
||||||
|
setValue((p) => {
|
||||||
|
const result = [...p];
|
||||||
|
for (let i = 0; i < references.length; i++) {
|
||||||
|
const index = p.findIndex((x) => x.id === references[i]);
|
||||||
|
if (index !== -1) {
|
||||||
|
result.splice(index, 1, {
|
||||||
|
...p[index],
|
||||||
|
status,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const ws = WebSocketManager.getInstance();
|
||||||
|
ws.subscribe('installDependence', handleMessage);
|
||||||
|
ws.subscribe('uninstallDependence', handleMessage);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
ws.unsubscribe('installDependence', handleMessage);
|
||||||
|
ws.unsubscribe('uninstallDependence', handleMessage);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const onTabChange = (activeKey: string) => {
|
const onTabChange = (activeKey: string) => {
|
||||||
setSelectedRowIds([]);
|
setSelectedRowIds([]);
|
||||||
|
@ -548,24 +548,25 @@ const Dependence = () => {
|
||||||
dependence={editedDependence}
|
dependence={editedDependence}
|
||||||
defaultType={type}
|
defaultType={type}
|
||||||
/>
|
/>
|
||||||
<DependenceLogModal
|
{logDependence && (
|
||||||
visible={isLogModalVisible}
|
<DependenceLogModal
|
||||||
handleCancel={(needRemove?: boolean) => {
|
visible={isLogModalVisible}
|
||||||
setIsLogModalVisible(false);
|
handleCancel={(needRemove?: boolean) => {
|
||||||
if (needRemove) {
|
setIsLogModalVisible(false);
|
||||||
const index = value.findIndex((x) => x.id === logDependence.id);
|
if (needRemove) {
|
||||||
const result = [...value];
|
const index = value.findIndex((x) => x.id === logDependence.id);
|
||||||
if (index !== -1) {
|
const result = [...value];
|
||||||
result.splice(index, 1);
|
if (index !== -1) {
|
||||||
setValue(result);
|
result.splice(index, 1);
|
||||||
|
setValue(result);
|
||||||
|
}
|
||||||
|
} else if ([...value].map((x) => x.id).includes(logDependence.id)) {
|
||||||
|
getDependenceDetail(logDependence);
|
||||||
}
|
}
|
||||||
} else if ([...value].map((x) => x.id).includes(logDependence.id)) {
|
}}
|
||||||
getDependenceDetail(logDependence);
|
dependence={logDependence}
|
||||||
}
|
/>
|
||||||
}}
|
)}
|
||||||
socketMessage={socketMessage}
|
|
||||||
dependence={logDependence}
|
|
||||||
/>
|
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,17 +9,16 @@ import {
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { PageLoading } from '@ant-design/pro-layout';
|
import { PageLoading } from '@ant-design/pro-layout';
|
||||||
import Ansi from 'ansi-to-react';
|
import Ansi from 'ansi-to-react';
|
||||||
|
import WebSocketManager from '@/utils/websocket';
|
||||||
|
|
||||||
const DependenceLogModal = ({
|
const DependenceLogModal = ({
|
||||||
dependence,
|
dependence,
|
||||||
handleCancel,
|
handleCancel,
|
||||||
visible,
|
visible,
|
||||||
socketMessage,
|
|
||||||
}: {
|
}: {
|
||||||
dependence?: any;
|
dependence?: any;
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
handleCancel: (needRemove?: boolean) => void;
|
handleCancel: (needRemove?: boolean) => void;
|
||||||
socketMessage: any;
|
|
||||||
}) => {
|
}) => {
|
||||||
const [value, setValue] = useState<string>('');
|
const [value, setValue] = useState<string>('');
|
||||||
const [executing, setExecuting] = useState<any>(true);
|
const [executing, setExecuting] = useState<any>(true);
|
||||||
|
@ -54,7 +53,7 @@ const DependenceLogModal = ({
|
||||||
code === 200 &&
|
code === 200 &&
|
||||||
localStorage.getItem('logDependence') === String(dependence.id)
|
localStorage.getItem('logDependence') === String(dependence.id)
|
||||||
) {
|
) {
|
||||||
const log = (data.log || []).join('') as string;
|
const log = (data?.log || []).join('') as string;
|
||||||
setValue(log);
|
setValue(log);
|
||||||
setExecuting(!log.includes('结束时间'));
|
setExecuting(!log.includes('结束时间'));
|
||||||
setIsRemoveFailed(log.includes('删除失败'));
|
setIsRemoveFailed(log.includes('删除失败'));
|
||||||
|
@ -95,21 +94,25 @@ const DependenceLogModal = ({
|
||||||
}
|
}
|
||||||
}, [dependence]);
|
}, [dependence]);
|
||||||
|
|
||||||
useEffect(() => {
|
const handleMessage = (payload: any) => {
|
||||||
if (!socketMessage || !dependence) return;
|
const { message, references } = payload;
|
||||||
const { type, message, references } = socketMessage;
|
if (references.length > 0 && references.includes(dependence.id)) {
|
||||||
if (
|
|
||||||
type === 'installDependence' &&
|
|
||||||
references.length > 0 &&
|
|
||||||
references.includes(dependence.id)
|
|
||||||
) {
|
|
||||||
if (message.includes('结束时间')) {
|
if (message.includes('结束时间')) {
|
||||||
setExecuting(false);
|
setExecuting(false);
|
||||||
setIsRemoveFailed(message.includes('删除失败'));
|
setIsRemoveFailed(message.includes('删除失败'));
|
||||||
}
|
}
|
||||||
setValue(`${value}${message}`);
|
setValue((p) => `${p}${message}`);
|
||||||
}
|
}
|
||||||
}, [socketMessage]);
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const ws = WebSocketManager.getInstance();
|
||||||
|
ws.subscribe('installDependence', handleMessage);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
ws.unsubscribe('installDependence', handleMessage);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsPhone(document.body.clientWidth < 768);
|
setIsPhone(document.body.clientWidth < 768);
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import React, { useEffect, useState, useRef } from 'react';
|
import React, {
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
useRef,
|
||||||
|
useCallback,
|
||||||
|
useReducer,
|
||||||
|
} from 'react';
|
||||||
import { Drawer, Button, Tabs, Badge, Select, TreeSelect } from 'antd';
|
import { Drawer, Button, Tabs, Badge, Select, TreeSelect } from 'antd';
|
||||||
import { request } from '@/utils/http';
|
import { request } from '@/utils/http';
|
||||||
import config from '@/utils/config';
|
import config from '@/utils/config';
|
||||||
|
@ -9,6 +15,7 @@ import SaveModal from './saveModal';
|
||||||
import SettingModal from './setting';
|
import SettingModal from './setting';
|
||||||
import { useTheme } from '@/utils/hooks';
|
import { useTheme } from '@/utils/hooks';
|
||||||
import { getEditorMode, logEnded } from '@/utils';
|
import { getEditorMode, logEnded } from '@/utils';
|
||||||
|
import WebSocketManager from '@/utils/websocket';
|
||||||
|
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
|
@ -18,12 +25,10 @@ const EditModal = ({
|
||||||
content,
|
content,
|
||||||
handleCancel,
|
handleCancel,
|
||||||
visible,
|
visible,
|
||||||
socketMessage,
|
|
||||||
}: {
|
}: {
|
||||||
treeData?: any;
|
treeData?: any;
|
||||||
content?: string;
|
content?: string;
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
socketMessage: any;
|
|
||||||
currentNode: any;
|
currentNode: any;
|
||||||
handleCancel: () => void;
|
handleCancel: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -34,12 +39,11 @@ const EditModal = ({
|
||||||
const [saveModalVisible, setSaveModalVisible] = useState<boolean>(false);
|
const [saveModalVisible, setSaveModalVisible] = useState<boolean>(false);
|
||||||
const [settingModalVisible, setSettingModalVisible] =
|
const [settingModalVisible, setSettingModalVisible] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
const [log, setLog] = useState<string>('');
|
const [log, setLog] = useState('');
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const editorRef = useRef<any>(null);
|
const editorRef = useRef<any>(null);
|
||||||
const [isRunning, setIsRunning] = useState(false);
|
const [isRunning, setIsRunning] = useState(false);
|
||||||
const [currentPid, setCurrentPid] = useState(null);
|
const [currentPid, setCurrentPid] = useState(null);
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
handleCancel();
|
handleCancel();
|
||||||
};
|
};
|
||||||
|
@ -104,28 +108,25 @@ const EditModal = ({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const handleMessage = useCallback((payload: any) => {
|
||||||
if (!socketMessage) {
|
let { message: _message } = payload;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { type, message: _message, references } = socketMessage;
|
|
||||||
|
|
||||||
if (type !== 'manuallyRunScript') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logEnded(_message)) {
|
if (logEnded(_message)) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setIsRunning(false);
|
setIsRunning(false);
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log) {
|
setLog(p=>`${p}${_message}`);
|
||||||
_message = `\n${_message}`;
|
}, []);
|
||||||
}
|
|
||||||
setLog(`${log}${_message}`);
|
useEffect(() => {
|
||||||
}, [socketMessage]);
|
const ws = WebSocketManager.getInstance();
|
||||||
|
ws.subscribe('manuallyRunScript', handleMessage);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
ws.unsubscribe('manuallyRunScript', handleMessage);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLog('');
|
setLog('');
|
||||||
|
|
|
@ -48,7 +48,7 @@ import { langs } from '@uiw/codemirror-extensions-langs';
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
const Script = () => {
|
const Script = () => {
|
||||||
const { headerStyle, isPhone, theme, socketMessage } =
|
const { headerStyle, isPhone, theme } =
|
||||||
useOutletContext<SharedContext>();
|
useOutletContext<SharedContext>();
|
||||||
const [value, setValue] = useState(intl.get('请选择脚本文件'));
|
const [value, setValue] = useState(intl.get('请选择脚本文件'));
|
||||||
const [select, setSelect] = useState<string>('');
|
const [select, setSelect] = useState<string>('');
|
||||||
|
@ -591,16 +591,15 @@ const Script = () => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<EditModal
|
{isLogModalVisible && <EditModal
|
||||||
visible={isLogModalVisible}
|
visible={isLogModalVisible}
|
||||||
treeData={data}
|
treeData={data}
|
||||||
currentNode={currentNode}
|
currentNode={currentNode}
|
||||||
content={value}
|
content={value}
|
||||||
socketMessage={socketMessage}
|
|
||||||
handleCancel={() => {
|
handleCancel={() => {
|
||||||
setIsLogModalVisible(false);
|
setIsLogModalVisible(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>}
|
||||||
<EditScriptNameModal
|
<EditScriptNameModal
|
||||||
visible={isAddFileModalVisible}
|
visible={isAddFileModalVisible}
|
||||||
treeData={data}
|
treeData={data}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import React, { useEffect, useState, useRef } from 'react';
|
import React, { useEffect, useState, useRef, useCallback } from 'react';
|
||||||
import { Statistic, Modal, Tag, Button, Spin, message } from 'antd';
|
import { Statistic, Modal, Tag, Button, Spin, message } from 'antd';
|
||||||
import { request } from '@/utils/http';
|
import { request } from '@/utils/http';
|
||||||
import config from '@/utils/config';
|
import config from '@/utils/config';
|
||||||
|
import WebSocketManager from '@/utils/websocket';
|
||||||
|
import Ansi from 'ansi-to-react';
|
||||||
|
|
||||||
const { Countdown } = Statistic;
|
const { Countdown } = Statistic;
|
||||||
|
|
||||||
const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
|
const CheckUpdate = ({ systemInfo }: any) => {
|
||||||
const [updateLoading, setUpdateLoading] = useState(false);
|
const [updateLoading, setUpdateLoading] = useState(false);
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
const modalRef = useRef<any>();
|
const modalRef = useRef<any>();
|
||||||
|
@ -149,17 +151,8 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!modalRef.current || !socketMessage) {
|
if (!value) return;
|
||||||
return;
|
const updateFailed = value.includes('失败,请检查');
|
||||||
}
|
|
||||||
const { type, message: _message, references } = socketMessage;
|
|
||||||
|
|
||||||
if (type !== 'updateSystemVersion') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newMessage = `${value}${_message}`;
|
|
||||||
const updateFailed = newMessage.includes('失败');
|
|
||||||
|
|
||||||
modalRef.current.update({
|
modalRef.current.update({
|
||||||
maskClosable: updateFailed,
|
maskClosable: updateFailed,
|
||||||
|
@ -167,29 +160,46 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
|
||||||
okButtonProps: { disabled: !updateFailed },
|
okButtonProps: { disabled: !updateFailed },
|
||||||
content: (
|
content: (
|
||||||
<>
|
<>
|
||||||
<pre>{newMessage}</pre>
|
<pre>
|
||||||
|
<Ansi>{value}</Ansi>
|
||||||
|
</pre>
|
||||||
<div id="log-identifier" style={{ paddingBottom: 5 }}></div>
|
<div id="log-identifier" style={{ paddingBottom: 5 }}></div>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
if (updateFailed && !value.includes('失败,请检查')) {
|
const handleMessage = useCallback((payload: any) => {
|
||||||
|
let { message: _message } = payload;
|
||||||
|
const updateFailed = _message.includes('失败,请检查');
|
||||||
|
|
||||||
|
if (updateFailed) {
|
||||||
message.error(intl.get('更新失败,请检查网络及日志或稍后再试'));
|
message.error(intl.get('更新失败,请检查网络及日志或稍后再试'));
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue(newMessage);
|
setTimeout(() => {
|
||||||
|
|
||||||
document.getElementById('log-identifier') &&
|
|
||||||
document
|
document
|
||||||
.getElementById('log-identifier')!
|
.querySelector('#log-identifier')!
|
||||||
.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
.scrollIntoView({ behavior: 'smooth' });
|
||||||
|
}, 600);
|
||||||
|
|
||||||
if (_message.includes('更新包下载成功')) {
|
if (_message.includes('更新包下载成功')) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showReloadModal();
|
showReloadModal();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
}, [socketMessage]);
|
|
||||||
|
setValue((p) => `${p}${_message}`);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const ws = WebSocketManager.getInstance();
|
||||||
|
ws.subscribe('updateSystemVersion', handleMessage);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
ws.unsubscribe('updateSystemVersion', handleMessage);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -46,7 +46,6 @@ const Setting = () => {
|
||||||
theme,
|
theme,
|
||||||
reloadUser,
|
reloadUser,
|
||||||
reloadTheme,
|
reloadTheme,
|
||||||
socketMessage,
|
|
||||||
systemInfo,
|
systemInfo,
|
||||||
} = useOutletContext<SharedContext>();
|
} = useOutletContext<SharedContext>();
|
||||||
const columns = [
|
const columns = [
|
||||||
|
@ -376,7 +375,6 @@ const Setting = () => {
|
||||||
children: (
|
children: (
|
||||||
<Other
|
<Other
|
||||||
reloadTheme={reloadTheme}
|
reloadTheme={reloadTheme}
|
||||||
socketMessage={socketMessage}
|
|
||||||
systemInfo={systemInfo}
|
systemInfo={systemInfo}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
|
|
@ -24,9 +24,8 @@ import useProgress from './progress';
|
||||||
|
|
||||||
const Other = ({
|
const Other = ({
|
||||||
systemInfo,
|
systemInfo,
|
||||||
socketMessage,
|
|
||||||
reloadTheme,
|
reloadTheme,
|
||||||
}: Pick<SharedContext, 'socketMessage' | 'reloadTheme' | 'systemInfo'>) => {
|
}: Pick<SharedContext, 'reloadTheme' | 'systemInfo'>) => {
|
||||||
const defaultTheme = localStorage.getItem('qinglong_dark_theme') || 'auto';
|
const defaultTheme = localStorage.getItem('qinglong_dark_theme') || 'auto';
|
||||||
const [systemConfig, setSystemConfig] = useState<{
|
const [systemConfig, setSystemConfig] = useState<{
|
||||||
logRemoveFrequency?: number | null;
|
logRemoveFrequency?: number | null;
|
||||||
|
@ -274,7 +273,7 @@ const Other = ({
|
||||||
</Upload>
|
</Upload>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={intl.get('检查更新')} name="update">
|
<Form.Item label={intl.get('检查更新')} name="update">
|
||||||
<CheckUpdate systemInfo={systemInfo} socketMessage={socketMessage} />
|
<CheckUpdate systemInfo={systemInfo} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
message,
|
message,
|
||||||
|
@ -36,6 +36,7 @@ import './index.less';
|
||||||
import SubscriptionLogModal from './logModal';
|
import SubscriptionLogModal from './logModal';
|
||||||
import { SharedContext } from '@/layouts';
|
import { SharedContext } from '@/layouts';
|
||||||
import useTableScrollHeight from '@/hooks/useTableScrollHeight';
|
import useTableScrollHeight from '@/hooks/useTableScrollHeight';
|
||||||
|
import WebSocketManager from '@/utils/websocket';
|
||||||
|
|
||||||
const { Text, Paragraph } = Typography;
|
const { Text, Paragraph } = Typography;
|
||||||
const { Search } = Input;
|
const { Search } = Input;
|
||||||
|
@ -61,8 +62,7 @@ export enum SubscriptionType {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Subscription = () => {
|
const Subscription = () => {
|
||||||
const { headerStyle, isPhone, socketMessage } =
|
const { headerStyle, isPhone } = useOutletContext<SharedContext>();
|
||||||
useOutletContext<SharedContext>();
|
|
||||||
|
|
||||||
const columns: any = [
|
const columns: any = [
|
||||||
{
|
{
|
||||||
|
@ -508,23 +508,31 @@ const Subscription = () => {
|
||||||
: 'subscription';
|
: 'subscription';
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const handleMessage = useCallback((payload: any) => {
|
||||||
if (!socketMessage) return;
|
const { message, references } = payload;
|
||||||
const { type, message, references } = socketMessage;
|
setValue((p) => {
|
||||||
if (type === 'runSubscriptionEnd' && references.length > 0) {
|
const result = [...p];
|
||||||
const result = [...value];
|
|
||||||
for (let i = 0; i < references.length; i++) {
|
for (let i = 0; i < references.length; i++) {
|
||||||
const index = value.findIndex((x) => x.id === references[i]);
|
const index = p.findIndex((x) => x.id === references[i]);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
result.splice(index, 1, {
|
result.splice(index, 1, {
|
||||||
...value[index],
|
...p[index],
|
||||||
status: SubscriptionStatus.idle,
|
status: SubscriptionStatus.idle,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setValue(result);
|
return result;
|
||||||
}
|
});
|
||||||
}, [socketMessage]);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const ws = WebSocketManager.getInstance();
|
||||||
|
ws.subscribe('runSubscriptionEnd', handleMessage);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
ws.unsubscribe('runSubscriptionEnd', handleMessage);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (logSubscription) {
|
if (logSubscription) {
|
||||||
|
|
8
src/utils/type.ts
Normal file
8
src/utils/type.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
export type SockMessageType =
|
||||||
|
| 'ping'
|
||||||
|
| 'installDependence'
|
||||||
|
| 'uninstallDependence'
|
||||||
|
| 'updateSystemVersion'
|
||||||
|
| 'manuallyRunScript'
|
||||||
|
| 'runSubscriptionEnd'
|
||||||
|
| 'reloadSystem';
|
154
src/utils/websocket.ts
Normal file
154
src/utils/websocket.ts
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
import SockJS from 'sockjs-client';
|
||||||
|
import { SockMessageType } from './type';
|
||||||
|
|
||||||
|
class WebSocketManager {
|
||||||
|
private static instance: WebSocketManager | null = null;
|
||||||
|
private url: string;
|
||||||
|
private socket: WebSocket | null = null;
|
||||||
|
private subscriptions: Map<SockMessageType, Set<(p: any) => void>> = new Map();
|
||||||
|
private options: {
|
||||||
|
maxReconnectAttempts: number;
|
||||||
|
reconnectInterval: number;
|
||||||
|
heartbeatInterval: number;
|
||||||
|
};
|
||||||
|
private reconnectAttempts: number = 0;
|
||||||
|
private heartbeatTimeout: NodeJS.Timeout | null = null;
|
||||||
|
private state: 'closed' | 'connecting' | 'open' = 'closed';
|
||||||
|
|
||||||
|
constructor(url: string, options: Partial<typeof WebSocketManager.prototype.options> = {}) {
|
||||||
|
this.url = url;
|
||||||
|
this.options = {
|
||||||
|
maxReconnectAttempts: options.maxReconnectAttempts || 5,
|
||||||
|
reconnectInterval: options.reconnectInterval || 3000,
|
||||||
|
heartbeatInterval: options.heartbeatInterval || 30000,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getInstance(url: string = '', options?: Partial<typeof WebSocketManager.prototype.options>): WebSocketManager {
|
||||||
|
if (!WebSocketManager.instance) {
|
||||||
|
WebSocketManager.instance = new WebSocketManager(url, options);
|
||||||
|
}
|
||||||
|
return WebSocketManager.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async init() {
|
||||||
|
try {
|
||||||
|
this.state = 'connecting';
|
||||||
|
this.emit('connecting');
|
||||||
|
|
||||||
|
while (this.reconnectAttempts < this.options.maxReconnectAttempts) {
|
||||||
|
this.socket = new SockJS(this.url);
|
||||||
|
this.setupEventListeners();
|
||||||
|
this.startHeartbeat();
|
||||||
|
await this.waitForClose();
|
||||||
|
this.stopHeartbeat();
|
||||||
|
this.socket = null;
|
||||||
|
this.reconnectAttempts++;
|
||||||
|
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, this.options.reconnectInterval));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.handleError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupEventListeners() {
|
||||||
|
if (!this.socket) return;
|
||||||
|
|
||||||
|
this.socket.onopen = () => {
|
||||||
|
this.state = 'open';
|
||||||
|
this.emit('open');
|
||||||
|
};
|
||||||
|
|
||||||
|
this.socket.onmessage = (event) => {
|
||||||
|
const message = JSON.parse(event.data);
|
||||||
|
this.dispatchMessage(message);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.socket.onclose = () => {
|
||||||
|
this.state = 'closed';
|
||||||
|
this.emit('close');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async waitForClose() {
|
||||||
|
while (this.socket?.readyState !== SockJS.CLOSED) {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public subscribe(topic: SockMessageType, callback: (v: any) => void) {
|
||||||
|
const topicSubscriptions = this.subscriptions.get(topic) || new Set();
|
||||||
|
|
||||||
|
if (!topicSubscriptions.has(callback)) {
|
||||||
|
topicSubscriptions.add(callback);
|
||||||
|
this.subscriptions.set(topic, topicSubscriptions);
|
||||||
|
|
||||||
|
const subscriptionMessage = { action: 'subscribe', topic };
|
||||||
|
this.send(subscriptionMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsubscribe(topic: SockMessageType, callback: (v: any) => void) {
|
||||||
|
const topicSubscriptions = this.subscriptions.get(topic) || new Set();
|
||||||
|
if (topicSubscriptions.has(callback)) {
|
||||||
|
topicSubscriptions.delete(callback);
|
||||||
|
|
||||||
|
const unsubscribeMessage = { action: 'unsubscribe', topic };
|
||||||
|
this.send(unsubscribeMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public send(message: any) {
|
||||||
|
if (this.socket?.readyState === SockJS.OPEN) {
|
||||||
|
this.socket.send(JSON.stringify(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private dispatchMessage(message: any) {
|
||||||
|
const { type, ...others } = message;
|
||||||
|
const topicSubscriptions = this.subscriptions.get(type) || new Set();
|
||||||
|
|
||||||
|
[...topicSubscriptions].forEach((callback) => callback(others));
|
||||||
|
}
|
||||||
|
|
||||||
|
private startHeartbeat() {
|
||||||
|
this.heartbeatTimeout = setInterval(() => {
|
||||||
|
if (this.socket?.readyState === SockJS.OPEN) {
|
||||||
|
this.socket.send(JSON.stringify({ type: 'heartbeat' }));
|
||||||
|
}
|
||||||
|
}, this.options.heartbeatInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
private stopHeartbeat() {
|
||||||
|
if (this.heartbeatTimeout) {
|
||||||
|
clearInterval(this.heartbeatTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public close() {
|
||||||
|
if (this.socket) {
|
||||||
|
this.state = 'closed';
|
||||||
|
this.stopHeartbeat();
|
||||||
|
this.socket.close();
|
||||||
|
this.emit('close');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleError(error: any) {
|
||||||
|
console.error('WebSocket错误:', error);
|
||||||
|
this.emit('error', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public on(event: string, listener: Function) {
|
||||||
|
// this.addListener(event, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public emit(event: string, data?: any) {
|
||||||
|
// this.listeners(event).forEach((listener) => listener(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WebSocketManager;
|
Loading…
Reference in New Issue
Block a user