| 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 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 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('/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'); |
| }); |