|
|
|
|
@@ -8,16 +8,32 @@ router.get('/', (req, res) => {
|
|
|
|
|
res.render('seed', { title: 'Seed & Reset' });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function dateStr(daysAgo, hours, minutes) {
|
|
|
|
|
const d = new Date();
|
|
|
|
|
d.setDate(d.getDate() - daysAgo);
|
|
|
|
|
d.setHours(hours || 0, minutes || 0, 0, 0);
|
|
|
|
|
return d.toISOString().replace('T', ' ').substring(0, 19);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function randInt(min, max) {
|
|
|
|
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function pick(arr) {
|
|
|
|
|
return arr[Math.floor(Math.random() * arr.length)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Require re-auth (re-enter admin token) before destructive actions
|
|
|
|
|
router.post('/seed-demo', requireReAuth, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const delTables = ['purchases', 'transactions', 'crypto_wallets', 'audit_log',
|
|
|
|
|
'user_states', 'products', 'subcategories', 'categories', 'users', 'locations'];
|
|
|
|
|
'user_states', 'products', 'subcategories', 'categories', 'users', 'locations', 'commission_payments'];
|
|
|
|
|
for (const t of delTables) {
|
|
|
|
|
await db.runAsync(`DELETE FROM ${t}`);
|
|
|
|
|
}
|
|
|
|
|
await db.runAsync("DELETE FROM sqlite_sequence WHERE name != '_meta'");
|
|
|
|
|
|
|
|
|
|
// ── Locations ──────────────────────────────────────────────
|
|
|
|
|
await db.runAsync(`INSERT INTO locations (country, city, district) VALUES
|
|
|
|
|
('Russia', 'Moscow', 'Center'),
|
|
|
|
|
('Russia', 'Saint Petersburg', 'North'),
|
|
|
|
|
@@ -27,6 +43,7 @@ router.post('/seed-demo', requireReAuth, async (req, res) => {
|
|
|
|
|
const locSPb = locs.find(l => l.city === 'Saint Petersburg').id;
|
|
|
|
|
const locBerlin = locs.find(l => l.city === 'Berlin').id;
|
|
|
|
|
|
|
|
|
|
// ── Categories ─────────────────────────────────────────────
|
|
|
|
|
await db.runAsync('INSERT INTO categories (location_id, name) VALUES (?, ?), (?, ?), (?, ?), (?, ?), (?, ?)',
|
|
|
|
|
[locMoscow, 'Digital', locMoscow, 'Physical', locSPb, 'Premium', locSPb, 'VIP', locBerlin, 'Standard']);
|
|
|
|
|
const cats = await db.allAsync('SELECT id, name FROM categories');
|
|
|
|
|
@@ -36,6 +53,7 @@ router.post('/seed-demo', requireReAuth, async (req, res) => {
|
|
|
|
|
const catVIP = cats.find(c => c.name === 'VIP').id;
|
|
|
|
|
const catStandard = cats.find(c => c.name === 'Standard').id;
|
|
|
|
|
|
|
|
|
|
// ── Subcategories ──────────────────────────────────────────
|
|
|
|
|
await db.runAsync(`INSERT INTO subcategories (category_id, name) VALUES
|
|
|
|
|
(?, 'VPN'), (?, 'Accounts'), (?, 'Software'),
|
|
|
|
|
(?, 'Hardware'), (?, 'Accessories'),
|
|
|
|
|
@@ -59,18 +77,32 @@ router.post('/seed-demo', requireReAuth, async (req, res) => {
|
|
|
|
|
const subStarter = subs.find(s => s.name === 'Starter' && s.category_id === catStandard).id;
|
|
|
|
|
const subSoftware = subs.find(s => s.name === 'Software' && s.category_id === catDigital).id;
|
|
|
|
|
|
|
|
|
|
await db.runAsync(`INSERT INTO users (telegram_id, username, country, city, district, status, total_balance, bonus_balance) VALUES
|
|
|
|
|
('1001', 'alice', 'Russia', 'Moscow', 'Center', 0, 150.00, 25.00),
|
|
|
|
|
('1002', 'bob', 'Russia', 'Moscow', 'Center', 0, 85.50, 10.00),
|
|
|
|
|
('1003', 'charlie', 'Russia', 'Saint Petersburg', 'North', 0, 320.75, 50.00),
|
|
|
|
|
('1004', 'diana', 'Germany', 'Berlin', 'Mitte', 0, 45.00, 5.00),
|
|
|
|
|
('1005', 'evan', 'Germany', 'Berlin', 'Mitte', 0, 0.00, 0.00)`);
|
|
|
|
|
const users = await db.allAsync('SELECT id, username FROM users');
|
|
|
|
|
const uAlice = users.find(u => u.username === 'alice').id;
|
|
|
|
|
const uBob = users.find(u => u.username === 'bob').id;
|
|
|
|
|
const uCharlie = users.find(u => u.username === 'charlie').id;
|
|
|
|
|
const uDiana = users.find(u => u.username === 'diana').id;
|
|
|
|
|
// ── Users (created_at spread across last 30 days) ──────────
|
|
|
|
|
const userDefs = [
|
|
|
|
|
{ tg: '1001', name: 'alice', country: 'Russia', city: 'Moscow', district: 'Center', bal: 150.00, bonus: 25.00, daysAgo: 28 },
|
|
|
|
|
{ tg: '1002', name: 'bob', country: 'Russia', city: 'Moscow', district: 'Center', bal: 85.50, bonus: 10.00, daysAgo: 25 },
|
|
|
|
|
{ tg: '1003', name: 'charlie', country: 'Russia', city: 'Saint Petersburg', district: 'North', bal: 320.75, bonus: 50.00, daysAgo: 22 },
|
|
|
|
|
{ tg: '1004', name: 'diana', country: 'Germany', city: 'Berlin', district: 'Mitte', bal: 45.00, bonus: 5.00, daysAgo: 18 },
|
|
|
|
|
{ tg: '1005', name: 'evan', country: 'Germany', city: 'Berlin', district: 'Mitte', bal: 0.00, bonus: 0.00, daysAgo: 15 },
|
|
|
|
|
{ tg: '1006', name: 'frank', country: 'Russia', city: 'Moscow', district: 'Center', bal: 210.00, bonus: 30.00, daysAgo: 12 },
|
|
|
|
|
{ tg: '1007', name: 'grace', country: 'Russia', city: 'Saint Petersburg', district: 'North', bal: 75.25, bonus: 15.00, daysAgo: 8 },
|
|
|
|
|
{ tg: '1008', name: 'henry', country: 'Germany', city: 'Berlin', district: 'Mitte', bal: 500.00, bonus: 100.00, daysAgo: 5 },
|
|
|
|
|
{ tg: '1009', name: 'iris', country: 'Russia', city: 'Moscow', district: 'Center', bal: 0.00, bonus: 0.00, daysAgo: 3 },
|
|
|
|
|
{ tg: '1010', name: 'jack', country: 'Germany', city: 'Berlin', district: 'Mitte', bal: 33.00, bonus: 5.00, daysAgo: 1 },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
for (const u of userDefs) {
|
|
|
|
|
await db.runAsync(
|
|
|
|
|
`INSERT INTO users (telegram_id, username, country, city, district, status, total_balance, bonus_balance, created_at)
|
|
|
|
|
VALUES (?, ?, ?, ?, ?, 0, ?, ?, ?)`,
|
|
|
|
|
[u.tg, u.name, u.country, u.city, u.district, u.bal, u.bonus, dateStr(u.daysAgo, randInt(8, 20), randInt(0, 59))]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
const users = await db.allAsync('SELECT id, username FROM users');
|
|
|
|
|
const uid = {};
|
|
|
|
|
for (const u of users) uid[u.username] = u.id;
|
|
|
|
|
|
|
|
|
|
// ── Products ───────────────────────────────────────────────
|
|
|
|
|
await db.runAsync(`INSERT INTO products (location_id, category_id, subcategory_id, name, description, price, quantity_in_stock, photo_url) VALUES
|
|
|
|
|
(?, ?, ?, 'VPN Subscription 30d', 'Premium VPN access for 30 days', 9.99, 100, ''),
|
|
|
|
|
(?, ?, ?, 'VPN Subscription 90d', 'Premium VPN access for 90 days', 24.99, 50, ''),
|
|
|
|
|
@@ -87,34 +119,85 @@ router.post('/seed-demo', requireReAuth, async (req, res) => {
|
|
|
|
|
locBerlin, catStandard, subBasic, locMoscow, catDigital, subSoftware,
|
|
|
|
|
locSPb, catVIP, subExpress, locBerlin, catStandard, subStarter]);
|
|
|
|
|
|
|
|
|
|
const prods = await db.allAsync('SELECT id, name FROM products');
|
|
|
|
|
const pVPN30 = prods.find(p => p.name.includes('30d')).id;
|
|
|
|
|
const pUSB = prods.find(p => p.name.includes('USB')).id;
|
|
|
|
|
const pPrem1y = prods.find(p => p.name.includes('1 Year')).id;
|
|
|
|
|
const pStd = prods.find(p => p.name.includes('Standard')).id;
|
|
|
|
|
const pStarter = prods.find(p => p.name.includes('Starter')).id;
|
|
|
|
|
const prods = await db.allAsync('SELECT id, name, price FROM products');
|
|
|
|
|
|
|
|
|
|
await db.runAsync(`INSERT INTO crypto_wallets (user_id, wallet_type, address, derivation_path, mnemonic, balance) VALUES
|
|
|
|
|
(?, 'BTC', 'bc1qexample1addr', 'm/44h/0h/0h/0/0', 'encrypted:1', 0.00543),
|
|
|
|
|
(?, 'ETH', '0xExampleEth1addr', 'm/44h/60h/0h/0/0', 'encrypted:2', 0.12500),
|
|
|
|
|
(?, 'BTC', 'bc1qexample2addr', 'm/44h/0h/0h/0/1', 'encrypted:3', 0.00210),
|
|
|
|
|
(?, 'LTC', 'ltc1qexample3addr', 'm/44h/2h/0h/0/0', 'encrypted:4', 1.50000),
|
|
|
|
|
(?, 'ETH', '0xExampleEth4addr', 'm/44h/60h/0h/0/1', 'encrypted:5', 0.50000)`,
|
|
|
|
|
[uAlice, uAlice, uBob, uCharlie, uDiana]);
|
|
|
|
|
// ── Purchases (25-30, spread across last 30 days) ──────────
|
|
|
|
|
const walletTypes = ['BTC', 'ETH', 'LTC', 'USDT'];
|
|
|
|
|
const userList = Object.values(uid);
|
|
|
|
|
const count = randInt(25, 30);
|
|
|
|
|
|
|
|
|
|
await db.runAsync(`INSERT INTO purchases (user_id, product_id, wallet_type, tx_hash, quantity, total_price, status) VALUES
|
|
|
|
|
(?, ?, 'BTC', 'tx_a1b2c3d4', 1, 9.99, 'completed'),
|
|
|
|
|
(?, ?, 'ETH', 'tx_b2c3d4e5', 2, 59.98, 'completed'),
|
|
|
|
|
(?, ?, 'LTC', 'tx_c3d4e5f6', 1, 99.99, 'pending'),
|
|
|
|
|
(?, ?, 'BTC', 'tx_d4e5f6a7', 1, 14.99, 'completed'),
|
|
|
|
|
(?, ?, 'ETH', 'tx_e5f6a7b8', 3, 14.97, 'cancelled')`,
|
|
|
|
|
[uAlice, pVPN30, uBob, pUSB, uCharlie, pPrem1y, uAlice, pStd, uDiana, pStarter]);
|
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
|
|
|
const daysAgo = randInt(0, 29);
|
|
|
|
|
const product = pick(prods);
|
|
|
|
|
const user = pick(userList);
|
|
|
|
|
const qty = randInt(1, 5);
|
|
|
|
|
const totalPrice = (product.price * qty).toFixed(2);
|
|
|
|
|
const roll = Math.random();
|
|
|
|
|
const status = roll < 0.70 ? 'completed' : (roll < 0.90 ? 'pending' : 'cancelled');
|
|
|
|
|
const txHash = 'tx_' + Math.random().toString(36).substring(2, 10);
|
|
|
|
|
const wallet = pick(walletTypes);
|
|
|
|
|
|
|
|
|
|
await db.runAsync(
|
|
|
|
|
`INSERT INTO purchases (user_id, product_id, wallet_type, tx_hash, quantity, total_price, status, purchase_date)
|
|
|
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
|
|
|
[user, product.id, wallet, txHash, qty, totalPrice, status, dateStr(daysAgo, randInt(0, 23), randInt(0, 59))]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Crypto Wallets (10 total, diverse coins) ───────────────
|
|
|
|
|
const walletDefs = [
|
|
|
|
|
{ user: 'alice', type: 'BTC', addr: 'bc1qexample1addr', path: 'm/44h/0h/0h/0/0', mnem: 'encrypted:1', bal: 0.00543 },
|
|
|
|
|
{ user: 'alice', type: 'ETH', addr: '0xExampleEth1addr', path: 'm/44h/60h/0h/0/0', mnem: 'encrypted:2', bal: 0.12500 },
|
|
|
|
|
{ user: 'bob', type: 'BTC', addr: 'bc1qexample2addr', path: 'm/44h/0h/0h/0/1', mnem: 'encrypted:3', bal: 0.00210 },
|
|
|
|
|
{ user: 'charlie', type: 'LTC', addr: 'ltc1qexample3addr', path: 'm/44h/2h/0h/0/0', mnem: 'encrypted:4', bal: 1.50000 },
|
|
|
|
|
{ user: 'diana', type: 'ETH', addr: '0xExampleEth4addr', path: 'm/44h/60h/0h/0/1', mnem: 'encrypted:5', bal: 0.50000 },
|
|
|
|
|
{ user: 'frank', type: 'XRP', addr: 'rExampleXRP1addr', path: 'm/44h/144h/0h/0/0', mnem: 'encrypted:6', bal: 250.00000 },
|
|
|
|
|
{ user: 'grace', type: 'BCH', addr: 'bitcoincash:qexample1', path: 'm/44h/145h/0h/0/0', mnem: 'encrypted:7', bal: 0.75000 },
|
|
|
|
|
{ user: 'henry', type: 'DOGE', addr: 'DExampleDoge1addr', path: 'm/44h/3h/0h/0/0', mnem: 'encrypted:8', bal: 5000.00000 },
|
|
|
|
|
{ user: 'iris', type: 'BTC', addr: 'bc1qexample9addr', path: 'm/44h/0h/0h/0/2', mnem: 'encrypted:9', bal: 0.00000 },
|
|
|
|
|
{ user: 'jack', type: 'USDT', addr: '0xExampleUSDTaddr', path: 'm/44h/60h/0h/0/2', mnem: 'encrypted:10', bal: 0.00000 },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
for (const w of walletDefs) {
|
|
|
|
|
await db.runAsync(
|
|
|
|
|
`INSERT INTO crypto_wallets (user_id, wallet_type, address, derivation_path, mnemonic, balance)
|
|
|
|
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
|
|
|
[uid[w.user], w.type, w.addr, w.path, w.mnem, w.bal]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Transactions ───────────────────────────────────────────
|
|
|
|
|
await db.runAsync(`INSERT INTO transactions (user_id, wallet_type, tx_hash, amount) VALUES
|
|
|
|
|
(?, 'BTC', 'tx_f6a7b8c9', 0.01000),
|
|
|
|
|
(?, 'ETH', 'tx_a7b8c9d0', 0.50000),
|
|
|
|
|
(?, 'LTC', 'tx_b8c9d0e1', 2.00000)`,
|
|
|
|
|
[uAlice, uBob, uCharlie]);
|
|
|
|
|
(?, 'LTC', 'tx_b8c9d0e1', 2.00000),
|
|
|
|
|
(?, 'XRP', 'tx_xrp1a2b3', 100.00000),
|
|
|
|
|
(?, 'DOGE', 'tx_doge1c2d', 1000.00000)`,
|
|
|
|
|
[uid.alice, uid.bob, uid.charlie, uid.frank, uid.henry]);
|
|
|
|
|
|
|
|
|
|
// ── Audit Log (10+ entries, spread across last 7 days) ─────
|
|
|
|
|
await db.runAsync(`INSERT INTO audit_log (action, admin_id, details, created_at) VALUES
|
|
|
|
|
('login', 'admin', 'Admin logged in', ?),
|
|
|
|
|
('product_created', 'admin', 'Created VPN Subscription', ?),
|
|
|
|
|
('user_blocked', 'admin', 'Blocked user bob', ?),
|
|
|
|
|
('settings_changed', 'system', 'Commission rate updated to 5%', ?),
|
|
|
|
|
('purchase_approved', 'auto', 'Auto-approved pending purchase #3', ?),
|
|
|
|
|
('wallet_added', 'admin', 'Added BTC wallet for alice', ?),
|
|
|
|
|
('category_created', 'admin', 'Created Premium category', ?),
|
|
|
|
|
('product_updated', 'admin', 'Updated USB Drive stock', ?),
|
|
|
|
|
('commission_paid', 'admin', 'Paid commission $50', ?),
|
|
|
|
|
('user_registered', 'auto', 'New user evan registered', ?),
|
|
|
|
|
('login', 'admin', 'Admin logged in from new IP', ?),
|
|
|
|
|
('settings_changed', 'system', 'Maintenance mode disabled', ?)`,
|
|
|
|
|
[dateStr(1, 9, 15), dateStr(2, 11, 30), dateStr(3, 14, 0), dateStr(4, 8, 45),
|
|
|
|
|
dateStr(5, 16, 20), dateStr(6, 10, 0), dateStr(7, 13, 10), dateStr(1, 17, 55),
|
|
|
|
|
dateStr(10, 12, 0), dateStr(8, 9, 30), dateStr(0, 7, 0), dateStr(2, 22, 15)]);
|
|
|
|
|
|
|
|
|
|
// ── Commission Payments ─────────────────────────────────────
|
|
|
|
|
await db.runAsync(`INSERT INTO commission_payments (total_balance_usd, commission_rate, commission_amount_usd, paid_amount_usd, wallet_count, note, created_at) VALUES
|
|
|
|
|
(1250.00, 5.0, 62.50, 50.00, 8, 'Monthly commission payment', ?),
|
|
|
|
|
(980.00, 5.0, 49.00, 25.50, 6, 'Partial commission', ?)`,
|
|
|
|
|
[dateStr(10, 12, 0), dateStr(20, 15, 30)]);
|
|
|
|
|
|
|
|
|
|
res.redirect('/?seeded=1');
|
|
|
|
|
} catch (err) {
|
|
|
|
|
@@ -126,7 +209,7 @@ router.post('/seed-demo', requireReAuth, async (req, res) => {
|
|
|
|
|
router.post('/clear-all', requireReAuth, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const tables = ['purchases', 'transactions', 'crypto_wallets', 'audit_log',
|
|
|
|
|
'user_states', 'products', 'subcategories', 'categories', 'users', 'locations'];
|
|
|
|
|
'user_states', 'products', 'subcategories', 'categories', 'users', 'locations', 'commission_payments'];
|
|
|
|
|
for (const t of tables) {
|
|
|
|
|
await db.runAsync(`DELETE FROM ${t}`);
|
|
|
|
|
}
|
|
|
|
|
|