Manajemen Pengguna (Admin)

Serial Bagian 5: Membangun dashboard internal untuk mendaftarkan pengguna baru dan meng-generate API Key secara aman.

18. Admin API Endpoint

Kita akan menambahkan endpoint baru di backend khusus untuk Admin. Endpoint ini memiliki hak akses untuk melihat semua user dan membuat user baru.

File: /www/wwwroot/app-collection/ao/app/api/v1/endpoints/admin.py
from fastapi import APIRouter, Depends, HTTPException, Header
from sqlalchemy.orm import Session
import secrets
import hashlib

from core.db import get_db
import core.models as models

router = APIRouter()

# --- KONFIGURASI ADMIN ---
# Password sederhana untuk mengakses panel admin.
# DI PRODUKSI, gunakan sistem login JWT atau Basic Auth yang lebih aman.
ADMIN_SECRET_KEY = "rahasia_admin_ganti_ini_di_production"

def verify_admin(x_admin_key: str = Header(...)):
    if x_admin_key != ADMIN_SECRET_KEY:
        raise HTTPException(status_code=403, detail="Unauthorized Admin Access")
    return True

@router.get("/users")
def list_users(
    admin_verified: bool = Depends(verify_admin),
    db: Session = Depends(get_db)
):
    """Menampilkan daftar semua pengguna beserta API Key (tampilan terakhir)."""
    users = db.query(models.User).all()
    return users

@router.post("/users/create")
def create_user(
    username: str,
    email: str,
    admin_verified: bool = Depends(verify_admin),
    db: Session = Depends(get_db)
):
    """Membuat user baru dan meng-generate API Key acak."""
    
    # Cek duplikasi
    db_user = db.query(models.User).filter(models.User.email == email).first()
    if db_user:
        raise HTTPException(status_code=400, detail="Email already registered")

    # Generate API Key sederhana
    # Format: prod-sk-16randomhex
    api_key_raw = secrets.token_hex(16)
    api_key = f"prod-sk-{api_key_raw}"
    
    # Hashing API Key sebelum simpan ke DB (Best Practice Keamanan)
    # Namun untuk SaaS kecil yang butuh melihat kunci lagi, kita simpan raw dulu.
    # Jika Anda ingin super aman, simpan hashnya, dan simpan rawnya di tempat lain.
    
    new_user = models.User(
        username=username,
        email=email,
        api_key=api_key, # Disimpan raw agar mudah dicopy user
        is_active=True
    )
    
    db.add(new_user)
    db.commit()
    db.refresh(new_user)
    
    return {"message": "User created successfully", "api_key": api_key}

Setelah file dibuat, daftarkan router ini di main.py.

Update: /www/wwwroot/app-collection/ao/main.py
# ... import existing
from app.api.v1.endpoints import admin  # Import admin

app.include_router(
    admin.router, 
    prefix="/api/v1/admin", 
    tags=["Admin Management"]
)

19. Admin Dashboard UI

Kita buat file HTML terpisah untuk Admin. File ini hanya bisa diakses oleh Anda (admin).

File: /www/wwwroot/ao.baktimakmur.com/admin.html
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AO Admin Panel</title>
    <link rel="stylesheet" href="/static/css/style.css">
    <style>
        .admin-card { border: 2px solid var(--primary); }
        .user-list { margin-top: 20px; }
        .user-item { background: #fff; padding: 10px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; }
        .api-key-display { font-family: monospace; background: #f1f5f9; padding: 2px 5px; border-radius: 4px; }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>Admin Panel</h1>
            <p>Kelola Pengguna & API Keys</p>
        </header>

        <!-- Form Buat User Baru -->
        <section class="card admin-card">
            <h2>Tambah Pengguna Baru</h2>
            <form id="createUserForm">
                <div class="form-group">
                    <label>Username</label>
                    <input type="text" id="username" required>
                </div>
                <div class="form-group">
                    <label>Email</label>
                    <input type="email" id="email" required>
                </div>
                <button type="submit" style="background: #10b981;">Buat User</button>
            </form>
            <div id="createResult" style="margin-top:10px; display:none;">
                <p style="color:green; font-weight:bold;">API Key Baru: <span id="newApiKey"></span></p>
                <small>Salin sekarang! Kunci ini tidak akan muncul lagi.</small>
            </div>
        </section>

        <!-- Daftar User -->
        <section class="card">
            <h2>Daftar Pengguna</h2>
            <button onclick="loadUsers()">Refresh List</button>
            <div id="userList" class="user-list">
                <p>Memuat data...</p>
            </div>
        </section>
    </div>

    <script src="/static/js/admin.js"></script>
</body>
</html>

20. Logika Admin (JavaScript)

File JS ini menangani komunikasi dengan Admin API. Perhatikan bagian pengiriman Header X-Admin-Key.

File: /www/wwwroot/ao.baktimakmur.com/static/js/admin.js
// Konfigurasi
const ADMIN_API_URL = '/api/v1/admin';
// PASSWORD ADMIN HARUS SAMA DENGAN YANG DISET DI main.py / admin.py
const ADMIN_SECRET = 'rahasia_admin_ganti_ini_di_production';

// Fungsi Buat User
document.getElementById('createUserForm').addEventListener('submit', async (e) => {
    e.preventDefault();
    
    const username = document.getElementById('username').value;
    const email = document.getElementById('email').value;
    const btn = e.target.querySelector('button');

    btn.disabled = true;
    btn.textContent = 'Membuat...';

    try {
        const response = await fetch(`${ADMIN_API_URL}/users/create?username=${username}&email=${email}`, {
            method: 'POST',
            headers: {
                'X-Admin-Key': ADMIN_SECRET
            }
        });

        const data = await response.json();

        if (response.ok) {
            // Tampilkan API Key baru
            document.getElementById('newApiKey').textContent = data.api_key;
            document.getElementById('createResult').style.display = 'block';
            
            // Reset form
            e.target.reset();
            loadUsers(); // Refresh list
        } else {
            alert('Gagal: ' + data.detail);
        }

    } catch (error) {
        alert('Error koneksi: ' + error.message);
    } finally {
        btn.disabled = false;
        btn.textContent = 'Buat User';
    }
});

// Fungsi Load List User
async function loadUsers() {
    const listContainer = document.getElementById('userList');
    listContainer.innerHTML = 'Memuat...';

    try {
        const response = await fetch(`${ADMIN_API_URL}/users`, {
            method: 'GET',
            headers: {
                'X-Admin-Key': ADMIN_SECRET
            }
        });

        const users = await response.json();

        let html = '';
        users.forEach(user => {
            html += `
                <div class="user-item">
                    <div>
                        <strong>${user.username}</strong> (${user.email})<br>
                        <small>Active: ${user.is_active}</small>
                    </div>
                    <div>
                        <span class="api-key-display">${user.api_key}</span>
                    </div>
                </div>
            `;
        });

        listContainer.innerHTML = html;

    } catch (error) {
        listContainer.innerHTML = 'Gagal memuat data. Pastikan Password Admin benar.';
    }
}

// Load saat halaman dibuka
loadUsers();

Integrasi Final

Sekarang Anda memiliki cara resmi untuk membuat user:

  1. Buka http://ao.baktimakmur.com/admin.html
  2. Masukkan username dan email.
  3. Klik Buat User.
  4. Salin API Key yang muncul.
  5. Update file app.js (Bagian 4) dengan API Key baru tersebut.
PENTING KEAMANAN:
  • File admin.html sebaiknya dilindungi dengan password server (.htaccess atau Nginx auth_basic) agar tidak bisa diakses sembarang orang.
  • Ubah variabel ADMIN_SECRET di Python dan JS menjadi sesuatu yang sangat sulit ditebak.