mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
moving model functionality into session.ts
This commit is contained in:
+27
-112
@@ -6,75 +6,8 @@ import {boundMethod} from "autobind-decorator";
|
|||||||
import * as dayjs from 'dayjs'
|
import * as dayjs from 'dayjs'
|
||||||
import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components";
|
import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components";
|
||||||
import cn from "classnames"
|
import cn from "classnames"
|
||||||
import {GlobalWS} from "./ws";
|
import {TermWrap} from "./term";
|
||||||
import {TermWrap, getOrCreateTermWrap} from "./term";
|
import {getDefaultSession} from "./session";
|
||||||
|
|
||||||
type LineType = {
|
|
||||||
sessionid : string,
|
|
||||||
windowid : string,
|
|
||||||
lineid : number,
|
|
||||||
ts : number,
|
|
||||||
userid : string,
|
|
||||||
linetype : string,
|
|
||||||
text : string,
|
|
||||||
cmdid : string,
|
|
||||||
cmdtext : string,
|
|
||||||
isnew : boolean,
|
|
||||||
};
|
|
||||||
|
|
||||||
type SessionType = {
|
|
||||||
sessionid : string;
|
|
||||||
name : string;
|
|
||||||
windows : WindowType[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type WindowType = {
|
|
||||||
windowid : string;
|
|
||||||
name : string;
|
|
||||||
lines : LineType[];
|
|
||||||
};
|
|
||||||
|
|
||||||
var GlobalUser = "sawka";
|
|
||||||
var GSessionId = "47445c53-cfcf-4943-8339-2c04447f20a1";
|
|
||||||
var GWindowId = "1";
|
|
||||||
|
|
||||||
var GlobalLines = mobx.observable.box([
|
|
||||||
{sessionid: GSessionId, windowid: GWindowId, lineid: 1, userid: "sawka", ts: 1654631122000, linetype: "text", text: "hello"},
|
|
||||||
{sessionid: GSessionId, windowid: GWindowId, lineid: 2, userid: "sawka", ts: 1654631125000, linetype: "text", text: "again"},
|
|
||||||
]);
|
|
||||||
|
|
||||||
function fetchJsonData(resp : any, ctErr : boolean) : Promise<any> {
|
|
||||||
let contentType = resp.headers.get("Content-Type");
|
|
||||||
if (contentType != null && contentType.startsWith("application/json")) {
|
|
||||||
return resp.text().then((textData) => {
|
|
||||||
try {
|
|
||||||
return JSON.parse(textData);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
let errMsg = sprintf("Unparseable JSON: " + err.message);
|
|
||||||
let rtnErr = new Error(errMsg);
|
|
||||||
throw rtnErr;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (ctErr) {
|
|
||||||
throw new Error("non-json content-type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleJsonFetchResponse(url : URL, resp : any) : Promise<any> {
|
|
||||||
if (!resp.ok) {
|
|
||||||
let errData = fetchJsonData(resp, false);
|
|
||||||
if (errData && errData["error"]) {
|
|
||||||
throw new Error(errData["error"])
|
|
||||||
}
|
|
||||||
let errMsg = sprintf("Bad status code response from fetch '%s': %d %s", url.toString(), resp.status, resp.statusText);
|
|
||||||
let rtnErr = new Error(errMsg);
|
|
||||||
throw rtnErr;
|
|
||||||
}
|
|
||||||
let rtnData = fetchJsonData(resp, true);
|
|
||||||
return rtnData;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mobxReact.observer
|
@mobxReact.observer
|
||||||
class LineMeta extends React.Component<{line : LineType}, {}> {
|
class LineMeta extends React.Component<{line : LineType}, {}> {
|
||||||
@@ -91,13 +24,13 @@ class LineMeta extends React.Component<{line : LineType}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@mobxReact.observer
|
@mobxReact.observer
|
||||||
class LineText extends React.Component<{line : LineType}, {}> {
|
class LineText extends React.Component<{line : LineType, session : Session}, {}> {
|
||||||
render() {
|
render() {
|
||||||
let line = this.props.line;
|
let line = this.props.line;
|
||||||
return (
|
return (
|
||||||
<div className="line line-text">
|
<div className="line line-text">
|
||||||
<div className="avatar">
|
<div className="avatar">
|
||||||
M
|
S
|
||||||
</div>
|
</div>
|
||||||
<div className="line-content">
|
<div className="line-content">
|
||||||
<div className="meta">
|
<div className="meta">
|
||||||
@@ -114,20 +47,16 @@ class LineText extends React.Component<{line : LineType}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@mobxReact.observer
|
@mobxReact.observer
|
||||||
class LineCmd extends React.Component<{line : LineType}, {}> {
|
class LineCmd extends React.Component<{line : LineType, session : Session}, {}> {
|
||||||
termWrap : TermWrap;
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
let {line} = this.props;
|
|
||||||
this.termWrap = new TermWrap(line.sessionid, line.cmdid, line.windowid, line.lineid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
let {line} = this.props;
|
let {session, line} = this.props;
|
||||||
let termElem = document.getElementById(this.getId());
|
let termElem = document.getElementById(this.getId());
|
||||||
this.termWrap.connectToElem(termElem);
|
let termWrap = session.getTermWrap(line);
|
||||||
this.termWrap.reloadTerminal(0);
|
termWrap.connectToElem(termElem);
|
||||||
if (line.isnew) {
|
if (line.isnew) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
let lineElem = document.getElementById("line-" + this.getId());
|
let lineElem = document.getElementById("line-" + this.getId());
|
||||||
@@ -137,7 +66,7 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
|
|||||||
})();
|
})();
|
||||||
}, 100);
|
}, 100);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.termWrap.reloadTerminal(0);
|
termWrap.reloadTerminal(0);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,7 +78,9 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
|
|||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
doRefresh() {
|
doRefresh() {
|
||||||
this.termWrap.reloadTerminal(500);
|
let {session, line} = this.props;
|
||||||
|
let termWrap = session.getTermWrap(line);
|
||||||
|
termWrap.reloadTerminal(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
@@ -169,14 +100,15 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let {line} = this.props;
|
let {session, line} = this.props;
|
||||||
let lineid = line.lineid.toString();
|
let lineid = line.lineid.toString();
|
||||||
let running = false;
|
let running = false;
|
||||||
let rows = 0;
|
let rows = 0;
|
||||||
let cols = 0;
|
let cols = 0;
|
||||||
let renderVersion = this.termWrap.getRenderVersion();
|
let termWrap = session.getTermWrap(line);
|
||||||
this.termWrap.resizeToContent();
|
let renderVersion = termWrap.getRenderVersion();
|
||||||
let termSize = this.termWrap.getSize();
|
termWrap.resizeToContent();
|
||||||
|
let termSize = termWrap.getSize();
|
||||||
return (
|
return (
|
||||||
<div className="line line-cmd" id={"line-" + this.getId()}>
|
<div className="line line-cmd" id={"line-" + this.getId()}>
|
||||||
<div className={cn("avatar",{"num4": lineid.length == 4}, {"num5": lineid.length >= 5}, {"running": running})}>
|
<div className={cn("avatar",{"num4": lineid.length == 4}, {"num5": lineid.length >= 5}, {"running": running})}>
|
||||||
@@ -186,10 +118,10 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
|
|||||||
<div className="meta">
|
<div className="meta">
|
||||||
<div className="user">{line.userid}</div>
|
<div className="user">{line.userid}</div>
|
||||||
<div className="ts">{dayjs(line.ts).format("hh:mm:ss a")}</div>
|
<div className="ts">{dayjs(line.ts).format("hh:mm:ss a")}</div>
|
||||||
<div className="metapart-mono">{line.cmdid} <If condition={termSize.rows > 0}>({termSize.rows}x{termSize.cols})</If> {this.termWrap.ptyPos} bytes, v{renderVersion}</div>
|
<div className="metapart-mono">{line.cmdid} <If condition={termSize.rows > 0}>({termSize.rows}x{termSize.cols})</If> {termWrap.ptyPos} bytes, v{renderVersion}</div>
|
||||||
<div className="metapart-mono cmdtext">> {this.singleLineCmdText(line.cmdtext)}</div>
|
<div className="metapart-mono cmdtext">> {this.singleLineCmdText(line.cmdtext)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={cn("terminal-wrapper", {"focus": this.termWrap.isFocused.get()})}>
|
<div className={cn("terminal-wrapper", {"focus": termWrap.isFocused.get()})}>
|
||||||
<div className="terminal" id={this.getId()}></div>
|
<div className="terminal" id={this.getId()}></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -202,7 +134,7 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@mobxReact.observer
|
@mobxReact.observer
|
||||||
class Line extends React.Component<{line : LineType}, {}> {
|
class Line extends React.Component<{line : LineType, session : Session}, {}> {
|
||||||
render() {
|
render() {
|
||||||
let line = this.props.line;
|
let line = this.props.line;
|
||||||
if (line.linetype == "text") {
|
if (line.linetype == "text") {
|
||||||
@@ -216,7 +148,7 @@ class Line extends React.Component<{line : LineType}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@mobxReact.observer
|
@mobxReact.observer
|
||||||
class CmdInput extends React.Component<{sessionid : string, windowid : string}, {}> {
|
class CmdInput extends React.Component<{session : Session, windowid : string}, {}> {
|
||||||
curLine : mobx.IObservableValue<string> = mobx.observable("", {name: "command-line"});
|
curLine : mobx.IObservableValue<string> = mobx.observable("", {name: "command-line"});
|
||||||
|
|
||||||
@mobx.action @boundMethod
|
@mobx.action @boundMethod
|
||||||
@@ -241,22 +173,12 @@ class CmdInput extends React.Component<{sessionid : string, windowid : string},
|
|||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
doSubmitCmd() {
|
doSubmitCmd() {
|
||||||
let {sessionid, windowid} = this.props;
|
let {session, windowid} = this.props;
|
||||||
let commandStr = this.curLine.get();
|
let commandStr = this.curLine.get();
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
this.curLine.set("");
|
this.curLine.set("");
|
||||||
})();
|
})();
|
||||||
let url = sprintf("http://localhost:8080/api/run-command");
|
session.submitCommand(windowid, commandStr);
|
||||||
let data = {sessionid: sessionid, windowid: windowid, command: commandStr, userid: GlobalUser};
|
|
||||||
fetch(url, {method: "post", body: JSON.stringify(data)}).then((resp) => handleJsonFetchResponse(url, resp)).then((data) => {
|
|
||||||
mobx.action(() => {
|
|
||||||
let lines = GlobalLines.get();
|
|
||||||
data.data.line.isnew = true;
|
|
||||||
lines.push(data.data.line);
|
|
||||||
})();
|
|
||||||
}).catch((err) => {
|
|
||||||
console.log("error calling run-command", err)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -291,16 +213,16 @@ class CmdInput extends React.Component<{sessionid : string, windowid : string},
|
|||||||
class SessionView extends React.Component<{session : SessionType}, {}> {
|
class SessionView extends React.Component<{session : SessionType}, {}> {
|
||||||
render() {
|
render() {
|
||||||
let session = this.props.session;
|
let session = this.props.session;
|
||||||
let window = session.windows[0];
|
let window = session.getActiveWindow();
|
||||||
let lines = window.lines;
|
let lines = window.lines;
|
||||||
return (
|
return (
|
||||||
<div className="session-view">
|
<div className="session-view">
|
||||||
<div className="lines">
|
<div className="lines">
|
||||||
<For each="line" of={lines}>
|
<For each="line" of={lines}>
|
||||||
<Line key={line.lineid} line={line}/>
|
<Line key={line.lineid} line={line} session={session}/>
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
<CmdInput sessionid={session.sessionid} windowid={window.windowid}/>
|
<CmdInput session={session} windowid={window.windowid}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -313,14 +235,7 @@ class Main extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let windowLines = GlobalLines.get();
|
let session = getDefaultSession();
|
||||||
let session : SessionType = {
|
|
||||||
sessionid: GSessionId,
|
|
||||||
name: "default",
|
|
||||||
windows: [
|
|
||||||
{windowid: GWindowId, name: "default", lines: windowLines},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<div className="main">
|
<div className="main">
|
||||||
<h1 className="title scripthaus-logo-small">
|
<h1 className="title scripthaus-logo-small">
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import * as mobx from "mobx";
|
||||||
|
import {sprintf} from "sprintf-js";
|
||||||
|
import {boundMethod} from "autobind-decorator";
|
||||||
|
import {handleJsonFetchResponse} from "./util";
|
||||||
|
import {GlobalWS} from "./ws";
|
||||||
|
import {TermWrap, makeTermKey} from "./term";
|
||||||
|
|
||||||
|
var GlobalUser = "sawka";
|
||||||
|
var GSessionId = "47445c53-cfcf-4943-8339-2c04447f20a1";
|
||||||
|
var GWindowId = "1";
|
||||||
|
|
||||||
|
var GlobalLines = mobx.observable.box([
|
||||||
|
{sessionid: GSessionId, windowid: GWindowId, lineid: 1, userid: "sawka", ts: 1654631122000, linetype: "text", text: "hello"},
|
||||||
|
{sessionid: GSessionId, windowid: GWindowId, lineid: 2, userid: "sawka", ts: 1654631125000, linetype: "text", text: "again"},
|
||||||
|
]);
|
||||||
|
|
||||||
|
type LineType = {
|
||||||
|
sessionid : string,
|
||||||
|
windowid : string,
|
||||||
|
lineid : number,
|
||||||
|
ts : number,
|
||||||
|
userid : string,
|
||||||
|
linetype : string,
|
||||||
|
text : string,
|
||||||
|
cmdid : string,
|
||||||
|
cmdtext : string,
|
||||||
|
isnew : boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
type WindowType = {
|
||||||
|
sessionid : string;
|
||||||
|
windowid : string;
|
||||||
|
name : string;
|
||||||
|
lines : LineType[];
|
||||||
|
};
|
||||||
|
|
||||||
|
class Session {
|
||||||
|
sessionid : string;
|
||||||
|
name : string;
|
||||||
|
windows : WindowType[];
|
||||||
|
activeWindowId : string;
|
||||||
|
termMap : Record<string, TermWrap> = {};
|
||||||
|
|
||||||
|
submitCommand(windowid : string, commandStr : string) {
|
||||||
|
let url = sprintf("http://localhost:8080/api/run-command");
|
||||||
|
let data = {sessionid: this.sessionid, windowid: windowid, command: commandStr, userid: GlobalUser};
|
||||||
|
fetch(url, {method: "post", body: JSON.stringify(data)}).then((resp) => handleJsonFetchResponse(url, resp)).then((data) => {
|
||||||
|
mobx.action(() => {
|
||||||
|
let lines = GlobalLines.get();
|
||||||
|
data.data.line.isnew = true;
|
||||||
|
lines.push(data.data.line);
|
||||||
|
})();
|
||||||
|
}).catch((err) => {
|
||||||
|
console.log("error calling run-command", err)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getTermWrap(line : LineType) {
|
||||||
|
let termKey = makeTermKey(line.sessionid, line.cmdid, line.windowid, line.lineid);
|
||||||
|
let termWrap = this.termMap[termKey];
|
||||||
|
if (termWrap != null) {
|
||||||
|
return termWrap;
|
||||||
|
}
|
||||||
|
termWrap = new TermWrap(line.sessionid, line.cmdid, line.windowid, line.lineid);
|
||||||
|
this.termMap[termKey] = termWrap;
|
||||||
|
termWrap.initialized = true;
|
||||||
|
termWrap.reloadTerminal(0);
|
||||||
|
return termWrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
getActiveWindow() : WindowType {
|
||||||
|
if (this.windows == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (let i=0; i<this.windows.length; i++) {
|
||||||
|
if (this.windows[i].windowid == this.activeWindowId) {
|
||||||
|
return this.windows[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDefaultSession() : Session {
|
||||||
|
let windowLines = GlobalLines.get();
|
||||||
|
let session = new Session();
|
||||||
|
session.sessionid = GSessionId;
|
||||||
|
session.name = "default";
|
||||||
|
session.activeWindowId = GWindowId;
|
||||||
|
session.windows = [
|
||||||
|
{sessionid: GSessionId, windowid: GWindowId, name: "default", lines: windowLines},
|
||||||
|
];
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.getDefaultSession = getDefaultSession;
|
||||||
|
|
||||||
|
export {Session, getDefaultSession};
|
||||||
|
export type {LineType, WindowType};
|
||||||
+1
-20
@@ -4,19 +4,6 @@ import {sprintf} from "sprintf-js";
|
|||||||
import {boundMethod} from "autobind-decorator";
|
import {boundMethod} from "autobind-decorator";
|
||||||
import {GlobalWS} from "./ws";
|
import {GlobalWS} from "./ws";
|
||||||
|
|
||||||
//
|
|
||||||
var TermMap : Record<string, TermWrap>;
|
|
||||||
|
|
||||||
function getOrCreateTermWrap(sessionId : string, cmdId : string, windowId : string, lineid : number) : TermWrap {
|
|
||||||
let termKey = makeTermKey(sessionId, cmdId, windowId, lineid);
|
|
||||||
let termWrap = TermMap[termKey];
|
|
||||||
if (termWrap != null) {
|
|
||||||
return termWrap;
|
|
||||||
}
|
|
||||||
termWrap = new TermWrap(sessionId, cmdId, windowId, lineid);
|
|
||||||
return termWrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeTermKey(sessionId : string, cmdId : string, windowId : string, lineid : number) : string {
|
function makeTermKey(sessionId : string, cmdId : string, windowId : string, lineid : number) : string {
|
||||||
return sprintf("%s/%s/%s/%s", sessionId, cmdId, windowId, lineid);
|
return sprintf("%s/%s/%s/%s", sessionId, cmdId, windowId, lineid);
|
||||||
}
|
}
|
||||||
@@ -57,7 +44,6 @@ class TermWrap {
|
|||||||
this.windowId = windowId;
|
this.windowId = windowId;
|
||||||
this.lineid = lineid;
|
this.lineid = lineid;
|
||||||
this.terminal = new Terminal({rows: 2, cols: 80});
|
this.terminal = new Terminal({rows: 2, cols: 80});
|
||||||
TermMap[cmdId] = this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
@@ -150,9 +136,4 @@ class TermWrap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.TermMap == null) {
|
export {TermWrap, makeTermKey};
|
||||||
TermMap = {};
|
|
||||||
window.TermMap = TermMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
export {TermWrap, TermMap, makeTermKey, getOrCreateTermWrap};
|
|
||||||
|
|||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
function fetchJsonData(resp : any, ctErr : boolean) : Promise<any> {
|
||||||
|
let contentType = resp.headers.get("Content-Type");
|
||||||
|
if (contentType != null && contentType.startsWith("application/json")) {
|
||||||
|
return resp.text().then((textData) => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(textData);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
let errMsg = sprintf("Unparseable JSON: " + err.message);
|
||||||
|
let rtnErr = new Error(errMsg);
|
||||||
|
throw rtnErr;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (ctErr) {
|
||||||
|
throw new Error("non-json content-type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleJsonFetchResponse(url : URL, resp : any) : Promise<any> {
|
||||||
|
if (!resp.ok) {
|
||||||
|
let errData = fetchJsonData(resp, false);
|
||||||
|
if (errData && errData["error"]) {
|
||||||
|
throw new Error(errData["error"])
|
||||||
|
}
|
||||||
|
let errMsg = sprintf("Bad status code response from fetch '%s': %d %s", url.toString(), resp.status, resp.statusText);
|
||||||
|
let rtnErr = new Error(errMsg);
|
||||||
|
throw rtnErr;
|
||||||
|
}
|
||||||
|
let rtnData = fetchJsonData(resp, true);
|
||||||
|
return rtnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {handleJsonFetchResponse};
|
||||||
Reference in New Issue
Block a user