mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
When client is disconnected change log to show last 50 lines of wavesrv.log (#210)
* improved disconnected modal * wrap pre with div * make number of a lines param a constant * revert app.tsx * use tail command and capture the output instead * reset TabSwitcherModal
This commit is contained in:
parent
d1319c0a2c
commit
b2a1bb3818
@ -68,7 +68,6 @@ class App extends React.Component<{}, {}> {
|
||||
}
|
||||
|
||||
render() {
|
||||
let clientSettingsModal = GlobalModel.clientSettingsModal.get();
|
||||
let remotesModel = GlobalModel.remotesModel;
|
||||
let disconnected = !GlobalModel.ws.open.get() || !GlobalModel.waveSrvRunning.get();
|
||||
let hasClientStop = GlobalModel.getHasClientStop();
|
||||
|
@ -1085,16 +1085,18 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
}
|
||||
|
||||
interface ModalHeaderProps {
|
||||
onClose: () => void;
|
||||
onClose?: () => void;
|
||||
title: string;
|
||||
}
|
||||
|
||||
const ModalHeader: React.FC<ModalHeaderProps> = ({ onClose, title }) => (
|
||||
<div className="wave-modal-header">
|
||||
{<div className="wave-modal-title">{title}</div>}
|
||||
<If condition={onClose}>
|
||||
<IconButton theme="secondary" variant="ghost" onClick={onClose}>
|
||||
<i className="fa-sharp fa-solid fa-xmark"></i>
|
||||
</IconButton>
|
||||
</If>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -17,6 +17,10 @@
|
||||
}
|
||||
|
||||
.disconnected-modal {
|
||||
.wave-modal-content {
|
||||
.wave-modal-body {
|
||||
padding: 0;
|
||||
|
||||
.modal-content {
|
||||
footer {
|
||||
.footer-text-link {
|
||||
@ -27,14 +31,32 @@
|
||||
}
|
||||
|
||||
.inner-content {
|
||||
.ws-log {
|
||||
padding: 5px;
|
||||
background-color: @term-black;
|
||||
height: 250px;
|
||||
.log {
|
||||
height: 335px;
|
||||
margin-bottom: 20px;
|
||||
overflow: auto;
|
||||
|
||||
.ws-logline {
|
||||
&::-webkit-scrollbar-track,
|
||||
&::-webkit-scrollbar-thumb,
|
||||
&::-webkit-scrollbar-corner {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover::-webkit-scrollbar-thumb {
|
||||
display: block;
|
||||
}
|
||||
|
||||
pre {
|
||||
color: @term-white;
|
||||
background-color: @term-black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wave-modal-footer {
|
||||
button:first-child {
|
||||
color: @term-green;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,10 +27,9 @@ import {
|
||||
import * as util from "../../../util/util";
|
||||
import * as textmeasure from "../../../util/textmeasure";
|
||||
import { ClientDataType } from "../../../types/types";
|
||||
import { Session, Screen } from "../../../model/model";
|
||||
import { Screen } from "../../../model/model";
|
||||
import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg";
|
||||
|
||||
import { ReactComponent as WarningIcon } from "../../assets/icons/line/triangle-exclamation.svg";
|
||||
import shield from "../../assets/icons/shield_check.svg";
|
||||
import help from "../../assets/icons/help_filled.svg";
|
||||
import github from "../../assets/icons/github.svg";
|
||||
@ -48,6 +47,7 @@ type OArr<V> = mobx.IObservableArray<V>;
|
||||
|
||||
const RemotePtyRows = 9;
|
||||
const RemotePtyCols = 80;
|
||||
const NumOfLines = 50;
|
||||
const PasswordUnchangedSentinel = "--unchanged--";
|
||||
|
||||
@mobxReact.observer
|
||||
@ -70,7 +70,8 @@ class ModalsProvider extends React.Component {
|
||||
@mobxReact.observer
|
||||
class DisconnectedModal extends React.Component<{}, {}> {
|
||||
logRef: any = React.createRef();
|
||||
showLog: mobx.IObservableValue<boolean> = mobx.observable.box(false);
|
||||
logs: mobx.IObservableValue<string> = mobx.observable.box("");
|
||||
logInterval: NodeJS.Timeout = null;
|
||||
|
||||
@boundMethod
|
||||
restartServer() {
|
||||
@ -83,8 +84,16 @@ class DisconnectedModal extends React.Component<{}, {}> {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.logRef.current != null) {
|
||||
this.logRef.current.scrollTop = this.logRef.current.scrollHeight;
|
||||
this.fetchLogs();
|
||||
|
||||
this.logInterval = setInterval(() => {
|
||||
this.fetchLogs();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.logInterval) {
|
||||
clearInterval(this.logInterval);
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,58 +103,52 @@ class DisconnectedModal extends React.Component<{}, {}> {
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleShowLog(): void {
|
||||
mobx.action(() => {
|
||||
this.showLog.set(!this.showLog.get());
|
||||
})();
|
||||
fetchLogs() {
|
||||
GlobalModel.getLastLogs(
|
||||
NumOfLines,
|
||||
mobx.action((logs) => {
|
||||
this.logs.set(logs);
|
||||
if (this.logRef.current != null) {
|
||||
this.logRef.current.scrollTop = this.logRef.current.scrollHeight;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
let model = GlobalModel;
|
||||
let logLine: string = null;
|
||||
let idx: number = 0;
|
||||
return (
|
||||
<div className="prompt-modal disconnected-modal modal is-active">
|
||||
<div className="modal-background"></div>
|
||||
<Modal className="disconnected-modal">
|
||||
<Modal.Header title="Wave Client Disconnected" />
|
||||
<div className="wave-modal-body">
|
||||
<div className="modal-content">
|
||||
<div className="message-header">
|
||||
<div className="modal-title">Wave Client Disconnected</div>
|
||||
</div>
|
||||
<If condition={this.showLog.get()}>
|
||||
<div className="inner-content">
|
||||
<div className="ws-log" ref={this.logRef}>
|
||||
<For each="logLine" index="idx" of={GlobalModel.ws.wsLog}>
|
||||
<div key={idx} className="ws-logline">
|
||||
{logLine}
|
||||
</div>
|
||||
</For>
|
||||
<div className="log" ref={this.logRef}>
|
||||
<pre>{this.logs.get()}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</If>
|
||||
<footer>
|
||||
<div className="footer-text-link" style={{ marginLeft: 10 }} onClick={this.handleShowLog}>
|
||||
<If condition={!this.showLog.get()}>
|
||||
<i className="fa-sharp fa-solid fa-plus" /> Show Log
|
||||
</If>
|
||||
<If condition={this.showLog.get()}>
|
||||
<i className="fa-sharp fa-solid fa-minus" /> Hide Log
|
||||
</If>
|
||||
</div>
|
||||
<div className="flex-spacer" />
|
||||
<button onClick={this.tryReconnect} className="button">
|
||||
</div>
|
||||
<div className="wave-modal-footer">
|
||||
<Button
|
||||
theme="secondary"
|
||||
onClick={this.tryReconnect}
|
||||
leftIcon={
|
||||
<span className="icon">
|
||||
<i className="fa-sharp fa-solid fa-rotate" />
|
||||
</span>
|
||||
<span>Try Reconnect</span>
|
||||
</button>
|
||||
<button onClick={this.restartServer} className="button is-danger" style={{ marginLeft: 10 }}>
|
||||
<WarningIcon className="icon" />
|
||||
<span>Restart Server</span>
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
Try Reconnect
|
||||
</Button>
|
||||
<Button
|
||||
theme="secondary"
|
||||
onClick={this.restartServer}
|
||||
leftIcon={<i className="fa-sharp fa-solid fa-triangle-exclamation"></i>}
|
||||
>
|
||||
Restart Server
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -485,6 +485,33 @@ electron.ipcMain.on("reload-window", (event) => {
|
||||
return;
|
||||
});
|
||||
|
||||
electron.ipcMain.on("get-last-logs", async (event, numberOfLines) => {
|
||||
try {
|
||||
const logPath = path.join(getWaveHomeDir(), "wavesrv.log");
|
||||
const lastLines = await readLastLinesOfFile(logPath, numberOfLines);
|
||||
event.reply("last-logs", lastLines);
|
||||
} catch (err) {
|
||||
console.error("Error reading log file:", err);
|
||||
event.reply("last-logs", "Error reading log file.");
|
||||
}
|
||||
});
|
||||
|
||||
function readLastLinesOfFile(filePath, lineCount) {
|
||||
return new Promise((resolve, reject) => {
|
||||
child_process.exec(`tail -n ${lineCount} "${filePath}"`, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
reject(err.message);
|
||||
return;
|
||||
}
|
||||
if (stderr) {
|
||||
reject(stderr);
|
||||
return;
|
||||
}
|
||||
resolve(stdout);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getContextMenu(): any {
|
||||
let menu = new electron.Menu();
|
||||
let menuItem = new electron.MenuItem({ label: "Testing", click: () => console.log("click testing!") });
|
||||
|
@ -6,6 +6,10 @@ contextBridge.exposeInMainWorld("api", {
|
||||
getIsDev: () => ipcRenderer.sendSync("get-isdev"),
|
||||
getAuthKey: () => ipcRenderer.sendSync("get-authkey"),
|
||||
getWaveSrvStatus: () => ipcRenderer.sendSync("wavesrv-status"),
|
||||
getLastLogs: (numberOfLines, callback) => {
|
||||
ipcRenderer.send("get-last-logs", numberOfLines);
|
||||
ipcRenderer.once("last-logs", (event, data) => callback(data));
|
||||
},
|
||||
restartWaveSrv: () => ipcRenderer.sendSync("restart-server"),
|
||||
reloadWindow: () => ipcRenderer.sendSync("reload-window"),
|
||||
onTCmd: (callback) => ipcRenderer.on("t-cmd", callback),
|
||||
|
@ -208,6 +208,7 @@ type ElectronApi = {
|
||||
contextScreen: (screenOpts: { screenId: string }, position: { x: number; y: number }) => void;
|
||||
contextEditMenu: (position: { x: number; y: number }, opts: ContextMenuOpts) => void;
|
||||
onWaveSrvStatusChange: (callback: (status: boolean, pid: number) => void) => void;
|
||||
getLastLogs: (numOfLines: number, callback: (logs: any) => void) => void;
|
||||
};
|
||||
|
||||
function getApi(): ElectronApi {
|
||||
@ -3476,6 +3477,10 @@ class Model {
|
||||
})();
|
||||
}
|
||||
|
||||
getLastLogs(numbOfLines: number, cb: (logs: any) => void): void {
|
||||
getApi().getLastLogs(numbOfLines, cb);
|
||||
}
|
||||
|
||||
getContentHeight(context: RendererContext): number {
|
||||
let key = context.screenId + "/" + context.lineId;
|
||||
return this.termUsedRowsCache[key];
|
||||
|
Loading…
Reference in New Issue
Block a user