mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-04-16 14:48:38 +01:00
bug fixes
This commit is contained in:
parent
2014dcd03d
commit
d6c0edf20d
@ -257,6 +257,7 @@ export class Areas {
|
|||||||
public static ShareDialog: string = "Share Access Dialog";
|
public static ShareDialog: string = "Share Access Dialog";
|
||||||
public static Notebook: string = "Notebook";
|
public static Notebook: string = "Notebook";
|
||||||
public static Copilot: string = "Copilot";
|
public static Copilot: string = "Copilot";
|
||||||
|
public static CloudShell: string = "Cloud Shell";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HttpHeaders {
|
export class HttpHeaders {
|
||||||
|
@ -17,7 +17,6 @@ export const CloudShellTerminalComponent: React.FC<CloudShellTerminalComponentPr
|
|||||||
const terminalRef = useRef(null); // Reference for terminal container
|
const terminalRef = useRef(null); // Reference for terminal container
|
||||||
const xtermRef = useRef(null); // Reference for XTerm instance
|
const xtermRef = useRef(null); // Reference for XTerm instance
|
||||||
const socketRef = useRef(null); // Reference for WebSocket
|
const socketRef = useRef(null); // Reference for WebSocket
|
||||||
const fitAddon = new FitAddon();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Initialize XTerm instance
|
// Initialize XTerm instance
|
||||||
@ -34,18 +33,33 @@ export const CloudShellTerminalComponent: React.FC<CloudShellTerminalComponentPr
|
|||||||
scrollback: 1000
|
scrollback: 1000
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const fitAddon = new FitAddon();
|
||||||
term.loadAddon(fitAddon);
|
term.loadAddon(fitAddon);
|
||||||
|
|
||||||
fitAddon.fit(); // Fit the terminal to the container size
|
|
||||||
// Attach terminal to the DOM
|
// Attach terminal to the DOM
|
||||||
if (terminalRef.current) {
|
if (terminalRef.current) {
|
||||||
term.open(terminalRef.current);
|
term.open(terminalRef.current);
|
||||||
xtermRef.current = term;
|
xtermRef.current = term;
|
||||||
}
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
fitAddon.fit();
|
||||||
|
}, 0);
|
||||||
|
|
||||||
// Adjust terminal size on window resize
|
// Use ResizeObserver instead of window resize
|
||||||
const handleResize = () => fitAddon.fit();
|
const resizeObserver = new ResizeObserver(() => {
|
||||||
window.addEventListener('resize', handleResize);
|
const container = terminalRef.current;
|
||||||
|
if (
|
||||||
|
container &&
|
||||||
|
container.offsetWidth > 0 &&
|
||||||
|
container.offsetHeight > 0
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
fitAddon.fit();
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Fit failed on resize:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resizeObserver.observe(terminalRef.current);
|
||||||
|
|
||||||
socketRef.current = startCloudShellTerminal(term, props.shellType);
|
socketRef.current = startCloudShellTerminal(term, props.shellType);
|
||||||
|
|
||||||
@ -55,10 +69,11 @@ export const CloudShellTerminalComponent: React.FC<CloudShellTerminalComponentPr
|
|||||||
if (socketRef.current && socketRef.current.readyState && socketRef.current.readyState === WebSocket.OPEN) {
|
if (socketRef.current && socketRef.current.readyState && socketRef.current.readyState === WebSocket.OPEN) {
|
||||||
socketRef.current.close(); // Close WebSocket connection
|
socketRef.current.close(); // Close WebSocket connection
|
||||||
}
|
}
|
||||||
window.removeEventListener('resize', handleResize);
|
if (resizeObserver && terminalRef.current) {
|
||||||
|
resizeObserver.unobserve(terminalRef.current);
|
||||||
|
}
|
||||||
term.dispose(); // Clean up XTerm instance
|
term.dispose(); // Clean up XTerm instance
|
||||||
};
|
};
|
||||||
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <div ref={terminalRef} style={{ width: "100%", height: "500px"}} />;
|
return <div ref={terminalRef} style={{ width: "100%", height: "500px"}} />;
|
||||||
|
@ -3,8 +3,12 @@
|
|||||||
* Core functionality for CloudShell terminal management
|
* Core functionality for CloudShell terminal management
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
||||||
import { Terminal } from "xterm";
|
import { Terminal } from "xterm";
|
||||||
|
import { Areas } from "../../../Common/Constants";
|
||||||
import { TerminalKind } from "../../../Contracts/ViewModels";
|
import { TerminalKind } from "../../../Contracts/ViewModels";
|
||||||
|
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||||
|
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { userContext } from "../../../UserContext";
|
import { userContext } from "../../../UserContext";
|
||||||
import {
|
import {
|
||||||
authorizeSession,
|
authorizeSession,
|
||||||
@ -14,7 +18,7 @@ import {
|
|||||||
registerCloudShellProvider,
|
registerCloudShellProvider,
|
||||||
verifyCloudShellProviderRegistration
|
verifyCloudShellProviderRegistration
|
||||||
} from "./Data/CloudShellClient";
|
} from "./Data/CloudShellClient";
|
||||||
import { START_MARKER, AbstractShellHandler } from "./ShellTypes/AbstractShellHandler";
|
import { AbstractShellHandler, START_MARKER } from "./ShellTypes/AbstractShellHandler";
|
||||||
import { ShellTypeHandlerFactory } from "./ShellTypes/ShellTypeFactory";
|
import { ShellTypeHandlerFactory } from "./ShellTypes/ShellTypeFactory";
|
||||||
import { AttachAddon } from "./Utils/AttachAddOn";
|
import { AttachAddon } from "./Utils/AttachAddOn";
|
||||||
import { askConfirmation, wait } from "./Utils/CommonUtils";
|
import { askConfirmation, wait } from "./Utils/CommonUtils";
|
||||||
@ -37,42 +41,72 @@ const MAX_PING_COUNT = 20 * 60; // 20 minutes (60 seconds/minute)
|
|||||||
export const startCloudShellTerminal =
|
export const startCloudShellTerminal =
|
||||||
async (terminal: Terminal, shellType: TerminalKind): Promise<WebSocket> => {
|
async (terminal: Terminal, shellType: TerminalKind): Promise<WebSocket> => {
|
||||||
|
|
||||||
await ensureCloudShellProviderRegistered();
|
const startKey = TelemetryProcessor.traceStart(Action.CloudShellTerminalSession, {
|
||||||
|
shellType: TerminalKind[shellType],
|
||||||
|
dataExplorerArea: Areas.CloudShell
|
||||||
|
});
|
||||||
|
|
||||||
const resolvedRegion = determineCloudShellRegion();
|
try {
|
||||||
// Ask for user consent for region
|
await ensureCloudShellProviderRegistered();
|
||||||
const consentGranted = await askConfirmation(terminal, formatWarningMessage("This shell might be in a different region than the database region. Do you want to proceed?"));
|
|
||||||
if (!consentGranted) {
|
const resolvedRegion = determineCloudShellRegion();
|
||||||
return null; // Exit if user declined
|
// Ask for user consent for region
|
||||||
|
const consentGranted = await askConfirmation(terminal, formatWarningMessage("This shell might be in a different region than the database region. Do you want to proceed?"));
|
||||||
|
|
||||||
|
// Track user decision
|
||||||
|
TelemetryProcessor.trace(Action.CloudShellUserConsent,
|
||||||
|
consentGranted ? ActionModifiers.Success : ActionModifiers.Cancel,
|
||||||
|
{ dataExplorerArea: Areas.CloudShell }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!consentGranted) {
|
||||||
|
return null; // Exit if user declined
|
||||||
|
}
|
||||||
|
|
||||||
|
terminal.writeln(formatInfoMessage("Connecting to CloudShell....."));
|
||||||
|
|
||||||
|
let sessionDetails: {
|
||||||
|
socketUri?: string;
|
||||||
|
provisionConsoleResponse?: any;
|
||||||
|
targetUri?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
sessionDetails = await provisionCloudShellSession(resolvedRegion, terminal);
|
||||||
|
|
||||||
|
if (!sessionDetails.socketUri) {
|
||||||
|
terminal.writeln(formatErrorMessage("Failed to establish a connection. Please try again later."));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the shell handler for this type
|
||||||
|
const shellHandler = await ShellTypeHandlerFactory.getHandler(shellType);
|
||||||
|
// Configure WebSocket connection with shell-specific commands
|
||||||
|
const socket = await establishTerminalConnection(
|
||||||
|
terminal,
|
||||||
|
shellHandler,
|
||||||
|
sessionDetails.socketUri,
|
||||||
|
sessionDetails.provisionConsoleResponse,
|
||||||
|
sessionDetails.targetUri
|
||||||
|
);
|
||||||
|
|
||||||
|
TelemetryProcessor.traceSuccess(Action.CloudShellTerminalSession, {
|
||||||
|
shellType: TerminalKind[shellType],
|
||||||
|
dataExplorerArea: Areas.CloudShell,
|
||||||
|
region: resolvedRegion
|
||||||
|
}, startKey);
|
||||||
|
|
||||||
|
return socket;
|
||||||
}
|
}
|
||||||
|
catch (err) {
|
||||||
|
TelemetryProcessor.traceFailure(Action.CloudShellTerminalSession, {
|
||||||
|
shellType: TerminalKind[shellType],
|
||||||
|
dataExplorerArea: Areas.CloudShell,
|
||||||
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
|
}, startKey);
|
||||||
|
|
||||||
terminal.writeln(formatInfoMessage("Connecting to CloudShell....."));
|
terminal.writeln(formatErrorMessage(`Failed with error.${getErrorMessage(error)}`));
|
||||||
|
|
||||||
let sessionDetails: {
|
|
||||||
socketUri?: string;
|
|
||||||
provisionConsoleResponse?: any;
|
|
||||||
targetUri?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
sessionDetails = await provisionCloudShellSession(resolvedRegion, terminal);
|
|
||||||
|
|
||||||
if (!sessionDetails.socketUri) {
|
|
||||||
terminal.writeln(formatErrorMessage("Failed to establish a connection. Please try again later."));
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the shell handler for this type
|
|
||||||
const shellHandler = await ShellTypeHandlerFactory.getHandler(shellType);
|
|
||||||
// Configure WebSocket connection with shell-specific commands
|
|
||||||
const socket = await establishTerminalConnection(
|
|
||||||
terminal,
|
|
||||||
shellHandler,
|
|
||||||
sessionDetails.socketUri,
|
|
||||||
sessionDetails.provisionConsoleResponse,
|
|
||||||
sessionDetails.targetUri
|
|
||||||
);
|
|
||||||
|
|
||||||
return socket;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
export const START_MARKER = `echo "START INITIALIZATION" > /dev/null`;
|
export const START_MARKER = `echo "START INITIALIZATION" > /dev/null`;
|
||||||
|
export const DISABLE_HISTORY = `set +o history`;
|
||||||
|
|
||||||
export abstract class AbstractShellHandler {
|
export abstract class AbstractShellHandler {
|
||||||
|
|
||||||
@ -14,6 +15,7 @@ export abstract class AbstractShellHandler {
|
|||||||
|
|
||||||
const allCommands = [
|
const allCommands = [
|
||||||
START_MARKER,
|
START_MARKER,
|
||||||
|
DISABLE_HISTORY,
|
||||||
...setupCommands,
|
...setupCommands,
|
||||||
connectionCommand
|
connectionCommand
|
||||||
];
|
];
|
||||||
|
@ -33,7 +33,7 @@ export class PostgresShellHandler extends AbstractShellHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getConnectionCommand(): string {
|
public getConnectionCommand(): string {
|
||||||
return `psql 'read -p "Enter Database Name: " dbname && read -p "Enter Username: " username && host=${this.getEndpoint()} port=5432 dbname=$dbname user=$username sslmode=require'`;
|
return `read -p "Enter Database Name: " dbname && read -p "Enter Username: " username && psql -h "${this.getEndpoint()}" -p 5432 -d "$dbname" -U "$username" --set=sslmode=require`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTerminalSuppressedData(): string {
|
public getTerminalSuppressedData(): string {
|
||||||
|
@ -83,11 +83,14 @@ export class AttachAddon implements ITerminalAddon {
|
|||||||
this._allowTerminalWrite = false;
|
this._allowTerminalWrite = false;
|
||||||
terminal.write(`Preparing ${this._shellHandler.getShellName()} environment...\r\n`);
|
terminal.write(`Preparing ${this._shellHandler.getShellName()} environment...\r\n`);
|
||||||
}
|
}
|
||||||
if (this._allowTerminalWrite &&
|
|
||||||
this._shellHandler?.getTerminalSuppressedData() &&
|
if (this._allowTerminalWrite) {
|
||||||
this._shellHandler?.getTerminalSuppressedData().length > 0 &&
|
const suppressedData = this._shellHandler?.getTerminalSuppressedData();
|
||||||
!data.includes(this._shellHandler?.getTerminalSuppressedData())) {
|
const hasSuppressedData = suppressedData && suppressedData.length > 0;
|
||||||
terminal.write(data);
|
|
||||||
|
if (!hasSuppressedData || !data.includes(suppressedData)) {
|
||||||
|
terminal.write(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.includes(this._shellHandler.getConnectionCommand())) {
|
if (data.includes(this._shellHandler.getConnectionCommand())) {
|
||||||
|
@ -143,6 +143,8 @@ export enum Action {
|
|||||||
ReadPersistedTabState,
|
ReadPersistedTabState,
|
||||||
SavePersistedTabState,
|
SavePersistedTabState,
|
||||||
DeletePersistedTabState,
|
DeletePersistedTabState,
|
||||||
|
CloudShellUserConsent,
|
||||||
|
CloudShellTerminalSession
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ActionModifiers = {
|
export const ActionModifiers = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user