Improve WebCRT new-session flow for SecureCRT-like SSH/Telnet use.

Add quick-connect sessions with credential retry, raw interactive drivers, bootstrap banner replay, Huawei telnet prompt cleanup, and session rename/delete in the tree.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-08-01 22:52:31 +08:00
parent accd28c9a1
commit f6b399e03e
23 changed files with 3682 additions and 225 deletions

View file

@ -905,7 +905,7 @@ const en = {
sessionManager: "Device list",
connect: "Connect",
hint: "Pick an NE and open an interactive terminal in the browser. Credentials stay on the server.",
hintCrt: "Click an NE on the left to open a terminal. Includes managed and UME inventory; search + paging for large fleets.",
hintCrt: "Click an NE on the left to open a terminal. Includes managed, Sessions, and UME; or use New Session for a quick login.",
filterKeyword: "Filter",
filterKeywordPh: "Filter by name / IP / tag",
empty: "No matching NEs. Switch source or refine the search.",
@ -919,8 +919,25 @@ const en = {
source: {
all: "All",
managed: "Managed",
webcrt: "Sessions",
ume: "UME",
},
newSession: {
title: "New Session",
hint: "Enter host and protocol. SSH prompts for username/password (editable on retry); Telnet logs in interactively. Duplicate names become (1), (2)…; same IP is allowed.",
connect: "Connect",
connecting: "Connecting…",
protocol: "Protocol",
host: "Hostname",
port: "Port",
sessionName: "Session name",
sessionNamePh: "Optional; defaults to IP",
authTitle: "SSH Authentication",
savePassword: "Save password",
ipRequired: "Hostname is required",
userRequired: "Username is required",
passwordRequired: "Password is required",
},
status: {
idle: "Idle",
connecting: "Connecting",
@ -928,6 +945,47 @@ const en = {
error: "Error",
closed: "Closed",
},
phase: {
creating: "Creating session",
authenticating: "Authenticating",
waitingPrompt: "Waiting for device prompt",
hint: "Slow devices may take more than 10 seconds",
},
tabMenu: {
close: "Close",
closeOthers: "Close others",
closeAll: "Close all",
copyIp: "Copy IP",
copyIpOk: "Copied {{ip}}",
copyIpEmpty: "No IP on this tab",
},
treeMenu: {
rename: "Rename",
renameLabel: "Session name",
renameSave: "Save",
renameRequired: "Session name is required",
renamed: "Session renamed",
delete: "Delete session",
deleteConfirm: "Delete session \"{{name}}\"? This cannot be undone.",
deleted: "Session deleted",
},
keywordHl: {
title: "Keyword Highlighting",
enabled: "Enabled",
newKeyword: "New keyword",
newKeywordPh: "Keyword or regex",
regex: "Regular expression",
add: "Add",
colKeyword: "Keyword",
colRegex: "Regex",
empty: "No keywords yet",
moveUp: "Move up",
moveDown: "Move down",
remove: "Remove",
caseSensitive: "Case sensitive",
color: "Text color",
saved: "Keyword highlighting saved",
},
actions: {
reconnect: "Reconnect",
recordLog: "Record log",
@ -949,7 +1007,34 @@ const en = {
recent: "Recent",
favorite: "Toggle favorite",
encoding: "Encoding",
fontSize: "Font",
fontSize: "Font size",
theme: "Theme",
themeDark: "Dark",
themeLight: "Light",
colorScheme: "Color scheme",
backgroundColor: "Background",
foregroundColor: "Font color",
scheme: {
dark: "Deep blue (default)",
blackWhite: "Black on white",
whiteBlack: "White on black",
greenBlack: "Green on black",
amberBlack: "Amber on black",
custom: "Custom",
},
copyOnSelect: "Copy on select",
pasteDelay: "Paste max line wait",
pasteDelayOff: "No delay (bulk send)",
keepalive: "SSH keepalive",
keepaliveOff: "Off",
keepaliveSec: "{{n}} sec",
options: "Options",
globalSessionOptions: "Global Session Options",
sessionOptionsSave: "Save",
sessionOptionsCancel: "Cancel",
sessionOptionsSaved: "Global session options saved",
disconnectBanner: "Session disconnected",
disconnectBannerError: "Session error or connection lost",
broadcastPh: "Send to all tabs…",
buttonBarHint: "One per line: label|command",
postLoginPrompt: "Post-login commands (one per line, this device)",
@ -960,9 +1045,12 @@ const en = {
uploaded: "Upload complete",
},
err: {
deviceType: "Unsupported device type",
ipConflictRestart: "Database still enforces unique IP; restart the netx API and retry",
sessionLimit: "Session limit reached; close other terminals first",
neNotFound: "Network element not found",
credsIncomplete: "Incomplete username/password on this NE",
authRejected: "Wrong username or password. Please try again.",
cliProfile: "UME NEs require a CLI connect profile (default account / hop)",
connectFailed: "Device login failed: {{detail}}",
sftpHop: "SFTP does not support hop devices yet (direct SSH only)",

View file

@ -902,7 +902,7 @@ const zh = {
sessionManager: "设备列表",
connect: "连接",
hint: "选择网元,在浏览器中打开交互式终端。凭据由服务端解密,不会下发到前端。",
hintCrt: "在左侧点击网元即可打开终端;支持托管与 UME 网元,可用搜索与分页浏览。",
hintCrt: "在左侧点击网元即可打开终端;支持托管、会话与 UME;也可用菜单「新建会话」快速登录。",
filterKeyword: "筛选",
filterKeywordPh: "按名称 / IP / 标签筛选",
empty: "没有匹配的网元。可切换来源或调整搜索关键字。",
@ -916,8 +916,25 @@ const zh = {
source: {
all: "全部",
managed: "托管",
webcrt: "会话",
ume: "UME",
},
newSession: {
title: "新建会话",
hint: "填写主机与协议后连接。SSH 弹出用户名/密码(可改);Telnet 进终端交互登录。同名会话自动加 (1)、(2)…,允许相同 IP。",
connect: "连接",
connecting: "连接中…",
protocol: "协议",
host: "主机",
port: "端口",
sessionName: "会话名",
sessionNamePh: "可选,默认使用 IP",
authTitle: "SSH 登录",
savePassword: "保存密码",
ipRequired: "请填写主机地址",
userRequired: "请填写用户名",
passwordRequired: "请填写密码",
},
status: {
idle: "空闲",
connecting: "连接中",
@ -925,6 +942,47 @@ const zh = {
error: "错误",
closed: "已关闭",
},
phase: {
creating: "创建会话",
authenticating: "正在认证",
waitingPrompt: "等待设备提示符",
hint: "设备较慢时可能需要十几秒,请稍候",
},
tabMenu: {
close: "关闭",
closeOthers: "关闭其他",
closeAll: "关闭全部",
copyIp: "复制 IP",
copyIpOk: "已复制 {{ip}}",
copyIpEmpty: "该标签没有 IP",
},
treeMenu: {
rename: "重命名",
renameLabel: "会话名",
renameSave: "保存",
renameRequired: "请填写会话名",
renamed: "会话已重命名",
delete: "删除会话",
deleteConfirm: "确定删除会话「{{name}}」?此操作不可恢复。",
deleted: "会话已删除",
},
keywordHl: {
title: "关键字突出显示",
enabled: "启用",
newKeyword: "新建关键字",
newKeywordPh: "输入关键字或正则",
regex: "正则表达式",
add: "添加",
colKeyword: "关键字",
colRegex: "正则",
empty: "尚未添加关键字",
moveUp: "上移",
moveDown: "下移",
remove: "删除",
caseSensitive: "区分大小写",
color: "字符颜色",
saved: "关键字突出显示已保存",
},
actions: {
reconnect: "重新连接",
recordLog: "记录日志",
@ -947,6 +1005,33 @@ const zh = {
favorite: "收藏/取消",
encoding: "编码",
fontSize: "字号",
theme: "主题",
themeDark: "深色",
themeLight: "浅色",
colorScheme: "配色方案",
backgroundColor: "背景色",
foregroundColor: "字体色",
scheme: {
dark: "深蓝(默认)",
blackWhite: "白底黑字",
whiteBlack: "黑底白字",
greenBlack: "黑底绿字",
amberBlack: "黑底琥珀",
custom: "自定义",
},
copyOnSelect: "选中即复制",
pasteDelay: "粘贴最大行间隔",
pasteDelayOff: "无延迟(整块发送)",
keepalive: "SSH Keepalive",
keepaliveOff: "关闭",
keepaliveSec: "{{n}} 秒",
options: "选项",
globalSessionOptions: "全局会话选项",
sessionOptionsSave: "保存",
sessionOptionsCancel: "取消",
sessionOptionsSaved: "已保存全局会话选项",
disconnectBanner: "会话已断开",
disconnectBannerError: "会话出错或连接中断",
broadcastPh: "发送到全部标签…",
buttonBarHint: "每行:按钮名|命令",
postLoginPrompt: "登录后自动执行(每行一条,仅本设备)",
@ -957,9 +1042,12 @@ const zh = {
uploaded: "上传成功",
},
err: {
deviceType: "不支持的设备类型",
ipConflictRestart: "数据库仍限制 IP 唯一,请重启 netx API 后再试",
sessionLimit: "并发会话已达上限,请先关闭其他终端",
neNotFound: "网元不存在",
credsIncomplete: "网元账号或密码不完整",
authRejected: "用户名或密码错误,请重新输入",
cliProfile: "UME 网元需要先配置 CLI 连接模板(默认账号/跳板)",
connectFailed: "登录设备失败:{{detail}}",
sftpHop: "SFTP 暂不支持跳板设备,请使用直连 SSH",