Module 9: Admin Panel (අමතර කොටස)

සැබෑ ලෝකයේ යෙදුම් වලදී, යෙදුම කළමනාකරණය කිරීමට පරිපාලකයෙකුට (admin) විශේෂ ප්‍රවේශයක් අවශ්‍ය වේ. අපි එවැනි මූලික පද්ධතියක් ගොඩනගමු.

1. User කෙනෙකුට Admin Role එකක් ලබා දීම

Admin කෙනෙක් යනු කවුදැයි හඳුනාගැනීමට අපේ database එකට ක්‍රමයක් අවශ්‍යයි. සරලම ක්‍රමය වන්නේ users table එකට isAdmin වැනි අලුත් column එකක් එකතු කිරීමයි. මෙම column එකේ අගය 1 (true) නම්, එම user admin කෙනෙකි. 0 (false) නම්, සාමාන්‍ය user කෙනෙකි.

➡️ Database Table එක Alter කිරීම

init-db.js file එක open කර, db.exec() function එක තුළට users table එකට අලුත් column එකක් එකතු කරන SQL command එකක් ඇතුළත් කරන්න.

// Inside lib/db.js -> initDb function (or a new migration script)

// Add this command. It will only run if the column doesn't exist, preventing errors.
try {
    db.exec("ALTER TABLE users ADD COLUMN isAdmin INTEGER DEFAULT 0");
} catch (e) {
    // Ignore error if column already exists
    console.log("'isAdmin' column already exists.");
}

මෙම වෙනස කිරීමෙන් පසු, node init-db.js command එක නැවත run කරන්න. දැන් users table එකේ isAdmin නමින් අලුත් column එකක් ඇත.

➡️ User කෙනෙක් Admin ලෙස Set කිරීම

දැනට, අපි අතින් (manually) user කෙනෙක් admin බවට පත් කරමු. මේ සඳහා ඔබට DB Browser for SQLite වැනි tool එකක් භාවිතා කර notes.db file එක open කර, user කෙනෙකුගේ isAdmin අගය 1 ලෙස වෙනස් කළ හැක.


2. Admin Routes ආරක්ෂා කිරීම

Admin panel එකට පිවිසිය යුත්තේ admin users ලා පමණි. මේ සඳහා අපි Module 5 හි සෑදූ middleware.ts file එක update කර, /admin path එකට පිවිසීමට පෙර user admin ද යන්න පරීක්ෂා කරමු.

➡️ Login API එක සහ JWT එක යාවත්කාලීන කිරීම

User login වන විට, ඔහුගේ/ඇයගේ isAdmin status එක JWT token එකට ඇතුළත් කළ යුතුය. /api/auth/login/route.ts file එකේ jwt.sign කරන කොටස මෙසේ වෙනස් කරන්න.

// Inside /api/auth/login/route.ts

// First, fetch the isAdmin status from the user object
const user = db.prepare('SELECT id, username, password_hash, isAdmin FROM users WHERE username = ?').get(username) as any;

// ... (after password validation)

// Add isAdmin to the JWT payload
const token = jwt.sign(
  { userId: user.id, username: user.username, isAdmin: user.isAdmin === 1 }, // Check for 1
  secret,
  { expiresIn: '1h' }
);
➡️ Middleware යාවත්කාලීන කිරීම

දැන් middleware.ts file එකේ, /admin path එකට එන requests පරීක්ෂා කිරීමට logic එකතු කරමු.

// Inside middleware.ts
import { jwtVerify } from 'jose'; // A more secure way to verify JWTs on the edge

// ... inside the middleware function
export async function middleware(request: NextRequest) {
    const sessionToken = request.cookies.get('session_token')?.value;
    const { pathname } = request.nextUrl;

    // --- Admin Route Protection ---
    if (pathname.startsWith('/admin')) {
        if (!sessionToken) {
            return NextResponse.redirect(new URL('/auth/login', request.url));
        }

        try {
            const secret = new TextEncoder().encode(process.env.JWT_SECRET!);
            const { payload } = await jwtVerify(sessionToken, secret);
            
            if (!payload.isAdmin) {
                // If not an admin, redirect to the normal notes page
                return NextResponse.redirect(new URL('/notes', request.url));
            }
        } catch (err) {
            // If token is invalid, redirect to login
            return NextResponse.redirect(new URL('/auth/login', request.url));
        }
    }
    
    // ... (existing middleware logic for /notes and /auth)

    return NextResponse.next();
}

export const config = {
  matcher: ['/notes/:path*', '/auth/login', '/auth/register', '/admin/:path*'], // Add admin path
};

// You might need to install jose: npm install jose

3. Admin Dashboard එක ගොඩනැගීම

Admin dashboard එකේදී, අපට සියලුම පරිශීලකයින්ගේ සියලුම සටහන් බැලීමට හැකි විය යුතුය. මෙය server component එකක් ලෙස නිර්මාණය කර, server-side එකේදීම සියලුම දත්ත fetch කරගනිමු.

➡️ /admin/dashboard/page.tsx නිර්මාණය කිරීම

app/admin/dashboard/page.tsx යන path එකට අලුත් file එකක් සාදා පහත කේතය ඇතුළත් කරන්න.

import db from '@/lib/db';

interface AdminNote {
  id: number;
  title: string;
  content: string;
  created_at: string;
  username: string; // To show who created the note
}

// This is a Server Component, so we can directly access the database
async function getAllNotes(): Promise<AdminNote[]> {
  // Use a JOIN query to get the username from the users table
  const query = `
    SELECT notes.id, notes.title, notes.content, notes.created_at, users.username
    FROM notes
    JOIN users ON notes.user_id = users.id
    ORDER BY notes.created_at DESC
  `;
  const notes = db.prepare(query).all() as AdminNote[];
  return notes;
}

export default async function AdminDashboardPage() {
  const allNotes = await getAllNotes();

  return (
    <div>
      <h1>Admin Dashboard</h1>
      <p>Viewing all notes from all users.</p>

      <div className="table-responsive mt-4">
        <table className="table table-striped table-bordered">
          <thead>
            <tr>
              <th>Note ID</th>
              <th>Title</th>
              <th>Author (Username)</th>
              <th>Created At</th>
              <th>Actions</th>
            </tr>
          </thead>
          <tbody>
            {allNotes.map(note => (
              <tr key={note.id}>
                <td>{note.id}</td>
                <td>{note.title}</td>
                <td>{note.username}</td>
                <td>{new Date(note.created_at).toLocaleString()}</td>
                <td>
                  {/* Future: Add admin-specific actions like delete any note */}
                  <button className="btn btn-sm btn-danger" disabled>Delete</button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}

දැන්, admin user කෙනෙක් ලෙස login වී /admin/dashboard වෙත පිවිසි විට, ඔබට සියලුම users ලා විසින් සාදන ලද සියලුම notes පිළිවෙලකට table එකක දැකගත හැකි වනු ඇත.