// controllers/authController.js

const Admin = require("../models/Admin");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");

// Helper function to generate JWT
const generateToken = (admin) => {
  return jwt.sign(
    { _id: admin._id, role: admin.role },
    process.env.JWT_SECRET,
    { expiresIn: "1h" }
  );
};

// Register Controller
exports.register = async (req, res) => {
  try {
    const { username, email, password } = req.body;

    // Validate input
    if (!username || !email || !password) {
      return res.status(400).json({ error: "All fields are required." });
    }

    // Check if email or username already exists
    const existingAdmin = await Admin.findOne({
      $or: [{ email }, { username }],
    });

    if (existingAdmin) {
      return res.status(400).json({ error: "Admin already exists." });
    }

    // Hash password
    const hashedPassword = await bcrypt.hash(password, 10);

    // Create new admin
    const newAdmin = new Admin({
      username,
      email,
      password: hashedPassword,
    });

    await newAdmin.save();

    res.status(201).json({ message: "Admin registered successfully." });
  } catch (error) {
    console.error("Registration Error:", error);
    res.status(500).json({ error: "Server error during registration." });
  }
};

// Login Controller
exports.login = async (req, res) => {
  try {
    const { email, password } = req.body;

    // Validate input
    if (!email || !password) {
      return res.status(400).json({ error: "All fields are required." });
    }

    // Find admin by email
    const admin = await Admin.findOne({ email });

    if (!admin) {
      return res.status(400).json({ error: "Invalid credentials." });
    }

    // Compare passwords
    if (!admin.password) {
      // Handle users without passwords (e.g., social logins)
      return res
        .status(400)
        .json({ error: "Please log in using a social account." });
    }

    const isMatch = await bcrypt.compare(password, admin.password);

    if (!isMatch) {
      return res.status(400).json({ error: "Invalid credentials." });
    }

    // Generate JWT
    const token = generateToken(admin);

    // Determine if the user is a superuser
    if (admin.role === "superuser") {
      res.json({ token, role: "superuser" });
    } else {
      res.json({ token, role: admin.role });
    }
  } catch (error) {
    console.error("Login Error:", error);
    res.status(500).json({ error: "Server error during login." });
  }
};

// Logout Controller
exports.logout = (req, res) => {
  // Since JWT is stateless, logout can be handled on the client by deleting the token.
  // To implement server-side logout (like token blacklisting), additional setup is required.
  res.json({
    message: "Logout successful. Please delete the token on client side.",
  });
};

// Verify Token Controller
exports.verifyToken = async (req, res) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith("Bearer ")) {
    return res
      .status(401)
      .json({ message: "Authorization header missing or malformed." });
  }
  const token = authHeader.split(" ")[1];

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    const admin = await Admin.findById(decoded._id);
    if (!admin) {
      return res.status(401).json({ message: "Invalid token." });
    }
    res.json({ _id: admin._id, role: admin.role });
  } catch (err) {
    return res.status(401).json({ message: "Invalid token." });
  }
};

// Social Login Callback Controller
exports.socialLoginCallback = async (req, res) => {
  try {
    const admin = req.user;

    // Generate JWT
    const token = generateToken(admin);

    // Redirect or respond with token
    // For APIs, it's common to send the token in the response or set it as a query parameter
    // For simplicity, we'll redirect to a front-end route with the token as a query param
    res.redirect(
      `${process.env.FRONTEND_URL}/social-login-success?token=${token}`
    );
  } catch (error) {
    console.error("Social Login Callback Error:", error);
    res.status(500).json({ error: "Server error during social login." });
  }
};

// controllers/adminController.js
const Article = require("../models/Article");
const News = require("../models/News");
const { handleControllerError } = require("../utils/errorHandler");
const fs = require("fs");
const path = require("path");

// Helper function to delete files
const deleteFile = (filePath) => {
  fs.unlink(path.join(__dirname, "..", filePath), (err) => {
    if (err) console.error(`Failed to delete file ${filePath}:`, err);
  });
};

// --- Articles CRUD Operations ---

// Create a new article
const addArticle = async (req, res) => {
  try {
    const { title, content, author } = req.body;
    const headingImage = req.file ? `/uploads/${req.file.filename}` : null;

    if (!title || !content || !author) {
      return res.status(400).json({ message: "All fields are required." });
    }

    const newArticle = new Article({
      title,
      content,
      author,
      headingImage,
      createdBy: req.admin._id,
    });

    await newArticle.save();

    res.status(201).json({
      message: "Article created successfully.",
      article: newArticle,
    });
  } catch (error) {
    handleControllerError(error, res, "addArticle");
  }
};

// Get all articles
const getAllArticles = async (req, res) => {
  try {
    const articles = await Article.find()
      .populate("createdBy", "username")
      .sort({ createdAt: -1 });
    res.json(articles);
  } catch (error) {
    handleControllerError(error, res, "getAllArticles");
  }
};

// Get a single article by ID
const getArticleById = async (req, res) => {
  try {
    const { id } = req.params;
    const article = await Article.findById(id).populate(
      "createdBy",
      "username"
    );

    if (!article) {
      return res.status(404).json({ message: "Article not found." });
    }

    res.json(article);
  } catch (error) {
    handleControllerError(error, res, "getArticleById");
  }
};

// Update an existing article
const updateArticle = async (req, res) => {
  try {
    const { id } = req.params;
    const { title, content, author } = req.body;
    let headingImage;

    const article = await Article.findById(id);
    if (!article) {
      return res.status(404).json({ message: "Article not found." });
    }

    if (req.file) {
      // Delete the old image if it exists
      if (article.headingImage) {
        deleteFile(article.headingImage);
      }
      headingImage = `/uploads/${req.file.filename}`;
      article.headingImage = headingImage;
    }

    // Update fields if provided
    if (title) article.title = title;
    if (content) article.content = content;
    if (author) article.author = author;

    await article.save();

    res.json({ message: "Article updated successfully.", article });
  } catch (error) {
    handleControllerError(error, res, "updateArticle");
  }
};

// Delete an article
const deleteArticle = async (req, res) => {
  try {
    const { id } = req.params;

    const article = await Article.findByIdAndDelete(id);
    if (!article) {
      return res.status(404).json({ message: "Article not found." });
    }

    // Delete the heading image if it exists
    if (article.headingImage) {
      deleteFile(article.headingImage);
    }

    res.json({ message: "Article deleted successfully." });
  } catch (error) {
    handleControllerError(error, res, "deleteArticle");
  }
};
const reviewArticle = async (req, res) => {
  try {
    const { id } = req.params;
    const { review } = req.body;

    const article = await Article.findById(id);
    if (!article) {
      return res.status(404).json({ message: "Article not found." });
    }

    // Assuming you have a reviews array in your Article model
    article.reviews.push({
      reviewer: req.admin._id,
      review,
      reviewedAt: new Date(),
    });

    await article.save();

    res
      .status(200)
      .json({ message: "Article reviewed successfully.", article });
  } catch (error) {
    handleControllerError(error, res, "reviewArticle");
  }
};


// --- News CRUD Operations ---

// Create a new news item
const addNews = async (req, res) => {
  try {
    const { title, content } = req.body;
    const headingImage = req.files["headingImage"]
      ? `/uploads/${req.files["headingImage"][0].filename}`
      : null;
    const images = req.files["images"]
      ? req.files["images"].map((file) => `/uploads/${file.filename}`)
      : [];

    if (!title || !content) {
      return res
        .status(400)
        .json({ message: "Title and content are required." });
    }

    const newNews = new News({
      title,
      content,
      headingImage,
      images,
      createdBy: req.admin._id,
    });

    await newNews.save();

    res.status(201).json({
      message: "News created successfully.",
      news: newNews,
    });
  } catch (error) {
    handleControllerError(error, res, "addNews");
  }
};

// Get all news
const getAllNews = async (req, res) => {
  try {
    const newsList = await News.find()
      .populate("createdBy", "username")
      .sort({ createdAt: -1 });
    res.json(newsList);
  } catch (error) {
    handleControllerError(error, res, "getAllNews");
  }
};

// Get a single news item by ID
const getNewsById = async (req, res) => {
  try {
    const { id } = req.params;
    const news = await News.findById(id).populate("createdBy", "username");

    if (!news) {
      return res.status(404).json({ message: "News not found." });
    }

    res.json(news);
  } catch (error) {
    handleControllerError(error, res, "getNewsById");
  }
};

// Update an existing news item
const updateNews = async (req, res) => {
  try {
    const { id } = req.params;
    const { title, content } = req.body;
    let headingImage;
    let images;

    const news = await News.findById(id);
    if (!news) {
      return res.status(404).json({ message: "News not found." });
    }

    if (req.files["headingImage"]) {
      // Delete the old heading image if it exists
      if (news.headingImage) {
        deleteFile(news.headingImage);
      }
      headingImage = `/uploads/${req.files["headingImage"][0].filename}`;
      news.headingImage = headingImage;
    }

    if (req.files["images"] && req.files["images"].length > 0) {
      // Optionally delete old carousel images
      news.images.forEach((img) => deleteFile(img));
      images = req.files["images"].map((file) => `/uploads/${file.filename}`);
      news.images = images;
    }

    // Update fields if provided
    if (title) news.title = title;
    if (content) news.content = content;

    await news.save();

    res.json({ message: "News updated successfully.", news });
  } catch (error) {
    handleControllerError(error, res, "updateNews");
  }
};

// Delete a news item
const deleteNews = async (req, res) => {
  try {
    const { id } = req.params;

    const news = await News.findByIdAndDelete(id);
    if (!news) {
      return res.status(404).json({ message: "News not found." });
    }

    // Delete heading and carousel images if they exist
    if (news.headingImage) {
      deleteFile(news.headingImage);
    }
    news.images.forEach((img) => deleteFile(img));

    res.json({ message: "News deleted successfully." });
  } catch (error) {
    handleControllerError(error, res, "deleteNews");
  }
};

module.exports = {
  // Articles
  addArticle,
  getAllArticles,
  getArticleById,
  updateArticle,
  deleteArticle,

  // News
  addNews,
  getAllNews,
  getNewsById,
  updateNews,
  deleteNews,
  reviewArticle,
};

// config/passport.js
const passport = require("passport");
const JwtStrategy = require("passport-jwt").Strategy;
const ExtractJwt = require("passport-jwt").ExtractJwt;
const GoogleStrategy = require("passport-google-oauth20").Strategy;
const FacebookStrategy = require("passport-facebook").Strategy;
const TwitterStrategy = require("passport-twitter").Strategy;
const Admin = require("../models/Admin");
const dotenv = require("dotenv");

dotenv.config();

// JWT Options
const opts = {
  jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
  secretOrKey: process.env.JWT_SECRET,
};

// JWT Strategy
passport.use(
  new JwtStrategy(opts, async (jwt_payload, done) => {
    try {
      const admin = await Admin.findById(jwt_payload._id);
      if (admin) {
        return done(null, admin);
      }
      return done(null, false);
    } catch (err) {
      return done(err, false);
    }
  })
);

// Google Strategy
passport.use(
  new GoogleStrategy(
    {
      clientID: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      callbackURL: "/auth/google/callback",
    },
    async (accessToken, refreshToken, profile, done) => {
      try {
        // Check if admin with this Google ID exists
        let admin = await Admin.findOne({ googleId: profile.id });

        if (admin) {
          return done(null, admin);
        }

        // If not, check if an admin with the same email exists
        admin = await Admin.findOne({ email: profile.emails[0].value });

        if (admin) {
          // Link Google account
          admin.googleId = profile.id;
          admin.profilePicture = profile.photos[0].value;
          await admin.save();
          return done(null, admin);
        }

        // If no admin exists, create a new one
        const newAdmin = new Admin({
          username: profile.displayName,
          email: profile.emails[0].value,
          googleId: profile.id,
          profilePicture: profile.photos[0].value,
        });

        await newAdmin.save();
        return done(null, newAdmin);
      } catch (error) {
        return done(error, false);
      }
    }
  )
);

// Facebook Strategy
passport.use(
  new FacebookStrategy(
    {
      clientID: process.env.FACEBOOK_APP_ID,
      clientSecret: process.env.FACEBOOK_APP_SECRET,
      callbackURL: "/auth/facebook/callback",
      profileFields: ["id", "displayName", "emails", "photos"],
    },
    async (accessToken, refreshToken, profile, done) => {
      try {
        let admin = await Admin.findOne({ facebookId: profile.id });

        if (admin) {
          return done(null, admin);
        }

        // Check if admin with the same email exists
        const email = profile.emails ? profile.emails[0].value : null;
        if (email) {
          admin = await Admin.findOne({ email });

          if (admin) {
            // Link Facebook account
            admin.facebookId = profile.id;
            admin.profilePicture = profile.photos[0].value;
            await admin.save();
            return done(null, admin);
          }
        }

        // Create a new admin
        const newAdmin = new Admin({
          username: profile.displayName,
          email: email,
          facebookId: profile.id,
          profilePicture: profile.photos[0].value,
        });

        await newAdmin.save();
        return done(null, newAdmin);
      } catch (error) {
        return done(error, false);
      }
    }
  )
);

// Twitter Strategy
passport.use(
  new TwitterStrategy(
    {
      consumerKey: process.env.TWITTER_CONSUMER_KEY,
      consumerSecret: process.env.TWITTER_CONSUMER_SECRET,
      callbackURL: "/auth/twitter/callback",
      includeEmail: true,
    },
    async (token, tokenSecret, profile, done) => {
      try {
        let admin = await Admin.findOne({ twitterId: profile.id });

        if (admin) {
          return done(null, admin);
        }

        // Note: Twitter may not always provide email
        const email = profile.emails ? profile.emails[0].value : null;
        if (email) {
          admin = await Admin.findOne({ email });

          if (admin) {
            // Link Twitter account
            admin.twitterId = profile.id;
            admin.profilePicture = profile.photos[0].value;
            await admin.save();
            return done(null, admin);
          }
        }

        // Create a new admin
        const newAdmin = new Admin({
          username: profile.displayName,
          email: email, // May be undefined
          twitterId: profile.id,
          profilePicture: profile.photos[0].value,
        });

        await newAdmin.save();
        return done(null, newAdmin);
      } catch (error) {
        return done(error, false);
      }
    }
  )
);

// controllers/userController.js
const Suggestion = require("../models/Suggestion");
const { handleControllerError } = require("../utils/errorHandler");

// Submit a suggestion
const submitSuggestion = async (req, res) => {
  try {
    const { articleId, content } = req.body;

    if (!articleId || !content) {
      return res
        .status(400)
        .json({ message: "Article ID and content are required." });
    }

    const suggestion = new Suggestion({
      article: articleId,
      user: req.admin._id,
      content,
    });

    await suggestion.save();

    res
      .status(201)
      .json({ message: "Suggestion submitted successfully.", suggestion });
  } catch (error) {
    handleControllerError(error, res, "submitSuggestion");
  }
};

// Get all suggestions (for admins and reviewers)
const getAllSuggestions = async (req, res) => {
  try {
    const suggestions = await Suggestion.find()
      .populate("article", "title")
      .populate("user", "username")
      .sort({ createdAt: -1 });
    res.json(suggestions);
  } catch (error) {
    handleControllerError(error, res, "getAllSuggestions");
  }
};

// Update suggestion status (for admins and reviewers)
const updateSuggestionStatus = async (req, res) => {
  try {
    const { suggestionId, status } = req.body;

    if (!suggestionId || !status) {
      return res
        .status(400)
        .json({ message: "Suggestion ID and status are required." });
    }

    const validStatuses = ["pending", "addressed", "rejected"];
    if (!validStatuses.includes(status)) {
      return res
        .status(400)
        .json({
          message: `Status must be one of: ${validStatuses.join(", ")}`,
        });
    }

    const suggestion = await Suggestion.findById(suggestionId);
    if (!suggestion) {
      return res.status(404).json({ message: "Suggestion not found." });
    }

    suggestion.status = status;
    await suggestion.save();

    res.json({
      message: "Suggestion status updated successfully.",
      suggestion,
    });
  } catch (error) {
    handleControllerError(error, res, "updateSuggestionStatus");
  }
};

module.exports = {
  submitSuggestion,
  getAllSuggestions,
  updateSuggestionStatus,
};
// middlewares/auth.js
const jwt = require("jsonwebtoken");
const Admin = require("../models/Admin");

const verifyToken = async (req, res, next) => {
  const authHeader = req.headers.authorization || req.headers.Authorization;

  if (!authHeader || !authHeader.startsWith("Bearer ")) {
    return res
      .status(401)
      .json({ message: "Access denied. No token provided." });
  }

  const token = authHeader.split(" ")[1];

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);

    // Fetch admin from DB
    const admin = await Admin.findById(decoded._id);
    if (!admin) {
      return res.status(401).json({ message: "Invalid token." });
    }

    req.admin = admin; // Attach admin to the request object
    next();
  } catch (error) {
    console.error("Token verification failed:", error);
    res.status(400).json({ message: "Invalid token." });
  }
};

module.exports = { verifyToken };

// middlewares/authorizeRoles.js
const Admin = require("../models/Admin");

/**
 * Middleware to authorize based on roles.
 * @param  {...string} roles Allowed roles for the route
 */
const authorizeRoles = (...roles) => {
  return async (req, res, next) => {
    try {
      const admin = await Admin.findById(req.admin._id);
      if (!admin) {
        return res.status(401).json({ message: "Unauthorized" });
      }

      if (!roles.includes(admin.role)) {
        return res.status(403).json({ message: "Forbidden: Access denied" });
      }

      next();
    } catch (error) {
      console.error("Authorization Middleware Error:", error);
      res.status(500).json({ message: "Server Error" });
    }
  };
};

module.exports = authorizeRoles;

// middlewares/authMiddleware.js
const jwt = require("jsonwebtoken");

const authenticateToken = (req, res, next) => {
  const token = req.header("Authorization");
  if (!token) return res.status(401).json({ error: "Access Denied" });

  try {
    const verified = jwt.verify(token, process.env.JWT_SECRET);
    req.user = verified;
    next();
  } catch (err) {
    res.status(400).json({ error: "Invalid Token" });
  }
};

module.exports = authenticateToken;

const mongoose = require("mongoose");

const AdminSchema = new mongoose.Schema(
  {
    username: {
      type: String,
      required: true,
      unique: true,
    },
    email: {
      type: String,
      required: true,
      unique: true,
    },
    password: {
      type: String,
      // Not required for social logins
    },
    googleId: {
      type: String,
    },
    facebookId: {
      type: String,
    },
    twitterId: {
      type: String,
    },
    profilePicture: {
      type: String,
    },
    role: {
      type: String,
      enum: ["superuser", "admin", "publisher", "reviewer", "user"],
      default: "user",
    },
  },
  { timestamps: true }
);

module.exports = mongoose.model("Admin", AdminSchema);

const Article = require("../models/Article");
const { handleControllerError } = require("../utils/errorHandler");

// Function to handle uploading a new article
const addArticle = async (req, res) => {
  try {
    const { title, content, author } = req.body;
    const headingImage = req.file ? `/uploads/${req.file.filename}` : null;

    if (!title || !content || !author) {
      return res.status(400).json({ message: "All fields are required." });
    }

    const newArticle = new Article({
      title,
      content,
      author,
      headingImage,
    });

    await newArticle.save();

    res
      .status(201)
      .json({
        message: "Article uploaded successfully.",
        articleId: newArticle._id,
      });
  } catch (error) {
    handleControllerError(error, res, "addArticle");
  }
};

// Function to fetch all articles
const getArticles = async (req, res) => {
  try {
    const articles = await Article.find().sort({ createdAt: -1 });
    res.json(articles);
  } catch (error) {
    handleControllerError(error, res, "getArticles");
  }
};

// Function to fetch a single article by ID
const getArticleById = async (req, res) => {
  try {
    const { id } = req.params;
    const article = await Article.findById(id);

    if (!article) {
      return res.status(404).json({ message: "Article not found." });
    }

    res.json(article);
  } catch (error) {
    handleControllerError(error, res, "getArticleById");
  }
};

module.exports = {
  getArticles,
  addArticle,
  getArticleById,
};

// controllers/newsController.js

const { getCachedData } = require("../utils/cacheHelper");
const axiosInstance = require("../utils/axiosInstance");

// Centralized Error Handling for Controller
const handleControllerError = (error, res, functionName) => {
  if (error.response) {
    // API responded with a status code outside 2xx
    console.error(
      `Error in ${functionName}: ${error.response.status} - ${JSON.stringify(
        error.response.data
      )}`
    );
    res.status(error.response.status).json({
      message: error.response.data.message || `Error in ${functionName}`,
      details: error.response.data,
    });
  } else if (error.request) {
    // No response received from API
    console.error(`No response received in ${functionName}:`, error.request);
    res.status(503).json({
      message: `No response from Football API during ${functionName}`,
    });
  } else {
    // Other errors
    console.error(`Error in ${functionName}:`, error.message);
    res
      .status(500)
      .json({ message: `Internal server error in ${functionName}` });
  }
};

// Function to fetch top 10 players for a specific club
const getTopPlayersByClub = async (req, res) => {
  const { club } = req.params; // e.g., 'barcelona', 'realmadrid'
  const cacheKey = `top_players_${club.toLowerCase()}`;
  const fetchFn = async () => {
    try {
      const response = await axiosInstance.get(
        `/players/${encodeURIComponent(club.toLowerCase())}`
      );
      // Assuming the API returns an array of player objects
      const topPlayers = response.data.slice(0, 10); // Get top 10 players
      return topPlayers;
    } catch (error) {
      throw error;
    }
  };

  try {
    const topPlayers = await getCachedData(cacheKey, fetchFn);
    res.status(200).json({
      club: club.charAt(0).toUpperCase() + club.slice(1),
      topPlayers,
    });
  } catch (error) {
    handleControllerError(error, res, `getTopPlayersByClub ${club}`);
  }
};

// Existing functions (getAllPlayers, getPlayersTransfers, etc.)

const getAllPlayers = async (req, res) => {
  const cacheKey = "all_players";
  const fetchFn = async () => {
    const response = await axiosInstance.get("/players");
    return response.data; // Assuming it's an array of players
  };

  try {
    const players = await getCachedData(cacheKey, fetchFn);
    res.status(200).json(players);
  } catch (error) {
    handleControllerError(error, res, "getAllPlayers");
  }
};

const getPlayersTransfers = async (req, res) => {
  const cacheKey = "players_transfers";
  const fetchFn = async () => {
    const response = await axiosInstance.get("/players/transfers");
    return response.data; // Assuming it's an array of transfers
  };

  try {
    const transfers = await getCachedData(cacheKey, fetchFn);
    res.status(200).json(transfers);
  } catch (error) {
    handleControllerError(error, res, "getPlayersTransfers");
  }
};

const getPlayersNews = async (req, res) => {
  const cacheKey = "players_news";
  const fetchFn = async () => {
    const response = await axiosInstance.get("/players/news");
    return response.data; // Assuming it's an array of news articles
  };

  try {
    const news = await getCachedData(cacheKey, fetchFn);
    res.status(200).json(news);
  } catch (error) {
    handleControllerError(error, res, "getPlayersNews");
  }
};

const getBettingLaLiga = async (req, res) => {
  const cacheKey = "betting_laliga";
  const fetchFn = async () => {
    const response = await axiosInstance.get("/betting/laliga");
    return response.data; // Assuming it's betting information
  };

  try {
    const laLigaData = await getCachedData(cacheKey, fetchFn);
    res.status(200).json(laLigaData);
  } catch (error) {
    handleControllerError(error, res, "getBettingLaLiga");
  }
};

const getBettingPremierLeague = async (req, res) => {
  const cacheKey = "betting_premierleague";
  const fetchFn = async () => {
    const response = await axiosInstance.get("/betting/premuire-league"); // Fixed endpoint spelling
    return response.data; // Assuming it's betting information
  };

  try {
    const premierLeagueData = await getCachedData(cacheKey, fetchFn);
    res.status(200).json(premierLeagueData);
  } catch (error) {
    handleControllerError(error, res, "getBettingPremierLeague");
  }
};

// ... other functions as needed

module.exports = {
  getAllPlayers,
  getPlayersTransfers,
  getPlayersNews,
  getTopPlayersByClub,
  getBettingLaLiga,
  getBettingPremierLeague,
  // ... other exports
};

Token verification failed: TokenExpiredError: jwt expired
    at C:\Development\Node js programs\Football App\football-app-backend\node_modules\jsonwebtoken\verify.js:190:21
    at getSecret (C:\Development\Node js programs\Football App\football-app-backend\node_modules\jsonwebtoken\verify.js:97:14)
    at module.exports [as verify] (C:\Development\Node js programs\Football App\football-app-backend\node_modules\jsonwebtoken\verify.js:101:10)
    at verifyToken (C:\Development\Node js programs\Football App\football-app-backend\middlewares\auth.js:17:25)
    at Layer.handle [as handle_request] (C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\layer.js:95:5)
    at trim_prefix (C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\index.js:328:13)
    at C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\index.js:286:9
    at Function.process_params (C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\index.js:346:12)
    at next (C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\index.js:280:10)
    at Function.handle (C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\index.js:175:3) {
  expiredAt: 2024-11-14T18:52:58.000Z
}

// middlewares/upload.js
const multer = require("multer");
const path = require("path");

// Define storage for uploaded files
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, "uploads/"); // Ensure this directory exists
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
    cb(null, uniqueSuffix + path.extname(file.originalname));
  },
});

// File filter to accept only images and GIFs
const fileFilter = (req, file, cb) => {
  const allowedTypes = /jpeg|jpg|png|gif/;
  const extname = allowedTypes.test(
    path.extname(file.originalname).toLowerCase()
  );
  const mimetype = allowedTypes.test(file.mimetype);

  if (mimetype && extname) {
    return cb(null, true);
  }
  cb(new Error("Only images and GIFs are allowed"));
};

// Initialize multer
const upload = multer({
  storage,
  fileFilter,
  limits: { fileSize: 5 * 1024 * 1024 }, // 5MB limit, adjust as needed
});

module.exports = upload;

// routes/admin.js
const express = require("express");
const router = express.Router();
const adminController = require("../controllers/adminController");
const { verifyToken } = require("../middlewares/auth");
const authorizeRoles = require("../middlewares/authorizeRoles");
const upload = require("../middlewares/upload");

// Protect all admin routes
router.use(verifyToken);
router.use(authorizeRoles("admin", "superuser"));

// --- Article Routes ---

// Publishers can create and update articles
router.post(
  "/articles",
  authorizeRoles("publisher", "admin", "superuser"),
  upload.single("headingImage"),
  adminController.addArticle
);

// Publishers and Admins can update articles
router.put(
  "/articles/:id",
  authorizeRoles("publisher", "admin", "superuser"),
  upload.single("headingImage"),
  adminController.updateArticle
);

// SuperUsers and Admins can delete any article
router.delete(
  "/articles/:id",
  authorizeRoles("admin", "superuser"),
  adminController.deleteArticle
);

// Reviewers can review articles
router.post(
  "/articles/:id/review",
  authorizeRoles("reviewer", "admin", "superuser"),
  adminController.reviewArticle
);

// Users can read articles
router.get(
  "/articles",
  authorizeRoles("user", "reviewer", "publisher", "admin", "superuser"),
  adminController.getAllArticles
);
router.get(
  "/articles/:id",
  authorizeRoles("user", "reviewer", "publisher", "admin", "superuser"),
  adminController.getArticleById
);


// --- News Routes ---

// Similar role-based access can be applied to news routes

module.exports = router;
// routes/articleRoutes.js

const express = require("express");
const {
  getAllArticles, // Corrected function name
  addArticle,
  getArticleById,
} = require("../controllers/adminController"); // Corrected path if using adminController.js

const router = express.Router();
const fs = require("fs");
const authenticateToken = require("../middlewares/authMiddleware");
const multer = require("multer");
const path = require("path");

// Configure Multer for file uploads
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, "uploads/"); // Temporary upload directory
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
    cb(null, `${uniqueSuffix}-${file.originalname}`);
  },
});

const upload = multer({
  storage,
  limits: { fileSize: 5 * 1024 * 1024 }, // Limit file size to 5MB
  fileFilter: (req, file, cb) => {
    const filetypes = /jpeg|jpg|png|gif/;
    const mimetype = filetypes.test(file.mimetype);
    const extname = filetypes.test(
      path.extname(file.originalname).toLowerCase()
    );

    if (mimetype && extname) {
      return cb(null, true);
    }
    cb(new Error("Only image files are allowed!"));
  },
});

// Routes for user articles
router.get("/", getAllArticles); // Updated to match the controller function name
router.post(
  "/add",
  authenticateToken,
  upload.single("headingImage"),
  addArticle
); // POST /api/articles/add
router.get("/:id", getArticleById); // GET /api/articles/:id

module.exports = router;

// routes/auth.js

const express = require("express");
const router = express.Router();
const passport = require("passport");
const authController = require("../controllers/authController");

// Local Registration
router.post("/register", authController.register);

// Local Login
router.post("/login", authController.login);

// Logout
router.post("/logout", authController.logout);

// Verify Token
router.post("/verify-token", authController.verifyToken);

// Google OAuth Routes
router.get(
  "/google",
  passport.authenticate("google", { scope: ["profile", "email"] })
);

router.get(
  "/google/callback",
  passport.authenticate("google", {
    session: false,
    failureRedirect: "/login",
  }),
  authController.socialLoginCallback
);

// Facebook OAuth Routes
router.get(
  "/facebook",
  passport.authenticate("facebook", { scope: ["email"] })
);

router.get(
  "/facebook/callback",
  passport.authenticate("facebook", {
    session: false,
    failureRedirect: "/login",
  }),
  authController.socialLoginCallback
);

// Twitter OAuth Routes
router.get("/twitter", passport.authenticate("twitter"));

router.get(
  "/twitter/callback",
  passport.authenticate("twitter", {
    session: false,
    failureRedirect: "/login",
  }),
  authController.socialLoginCallback
);

module.exports = router;





// routes/newsRoutes.js

const express = require("express");
const {
  getAllPlayers,
  getPlayersTransfers,
  getPlayersNews,
  getBettingLaLiga,
  getBettingPremierLeague,
  // ...other controller imports
} = require("../controllers/newsController");
const router = express.Router();


// New Routes
router.get("/players", getAllPlayers); // GET /players
router.get("/players/transfers", getPlayersTransfers); // GET /players/transfers
router.get("/players/news", getPlayersNews); // GET /players/news

router.get("/betting/laliga", getBettingLaLiga); // GET /betting/laliga
router.get("/betting/premuire-league", getBettingPremierLeague); // GET /betting/premuire-league

// ...other routes

module.exports = router;

Token verification failed: TokenExpiredError: jwt expired
    at C:\Development\Node js programs\Football App\football-app-backend\node_modules\jsonwebtoken\verify.js:190:21
    at getSecret (C:\Development\Node js programs\Football App\football-app-backend\node_modules\jsonwebtoken\verify.js:97:14)
    at module.exports [as verify] (C:\Development\Node js programs\Football App\football-app-backend\node_modules\jsonwebtoken\verify.js:101:10)
    at verifyToken (C:\Development\Node js programs\Football App\football-app-backend\middlewares\auth.js:17:25)
    at Layer.handle [as handle_request] (C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\layer.js:95:5)
    at trim_prefix (C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\index.js:328:13)
    at C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\index.js:286:9
    at Function.process_params (C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\index.js:346:12)
    at next (C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\index.js:280:10)
    at Function.handle (C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\index.js:175:3) {
  expiredAt: 2024-11-14T18:52:58.000Z
}
Inserted 11 new matches.
Fetched and saved live scores to DB
Token verification failed: TokenExpiredError: jwt expired
    at C:\Development\Node js programs\Football App\football-app-backend\node_modules\jsonwebtoken\verify.js:190:21
    at getSecret (C:\Development\Node js programs\Football App\football-app-backend\node_modules\jsonwebtoken\verify.js:97:14)
    at module.exports [as verify] (C:\Development\Node js programs\Football App\football-app-backend\node_modules\jsonwebtoken\verify.js:101:10)
    at verifyToken (C:\Development\Node js programs\Football App\football-app-backend\middlewares\auth.js:17:25)
    at Layer.handle [as handle_request] (C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\layer.js:95:5)
    at trim_prefix (C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\index.js:328:13)
    at C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\index.js:286:9
    at Function.process_params (C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\index.js:346:12)
    at next (C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\index.js:280:10)
    at Function.handle (C:\Development\Node js programs\Football App\football-app-backend\node_modules\express\lib\router\index.js:175:3) {
  expiredAt: 2024-11-14T18:52:58.000Z
}
Cron job triggered: Fetching live scores...
Deleted all previous matches.
Inserted 11 new matches.
Fetched and saved live scores to DB

On adding article or news in frontend it is showing this message I have provided you the necessary controllers and models  Can you fix the error??
// components/AdminDashboard.tsx

'use client'

import { useState } from 'react'
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { useToast } from "@/components/ui/use-toast"
import { ToastAction } from "@/components/ui/toast"

export default function AdminDashboard() {
  const [activeTab, setActiveTab] = useState('articles')
  const { toast } = useToast()

  const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault()
    const form = event.currentTarget as HTMLFormElement
    const formData = new FormData(form)
    
    let endpoint = ''
    switch (activeTab) {
      case 'articles':
        endpoint = 'articles'
        break
      case 'videos':
        endpoint = 'videos'
        break
      case 'livescores':
        endpoint = 'livescores'
        break
      default:
        endpoint = ''
        break
    }

    if (!endpoint) {
      toast({
        variant: "destructive",
        title: "Invalid Tab",
        description: "Please select a valid tab.",
        action: <ToastAction altText="Okay">Okay</ToastAction>,
      })
      return
    }

    try {
      const token = localStorage.getItem('adminToken')
      if (!token) throw new Error('Unauthorized')

      const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/admin/${endpoint}`, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}`,
        },
        body: formData,
      })

      if (response.ok) {
        toast({
          title: "Success",
          description: `${endpoint.slice(0, -1)} created successfully`,
          action: <ToastAction altText="Okay">Okay</ToastAction>,
        })
        form.reset()
      } else {
        const errorData = await response.json()
        throw new Error(errorData.error || `Failed to create ${endpoint.slice(0, -1)}`)
      }
    } catch (error: any) {
      console.error(`Error creating ${activeTab.slice(0, -1)}:`, error)
      toast({
        variant: "destructive",
        title: "Error",
        description: error.message || `Failed to create ${activeTab.slice(0, -1)}`,
        action: <ToastAction altText="Try Again">Try Again</ToastAction>,
      })
    }
  }

  return (
    <Card className="w-full max-w-4xl mx-auto">
      <CardHeader>
        <CardTitle className="text-2xl font-bold">Admin Dashboard</CardTitle>
      </CardHeader>
      <CardContent>
        <Tabs value={activeTab} onValueChange={setActiveTab}>
          <TabsList className="grid w-full grid-cols-3">
            <TabsTrigger value="articles">Articles</TabsTrigger>
            <TabsTrigger value="videos">Videos</TabsTrigger>
            <TabsTrigger value="livescores">Live Scores</TabsTrigger>
          </TabsList>
          <TabsContent value="articles">
            <form onSubmit={handleSubmit} className="space-y-4 mt-4">
              <Input name="title" placeholder="Article Title" required />
              <Textarea name="content" placeholder="Article Content" required />
              <Input name="author" placeholder="Author Name" required />
              <Input name="headingImage" type="file" accept="image/*" required />
              <Button type="submit">Create Article</Button>
            </form>
          </TabsContent>
          <TabsContent value="videos">
            <form onSubmit={handleSubmit} className="space-y-4 mt-4">
              <Input name="title" placeholder="Video Title" required />
              <Input name="url" type="url" placeholder="Video URL" required />
              <Input name="thumbnail" type="file" accept="image/*" required />
              <Button type="submit">Add Video</Button>
            </form>
          </TabsContent>
          <TabsContent value="livescores">
            <form onSubmit={handleSubmit} className="space-y-4 mt-4">
              <Input name="homeTeam" placeholder="Home Team" required />
              <Input name="awayTeam" placeholder="Away Team" required />
              <Input name="date" type="datetime-local" required />
              <Button type="submit">Add Live Score</Button>
            </form>
          </TabsContent>
        </Tabs>
      </CardContent>
    </Card>
  )
}
// components/UploadArticle.tsx

'use client'

import { useState } from 'react'
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { Button } from "@/components/ui/button"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { useToast } from "@/components/ui/use-toast"
import { ToastAction } from "@/components/ui/toast"

export default function UploadArticle() {
  const [formData, setFormData] = useState({
    title: '',
    content: '',
    author: '',
  })
  const [image, setImage] = useState<File | null>(null)
  const [status, setStatus] = useState<{ type: string; message: string } | null>(null)
  const [isUploading, setIsUploading] = useState(false)
  const { toast } = useToast()

  const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
    setFormData({ ...formData, [e.target.name]: e.target.value })
  }

  const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files && e.target.files[0]) {
      setImage(e.target.files[0])
    }
  }

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    setIsUploading(true)
    setStatus(null)

    const data = new FormData()
    data.append('title', formData.title)
    data.append('content', formData.content)
    data.append('author', formData.author)
    if (image) {
      data.append('headingImage', image)
    }

    try {
      const token = localStorage.getItem('adminToken')
      if (!token) throw new Error('Unauthorized')

      const res = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/admin/articles`, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}`,
        },
        body: data,
      })

      if (!res.ok) {
        const errorData = await res.json()
        throw new Error(errorData.error || 'Something went wrong')
      }

      const result = await res.json()
      setStatus({ type: 'success', message: 'Article uploaded successfully!' })
      setFormData({ title: '', content: '', author: '' })
      setImage(null)
      toast({
        title: "Success",
        description: "Article uploaded successfully!",
        action: <ToastAction altText="Okay">Okay</ToastAction>,
      })
    } catch (error: any) {
      console.error('Error uploading article:', error)
      setStatus({ type: 'error', message: error.message || 'Failed to upload article' })
      toast({
        variant: "destructive",
        title: "Error",
        description: error.message || 'Failed to upload article',
        action: <ToastAction altText="Try again">Try again</ToastAction>,
      })
    } finally {
      setIsUploading(false)
    }
  }

  return (
    <Card>
      <CardHeader>
        <CardTitle>Upload New Article</CardTitle>
      </CardHeader>
      <CardContent>
        {status && (
          <Alert variant={status.type === 'error' ? 'destructive' : 'success'}>
            <AlertTitle>{status.type === 'error' ? 'Error' : 'Success'}</AlertTitle>
            <AlertDescription>{status.message}</AlertDescription>
          </Alert>
        )}

        <form onSubmit={handleSubmit} className="space-y-4 mt-4">
          <div>
            <label className="block text-sm font-medium text-gray-700">Title</label>
            <Input
              type="text"
              name="title"
              value={formData.title}
              onChange={handleChange}
              required
              placeholder="Enter article title"
            />
          </div>

          <div>
            <label className="block text-sm font-medium text-gray-700">Content</label>
            <Textarea
              name="content"
              value={formData.content}
              onChange={handleChange}
              required
              placeholder="Enter article content"
              rows={5}
            />
          </div>

          <div>
            <label className="block text-sm font-medium text-gray-700">Author</label>
            <Input
              type="text"
              name="author"
              value={formData.author}
              onChange={handleChange}
              required
              placeholder="Author name"
            />
          </div>

          <div>
            <label className="block text-sm font-medium text-gray-700">Heading Image</label>
            <input
              type="file"
              name="headingImage"
              accept="image/*"
              onChange={handleImageChange}
              className="mt-1 block w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 cursor-pointer focus:outline-none"
              required
            />
            {image && <p className="text-sm text-gray-500 mt-1">{image.name}</p>}
          </div>

          <div>
            <Button type="submit" disabled={isUploading}>
              {isUploading ? 'Uploading...' : 'Upload Article'}
            </Button>
          </div>
        </form>
      </CardContent>
    </Card>
  )
}
Can you fix this problem ??
I am using shadcn UI with nextjs in front end