mirror of
https://github.com/louislam/dockge.git
synced 2025-03-25 02:30:49 +00:00
330 lines
10 KiB
TypeScript
330 lines
10 KiB
TypeScript
import { io } from "socket.io-client";
|
|
import { Socket } from "socket.io-client";
|
|
import { defineComponent } from "vue";
|
|
import jwtDecode from "jwt-decode";
|
|
import { Terminal } from "@xterm/xterm";
|
|
|
|
let socket : Socket;
|
|
|
|
let terminalMap : Map<string, Terminal> = new Map();
|
|
|
|
export default defineComponent({
|
|
data() {
|
|
return {
|
|
socketIO: {
|
|
token: null,
|
|
firstConnect: true,
|
|
connected: false,
|
|
connectCount: 0,
|
|
initedSocketIO: false,
|
|
connectionErrorMsg: `${this.$t("Cannot connect to the socket server.")} ${this.$t("Reconnecting...")}`,
|
|
showReverseProxyGuide: true,
|
|
connecting: false,
|
|
},
|
|
info: {
|
|
|
|
},
|
|
remember: (localStorage.remember !== "0"),
|
|
loggedIn: false,
|
|
allowLoginDialog: false,
|
|
username: null,
|
|
stackList: {},
|
|
composeTemplate: "",
|
|
};
|
|
},
|
|
computed: {
|
|
usernameFirstChar() {
|
|
if (typeof this.username == "string" && this.username.length >= 1) {
|
|
return this.username.charAt(0).toUpperCase();
|
|
} else {
|
|
return "🐻";
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Frontend Version
|
|
* It should be compiled to a static value while building the frontend.
|
|
* Please see ./frontend/vite.config.ts, it is defined via vite.js
|
|
* @returns {string}
|
|
*/
|
|
frontendVersion() {
|
|
// eslint-disable-next-line no-undef
|
|
return FRONTEND_VERSION;
|
|
},
|
|
|
|
/**
|
|
* Are both frontend and backend in the same version?
|
|
* @returns {boolean}
|
|
*/
|
|
isFrontendBackendVersionMatched() {
|
|
if (!this.info.version) {
|
|
return true;
|
|
}
|
|
return this.info.version === this.frontendVersion;
|
|
},
|
|
|
|
},
|
|
watch: {
|
|
remember() {
|
|
localStorage.remember = (this.remember) ? "1" : "0";
|
|
},
|
|
|
|
// Reload the SPA if the server version is changed.
|
|
"info.version"(to, from) {
|
|
if (from && from !== to) {
|
|
window.location.reload();
|
|
}
|
|
},
|
|
},
|
|
created() {
|
|
this.initSocketIO();
|
|
},
|
|
mounted() {
|
|
return;
|
|
|
|
},
|
|
methods: {
|
|
/**
|
|
* Initialize connection to socket server
|
|
* @param bypass Should the check for if we
|
|
* are on a status page be bypassed?
|
|
*/
|
|
initSocketIO(bypass = false) {
|
|
// No need to re-init
|
|
if (this.socketIO.initedSocketIO) {
|
|
return;
|
|
}
|
|
|
|
this.socketIO.initedSocketIO = true;
|
|
let url : string;
|
|
const env = process.env.NODE_ENV || "production";
|
|
if (env === "development" || localStorage.dev === "dev") {
|
|
url = location.protocol + "//" + location.hostname + ":5001";
|
|
} else {
|
|
url = location.protocol + "//" + location.host;
|
|
}
|
|
|
|
let connectingMsgTimeout = setTimeout(() => {
|
|
this.socketIO.connecting = true;
|
|
}, 1500);
|
|
|
|
socket = io(url, {
|
|
transports: [ "websocket", "polling" ]
|
|
});
|
|
|
|
socket.on("connect", () => {
|
|
console.log("Connected to the socket server");
|
|
|
|
clearTimeout(connectingMsgTimeout);
|
|
this.socketIO.connecting = false;
|
|
|
|
this.socketIO.connectCount++;
|
|
this.socketIO.connected = true;
|
|
this.socketIO.showReverseProxyGuide = false;
|
|
const token = this.storage().token;
|
|
|
|
if (token) {
|
|
if (token !== "autoLogin") {
|
|
console.log("Logging in by token");
|
|
this.loginByToken(token);
|
|
} else {
|
|
// Timeout if it is not actually auto login
|
|
setTimeout(() => {
|
|
if (! this.loggedIn) {
|
|
this.allowLoginDialog = true;
|
|
this.storage().removeItem("token");
|
|
}
|
|
}, 5000);
|
|
}
|
|
} else {
|
|
this.allowLoginDialog = true;
|
|
}
|
|
|
|
this.socketIO.firstConnect = false;
|
|
});
|
|
|
|
socket.on("disconnect", () => {
|
|
console.log("disconnect");
|
|
this.socketIO.connectionErrorMsg = "Lost connection to the socket server. Reconnecting...";
|
|
this.socketIO.connected = false;
|
|
});
|
|
|
|
socket.on("connect_error", (err) => {
|
|
console.error(`Failed to connect to the backend. Socket.io connect_error: ${err.message}`);
|
|
this.socketIO.connectionErrorMsg = `${this.$t("Cannot connect to the socket server.")} [${err}] ${this.$t("reconnecting...")}`;
|
|
this.socketIO.showReverseProxyGuide = true;
|
|
this.socketIO.connected = false;
|
|
this.socketIO.firstConnect = false;
|
|
this.socketIO.connecting = false;
|
|
});
|
|
|
|
// Custom Events
|
|
|
|
socket.on("info", (info) => {
|
|
this.info = info;
|
|
});
|
|
|
|
socket.on("autoLogin", () => {
|
|
this.loggedIn = true;
|
|
this.storage().token = "autoLogin";
|
|
this.socketIO.token = "autoLogin";
|
|
this.allowLoginDialog = false;
|
|
this.afterLogin();
|
|
});
|
|
|
|
socket.on("setup", () => {
|
|
console.log("setup");
|
|
this.$router.push("/setup");
|
|
});
|
|
|
|
socket.on("terminalWrite", (terminalName, data) => {
|
|
const terminal = terminalMap.get(terminalName);
|
|
if (!terminal) {
|
|
//console.error("Terminal not found: " + terminalName);
|
|
return;
|
|
}
|
|
terminal.write(data);
|
|
});
|
|
|
|
socket.on("stackList", (res) => {
|
|
if (res.ok) {
|
|
this.stackList = res.stackList;
|
|
}
|
|
});
|
|
|
|
socket.on("stackStatusList", (res) => {
|
|
if (res.ok) {
|
|
for (let stackName in res.stackStatusList) {
|
|
const stackObj = this.stackList[stackName];
|
|
if (stackObj) {
|
|
stackObj.status = res.stackStatusList[stackName];
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
socket.on("refresh", () => {
|
|
location.reload();
|
|
});
|
|
},
|
|
|
|
/**
|
|
* The storage currently in use
|
|
* @returns Current storage
|
|
*/
|
|
storage() : Storage {
|
|
return (this.remember) ? localStorage : sessionStorage;
|
|
},
|
|
|
|
getSocket() : Socket {
|
|
return socket;
|
|
},
|
|
|
|
/**
|
|
* Get payload of JWT cookie
|
|
* @returns {(object | undefined)} JWT payload
|
|
*/
|
|
getJWTPayload() {
|
|
const jwtToken = this.storage().token;
|
|
|
|
if (jwtToken && jwtToken !== "autoLogin") {
|
|
return jwtDecode(jwtToken);
|
|
}
|
|
return undefined;
|
|
},
|
|
|
|
/**
|
|
* Send request to log user in
|
|
* @param {string} username Username to log in with
|
|
* @param {string} password Password to log in with
|
|
* @param {string} token User token
|
|
* @param {loginCB} callback Callback to call with result
|
|
* @returns {void}
|
|
*/
|
|
login(username : string, password : string, token : string, callback) {
|
|
this.getSocket().emit("login", {
|
|
username,
|
|
password,
|
|
token,
|
|
}, (res) => {
|
|
if (res.tokenRequired) {
|
|
callback(res);
|
|
}
|
|
|
|
if (res.ok) {
|
|
this.storage().token = res.token;
|
|
this.socketIO.token = res.token;
|
|
this.loggedIn = true;
|
|
this.username = this.getJWTPayload()?.username;
|
|
|
|
this.afterLogin();
|
|
|
|
// Trigger Chrome Save Password
|
|
history.pushState({}, "");
|
|
}
|
|
|
|
callback(res);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Log in using a token
|
|
* @param {string} token Token to log in with
|
|
* @returns {void}
|
|
*/
|
|
loginByToken(token : string) {
|
|
socket.emit("loginByToken", token, (res) => {
|
|
this.allowLoginDialog = true;
|
|
|
|
if (! res.ok) {
|
|
this.logout();
|
|
} else {
|
|
this.loggedIn = true;
|
|
this.username = this.getJWTPayload()?.username;
|
|
this.afterLogin();
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Log out of the web application
|
|
* @returns {void}
|
|
*/
|
|
logout() {
|
|
socket.emit("logout", () => { });
|
|
this.storage().removeItem("token");
|
|
this.socketIO.token = null;
|
|
this.loggedIn = false;
|
|
this.username = null;
|
|
this.clearData();
|
|
},
|
|
|
|
/**
|
|
* @returns {void}
|
|
*/
|
|
clearData() {
|
|
|
|
},
|
|
|
|
afterLogin() {
|
|
|
|
},
|
|
|
|
bindTerminal(terminalName : string, terminal : Terminal) {
|
|
// Load terminal, get terminal screen
|
|
socket.emit("terminalJoin", terminalName, (res) => {
|
|
if (res.ok) {
|
|
terminal.write(res.buffer);
|
|
terminalMap.set(terminalName, terminal);
|
|
} else {
|
|
this.toastRes(res);
|
|
}
|
|
});
|
|
},
|
|
|
|
unbindTerminal(terminalName : string) {
|
|
terminalMap.delete(terminalName);
|
|
},
|
|
|
|
}
|
|
});
|