mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-10 04:56:56 +00:00
ui and functional changes
This commit is contained in:
@@ -18,10 +18,18 @@ export const CloudShellTerminalComponent: React.FC<CloudShellTerminalProps> = ({
|
|||||||
const fitAddon = new FitAddon();
|
const fitAddon = new FitAddon();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Initialize XTerm instance
|
// Initialize XTerm instance
|
||||||
const term = new Terminal({
|
const term = new Terminal({
|
||||||
cursorBlink: true,
|
cursorBlink: true,
|
||||||
theme: { background: "#1d1f21", foreground: "#c5c8c6" }
|
cursorStyle: 'bar',
|
||||||
|
fontFamily: 'Courier New, monospace',
|
||||||
|
fontSize: 14,
|
||||||
|
theme: {
|
||||||
|
background: "#1e1e1e",
|
||||||
|
foreground: "#d4d4d4",
|
||||||
|
cursor: "#ffcc00"
|
||||||
|
},
|
||||||
|
scrollback: 1000
|
||||||
});
|
});
|
||||||
|
|
||||||
term.loadAddon(fitAddon);
|
term.loadAddon(fitAddon);
|
||||||
@@ -30,8 +38,23 @@ export const CloudShellTerminalComponent: React.FC<CloudShellTerminalProps> = ({
|
|||||||
if (terminalRef.current) {
|
if (terminalRef.current) {
|
||||||
term.open(terminalRef.current);
|
term.open(terminalRef.current);
|
||||||
xtermRef.current = term;
|
xtermRef.current = term;
|
||||||
|
|
||||||
|
// Ensure the CSS is injected only once
|
||||||
|
if (!document.getElementById("xterm-custom-style")) {
|
||||||
|
const style = document.createElement("style");
|
||||||
|
style.id = "xterm-custom-style"; // Unique ID to prevent duplicates
|
||||||
|
style.innerHTML = `
|
||||||
|
.xterm-text-layer {
|
||||||
|
transform: translateX(10px); /* Adds left padding */
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fitAddon) {
|
||||||
|
fitAddon.fit();
|
||||||
}
|
}
|
||||||
fitAddon.fit();
|
|
||||||
|
|
||||||
// Adjust terminal size on window resize
|
// Adjust terminal size on window resize
|
||||||
const handleResize = () => fitAddon.fit();
|
const handleResize = () => fitAddon.fit();
|
||||||
@@ -47,14 +70,20 @@ export const CloudShellTerminalComponent: React.FC<CloudShellTerminalProps> = ({
|
|||||||
|
|
||||||
// Cleanup function to close WebSocket and dispose terminal
|
// Cleanup function to close WebSocket and dispose terminal
|
||||||
return () => {
|
return () => {
|
||||||
|
if (!socketRef.current) return; // Prevent errors if WebSocket is not initialized
|
||||||
if (socketRef.current) {
|
if (socketRef.current) {
|
||||||
socketRef.current.close(); // Close WebSocket connection
|
socketRef.current.close(); // Close WebSocket connection
|
||||||
}
|
}
|
||||||
window.removeEventListener('resize', handleResize);
|
window.removeEventListener('resize', handleResize);
|
||||||
term.dispose(); // Clean up XTerm instance
|
term.dispose(); // Clean up XTerm instance
|
||||||
|
|
||||||
|
const styleElement = document.getElementById("xterm-custom-style");
|
||||||
|
if (styleElement) {
|
||||||
|
styleElement.remove(); // Clean up CSS on unmount
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <div ref={terminalRef} style={{ width: "100%", height: "500px" }} />;
|
return <div ref={terminalRef} style={{ width: "100%", height: "500px"}} />;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export const putEphemeralUserSettings = async (userSubscriptionId: string, userR
|
|||||||
preferredOsType: OsType.Linux,
|
preferredOsType: OsType.Linux,
|
||||||
preferredShellType: ShellType.Bash,
|
preferredShellType: ShellType.Bash,
|
||||||
preferredLocation: userRegion,
|
preferredLocation: userRegion,
|
||||||
networkType: NetworkType.Default,
|
networkType: (!vNetSettings || Object.keys(vNetSettings).length === 0) ? NetworkType.Default : (vNetSettings ? NetworkType.Isolated : NetworkType.Default),
|
||||||
sessionType: SessionType.Ephemeral,
|
sessionType: SessionType.Ephemeral,
|
||||||
userSubscription: userSubscriptionId,
|
userSubscription: userSubscriptionId,
|
||||||
vnetSettings: vNetSettings ?? {}
|
vnetSettings: vNetSettings ?? {}
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ export type UserSettingProperties = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type VnetSettings = {
|
export type VnetSettings = {
|
||||||
networkProfileResourceId: string;
|
networkProfileResourceId?: string;
|
||||||
relayNamespaceResourceId: string;
|
relayNamespaceResourceId?: string;
|
||||||
location: string;
|
location?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProvisionConsoleResponse = {
|
export type ProvisionConsoleResponse = {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { RelayNamespaceResponse, VnetModel } from "Explorer/Tabs/CloudShellTab/DataModels";
|
import { RelayNamespaceResponse, VnetModel, VnetSettings } from "Explorer/Tabs/CloudShellTab/DataModels";
|
||||||
import { listKeys } from "Utils/arm/generatedClients/cosmos/databaseAccounts";
|
import { listKeys } from "Utils/arm/generatedClients/cosmos/databaseAccounts";
|
||||||
import { Terminal } from "xterm";
|
import { Terminal } from "xterm";
|
||||||
import { TerminalKind } from "../../../Contracts/ViewModels";
|
import { TerminalKind } from "../../../Contracts/ViewModels";
|
||||||
@@ -15,22 +15,37 @@ import { LogError, LogInfo } from "./LogFormatter";
|
|||||||
export const startCloudShellTerminal = async (terminal: Terminal, shellType: TerminalKind) => {
|
export const startCloudShellTerminal = async (terminal: Terminal, shellType: TerminalKind) => {
|
||||||
|
|
||||||
// validate that the subscription id is registered in the CloudShell namespace
|
// validate that the subscription id is registered in the CloudShell namespace
|
||||||
|
terminal.writeln("");
|
||||||
try {
|
try {
|
||||||
const response: any = await verifyCloudShellProviderRegistration(userContext.subscriptionId);
|
terminal.writeln("\x1B[34mVerifying CloudShell provider registration...\x1B[0m");
|
||||||
if (response.registrationState !== "Registered") {
|
const response: any = await verifyCloudShellProviderRegistration(userContext.subscriptionId);
|
||||||
await registerCloudShellProvider(userContext.subscriptionId);
|
if (response.registrationState !== "Registered") {
|
||||||
}
|
terminal.writeln("\x1B[33mCloudShell provider registration is not found. Registering now...\x1B[0m");
|
||||||
|
await registerCloudShellProvider(userContext.subscriptionId);
|
||||||
|
terminal.writeln("\x1B[32mCloudShell provider registration completed successfully.\x1B[0m");
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
terminal.writeln(LogError('Unable to verify CloudShell provider registration.'));
|
terminal.writeln("\x1B[31mError: Unable to verify CloudShell provider registration.\x1B[0m");
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
const settings = await getUserSettings();
|
terminal.writeln("");
|
||||||
let vNetSettings = {};
|
terminal.writeln("\x1B[34mFetching user settings...\x1B[0m");
|
||||||
|
let settings;
|
||||||
|
try {
|
||||||
|
settings = await getUserSettings();
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
terminal.writeln("\x1B[33mNo user settings found. Proceeding with default settings.\x1B[0m");
|
||||||
|
}
|
||||||
|
|
||||||
|
let vNetSettings: VnetSettings;
|
||||||
if(settings?.properties?.vnetSettings && Object.keys(settings?.properties?.vnetSettings).length > 0) {
|
if(settings?.properties?.vnetSettings && Object.keys(settings?.properties?.vnetSettings).length > 0) {
|
||||||
|
|
||||||
terminal.writeln(" Network Profile Resource ID: " + settings.properties.vnetSettings.networkProfileResourceId);
|
terminal.writeln("\x1B[1mUsing existing VNet settings:\x1B[0m");
|
||||||
terminal.writeln(" Relay Namespace Resource ID: " + settings.properties.vnetSettings.relayNamespaceResourceId);
|
terminal.writeln(" - Network Profile Resource ID: \x1B[32m" + settings.properties.vnetSettings.networkProfileResourceId + "\x1B[0m");
|
||||||
|
terminal.writeln(" - Relay Namespace Resource ID: \x1B[32m" + settings.properties.vnetSettings.relayNamespaceResourceId + "\x1B[0m");
|
||||||
|
terminal.writeln(" - VNet Location: \x1B[32m" + settings.properties.vnetSettings.location + "\x1B[0m");
|
||||||
|
|
||||||
vNetSettings = {
|
vNetSettings = {
|
||||||
networkProfileResourceId: settings.properties.vnetSettings.networkProfileResourceId,
|
networkProfileResourceId: settings.properties.vnetSettings.networkProfileResourceId,
|
||||||
@@ -40,103 +55,101 @@ export const startCloudShellTerminal = async (terminal: Terminal, shellType: Ter
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
terminal.writeln("\x1B[1;31m No VNet settings found. Continuing with default settings\x1B[0m");
|
terminal.writeln("\x1B[33mNo existing VNet settings found. Proceeding with default settings.\x1B[0m");
|
||||||
}
|
}
|
||||||
|
|
||||||
terminal.writeln("\x1B[1;37m Press '1' to continue with current or default setting.\x1B[0m");
|
terminal.writeln("");
|
||||||
terminal.writeln("\x1B[1;37m Press '2' to configure new VNet to CloudShell.\x1B[0m");
|
|
||||||
terminal.writeln("\x1B[1;37m Press '3' to Reset CloudShell VNet Settings.\x1B[0m");
|
terminal.writeln("\x1B[1mSelect an option to continue:\x1B[0m");
|
||||||
terminal.writeln("\x1B[1;37m Note: To learn how to configure VNet for CloudShell, go to this link https://aka.ms/cloudhellvnetsetup \x1B[0m");
|
terminal.writeln("\x1B[33m1 - Use current/default VNet settings\x1B[0m");
|
||||||
|
terminal.writeln("\x1B[33m2 - Configure a new VNet for CloudShell\x1B[0m");
|
||||||
|
terminal.writeln("\x1B[33m3 - Reset CloudShell VNet settings to default\x1B[0m");
|
||||||
|
terminal.writeln("\x1B[34mFor details on configuring VNet for CloudShell, visit: https://aka.ms/cloudshellvnetsetup\x1B[0m");
|
||||||
|
|
||||||
terminal.focus();
|
terminal.focus();
|
||||||
|
|
||||||
let isDefaultSetting = false;
|
let isDefaultSetting = false;
|
||||||
const handleKeyPress = terminal.onKey(async ({ key }: { key: string }) => {
|
const handleKeyPress = terminal.onKey(async ({ key }: { key: string }) => {
|
||||||
|
terminal.writeln("")
|
||||||
if (key === "1") {
|
if (key === "1") {
|
||||||
terminal.writeln("\x1B[1;32m Pressed 1, Continuing with current/default settings.\x1B[0m");
|
terminal.writeln("\x1B[34mYou selected option 1: Proceeding with current/default settings.\x1B[0m");
|
||||||
|
|
||||||
isDefaultSetting = true;
|
|
||||||
handleKeyPress.dispose();
|
handleKeyPress.dispose();
|
||||||
}
|
}
|
||||||
else if (key === "2") {
|
else if (key === "2") {
|
||||||
isDefaultSetting = false;
|
terminal.writeln("\x1B[34mYou selected option 2: Please provide the following details.\x1B[0m");
|
||||||
handleKeyPress.dispose();
|
handleKeyPress.dispose();
|
||||||
|
|
||||||
|
const subscriptionId = await askQuestion(terminal, "Enter VNet Subscription ID");
|
||||||
|
const resourceGroup = await askQuestion(terminal, "Enter VNet Resource Group");
|
||||||
|
const vNetName = await askQuestion(terminal, "Enter VNet Name");
|
||||||
|
|
||||||
|
terminal.writeln("\x1B[34mFetching VNet details...\x1B[0m");
|
||||||
|
const vNetConfig = await getARMInfo<VnetModel>(`/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Network/virtualNetworks/${vNetName}`);
|
||||||
|
|
||||||
|
terminal.writeln(" - VNet Location: \x1B[32m" + vNetConfig.location + "\x1B[0m");
|
||||||
|
terminal.writeln(" - Available Network Profiles:");
|
||||||
|
const ipConfigurationProfiles = vNetConfig.properties.subnets.reduce<{ id: string }[]>(
|
||||||
|
(acc, subnet) => acc.concat(subnet.properties.ipConfigurationProfiles || []),
|
||||||
|
[]);
|
||||||
|
for (let i = 0; i < ipConfigurationProfiles.length; i++) {
|
||||||
|
const match = ipConfigurationProfiles[i].id.match(/\/networkProfiles\/([^/]+)/);
|
||||||
|
const result = match ? `${match[1]}` : null;
|
||||||
|
terminal.writeln(" - \x1B[32m" + result + "\x1B[0m");
|
||||||
|
}
|
||||||
|
|
||||||
|
const networkProfile = await askQuestion(terminal, "Network Profile to use");
|
||||||
|
|
||||||
|
const relays = await getARMInfo<RelayNamespaceResponse>(
|
||||||
|
`/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Relay/namespaces`,
|
||||||
|
"2024-01-01");
|
||||||
|
terminal.writeln(" - Available Network Relays:");
|
||||||
|
for (let i = 0; i < relays.value.length; i++) {
|
||||||
|
terminal.writeln(" - \x1B[32m" + relays.value[i].name + "\x1B[0m");
|
||||||
|
}
|
||||||
|
|
||||||
|
const relayName = await askQuestion(terminal, "Network Relay to use:");
|
||||||
|
|
||||||
|
const networkProfileResourceId = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Network/networkProfiles/${networkProfile.replace(/[\n\r]/g, "")}`;
|
||||||
|
const relayResourceId = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Relay/namespaces/${relayName.replace(/[\n\r]/g, "")}`;
|
||||||
|
|
||||||
|
vNetSettings = {
|
||||||
|
networkProfileResourceId: networkProfileResourceId,
|
||||||
|
relayNamespaceResourceId: relayResourceId,
|
||||||
|
location: vNetConfig.location
|
||||||
|
};
|
||||||
}
|
}
|
||||||
else if (key === "3") {
|
else if (key === "3") {
|
||||||
isDefaultSetting = true;
|
terminal.writeln("\x1B[34mYou selected option 3: Resetting VNet settings to default.\x1B[0m");
|
||||||
|
|
||||||
vNetSettings = {};
|
vNetSettings = {};
|
||||||
handleKeyPress.dispose();
|
handleKeyPress.dispose();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
terminal.writeln("\x1B[1;31m Entered Wrong Input, only 1 or 2 are allowed. Exiting...\x1B[0m");
|
terminal.writeln("\x1B[31mInvalid selection. Only options 1, 2, and 3 are allowed. Exiting...\x1B[0m");
|
||||||
setTimeout(() => terminal.dispose(), 2000); // Close terminal after 2 sec
|
setTimeout(() => terminal.dispose(), 2000); // Close terminal after 2 sec
|
||||||
handleKeyPress.dispose();
|
handleKeyPress.dispose();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isDefaultSetting) {
|
terminal.writeln("\x1B[34mDetermining CloudShell region...\x1B[0m");
|
||||||
terminal.writeln("\x1B[1;32m Pressed 2, Enter below details:\x1B[0m");
|
const region = vNetSettings?.location ?? userContext.databaseAccount?.location;
|
||||||
const subscriptionId = await askQuestion(terminal, "Existing VNet Subscription ID");
|
terminal.writeln(" - Identified Database Account Region: \x1B[32m" + region + "\x1B[0m");
|
||||||
const resourceGroup = await askQuestion(terminal, "Existing VNet Resource Group");
|
|
||||||
const vNetName = await askQuestion(terminal, "Existing VNet Name");
|
|
||||||
|
|
||||||
const vNetConfig = await getARMInfo<VnetModel>(`/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Network/virtualNetworks/${vNetName}`);
|
|
||||||
|
|
||||||
terminal.writeln("Suggested Network Profiles:");
|
|
||||||
const ipConfigurationProfiles = vNetConfig.properties.subnets.reduce<{ id: string }[]>(
|
|
||||||
(acc, subnet) => acc.concat(subnet.properties.ipConfigurationProfiles || []),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
for (let i = 0; i < ipConfigurationProfiles.length; i++) {
|
|
||||||
const match = ipConfigurationProfiles[i].id.match(/\/networkProfiles\/([^/]+)/);
|
|
||||||
const result = match ? `/${match[1]}` : null;
|
|
||||||
terminal.writeln(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
const networkProfile = await askQuestion(terminal, "Associated Network Profile");
|
|
||||||
|
|
||||||
const relays = await getARMInfo<RelayNamespaceResponse>(
|
|
||||||
`/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Relay/namespaces`,
|
|
||||||
"2024-01-01");
|
|
||||||
terminal.writeln("Suggested Network Relays:");
|
|
||||||
for (let i = 0; i < relays.value.length; i++) {
|
|
||||||
terminal.writeln(relays.value[i].name);
|
|
||||||
}
|
|
||||||
|
|
||||||
const relayName = await askQuestion(terminal, "Network Relay");
|
|
||||||
|
|
||||||
const networkProfileResourceId = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Network/networkProfiles/${networkProfile}`;
|
|
||||||
const relayResourceId = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Relay/namespaces/${relayName}`;
|
|
||||||
|
|
||||||
// const vNetRules = userContext.databaseAccount.properties?.virtualNetworkRules;
|
|
||||||
// if(vNetRules && vNetRules.length > 0) {
|
|
||||||
// for (let i = 0; i < vNetRules.length; i++) {
|
|
||||||
// const vNetName = vNetRules[i].id;
|
|
||||||
|
|
||||||
// terminal.writeln(vNetName);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
vNetSettings = {
|
|
||||||
networkProfileResourceId: networkProfileResourceId,
|
|
||||||
relayNamespaceResourceId: relayResourceId,
|
|
||||||
location: userContext.databaseAccount.location
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const region = userContext.databaseAccount?.location;
|
|
||||||
terminal.writeln(LogInfo(` Database Account Region identified as '${region}'`));
|
|
||||||
|
|
||||||
const defaultCloudShellRegion = "westus";
|
const defaultCloudShellRegion = "westus";
|
||||||
const resolvedRegion = getNormalizedRegion(region, defaultCloudShellRegion);
|
const resolvedRegion = getNormalizedRegion(region, defaultCloudShellRegion);
|
||||||
|
|
||||||
|
terminal.writeln("\x1B[33mAttempting to provision CloudShell in region: \x1B[1m" + resolvedRegion + "\x1B[0m");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
terminal.writeln("\x1B[34mProvisioning CloudShell session...\x1B[0m");
|
||||||
var { socketUri, provisionConsoleResponse, targetUri } = await provisionCloudShellSession(resolvedRegion, terminal, vNetSettings);
|
var { socketUri, provisionConsoleResponse, targetUri } = await provisionCloudShellSession(resolvedRegion, terminal, vNetSettings);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
terminal.writeln(LogError(err));
|
terminal.writeln(LogError(err));
|
||||||
terminal.writeln(LogError(`Unable to provision console in request region, Falling back to default region i.e. ${defaultCloudShellRegion}`));
|
terminal.writeln("\x1B[31mError: Unable to provision CloudShell in the requested region.\x1B[0m");
|
||||||
|
terminal.writeln("\x1B[33mFalling back to default region: " + defaultCloudShellRegion + "\x1B[0m");
|
||||||
|
terminal.writeln("\x1B[34mAttempting to provision CloudShell in the default region...\x1B[0m");
|
||||||
|
|
||||||
var { socketUri, provisionConsoleResponse, targetUri } = await provisionCloudShellSession(defaultCloudShellRegion, terminal, vNetSettings);
|
var { socketUri, provisionConsoleResponse, targetUri } = await provisionCloudShellSession(defaultCloudShellRegion, terminal, vNetSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,12 +175,15 @@ export const startCloudShellTerminal = async (terminal: Terminal, shellType: Ter
|
|||||||
|
|
||||||
// authorize the session
|
// authorize the session
|
||||||
try {
|
try {
|
||||||
|
terminal.writeln("\x1B[34mAuthorizing CloudShell session...\x1B[0m");
|
||||||
|
|
||||||
const authorizeResponse = await authorizeSession(provisionConsoleResponse.properties.uri);
|
const authorizeResponse = await authorizeSession(provisionConsoleResponse.properties.uri);
|
||||||
const cookieToken = authorizeResponse.token;
|
const cookieToken = authorizeResponse.token;
|
||||||
const a = document.createElement("img");
|
const a = document.createElement("img");
|
||||||
a.src = targetUri + "&token=" + encodeURIComponent(cookieToken);
|
a.src = targetUri + "&token=" + encodeURIComponent(cookieToken);
|
||||||
|
terminal.writeln("\x1B[32mAuthorization successful. Establishing connection...\x1B[0m");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
terminal.writeln(LogError('Unable to authorize the session'));
|
terminal.writeln("\x1B[31mError: Unable to authorize CloudShell session.\x1B[0m");
|
||||||
socket.close();
|
socket.close();
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@@ -182,7 +198,8 @@ export const startCloudShellTerminal = async (terminal: Terminal, shellType: Ter
|
|||||||
|
|
||||||
const askQuestion = (terminal: any, question: string) => {
|
const askQuestion = (terminal: any, question: string) => {
|
||||||
return new Promise<string>((resolve) => {
|
return new Promise<string>((resolve) => {
|
||||||
terminal.write(`\x1B[1;34m${question}: \x1B[0m`);
|
const prompt = `\x1B[1;34m${question}: \x1B[0m`;
|
||||||
|
terminal.writeln(prompt);
|
||||||
terminal.focus();
|
terminal.focus();
|
||||||
let response = "";
|
let response = "";
|
||||||
|
|
||||||
@@ -194,11 +211,18 @@ const askQuestion = (terminal: any, question: string) => {
|
|||||||
} else if (data === "\u007F" || data === "\b") { // Handle backspace
|
} else if (data === "\u007F" || data === "\b") { // Handle backspace
|
||||||
if (response.length > 0) {
|
if (response.length > 0) {
|
||||||
response = response.slice(0, -1);
|
response = response.slice(0, -1);
|
||||||
terminal.write("\b \b"); // Move cursor back, clear character
|
terminal.write("\x1B[D \x1B[D");// Move cursor back, clear character
|
||||||
}
|
}
|
||||||
} else {
|
} else if (data.charCodeAt(0) >= 32) { // Ignore control characters
|
||||||
response += data;
|
response += data;
|
||||||
terminal.write(data); // Display the typed or pasted characters
|
terminal.write(data); // Display typed characters
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Prevent cursor movement beyond the prompt
|
||||||
|
terminal.onKey(({ domEvent} : { domEvent: KeyboardEvent }) => {
|
||||||
|
if (domEvent.key === "ArrowLeft" && response.length === 0) {
|
||||||
|
domEvent.preventDefault(); // Stop moving left beyond the question
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -299,10 +323,12 @@ const provisionCloudShellSession = async(
|
|||||||
vNetSettings: object
|
vNetSettings: object
|
||||||
): Promise<{ socketUri?: string; provisionConsoleResponse?: any; targetUri?: string }> => {
|
): Promise<{ socketUri?: string; provisionConsoleResponse?: any; targetUri?: string }> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
terminal.writeln("");
|
||||||
// Show consent message inside the terminal
|
// Show consent message inside the terminal
|
||||||
terminal.writeln(`\x1B[1;33m ⚠️ Are you agreeing to continue with CloudShell terminal at ${resolvedRegion}.\x1B[0m`);
|
terminal.writeln(`\x1B[1;33m⚠️ CloudShell is not available in your database account region.\x1B[0m`);
|
||||||
terminal.writeln("\x1B[1;37m Press 'Y' to continue or 'N' to exit.\x1B[0m");
|
terminal.writeln(`\x1B[1;33mWould you like to continue with CloudShell in ${resolvedRegion} instead?\x1B[0m`);
|
||||||
|
terminal.writeln("\x1B[1;37mPress 'Y' to proceed or 'N' to cancel.\x1B[0m");
|
||||||
|
|
||||||
terminal.focus();
|
terminal.focus();
|
||||||
// Listen for user input
|
// Listen for user input
|
||||||
const handleKeyPress = terminal.onKey(async ({ key }: { key: string }) => {
|
const handleKeyPress = terminal.onKey(async ({ key }: { key: string }) => {
|
||||||
@@ -310,8 +336,9 @@ const provisionCloudShellSession = async(
|
|||||||
handleKeyPress.dispose();
|
handleKeyPress.dispose();
|
||||||
|
|
||||||
if (key.toLowerCase() === "y") {
|
if (key.toLowerCase() === "y") {
|
||||||
terminal.writeln("\x1B[1;32mConsent given. Requesting CloudShell. !\x1B[0m");
|
terminal.writeln("\x1B[1;32m✔ Consent received. Requesting CloudShell...\x1B[0m");
|
||||||
terminal.writeln(LogInfo('Applying fresh user settings...'));
|
terminal.writeln("");
|
||||||
|
terminal.writeln(LogInfo('Applying User Settings...'));
|
||||||
try {
|
try {
|
||||||
await putEphemeralUserSettings(userContext.subscriptionId, resolvedRegion, vNetSettings);
|
await putEphemeralUserSettings(userContext.subscriptionId, resolvedRegion, vNetSettings);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -327,19 +354,20 @@ const provisionCloudShellSession = async(
|
|||||||
terminal.writeln(LogError('Unable to provision console.'));
|
terminal.writeln(LogError('Unable to provision console.'));
|
||||||
return reject(err);
|
return reject(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add check for provisioning state
|
||||||
if (provisionConsoleResponse.properties.provisioningState !== "Succeeded") {
|
if (provisionConsoleResponse.properties.provisioningState !== "Succeeded") {
|
||||||
terminal.writeln(LogError("Failed to provision console."));
|
terminal.writeln(LogError("Failed to provision console."));
|
||||||
return reject(new Error("Failed to provision console."));
|
return reject(new Error("Failed to provision console."));
|
||||||
}
|
}
|
||||||
|
|
||||||
terminal.writeln(LogInfo("Connecting to CloudShell Terminal..."));
|
terminal.writeln("\x1B[34mConnecting to CloudShell Terminal...\x1B[0m");
|
||||||
// connect the terminal
|
// connect the terminal
|
||||||
let connectTerminalResponse;
|
let connectTerminalResponse;
|
||||||
try {
|
try {
|
||||||
connectTerminalResponse = await connectTerminal(provisionConsoleResponse.properties.uri, { rows: terminal.rows, cols: terminal.cols });
|
connectTerminalResponse = await connectTerminal(provisionConsoleResponse.properties.uri, { rows: terminal.rows, cols: terminal.cols });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
terminal.writeln(LogError('Unable to connect terminal.'));
|
terminal.writeln(LogError(`Unable to connect terminal. ${err}`));
|
||||||
return reject(err);
|
return reject(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,7 +387,7 @@ const provisionCloudShellSession = async(
|
|||||||
|
|
||||||
} else if (key.toLowerCase() === "n") {
|
} else if (key.toLowerCase() === "n") {
|
||||||
|
|
||||||
terminal.writeln("\x1B[1;31m Consent denied. Exiting...\x1B[0m");
|
terminal.writeln("\x1B[1;31mConsent denied. Exiting...\x1B[0m");
|
||||||
setTimeout(() => terminal.dispose(), 2000); // Close terminal after 2 sec
|
setTimeout(() => terminal.dispose(), 2000); // Close terminal after 2 sec
|
||||||
return resolve({});
|
return resolve({});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user