mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-08 03:57:31 +00:00
Merge branch 'master' of https://github.com/Azure/cosmos-explorer
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Accordion top class
|
* Accordion top class
|
||||||
*/
|
*/
|
||||||
import { Link, makeStyles, tokens } from "@fluentui/react-components";
|
import { makeStyles, tokens } from "@fluentui/react-components";
|
||||||
import { DocumentAddRegular, LinkMultipleRegular } from "@fluentui/react-icons";
|
import { DocumentAddRegular, LinkMultipleRegular } from "@fluentui/react-icons";
|
||||||
import { SampleDataImportDialog } from "Explorer/SplashScreen/SampleDataImportDialog";
|
import { SampleDataImportDialog } from "Explorer/SplashScreen/SampleDataImportDialog";
|
||||||
import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
|
import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
|
||||||
@@ -9,7 +9,6 @@ import { isFabricNative, isFabricNativeReadOnly } from "Platform/Fabric/FabricUt
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import CosmosDbBlackIcon from "../../../images/CosmosDB_black.svg";
|
import CosmosDbBlackIcon from "../../../images/CosmosDB_black.svg";
|
||||||
import LinkIcon from "../../../images/Link_blue.svg";
|
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
|
|
||||||
export interface SplashScreenProps {
|
export interface SplashScreenProps {
|
||||||
@@ -186,12 +185,12 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
|
|||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
{getSplashScreenButtons()}
|
{getSplashScreenButtons()}
|
||||||
<div className={styles.footer}>
|
{/* <div className={styles.footer}>
|
||||||
Need help?{" "}
|
Need help?{" "}
|
||||||
<Link href="https://aka.ms/cosmosdbfabricdocs" target="_blank">
|
<Link href="https://aka.ms/cosmosdbfabricdocs" target="_blank">
|
||||||
Learn more <img src={LinkIcon} alt="Learn more" />
|
Learn more <img src={LinkIcon} alt="Learn more" />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div> */}
|
||||||
</CosmosFluentProvider>
|
</CosmosFluentProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -43,32 +43,52 @@ export const startCloudShellTerminal = async (terminal: Terminal, shellType: Ter
|
|||||||
await ensureCloudShellProviderRegistered();
|
await ensureCloudShellProviderRegistered();
|
||||||
|
|
||||||
resolvedRegion = determineCloudShellRegion();
|
resolvedRegion = determineCloudShellRegion();
|
||||||
// Ask for user consent for region
|
|
||||||
const consentGranted = await askConfirmation(
|
resolvedRegion = determineCloudShellRegion();
|
||||||
terminal,
|
|
||||||
formatWarningMessage(
|
terminal.writeln(formatWarningMessage("⚠️ IMPORTANT: Azure Cloud Shell Region Notice ⚠️"));
|
||||||
"The shell environment may be operating in a region different from that of the database, which could impact performance or data compliance. Do you wish to proceed?",
|
terminal.writeln(
|
||||||
|
formatInfoMessage(
|
||||||
|
"The Cloud Shell environment will operate in a region that may differ from your database's region.",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
terminal.writeln(formatInfoMessage("This has two potential implications:"));
|
||||||
|
terminal.writeln(formatInfoMessage("1. Performance Impact:"));
|
||||||
|
terminal.writeln(
|
||||||
|
formatInfoMessage(" Commands may experience higher latency due to geographic distance between regions."),
|
||||||
|
);
|
||||||
|
terminal.writeln(formatInfoMessage("2. Data Compliance Considerations:"));
|
||||||
|
terminal.writeln(
|
||||||
|
formatInfoMessage(
|
||||||
|
" Data processed through this shell could temporarily reside in a different geographic region,",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
terminal.writeln(
|
||||||
|
formatInfoMessage(" which may affect compliance with data residency requirements or regulations specific"),
|
||||||
|
);
|
||||||
|
terminal.writeln(formatInfoMessage(" to your organization."));
|
||||||
|
terminal.writeln("");
|
||||||
|
|
||||||
|
terminal.writeln("\x1b[94mFor more information on Azure Cosmos DB data governance and compliance, please visit:");
|
||||||
|
terminal.writeln("\x1b[94mhttps://learn.microsoft.com/en-us/azure/cosmos-db/data-residency\x1b[0m");
|
||||||
|
|
||||||
|
// Ask for user consent for region
|
||||||
|
const consentGranted = await askConfirmation(terminal, formatWarningMessage("Do you wish to proceed?"));
|
||||||
|
|
||||||
// Track user decision
|
// Track user decision
|
||||||
TelemetryProcessor.trace(
|
TelemetryProcessor.trace(
|
||||||
Action.CloudShellUserConsent,
|
Action.CloudShellUserConsent,
|
||||||
consentGranted ? ActionModifiers.Success : ActionModifiers.Cancel,
|
consentGranted ? ActionModifiers.Success : ActionModifiers.Cancel,
|
||||||
{ dataExplorerArea: Areas.CloudShell },
|
{
|
||||||
|
dataExplorerArea: Areas.CloudShell,
|
||||||
|
shellType: TerminalKind[shellType],
|
||||||
|
isConsent: consentGranted,
|
||||||
|
region: resolvedRegion,
|
||||||
|
},
|
||||||
|
startKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!consentGranted) {
|
if (!consentGranted) {
|
||||||
TelemetryProcessor.traceCancel(
|
|
||||||
Action.CloudShellTerminalSession,
|
|
||||||
{
|
|
||||||
shellType: TerminalKind[shellType],
|
|
||||||
dataExplorerArea: Areas.CloudShell,
|
|
||||||
region: resolvedRegion,
|
|
||||||
isConsent: false,
|
|
||||||
},
|
|
||||||
startKey,
|
|
||||||
);
|
|
||||||
terminal.writeln(
|
terminal.writeln(
|
||||||
formatErrorMessage("Session ended. Please close this tab and initiate a new shell session if needed."),
|
formatErrorMessage("Session ended. Please close this tab and initiate a new shell session if needed."),
|
||||||
);
|
);
|
||||||
@@ -262,28 +282,27 @@ export const configureSocketConnection = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const sendTerminalStartupCommands = (socket: WebSocket, initCommands: string): void => {
|
export const sendTerminalStartupCommands = (socket: WebSocket, initCommands: string): void => {
|
||||||
|
// ensures connections don't remain open indefinitely by implementing an automatic timeout after 120 minutes.
|
||||||
|
const keepSocketAlive = (socket: WebSocket) => {
|
||||||
|
if (socket.readyState === WebSocket.OPEN) {
|
||||||
|
if (pingCount >= MAX_PING_COUNT) {
|
||||||
|
socket.close();
|
||||||
|
} else {
|
||||||
|
pingCount++;
|
||||||
|
// The code uses a recursive setTimeout pattern rather than setInterval,
|
||||||
|
// which ensures each new ping only happens after the previous one completes
|
||||||
|
// and naturally stops if the socket closes.
|
||||||
|
keepAliveID = setTimeout(() => keepSocketAlive(socket), 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||||
socket.send(initCommands);
|
socket.send(initCommands);
|
||||||
|
keepSocketAlive(socket);
|
||||||
} else {
|
} else {
|
||||||
socket.onopen = () => {
|
socket.onopen = () => {
|
||||||
socket.send(initCommands);
|
socket.send(initCommands);
|
||||||
|
|
||||||
// ensures connections don't remain open indefinitely by implementing an automatic timeout after 20 minutes.
|
|
||||||
const keepSocketAlive = (socket: WebSocket) => {
|
|
||||||
if (socket.readyState === WebSocket.OPEN) {
|
|
||||||
if (pingCount >= MAX_PING_COUNT) {
|
|
||||||
socket.close();
|
|
||||||
} else {
|
|
||||||
socket.send("");
|
|
||||||
pingCount++;
|
|
||||||
// The code uses a recursive setTimeout pattern rather than setInterval,
|
|
||||||
// which ensures each new ping only happens after the previous one completes
|
|
||||||
// and naturally stops if the socket closes.
|
|
||||||
keepAliveID = setTimeout(() => keepSocketAlive(socket), 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
keepSocketAlive(socket);
|
keepSocketAlive(socket);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ export const EXIT_COMMAND = ` printf "\\033[1;31mSession ended. Please close thi
|
|||||||
* the required methods.
|
* the required methods.
|
||||||
*/
|
*/
|
||||||
export abstract class AbstractShellHandler {
|
export abstract class AbstractShellHandler {
|
||||||
|
/**
|
||||||
|
* The name of the application using this shell handler.
|
||||||
|
* This is used for telemetry and logging purposes.
|
||||||
|
*/
|
||||||
|
protected APP_NAME = "CosmosExplorerTerminal";
|
||||||
|
|
||||||
abstract getShellName(): string;
|
abstract getShellName(): string;
|
||||||
abstract getSetUpCommands(): string[];
|
abstract getSetUpCommands(): string[];
|
||||||
abstract getConnectionCommand(): string;
|
abstract getConnectionCommand(): string;
|
||||||
@@ -56,4 +62,30 @@ export abstract class AbstractShellHandler {
|
|||||||
|
|
||||||
return allCommands.join("\n").concat("\n");
|
return allCommands.join("\n").concat("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup commands for MongoDB shell:
|
||||||
|
*
|
||||||
|
* 1. Check if mongosh is already installed
|
||||||
|
* 2. Download mongosh package if not installed
|
||||||
|
* 3. Extract the package to access mongosh binaries
|
||||||
|
* 4. Move extracted files to ~/mongosh directory
|
||||||
|
* 5. Add mongosh binary path to system PATH
|
||||||
|
* 6. Apply PATH changes by sourcing .bashrc
|
||||||
|
*
|
||||||
|
* Each command runs conditionally only if mongosh
|
||||||
|
* is not already present in the environment.
|
||||||
|
*/
|
||||||
|
protected mongoShellSetupCommands(): string[] {
|
||||||
|
const PACKAGE_VERSION: string = "2.5.0";
|
||||||
|
return [
|
||||||
|
"if ! command -v mongosh &> /dev/null; then echo '⚠️ mongosh not found. Installing...'; fi",
|
||||||
|
`if ! command -v mongosh &> /dev/null; then curl -LO https://downloads.mongodb.com/compass/mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
|
||||||
|
`if ! command -v mongosh &> /dev/null; then tar -xvzf mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
|
||||||
|
`if ! command -v mongosh &> /dev/null; then mkdir -p ~/mongosh/bin && mv mongosh-${PACKAGE_VERSION}-linux-x64/bin/mongosh ~/mongosh/bin/ && chmod +x ~/mongosh/bin/mongosh; fi`,
|
||||||
|
`if ! command -v mongosh &> /dev/null; then rm -rf mongosh-${PACKAGE_VERSION}-linux-x64 mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
|
||||||
|
"if ! command -v mongosh &> /dev/null; then echo 'export PATH=$HOME/mongosh/bin:$PATH' >> ~/.bashrc; fi",
|
||||||
|
"source ~/.bashrc",
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ describe("CassandraShellHandler", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should return correct connection command", () => {
|
test("should return correct connection command", () => {
|
||||||
const expectedCommand = "cqlsh test-endpoint.cassandra.cosmos.azure.com 10350 -u test-account -p test-key --ssl";
|
const expectedCommand = `cqlsh test-endpoint.cassandra.cosmos.azure.com 10350 -u test-account -p test-key --ssl`;
|
||||||
|
|
||||||
expect(handler.getConnectionCommand()).toBe(expectedCommand);
|
expect(handler.getConnectionCommand()).toBe(expectedCommand);
|
||||||
expect(CommonUtils.getHostFromUrl).toHaveBeenCalledWith("https://test-endpoint.cassandra.cosmos.azure.com:443/");
|
expect(CommonUtils.getHostFromUrl).toHaveBeenCalledWith("https://test-endpoint.cassandra.cosmos.azure.com:443/");
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ describe("MongoShellHandler", () => {
|
|||||||
const commands = mongoShellHandler.getSetUpCommands();
|
const commands = mongoShellHandler.getSetUpCommands();
|
||||||
|
|
||||||
expect(Array.isArray(commands)).toBe(true);
|
expect(Array.isArray(commands)).toBe(true);
|
||||||
expect(commands.length).toBe(6);
|
expect(commands.length).toBe(7);
|
||||||
expect(commands[1]).toContain("mongosh-2.5.0-linux-x64.tgz");
|
expect(commands[1]).toContain("mongosh-2.5.0-linux-x64.tgz");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -91,7 +91,7 @@ describe("MongoShellHandler", () => {
|
|||||||
const command = mongoShellHandler.getConnectionCommand();
|
const command = mongoShellHandler.getConnectionCommand();
|
||||||
|
|
||||||
expect(command).toBe(
|
expect(command).toBe(
|
||||||
"mongosh --host test-mongo.documents.azure.com --port 10255 --username test-account --password test-key --tls --tlsAllowInvalidCertificates",
|
"mongosh mongodb://test-mongo.documents.azure.com:10255?appName=CosmosExplorerTerminal --username test-account --password test-key --tls --tlsAllowInvalidCertificates",
|
||||||
);
|
);
|
||||||
expect(CommonUtils.getHostFromUrl).toHaveBeenCalledWith("https://test-mongo.documents.azure.com:443/");
|
expect(CommonUtils.getHostFromUrl).toHaveBeenCalledWith("https://test-mongo.documents.azure.com:443/");
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { userContext } from "../../../../UserContext";
|
|||||||
import { getHostFromUrl } from "../Utils/CommonUtils";
|
import { getHostFromUrl } from "../Utils/CommonUtils";
|
||||||
import { AbstractShellHandler } from "./AbstractShellHandler";
|
import { AbstractShellHandler } from "./AbstractShellHandler";
|
||||||
|
|
||||||
const PACKAGE_VERSION: string = "2.5.0";
|
|
||||||
|
|
||||||
export class MongoShellHandler extends AbstractShellHandler {
|
export class MongoShellHandler extends AbstractShellHandler {
|
||||||
private _key: string;
|
private _key: string;
|
||||||
private _endpoint: string | undefined;
|
private _endpoint: string | undefined;
|
||||||
@@ -18,14 +16,7 @@ export class MongoShellHandler extends AbstractShellHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getSetUpCommands(): string[] {
|
public getSetUpCommands(): string[] {
|
||||||
return [
|
return this.mongoShellSetupCommands();
|
||||||
"if ! command -v mongosh &> /dev/null; then echo '⚠️ mongosh not found. Installing...'; fi",
|
|
||||||
`if ! command -v mongosh &> /dev/null; then curl -LO https://downloads.mongodb.com/compass/mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
|
|
||||||
`if ! command -v mongosh &> /dev/null; then tar -xvzf mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
|
|
||||||
`if ! command -v mongosh &> /dev/null; then mkdir -p ~/mongosh && mv mongosh-${PACKAGE_VERSION}-linux-x64/* ~/mongosh/; fi`,
|
|
||||||
"if ! command -v mongosh &> /dev/null; then echo 'export PATH=$HOME/mongosh/bin:$PATH' >> ~/.bashrc; fi",
|
|
||||||
"source ~/.bashrc",
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getConnectionCommand(): string {
|
public getConnectionCommand(): string {
|
||||||
@@ -37,9 +28,17 @@ export class MongoShellHandler extends AbstractShellHandler {
|
|||||||
if (!dbName) {
|
if (!dbName) {
|
||||||
return "echo 'Database name not found.'";
|
return "echo 'Database name not found.'";
|
||||||
}
|
}
|
||||||
return `mongosh --host ${getHostFromUrl(this._endpoint)} --port 10255 --username ${dbName} --password ${
|
return (
|
||||||
this._key
|
"mongosh mongodb://" +
|
||||||
} --tls --tlsAllowInvalidCertificates`;
|
getHostFromUrl(this._endpoint) +
|
||||||
|
":10255?appName=" +
|
||||||
|
this.APP_NAME +
|
||||||
|
" --username " +
|
||||||
|
dbName +
|
||||||
|
" --password " +
|
||||||
|
this._key +
|
||||||
|
" --tls --tlsAllowInvalidCertificates"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTerminalSuppressedData(): string {
|
public getTerminalSuppressedData(): string {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export class PostgresShellHandler extends AbstractShellHandler {
|
|||||||
// All Azure Cosmos DB PostgreSQL deployments follow this convention.
|
// All Azure Cosmos DB PostgreSQL deployments follow this convention.
|
||||||
// Ref. https://learn.microsoft.com/en-us/azure/cosmos-db/postgresql/reference-limits#database-creation
|
// Ref. https://learn.microsoft.com/en-us/azure/cosmos-db/postgresql/reference-limits#database-creation
|
||||||
const loginName = userContext.postgresConnectionStrParams.adminLogin;
|
const loginName = userContext.postgresConnectionStrParams.adminLogin;
|
||||||
return `psql -h "${this._endpoint}" -p 5432 -d "citus" -U "${loginName}" --set=sslmode=require`;
|
return `psql -h "${this._endpoint}" -p 5432 -d "citus" -U "${loginName}" --set=sslmode=require --set=application_name=${this.APP_NAME}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTerminalSuppressedData(): string {
|
public getTerminalSuppressedData(): string {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ describe("VCoreMongoShellHandler", () => {
|
|||||||
const commands = vcoreMongoShellHandler.getSetUpCommands();
|
const commands = vcoreMongoShellHandler.getSetUpCommands();
|
||||||
|
|
||||||
expect(Array.isArray(commands)).toBe(true);
|
expect(Array.isArray(commands)).toBe(true);
|
||||||
expect(commands.length).toBe(6);
|
expect(commands.length).toBe(7);
|
||||||
expect(commands[1]).toContain("mongosh-2.5.0-linux-x64.tgz");
|
expect(commands[1]).toContain("mongosh-2.5.0-linux-x64.tgz");
|
||||||
expect(commands[0]).toContain("mongosh not found");
|
expect(commands[0]).toContain("mongosh not found");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { userContext } from "../../../../UserContext";
|
import { userContext } from "../../../../UserContext";
|
||||||
import { AbstractShellHandler } from "./AbstractShellHandler";
|
import { AbstractShellHandler } from "./AbstractShellHandler";
|
||||||
|
|
||||||
const PACKAGE_VERSION: string = "2.5.0";
|
|
||||||
|
|
||||||
export class VCoreMongoShellHandler extends AbstractShellHandler {
|
export class VCoreMongoShellHandler extends AbstractShellHandler {
|
||||||
private _endpoint: string | undefined;
|
private _endpoint: string | undefined;
|
||||||
|
|
||||||
@@ -15,28 +13,8 @@ export class VCoreMongoShellHandler extends AbstractShellHandler {
|
|||||||
return "MongoDB VCore";
|
return "MongoDB VCore";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup commands for MongoDB VCore shell:
|
|
||||||
*
|
|
||||||
* 1. Check if mongosh is already installed
|
|
||||||
* 2. Download mongosh package if not installed
|
|
||||||
* 3. Extract the package to access mongosh binaries
|
|
||||||
* 4. Move extracted files to ~/mongosh directory
|
|
||||||
* 5. Add mongosh binary path to system PATH
|
|
||||||
* 6. Apply PATH changes by sourcing .bashrc
|
|
||||||
*
|
|
||||||
* Each command runs conditionally only if mongosh
|
|
||||||
* is not already present in the environment.
|
|
||||||
*/
|
|
||||||
public getSetUpCommands(): string[] {
|
public getSetUpCommands(): string[] {
|
||||||
return [
|
return this.mongoShellSetupCommands();
|
||||||
"if ! command -v mongosh &> /dev/null; then echo '⚠️ mongosh not found. Installing...'; fi",
|
|
||||||
`if ! command -v mongosh &> /dev/null; then curl -LO https://downloads.mongodb.com/compass/mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
|
|
||||||
`if ! command -v mongosh &> /dev/null; then tar -xvzf mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
|
|
||||||
`if ! command -v mongosh &> /dev/null; then mkdir -p ~/mongosh && mv mongosh-${PACKAGE_VERSION}-linux-x64/* ~/mongosh/; fi`,
|
|
||||||
"if ! command -v mongosh &> /dev/null; then echo 'export PATH=$HOME/mongosh/bin:$PATH' >> ~/.bashrc; fi",
|
|
||||||
"source ~/.bashrc",
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getConnectionCommand(): string {
|
public getConnectionCommand(): string {
|
||||||
@@ -45,7 +23,7 @@ export class VCoreMongoShellHandler extends AbstractShellHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const userName = userContext.vcoreMongoConnectionParams.adminLogin;
|
const userName = userContext.vcoreMongoConnectionParams.adminLogin;
|
||||||
return `mongosh "mongodb+srv://${userName}:@${this._endpoint}/?authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000"`;
|
return `mongosh "mongodb+srv://${userName}:@${this._endpoint}/?authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000&appName=${this.APP_NAME}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTerminalSuppressedData(): string {
|
public getTerminalSuppressedData(): string {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { AbstractShellHandler } from "Explorer/Tabs/CloudShellTab/ShellTypes/AbstractShellHandler";
|
|
||||||
import { IDisposable, ITerminalAddon, Terminal } from "@xterm/xterm";
|
import { IDisposable, ITerminalAddon, Terminal } from "@xterm/xterm";
|
||||||
|
import { AbstractShellHandler } from "../ShellTypes/AbstractShellHandler";
|
||||||
|
import { formatErrorMessage } from "./TerminalLogFormats";
|
||||||
|
|
||||||
interface IAttachOptions {
|
interface IAttachOptions {
|
||||||
bidirectional?: boolean;
|
bidirectional?: boolean;
|
||||||
@@ -56,8 +57,27 @@ export class AttachAddon implements ITerminalAddon {
|
|||||||
this._disposables.push(terminal.onBinary((data) => this._sendBinary(data)));
|
this._disposables.push(terminal.onBinary((data) => this._sendBinary(data)));
|
||||||
}
|
}
|
||||||
|
|
||||||
this._disposables.push(addSocketListener(this._socket, "close", () => this.dispose()));
|
this._disposables.push(addSocketListener(this._socket, "close", () => this._handleSocketClose(terminal)));
|
||||||
this._disposables.push(addSocketListener(this._socket, "error", () => this.dispose()));
|
this._disposables.push(addSocketListener(this._socket, "error", () => this._handleSocketClose(terminal)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles socket close events by terminating processes and showing a message
|
||||||
|
*/
|
||||||
|
private _handleSocketClose(terminal: Terminal): void {
|
||||||
|
if (terminal) {
|
||||||
|
terminal.writeln(
|
||||||
|
formatErrorMessage("Session ended. Please close this tab and initiate a new shell session if needed."),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Send exit command to terminal
|
||||||
|
if (this._bidirectional) {
|
||||||
|
terminal.write(formatErrorMessage("exit\r\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up resources
|
||||||
|
this.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user