This commit is contained in:
TYt50
2026-01-29 17:27:34 +08:00
parent e317b7f6ae
commit b1937251f0
5 changed files with 58 additions and 21 deletions

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@ node_modules/
# Database and user data (Portable data) # Database and user data (Portable data)
accounts.db accounts.db
data/ data/
DB/
*.db-journal *.db-journal
# Exports (Contains sensitive information) # Exports (Contains sensitive information)

View File

@@ -60,10 +60,11 @@ class AccountDatabase {
) )
`); `);
// 迁移: 添加 tags 和 vault_id 和 proxy 列 (如果不存在) // 迁移: 添加 tags 和 vault_id 和 proxy 和 browser_id 列 (如果不存在)
try { this.db.run(`ALTER TABLE accounts ADD COLUMN tags TEXT`); } catch (e) { } try { this.db.run(`ALTER TABLE accounts ADD COLUMN tags TEXT`); } catch (e) { }
try { this.db.run(`ALTER TABLE accounts ADD COLUMN vault_id INTEGER`); } catch (e) { } try { this.db.run(`ALTER TABLE accounts ADD COLUMN vault_id INTEGER`); } catch (e) { }
try { this.db.run(`ALTER TABLE accounts ADD COLUMN proxy TEXT`); } catch (e) { } try { this.db.run(`ALTER TABLE accounts ADD COLUMN proxy TEXT`); } catch (e) { }
try { this.db.run(`ALTER TABLE accounts ADD COLUMN browser_id TEXT`); } catch (e) { }
// 创建默认资料库 (如果不存在) // 创建默认资料库 (如果不存在)
const defaultVault = this.db.exec("SELECT id FROM vaults WHERE name = '默认'"); const defaultVault = this.db.exec("SELECT id FROM vaults WHERE name = '默认'");
@@ -218,8 +219,8 @@ class AccountDatabase {
await this.ready; await this.ready;
this.db.run(` this.db.run(`
INSERT INTO accounts (vault_id, name, username, password, totp_secret, tags, email, proxy, notes) INSERT INTO accounts (vault_id, name, username, password, totp_secret, tags, email, proxy, notes, browser_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, [ `, [
account.vault_id || null, account.vault_id || null,
account.name, account.name,
@@ -229,7 +230,8 @@ class AccountDatabase {
account.tags || '', account.tags || '',
account.email, account.email,
account.proxy || '', account.proxy || '',
account.notes account.notes,
account.browser_id || ''
]); ]);
this.save(); this.save();
@@ -246,7 +248,7 @@ class AccountDatabase {
this.db.run(` this.db.run(`
UPDATE accounts UPDATE accounts
SET vault_id = ?, name = ?, username = ?, password = ?, totp_secret = ?, SET vault_id = ?, name = ?, username = ?, password = ?, totp_secret = ?,
tags = ?, email = ?, proxy = ?, notes = ?, updated_at = datetime('now') tags = ?, email = ?, proxy = ?, notes = ?, browser_id = ?, updated_at = datetime('now')
WHERE id = ? WHERE id = ?
`, [ `, [
account.vault_id || null, account.vault_id || null,
@@ -258,6 +260,7 @@ class AccountDatabase {
account.email, account.email,
account.proxy || '', account.proxy || '',
account.notes, account.notes,
account.browser_id || '',
id id
]); ]);

View File

@@ -5,7 +5,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" <meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; font-src 'self' data: https://cdnjs.cloudflare.com"> content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; font-src 'self' data: https://cdnjs.cloudflare.com; connect-src 'self' http://localhost:12138 http://127.0.0.1:12138">
<title>2FA 账号管理器</title> <title>2FA 账号管理器</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="styles.css"> <link rel="stylesheet" href="styles.css">
@@ -108,6 +108,11 @@
<input type="text" id="proxy" placeholder="如: socks5://127.0.0.1:1080"> <input type="text" id="proxy" placeholder="如: socks5://127.0.0.1:1080">
</div> </div>
<div class="form-group">
<label for="browserId">Browser ID</label>
<input type="text" id="browserId" placeholder="Browser 环境 ID 或名称">
</div>
<div class="form-group"> <div class="form-group">
<label for="tags">标签</label> <label for="tags">标签</label>
<input type="text" id="tags" placeholder="用逗号分隔,如: 工作, 社交"> <input type="text" id="tags" placeholder="用逗号分隔,如: 工作, 社交">

View File

@@ -492,6 +492,11 @@ function renderAccounts() {
<div class="card-header"> <div class="card-header">
${tagsHtml || '<span class="card-name-placeholder">无标签</span>'} ${tagsHtml || '<span class="card-name-placeholder">无标签</span>'}
<div class="card-actions"> <div class="card-actions">
${acc.browser_id ? `
<button class="card-action-btn browser" onclick="openBrowser('${escapeJs(acc.browser_id)}')" title="打开 Browser">
<i class="fa-solid fa-arrow-up-right-from-square"></i>
</button>
` : ''}
<button class="card-action-btn move" onclick="openMoveDialog(${acc.id})" title="迁移"> <button class="card-action-btn move" onclick="openMoveDialog(${acc.id})" title="迁移">
<i class="fa-solid fa-arrow-right-arrow-left"></i> <i class="fa-solid fa-arrow-right-arrow-left"></i>
</button> </button>
@@ -657,6 +662,7 @@ async function handleFormSubmit(e) {
tags: tags, tags: tags,
email: document.getElementById('email').value.trim(), email: document.getElementById('email').value.trim(),
proxy: document.getElementById('proxy').value.trim(), proxy: document.getElementById('proxy').value.trim(),
browser_id: document.getElementById('browserId').value.trim(),
notes: document.getElementById('notes').value.trim() notes: document.getElementById('notes').value.trim()
}; };
@@ -695,6 +701,7 @@ function editAccount(id) {
document.getElementById('totpSecret').value = acc.totp_secret || ''; document.getElementById('totpSecret').value = acc.totp_secret || '';
document.getElementById('email').value = acc.email || ''; document.getElementById('email').value = acc.email || '';
document.getElementById('proxy').value = acc.proxy || ''; document.getElementById('proxy').value = acc.proxy || '';
document.getElementById('browserId').value = acc.browser_id || '';
document.getElementById('tags').value = acc.tags || ''; document.getElementById('tags').value = acc.tags || '';
document.getElementById('notes').value = acc.notes || ''; document.getElementById('notes').value = acc.notes || '';
@@ -744,6 +751,22 @@ async function copyToClipboard(text) {
} }
} }
// 打开 Browser 浏览器环境
async function openBrowser(idOrName) {
try {
const response = await fetch(`http://localhost:12138/api/open/${encodeURIComponent(idOrName)}`);
if (response.ok) {
showToast('正在启动 Browser 环境...', 'success');
} else {
const error = await response.text();
showToast(`启动失败: ${error}`, 'error');
}
} catch (e) {
console.error('Open browser failed:', e);
showToast('无法连接 Browser API请确保 Browser 已启动并开启 API 服务', 'error');
}
}
// 生成随机名称 (不含符号的长用户名) // 生成随机名称 (不含符号的长用户名)
function generateRandomName() { function generateRandomName() {
const firstName = firstNames[Math.floor(Math.random() * firstNames.length)]; const firstName = firstNames[Math.floor(Math.random() * firstNames.length)];

View File

@@ -600,6 +600,11 @@ body {
color: var(--danger); color: var(--danger);
} }
.card-action-btn.browser:hover {
background: rgba(59, 130, 246, 0.15);
color: var(--accent);
}
/* 信息行 - 点击复制 */ /* 信息行 - 点击复制 */
.info-row { .info-row {
display: flex; display: flex;