HEX
Server: Apache/2.4.65 (Debian)
System: Linux kubikelcreative 5.10.0-35-amd64 #1 SMP Debian 5.10.237-1 (2025-05-19) x86_64
User: www-data (33)
PHP: 8.4.13
Disabled: NONE
Upload Files
File: /var/www/indoadvisory_new/webapp/src/routes/admin.js
import { Hono } from 'hono';
import { getCookie, setCookie, deleteCookie } from 'hono/cookie';
import { generateSessionId, validatePassword, isValidSession } from '../utils/auth';
import { initializeDatabase } from '../utils/database';
const admin = new Hono();
// Middleware to check admin authentication
admin.use('/*', async (c, next) => {
    // Skip auth check for login page and login POST
    if (c.req.path === '/admin/login' && (c.req.method === 'GET' || c.req.method === 'POST')) {
        return next();
    }
    const sessionId = getCookie(c, 'admin_session');
    if (!sessionId) {
        return c.redirect('/admin/login');
    }
    try {
        const session = await c.env.DB.prepare('SELECT s.*, u.id as user_id, u.username, u.email, u.name, u.role FROM admin_sessions s JOIN admin_users u ON s.user_id = u.id WHERE s.id = ?').bind(sessionId).first();
        if (!session || !isValidSession(session)) {
            deleteCookie(c, 'admin_session');
            return c.redirect('/admin/login');
        }
        c.set('user', session);
        return next();
    }
    catch (error) {
        return c.redirect('/admin/login');
    }
});
// Admin Login Page
admin.get('/login', (c) => {
    return c.html(`
    <!DOCTYPE html>
    <html lang="id">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Admin Login - IndoPrivate</title>
        <script src="https://cdn.tailwindcss.com"></script>
        <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    </head>
    <body class="bg-gray-100 min-h-screen flex items-center justify-center">
        <div class="bg-white p-8 rounded-xl shadow-lg w-full max-w-md">
            <div class="text-center mb-8">
                <h1 class="text-2xl font-bold text-gray-800">
                    <i class="fas fa-chart-line text-blue-600 mr-2"></i>
                    IndoPrivate Admin
                </h1>
                <p class="text-gray-600 mt-2">Masuk ke dashboard admin</p>
            </div>
            
            <form action="/admin/login" method="POST" class="space-y-6">
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-2">Username</label>
                    <input 
                        type="text" 
                        name="username" 
                        required 
                        class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                        placeholder="Masukkan username"
                    />
                </div>
                
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-2">Password</label>
                    <input 
                        type="password" 
                        name="password" 
                        required 
                        class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                        placeholder="Masukkan password"
                    />
                </div>
                
                <button 
                    type="submit" 
                    class="w-full bg-blue-600 text-white py-3 px-6 rounded-lg font-semibold hover:bg-blue-700 transition-colors"
                >
                    <i class="fas fa-sign-in-alt mr-2"></i>
                    Masuk
                </button>
            </form>
            
            <div class="mt-6 text-center text-sm text-gray-500">
                <p>Demo credentials: <strong>admin / admin123</strong></p>
            </div>
        </div>
    </body>
    </html>
  `);
});
// Admin Login Handler
admin.post('/login', async (c) => {
    const { username, password } = await c.req.parseBody();
    try {
        await initializeDatabase(c.env.DB);
        // Insert demo admin if not exists
        await c.env.DB.prepare('INSERT OR IGNORE INTO admin_users (username, password_hash, email, name, role) VALUES (?, ?, ?, ?, ?)').bind('admin', 'admin123', 'admin@indoprivate.co.id', 'Administrator', 'admin').run();
        const user = await c.env.DB.prepare('SELECT * FROM admin_users WHERE username = ?').bind(username).first();
        if (!user || !validatePassword(password, user.password_hash)) {
            return c.html(`
        <script>
          alert('Username atau password salah');
          window.location.href = '/admin/login';
        </script>
      `);
        }
        // Create session
        const sessionId = generateSessionId();
        const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours
        await c.env.DB.prepare('INSERT INTO admin_sessions (id, user_id, expires_at) VALUES (?, ?, ?)').bind(sessionId, user.id, expiresAt.toISOString()).run();
        setCookie(c, 'admin_session', sessionId, {
            maxAge: 24 * 60 * 60,
            httpOnly: true,
            secure: false, // Set to true in production with HTTPS
            sameSite: 'Lax'
        });
        return c.redirect('/admin/dashboard');
    }
    catch (error) {
        return c.html(`
      <script>
        alert('Terjadi kesalahan sistem');
        window.location.href = '/admin/login';
      </script>
    `);
    }
});
// Admin Dashboard
admin.get('/dashboard', async (c) => {
    const user = c.get('user');
    try {
        // Get dashboard statistics
        const articlesCount = await c.env.DB.prepare('SELECT COUNT(*) as count FROM articles').first();
        const inquiriesCount = await c.env.DB.prepare('SELECT COUNT(*) as count FROM contact_inquiries WHERE status = "new"').first();
        const publishedArticles = await c.env.DB.prepare('SELECT COUNT(*) as count FROM articles WHERE status = "published"').first();
        const clientsCount = await c.env.DB.prepare('SELECT COUNT(*) as count FROM clients WHERE is_active = 1').first();
        return c.html(`
      <!DOCTYPE html>
      <html lang="id">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Dashboard Admin - IndoPrivate</title>
          <script src="https://cdn.tailwindcss.com"></script>
          <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
      </head>
      <body class="bg-gray-100">
          ${adminNavbar(user)}
          
          <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
              <div class="mb-8">
                  <h1 class="text-3xl font-bold text-gray-800">Dashboard Admin</h1>
                  <p class="text-gray-600 mt-2">Selamat datang kembali, ${user.name}</p>
              </div>
              
              <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
                  <div class="bg-white p-6 rounded-xl shadow-lg">
                      <div class="flex items-center">
                          <div class="bg-blue-100 p-3 rounded-full">
                              <i class="fas fa-newspaper text-blue-600 text-xl"></i>
                          </div>
                          <div class="ml-4">
                              <h3 class="text-lg font-semibold text-gray-800">Total Artikel</h3>
                              <p class="text-2xl font-bold text-blue-600">${articlesCount.count}</p>
                          </div>
                      </div>
                  </div>
                  
                  <div class="bg-white p-6 rounded-xl shadow-lg">
                      <div class="flex items-center">
                          <div class="bg-green-100 p-3 rounded-full">
                              <i class="fas fa-check-circle text-green-600 text-xl"></i>
                          </div>
                          <div class="ml-4">
                              <h3 class="text-lg font-semibold text-gray-800">Artikel Published</h3>
                              <p class="text-2xl font-bold text-green-600">${publishedArticles.count}</p>
                          </div>
                      </div>
                  </div>
                  
                  <div class="bg-white p-6 rounded-xl shadow-lg">
                      <div class="flex items-center">
                          <div class="bg-orange-100 p-3 rounded-full">
                              <i class="fas fa-envelope text-orange-600 text-xl"></i>
                          </div>
                          <div class="ml-4">
                              <h3 class="text-lg font-semibold text-gray-800">Inquiry Baru</h3>
                              <p class="text-2xl font-bold text-orange-600">${inquiriesCount.count}</p>
                          </div>
                      </div>
                  </div>
                  
                  <div class="bg-white p-6 rounded-xl shadow-lg">
                      <div class="flex items-center">
                          <div class="bg-purple-100 p-3 rounded-full">
                              <i class="fas fa-building text-purple-600 text-xl"></i>
                          </div>
                          <div class="ml-4">
                              <h3 class="text-lg font-semibold text-gray-800">Klien Aktif</h3>
                              <p class="text-2xl font-bold text-purple-600">${clientsCount.count}</p>
                          </div>
                      </div>
                  </div>
              </div>
              
              <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
                  <div class="bg-white p-6 rounded-xl shadow-lg">
                      <h2 class="text-xl font-bold text-gray-800 mb-4">
                          <i class="fas fa-newspaper mr-2"></i>
                          Quick Actions
                      </h2>
                      <div class="space-y-4">
                          <a href="/admin/articles/create" class="flex items-center p-4 bg-blue-50 rounded-lg hover:bg-blue-100 transition-colors">
                              <i class="fas fa-plus text-blue-600 mr-3"></i>
                              <div>
                                  <h3 class="font-semibold text-gray-800">Buat Artikel Baru</h3>
                                  <p class="text-gray-600 text-sm">Tulis dan publikasikan artikel terbaru</p>
                              </div>
                          </a>
                          
                          <a href="/admin/articles" class="flex items-center p-4 bg-green-50 rounded-lg hover:bg-green-100 transition-colors">
                              <i class="fas fa-list text-green-600 mr-3"></i>
                              <div>
                                  <h3 class="font-semibold text-gray-800">Kelola Artikel</h3>
                                  <p class="text-gray-600 text-sm">Edit atau hapus artikel yang ada</p>
                              </div>
                          </a>
                          
                          <a href="/admin/inquiries" class="flex items-center p-4 bg-orange-50 rounded-lg hover:bg-orange-100 transition-colors">
                              <i class="fas fa-envelope text-orange-600 mr-3"></i>
                              <div>
                                  <h3 class="font-semibold text-gray-800">Lihat Inquiry</h3>
                                  <p class="text-gray-600 text-sm">Respon pertanyaan dari prospek</p>
                              </div>
                          </a>
                          
                          <a href="/admin/clients" class="flex items-center p-4 bg-purple-50 rounded-lg hover:bg-purple-100 transition-colors">
                              <i class="fas fa-building text-purple-600 mr-3"></i>
                              <div>
                                  <h3 class="font-semibold text-gray-800">Kelola Klien</h3>
                                  <p class="text-gray-600 text-sm">Manage client showcase dan portfolio</p>
                              </div>
                          </a>
                          
                          <a href="/admin/team" class="flex items-center p-4 bg-teal-50 rounded-lg hover:bg-teal-100 transition-colors">
                              <i class="fas fa-users text-teal-600 mr-3"></i>
                              <div>
                                  <h3 class="font-semibold text-gray-800">Kelola Tim</h3>
                                  <p class="text-gray-600 text-sm">Manage profil anggota tim</p>
                              </div>
                          </a>
                      </div>
                  </div>
                  
                  <div class="bg-white p-6 rounded-xl shadow-lg">
                      <h2 class="text-xl font-bold text-gray-800 mb-4">
                          <i class="fas fa-chart-bar mr-2"></i>
                          Website Statistics
                      </h2>
                      <div class="space-y-4">
                          <div class="flex justify-between items-center">
                              <span class="text-gray-600">Artikel Terbaru</span>
                              <span class="font-semibold">7 hari terakhir</span>
                          </div>
                          <div class="flex justify-between items-center">
                              <span class="text-gray-600">Total Halaman</span>
                              <span class="font-semibold">8 halaman</span>
                          </div>
                          <div class="flex justify-between items-center">
                              <span class="text-gray-600">Status Website</span>
                              <span class="text-green-600 font-semibold">
                                  <i class="fas fa-check-circle mr-1"></i>
                                  Online
                              </span>
                          </div>
                      </div>
                  </div>
              </div>
          </div>
      </body>
      </html>
    `);
    }
    catch (error) {
        console.error('Dashboard error:', error);
        return c.html(`<div>Error loading dashboard</div>`);
    }
});
// Admin Navbar Component
function adminNavbar(user) {
    return `
    <nav class="bg-white shadow-lg border-b">
        <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
            <div class="flex justify-between items-center h-16">
                <div class="flex items-center">
                    <h1 class="text-xl font-bold text-gray-800">
                        <i class="fas fa-chart-line text-blue-600 mr-2"></i>
                        IndoPrivate Admin
                    </h1>
                </div>
                
                <div class="hidden md:flex items-center space-x-6">
                    <a href="/admin/dashboard" class="text-gray-700 hover:text-blue-600 px-3 py-2 rounded-md text-sm font-medium">
                        <i class="fas fa-tachometer-alt mr-1"></i>
                        Dashboard
                    </a>
                    <a href="/admin/articles" class="text-gray-700 hover:text-blue-600 px-3 py-2 rounded-md text-sm font-medium">
                        <i class="fas fa-newspaper mr-1"></i>
                        Artikel
                    </a>
                    <a href="/admin/inquiries" class="text-gray-700 hover:text-blue-600 px-3 py-2 rounded-md text-sm font-medium">
                        <i class="fas fa-envelope mr-1"></i>
                        Inquiry
                    </a>
                    <a href="/admin/clients" class="text-gray-700 hover:text-blue-600 px-3 py-2 rounded-md text-sm font-medium">
                        <i class="fas fa-building mr-1"></i>
                        Klien
                    </a>
                    <a href="/admin/team" class="text-gray-700 hover:text-blue-600 px-3 py-2 rounded-md text-sm font-medium">
                        <i class="fas fa-users mr-1"></i>
                        Tim
                    </a>
                    <a href="/admin/settings" class="text-gray-700 hover:text-blue-600 px-3 py-2 rounded-md text-sm font-medium">
                        <i class="fas fa-cog mr-1"></i>
                        Settings
                    </a>
                    <a href="/" target="_blank" class="text-gray-700 hover:text-blue-600 px-3 py-2 rounded-md text-sm font-medium">
                        <i class="fas fa-external-link-alt mr-1"></i>
                        Lihat Website
                    </a>
                </div>
                
                <div class="flex items-center">
                    <span class="text-gray-700 mr-4">
                        <i class="fas fa-user mr-1"></i>
                        ${user.name}
                    </span>
                    <a href="/admin/logout" class="text-red-600 hover:text-red-700 px-3 py-2 rounded-md text-sm font-medium">
                        <i class="fas fa-sign-out-alt mr-1"></i>
                        Logout
                    </a>
                </div>
            </div>
        </div>
    </nav>
  `;
}
// Logout
admin.get('/logout', (c) => {
    deleteCookie(c, 'admin_session');
    return c.redirect('/admin/login');
});
export default admin;