import mongoose from "mongoose"; import User from "./models/User.js"; import Session from "./models/Session.js"; import Transaction from "./models/Transaction.js"; import dotenv from "dotenv"; dotenv.config(); const MONGODB_URI = process.env.MONGODB_URI || "mongodb://localhost:27017/turbotrades"; // Transaction types and their properties const transactionTypes = [ { type: "deposit", direction: "positive", descriptions: [ "PayPal deposit", "Stripe payment", "Crypto deposit", "Balance top-up", ], }, { type: "withdrawal", direction: "negative", descriptions: [ "PayPal withdrawal", "Bank transfer", "Crypto withdrawal", "Cash out", ], }, { type: "purchase", direction: "negative", descriptions: [ { name: "AWP | Dragon Lore", image: "https://community.cloudflare.steamstatic.com/economy/image/-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpot621FAR17PLfYQJD_9W7m5a0mvLwOq7cqWdQ-sJ0teXI8oThxlawrRI9fSmtc9TCJgI2ZlyDq1jvxuq5g8W6v5SYwXU37yEl7S7em0TmiRhEZ-BxxavJZlnsNrA/360fx360f", }, { name: "Karambit | Fade", image: "https://community.cloudflare.steamstatic.com/economy/image/-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpovbSsLQJf2PLacDBA5ciJlY20k_jkI7fUhFRc4cJ5ntbN9J7yjRrm-UBrNzykI9CcdwRtaV3R-lS8xOu-hpK1u8zPzCRmuiEj-z-DyIHVYeJG/360fx360f", }, { name: "M4A4 | Howl", image: "https://community.cloudflare.steamstatic.com/economy/image/-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpou-6kejhz2v_Nfz5H_uO1gb-Gw_alIITSg3tu5Mx2gv2PqNnz3le1-Etr9zqrOoWVcFU3M16FqVG5kO_qhcW4v8_AynZ9-n51s35gZPo/360fx360f", }, { name: "Glock-18 | Fade", image: "https://community.cloudflare.steamstatic.com/economy/image/-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgposbaqKAxf0Ob3djFN79eJkIGZqPv1IbfQmGpD6e11jOzA4YfwjAy3_0ttamv6INWVe1RvZ1vY-li9lbzqhp7vusvXiSw0I5LNEws/360fx360f", }, { name: "AK-47 | Fire Serpent", image: "https://community.cloudflare.steamstatic.com/economy/image/-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpot7HxfDhjxszJemkV08-mkYGHqPv9NLPF2GpVvZIpi-yWo96l2AK3-kZvYjv6cYOWcQU-YlrQ-1C9xb--gZLutczMmHtivj5iuyiMF8f2Bg/360fx360f", }, ], }, { type: "sale", direction: "positive", descriptions: [ { name: "Sold AWP | Asiimov", image: "https://community.cloudflare.steamstatic.com/economy/image/-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpot621FAR17PLfYQJD_9W7m5a0mvLwOq7c2GpTu8Ah2ezDpIqh3wO1rhFuNW2gIoPDcQU_YlyE-gW9k-_ugJO86czXiSw0jMXQfH8/360fx360f", }, { name: "Sold Butterfly Knife", image: "https://community.cloudflare.steamstatic.com/economy/image/-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpovbSsLQJf0ebcZThQ6tCvq4GGqPD1I6vdk1Rd4cJ5nqeQpYmtjVHm_RJlNTiiLYGddABvNVqB-QXow-q5hZK46svAziFruSR3sHrVlgv330-LpY0XQg/360fx360f", }, { name: "Sold StatTrakβ„’ AK-47", image: "https://community.cloudflare.steamstatic.com/economy/image/-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpot7HxfDhjxszJemkV09-5lpKKqPrxN7LEmyVQ7MEpiLuSrYqnjQCx_0NvZGHxdoKWJ1RsYF_V_we-xui915bpv8zLznBg7z5iuyjH3ErYgA/360fx360f", }, { name: "Sold M9 Bayonet", image: "https://community.cloudflare.steamstatic.com/economy/image/-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpovbSsLQJf3qr3czxb49KzgL-DjsjwN6vdk1Rd4cJ5nqfE842s2AewqBJpMTrzLIWWcFBsYgrT_FK6ku_uh5G96JXPzCQ37iF2sH6Plgv330_SkBhtxg/360fx360f", }, ], }, { type: "bonus", direction: "positive", descriptions: [ "Welcome bonus", "Referral bonus", "Loyalty reward", "Promotional credit", ], }, { type: "refund", direction: "positive", descriptions: [ "Purchase refund", "Transaction reversal", "Cancelled order refund", ], }, ]; const paymentMethods = ["stripe", "paypal", "crypto", "balance", "steam"]; const statuses = ["completed", "pending", "processing"]; // Generate random amount based on transaction type function generateAmount(type) { switch (type) { case "deposit": case "withdrawal": return parseFloat((Math.random() * 200 + 10).toFixed(2)); // $10-$210 case "purchase": case "sale": return parseFloat((Math.random() * 500 + 5).toFixed(2)); // $5-$505 case "bonus": return parseFloat((Math.random() * 50 + 5).toFixed(2)); // $5-$55 case "refund": return parseFloat((Math.random() * 150 + 10).toFixed(2)); // $10-$160 default: return parseFloat((Math.random() * 100 + 10).toFixed(2)); } } // Generate random date within last 30 days function generateRandomDate() { const now = new Date(); const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); const randomTime = thirtyDaysAgo.getTime() + Math.random() * (now.getTime() - thirtyDaysAgo.getTime()); return new Date(randomTime); } // Select random item from array function randomItem(array) { return array[Math.floor(Math.random() * array.length)]; } async function seedTransactions() { try { console.log("πŸ”Œ Connecting to MongoDB..."); await mongoose.connect(MONGODB_URI); console.log("βœ… Connected to MongoDB"); // Find a user (preferably with sessions) console.log("πŸ‘€ Finding user..."); // Check for command line argument for Steam ID const targetSteamId = process.argv[2]; let user; if (targetSteamId) { console.log(` Looking for Steam ID: ${targetSteamId}`); user = await User.findOne({ steamId: targetSteamId }); if (!user) { console.error(`❌ User with Steam ID ${targetSteamId} not found!`); console.error("πŸ’‘ Make sure you're logged in with this Steam account."); process.exit(1); } } else { // Default: find most recent user user = await User.findOne().sort({ createdAt: -1 }); if (!user) { console.error( "❌ No users found. Please create a user first by logging in via Steam." ); process.exit(1); } } console.log(`βœ… Found user: ${user.username} (${user.steamId})`); // Get existing sessions for this user console.log("πŸ” Finding sessions..."); let sessions = await Session.find({ userId: user._id, isActive: true }); if (sessions.length === 0) { console.error("❌ No active sessions found!"); console.error( "πŸ’‘ Please log in via Steam first to create a real session." ); console.error(" Then run this script again."); process.exit(1); } console.log(`βœ… Found ${sessions.length} active sessions`); sessions.forEach((session) => { console.log( ` - ${session.browser || "Unknown"} on ${ session.os || "Unknown" } (...${session._id.toString().slice(-6)})` ); }); // Generate 20-30 fake transactions const transactionCount = Math.floor(Math.random() * 11) + 20; console.log(`\nπŸ’° Generating ${transactionCount} fake transactions...`); let currentBalance = user.balance || 1000; // Start with current balance or $1000 const createdTransactions = []; for (let i = 0; i < transactionCount; i++) { // Pick random transaction type const txTypeObj = randomItem(transactionTypes); const amount = generateAmount(txTypeObj.type); const descriptionItem = randomItem(txTypeObj.descriptions); const description = typeof descriptionItem === "string" ? descriptionItem : descriptionItem.name; const itemImage = typeof descriptionItem === "object" ? descriptionItem.image : null; // Calculate balance const balanceBefore = currentBalance; let balanceAfter; if (txTypeObj.direction === "positive") { balanceAfter = balanceBefore + amount; } else { balanceAfter = balanceBefore - amount; } currentBalance = balanceAfter; // Pick random session const session = randomItem(sessions); // Pick random status (mostly completed) const status = Math.random() < 0.85 ? "completed" : randomItem(statuses); // Create transaction data const transactionData = { userId: user._id, steamId: user.steamId, type: txTypeObj.type, status: status, amount: amount, currency: "USD", balanceBefore: balanceBefore, balanceAfter: balanceAfter, sessionId: session._id, description: description, fee: txTypeObj.type === "withdrawal" ? parseFloat((amount * 0.02).toFixed(2)) : 0, feePercentage: txTypeObj.type === "withdrawal" ? 2 : 0, }; // Add payment method for deposits/withdrawals if (txTypeObj.type === "deposit" || txTypeObj.type === "withdrawal") { transactionData.paymentMethod = randomItem(paymentMethods); } // Add item name and image for purchases/sales if (txTypeObj.type === "purchase" || txTypeObj.type === "sale") { transactionData.itemName = description; if (itemImage) { transactionData.itemImage = itemImage; } } // Set completed date if status is completed if (status === "completed") { transactionData.completedAt = generateRandomDate(); } const transaction = await Transaction.createTransaction(transactionData); // Update createdAt to be in the past (for realistic history) transaction.createdAt = generateRandomDate(); await transaction.save(); createdTransactions.push(transaction); // Log progress const sessionShort = session._id.toString().slice(-6).toUpperCase(); console.log( ` βœ… [${i + 1}/${transactionCount}] ${txTypeObj.type .toUpperCase() .padEnd(12)} $${amount .toFixed(2) .padStart(8)} - Session: ${sessionShort} - ${description}` ); } // Sort by date createdTransactions.sort((a, b) => b.createdAt - a.createdAt); console.log("\nπŸ“Š Transaction Summary:"); console.log(` Total created: ${createdTransactions.length}`); const typeCounts = {}; const sessionCounts = {}; createdTransactions.forEach((tx) => { typeCounts[tx.type] = (typeCounts[tx.type] || 0) + 1; const sessionShort = tx.sessionIdShort || "SYSTEM"; sessionCounts[sessionShort] = (sessionCounts[sessionShort] || 0) + 1; }); console.log("\n By Type:"); Object.entries(typeCounts).forEach(([type, count]) => { console.log(` ${type.padEnd(12)}: ${count}`); }); console.log("\n By Session:"); Object.entries(sessionCounts).forEach(([sessionShort, count]) => { console.log(` ${sessionShort}: ${count} transactions`); }); console.log("\nβœ… Seeding completed successfully!"); console.log( `\nπŸ’‘ You can now view these transactions at: http://localhost:5173/transactions` ); } catch (error) { console.error("❌ Error seeding transactions:", error); process.exit(1); } finally { await mongoose.disconnect(); console.log("\nπŸ”Œ Disconnected from MongoDB"); process.exit(0); } } // Run the seed seedTransactions();