mirror of
https://github.com/louislam/dockge.git
synced 2026-05-21 22:12:17 +00:00
wip
This commit is contained in:
@@ -424,14 +424,27 @@ export class DockgeServer {
|
||||
}
|
||||
|
||||
sendStackList(useCache = false) {
|
||||
let stackList = Stack.getStackList(this, useCache);
|
||||
let roomList = this.io.sockets.adapter.rooms.keys();
|
||||
let map : Map<string, object> | undefined;
|
||||
|
||||
for (let room of roomList) {
|
||||
// Check if the room is a number (user id)
|
||||
if (Number(room)) {
|
||||
|
||||
// Get the list only if there is a room
|
||||
if (!map) {
|
||||
map = new Map();
|
||||
let stackList = Stack.getStackList(this, useCache);
|
||||
|
||||
for (let [ stackName, stack ] of stackList) {
|
||||
map.set(stackName, stack.toSimpleJSON());
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("server", "Send stack list to room " + room);
|
||||
this.io.to(room).emit("stackList", {
|
||||
ok: true,
|
||||
stackList: Object.fromEntries(stackList),
|
||||
stackList: Object.fromEntries(map),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,13 @@ export class MainRouter extends Router {
|
||||
res.send(server.indexHTML);
|
||||
});
|
||||
|
||||
// Robots.txt
|
||||
router.get("/robots.txt", async (_request, response) => {
|
||||
let txt = "User-agent: *\nDisallow: /";
|
||||
response.setHeader("Content-Type", "text/plain");
|
||||
response.send(txt);
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ export class DockerSocketHandler extends SocketHandler {
|
||||
server.sendStackList();
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "Deployed",
|
||||
});
|
||||
} catch (e) {
|
||||
callbackError(e, callback);
|
||||
@@ -69,6 +70,9 @@ export class DockerSocketHandler extends SocketHandler {
|
||||
}
|
||||
|
||||
const stack = Stack.getStack(server, stackName);
|
||||
|
||||
stack.startCombinedTerminal(socket);
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
stack: stack.toJSON(),
|
||||
@@ -77,6 +81,107 @@ export class DockerSocketHandler extends SocketHandler {
|
||||
callbackError(e, callback);
|
||||
}
|
||||
});
|
||||
|
||||
// requestStackList
|
||||
socket.on("requestStackList", async (callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
server.sendStackList();
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "Updated"
|
||||
});
|
||||
} catch (e) {
|
||||
callbackError(e, callback);
|
||||
}
|
||||
});
|
||||
|
||||
// startStack
|
||||
socket.on("startStack", async (stackName : unknown, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
if (typeof(stackName) !== "string") {
|
||||
throw new ValidationError("Stack name must be a string");
|
||||
}
|
||||
|
||||
const stack = Stack.getStack(server, stackName);
|
||||
await stack.start(socket);
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "Started"
|
||||
});
|
||||
server.sendStackList();
|
||||
|
||||
stack.startCombinedTerminal(socket);
|
||||
|
||||
} catch (e) {
|
||||
callbackError(e, callback);
|
||||
}
|
||||
});
|
||||
|
||||
// stopStack
|
||||
socket.on("stopStack", async (stackName : unknown, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
if (typeof(stackName) !== "string") {
|
||||
throw new ValidationError("Stack name must be a string");
|
||||
}
|
||||
|
||||
const stack = Stack.getStack(server, stackName);
|
||||
await stack.stop(socket);
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "Stopped"
|
||||
});
|
||||
server.sendStackList();
|
||||
} catch (e) {
|
||||
callbackError(e, callback);
|
||||
}
|
||||
});
|
||||
|
||||
// restartStack
|
||||
socket.on("restartStack", async (stackName : unknown, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
if (typeof(stackName) !== "string") {
|
||||
throw new ValidationError("Stack name must be a string");
|
||||
}
|
||||
|
||||
const stack = Stack.getStack(server, stackName);
|
||||
await stack.restart(socket);
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "Restarted"
|
||||
});
|
||||
server.sendStackList();
|
||||
} catch (e) {
|
||||
callbackError(e, callback);
|
||||
}
|
||||
});
|
||||
|
||||
// updateStack
|
||||
socket.on("updateStack", async (stackName : unknown, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
if (typeof(stackName) !== "string") {
|
||||
throw new ValidationError("Stack name must be a string");
|
||||
}
|
||||
|
||||
const stack = Stack.getStack(server, stackName);
|
||||
await stack.update(socket);
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "Updated"
|
||||
});
|
||||
server.sendStackList();
|
||||
} catch (e) {
|
||||
callbackError(e, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
saveStack(socket : DockgeSocket, server : DockgeServer, name : unknown, composeYAML : unknown, isAdd : unknown) : Stack {
|
||||
@@ -95,5 +200,6 @@ export class DockerSocketHandler extends SocketHandler {
|
||||
stack.save(isAdd);
|
||||
return stack;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -8,64 +8,49 @@ import fs from "fs";
|
||||
import {
|
||||
allowedCommandList,
|
||||
allowedRawKeys,
|
||||
getComposeTerminalName,
|
||||
getComposeTerminalName, getContainerExecTerminalName,
|
||||
isDev,
|
||||
PROGRESS_TERMINAL_ROWS
|
||||
} from "../util-common";
|
||||
import { MainTerminal, Terminal } from "../terminal";
|
||||
import { InteractiveTerminal, MainTerminal, Terminal } from "../terminal";
|
||||
|
||||
export class TerminalSocketHandler extends SocketHandler {
|
||||
create(socket : DockgeSocket, server : DockgeServer) {
|
||||
|
||||
socket.on("terminalInputRaw", async (key : unknown) => {
|
||||
socket.on("terminalInput", async (terminalName : unknown, cmd : unknown, errorCallback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
if (typeof(key) !== "string") {
|
||||
throw new Error("Key must be a string.");
|
||||
if (typeof(terminalName) !== "string") {
|
||||
throw new Error("Terminal name must be a string.");
|
||||
}
|
||||
|
||||
if (allowedRawKeys.includes(key)) {
|
||||
server.terminal.write(key);
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("terminalInput", async (terminalName : unknown, cmd : unknown, errorCallback : unknown) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
if (typeof(cmd) !== "string") {
|
||||
throw new Error("Command must be a string.");
|
||||
}
|
||||
|
||||
// Check if the command is allowed
|
||||
const cmdParts = cmd.split(" ");
|
||||
const executable = cmdParts[0].trim();
|
||||
log.debug("console", "Executable: " + executable);
|
||||
log.debug("console", "Executable length: " + executable.length);
|
||||
|
||||
if (!allowedCommandList.includes(executable)) {
|
||||
throw new Error("Command not allowed.");
|
||||
let terminal = Terminal.getTerminal(terminalName);
|
||||
if (terminal instanceof InteractiveTerminal) {
|
||||
terminal.write(cmd);
|
||||
} else {
|
||||
throw new Error("Terminal not found or it is not a Interactive Terminal.");
|
||||
}
|
||||
|
||||
server.terminal.write(cmd);
|
||||
} catch (e) {
|
||||
if (typeof(errorCallback) === "function") {
|
||||
errorCallback({
|
||||
ok: false,
|
||||
msg: e.message,
|
||||
});
|
||||
}
|
||||
errorCallback({
|
||||
ok: false,
|
||||
msg: e.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Create Terminal
|
||||
// Main Terminal
|
||||
socket.on("mainTerminal", async (terminalName : unknown, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
// TODO: Reset the name here, force one main terminal for now
|
||||
terminalName = "console";
|
||||
|
||||
if (typeof(terminalName) !== "string") {
|
||||
throw new ValidationError("Terminal name must be a string.");
|
||||
}
|
||||
@@ -91,7 +76,40 @@ export class TerminalSocketHandler extends SocketHandler {
|
||||
}
|
||||
});
|
||||
|
||||
// Join Terminal
|
||||
// Interactive Terminal for containers
|
||||
socket.on("interactiveTerminal", async (stackName : unknown, serviceName : unknown, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
if (typeof(stackName) !== "string") {
|
||||
throw new ValidationError("Stack name must be a string.");
|
||||
}
|
||||
|
||||
if (typeof(serviceName) !== "string") {
|
||||
throw new ValidationError("Service name must be a string.");
|
||||
}
|
||||
|
||||
const terminalName = getContainerExecTerminalName(stackName, serviceName, 0);
|
||||
let terminal = Terminal.getTerminal(terminalName);
|
||||
|
||||
if (!terminal) {
|
||||
terminal = new InteractiveTerminal(server, terminalName);
|
||||
terminal.rows = 50;
|
||||
log.debug("deployStack", "Terminal created");
|
||||
}
|
||||
|
||||
terminal.join(socket);
|
||||
terminal.start();
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
});
|
||||
} catch (e) {
|
||||
callbackError(e, callback);
|
||||
}
|
||||
});
|
||||
|
||||
// Join Output Terminal
|
||||
socket.on("terminalJoin", async (terminalName : unknown, callback) => {
|
||||
if (typeof(callback) !== "function") {
|
||||
log.debug("console", "Callback is not a function.");
|
||||
@@ -124,18 +142,9 @@ export class TerminalSocketHandler extends SocketHandler {
|
||||
|
||||
});
|
||||
|
||||
// Resize Terminal
|
||||
// TODO: Resize Terminal
|
||||
socket.on("terminalResize", async (rows : unknown) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
if (typeof(rows) !== "number") {
|
||||
throw new Error("Rows must be a number.");
|
||||
}
|
||||
log.debug("console", "Resize terminal to " + rows + " rows.");
|
||||
server.terminal.resize(rows);
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
140
backend/stack.ts
140
backend/stack.ts
@@ -5,9 +5,11 @@ import yaml from "yaml";
|
||||
import { DockgeSocket, ValidationError } from "./util-server";
|
||||
import path from "path";
|
||||
import {
|
||||
COMBINED_TERMINAL_COLS,
|
||||
COMBINED_TERMINAL_ROWS,
|
||||
CREATED_FILE,
|
||||
CREATED_STACK,
|
||||
EXITED,
|
||||
EXITED, getCombinedTerminalName,
|
||||
getComposeTerminalName,
|
||||
PROGRESS_TERMINAL_ROWS,
|
||||
RUNNING,
|
||||
@@ -24,6 +26,8 @@ export class Stack {
|
||||
protected _configFilePath?: string;
|
||||
protected server: DockgeServer;
|
||||
|
||||
protected combinedTerminal? : Terminal;
|
||||
|
||||
protected static managedStackList: Map<string, Stack> = new Map();
|
||||
|
||||
constructor(server : DockgeServer, name : string, composeYAML? : string) {
|
||||
@@ -37,7 +41,6 @@ export class Stack {
|
||||
return {
|
||||
...obj,
|
||||
composeYAML: this.composeYAML,
|
||||
isManagedByDockge: this.isManagedByDockge,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,9 +49,20 @@ export class Stack {
|
||||
name: this.name,
|
||||
status: this._status,
|
||||
tags: [],
|
||||
isManagedByDockge: this.isManagedByDockge,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status of the stack from `docker compose ps --format json`
|
||||
*/
|
||||
ps() : object {
|
||||
let res = childProcess.execSync("docker compose ps --format json", {
|
||||
cwd: this.path
|
||||
});
|
||||
return JSON.parse(res.toString());
|
||||
}
|
||||
|
||||
get isManagedByDockge() : boolean {
|
||||
if (this._configFilePath) {
|
||||
return this._configFilePath.startsWith(this.server.stackDirFullPath) && fs.existsSync(this.path) && fs.statSync(this.path).isDirectory();
|
||||
@@ -128,70 +142,29 @@ export class Stack {
|
||||
fs.writeFileSync(path.join(dir, "compose.yaml"), this.composeYAML);
|
||||
}
|
||||
|
||||
deploy(socket? : DockgeSocket) : Promise<number> {
|
||||
async deploy(socket? : DockgeSocket) : Promise<number> {
|
||||
const terminalName = getComposeTerminalName(this.name);
|
||||
log.debug("deployStack", "Terminal name: " + terminalName);
|
||||
|
||||
const terminal = new Terminal(this.server, terminalName, "docker-compose", [ "up", "-d" ], this.path);
|
||||
log.debug("deployStack", "Terminal created");
|
||||
|
||||
terminal.rows = PROGRESS_TERMINAL_ROWS;
|
||||
|
||||
if (socket) {
|
||||
terminal.join(socket);
|
||||
log.debug("deployStack", "Terminal joined");
|
||||
} else {
|
||||
log.debug("deployStack", "No socket, not joining");
|
||||
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker-compose", [ "up", "-d", "--remove-orphans" ], this.path);
|
||||
if (exitCode !== 0) {
|
||||
throw new Error("Failed to deploy, please check the terminal output for more information.");
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
terminal.onExit((exitCode : number) => {
|
||||
if (exitCode === 0) {
|
||||
resolve(exitCode);
|
||||
} else {
|
||||
reject(new Error("Failed to deploy, please check the terminal output for more information."));
|
||||
}
|
||||
});
|
||||
terminal.start();
|
||||
});
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
delete(socket?: DockgeSocket) : Promise<number> {
|
||||
// Docker compose down
|
||||
async delete(socket?: DockgeSocket) : Promise<number> {
|
||||
const terminalName = getComposeTerminalName(this.name);
|
||||
log.debug("deleteStack", "Terminal name: " + terminalName);
|
||||
|
||||
const terminal = new Terminal(this.server, terminalName, "docker-compose", [ "down" ], this.path);
|
||||
|
||||
terminal.rows = PROGRESS_TERMINAL_ROWS;
|
||||
|
||||
if (socket) {
|
||||
terminal.join(socket);
|
||||
log.debug("deployStack", "Terminal joined");
|
||||
} else {
|
||||
log.debug("deployStack", "No socket, not joining");
|
||||
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker-compose", [ "down", "--remove-orphans", "--rmi", "all" ], this.path);
|
||||
if (exitCode !== 0) {
|
||||
throw new Error("Failed to delete, please check the terminal output for more information.");
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
terminal.onExit((exitCode : number) => {
|
||||
if (exitCode === 0) {
|
||||
// Remove the stack folder
|
||||
try {
|
||||
fs.rmSync(this.path, {
|
||||
recursive: true,
|
||||
force: true
|
||||
});
|
||||
resolve(exitCode);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
} else {
|
||||
reject(new Error("Failed to delete, please check the terminal output for more information."));
|
||||
}
|
||||
});
|
||||
terminal.start();
|
||||
// Remove the stack folder
|
||||
fs.rmSync(this.path, {
|
||||
recursive: true,
|
||||
force: true
|
||||
});
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
static getStackList(server : DockgeServer, useCacheForManaged = false) : Map<string, Stack> {
|
||||
@@ -257,6 +230,10 @@ export class Stack {
|
||||
return statusList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the status string from `docker compose ls` to the status number
|
||||
* @param status
|
||||
*/
|
||||
static statusConvert(status : string) : number {
|
||||
if (status.startsWith("created")) {
|
||||
return CREATED_STACK;
|
||||
@@ -290,4 +267,53 @@ export class Stack {
|
||||
stack._configFilePath = path.resolve(dir);
|
||||
return stack;
|
||||
}
|
||||
|
||||
async start(socket: DockgeSocket) {
|
||||
const terminalName = getComposeTerminalName(this.name);
|
||||
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker-compose", [ "up", "-d", "--remove-orphans" ], this.path);
|
||||
if (exitCode !== 0) {
|
||||
throw new Error("Failed to start, please check the terminal output for more information.");
|
||||
}
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
async stop(socket: DockgeSocket) : Promise<number> {
|
||||
const terminalName = getComposeTerminalName(this.name);
|
||||
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker-compose", [ "stop" ], this.path);
|
||||
if (exitCode !== 0) {
|
||||
throw new Error("Failed to stop, please check the terminal output for more information.");
|
||||
}
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
async restart(socket: DockgeSocket) : Promise<number> {
|
||||
const terminalName = getComposeTerminalName(this.name);
|
||||
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker-compose", [ "restart" ], this.path);
|
||||
if (exitCode !== 0) {
|
||||
throw new Error("Failed to restart, please check the terminal output for more information.");
|
||||
}
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
async update(socket: DockgeSocket) {
|
||||
const terminalName = getComposeTerminalName(this.name);
|
||||
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker-compose", [ "pull" ], this.path);
|
||||
if (exitCode !== 0) {
|
||||
throw new Error("Failed to pull, please check the terminal output for more information.");
|
||||
}
|
||||
exitCode = await Terminal.exec(this.server, socket, terminalName, "docker-compose", [ "up", "-d", "--remove-orphans" ], this.path);
|
||||
if (exitCode !== 0) {
|
||||
throw new Error("Failed to restart, please check the terminal output for more information.");
|
||||
}
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
async startCombinedTerminal(socket: DockgeSocket) {
|
||||
const terminalName = getCombinedTerminalName(this.name);
|
||||
const terminal = Terminal.getOrCreateTerminal(this.server, terminalName, "docker-compose", [ "logs", "-f" ], this.path);
|
||||
terminal.rows = COMBINED_TERMINAL_ROWS;
|
||||
terminal.cols = COMBINED_TERMINAL_COLS;
|
||||
terminal.join(socket);
|
||||
terminal.start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,14 @@ import * as os from "node:os";
|
||||
import * as pty from "@homebridge/node-pty-prebuilt-multiarch";
|
||||
import { LimitQueue } from "./utils/limit-queue";
|
||||
import { DockgeSocket } from "./util-server";
|
||||
import { getCryptoRandomInt, TERMINAL_COLS, TERMINAL_ROWS } from "./util-common";
|
||||
import {
|
||||
allowedCommandList, allowedRawKeys,
|
||||
getComposeTerminalName,
|
||||
getCryptoRandomInt,
|
||||
PROGRESS_TERMINAL_ROWS,
|
||||
TERMINAL_COLS,
|
||||
TERMINAL_ROWS
|
||||
} from "./util-common";
|
||||
import { sync as commandExistsSync } from "command-exists";
|
||||
import { log } from "./log";
|
||||
|
||||
@@ -25,6 +32,7 @@ export class Terminal {
|
||||
protected callback? : (exitCode : number) => void;
|
||||
|
||||
protected _rows : number = TERMINAL_ROWS;
|
||||
protected _cols : number = TERMINAL_COLS;
|
||||
|
||||
constructor(server : DockgeServer, name : string, file : string, args : string | string[], cwd : string) {
|
||||
this.server = server;
|
||||
@@ -43,7 +51,16 @@ export class Terminal {
|
||||
|
||||
set rows(rows : number) {
|
||||
this._rows = rows;
|
||||
this.ptyProcess?.resize(TERMINAL_COLS, rows);
|
||||
this.ptyProcess?.resize(this.cols, this.rows);
|
||||
}
|
||||
|
||||
get cols() {
|
||||
return this._cols;
|
||||
}
|
||||
|
||||
set cols(cols : number) {
|
||||
this._cols = cols;
|
||||
this.ptyProcess?.resize(this.cols, this.rows);
|
||||
}
|
||||
|
||||
public start() {
|
||||
@@ -119,6 +136,30 @@ export class Terminal {
|
||||
public static getTerminal(name : string) : Terminal | undefined {
|
||||
return Terminal.terminalMap.get(name);
|
||||
}
|
||||
|
||||
public static getOrCreateTerminal(server : DockgeServer, name : string, file : string, args : string | string[], cwd : string) : Terminal {
|
||||
let terminal = Terminal.getTerminal(name);
|
||||
if (!terminal) {
|
||||
terminal = new Terminal(server, name, file, args, cwd);
|
||||
}
|
||||
return terminal;
|
||||
}
|
||||
|
||||
public static exec(server : DockgeServer, socket : DockgeSocket | undefined, terminalName : string, file : string, args : string | string[], cwd : string) : Promise<number> {
|
||||
const terminal = new Terminal(server, terminalName, file, args, cwd);
|
||||
terminal.rows = PROGRESS_TERMINAL_ROWS;
|
||||
|
||||
if (socket) {
|
||||
terminal.join(socket);
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
terminal.onExit((exitCode : number) => {
|
||||
resolve(exitCode);
|
||||
});
|
||||
terminal.start();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,7 +181,7 @@ export class InteractiveTerminal extends Terminal {
|
||||
* User interactive terminal that use bash or powershell with limited commands such as docker, ls, cd, dir
|
||||
*/
|
||||
export class MainTerminal extends InteractiveTerminal {
|
||||
constructor(server : DockgeServer, name : string, cwd : string = "./") {
|
||||
constructor(server : DockgeServer, name : string) {
|
||||
let shell;
|
||||
|
||||
if (os.platform() === "win32") {
|
||||
@@ -152,6 +193,25 @@ export class MainTerminal extends InteractiveTerminal {
|
||||
} else {
|
||||
shell = "bash";
|
||||
}
|
||||
super(server, name, shell, [], cwd);
|
||||
super(server, name, shell, [], server.stacksDir);
|
||||
}
|
||||
|
||||
public write(input : string) {
|
||||
// For like Ctrl + C
|
||||
if (allowedRawKeys.includes(input)) {
|
||||
super.write(input);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the command is allowed
|
||||
const cmdParts = input.split(" ");
|
||||
const executable = cmdParts[0].trim();
|
||||
log.debug("console", "Executable: " + executable);
|
||||
log.debug("console", "Executable length: " + executable.length);
|
||||
|
||||
if (!allowedCommandList.includes(executable)) {
|
||||
throw new Error("Command not allowed.");
|
||||
}
|
||||
super.write(input);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
import { parseDocument, Document } from "yaml";
|
||||
|
||||
let randomBytes : (numBytes: number) => Uint8Array;
|
||||
|
||||
if (typeof window !== "undefined" && window.crypto) {
|
||||
@@ -50,13 +52,13 @@ export function statusName(status : number) : string {
|
||||
export function statusNameShort(status : number) : string {
|
||||
switch (status) {
|
||||
case CREATED_FILE:
|
||||
return "draft";
|
||||
return "inactive";
|
||||
case CREATED_STACK:
|
||||
return "inactive";
|
||||
case RUNNING:
|
||||
return "active";
|
||||
case EXITED:
|
||||
return "inactive";
|
||||
return "exited";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
@@ -67,7 +69,7 @@ export function statusColor(status : number) : string {
|
||||
case CREATED_FILE:
|
||||
return "dark";
|
||||
case CREATED_STACK:
|
||||
return "danger";
|
||||
return "dark";
|
||||
case RUNNING:
|
||||
return "primary";
|
||||
case EXITED:
|
||||
@@ -78,10 +80,13 @@ export function statusColor(status : number) : string {
|
||||
}
|
||||
|
||||
export const isDev = process.env.NODE_ENV === "development";
|
||||
export const TERMINAL_COLS = 80;
|
||||
export const TERMINAL_COLS = 105;
|
||||
export const TERMINAL_ROWS = 10;
|
||||
export const PROGRESS_TERMINAL_ROWS = 8;
|
||||
|
||||
export const COMBINED_TERMINAL_COLS = 50;
|
||||
export const COMBINED_TERMINAL_ROWS = 15;
|
||||
|
||||
export const ERROR_TYPE_VALIDATION = 1;
|
||||
|
||||
export const allowedCommandList : string[] = [
|
||||
@@ -182,11 +187,68 @@ export function getComposeTerminalName(stack : string) {
|
||||
return "compose-" + stack;
|
||||
}
|
||||
|
||||
export function getCombinedTerminalName(stack : string) {
|
||||
return "combined-" + stack;
|
||||
}
|
||||
|
||||
export function getContainerTerminalName(container : string) {
|
||||
return "container-" + container;
|
||||
}
|
||||
|
||||
export function getContainerExecTerminalName(container : string, index : number) {
|
||||
export function getContainerExecTerminalName(stackName : string, container : string, index : number) {
|
||||
return "container-exec-" + container + "-" + index;
|
||||
}
|
||||
|
||||
export function copyYAMLComments(doc : Document, src : Document) {
|
||||
doc.comment = src.comment;
|
||||
doc.commentBefore = src.commentBefore;
|
||||
|
||||
if (doc && doc.contents && src && src.contents) {
|
||||
// @ts-ignore
|
||||
copyYAMLCommentsItems(doc.contents.items, src.contents.items);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy yaml comments from srcItems to items
|
||||
* Typescript is super annoying here, so I have to use any here
|
||||
* TODO: Since comments are belong to the array index, the comments will be lost if the order of the items is changed or removed or added.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function copyYAMLCommentsItems(items : any, srcItems : any) {
|
||||
if (!items || !srcItems) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const item : any = items[i];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const srcItem : any = srcItems[i];
|
||||
|
||||
if (!srcItem) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.key && srcItem.key) {
|
||||
item.key.comment = srcItem.key.comment;
|
||||
item.key.commentBefore = srcItem.key.commentBefore;
|
||||
}
|
||||
|
||||
if (srcItem.comment) {
|
||||
item.comment = srcItem.comment;
|
||||
}
|
||||
|
||||
if (item.value && srcItem.value) {
|
||||
if (typeof item.value === "object" && typeof srcItem.value === "object") {
|
||||
item.value.comment = srcItem.value.comment;
|
||||
item.value.commentBefore = srcItem.value.commentBefore;
|
||||
|
||||
if (item.value.items && srcItem.value.items) {
|
||||
copyYAMLCommentsItems(item.value.items, srcItem.value.items);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user