feat(network): integrate outbound route module on Ultra1.4 baseline

This commit is contained in:
daniellee2015
2026-04-27 13:25:46 +08:00
parent c98cfd7053
commit 2706d98225
16 changed files with 7979 additions and 26 deletions
File diff suppressed because it is too large Load Diff
+35
View File
@@ -0,0 +1,35 @@
// sidepanel/ip-proxy-provider-711proxy.js — 711Proxy 面板专属逻辑
function normalizeIpProxyCountryCode(value = '') {
const raw = String(value || '').trim().toUpperCase().replace(/[^A-Z]/g, '');
return /^[A-Z]{2}$/.test(raw) ? raw : '';
}
function infer711RegionFromHost(host = '') {
const text = String(host || '').trim().toLowerCase().replace(/\.$/, '');
if (!text || !text.includes('.')) {
return '';
}
const firstLabel = String(text.split('.')[0] || '').trim();
return /^[a-z]{2}$/.test(firstLabel) ? firstLabel.toUpperCase() : '';
}
function infer711RegionFromUsername(username = '') {
const text = String(username || '').trim();
if (!text) {
return '';
}
const match = text.match(/(?:^|[-_])region[-_:]?([A-Za-z]{2})\b/i);
return normalizeIpProxyCountryCode(match ? match[1] : '');
}
function resolve711ProxyRegionFromInputs({ host = '', username = '', region = '' } = {}) {
const fromUsername = infer711RegionFromUsername(username);
if (fromUsername) {
return fromUsername;
}
const fromHost = infer711RegionFromHost(host);
if (fromHost) {
return fromHost;
}
return normalizeIpProxyCountryCode(region);
}
+242
View File
@@ -781,6 +781,248 @@ header {
min-width: 0;
}
.ip-proxy-fold-row {
display: block;
}
.ip-proxy-fold {
width: 100%;
border: none;
border-radius: 0;
background: transparent;
padding: 0;
}
.ip-proxy-fold-body {
display: flex;
flex-direction: column;
gap: 8px;
border-top: none;
padding-top: 0;
}
.ip-proxy-layout-row {
display: block;
}
.ip-proxy-layout {
width: 100%;
display: block;
}
.ip-proxy-layout.is-account-only {
display: block;
}
.ip-proxy-column {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0;
border: none;
border-radius: 0;
background: transparent;
}
.ip-proxy-column-header {
display: none;
}
.ip-proxy-column-hint {
font-size: 12px;
line-height: 1.5;
color: var(--text-secondary);
}
.ip-proxy-column-mode-btn {
width: 100%;
justify-content: center;
}
.ip-proxy-column-api.is-disabled {
opacity: 0.55;
}
.ip-proxy-column-api.is-disabled .data-input,
.ip-proxy-column-api.is-disabled .data-select,
.ip-proxy-column-api.is-disabled .data-textarea,
.ip-proxy-column-api.is-disabled .input-icon-btn,
.ip-proxy-column-api.is-disabled .btn,
.ip-proxy-column-api.is-disabled .choice-btn {
cursor: not-allowed;
}
@media (max-width: 900px) {
.ip-proxy-layout {
display: block;
}
}
.ip-proxy-enabled-inline {
justify-content: flex-end;
gap: 10px;
}
.ip-proxy-actions-inline {
flex-wrap: wrap;
align-items: center;
row-gap: 6px;
}
.ip-proxy-action-grid {
width: 100%;
display: flex;
flex-wrap: nowrap;
gap: 8px;
}
.ip-proxy-action-grid .data-inline-btn {
flex: 1 1 0;
min-width: 0;
}
.ip-proxy-action-hint {
flex: 1 1 100%;
font-size: 11px;
line-height: 1.5;
color: var(--text-secondary);
}
.ip-proxy-enabled-status {
margin-left: auto;
display: inline-flex;
align-items: center;
justify-content: flex-end;
gap: 6px;
min-width: 92px;
font-size: 12px;
font-weight: 600;
color: var(--text-muted);
text-align: right;
white-space: nowrap;
}
.ip-proxy-enabled-status-dot {
width: 7px;
height: 7px;
border-radius: 999px;
background: var(--text-muted);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--text-muted) 25%, transparent);
flex-shrink: 0;
}
.ip-proxy-enabled-status.is-on {
color: var(--green);
}
.ip-proxy-enabled-status.is-on .ip-proxy-enabled-status-dot {
background: var(--green);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--green) 30%, transparent);
}
.ip-proxy-enabled-status.is-off {
color: var(--text-muted);
}
.ip-proxy-runtime-status {
display: inline-flex;
align-items: flex-start;
gap: 8px;
width: 100%;
min-height: 32px;
padding: 6px 10px;
border: 1px solid var(--border-subtle);
border-radius: var(--radius-sm);
background: var(--bg-surface);
}
.ip-proxy-runtime-content {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 4px;
}
.ip-proxy-runtime-main {
min-width: 0;
}
.ip-proxy-runtime-meta {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.ip-proxy-check-ip-btn {
min-width: 64px;
padding-inline: 10px;
flex-shrink: 0;
}
.ip-proxy-runtime-current {
flex: 1;
min-width: 0;
font-size: 12px;
color: var(--text-secondary);
}
.ip-proxy-runtime-current.has-value {
color: var(--text-primary);
}
.ip-proxy-runtime-dot {
width: 8px;
height: 8px;
border-radius: 999px;
background: var(--text-muted);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--text-muted) 28%, transparent);
flex-shrink: 0;
margin-top: 6px;
}
.ip-proxy-runtime-status.state-applied .ip-proxy-runtime-dot {
background: var(--green);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--green) 28%, transparent);
}
.ip-proxy-runtime-status.state-warning .ip-proxy-runtime-dot {
background: var(--orange);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--orange) 28%, transparent);
}
.ip-proxy-runtime-status.state-error .ip-proxy-runtime-dot {
background: var(--red);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--red) 28%, transparent);
}
.ip-proxy-runtime-details {
margin: 0;
padding: 0;
}
.ip-proxy-runtime-details summary {
cursor: pointer;
user-select: none;
color: var(--text-secondary);
font-size: 11px;
line-height: 1.4;
}
.ip-proxy-runtime-details[open] summary {
color: var(--text-primary);
}
.ip-proxy-runtime-details-text {
margin-top: 4px;
font-size: 11px;
line-height: 1.5;
color: var(--text-secondary);
white-space: pre-wrap;
word-break: break-word;
}
.mail2925-base-inline {
gap: 10px;
}
+159
View File
@@ -194,6 +194,163 @@
<span class="data-label">默认代理</span>
<input type="text" id="input-sub2api-default-proxy" class="data-input" placeholder="留空则不使用代理;或填写代理名称 / ID" />
</div>
<div class="data-row" id="row-ip-proxy-enabled">
<span class="data-label">IP代理</span>
<div class="data-inline ip-proxy-enabled-inline">
<label class="toggle-switch" for="input-ip-proxy-enabled" title="启用或禁用 IP 代理接管">
<input type="checkbox" id="input-ip-proxy-enabled" />
<span class="toggle-switch-track" aria-hidden="true">
<span class="toggle-switch-thumb"></span>
</span>
</label>
<span id="ip-proxy-enabled-status" class="ip-proxy-enabled-status" aria-live="polite">
<span id="ip-proxy-enabled-status-dot" class="ip-proxy-enabled-status-dot" aria-hidden="true"></span>
<span id="ip-proxy-enabled-status-text" class="ip-proxy-enabled-status-text">未开启</span>
</span>
</div>
</div>
<div class="data-row ip-proxy-fold-row" id="row-ip-proxy-fold" style="display:none;">
<div id="ip-proxy-fold" class="ip-proxy-fold">
<div class="ip-proxy-fold-body">
<div class="data-row" id="row-ip-proxy-service" style="display:none;">
<span class="data-label">代理服务</span>
<div class="data-inline">
<select id="select-ip-proxy-service" class="data-select" disabled>
<option value="711proxy">711Proxy(首版)</option>
</select>
<button id="btn-ip-proxy-service-login" class="btn btn-outline btn-sm data-inline-btn" type="button">
登录
</button>
</div>
</div>
<div class="data-row" id="row-ip-proxy-mode" style="display:none;">
<span class="data-label">代理模式</span>
<div id="ip-proxy-mode-group" class="choice-group" role="group" aria-label="IP代理模式">
<button type="button" class="choice-btn" data-ip-proxy-mode="account">账号密码</button>
<button type="button" class="choice-btn" data-ip-proxy-mode="api" disabled title="首版暂未开放">API 拉取(暂未开放)</button>
</div>
</div>
<div class="data-row ip-proxy-layout-row" id="row-ip-proxy-layout" style="display:none;">
<div class="ip-proxy-layout" id="ip-proxy-layout">
<div class="ip-proxy-column ip-proxy-column-account" id="ip-proxy-account-panel">
<div class="ip-proxy-column-header">账号密码模式</div>
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-account-list" style="display:none;">
<span class="data-label">账号代理列表</span>
<textarea id="input-ip-proxy-account-list" class="data-textarea mono"
placeholder="每行一条:host:port 或 host:port:username:password&#10;例如 global.rotgb.711proxy.com:10000:username:password"></textarea>
</div>
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-account-session-prefix" style="display:none;">
<span class="data-label">会话(session)</span>
<input type="text" id="input-ip-proxy-account-session-prefix" class="data-input mono"
placeholder="会话前缀,例如 ZC28qZ0KQL" />
</div>
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-account-life-minutes" style="display:none;">
<span class="data-label">时长(life)</span>
<div class="data-inline">
<input type="number" id="input-ip-proxy-account-life-minutes" class="data-input" min="1" max="1440" step="1"
placeholder="5-180(分钟)" title="711Proxy 会话时长范围:5-180 分钟" />
<span class="data-unit">分钟</span>
</div>
</div>
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-host" style="display:none;">
<span class="data-label">代理 Host</span>
<input type="text" id="input-ip-proxy-host" class="data-input" placeholder="例如 global.rotgb.711proxy.com" />
</div>
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-port" style="display:none;">
<span class="data-label">代理 Port</span>
<input type="number" id="input-ip-proxy-port" class="data-input" min="1" max="65535" step="1"
placeholder="例如 10000" />
</div>
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-protocol" style="display:none;">
<span class="data-label">代理协议</span>
<select id="select-ip-proxy-protocol" class="data-select">
<option value="http">HTTP</option>
<option value="https">HTTPS</option>
<option value="socks5">SOCKS5</option>
<option value="socks4">SOCKS4</option>
</select>
</div>
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-username" style="display:none;">
<span class="data-label">代理账号</span>
<div class="input-with-icon">
<input type="password" id="input-ip-proxy-username" class="data-input data-input-with-icon"
placeholder="例如 USER047152-zone-custom-region-US-asn-ASN81" />
<button id="btn-toggle-ip-proxy-username" class="input-icon-btn" type="button" aria-label="显示代理账号"
title="显示代理账号"></button>
</div>
</div>
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-password" style="display:none;">
<span class="data-label">代理密码</span>
<div class="input-with-icon">
<input type="password" id="input-ip-proxy-password" class="data-input data-input-with-icon"
placeholder="账号密码代理的密码" />
<button id="btn-toggle-ip-proxy-password" class="input-icon-btn" type="button" aria-label="显示代理密码"
title="显示代理密码"></button>
</div>
</div>
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-region" style="display:none;">
<span class="data-label">地区参数</span>
<input type="text" id="input-ip-proxy-region" class="data-input" placeholder="可选,例如 US / DE / HK" />
</div>
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-pool-target-count" style="display:none;">
<span class="data-label">任务切换阈值</span>
<div class="data-inline">
<input type="number" id="input-ip-proxy-pool-target-count" class="data-input" min="1" max="500" step="1"
placeholder="20(成功轮次)" title="每成功多少轮任务后自动切换一次代理;仅 1 条节点时会跳过自动切换" />
<span class="data-unit"></span>
</div>
</div>
</div>
<div class="ip-proxy-column ip-proxy-column-api" id="ip-proxy-api-panel">
<div class="ip-proxy-column-header">API 模式</div>
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-api-url" style="display:none;">
<span class="data-label">代理API</span>
<div class="input-with-icon">
<input type="password" id="input-ip-proxy-api-url" class="data-input data-input-with-icon"
placeholder="粘贴完整 API 链接" />
<button id="btn-toggle-ip-proxy-api-url" class="input-icon-btn" type="button" aria-label="显示代理 API"
title="显示代理 API"></button>
</div>
</div>
</div>
</div>
</div>
<div class="data-row" id="row-ip-proxy-actions" style="display:none;">
<span class="data-label">切换代理</span>
<div class="data-inline ip-proxy-actions-inline">
<span id="ip-proxy-action-buttons" class="ip-proxy-action-grid">
<button id="btn-ip-proxy-refresh" class="btn btn-outline btn-sm data-inline-btn" type="button">拉取</button>
<button id="btn-ip-proxy-next" class="btn btn-outline btn-sm data-inline-btn" type="button">下一条</button>
<button id="btn-ip-proxy-change" class="btn btn-outline btn-sm data-inline-btn" type="button" title="保持当前会话并刷新出口(仅 711 + session">Change</button>
<button id="btn-ip-proxy-probe" class="btn btn-outline btn-sm data-inline-btn" type="button">检测出口</button>
</span>
<span id="ip-proxy-action-hint" class="ip-proxy-action-hint">
下一条:切到代理池下一条。Change:保持当前 session 重绑链路(仅 711 + session)。
</span>
</div>
</div>
<div class="data-row" id="row-ip-proxy-runtime-status" style="display:none;">
<span class="data-label">代理状态</span>
<div id="ip-proxy-runtime-status" class="ip-proxy-runtime-status" aria-live="polite">
<span id="ip-proxy-runtime-dot" class="ip-proxy-runtime-dot" aria-hidden="true"></span>
<div class="ip-proxy-runtime-content">
<div class="ip-proxy-runtime-main">
<span id="ip-proxy-runtime-text" class="data-value data-value-fill">未启用,沿用浏览器默认/全局代理。</span>
</div>
<div class="ip-proxy-runtime-meta">
<span id="ip-proxy-current" class="ip-proxy-runtime-current mono truncate">未启用</span>
<button id="btn-ip-proxy-check-ip" class="btn btn-outline btn-xs data-inline-btn ip-proxy-check-ip-btn" type="button">检查IP</button>
</div>
<details id="ip-proxy-runtime-details" class="ip-proxy-runtime-details" hidden>
<summary>查看详细信息</summary>
<div id="ip-proxy-runtime-details-text" class="ip-proxy-runtime-details-text"></div>
</details>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="data-row" id="row-codex2api-url" style="display:none;">
<span class="data-label">Codex2API</span>
<input type="text" id="input-codex2api-url" class="data-input"
@@ -867,6 +1024,8 @@
<script src="mail-2925-manager.js"></script>
<script src="icloud-manager.js"></script>
<script src="luckmail-manager.js"></script>
<script src="ip-proxy-provider-711proxy.js"></script>
<script src="ip-proxy-panel.js"></script>
<script src="contribution-mode.js"></script>
<script src="account-records-manager.js"></script>
<script src="sidepanel.js"></script>
File diff suppressed because it is too large Load Diff