- Complete Express.js API server - MongoDB integration with Mongoose - Admin authentication and authorization - Room management (CRUD operations) - Booking management system - Guest management - Payment processing (Stripe integration) - Content management (pages, blog, gallery) - Media upload and management - Integration services (Booking.com, Expedia, Opera PMS, Trip.com) - Email notifications - Comprehensive logging and error handling
380 lines
10 KiB
JavaScript
380 lines
10 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const jwt = require('jsonwebtoken');
|
|
const Admin = require('../models/Admin');
|
|
const adminAuth = require('../middleware/adminAuth');
|
|
const { body, validationResult } = require('express-validator');
|
|
|
|
// @route POST /api/admin/login
|
|
// @desc Admin login
|
|
// @access Public
|
|
router.post('/login', [
|
|
body('username').notEmpty().trim().withMessage('Username or email is required'),
|
|
body('password').notEmpty().withMessage('Password is required')
|
|
], async (req, res) => {
|
|
try {
|
|
const errors = validationResult(req);
|
|
if (!errors.isEmpty()) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
errors: errors.array()
|
|
});
|
|
}
|
|
|
|
const { username, password } = req.body;
|
|
|
|
// Find admin by credentials
|
|
const admin = await Admin.findByCredentials(username, password);
|
|
|
|
// Generate JWT token
|
|
const token = jwt.sign(
|
|
{
|
|
id: admin._id,
|
|
email: admin.email,
|
|
role: admin.role,
|
|
isAdmin: true
|
|
},
|
|
process.env.JWT_SECRET || 'your-secret-key',
|
|
{ expiresIn: '7d' }
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Login successful',
|
|
data: {
|
|
token,
|
|
admin: {
|
|
id: admin._id,
|
|
username: admin.username,
|
|
email: admin.email,
|
|
firstName: admin.firstName,
|
|
lastName: admin.lastName,
|
|
fullName: admin.fullName,
|
|
avatar: admin.avatar,
|
|
role: admin.role,
|
|
permissions: admin.permissions
|
|
}
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('Admin login error:', error);
|
|
res.status(401).json({
|
|
success: false,
|
|
message: error.message || 'Invalid credentials'
|
|
});
|
|
}
|
|
});
|
|
|
|
// @route POST /api/admin/register
|
|
// @desc Register new admin (super-admin only)
|
|
// @access Private (Super Admin)
|
|
router.post('/register', adminAuth, [
|
|
body('username').notEmpty().trim().toLowerCase().withMessage('Username is required'),
|
|
body('email').isEmail().normalizeEmail().withMessage('Valid email is required'),
|
|
body('password').isLength({ min: 8 }).withMessage('Password must be at least 8 characters'),
|
|
body('firstName').notEmpty().trim().withMessage('First name is required'),
|
|
body('lastName').notEmpty().trim().withMessage('Last name is required'),
|
|
body('role').optional().isIn(['admin', 'editor', 'manager']).withMessage('Invalid role')
|
|
], async (req, res) => {
|
|
try {
|
|
const errors = validationResult(req);
|
|
if (!errors.isEmpty()) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
errors: errors.array()
|
|
});
|
|
}
|
|
|
|
// Check if requester is super admin
|
|
const requester = await Admin.findById(req.admin.id);
|
|
if (!requester || !requester.isSuperAdmin) {
|
|
return res.status(403).json({
|
|
success: false,
|
|
message: 'Only super admins can create new admin accounts'
|
|
});
|
|
}
|
|
|
|
const { username, email, password, firstName, lastName, role, permissions } = req.body;
|
|
|
|
// Check if admin already exists
|
|
const existingAdmin = await Admin.findOne({
|
|
$or: [{ username }, { email }]
|
|
});
|
|
|
|
if (existingAdmin) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: 'Admin with this username or email already exists'
|
|
});
|
|
}
|
|
|
|
// Create new admin
|
|
const newAdmin = new Admin({
|
|
username,
|
|
email,
|
|
password,
|
|
firstName,
|
|
lastName,
|
|
role: role || 'admin',
|
|
permissions: permissions || ['manage_content', 'manage_rooms', 'manage_bookings']
|
|
});
|
|
|
|
await newAdmin.save();
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
message: 'Admin account created successfully',
|
|
data: {
|
|
admin: {
|
|
id: newAdmin._id,
|
|
username: newAdmin.username,
|
|
email: newAdmin.email,
|
|
fullName: newAdmin.fullName,
|
|
role: newAdmin.role,
|
|
permissions: newAdmin.permissions
|
|
}
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('Admin registration error:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Error creating admin account'
|
|
});
|
|
}
|
|
});
|
|
|
|
// @route GET /api/admin/me
|
|
// @desc Get current admin profile
|
|
// @access Private (Admin)
|
|
router.get('/me', adminAuth, async (req, res) => {
|
|
try {
|
|
const admin = await Admin.findById(req.admin.id);
|
|
|
|
if (!admin) {
|
|
return res.status(404).json({
|
|
success: false,
|
|
message: 'Admin not found'
|
|
});
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: { admin }
|
|
});
|
|
} catch (error) {
|
|
console.error('Get admin profile error:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Error fetching admin profile'
|
|
});
|
|
}
|
|
});
|
|
|
|
// @route PUT /api/admin/me
|
|
// @desc Update current admin profile
|
|
// @access Private (Admin)
|
|
router.put('/me', adminAuth, [
|
|
body('firstName').optional().notEmpty().trim(),
|
|
body('lastName').optional().notEmpty().trim(),
|
|
body('email').optional().isEmail().normalizeEmail(),
|
|
body('avatar').optional().isURL()
|
|
], async (req, res) => {
|
|
try {
|
|
const errors = validationResult(req);
|
|
if (!errors.isEmpty()) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
errors: errors.array()
|
|
});
|
|
}
|
|
|
|
const { firstName, lastName, email, avatar } = req.body;
|
|
const updateFields = {};
|
|
|
|
if (firstName) updateFields.firstName = firstName;
|
|
if (lastName) updateFields.lastName = lastName;
|
|
if (email) updateFields.email = email;
|
|
if (avatar) updateFields.avatar = avatar;
|
|
|
|
const admin = await Admin.findByIdAndUpdate(
|
|
req.admin.id,
|
|
{ $set: updateFields },
|
|
{ new: true, runValidators: true }
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Profile updated successfully',
|
|
data: { admin }
|
|
});
|
|
} catch (error) {
|
|
console.error('Update admin profile error:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Error updating profile'
|
|
});
|
|
}
|
|
});
|
|
|
|
// @route PUT /api/admin/change-password
|
|
// @desc Change admin password
|
|
// @access Private (Admin)
|
|
router.put('/change-password', adminAuth, [
|
|
body('currentPassword').notEmpty().withMessage('Current password is required'),
|
|
body('newPassword').isLength({ min: 8 }).withMessage('New password must be at least 8 characters')
|
|
], async (req, res) => {
|
|
try {
|
|
const errors = validationResult(req);
|
|
if (!errors.isEmpty()) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
errors: errors.array()
|
|
});
|
|
}
|
|
|
|
const { currentPassword, newPassword } = req.body;
|
|
const admin = await Admin.findById(req.admin.id);
|
|
|
|
if (!admin) {
|
|
return res.status(404).json({
|
|
success: false,
|
|
message: 'Admin not found'
|
|
});
|
|
}
|
|
|
|
// Verify current password
|
|
const isMatch = await admin.comparePassword(currentPassword);
|
|
if (!isMatch) {
|
|
return res.status(401).json({
|
|
success: false,
|
|
message: 'Current password is incorrect'
|
|
});
|
|
}
|
|
|
|
// Update password
|
|
admin.password = newPassword;
|
|
await admin.save();
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Password changed successfully'
|
|
});
|
|
} catch (error) {
|
|
console.error('Change password error:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Error changing password'
|
|
});
|
|
}
|
|
});
|
|
|
|
// @route GET /api/admin/list
|
|
// @desc Get all admins (super-admin only)
|
|
// @access Private (Super Admin)
|
|
router.get('/list', adminAuth, async (req, res) => {
|
|
try {
|
|
const requester = await Admin.findById(req.admin.id);
|
|
if (!requester || !requester.isSuperAdmin) {
|
|
return res.status(403).json({
|
|
success: false,
|
|
message: 'Only super admins can view admin list'
|
|
});
|
|
}
|
|
|
|
const admins = await Admin.find().sort({ createdAt: -1 });
|
|
|
|
res.json({
|
|
success: true,
|
|
data: { admins }
|
|
});
|
|
} catch (error) {
|
|
console.error('Get admin list error:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Error fetching admin list'
|
|
});
|
|
}
|
|
});
|
|
|
|
// @route GET /api/admin/stats
|
|
// @desc Get dashboard statistics
|
|
// @access Private (Admin)
|
|
router.get('/stats', adminAuth, async (req, res) => {
|
|
try {
|
|
const Booking = require('../models/Booking');
|
|
const Room = require('../models/Room');
|
|
const Guest = require('../models/Guest');
|
|
const BlogPost = require('../models/BlogPost');
|
|
|
|
const [
|
|
totalBookings,
|
|
activeBookings,
|
|
totalRooms,
|
|
availableRooms,
|
|
totalGuests,
|
|
totalBlogPosts
|
|
] = await Promise.all([
|
|
Booking.countDocuments(),
|
|
Booking.countDocuments({ status: { $in: ['Confirmed', 'Checked In'] } }),
|
|
Room.countDocuments(),
|
|
Room.countDocuments({ status: 'Available', isActive: true }),
|
|
Guest.countDocuments(),
|
|
BlogPost.countDocuments({ status: 'published' })
|
|
]);
|
|
|
|
// Get revenue for current month
|
|
const startOfMonth = new Date(new Date().getFullYear(), new Date().getMonth(), 1);
|
|
const revenueData = await Booking.aggregate([
|
|
{
|
|
$match: {
|
|
status: { $in: ['Confirmed', 'Checked In', 'Checked Out'] },
|
|
createdAt: { $gte: startOfMonth }
|
|
}
|
|
},
|
|
{
|
|
$group: {
|
|
_id: null,
|
|
totalRevenue: { $sum: '$totalAmount' }
|
|
}
|
|
}
|
|
]);
|
|
|
|
const monthlyRevenue = revenueData.length > 0 ? revenueData[0].totalRevenue : 0;
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
stats: {
|
|
bookings: {
|
|
total: totalBookings,
|
|
active: activeBookings
|
|
},
|
|
rooms: {
|
|
total: totalRooms,
|
|
available: availableRooms
|
|
},
|
|
guests: totalGuests,
|
|
blogPosts: totalBlogPosts,
|
|
revenue: {
|
|
monthly: monthlyRevenue
|
|
}
|
|
}
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('Get stats error:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Error fetching statistics'
|
|
});
|
|
}
|
|
});
|
|
|
|
// Health check
|
|
router.get('/health', (req, res) => {
|
|
res.json({ success: true, service: 'admin', status: 'ok' });
|
|
});
|
|
|
|
module.exports = router;
|