So I am making an football app as you know from previous chats Now I have implemented and admin panel having roles publisher reviewer and only user User's functionality will be added after some days So problem is occuring in frontend like I didn't give anyone the route from home page localhost:3000/superuser/login and dashboard but it's not reaching there Can you see it I am gonna paste everything important to you require("dotenv").config(); const mongoose = require("mongoose"); const bcrypt = require("bcryptjs"); const Admin = require("../models/Admin"); const seedSuperUser = async () => { await mongoose.connect(process.env.MONGO_URL, { useNewUrlParser: true, useUnifiedTopology: true, }); const existingSuperUser = await Admin.findOne({ role: "superuser" }); if (existingSuperUser) { console.log("Super User already exists."); process.exit(0); } const username = "abcbcdb"; // Change as needed const email = "skand4@gmail.com"; // Change as needed const password = "7asldnadm"; // Change as needed const hashedPassword = await bcrypt.hash(password, 10); const superUser = new Admin({ username, email, password: hashedPassword, role: "superuser", }); await superUser.save(); console.log("Super User seeded successfully."); process.exit(0); }; seedSuperUser().catch((err) => { console.error("Error seeding Super User:", err); process.exit(1); }); seedSuperUser(); const Admin = require("../models/Admin"); const bcrypt = require("bcryptjs"); const { handleControllerError } = require("../utils/errorHandler"); // View all admins const getAllAdmins = async (req, res) => { try { const admins = await Admin.find().select("-password"); res.json(admins); } catch (error) { handleControllerError(error, res, "getAllAdmins"); } }; // Assign role to an admin const assignRole = async (req, res) => { try { const { adminId, role } = req.body; if (!adminId || !role) { return res .status(400) .json({ message: "adminId and role are required." }); } const validRoles = ["publisher", "reviewer", "admin"]; if (!validRoles.includes(role)) { return res .status(400) .json({ message: `Role must be one of: ${validRoles.join(", ")}` }); } const admin = await Admin.findById(adminId); if (!admin) { return res.status(404).json({ message: "Admin not found." }); } admin.role = role; await admin.save(); res.json({ message: `Role updated to ${role} successfully.`, admin }); } catch (error) { handleControllerError(error, res, "assignRole"); } }; // Delete an admin const deleteAdmin = async (req, res) => { try { const { adminId } = req.params; if (!adminId) { return res.status(400).json({ message: "adminId is required." }); } const admin = await Admin.findByIdAndDelete(adminId); if (!admin) { return res.status(404).json({ message: "Admin not found." }); } res.json({ message: "Admin deleted successfully." }); } catch (error) { handleControllerError(error, res, "deleteAdmin"); } }; // Update Super User's own profile (optional) const updateSuperUserProfile = async (req, res) => { try { const { username, email, password } = req.body; const superUser = await Admin.findById(req.admin._id); if (!superUser) { return res.status(404).json({ message: "Super User not found." }); } if (username) superUser.username = username; if (email) superUser.email = email; if (password) { const hashedPassword = await bcrypt.hash(password, 10); superUser.password = hashedPassword; } await superUser.save(); res.json({ message: "Profile updated successfully.", superUser }); } catch (error) { handleControllerError(error, res, "updateSuperUserProfile"); } }; module.exports = { getAllAdmins, assignRole, deleteAdmin, updateSuperUserProfile, }; 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/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." }); } }; // 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); } } ) ); // 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/AdminLogin.tsx 'use client' import { useState } from 'react' import { useRouter } from 'next/navigation' import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Button } from "@/components/ui/button" import { useToast } from "@/components/ui/use-toast" import { ToastAction } from "@/components/ui/toast" import { FaGoogle, FaFacebook, FaTwitter } from 'react-icons/fa' import Link from 'next/link' export default function AdminLogin() { const [email, setEmail] = useState('') const [password, setPassword] = useState('') const router = useRouter() const { toast } = useToast() const handleLogin = async (e: React.FormEvent) => { e.preventDefault() try { const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }), }) const data = await response.json() if (response.ok) { localStorage.setItem('adminToken', data.token) router.push('/admin/dashboard') toast({ title: "Login Successful", description: "You have been successfully logged in.", }) } else { throw new Error(data.error || 'Login failed') } } catch (error) { toast({ variant: "destructive", title: "Login Failed", description: error instanceof Error ? error.message : "An error occurred", action: <ToastAction altText="Try Again">Try Again</ToastAction>, }) } } // Social Login URLs const socialLogins = [ { name: 'Google', icon: <FaGoogle />, url: `${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/google`, color: 'bg-red-500 hover:bg-red-600', }, { name: 'Facebook', icon: <FaFacebook />, url: `${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/facebook`, color: 'bg-blue-600 hover:bg-blue-700', }, { name: 'Twitter', icon: <FaTwitter />, url: `${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/twitter`, color: 'bg-blue-400 hover:bg-blue-500', }, ] return ( <div className="flex justify-center items-center min-h-screen bg-gray-100"> <Card className="w-full max-w-md"> <CardHeader> <CardTitle className="text-2xl font-bold text-center">Admin Login</CardTitle> </CardHeader> <CardContent> <form onSubmit={handleLogin} className="space-y-4"> <Input type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} required /> <Input type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} required /> <Button type="submit" className="w-full">Login</Button> </form> <div className="my-4 flex items-center"> <hr className="flex-grow border-gray-300" /> <span className="mx-2 text-gray-500">OR</span> <hr className="flex-grow border-gray-300" /> </div> <div className="space-y-2"> {socialLogins.map((provider) => ( <Button key={provider.name} variant="outline" className={`w-full justify-center ${provider.color}`} onClick={() => window.location.href = provider.url} > {provider.icon} <span className="ml-2">Continue with {provider.name}</span> </Button> ))} </div> <p className="mt-4 text-center text-sm text-gray-600"> Don't have an account?{' '} <Link href="/admin/register" className="text-blue-500 hover:underline"> Register </Link> </p> </CardContent> </Card> </div> ) } // components/withSuperUserAuth.tsx 'use client'; import { useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; import { useToast } from "@/components/ui/use-toast"; import { ToastAction } from "@/components/ui/toast"; const withSuperUserAuth = (WrappedComponent: React.ComponentType) => { return (props: any) => { const router = useRouter(); const { toast } = useToast(); const [isAuthenticated, setIsAuthenticated] = useState(false); const [isLoading, setIsLoading] = useState(true); useEffect(() => { const verifyToken = async () => { const token = localStorage.getItem('superuser_token'); if (!token) { toast({ variant: "destructive", title: "Unauthorized", description: "Please log in as a Super User.", action: <ToastAction altText="Okay">Okay</ToastAction>, }); router.push('/superuser/login'); return; } try { const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/verify-token`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, }); const data = await response.json(); if (response.ok && data.role === 'superuser') { setIsAuthenticated(true); } else { throw new Error('Access denied: Not a Super User.'); } } catch (error: any) { toast({ variant: "destructive", title: "Authentication Error", description: error.message || "Failed to authenticate.", action: <ToastAction altText="Okay">Okay</ToastAction>, }); router.push('/superuser/login'); } finally { setIsLoading(false); } }; verifyToken(); }, [router, toast]); if (isLoading) { return <p>Loading...</p>; } return isAuthenticated ? <WrappedComponent {...props} /> : null; }; }; export default withSuperUserAuth; // components/SuperUserLogin.tsx 'use client'; import { useState } from 'react'; import { useRouter } from 'next/navigation'; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { useToast } from "@/components/ui/use-toast"; import { ToastAction } from "@/components/ui/toast"; import { FaGoogle, FaFacebook, FaTwitter } from 'react-icons/fa'; import Link from 'next/link'; export default function SuperUserLogin() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const router = useRouter(); const { toast } = useToast(); const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); try { const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }), }); const data = await response.json(); if (response.ok) { if (data.role !== 'superuser') { throw new Error('Access denied: Not a Super User.'); } localStorage.setItem('superuser_token', data.token); router.push('/superuser/dashboard'); toast({ title: "Login Successful", description: "You have been successfully logged in as Super User.", }); } else { throw new Error(data.error || 'Login failed'); } } catch (error: any) { toast({ variant: "destructive", title: "Login Failed", description: error.message || "An error occurred", action: <ToastAction altText="Try Again">Try Again</ToastAction>, }); } }; // Social Login URLs (if applicable) const socialLogins = [ { name: 'Google', icon: <FaGoogle />, url: `${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/google`, color: 'bg-red-500 hover:bg-red-600', }, { name: 'Facebook', icon: <FaFacebook />, url: `${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/facebook`, color: 'bg-blue-600 hover:bg-blue-700', }, { name: 'Twitter', icon: <FaTwitter />, url: `${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/twitter`, color: 'bg-blue-400 hover:bg-blue-500', }, ]; return ( <div className="flex justify-center items-center min-h-screen bg-gray-100"> <Card className="w-full max-w-md"> <CardHeader> <CardTitle className="text-2xl font-bold text-center">Super User Login</CardTitle> </CardHeader> <CardContent> <form onSubmit={handleLogin} className="space-y-4"> <Input type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} required /> <Input type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} required /> <Button type="submit" className="w-full">Login</Button> </form> <div className="my-4 flex items-center"> <hr className="flex-grow border-gray-300" /> <span className="mx-2 text-gray-500">OR</span> <hr className="flex-grow border-gray-300" /> </div> <div className="space-y-2"> {socialLogins.map((provider) => ( <Button key={provider.name} variant="outline" className={`w-full justify-center ${provider.color}`} onClick={() => window.location.href = provider.url} > {provider.icon} <span className="ml-2">Continue with {provider.name}</span> </Button> ))} </div> <p className="mt-4 text-center text-sm text-gray-600"> Don't have an account?{' '} <Link href="/superuser/register" className="text-blue-500 hover:underline"> Register </Link> </p> </CardContent> </Card> </div> ); } // 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/roleMiddleware"); const upload = require("../middlewares/upload"); // Protect all admin routes router.use(verifyToken); router.use(authorizeRoles("superuser", "admin")); // --- Article Routes --- // Create a new article router.post( "/articles", upload.single("headingImage"), adminController.addArticle ); // Get all articles router.get("/articles", adminController.getAllArticles); // Get a single article by ID router.get("/articles/:id", adminController.getArticleById); // Update an article router.put( "/articles/:id", upload.single("headingImage"), adminController.updateArticle ); // Delete an article router.delete("/articles/:id", adminController.deleteArticle); // --- News Routes --- // Create a new news item router.post( "/news", upload.fields([ { name: "headingImage", maxCount: 1 }, { name: "images", maxCount: 10 }, // For carousel images ]), adminController.addNews ); // Get all news router.get("/news", adminController.getAllNews); // Get a single news item by ID router.get("/news/:id", adminController.getNewsById); // Update a news item router.put( "/news/:id", upload.fields([ { name: "headingImage", maxCount: 1 }, { name: "images", maxCount: 10 }, ]), adminController.updateNews ); // Delete a news item router.delete("/news/:id", adminController.deleteNews); module.exports = router; const express = require("express"); const router = express.Router(); const superUserController = require("../controllers/superUserController"); const { verifyToken } = require("../middlewares/auth"); const authorizeRoles = require("../middlewares/roleMiddleware"); // Protect routes: only superusers can access router.use(verifyToken); router.use(authorizeRoles("superuser")); // GET /api/superuser/admins - View all admins router.get("/admins", superUserController.getAllAdmins); // PUT /api/superuser/assign-role - Assign role to admin router.put("/assign-role", superUserController.assignRole); // DELETE /api/superuser/delete/:adminId - Delete an admin router.delete("/delete/:adminId", superUserController.deleteAdmin); // PUT /api/superuser/profile - Update Super User profile (optional) router.put("/profile", superUserController.updateSuperUserProfile); module.exports = router; These are the routes Can you solve this problem


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

// --- 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,
};

// components/SuperUserDashboard.tsx

'use client';

import { useEffect, useState } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Select, SelectTrigger, SelectContent, SelectItem } from "@/components/ui/select";
import { useToast } from "@/components/ui/use-toast";
import { ToastAction } from "@/components/ui/toast";

interface Admin {
  _id: string;
  username: string;
  email: string;
  role: string;
  createdAt: string;
  updatedAt: string;
}

export default function SuperUserDashboard() {
  const [admins, setAdmins] = useState<Admin[]>([]);
  const [selectedAdmin, setSelectedAdmin] = useState<Admin | null>(null);
  const [newRole, setNewRole] = useState('');
  const { toast } = useToast();

  // Fetch all admins on component mount
  useEffect(() => {
    const fetchAdmins = async () => {
      try {
        const token = localStorage.getItem('superuser_token');
        if (!token) throw new Error('Unauthorized');

        const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/superuser/admins`, {
          method: 'GET',
          headers: {
            'Authorization': `Bearer ${token}`,
          },
        });

        if (response.ok) {
          const data = await response.json();
          setAdmins(data);
        } else {
          const errorData = await response.json();
          throw new Error(errorData.message || 'Failed to fetch admins');
        }
      } catch (error: any) {
        toast({
          variant: "destructive",
          title: "Error",
          description: error.message || "Failed to load admins.",
          action: <ToastAction altText="Try Again">Try Again</ToastAction>,
        });
      }
    };

    fetchAdmins();
  }, [toast]);

  const handleAssignRole = async () => {
    if (!selectedAdmin || !newRole) {
      toast({
        variant: "destructive",
        title: "Invalid Input",
        description: "Please select an admin and a new role.",
        action: <ToastAction altText="Okay">Okay</ToastAction>,
      });
      return;
    }

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

      const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/superuser/assign-role`, {
        method: 'PUT',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${token}`,
        },
        body: JSON.stringify({ adminId: selectedAdmin._id, role: newRole }),
      });

      if (response.ok) {
        const updatedAdmin = await response.json();
        setAdmins(admins.map(admin => admin._id === updatedAdmin.admin._id ? updatedAdmin.admin : admin));
        toast({
          title: "Success",
          description: `Role updated to ${newRole} for ${updatedAdmin.admin.username}.`,
          action: <ToastAction altText="Okay">Okay</ToastAction>,
        });
        setSelectedAdmin(null);
        setNewRole('');
      } else {
        const errorData = await response.json();
        throw new Error(errorData.message || 'Failed to assign role');
      }
    } catch (error: any) {
      toast({
        variant: "destructive",
        title: "Error",
        description: error.message || "Failed to assign role.",
        action: <ToastAction altText="Try Again">Try Again</ToastAction>,
      });
    }
  };

  const handleDeleteAdmin = async (adminId: string) => {
    if (!window.confirm("Are you sure you want to delete this admin? This action cannot be undone.")) {
      return;
    }

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

      const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/superuser/delete/${adminId}`, {
        method: 'DELETE',
        headers: {
          'Authorization': `Bearer ${token}`,
        },
      });

      if (response.ok) {
        setAdmins(admins.filter(admin => admin._id !== adminId));
        toast({
          title: "Success",
          description: "Admin deleted successfully.",
          action: <ToastAction altText="Okay">Okay</ToastAction>,
        });
      } else {
        const errorData = await response.json();
        throw new Error(errorData.message || 'Failed to delete admin');
      }
    } catch (error: any) {
      toast({
        variant: "destructive",
        title: "Error",
        description: error.message || "Failed to delete admin.",
        action: <ToastAction altText="Try Again">Try Again</ToastAction>,
      });
    }
  };

  return (
    <Card className="w-full max-w-5xl mx-auto">
      <CardHeader>
        <CardTitle className="text-2xl font-bold">Super User Dashboard</CardTitle>
      </CardHeader>
      <CardContent>
        {/* Admins List */}
        <div className="mb-8">
          <h2 className="text-xl font-semibold mb-4">All Admins</h2>
          <table className="min-w-full border">
            <thead>
              <tr className="bg-gray-200">
                <th className="py-2 px-4 border">Username</th>
                <th className="py-2 px-4 border">Email</th>
                <th className="py-2 px-4 border">Role</th>
                <th className="py-2 px-4 border">Actions</th>
              </tr>
            </thead>
            <tbody>
              {admins.map(admin => (
                <tr key={admin._id} className="text-center">
                  <td className="py-2 px-4 border">{admin.username}</td>
                  <td className="py-2 px-4 border">{admin.email}</td>
                  <td className="py-2 px-4 border">{admin.role}</td>
                  <td className="py-2 px-4 border space-x-2">
                    <Button variant="outline" size="sm" onClick={() => setSelectedAdmin(admin)}>
                      Assign Role
                    </Button>
                    <Button variant="destructive" size="sm" onClick={() => handleDeleteAdmin(admin._id)}>
                      Delete
                    </Button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>

        {/* Assign Role Form */}
        {selectedAdmin && (
          <Card className="mb-8">
            <CardHeader>
              <CardTitle className="text-xl font-semibold">Assign Role to {selectedAdmin.username}</CardTitle>
            </CardHeader>
            <CardContent>
              <div className="flex items-center space-x-4">
                <Select onValueChange={(value) => setNewRole(value)}>
                  <SelectTrigger className="w-[180px]">
                    <Input placeholder="Select new role" readOnly value={newRole} />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="publisher">Publisher</SelectItem>
                    <SelectItem value="reviewer">Reviewer</SelectItem>
                    <SelectItem value="admin">Admin</SelectItem>
                  </SelectContent>
                </Select>
                <Button onClick={handleAssignRole}>Update Role</Button>
                <Button variant="ghost" onClick={() => setSelectedAdmin(null)}>Cancel</Button>
              </div>
            </CardContent>
          </Card>
        )}
      </CardContent>
    </Card>
  );
}
Can you solve this problem so that it no one other than me can access superuser first up and localhost:3000/superuser/login  is login page which is not routed and also superuser/dashboard is superuser dashboard where he has all the the power to write article delete any article and also it should have how many visits dashboard type to which article