325 lines
17 KiB
HTML
325 lines
17 KiB
HTML
<!doctype html>
|
|
<html lang="zh-CN" data-theme="dark">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<script type="module" src="/src/language-bootstrap.ts"></script>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
<meta name="theme-color" content="#080b10" />
|
|
<meta
|
|
name="description"
|
|
content="安全的 Cloudflare Workers 原生 WebSSH 终端"
|
|
data-i18n-content-zh="安全的 Cloudflare Workers 原生 WebSSH 终端"
|
|
data-i18n-content-en="A secure, native Cloudflare Workers WebSSH terminal"
|
|
/>
|
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
|
<title>Workers WebSSH</title>
|
|
</head>
|
|
<body>
|
|
<div id="app" hidden>
|
|
<header class="topbar">
|
|
<div class="brand-group">
|
|
<button
|
|
id="panel-toggle"
|
|
class="icon-button"
|
|
type="button"
|
|
aria-label="打开连接面板"
|
|
data-i18n-aria-label-zh="打开连接面板"
|
|
data-i18n-aria-label-en="Open connection panel"
|
|
aria-controls="connection-panel"
|
|
aria-expanded="false"
|
|
>
|
|
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16" /></svg>
|
|
</button>
|
|
<a
|
|
class="brand"
|
|
href="/"
|
|
aria-label="Workers WebSSH 首页"
|
|
data-i18n-aria-label-zh="Workers WebSSH 首页"
|
|
data-i18n-aria-label-en="Workers WebSSH home"
|
|
>
|
|
<span class="brand-mark" aria-hidden="true">>_</span>
|
|
<span><strong>Workers</strong> WebSSH</span>
|
|
</a>
|
|
</div>
|
|
|
|
<div class="topbar-actions">
|
|
<div id="global-status" class="status-pill" data-state="idle" role="status" aria-live="polite">
|
|
<span class="status-dot"></span>
|
|
<span id="global-status-label">离线</span>
|
|
</div>
|
|
<button id="language-toggle" class="icon-button" type="button" aria-label="切换到英文" title="切换到英文" data-language="zh-CN">
|
|
<span class="language-icon" aria-hidden="true"><span>中</span><span>E</span></span>
|
|
</button>
|
|
<button
|
|
id="theme-toggle"
|
|
class="icon-button"
|
|
type="button"
|
|
aria-label="切换颜色主题"
|
|
data-i18n-aria-label-zh="切换颜色主题"
|
|
data-i18n-aria-label-en="Toggle color theme"
|
|
>
|
|
<svg class="sun-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="4" /><path d="M12 2v2M12 20v2M4.93 4.93l1.42 1.42M17.65 17.65l1.42 1.42M2 12h2M20 12h2M4.93 19.07l1.42-1.42M17.65 6.35l1.42-1.42" /></svg>
|
|
</button>
|
|
<a
|
|
class="icon-button"
|
|
href="https://github.com/cmliu/CF-Workers-WebSSH"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
aria-label="打开源代码仓库"
|
|
data-i18n-aria-label-zh="打开源代码仓库"
|
|
data-i18n-aria-label-en="Open source repository"
|
|
>
|
|
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2a10 10 0 0 0-3.16 19.49c.5.09.68-.22.68-.48v-1.87c-2.78.6-3.37-1.18-3.37-1.18-.45-1.16-1.11-1.47-1.11-1.47-.91-.62.07-.61.07-.61 1 .07 1.53 1.03 1.53 1.03.9 1.53 2.35 1.09 2.92.83.09-.65.35-1.09.64-1.34-2.22-.25-4.55-1.11-4.55-4.94 0-1.09.39-1.98 1.03-2.68-.1-.25-.45-1.27.1-2.64 0 0 .84-.27 2.75 1.02A9.56 9.56 0 0 1 12 6.82c.85 0 1.71.11 2.51.34 1.91-1.29 2.75-1.02 2.75-1.02.55 1.37.2 2.39.1 2.64.64.7 1.03 1.59 1.03 2.68 0 3.84-2.34 4.68-4.57 4.93.36.31.68.92.68 1.86v2.76c0 .27.18.58.69.48A10 10 0 0 0 12 2Z" /></svg>
|
|
</a>
|
|
</div>
|
|
</header>
|
|
|
|
<div id="app-shell" class="app-shell">
|
|
<button
|
|
id="panel-scrim"
|
|
class="panel-scrim"
|
|
type="button"
|
|
aria-label="关闭连接面板"
|
|
data-i18n-aria-label-zh="关闭连接面板"
|
|
data-i18n-aria-label-en="Close connection panel"
|
|
hidden
|
|
></button>
|
|
<aside
|
|
id="connection-panel"
|
|
class="connection-panel"
|
|
aria-label="连接管理"
|
|
data-i18n-aria-label-zh="连接管理"
|
|
data-i18n-aria-label-en="Connection management"
|
|
>
|
|
<div class="panel-heading">
|
|
<div>
|
|
<p class="eyebrow" data-i18n-zh="连接管理" data-i18n-en="CONNECTIONS">连接管理</p>
|
|
<h1 data-i18n-zh="SSH 工作台" data-i18n-en="SSH workspace">SSH 工作台</h1>
|
|
</div>
|
|
</div>
|
|
|
|
<section class="profiles-section" aria-labelledby="profiles-title">
|
|
<div class="section-title-row">
|
|
<h2 id="profiles-title" data-i18n-zh="历史记录" data-i18n-en="History">历史记录</h2>
|
|
<span id="profile-count" class="count-badge">0</span>
|
|
</div>
|
|
<div id="profile-list" class="profile-list">
|
|
<p class="empty-list" data-i18n-zh="暂无历史记录" data-i18n-en="No connection history yet.">暂无历史记录</p>
|
|
</div>
|
|
</section>
|
|
|
|
<form id="connection-form" class="connection-form" autocomplete="off" novalidate>
|
|
<input id="profile-id" type="hidden" />
|
|
<div class="field-grid host-grid">
|
|
<label class="field">
|
|
<span data-i18n-zh="主机" data-i18n-en="Host">主机</span>
|
|
<input id="host" name="host" type="text" maxlength="253" placeholder="ssh.example.com" required spellcheck="false" />
|
|
</label>
|
|
<label class="field port-field">
|
|
<span data-i18n-zh="端口" data-i18n-en="Port">端口</span>
|
|
<input id="port" name="port" type="number" value="22" min="1" max="65535" required inputmode="numeric" />
|
|
</label>
|
|
</div>
|
|
|
|
<div class="field-grid single">
|
|
<label class="field">
|
|
<span data-i18n-zh="用户名" data-i18n-en="Username">用户名</span>
|
|
<input id="username" name="username" type="text" value="root" maxlength="128" required autocomplete="username" spellcheck="false" />
|
|
</label>
|
|
</div>
|
|
|
|
<fieldset class="auth-fieldset">
|
|
<legend data-i18n-zh="身份认证" data-i18n-en="Authentication">身份认证</legend>
|
|
<div
|
|
class="segmented-control"
|
|
role="radiogroup"
|
|
aria-label="身份认证方式"
|
|
data-i18n-aria-label-zh="身份认证方式"
|
|
data-i18n-aria-label-en="Authentication method"
|
|
>
|
|
<label><input type="radio" name="authMethod" value="password" checked /><span data-i18n-zh="密码" data-i18n-en="Password">密码</span></label>
|
|
<label><input type="radio" name="authMethod" value="publickey" /><span data-i18n-zh="SSH 密钥" data-i18n-en="SSH key">SSH 密钥</span></label>
|
|
</div>
|
|
|
|
<div id="password-field" class="field secret-field">
|
|
<label for="password">
|
|
<span data-i18n-zh="密码" data-i18n-en="Password">密码</span>
|
|
<small data-i18n-zh="连接成功后使用 AES-GCM 加密保存" data-i18n-en="Encrypted with AES-GCM after a successful connection">连接成功后使用 AES-GCM 加密保存</small>
|
|
</label>
|
|
<span class="input-action-wrap">
|
|
<input id="password" name="password" type="password" maxlength="4096" autocomplete="current-password" />
|
|
<button id="reveal-password" class="input-action" type="button" aria-label="显示密码">显示</button>
|
|
</span>
|
|
</div>
|
|
|
|
<div id="key-field" class="key-field" hidden>
|
|
<label class="field">
|
|
<span>
|
|
<span data-i18n-zh="私钥" data-i18n-en="Private key">私钥</span>
|
|
<small data-i18n-zh="未加密 OpenSSH" data-i18n-en="unencrypted OpenSSH">未加密 OpenSSH</small>
|
|
</span>
|
|
<textarea id="private-key" name="privateKey" rows="5" maxlength="65536" placeholder="-----BEGIN OPENSSH PRIVATE KEY-----"></textarea>
|
|
</label>
|
|
<label class="file-picker">
|
|
<input id="key-file" type="file" accept=".pem,.key,.openssh,text/plain" />
|
|
<span data-i18n-zh="选择密钥文件" data-i18n-en="Choose key file">选择密钥文件</span>
|
|
<small id="key-file-name">未选择文件</small>
|
|
</label>
|
|
</div>
|
|
</fieldset>
|
|
|
|
<details class="advanced-options">
|
|
<summary data-i18n-zh="高级选项" data-i18n-en="Advanced options">高级选项</summary>
|
|
<div class="advanced-body">
|
|
<label class="field">
|
|
<span data-i18n-zh="初始命令" data-i18n-en="Initial command">初始命令</span>
|
|
<input id="initial-command" name="initialCommand" type="text" maxlength="4096" placeholder="tmux attach || tmux" spellcheck="false" />
|
|
</label>
|
|
<div class="field-grid two">
|
|
<label class="field">
|
|
<span data-i18n-zh="终端类型" data-i18n-en="Terminal">终端类型</span>
|
|
<select id="term-type" name="termType">
|
|
<option value="xterm-256color">xterm-256color</option>
|
|
<option value="xterm">xterm</option>
|
|
<option value="screen-256color">screen-256color</option>
|
|
</select>
|
|
</label>
|
|
<label class="field">
|
|
<span data-i18n-zh="字符编码" data-i18n-en="Encoding">字符编码</span>
|
|
<select id="encoding" name="encoding">
|
|
<option value="utf-8">UTF-8</option>
|
|
<option value="gb18030">GB18030</option>
|
|
<option value="big5">Big5</option>
|
|
</select>
|
|
</label>
|
|
</div>
|
|
<label class="field">
|
|
<span>
|
|
<span data-i18n-zh="预期主机指纹" data-i18n-en="Expected host fingerprint">预期主机指纹</span>
|
|
<small>SHA-256</small>
|
|
</span>
|
|
<input
|
|
id="fingerprint"
|
|
name="fingerprint"
|
|
type="text"
|
|
maxlength="128"
|
|
placeholder="首次使用后自动保存"
|
|
data-i18n-placeholder-zh="首次使用后自动保存"
|
|
data-i18n-placeholder-en="Saved after first use"
|
|
spellcheck="false"
|
|
/>
|
|
</label>
|
|
</div>
|
|
</details>
|
|
|
|
<div id="form-error" class="form-error" role="alert" hidden></div>
|
|
<div class="form-actions">
|
|
<button id="connect-button" class="primary-button" type="submit">
|
|
<span class="button-icon" aria-hidden="true">>_</span>
|
|
<span>连接</span>
|
|
</button>
|
|
<button
|
|
id="share-link"
|
|
class="icon-button bordered"
|
|
type="button"
|
|
aria-label="复制连接链接(含密码)"
|
|
data-i18n-aria-label-zh="复制连接链接(含密码)"
|
|
data-i18n-aria-label-en="Copy connection link (includes password)"
|
|
>
|
|
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" /></svg>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
<p
|
|
class="privacy-note"
|
|
data-i18n-zh="连接成功后会自动加入此浏览器的历史记录;密码使用当前浏览器配置的密钥加密,私钥不会保存。密钥缺失或不匹配时,密码将留空。"
|
|
data-i18n-en="Successful connections are added to this browser's history automatically; passwords are encrypted with a key held by this browser profile, while private keys are not saved. The password remains blank if the key is missing or does not match."
|
|
>连接成功后会自动加入此浏览器的历史记录;密码使用当前浏览器配置的密钥加密,私钥不会保存。密钥缺失或不匹配时,密码将留空。</p>
|
|
</aside>
|
|
|
|
<main class="workspace">
|
|
<section id="terminal-card" class="terminal-card">
|
|
<div class="terminal-toolbar">
|
|
<div
|
|
class="session-target"
|
|
aria-label="实时连接状态"
|
|
data-i18n-aria-label-zh="实时连接状态"
|
|
data-i18n-aria-label-en="Live connection status"
|
|
>
|
|
<span class="live-orb" id="live-orb"></span>
|
|
<div>
|
|
<strong id="session-title">无活动会话</strong>
|
|
<span id="session-subtitle">选择目标并连接</span>
|
|
</div>
|
|
</div>
|
|
<div class="session-metrics">
|
|
<div><span data-i18n-zh="延迟" data-i18n-en="RTT">延迟</span><strong id="metric-rtt">--</strong></div>
|
|
<div><span data-i18n-zh="时长" data-i18n-en="UPTIME">时长</span><strong id="metric-uptime">00:00</strong></div>
|
|
<div><span data-i18n-zh="主机密钥" data-i18n-en="HOST KEY">主机密钥</span><strong id="metric-host-key">--</strong></div>
|
|
</div>
|
|
<div class="terminal-actions">
|
|
<button id="clear-terminal" class="toolbar-button" type="button" data-i18n-zh="清屏" data-i18n-en="Clear">清屏</button>
|
|
<button id="fullscreen-terminal" class="toolbar-button" type="button" data-i18n-zh="全屏" data-i18n-en="Fullscreen">全屏</button>
|
|
</div>
|
|
</div>
|
|
<div id="terminal-stage" class="terminal-stage">
|
|
<div
|
|
id="terminal"
|
|
role="application"
|
|
aria-label="SSH 终端"
|
|
data-i18n-aria-label-zh="SSH 终端"
|
|
data-i18n-aria-label-en="SSH terminal"
|
|
></div>
|
|
<div id="terminal-empty" class="terminal-empty">
|
|
<div class="empty-glyph" aria-hidden="true">>_</div>
|
|
<h2 data-i18n-zh="终端已就绪" data-i18n-en="Your terminal is ready">终端已就绪</h2>
|
|
<p
|
|
data-i18n-zh="从连接面板建立 SSH 连接,开始加密的边缘会话。"
|
|
data-i18n-en="Open an SSH connection from the panel to start an encrypted edge session."
|
|
>从连接面板建立 SSH 连接,开始加密的边缘会话。</p>
|
|
<button id="empty-connect" class="small-button" type="button" data-i18n-zh="配置连接" data-i18n-en="Configure connection">配置连接</button>
|
|
</div>
|
|
</div>
|
|
<div class="event-bar">
|
|
<span id="event-message">Worker 运行时待命</span>
|
|
<button id="event-toggle" type="button" aria-expanded="false" data-i18n-zh="会话日志" data-i18n-en="Session log">会话日志</button>
|
|
</div>
|
|
<div id="event-log" class="event-log" hidden aria-live="polite"></div>
|
|
</section>
|
|
</main>
|
|
</div>
|
|
|
|
<div id="toast-region" class="toast-region" aria-live="polite"></div>
|
|
|
|
<dialog id="host-key-dialog" class="host-key-dialog" aria-labelledby="host-key-title">
|
|
<form method="dialog">
|
|
<span class="dialog-icon" aria-hidden="true" data-i18n-zh="密钥" data-i18n-en="KEY">密钥</span>
|
|
<div>
|
|
<p class="eyebrow" data-i18n-zh="主机身份" data-i18n-en="HOST IDENTITY">主机身份</p>
|
|
<h2 id="host-key-title" data-i18n-zh="信任此 SSH 主机?" data-i18n-en="Trust this SSH host?">信任此 SSH 主机?</h2>
|
|
<p
|
|
data-i18n-zh="服务器提供了尚未为此目标固定的密钥。继续前,请通过可信渠道核验。"
|
|
data-i18n-en="The server presented a key that has not been pinned for this target. Verify it through a trusted channel before continuing."
|
|
>服务器提供了尚未为此目标固定的密钥。继续前,请通过可信渠道核验。</p>
|
|
</div>
|
|
<dl>
|
|
<div><dt data-i18n-zh="目标" data-i18n-en="Target">目标</dt><dd id="host-key-target">--</dd></div>
|
|
<div><dt data-i18n-zh="密钥类型" data-i18n-en="Key type">密钥类型</dt><dd id="host-key-type">--</dd></div>
|
|
<div><dt data-i18n-zh="SHA-256 指纹" data-i18n-en="SHA-256 fingerprint">SHA-256 指纹</dt><dd id="host-key-fingerprint">--</dd></div>
|
|
</dl>
|
|
<label class="trust-check">
|
|
<input id="remember-host-key" type="checkbox" checked />
|
|
<span data-i18n-zh="下次记住并验证此指纹" data-i18n-en="Remember and verify this fingerprint next time">下次记住并验证此指纹</span>
|
|
</label>
|
|
<div class="dialog-actions">
|
|
<button id="reject-host-key" class="secondary-button" value="reject" type="submit" data-i18n-zh="取消连接" data-i18n-en="Cancel">取消连接</button>
|
|
<button id="accept-host-key" class="primary-button" value="accept" type="submit" data-i18n-zh="信任并继续" data-i18n-en="Trust & continue">信任并继续</button>
|
|
</div>
|
|
</form>
|
|
</dialog>
|
|
</div>
|
|
<script type="module" src="/src/main.ts"></script>
|
|
</body>
|
|
</html>
|