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:
- Buka
http://ao.baktimakmur.com/admin.html - Masukkan username dan email.
- Klik Buat User.
- Salin API Key yang muncul.
- Update file
app.js(Bagian 4) dengan API Key baru tersebut.
PENTING KEAMANAN:
- File
admin.htmlsebaiknya dilindungi dengan password server (.htaccess atau Nginx auth_basic) agar tidak bisa diakses sembarang orang. - Ubah variabel
ADMIN_SECRETdi Python dan JS menjadi sesuatu yang sangat sulit ditebak.