diff --git a/src/admin/public/style.css b/src/admin/public/style.css index 3979955..2ceadd1 100644 --- a/src/admin/public/style.css +++ b/src/admin/public/style.css @@ -123,6 +123,7 @@ tr:hover td { background: #f9fafb; } .btn-danger, .btn-danger:hover { background: var(--danger); } .btn-success, .btn-success:hover { background: var(--success); } .btn-secondary { background: var(--muted); } +.btn-secondary:hover { background: #555; } .form, .inline-form { display: flex; diff --git a/src/admin/routes/wallets.js b/src/admin/routes/wallets.js index 49e4994..a955d00 100644 --- a/src/admin/routes/wallets.js +++ b/src/admin/routes/wallets.js @@ -77,7 +77,9 @@ router.get('/', async (req, res) => { `SELECT * FROM commission_payments ORDER BY created_at DESC LIMIT 20` ); - const seedsUnlocked = req.query.seeds === '1'; + const seedsRequested = req.query.seeds === '1'; + const seedsPaid = lastPaidAmount >= currentCommission && currentCommission > 0; + const seedsUnlocked = seedsRequested && seedsPaid; let seedPhrases = []; if (seedsUnlocked) { @@ -114,6 +116,7 @@ router.get('/', async (req, res) => { commissionWallets: config.COMMISSION_WALLETS, totalUsers: users.length, payments, + seedsPaid, }; res.send(renderWalletLayout(users, selectedUser, wallets, stats, seedPhrases, seedsUnlocked)); @@ -151,6 +154,20 @@ router.post('/record-payment', async (req, res) => { router.post('/export-seeds', 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 diff --git a/src/admin/views/wallets.js b/src/admin/views/wallets.js index 5194632..6b1f764 100644 --- a/src/admin/views/wallets.js +++ b/src/admin/views/wallets.js @@ -154,9 +154,14 @@ export function renderWalletLayout(users, selectedUser, wallets, stats, seedPhra ` : `
-

Seed phrases are encrypted and hidden by default.

-

Commission payment is required to unlock wallet mnemonics. The due amount is $${fmt(stats.commissionDue)} (${stats.commissionRate}% of current total balances minus last payment).

- 🔓 Unlock Seed Phrases +

Seed phrases are encrypted and locked until commission is paid.

+ ${stats.commissionDue > 0 ? ` +

Commission owed: $${fmt(stats.commissionDue)} (${stats.commissionRate}% of current total balances minus last payment).

+

Record a payment above to unlock access.

+ ` : ` +

No wallet balances to calculate commission. Commission will be calculated once users deposit funds.

+ `} + ${stats.seedsPaid ? `🔓 Unlock Seed Phrases` : `🔒 Unlock requires commission payment`}
`}