Files
gpt/templates/account-manager.html
432539 fef45134d5 Initial commit: GPT 账号注册机 (open source)
只开源「GPT 账号注册机 / 管理」(account-manager):账号列表/导入导出、检测/批量检测、
拿 RT、协议/浏览器注册等注册机能力。批量直开 / 协议支付 / 提取链接 / 直卡直开 等其它功能
不含实现代码,仅通过 iframe 内嵌外链访问。真实机密均由 .gitignore 忽略,仅提供脱敏示例配置。
2026-08-05 14:54:32 +08:00

414 lines
22 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="zh-CN" data-api-base="{{ request.script_root }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>账号管理</title>
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='app-shell.css', v='13') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='account-manager.css', v='24') }}">
</head>
<body>
<div class="layout">
{% set active = 'gpt' %}
{% include '_sidebar.html' %}
<div class="main">
<header class="topbar">
<div class="topbar-title">账号管理</div>
<div class="topbar-actions">
<button id="btnSettings" class="secondary small" type="button">⚙ 设置</button>
<span class="dot"></span>服务运行中
</div>
</header>
<div class="content">
<!-- 统计卡片 -->
<section class="am-stats">
<div class="am-stat-card"><div class="am-stat-num" id="statA">0</div><div class="am-stat-label" id="statAL">账号总数</div></div>
<div class="am-stat-card"><div class="am-stat-num" id="statB">0</div><div class="am-stat-label" id="statBL">已选中</div></div>
<div class="am-stat-card"><div class="am-stat-num" id="statC">0</div><div class="am-stat-label" id="statCL">有效 AT</div></div>
<div class="am-stat-card"><div class="am-stat-num" id="statD">0</div><div class="am-stat-label" id="statDL">续期数量</div></div>
</section>
<!-- ============ GPT 账号管理(primary ============ -->
<section class="card am-view" id="view-gpt">
<div class="am-toolbar">
<label class="am-auto-refresh" title="对未知状态、已过期或临近过期(<1天)的账号自动用 refresh_token 续期/校验">
<input type="checkbox" id="pbAutoRefresh">
<span>自动刷新 TOKEN</span>
<span class="muted" id="pbAutoRefreshHint"></span>
</label>
<label class="am-auto-refresh" title="只显示「批量直开」已开通成功的账号">
<input type="checkbox" id="pbOpenedOnly">
<span>只看已开通</span>
</label>
<span id="pbDetectStatus" class="am-detect-status" hidden></span>
<div class="am-toolbar-spacer"></div>
<div class="am-dropdown" id="ddBatch">
<button id="ddBatchToggle" class="am-dropdown-toggle secondary" type="button" aria-haspopup="true" aria-expanded="false">批量操作 <span class="am-caret"></span></button>
<div class="am-dropdown-menu" role="menu">
<button id="btnPbDetectBatch" type="button" role="menuitem">批量检测</button>
<button id="btnPbExportAt" type="button" role="menuitem">批量导出 AT</button>
<button id="btnPbExportSub" type="button" role="menuitem">批量导出 SUB 格式</button>
<button id="btnPbExportPw" type="button" role="menuitem">导出账密</button>
</div>
</div>
<div class="am-dropdown" id="ddDelete">
<button class="am-dropdown-toggle danger" type="button" aria-haspopup="true" aria-expanded="false">删除 <span class="am-caret"></span></button>
<div class="am-dropdown-menu" role="menu">
<button id="btnPbDelSel" class="danger-item" type="button" role="menuitem">删除所选</button>
<button id="btnPbDelAll" class="danger-item" type="button" role="menuitem">删除全部</button>
<button id="btnPbDelInvalid" class="danger-item" type="button" role="menuitem">删除失效</button>
<button id="btnPbDelExpired" class="danger-item" type="button" role="menuitem">删除过期</button>
</div>
</div>
<button id="btnPbRefetch" class="secondary" type="button" title="对勾选账号用 codex 客户端重新登录拿 RT(需要手机验证会自动接码)">重拿RT</button>
<button id="btnPbBindPhone" class="secondary" type="button" title="对「跳过手机注册」的待绑手机账号(勾选或全部)补做手机验证并拿 RT">批量绑手机拿RT</button>
<button id="btnProtocolReg" class="am-btn-green" type="button">账号注册</button>
<button id="btnPbImport" type="button">导入账号</button>
</div>
<div class="am-table-wrap">
<table class="am-table am-table-gpt" id="pbTable">
<thead>
<tr>
<th class="col-check"><input type="checkbox" id="pbCheckAll"></th>
<th class="col-idx">#</th>
<th class="col-email">邮箱</th>
<th class="col-at">AT + 有效期</th>
<th class="col-rt">RefreshToken</th>
<th class="col-sub">SUB</th>
<th class="col-status">状态</th>
<th class="col-ops">操作</th>
</tr>
</thead>
<tbody id="pbRows"></tbody>
</table>
<div class="empty" id="pbEmpty" hidden></div>
</div>
<div class="am-pager">
<div class="am-pager-left">
<span class="am-pager-label">每页</span>
<select id="pbPageSize" class="am-pagesize">
<option value="10">10</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="500">500</option>
<option value="1000">1000</option>
<option value="5000">5000</option>
</select>
<span class="am-pager-label"></span>
</div>
<div class="am-pager-right">
<span class="am-pager-info" id="pbPageInfo">第 1 / 1 页,共 0 条</span>
<button id="pbPagePrev" class="secondary small" type="button">上一页</button>
<button id="pbPageNext" class="secondary small" type="button">下一页</button>
</div>
</div>
</section>
<!-- ============ PLUS 邮箱(secondary,保留原有查收 / 拿RT ============ -->
<section class="card am-view" id="view-plus" hidden>
<div class="am-toolbar">
<div class="am-search">
<span class="am-search-icon">🔍</span>
<input type="text" id="plSearch" placeholder="搜索邮箱 / 域名 / 备注" autocomplete="off">
</div>
<select id="plFilter" class="am-filter" title="按 RT 状态筛选">
<option value="all">全部</option>
<option value="rt">已拿到RT</option>
<option value="nort">未拿到RT</option>
<option value="banned">已封号</option>
</select>
<div class="am-toolbar-spacer"></div>
<button id="btnPlImport" type="button">批量导入</button>
<button id="btnPlGetRT" type="button">批量拿RT</button>
<button id="btnPlExport" class="secondary" type="button">批量导出</button>
<button id="btnPlDeleteBanned" class="danger" type="button">批量删除封号</button>
<button id="btnPlDelete" class="danger" type="button">删除选中</button>
</div>
<div class="am-table-wrap">
<table class="am-table">
<thead>
<tr>
<th class="col-check"><input type="checkbox" id="plCheckAll"></th>
<th class="col-idx">#</th>
<th>邮箱</th>
<th>密码</th>
<th>是否是PLUS</th>
<th>是否有RT</th>
<th class="col-ops">操作</th>
</tr>
</thead>
<tbody id="plRows"></tbody>
</table>
<div class="empty" id="plEmpty" hidden>
<b>暂无 PLUS 账号</b>
<span>点击「批量导入」开始添加。</span>
</div>
</div>
<div class="am-pager">
<div class="am-pager-left">
<span class="am-pager-label">每页</span>
<select id="plPageSize" class="am-pagesize">
<option value="10">10</option>
<option value="100">100</option>
<option value="500">500</option>
<option value="1000">1000</option>
<option value="5000">5000</option>
</select>
<span class="am-pager-label"></span>
</div>
<div class="am-pager-right">
<span class="am-pager-info" id="plPageInfo">第 1 / 1 页,共 0 条</span>
<button id="plPagePrev" class="secondary small" type="button">上一页</button>
<button id="plPageNext" class="secondary small" type="button">下一页</button>
</div>
</div>
</section>
</div>
</div>
</div>
<!-- sub2api 导入弹窗 -->
<div id="pbImportModal" class="am-modal" hidden>
<div class="am-modal-dialog">
<div class="am-modal-head"><b>导入 GPT 账号 (sub2api)</b><button id="pbImportClose" class="am-modal-close" type="button" aria-label="关闭">×</button></div>
<p class="hint">支持三种格式:① 粘贴 <code>sub2api.json</code> 导出内容(含 accounts 数组);② 每行 <code>email----password----clientId----refreshToken</code>;③ 每行一个 <code>access token</code></p>
<textarea id="pbImportText" spellcheck="false" autocomplete="off" placeholder='粘贴 sub2api JSON、每行 email----password----clientId----refreshToken,或每行一个 access token'></textarea>
<div id="pbParsePreview" class="hint">识别到 0 个有效账号</div>
<div class="am-modal-actions">
<button id="btnDoPbImport" type="button">导入</button>
<button id="pbImportCancel" class="secondary" type="button">取消</button>
</div>
</div>
</div>
<!-- GPT 账号编辑弹窗 -->
<div id="pbEditModal" class="am-modal" hidden>
<div class="am-modal-dialog">
<div class="am-modal-head"><b>编辑 GPT 账号</b><button id="pbEditClose" class="am-modal-close" type="button" aria-label="关闭">×</button></div>
<input type="hidden" id="pbEditId">
<label>邮箱<input type="text" id="pbEditEmail" autocomplete="off"></label>
<label>密码<input type="text" id="pbEditPassword" autocomplete="off"></label>
<label>2FA 密钥<input type="text" id="pbEditTwoFactor" spellcheck="false" autocomplete="off" placeholder="base32 密钥"></label>
<label>Access Token (AT)<textarea id="pbEditAccessToken" class="am-mono-area am-mono-area-sm" rows="3" spellcheck="false" autocomplete="off"></textarea></label>
<label>Refresh Token<textarea id="pbEditRefreshToken" class="am-mono-area am-mono-area-sm" rows="3" spellcheck="false" autocomplete="off"></textarea></label>
<label>ChatGPT Account ID<input type="text" id="pbEditAccountId" autocomplete="off"></label>
<label>备注<input type="text" id="pbEditNote" autocomplete="off"></label>
<div class="am-modal-actions">
<button id="btnSavePbEdit" type="button">保存</button>
<button id="pbEditCancel" class="secondary" type="button">取消</button>
</div>
</div>
</div>
<!-- 设置弹窗:代理池 / 接码设置 / 邮箱池 -->
<div id="settingsModal" class="am-modal" hidden>
<div class="am-modal-dialog am-modal-wide">
<div class="am-modal-head"><b>设置</b><button id="settingsClose" class="am-modal-close" type="button" aria-label="关闭">×</button></div>
<div class="am-set-tabs">
<button class="am-set-tab active" data-set-tab="proxy" type="button">代理池</button>
<button class="am-set-tab" data-set-tab="sms" type="button">接码设置</button>
<button class="am-set-tab" data-set-tab="mailbox" type="button">邮箱池</button>
<button class="am-set-tab" data-set-tab="browser" type="button">浏览器</button>
</div>
<div class="am-set-panel" data-set-panel="proxy">
<div class="am-set-block">
<div class="section-title"><b>代理池</b><small>一行一个,支持 <code>host:port</code><code>host:port:user:pass</code><code>scheme://user:pass@host:port</code></small></div>
<textarea id="setProxies" class="am-mono-area am-set-proxies" spellcheck="false" autocomplete="off" placeholder="host:port:user:pass"></textarea>
<div class="hint" id="setProxyCount">共 0 条</div>
</div>
</div>
<div class="am-set-panel" data-set-panel="sms" hidden>
<div class="am-set-block">
<div class="section-title"><b>接码 / SMS 配置</b><small>拿 RT 若触发手机验证时使用</small></div>
<div class="am-set-grid">
<label>Provider
<select id="setSmsProvider">
<option value="smscode">smscode</option>
<option value="smsbower">smsbower</option>
<option value="herosms">herosms</option>
</select>
</label>
<label>API Key<input type="text" id="setSmsApiKey" autocomplete="off" placeholder="接码平台 API Key"></label>
<label>Base URL<input type="text" id="setSmsBaseUrl" autocomplete="off" placeholder="https://..."></label>
<label>国家 (country)<input type="text" id="setSmsCountry" autocomplete="off" placeholder="国家代码,如 22"></label>
<label>最高金额 (maxPrice)<input type="text" id="setSmsMaxPrice" autocomplete="off" placeholder="如 5"></label>
<label>Poll Timeout (ms)<input type="number" id="setSmsPollTimeout" autocomplete="off" placeholder="180000"></label>
</div>
<div class="am-sms-testbar">
<button id="smsBalanceBtn" class="secondary small" type="button">测试 / 查询余额</button>
<span id="smsBalanceResult" class="am-sms-bal"></span>
</div>
</div>
</div>
<div class="am-set-panel" data-set-panel="mailbox" hidden>
<div class="am-set-block">
<div class="section-title"><b>邮箱池</b><small>一行一个,格式 <code>邮箱----专用密码</code><code>邮箱----取件API链接</code></small></div>
<label class="am-set-check"><input type="checkbox" id="setMbSplit"> 分裂邮箱(为主邮箱生成 <code>local+XXXX@domain</code> 别名,共用同一收件箱)</label>
<div class="am-mb-splitbar" id="mbSplitBar">
每个主邮箱生成 <input type="number" id="mbSplitCount" class="am-mb-splitnum" min="1" max="200" value="10"> 个别名
<button id="mbSplitGen" class="secondary small" type="button">生成到列表</button>
<button id="mbToggleImport" class="secondary small" type="button">批量导入</button>
<button id="mbClearAll" class="secondary small am-danger" type="button">清空</button>
<span class="hint am-mb-count" id="setMbCount">共 0 个(未使用 0</span>
</div>
<div id="mbImportBox" hidden>
<textarea id="setMailbox" class="am-mono-area am-set-proxies" spellcheck="false" autocomplete="off" placeholder="name@icloud.com----abcd-efgh-ijkl-mnop(粘贴后点「导入到列表」)"></textarea>
<div class="am-mb-importbar">
<button id="mbImportBtn" class="secondary small" type="button">导入到列表</button>
</div>
</div>
<div id="mbList" class="am-mb-list"></div>
<div id="mbTestResult" class="am-mb-result" hidden></div>
</div>
</div>
<div class="am-set-panel" data-set-panel="browser" hidden>
<div class="am-set-block">
<div class="section-title"><b>浏览器注册(CloakBrowser</b><small>「账号注册」选择浏览器注册时使用,隐身 Chromium 自带指纹随机</small></div>
<div class="am-set-grid">
<label>CloakBrowser KEY<input type="text" id="setCloakKey" autocomplete="off" placeholder="cb_xxxxxxxxlicense key"></label>
<label>注册密码<input type="text" id="setRegPassword" autocomplete="off" placeholder="留空用内置默认强口令"></label>
</div>
<div class="hint">留空则以免费额度启动(可能受限)。首次运行会自动下载隐身 Chromium(需联网,约 200MB)。</div>
<div class="hint">注册密码:两种注册方式共用,所有账号统一用此口令;改后新注册生效(旧账号仍是旧口令)。</div>
</div>
</div>
<div class="am-modal-actions">
<button id="btnSaveSettings" type="button">保存</button>
<button id="settingsCancel" class="secondary" type="button">取消</button>
</div>
</div>
</div>
<!-- PLUS 批量导入弹窗 -->
<div id="plImportModal" class="am-modal" hidden>
<div class="am-modal-dialog">
<div class="am-modal-head"><b>批量导入 PLUS 账号</b><button id="plImportClose" class="am-modal-close" type="button" aria-label="关闭">×</button></div>
<p class="hint">每行一个账号,格式:<code>email----password----clientId----refreshToken</code>(也兼容 <code>|</code> / 制表符 / 空白分隔)。</p>
<textarea id="plImportText" spellcheck="false" autocomplete="off" placeholder="email----password----clientId----refreshToken"></textarea>
<div id="plParsePreview" class="hint">识别到 0 个有效账号</div>
<div class="am-modal-actions">
<button id="btnDoPlImport" type="button">导入</button>
<button id="plImportCancel" class="secondary" type="button">取消</button>
</div>
</div>
</div>
<!-- 查收抽屉 -->
<div id="inboxDrawer" class="am-drawer" hidden>
<div class="am-drawer-mask" id="inboxMask"></div>
<div class="am-drawer-panel">
<div class="am-drawer-head">
<div><b>收件箱</b><div class="am-drawer-addr" id="inboxAddr"></div></div>
<div class="am-drawer-head-actions">
<button id="inboxRefresh" class="secondary small" type="button">刷新</button>
<button id="inboxClose" class="am-modal-close" type="button" aria-label="关闭">×</button>
</div>
</div>
<div class="am-drawer-body" id="inboxBody"></div>
</div>
</div>
<!-- 拿 RT 进度面板 -->
<div id="rtPanel" class="am-rt-panel" hidden>
<div class="am-rt-head">
<b>批量拿 RT</b>
<span id="rtSummary" class="am-rt-summary"></span>
<button id="rtPanelClose" class="am-modal-close" type="button" aria-label="关闭">×</button>
</div>
<div class="am-rt-logs" id="rtLogs"></div>
</div>
<!-- 通用确认弹窗(替代原生 confirm/alert,主题化) -->
<div id="confirmModal" class="am-modal" hidden>
<div class="am-modal-dialog am-modal-confirm">
<div class="am-modal-head"><b id="confirmTitle">确认操作</b><button id="confirmCloseX" class="am-modal-close" type="button" aria-label="关闭">×</button></div>
<p class="hint" id="confirmMsg"></p>
<input id="confirmInput" class="am-prompt-input" type="text" autocomplete="off" spellcheck="false" hidden>
<div class="am-modal-actions">
<button id="confirmOk" type="button">确定</button>
<button id="confirmCancel" class="secondary" type="button">取消</button>
</div>
</div>
</div>
<!-- 邮箱编辑弹窗 -->
<div id="mbEditModal" class="am-modal" hidden>
<div class="am-modal-dialog am-modal-confirm">
<div class="am-modal-head"><b>编辑邮箱</b><button id="mbEditCloseX" class="am-modal-close" type="button" aria-label="关闭">×</button></div>
<div class="am-mb-edit-field"><span class="am-mb-edit-lbl">邮箱</span><div id="mbEditEmail" class="am-mb-edit-email"></div></div>
<div class="am-mb-edit-field"><span class="am-mb-edit-lbl">凭证(专用密码 / 取件 API 链接)</span>
<input id="mbEditCred" class="am-prompt-input" type="text" autocomplete="off" spellcheck="false" placeholder="专用密码 或 https://取件API…"></div>
<label class="am-set-check am-mb-edit-used"><input type="checkbox" id="mbEditUsed"> 已使用</label>
<div class="am-modal-actions">
<button id="mbEditSave" type="button">保存</button>
<button id="mbEditCancel" class="secondary" type="button">取消</button>
</div>
</div>
</div>
<!-- 协议注册:设置弹窗 -->
<div id="regModal" class="am-modal" hidden>
<div class="am-modal-dialog">
<div class="am-modal-head"><b>账号注册</b><button id="regCloseX" class="am-modal-close" type="button" aria-label="关闭">×</button></div>
<p class="hint">使用「设置」中已配置的代理池、接码、邮箱池,对<b>未使用</b>邮箱注册。任务后台运行,切换页面不会中断。</p>
<div class="am-set-grid">
<label>注册方式
<select id="regMode">
<option value="protocol">协议注册(HTTP 直连)</option>
<option value="browser">浏览器注册(CloakBrowser</option>
</select>
</label>
<label id="regHeadlessWrap" hidden>浏览器模式
<select id="regHeadless">
<option value="1">无头(后台运行)</option>
<option value="0">有头(显示窗口)</option>
</select>
</label>
<label>本次注册数量<input type="number" id="regCount" min="1" value="10"></label>
<label>线程数<input type="number" id="regThreads" min="1" max="20" value="3"></label>
</div>
<div class="reg-avail">未使用邮箱剩余:<b id="regUnused"></b></div>
<div class="am-modal-actions">
<button id="regStart" type="button">开始注册</button>
<button id="regCancel" class="secondary" type="button">取消</button>
</div>
</div>
</div>
<!-- 协议注册:右下角进度浮层 -->
<div id="regPanel" class="reg-panel" hidden>
<div class="reg-panel-head" id="regPanelHead">
<b class="reg-panel-title">账号注册</b>
<span class="reg-panel-stat" id="regPanelStat"></span>
<span class="reg-panel-tools">
<button id="regPanelClear" class="reg-panel-btn" type="button" title="清空日志">清空</button>
<button id="regPanelMin" class="reg-panel-btn" type="button" title="最小化"></button>
<button id="regPanelStop" class="reg-panel-btn" type="button" title="停止任务">停止</button>
<button id="regPanelClose" class="reg-panel-btn" type="button" title="关闭" hidden>×</button>
</span>
</div>
<div class="reg-panel-body">
<div class="reg-progress"><div class="reg-progress-bar" id="regProgressBar"></div></div>
<div class="reg-logs" id="regLogs"></div>
</div>
</div>
<div id="amToast" class="am-toast" role="status" aria-live="polite"></div>
<script src="{{ url_for('static', filename='account-manager.js', v='24') }}"></script>
</body>
</html>