Spaces:
Sleeping
Sleeping
| const express = require('express'); | |
| const mongoose = require('mongoose'); | |
| const bcrypt = require('bcryptjs'); | |
| const jwt = require('jsonwebtoken'); | |
| const nodemailer = require('nodemailer'); | |
| const rateLimit = require('express-rate-limit'); | |
| const path = require('path'); | |
| const fs = require('fs'); | |
| const { Resend } = require('resend'); | |
| const crypto = require('crypto'); | |
| const syntaxError = require('syntax-error'); | |
| const tmpRouter = require('./tmp'); | |
| const app = express(); | |
| app.use(express.json()); | |
| app.set('trust proxy', true); | |
| const getRealIP = (req, res, next) => { | |
| let ip; | |
| if (req.headers['cf-connecting-ip']) { | |
| ip = req.headers['cf-connecting-ip']; | |
| } else if (req.headers['x-real-ip']) { | |
| ip = req.headers['x-real-ip']; | |
| } else if (req.headers['x-forwarded-for']) { | |
| ip = req.headers['x-forwarded-for'].split(',')[0].trim(); | |
| } else { | |
| ip = req.socket.remoteAddress || req.connection.remoteAddress || '0.0.0.0'; | |
| } | |
| if (ip.startsWith('::ffff:')) { | |
| ip = ip.substring(7); | |
| } | |
| req.realIP = ip; | |
| next(); | |
| }; | |
| app.use(getRealIP); | |
| app.use(express.static('public')); | |
| const JWT_SECRET = process.env.JWT; | |
| const MONGODB_URI = process.env.MONGODB; | |
| mongoose.connect(MONGODB_URI) | |
| .then(() => console.log('Connected to MongoDB')) | |
| .catch(err => console.error('MongoDB connection error:', err)); | |
| const activitySchema = new mongoose.Schema({ | |
| userId: { type: String, required: true }, | |
| username: String, | |
| type: { | |
| type: String, | |
| enum: ['api_key_generated', 'limit_updated', 'profile_updated', 'redeem_code_used', 'premium_activated', 'api_request', 'temp_banned', 'ban_removed', 'limit_reset', 'role_expired', 'role_assigned', 'api_key_updated'], | |
| required: true | |
| }, | |
| description: String, | |
| metadata: Object, | |
| timestamp: { type: Date, default: Date.now } | |
| }); | |
| const userSchema = new mongoose.Schema({ | |
| username: { type: String, required: true, unique: true }, | |
| email: { type: String, required: true, unique: true }, | |
| password: { type: String, required: true }, | |
| apikey: { type: String, required: true, unique: true }, | |
| profileUrl: { type: String, default: 'https://files.catbox.moe/8l6hhm' }, | |
| verified: { type: Boolean, default: true }, | |
| role: { type: String, enum: ['user', 'admin'], default: 'user' }, | |
| userRole: { type: String, enum: ['cheap', 'premium', 'vip', 'supreme'], default: null }, | |
| userRoleExpiresAt: { type: Date, default: null }, | |
| premium: { type: Boolean, default: false }, | |
| premiumExpiredAt: { type: Date, default: null }, | |
| limit: { type: Number, default: 30 }, | |
| requests: { type: Number, default: 0 }, | |
| requestsToday: { type: Number, default: 0 }, | |
| lastReset: { type: Date, default: Date.now }, | |
| banned: { type: Boolean, default: false }, | |
| tempBanned: { type: Boolean, default: false }, | |
| tempBanUntil: { type: Date, default: null }, | |
| tempBanReason: { type: String, default: null }, | |
| ipAddress: String, | |
| createdAt: { type: Date, default: Date.now } | |
| }); | |
| const pendingVerificationSchema = new mongoose.Schema({ | |
| email: { type: String, required: true, unique: true }, | |
| username: { type: String, required: true }, | |
| password: { type: String, required: true }, | |
| verificationCode: { type: String, required: true }, | |
| ipAddress: String, | |
| ispInfo: Object, | |
| expiresAt: { type: Date, default: () => new Date(Date.now() + 15 * 60 * 1000) }, | |
| createdAt: { type: Date, default: Date.now } | |
| }); | |
| pendingVerificationSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 }); | |
| const redeemCodeSchema = new mongoose.Schema({ | |
| code: { type: String, required: true, unique: true }, | |
| type: { type: String, enum: ['limit', 'premium', 'both'], required: true }, | |
| limitValue: { type: Number, default: 0 }, | |
| codeExpired: { type: Date, required: true }, | |
| premiumExpired: { type: Date, default: null }, | |
| used: { type: Boolean, default: false }, | |
| usedBy: String, | |
| createdBy: { type: String, required: true }, | |
| createdAt: { type: Date, default: Date.now } | |
| }); | |
| redeemCodeSchema.index({ codeExpired: 1 }, { expireAfterSeconds: 0 }); | |
| const requestLogSchema = new mongoose.Schema({ | |
| userId: String, | |
| username: String, | |
| apikey: String, | |
| endpoint: String, | |
| ipAddress: String, | |
| userAgent: String, | |
| timestamp: { type: Date, default: Date.now }, | |
| success: Boolean, | |
| responseTime: Number, | |
| limitDeducted: { type: Number, default: 1 } | |
| }); | |
| const banListSchema = new mongoose.Schema({ | |
| ipAddress: String, | |
| bannedUntil: Date, | |
| reason: String, | |
| createdAt: { type: Date, default: Date.now } | |
| }); | |
| banListSchema.index({ bannedUntil: 1 }, { expireAfterSeconds: 0 }); | |
| const roleSchema = new mongoose.Schema({ | |
| userId: { type: String, required: true }, | |
| roleName: { type: String, enum: ['cheap', 'premium', 'vip', 'supreme'], required: true }, | |
| customApiKey: { type: String, unique: true, sparse: true }, | |
| expiresAt: { type: Date, required: true }, | |
| createdAt: { type: Date, default: Date.now }, | |
| createdBy: String | |
| }); | |
| const UserRole = mongoose.model('UserRole', roleSchema); | |
| const Activity = mongoose.model('Activity', activitySchema); | |
| const User = mongoose.model('User', userSchema); | |
| const PendingVerification = mongoose.model('PendingVerification', pendingVerificationSchema); | |
| const RedeemCode = mongoose.model('RedeemCode', redeemCodeSchema); | |
| const RequestLog = mongoose.model('RequestLog', requestLogSchema); | |
| const BanList = mongoose.model('BanList', banListSchema); | |
| /*const transporter = nodemailer.createTransport({ | |
| host: 'smtp-relay.brevo.com', | |
| port: 587, | |
| secure: false, | |
| auth: { | |
| user: '998a64001@smtp-brevo.com', // Login dari Brevo (BUKAN email kamu!) | |
| pass: 'xsmtpsib-c8f574e4088a014fc3859e8dc887afdf65a95bd21e1e3edd7ae32825ac6a44d5-A8ms8qx31RuVgZnc' // SMTP Key | |
| }, | |
| tls: { | |
| rejectUnauthorized: false | |
| } | |
| });*/ | |
| const generateApiKey = () => { | |
| return 'DHX-' + crypto.randomBytes(3).toString('hex').toUpperCase(); | |
| }; | |
| const generateVerificationCode = () => { | |
| return Math.random().toString(36).substring(2, 8).toUpperCase(); | |
| }; | |
| const logActivity = async (userId, username, type, description, metadata = {}) => { | |
| try { | |
| await Activity.create({ | |
| userId, | |
| username, | |
| type, | |
| description, | |
| metadata, | |
| timestamp: new Date() | |
| }); | |
| } catch (error) { | |
| console.error('Activity logging error:', error); | |
| } | |
| }; | |
| const getTimeAgo = (date) => { | |
| const now = new Date(); | |
| const diff = now - new Date(date); | |
| const minutes = Math.floor(diff / 60000); | |
| const hours = Math.floor(diff / 3600000); | |
| const days = Math.floor(diff / 86400000); | |
| if (minutes < 1) return 'Just now'; | |
| if (minutes < 60) return `${minutes} minutes ago`; | |
| if (hours < 24) return `${hours} hours ago`; | |
| return `${days} days ago`; | |
| }; | |
| const getNextMidnight = () => { | |
| const now = new Date(); | |
| const midnight = new Date(now); | |
| midnight.setHours(24, 0, 0, 0); | |
| return midnight; | |
| }; | |
| const getTimeUntilMidnight = () => { | |
| const now = new Date(); | |
| const midnight = getNextMidnight(); | |
| const diff = midnight - now; | |
| const hours = Math.floor(diff / (1000 * 60 * 60)); | |
| const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); | |
| const seconds = Math.floor((diff % (1000 * 60)) / 1000); | |
| return { hours, minutes, seconds, totalMs: diff }; | |
| }; | |
| const apiRateLimit = rateLimit({ | |
| windowMs: 10 * 1000, | |
| max: 3, | |
| message: { success: false, error: 'Too many requests, please try again later' }, | |
| standardHeaders: true, | |
| legacyHeaders: false, | |
| keyGenerator: (req) => req.realIP, | |
| handler: async (req, res) => { | |
| const ip = req.realIP; | |
| const bannedUntil = new Date(Date.now() + 10 * 60 * 1000); | |
| try { | |
| await BanList.create({ ipAddress: ip, bannedUntil, reason: 'Rate limit exceeded' }); | |
| } catch (error) { | |
| console.error('Ban list creation error:', error); | |
| } | |
| const randomStatus = Math.random() > 0.5 ? 429 : 403; | |
| res.status(randomStatus).json({ | |
| success: false, | |
| error: randomStatus === 429 ? 'Too many requests' : 'Forbidden - IP temporarily banned' | |
| }); | |
| } | |
| }); | |
| const authRateLimit = rateLimit({ | |
| windowMs: 5 * 60 * 1000, | |
| max: 10, | |
| message: { success: false, error: '429 Forbidden Request flood detected' }, | |
| standardHeaders: true, | |
| legacyHeaders: false, | |
| keyGenerator: (req) => req.realIP, | |
| handler: async (req, res) => { | |
| const ip = req.realIP; | |
| const bannedUntil = new Date(Date.now() + 30 * 60 * 1000); | |
| try { | |
| const existingBan = await BanList.findOne({ ipAddress: ip }); | |
| if (!existingBan) { | |
| await BanList.create({ | |
| ipAddress: ip, | |
| bannedUntil, | |
| reason: 'Authentication flood detected' | |
| }); | |
| console.log(`IP ${ip} banned for 30 minutes due to auth flood`); | |
| } | |
| } catch (error) { | |
| console.error('Auth ban list creation error:', error); | |
| } | |
| res.status(429).json({ | |
| success: false, | |
| error: '429 Forbidden Request flood detected', | |
| bannedUntil: bannedUntil.toISOString(), | |
| message: 'Your IP has been temporarily blocked for 30 minutes' | |
| }); | |
| } | |
| }); | |
| const checkBanned = async (req, res, next) => { | |
| const ip = req.realIP; | |
| try { | |
| const banned = await BanList.findOne({ | |
| ipAddress: ip, | |
| bannedUntil: { $gt: new Date() } | |
| }); | |
| if (banned) { | |
| return res.status(403).json({ | |
| success: false, | |
| error: 'IP is temporarily banned', | |
| bannedUntil: banned.bannedUntil, | |
| reason: banned.reason | |
| }); | |
| } | |
| } catch (error) { | |
| console.error('Check banned error:', error); | |
| } | |
| next(); | |
| }; | |
| const checkTempBan = async (user) => { | |
| if (user.tempBanned && user.tempBanUntil) { | |
| if (new Date() > user.tempBanUntil) { | |
| user.tempBanned = false; | |
| user.tempBanUntil = null; | |
| user.tempBanReason = null; | |
| await user.save(); | |
| await logActivity(user._id, user.username, 'ban_removed', 'Temporary ban expired automatically'); | |
| return false; | |
| } | |
| return true; | |
| } | |
| return false; | |
| }; | |
| const checkPremiumExpiry = async (user) => { | |
| if (user.premium && user.premiumExpiredAt) { | |
| if (new Date() > user.premiumExpiredAt) { | |
| user.premium = false; | |
| user.premiumExpiredAt = null; | |
| await user.save(); | |
| return true; | |
| } | |
| } | |
| return false; | |
| }; | |
| const checkRoleExpiry = async (user) => { | |
| if (user.userRoleExpiresAt && new Date() > user.userRoleExpiresAt) { | |
| const oldRole = user.userRole; | |
| const oldLimit = user.limit; | |
| user.userRole = null; | |
| user.userRoleExpiresAt = null; | |
| user.premium = false; | |
| user.premiumExpiredAt = null; | |
| if (user.limit <= 30) { | |
| user.limit = 30; | |
| } | |
| await user.save(); | |
| await UserRole.deleteOne({ userId: user._id }); | |
| await logActivity(user._id, user.username, 'role_expired', `Role ${oldRole} expired, limit ${oldLimit} → ${user.limit}`); | |
| return true; | |
| } | |
| return false; | |
| }; | |
| const resetUserLimitIfNeeded = async (user) => { | |
| const now = new Date(); | |
| const lastReset = new Date(user.lastReset); | |
| now.setHours(0, 0, 0, 0); | |
| lastReset.setHours(0, 0, 0, 0); | |
| const needsReset = now.getTime() !== lastReset.getTime(); | |
| if (needsReset) { | |
| if (user.userRole) { | |
| const roleConfig = { | |
| cheap: 500, | |
| premium: 1500, | |
| vip: 2500, | |
| supreme: 3000 | |
| }; | |
| const oldLimit = user.limit; | |
| user.limit = roleConfig[user.userRole] || 30; | |
| user.requestsToday = 0; | |
| user.lastReset = new Date(); | |
| await user.save(); | |
| await logActivity(user._id, user.username, 'limit_reset', `Daily limit reset from ${oldLimit} to ${user.limit} (Role: ${user.userRole})`); | |
| return true; | |
| } else { | |
| if (user.limit <= 30) { | |
| user.limit = 30; | |
| user.requestsToday = 0; | |
| user.lastReset = new Date(); | |
| await user.save(); | |
| await logActivity(user._id, user.username, 'limit_reset', 'Daily limit reset to 30 (No role)'); | |
| return true; | |
| } else { | |
| user.requestsToday = 0; | |
| user.lastReset = new Date(); | |
| await user.save(); | |
| await logActivity(user._id, user.username, 'limit_reset', `Requests reset, limit remains ${user.limit} (waiting to reach 30)`); | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| }; | |
| const authenticate = async (req, res, next) => { | |
| const token = req.headers.authorization?.replace('Bearer ', ''); | |
| if (!token) { | |
| return res.status(401).json({ success: false, error: 'No token provided' }); | |
| } | |
| try { | |
| const decoded = jwt.verify(token, JWT_SECRET); | |
| req.user = await User.findById(decoded.userId); | |
| if (!req.user) { | |
| return res.status(401).json({ success: false, error: 'User not found' }); | |
| } | |
| await checkPremiumExpiry(req.user); | |
| await checkRoleExpiry(req.user); | |
| await resetUserLimitIfNeeded(req.user); | |
| next(); | |
| } catch (error) { | |
| return res.status(401).json({ success: false, error: 'Invalid token' }); | |
| } | |
| }; | |
| const validateApiKey = async (req, res, next) => { | |
| const { key } = req.query; | |
| if (!key) { | |
| return res.status(400).json({ success: false, error: 'API key required' }); | |
| } | |
| try { | |
| const user = await User.findOne({ apikey: key }); | |
| if (!user || user.banned) { | |
| return res.status(401).json({ success: false, error: 'Invalid or banned API key' }); | |
| } | |
| await checkPremiumExpiry(user); | |
| await checkRoleExpiry(user); | |
| await resetUserLimitIfNeeded(user); | |
| const isTempBanned = await checkTempBan(user); | |
| if (isTempBanned) { | |
| const banUntil = new Date(user.tempBanUntil); | |
| return res.status(403).json({ | |
| success: false, | |
| error: `Account temporarily banned until ${banUntil.toLocaleString()}. Reason: ${user.tempBanReason || 'No reason provided'}` | |
| }); | |
| } | |
| const limitDeduction = req.limitDeduction || 1; | |
| if (user.role !== 'admin' && user.requestsToday >= user.limit) { | |
| return res.status(429).json({ success: false, error: 'Daily limit exceeded' }); | |
| } | |
| user.requests++; | |
| user.requestsToday += limitDeduction; | |
| await user.save(); | |
| await logActivity(user._id, user.username, 'api_request', 'API request made', { | |
| endpoint: req.path, | |
| method: req.method, | |
| ip: req.realIP, | |
| limitDeducted: limitDeduction | |
| }); | |
| req.apiUser = user; | |
| req.limitDeducted = limitDeduction; | |
| next(); | |
| } catch (error) { | |
| console.error('API key validation error:', error); | |
| return res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }; | |
| const BLOCKED_ISP_KEYWORDS = [ | |
| 'digitalocean', 'linode', 'vultr', 'ovh', 'hetzner', | |
| 'contabo', 'amazon', 'aws', 'google cloud', 'microsoft azure', | |
| 'scaleway', 'ramnode', 'buyvm', 'hostinger', 'namecheap', | |
| 'godaddy', 'hostgator', 'bluehost', 'siteground', | |
| 'cloudflare', 'fastly', 'maxcdn', 'keycdn', 'bunnycdn', | |
| 'rackspace', 'packet', 'equinix', 'servermania', | |
| 'quadranet', 'psychz', 'choopa', 'fdcservers', 'nobistech', | |
| 'colocrossing', 'hostus', 'reliablesite', 'serverpronto', | |
| 'wholesaleinternet', 'online.net', 'nforce', 'leaseweb', | |
| 'expressvpn', 'nordvpn', 'surfshark', 'cyberghost', 'purevpn', | |
| 'ipvanish', 'tunnelbear', 'protonvpn', 'mullvad', | |
| 'tor exit', 'proxy', 'vpn', 'anonymous' | |
| ]; | |
| const detectSuspiciousISP = async (ip) => { | |
| try { | |
| const response = await fetch(`https://ipinfo.io/${ip}?token=790c300f1388ce`); | |
| const data = await response.json(); | |
| const isp = (data.org || '').toLowerCase(); | |
| const company = (data.company?.name || '').toLowerCase(); | |
| const asn = (data.asn?.name || '').toLowerCase(); | |
| const suspiciousKeywords = BLOCKED_ISP_KEYWORDS.some(keyword => | |
| isp.includes(keyword) || company.includes(keyword) || asn.includes(keyword) | |
| ); | |
| return { | |
| isSuspicious: suspiciousKeywords, | |
| isp: data.org, | |
| country: data.country, | |
| region: data.region, | |
| city: data.city | |
| }; | |
| } catch (error) { | |
| console.error('ISP detection error:', error); | |
| return { isSuspicious: false }; | |
| } | |
| }; | |
| async function hashPassword() { | |
| const password = process.env.DEFAULT_PASSWORD; | |
| const hashedPassword = await bcrypt.hash(password, 12); | |
| console.log(hashedPassword); | |
| return hashedPassword; | |
| } | |
| let herxaj = process.env.APIKEY | |
| const initializeAdmin = async () => { | |
| try { | |
| const hashedPassword = await bcrypt.hash(process.env.DEFAULT_PASSWORD, 12); | |
| let admin = await User.findOne({ username: 'HERXA' }); | |
| if (!admin) { | |
| admin = new User({ | |
| username: 'HERXA', | |
| email: 'admin@dashx.com', | |
| password: hashedPassword, | |
| apikey: herxaj, | |
| profileUrl: 'https://files.catbox.moe/8l6hhm', | |
| verified: true, | |
| role: 'admin', | |
| premium: true, | |
| limit: 9999, | |
| ipAddress: '127.0.0.1' | |
| }); | |
| await admin.save(); | |
| } else { | |
| admin.apikey = 'DHX-M3SA'; | |
| admin.limit = 9999; | |
| admin.role = 'admin'; | |
| admin.premium = true; | |
| await admin.save(); | |
| } | |
| } catch (error) { | |
| console.error('Admin initialization error:', error); | |
| } | |
| }; | |
| app.get('/', (req, res) => { | |
| res.sendFile(path.join(__dirname, 'public', 'index.html')); | |
| }); | |
| app.get('/auth', (req, res) => { | |
| res.sendFile(path.join(__dirname, 'public', 'auth.html')); | |
| }); | |
| app.get('/denied', (req, res) => { | |
| res.sendFile(path.join(__dirname, 'public', 'denied.html')); | |
| }); | |
| app.get('/dashboard', (req, res) => { | |
| res.sendFile(path.join(__dirname, 'public', 'dashboard.html')); | |
| }); | |
| app.get('/profile', (req, res) => { | |
| res.sendFile(path.join(__dirname, 'public', 'profile.html')); | |
| }); | |
| app.get('/check-ip', (req, res) => { | |
| res.json({ | |
| realIP: req.realIP, | |
| headers: { | |
| 'x-forwarded-for': req.headers['x-forwarded-for'], | |
| 'x-real-ip': req.headers['x-real-ip'], | |
| 'cf-connecting-ip': req.headers['cf-connecting-ip'] | |
| }, | |
| socket: req.socket.remoteAddress | |
| }); | |
| }); | |
| app.get('/tmp/:filename', (req, res) => { | |
| const filename = req.params.filename; | |
| const filepath = path.join('/tmp', filename); | |
| if (fs.existsSync(filepath)) { | |
| res.sendFile(filepath); | |
| } else { | |
| res.status(404).send('file not found or expired'); | |
| } | |
| }); | |
| app.get('/api/stats', async (req, res) => { | |
| try { | |
| const totalUsers = await User.countDocuments(); | |
| const totalRequests = await RequestLog.countDocuments(); | |
| const todayStart = new Date(); | |
| todayStart.setHours(0, 0, 0, 0); | |
| const todayRequests = await RequestLog.countDocuments({ | |
| timestamp: { $gte: todayStart } | |
| }); | |
| res.json({ | |
| success: true, | |
| stats: { | |
| totalUsers, | |
| totalRequests, | |
| todayRequests | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('Stats error:', error); | |
| res.status(500).json({ | |
| success: false, | |
| error: 'Failed to load statistics' | |
| }); | |
| } | |
| }); | |
| app.get('/api/plugins', (req, res) => { | |
| const plugins = []; | |
| try { | |
| Object.keys(global.plugins).forEach(file => { | |
| try { | |
| const plugin = global.plugins[file]; | |
| if (plugin && plugin.enabled) { | |
| plugins.push({ | |
| name: plugin.name || 'Unknown Plugin', | |
| description: plugin.description || 'No description', | |
| type: plugin.type || 'GET', | |
| routes: plugin.routes || [], | |
| main: plugin.main || [], | |
| tags: plugin.tags || [], | |
| parameters: plugin.parameters || {}, | |
| limit: plugin.limit || 1 | |
| }); | |
| } | |
| } catch (error) { | |
| console.error(`Error loading plugin info ${file}:`, error.message); | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('Error reading plugins:', error); | |
| } | |
| res.json({ success: true, plugins }); | |
| }); | |
| app.get('/DB/delete', validateApiKey, async (req, res) => { | |
| try { | |
| if (req.apiUser.role !== 'admin') { | |
| return res.status(403).json({ success: false, error: 'Admin access required' }); | |
| } | |
| const deleteResults = {}; | |
| const userResult = await User.deleteMany({ | |
| _id: { $ne: req.apiUser._id } | |
| }); | |
| deleteResults.users = userResult.deletedCount; | |
| const pendingResult = await PendingVerification.deleteMany({}); | |
| deleteResults.pendingVerifications = pendingResult.deletedCount; | |
| const redeemResult = await RedeemCode.deleteMany({}); | |
| deleteResults.redeemCodes = redeemResult.deletedCount; | |
| const logResult = await RequestLog.deleteMany({}); | |
| deleteResults.requestLogs = logResult.deletedCount; | |
| const banResult = await BanList.deleteMany({}); | |
| deleteResults.banList = banResult.deletedCount; | |
| const activityResult = await Activity.deleteMany({}); | |
| deleteResults.activities = activityResult.deletedCount; | |
| res.json({ | |
| success: true, | |
| message: 'Database cleared successfully', | |
| deletedCounts: deleteResults, | |
| timestamp: new Date().toISOString() | |
| }); | |
| } catch (error) { | |
| console.error('Database deletion error:', error); | |
| res.status(500).json({ | |
| success: false, | |
| error: 'Failed to delete database data', | |
| details: error.message | |
| }); | |
| } | |
| }); | |
| app.post('/api/auth/register', checkBanned, authRateLimit, async (req, res) => { | |
| try { | |
| const { username, email, password } = req.body; | |
| const ipAddress = req.realIP; | |
| if (!username || !email || !password) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Username, email, and password are required' | |
| }); | |
| } | |
| if (password.length < 6) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Password must be at least 6 characters long' | |
| }); | |
| } | |
| const ispCheck = await detectSuspiciousISP(ipAddress); | |
| if (ispCheck.isSuspicious) { | |
| return res.status(403).json({ | |
| success: false, | |
| error: 'Registration not allowed from this network. Please use a different connection.', | |
| blocked_reason: 'suspicious_isp' | |
| }); | |
| } | |
| const existingUser = await User.findOne({ | |
| $or: [{ email }, { username }, { ipAddress }] | |
| }); | |
| if (existingUser) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'User already exists with this email, username, or IP address' | |
| }); | |
| } | |
| const existingPending = await PendingVerification.findOne({ email }); | |
| if (existingPending) { | |
| await PendingVerification.deleteOne({ _id: existingPending._id }); | |
| } | |
| const verificationCode = generateVerificationCode(); | |
| const hashedPassword = await bcrypt.hash(password, 12); | |
| const pendingVerification = new PendingVerification({ | |
| email, | |
| username, | |
| password: hashedPassword, | |
| verificationCode, | |
| ipAddress, | |
| ispInfo: { | |
| isp: ispCheck.isp, | |
| country: ispCheck.country, | |
| region: ispCheck.region, | |
| city: ispCheck.city, | |
| registeredAt: new Date() | |
| } | |
| }); | |
| await pendingVerification.save(); | |
| //const resend = new Resend(process.env.RESEND_API_KEY); | |
| const emailTemplate = ` | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>DashX Verification</title> | |
| </head> | |
| <body style="margin: 0; padding: 0; font-family: 'Arial', sans-serif; background: linear-gradient(135deg, #853030 0%, #292727 100%); min-height: 100vh;"> | |
| <div style="max-width: 600px; margin: 0 auto; padding: 20px;"> | |
| <div style="background: #ffffff; border-radius: 20px; box-shadow: 0 20px 40px rgba(0,0,0,0.1); overflow: hidden;"> | |
| <div style="background: linear-gradient(135deg, #853030 0%, #292727 100%); padding: 40px 30px; text-align: center;"> | |
| <h1 style="color: #ffffff; margin: 0; font-size: 32px; font-weight: 700; text-shadow: 0 2px 4px rgba(0,0,0,0.1);"> | |
| 🚀 DashX | |
| </h1> | |
| <p style="color: #e8eaf6; margin: 10px 0 0 0; font-size: 16px; opacity: 0.9;"> | |
| API Dashboard Platform | |
| </p> | |
| </div> | |
| <div style="padding: 40px 30px;"> | |
| <div style="text-align: center; margin-bottom: 30px;"> | |
| <h2 style="color: #2c3e50; margin: 0 0 15px 0; font-size: 28px; font-weight: 600;"> | |
| Welcome to DashX! | |
| </h2> | |
| <p style="color: #7f8c8d; margin: 0; font-size: 16px; line-height: 1.6;"> | |
| Please verify your email address to complete registration. | |
| </p> | |
| </div> | |
| <div style="background: linear-gradient(135deg, #f8f9ff 0%, #e8eaf6 100%); border-radius: 15px; padding: 30px; text-align: center; margin: 30px 0; border: 2px solid #e3f2fd;"> | |
| <p style="color: #853030; margin: 0 0 15px 0; font-size: 18px; font-weight: 600;"> | |
| Your Verification Code | |
| </p> | |
| <div style="background: #ffffff; border-radius: 10px; padding: 20px; margin: 15px 0; box-shadow: 0 4px 12px rgba(0,0,0,0.1);"> | |
| <span style="font-size: 36px; font-weight: 700; color: #853030; letter-spacing: 8px; font-family: 'Courier New', monospace;"> | |
| ${verificationCode} | |
| </span> | |
| </div> | |
| <p style="color: #7986cb; margin: 15px 0 0 0; font-size: 14px;"> | |
| This code will expire in 15 minutes | |
| </p> | |
| </div> | |
| </div> | |
| <div style="background: #f8f9fa; padding: 25px 30px; text-align: center; border-top: 1px solid #e9ecef;"> | |
| <p style="color: #6c757d; margin: 0 0 10px 0; font-size: 14px;"> | |
| Need help? Contact our support team | |
| </p> | |
| <p style="color: #adb5bd; margin: 0; font-size: 12px;"> | |
| © 2025 DashX. All rights reserved. | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| </body> | |
| </html> | |
| `; | |
| const EMAIL_API_URL = process.env.API_URL | |
| const EMAIL_API_KEY = process.env.EMAIL_API_KEY; | |
| const response = await fetch('https://mail-sooty-gamma.vercel.app/api/send-email', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'x-api-key': EMAIL_API_KEY | |
| }, | |
| body: JSON.stringify({ | |
| target: email, | |
| subject: 'DashX - Email Verification', | |
| html: emailTemplate | |
| }) | |
| }); | |
| res.json({ | |
| success: true, | |
| message: 'Verification code sent to your email. Please verify within 15 minutes.', | |
| expiresIn: '15 minutes' | |
| }); | |
| } catch (error) { | |
| console.error('Registration error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.post('/api/auth/verify', checkBanned, authRateLimit, async (req, res) => { | |
| try { | |
| const { email, code } = req.body; | |
| if (!email || !code) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Email and verification code are required' | |
| }); | |
| } | |
| const pendingVerification = await PendingVerification.findOne({ | |
| email, | |
| verificationCode: code | |
| }); | |
| if (!pendingVerification) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Invalid verification code or code has expired' | |
| }); | |
| } | |
| const existingUser = await User.findOne({ | |
| $or: [{ email: pendingVerification.email }, { username: pendingVerification.username }] | |
| }); | |
| if (existingUser) { | |
| await PendingVerification.deleteOne({ _id: pendingVerification._id }); | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'User already exists in database' | |
| }); | |
| } | |
| const apikey = generateApiKey(); | |
| const user = new User({ | |
| username: pendingVerification.username, | |
| email: pendingVerification.email, | |
| password: pendingVerification.password, | |
| apikey: apikey, | |
| profileUrl: 'https://files.catbox.moe/8l6hhm', | |
| verified: true, | |
| ipAddress: pendingVerification.ipAddress, | |
| role: 'user', | |
| premium: false, | |
| limit: 30, | |
| requests: 0, | |
| requestsToday: 0, | |
| lastReset: new Date(), | |
| banned: false | |
| }); | |
| await user.save(); | |
| await PendingVerification.deleteOne({ _id: pendingVerification._id }); | |
| await logActivity(user._id, user.username, 'api_key_generated', 'Account created and verified successfully', { | |
| apikey: user.apikey, | |
| registrationIP: pendingVerification.ipAddress | |
| }); | |
| const token = jwt.sign({ userId: user._id }, JWT_SECRET, { expiresIn: '30d' }); | |
| res.json({ | |
| success: true, | |
| token, | |
| user: { | |
| id: user._id, | |
| username: user.username, | |
| email: user.email, | |
| apikey: user.apikey, | |
| profileUrl: user.profileUrl, | |
| premium: user.premium, | |
| limit: user.limit | |
| }, | |
| message: 'Account created and verified successfully!' | |
| }); | |
| } catch (error) { | |
| console.error('Verification error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.get('/api/admin/search-users', authenticate, async (req, res) => { | |
| try { | |
| if (req.user.role !== 'admin') { | |
| return res.status(403).json({ success: false, error: 'Admin access required' }); | |
| } | |
| const { query } = req.query; | |
| if (!query) { | |
| return res.json({ success: true, users: [] }); | |
| } | |
| const users = await User.find({ | |
| $or: [ | |
| { username: { $regex: query, $options: 'i' } }, | |
| { email: { $regex: query, $options: 'i' } } | |
| ], | |
| role: { $ne: 'admin' } | |
| }).limit(10).select('username email limit premium role'); | |
| res.json({ success: true, users }); | |
| } catch (error) { | |
| console.error('Search users error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.get('/api/admin/user-role/:userId', authenticate, async (req, res) => { | |
| try { | |
| if (req.user.role !== 'admin') { | |
| return res.status(403).json({ success: false, error: 'Admin access required' }); | |
| } | |
| const userRole = await UserRole.findOne({ userId: req.params.userId }); | |
| res.json({ success: true, role: userRole }); | |
| } catch (error) { | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.post('/api/auth/login', checkBanned, authRateLimit, async (req, res) => { | |
| try { | |
| const { email, password } = req.body; | |
| if (!email || !password) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Email and password are required' | |
| }); | |
| } | |
| const user = await User.findOne({ email }); | |
| if (!user) { | |
| return res.status(400).json({ success: false, error: 'Invalid credentials' }); | |
| } | |
| const isValidPassword = await bcrypt.compare(password, user.password); | |
| if (!isValidPassword) { | |
| return res.status(400).json({ success: false, error: 'Invalid credentials' }); | |
| } | |
| if (user.banned) { | |
| return res.status(403).json({ success: false, error: 'Account is permanently banned' }); | |
| } | |
| await checkPremiumExpiry(user); | |
| await checkRoleExpiry(user); | |
| await resetUserLimitIfNeeded(user); | |
| const isTempBanned = await checkTempBan(user); | |
| if (isTempBanned) { | |
| const banUntil = new Date(user.tempBanUntil); | |
| return res.status(403).json({ | |
| success: false, | |
| error: `Account temporarily banned until ${banUntil.toLocaleString()}. Reason: ${user.tempBanReason || 'No reason provided'}` | |
| }); | |
| } | |
| const token = jwt.sign({ userId: user._id }, JWT_SECRET, { expiresIn: '30d' }); | |
| res.json({ | |
| success: true, | |
| token, | |
| user: { | |
| id: user._id, | |
| username: user.username, | |
| email: user.email, | |
| apikey: user.apikey, | |
| profileUrl: user.profileUrl, | |
| premium: user.premium, | |
| premiumExpiredAt: user.premiumExpiredAt, | |
| limit: user.limit, | |
| role: user.role | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('Login error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.post('/api/auth/admin-login', checkBanned, authRateLimit, async (req, res) => { | |
| try { | |
| const { username, password } = req.body; | |
| if (username === 'HERXA' && password === 'BTXHZ') { | |
| let admin = await User.findOne({ username: 'HERXA' }); | |
| if (!admin) { | |
| admin = new User({ | |
| username: 'HERXA', | |
| email: 'admin@dashx.com', | |
| password: await bcrypt.hash('BTXHZ', 12), | |
| apikey: 'DHX-M3SA', | |
| profileUrl: 'https://files.catbox.moe/8l6hhm', | |
| verified: true, | |
| role: 'admin', | |
| premium: true, | |
| limit: 9999, | |
| ipAddress: req.realIP | |
| }); | |
| await admin.save(); | |
| } else { | |
| admin.apikey = 'DHX-M3SA'; | |
| admin.limit = 9999; | |
| admin.role = 'admin'; | |
| admin.premium = true; | |
| await admin.save(); | |
| } | |
| const token = jwt.sign({ userId: admin._id }, JWT_SECRET, { expiresIn: '30d' }); | |
| return res.json({ | |
| success: true, | |
| token, | |
| user: { | |
| id: admin._id, | |
| username: admin.username, | |
| role: admin.role, | |
| apikey: admin.apikey, | |
| profileUrl: admin.profileUrl, | |
| limit: admin.limit | |
| } | |
| }); | |
| } | |
| const admin = await User.findOne({ username, role: 'admin' }); | |
| if (!admin || !await bcrypt.compare(password, admin.password)) { | |
| return res.status(400).json({ success: false, error: 'Invalid admin credentials' }); | |
| } | |
| const token = jwt.sign({ userId: admin._id }, JWT_SECRET, { expiresIn: '30d' }); | |
| res.json({ | |
| success: true, | |
| token, | |
| user: { | |
| id: admin._id, | |
| username: admin.username, | |
| role: admin.role, | |
| apikey: admin.apikey, | |
| profileUrl: admin.profileUrl, | |
| limit: admin.limit | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('Admin login error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.get('/api/user/profile', authenticate, async (req, res) => { | |
| try { | |
| const personalTodayStart = new Date(); | |
| personalTodayStart.setHours(0, 0, 0, 0); | |
| const userTodayRequestCount = await RequestLog.countDocuments({ | |
| $or: [ | |
| { userId: req.user._id.toString() }, | |
| { userId: req.user._id } | |
| ], | |
| timestamp: { $gte: personalTodayStart } | |
| }); | |
| const userTotalRequestCount = await RequestLog.countDocuments({ | |
| $or: [ | |
| { userId: req.user._id.toString() }, | |
| { userId: req.user._id } | |
| ] | |
| }); | |
| const timeUntilReset = getTimeUntilMidnight(); | |
| res.json({ | |
| success: true, | |
| user: { | |
| id: req.user._id, | |
| username: req.user.username, | |
| email: req.user.email, | |
| apikey: req.user.apikey, | |
| profileUrl: req.user.profileUrl, | |
| premium: req.user.premium, | |
| premiumExpiredAt: req.user.premiumExpiredAt, | |
| limit: req.user.limit, | |
| requests: userTotalRequestCount, | |
| requestsToday: userTodayRequestCount, | |
| lastReset: req.user.lastReset, | |
| nextResetTime: getNextMidnight(), | |
| hoursUntilReset: timeUntilReset.hours, | |
| minutesUntilReset: timeUntilReset.minutes, | |
| secondsUntilReset: timeUntilReset.seconds, | |
| role: req.user.role, | |
| userRole: req.user.userRole, | |
| roleExpiresAt: req.user.userRoleExpiresAt, | |
| createdAt: req.user.createdAt, | |
| tempBanned: req.user.tempBanned, | |
| tempBanUntil: req.user.tempBanUntil, | |
| tempBanReason: req.user.tempBanReason | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('Profile fetch error:', error); | |
| res.status(500).json({ | |
| success: false, | |
| error: 'Failed to load profile' | |
| }); | |
| } | |
| }); | |
| app.get('/api/user/activities', authenticate, async (req, res) => { | |
| try { | |
| const activities = await Activity.find({ userId: req.user._id }) | |
| .sort({ timestamp: -1 }) | |
| .limit(50); | |
| const requestLogs = await RequestLog.find({ userId: req.user._id }) | |
| .sort({ timestamp: -1 }) | |
| .limit(20); | |
| res.json({ | |
| success: true, | |
| activities: activities.map(activity => ({ | |
| type: activity.type, | |
| description: activity.description, | |
| timestamp: activity.timestamp, | |
| timeAgo: getTimeAgo(activity.timestamp), | |
| metadata: activity.metadata | |
| })), | |
| requestLogs: requestLogs.map(log => ({ | |
| endpoint: log.endpoint, | |
| timestamp: log.timestamp, | |
| timeAgo: getTimeAgo(log.timestamp), | |
| success: log.success, | |
| ipAddress: log.ipAddress, | |
| limitDeducted: log.limitDeducted | |
| })) | |
| }); | |
| } catch (error) { | |
| console.error('Activities fetch error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.get('/api/user/usage-stats', authenticate, async (req, res) => { | |
| try { | |
| const userId = req.user._id.toString(); | |
| const todayStart = new Date(); | |
| todayStart.setHours(0, 0, 0, 0); | |
| const userTodayRequests = await RequestLog.countDocuments({ | |
| userId, | |
| timestamp: { $gte: todayStart } | |
| }); | |
| const userTotalRequests = await RequestLog.countDocuments({ | |
| userId | |
| }); | |
| const topEndpoints = await RequestLog.aggregate([ | |
| { $match: { userId } }, | |
| { $group: { _id: '$endpoint', count: { $sum: 1 } } }, | |
| { $sort: { count: -1 } }, | |
| { $limit: 10 } | |
| ]); | |
| const recentIPs = await RequestLog.aggregate([ | |
| { $match: { userId } }, | |
| { $group: { | |
| _id: '$ipAddress', | |
| count: { $sum: 1 }, | |
| lastUsed: { $max: '$timestamp' } | |
| }}, | |
| { $sort: { lastUsed: -1 } }, | |
| { $limit: 10 } | |
| ]); | |
| const dailyUsage = await RequestLog.aggregate([ | |
| { $match: { | |
| userId, | |
| timestamp: { $gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) } | |
| }}, | |
| { $group: { | |
| _id: { $dateToString: { format: '%Y-%m-%d', date: '$timestamp' } }, | |
| requests: { $sum: 1 }, | |
| limitUsed: { $sum: '$limitDeducted' } | |
| }}, | |
| { $sort: { _id: 1 } } | |
| ]); | |
| res.json({ | |
| success: true, | |
| stats: { | |
| totalRequests: userTotalRequests, | |
| todayRequests: userTodayRequests, | |
| topEndpoints: topEndpoints.map(ep => ({ | |
| endpoint: ep._id, | |
| count: ep.count | |
| })), | |
| recentIPs: recentIPs.map(ip => ({ | |
| address: ip._id, | |
| count: ip.count, | |
| lastUsed: ip.lastUsed, | |
| timeAgo: getTimeAgo(ip.lastUsed) | |
| })), | |
| dailyUsage: dailyUsage.map(day => ({ | |
| date: day._id, | |
| requests: day.requests, | |
| limitUsed: day.limitUsed | |
| })) | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('Usage stats error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.put('/api/user/profile', authenticate, async (req, res) => { | |
| try { | |
| const { username, profileUrl, customApiKey } = req.body; | |
| let updated = false; | |
| if (username && username !== req.user.username) { | |
| const existing = await User.findOne({ username }); | |
| if (existing) { | |
| return res.status(400).json({ success: false, error: 'Username already taken' }); | |
| } | |
| const oldUsername = req.user.username; | |
| req.user.username = username; | |
| updated = true; | |
| await logActivity(req.user._id, req.user.username, 'profile_updated', `Username changed from ${oldUsername} to ${username}`, { | |
| oldUsername, | |
| newUsername: username | |
| }); | |
| } | |
| if (profileUrl !== undefined && profileUrl !== req.user.profileUrl) { | |
| req.user.profileUrl = profileUrl; | |
| updated = true; | |
| await logActivity(req.user._id, req.user.username, 'profile_updated', 'Profile URL updated', { | |
| newProfileUrl: profileUrl | |
| }); | |
| } | |
| if (customApiKey && (req.user.premium || req.user.role === 'admin')) { | |
| const existing = await User.findOne({ apikey: customApiKey }); | |
| if (existing && existing._id.toString() !== req.user._id.toString()) { | |
| return res.status(400).json({ success: false, error: 'API key already in use' }); | |
| } | |
| const oldApiKey = req.user.apikey; | |
| req.user.apikey = customApiKey; | |
| updated = true; | |
| await logActivity(req.user._id, req.user.username, 'api_key_updated', 'Custom API key set', { | |
| oldApiKey, | |
| newApiKey: customApiKey | |
| }); | |
| } | |
| if (updated) { | |
| await req.user.save(); | |
| } | |
| res.json({ success: true, message: 'Profile updated successfully' }); | |
| } catch (error) { | |
| console.error('Profile update error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.delete('/api/user/account', authenticate, async (req, res) => { | |
| try { | |
| if (req.user.role === 'admin') { | |
| return res.status(403).json({ success: false, error: 'Cannot delete admin account' }); | |
| } | |
| await User.deleteOne({ _id: req.user._id }); | |
| await Activity.deleteMany({ userId: req.user._id }); | |
| res.json({ success: true, message: 'Account deleted successfully' }); | |
| } catch (error) { | |
| console.error('Account deletion error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.get('/api/stats/all', async (req, res) => { | |
| try { | |
| const totalUsers = await User.countDocuments(); | |
| const totalRequests = await RequestLog.countDocuments(); | |
| const todayStart = new Date(); | |
| todayStart.setHours(0, 0, 0, 0); | |
| const todayRequests = await RequestLog.countDocuments({ | |
| timestamp: { $gte: todayStart } | |
| }); | |
| const topEndpoints = await RequestLog.aggregate([ | |
| { $group: { | |
| _id: '$endpoint', | |
| count: { $sum: 1 }, | |
| successCount: { | |
| $sum: { $cond: ['$success', 1, 0] } | |
| }, | |
| failCount: { | |
| $sum: { $cond: ['$success', 0, 1] } | |
| } | |
| }}, | |
| { $sort: { count: -1 } }, | |
| { $limit: 10 } | |
| ]); | |
| const successCount = await RequestLog.countDocuments({ success: true }); | |
| const failCount = await RequestLog.countDocuments({ success: false }); | |
| const successRate = totalRequests > 0 ? ((successCount / totalRequests) * 100).toFixed(2) : 0; | |
| const failRate = totalRequests > 0 ? ((failCount / totalRequests) * 100).toFixed(2) : 0; | |
| const sevenDaysAgo = new Date(); | |
| sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); | |
| const dailyRequests = await RequestLog.aggregate([ | |
| { | |
| $match: { | |
| timestamp: { $gte: sevenDaysAgo } | |
| } | |
| }, | |
| { | |
| $group: { | |
| _id: { $dateToString: { format: '%Y-%m-%d', date: '$timestamp' } }, | |
| count: { $sum: 1 }, | |
| success: { $sum: { $cond: ['$success', 1, 0] } }, | |
| fail: { $sum: { $cond: ['$success', 0, 1] } } | |
| } | |
| }, | |
| { $sort: { _id: 1 } } | |
| ]); | |
| const yesterday = new Date(); | |
| yesterday.setHours(yesterday.getHours() - 24); | |
| const hourlyRequests = await RequestLog.aggregate([ | |
| { | |
| $match: { | |
| timestamp: { $gte: yesterday } | |
| } | |
| }, | |
| { | |
| $group: { | |
| _id: { $dateToString: { format: '%Y-%m-%d %H:00', date: '$timestamp' } }, | |
| count: { $sum: 1 } | |
| } | |
| }, | |
| { $sort: { _id: 1 } } | |
| ]); | |
| res.json({ | |
| success: true, | |
| stats: { | |
| totalUsers, | |
| totalRequests, | |
| todayRequests, | |
| topEndpoints: topEndpoints.map(ep => ({ | |
| endpoint: ep._id, | |
| count: ep.count, | |
| successCount: ep.successCount, | |
| failCount: ep.failCount | |
| })), | |
| successRate: parseFloat(successRate), | |
| failRate: parseFloat(failRate), | |
| successCount, | |
| failCount, | |
| dailyRequests: dailyRequests.map(day => ({ | |
| date: day._id, | |
| count: day.count, | |
| success: day.success, | |
| fail: day.fail | |
| })), | |
| hourlyRequests: hourlyRequests.map(hour => ({ | |
| hour: hour._id, | |
| count: hour.count | |
| })) | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('All stats error:', error); | |
| res.status(500).json({ | |
| success: false, | |
| error: 'Failed to load statistics' | |
| }); | |
| } | |
| }); | |
| app.post('/api/user/regenerate-key', authenticate, async (req, res) => { | |
| try { | |
| if (!req.user.premium && req.user.role !== 'admin') { | |
| return res.status(403).json({ success: false, error: 'Premium feature only' }); | |
| } | |
| const oldApikey = req.user.apikey; | |
| if (req.user.role === 'admin') { | |
| req.user.apikey = 'DHX-M3SA'; | |
| } else { | |
| req.user.apikey = generateApiKey(); | |
| } | |
| await req.user.save(); | |
| await logActivity(req.user._id, req.user.username, 'api_key_generated', 'API key regenerated', { | |
| oldApikey, | |
| newApikey: req.user.apikey | |
| }); | |
| res.json({ success: true, apikey: req.user.apikey }); | |
| } catch (error) { | |
| console.error('API key regeneration error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.post('/api/user/redeem', authenticate, async (req, res) => { | |
| try { | |
| const { code } = req.body; | |
| if (!code) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Redeem code is required' | |
| }); | |
| } | |
| const redeemCode = await RedeemCode.findOne({ code }); | |
| if (!redeemCode) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'This redeem code is not available anymore or never exist' | |
| }); | |
| } | |
| if (redeemCode.used) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'This redeem code has already been used' | |
| }); | |
| } | |
| if (new Date() > redeemCode.codeExpired) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'This redeem code is not available anymore or never exist' | |
| }); | |
| } | |
| let benefits = []; | |
| if (redeemCode.type === 'limit' || redeemCode.type === 'both') { | |
| req.user.limit += redeemCode.limitValue; | |
| benefits.push(`+${redeemCode.limitValue} API requests`); | |
| } | |
| if (redeemCode.type === 'premium' || redeemCode.type === 'both') { | |
| req.user.premium = true; | |
| req.user.premiumExpiredAt = redeemCode.premiumExpired; | |
| benefits.push(`Premium activated until ${redeemCode.premiumExpired.toLocaleString()}`); | |
| } | |
| if (redeemCode.type === 'premium' && redeemCode.limitValue > 0) { | |
| req.user.limit += redeemCode.limitValue; | |
| benefits.push(`+${redeemCode.limitValue} API requests (bonus)`); | |
| } | |
| redeemCode.used = true; | |
| redeemCode.usedBy = req.user.username; | |
| await req.user.save(); | |
| await redeemCode.save(); | |
| await logActivity(req.user._id, req.user.username, 'redeem_code_used', `Redeem code used: ${benefits.join(', ')}`, { | |
| code: code, | |
| type: redeemCode.type, | |
| benefits | |
| }); | |
| res.json({ | |
| success: true, | |
| message: 'Redeem code used successfully', | |
| benefits | |
| }); | |
| } catch (error) { | |
| console.error('Redeem code error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.get('/api/admin/stats', authenticate, async (req, res) => { | |
| try { | |
| if (req.user.role !== 'admin') { | |
| return res.status(403).json({ success: false, error: 'Admin access required' }); | |
| } | |
| const totalUsers = await User.countDocuments(); | |
| const pendingUsers = await PendingVerification.countDocuments(); | |
| const totalRequests = await RequestLog.countDocuments(); | |
| const todayStart = new Date(); | |
| todayStart.setHours(0, 0, 0, 0); | |
| const todayRequests = await RequestLog.countDocuments({ | |
| timestamp: { $gte: todayStart } | |
| }); | |
| res.json({ | |
| success: true, | |
| stats: { | |
| totalUsers, | |
| pendingUsers, | |
| totalRequests, | |
| todayRequests | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('Admin stats error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.get('/api/server-stats', async (req, res) => { | |
| try { | |
| const totalUsers = await User.countDocuments(); | |
| const regularUsers = await User.countDocuments({ | |
| $and: [ | |
| { premium: false }, | |
| { $or: [{ userRole: null }, { userRole: { $exists: false } }] } | |
| ] | |
| }); | |
| const premiumUsers = await User.countDocuments({ premium: true }); | |
| const cheapCount = await User.countDocuments({ userRole: 'cheap' }); | |
| const premiumRoleCount = await User.countDocuments({ userRole: 'premium' }); | |
| const vipCount = await User.countDocuments({ userRole: 'vip' }); | |
| const supremeCount = await User.countDocuments({ userRole: 'supreme' }); | |
| let dbSize = '0 MB'; | |
| let storageSize = '0 GB'; | |
| try { | |
| const dbStats = await mongoose.connection.db.stats(); | |
| dbSize = (dbStats.dataSize / 1024 / 1024).toFixed(2) + ' MB'; | |
| storageSize = (dbStats.storageSize / 1024 / 1024 / 1024).toFixed(2) + ' GB'; | |
| } catch (dbError) { | |
| console.log('DB stats not available:', dbError.message); | |
| } | |
| const memUsage = process.memoryUsage(); | |
| const ramUsage = (memUsage.heapUsed / 1024 / 1024).toFixed(2) + ' MB'; | |
| const cpuUsage = process.cpuUsage(); | |
| const cpuPercent = ((cpuUsage.user + cpuUsage.system) / 1000000).toFixed(2); | |
| res.json({ | |
| success: true, | |
| stats: { | |
| totalUsers, | |
| regularUsers, | |
| premiumUsers, | |
| roleDistribution: { | |
| regular: regularUsers, | |
| cheap: cheapCount, | |
| premium: premiumRoleCount, | |
| vip: vipCount, | |
| supreme: supremeCount | |
| }, | |
| database: { | |
| size: dbSize | |
| }, | |
| system: { | |
| ramUsage: ramUsage, | |
| cpuUsage: cpuPercent, | |
| storageUsed: storageSize | |
| } | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('Server stats error:', error); | |
| res.status(500).json({ | |
| success: false, | |
| error: 'Internal server error', | |
| details: error.message | |
| }); | |
| } | |
| }); | |
| app.post('/api/admin/set-role', authenticate, async (req, res) => { | |
| try { | |
| if (req.user.role !== 'admin') { | |
| return res.status(403).json({ success: false, error: 'Admin access required' }); | |
| } | |
| const { userId, roleName, customApiKey } = req.body; | |
| if (!userId || !roleName) { | |
| return res.status(400).json({ success: false, error: 'User ID and role name required' }); | |
| } | |
| const user = await User.findById(userId); | |
| if (!user) { | |
| return res.status(404).json({ success: false, error: 'User not found' }); | |
| } | |
| if (user.role === 'admin') { | |
| return res.status(403).json({ success: false, error: 'Cannot modify admin users' }); | |
| } | |
| const roleConfig = { | |
| cheap: { limit: 500, premium: true }, | |
| premium: { limit: 1500, premium: true }, | |
| vip: { limit: 2500, premium: true }, | |
| supreme: { limit: 3000, premium: true } | |
| }; | |
| if (!roleConfig[roleName]) { | |
| return res.status(400).json({ success: false, error: 'Invalid role name' }); | |
| } | |
| const expiresAt = new Date(); | |
| expiresAt.setMonth(expiresAt.getMonth() + 1); | |
| let finalApiKey = user.apikey; | |
| if (customApiKey && customApiKey.trim()) { | |
| const existing = await User.findOne({ apikey: customApiKey }); | |
| if (existing && existing._id.toString() !== userId) { | |
| return res.status(400).json({ success: false, error: 'API key already in use' }); | |
| } | |
| user.apikey = customApiKey; | |
| finalApiKey = customApiKey; | |
| } | |
| user.userRole = roleName; | |
| user.userRoleExpiresAt = expiresAt; | |
| user.limit = roleConfig[roleName].limit; | |
| user.premium = roleConfig[roleName].premium; | |
| user.premiumExpiredAt = expiresAt; | |
| await user.save(); | |
| const userRole = await UserRole.findOne({ userId: user._id }); | |
| if (userRole) { | |
| userRole.roleName = roleName; | |
| userRole.customApiKey = customApiKey || null; | |
| userRole.expiresAt = expiresAt; | |
| userRole.createdBy = req.user.username; | |
| await userRole.save(); | |
| } else { | |
| await UserRole.create({ | |
| userId: user._id, | |
| roleName, | |
| customApiKey: customApiKey || null, | |
| expiresAt, | |
| createdBy: req.user.username | |
| }); | |
| } | |
| await logActivity(user._id, user.username, 'role_assigned', `Role ${roleName} assigned by admin`, { | |
| roleName, | |
| limit: user.limit, | |
| expiresAt, | |
| customApiKey: customApiKey || 'none', | |
| assignedBy: req.user.username | |
| }); | |
| res.json({ | |
| success: true, | |
| message: `Role ${roleName} assigned successfully`, | |
| expiresAt, | |
| newLimit: user.limit, | |
| apiKey: user.apikey | |
| }); | |
| } catch (error) { | |
| console.error('Set role error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.get('/api/admin/users', authenticate, async (req, res) => { | |
| try { | |
| if (req.user.role !== 'admin') { | |
| return res.status(403).json({ success: false, error: 'Admin access required' }); | |
| } | |
| const users = await User.find({}, { | |
| password: 0 | |
| }).sort({ createdAt: -1 }); | |
| res.json({ | |
| success: true, | |
| users: users.map(user => ({ | |
| id: user._id, | |
| username: user.username, | |
| email: user.email, | |
| role: user.role, | |
| userRole: user.userRole, | |
| userRoleExpiresAt: user.userRoleExpiresAt, | |
| premium: user.premium, | |
| premiumExpiredAt: user.premiumExpiredAt, | |
| banned: user.banned, | |
| tempBanned: user.tempBanned, | |
| tempBanUntil: user.tempBanUntil, | |
| tempBanReason: user.tempBanReason, | |
| limit: user.limit, | |
| requests: user.requests, | |
| requestsToday: user.requestsToday, | |
| createdAt: user.createdAt, | |
| ipAddress: user.ipAddress | |
| })) | |
| }); | |
| } catch (error) { | |
| console.error('Admin users fetch error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.post('/api/admin/temp-ban', authenticate, async (req, res) => { | |
| try { | |
| if (req.user.role !== 'admin') { | |
| return res.status(403).json({ success: false, error: 'Admin access required' }); | |
| } | |
| const { userId, banUntil, reason } = req.body; | |
| if (!userId || !banUntil) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'User ID and ban until date are required' | |
| }); | |
| } | |
| const user = await User.findById(userId); | |
| if (!user) { | |
| return res.status(404).json({ success: false, error: 'User not found' }); | |
| } | |
| if (user.role === 'admin') { | |
| return res.status(403).json({ success: false, error: 'Cannot ban admin users' }); | |
| } | |
| const banUntilDate = new Date(banUntil); | |
| if (banUntilDate <= new Date()) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Ban until date must be in the future' | |
| }); | |
| } | |
| user.tempBanned = true; | |
| user.tempBanUntil = banUntilDate; | |
| user.tempBanReason = reason || 'No reason provided'; | |
| await user.save(); | |
| await logActivity(user._id, user.username, 'temp_banned', `User temporarily banned until ${banUntilDate.toLocaleString()}`, { | |
| bannedBy: req.user.username, | |
| banUntil: banUntilDate, | |
| reason: user.tempBanReason | |
| }); | |
| res.json({ | |
| success: true, | |
| message: `User ${user.username} temporarily banned until ${banUntilDate.toLocaleString()}` | |
| }); | |
| } catch (error) { | |
| console.error('Temp ban error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.post('/api/admin/remove-temp-ban', authenticate, async (req, res) => { | |
| try { | |
| if (req.user.role !== 'admin') { | |
| return res.status(403).json({ success: false, error: 'Admin access required' }); | |
| } | |
| const { userId } = req.body; | |
| if (!userId) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'User ID is required' | |
| }); | |
| } | |
| const user = await User.findById(userId); | |
| if (!user) { | |
| return res.status(404).json({ success: false, error: 'User not found' }); | |
| } | |
| user.tempBanned = false; | |
| user.tempBanUntil = null; | |
| user.tempBanReason = null; | |
| await user.save(); | |
| await logActivity(user._id, user.username, 'ban_removed', 'Temporary ban removed by admin', { | |
| removedBy: req.user.username | |
| }); | |
| res.json({ | |
| success: true, | |
| message: `Temporary ban removed for user ${user.username}` | |
| }); | |
| } catch (error) { | |
| console.error('Remove temp ban error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.post('/api/admin/redeem-code', authenticate, async (req, res) => { | |
| try { | |
| if (req.user.role !== 'admin') { | |
| return res.status(403).json({ success: false, error: 'Admin access required' }); | |
| } | |
| const { type, limitValue, codeExpired, premiumExpired } = req.body; | |
| if (!type || !codeExpired) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Type and code expiration date are required' | |
| }); | |
| } | |
| const codeExpiredDate = new Date(codeExpired); | |
| if (codeExpiredDate <= new Date()) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Code expiration date must be in the future' | |
| }); | |
| } | |
| if (type === 'limit' && (!limitValue || limitValue <= 0)) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Limit value must be greater than 0 for Limit Only type' | |
| }); | |
| } | |
| if (type === 'premium' && !premiumExpired) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Premium expiration date is required for Premium Only type' | |
| }); | |
| } | |
| if (type === 'both') { | |
| if (!limitValue || limitValue <= 0) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Limit value must be greater than 0 for Both type' | |
| }); | |
| } | |
| if (!premiumExpired) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Premium expiration date is required for Both type' | |
| }); | |
| } | |
| } | |
| let premiumExpiredDate = null; | |
| if (premiumExpired) { | |
| premiumExpiredDate = new Date(premiumExpired); | |
| if (premiumExpiredDate <= new Date()) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Premium expiration date must be in the future' | |
| }); | |
| } | |
| } | |
| const code = crypto.randomBytes(4).toString('hex').toUpperCase(); | |
| const redeemCode = new RedeemCode({ | |
| code, | |
| type, | |
| limitValue: parseInt(limitValue) || 0, | |
| codeExpired: codeExpiredDate, | |
| premiumExpired: premiumExpiredDate, | |
| createdBy: req.user.username | |
| }); | |
| await redeemCode.save(); | |
| await logActivity(req.user._id, req.user.username, 'redeem_code_created', `Created redeem code: ${code}`, { | |
| code, | |
| type, | |
| limitValue, | |
| codeExpired: codeExpiredDate, | |
| premiumExpired: premiumExpiredDate | |
| }); | |
| res.json({ success: true, code }); | |
| } catch (error) { | |
| console.error('Redeem code creation error:', error); | |
| res.status(500).json({ success: false, error: 'Internal server error' }); | |
| } | |
| }); | |
| app.use('/api', checkBanned); | |
| app.use('/api', apiRateLimit); | |
| let pluginsDir = path.join(__dirname, "plugins"); | |
| let isJavaScriptFile = (fileName) => /\.js$/.test(fileName); | |
| global.plugins = {}; | |
| const loadPlugins = () => { | |
| if (!fs.existsSync(pluginsDir)) { | |
| console.log('Plugins directory not found, creating...'); | |
| try { | |
| fs.mkdirSync(pluginsDir, { recursive: true }); | |
| } catch (error) { | |
| console.error('Error creating plugins directory:', error); | |
| } | |
| return; | |
| } | |
| for (let pluginFile of fs.readdirSync(pluginsDir).filter(isJavaScriptFile)) { | |
| try { | |
| delete require.cache[require.resolve(path.join(pluginsDir, pluginFile))]; | |
| global.plugins[pluginFile] = require(path.join(pluginsDir, pluginFile)); | |
| } catch (error) { | |
| console.error(`Error loading plugin ${pluginFile}:`, error.message); | |
| delete global.plugins[pluginFile]; | |
| } | |
| } | |
| console.log('Loaded plugins:', Object.keys(global.plugins)); | |
| }; | |
| global.reload = (event, filename) => { | |
| if (/\.js$/.test(filename)) { | |
| let fullFilePath = path.join(pluginsDir, filename); | |
| if (fullFilePath in require.cache) { | |
| delete require.cache[fullFilePath]; | |
| if (fs.existsSync(fullFilePath)) { | |
| console.log(`♻️ Re-requiring plugin '${filename}'`); | |
| } else { | |
| console.log(`🗑️ Deleted plugin '${filename}'`); | |
| return delete global.plugins[filename]; | |
| } | |
| } else { | |
| console.log(`🔁 Requiring new plugin '${filename}'`); | |
| } | |
| let errorCheck = syntaxError(fs.readFileSync(fullFilePath), filename); | |
| if (errorCheck) { | |
| console.error(`❌ Syntax error while loading '${filename}':\n${errorCheck}`); | |
| } else { | |
| try { | |
| global.plugins[filename] = require(fullFilePath); | |
| reloadHandler(); | |
| } catch (error) { | |
| console.error(`Error loading plugin ${filename}:`, error); | |
| } finally { | |
| global.plugins = Object.fromEntries(Object.entries(global.plugins).sort(([a], [b]) => a.localeCompare(b))); | |
| } | |
| } | |
| } | |
| }; | |
| const reloadHandler = () => { | |
| const routes = []; | |
| Object.keys(global.plugins).forEach(file => { | |
| const plugin = global.plugins[file]; | |
| if (plugin && plugin.enabled && plugin.routes && plugin.handler) { | |
| plugin.routes.forEach(route => { | |
| routes.push({ route, plugin, file }); | |
| }); | |
| } | |
| }); | |
| routes.forEach(({ route, plugin, file }) => { | |
| const method = (plugin.type || 'get').toLowerCase(); | |
| if (app[method] && typeof app[method] === 'function') { | |
| app[method](`/${route}`, (req, res, next) => { | |
| req.limitDeduction = plugin.limit || 1; | |
| next(); | |
| }, validateApiKey, async (req, res) => { | |
| const startTime = Date.now(); | |
| try { | |
| await plugin.handler(req, res); | |
| await RequestLog.create({ | |
| userId: req.apiUser._id, | |
| username: req.apiUser.username, | |
| apikey: req.apiUser.apikey, | |
| endpoint: route, | |
| ipAddress: req.realIP, | |
| userAgent: req.get('User-Agent'), | |
| success: true, | |
| responseTime: Date.now() - startTime, | |
| limitDeducted: req.limitDeducted || 1 | |
| }); | |
| } catch (error) { | |
| console.error(`Plugin ${plugin.name} error:`, error); | |
| await RequestLog.create({ | |
| userId: req.apiUser._id, | |
| username: req.apiUser.username, | |
| apikey: req.apiUser.apikey, | |
| endpoint: route, | |
| ipAddress: req.realIP, | |
| userAgent: req.get('User-Agent'), | |
| success: false, | |
| responseTime: Date.now() - startTime, | |
| limitDeducted: req.limitDeducted || 1 | |
| }); | |
| if (!res.headersSent) { | |
| res.status(500).json({ | |
| success: false, | |
| error: 'Plugin execution failed' | |
| }); | |
| } | |
| } | |
| }); | |
| console.log(`✅ Registered route: ${method.toUpperCase()} /${route} from ${file}`); | |
| } | |
| }); | |
| }; | |
| Object.freeze(global.reload); | |
| fs.watch(pluginsDir, global.reload); | |
| setInterval(async () => { | |
| const now = new Date(); | |
| if (now.getHours() === 0 && now.getMinutes() === 0) { | |
| console.log('Running daily limit reset at midnight...'); | |
| try { | |
| const users = await User.find({}); | |
| for (const user of users) { | |
| await resetUserLimitIfNeeded(user); | |
| } | |
| console.log('Daily limit reset completed for all users'); | |
| } catch (error) { | |
| console.error('Daily reset error:', error); | |
| } | |
| } | |
| }, 60000); | |
| initializeAdmin(); | |
| loadPlugins(); | |
| reloadHandler(); | |
| app.use((err, req, res, next) => { | |
| console.error('Unhandled error:', err); | |
| res.status(500).json({ | |
| success: false, | |
| error: 'Internal server error' | |
| }); | |
| }); | |
| const PORT = process.env.PORT || 7860; | |
| app.listen(PORT, () => { | |
| console.log(`DashX API Server running on port ${PORT}`); | |
| console.log('=== ADMIN ACCOUNT INFO ==='); | |
| console.log('Username: HERXA'); | |
| console.log('Password: #HIDDEN#'); | |
| console.log('API Key: #HIDDEN#'); | |
| console.log('Limit: 9999'); | |
| console.log('========================='); | |
| console.log('Database connected successfully'); | |
| console.log('Plugin auto-reload enabled'); | |
| console.log('Daily reset cron job started'); | |
| }); |