- Published on
Tahap 3 – Integrasi ESP-Gateway dengan Server Kustom
- Authors
Proyek Serial HortiLink: Tahap 3 – Integrasi ESP-Gateway dengan Server Kustom
Rangkuman Proyek:
Selamat datang di Proyek Serial HortiLink: Tahap 3 – Integrasi ESP-Gateway dengan Server Kustom
Setelah persiapan perangkat dan sensor selesai, kini kita memasuki Tahap 3: Integrasi ESP-Gateway dengan Server Kustom. Di sinilah semua data mulai hidup dan bekerja untuk kita—mulai dari koneksi sensor di lapangan hingga server yang menyimpan dan menampilkan data secara real-time!
Di tahap ini, HortiLink berubah dari sekadar perangkat pemantau jadi sistem lengkap yang bisa diakses kapan saja, dari mana saja. Dengan sistem komunikasi antar perangkat IoT yang stabil, server khusus untuk menyimpan data historis, dan dasbor yang keren, kita bisa langsung tahu apa yang terjadi di lahan—termasuk notifikasi real-time yang mengingatkan saat ada yang butuh perhatian ekstra. 🌱✨
Yuk, simak langkah-langkah seru di Tahap 3 ini!

Konsep HortiLink Tahap3 - sumber ChatGpt
Seluruh update file tahap3 ini bisa dilihat pada github
Berikut rangkuman Tahap 3 dari proyek HortiLink dengan gaya santai tapi tetap informatif:
Tahap 3: Integrasi ESP-Gateway dengan Server Kustom
Di tahap ini, HortiLink mulai lebih canggih dengan menghubungkan perangkat IoT (ESP-Lora) ke server kustom untuk menangkap dan menampilkan data secara real-time! 🎉
- 1.1 Pengembangan Komunikasi Antar ESP-Lora
- 1.2 Pengembangan Server Kustom
- 1.3 Pengembangan Dasbor dan Notifikasi Real-Time
- Alasan Pemilihan Sub-Bab 1.2 Pengembangan Server Kustom
- Kreteria rancangan
- Setup Project
- Pattern MVC dan Atomic Design
- Otentikasi dan Otorisasi
- Mengapa Otentikasi dan Otorisasi Itu Penting?
- Tujuan Otentikasi dan Otorisasi di HortiLink
- Manfaat Langsung bagi HortiLink
- 1. Instalasi dan Setup NextAuth
- 2. Integrasi NextAuth dengan MongoDB Menggunakan Mongoose
- 3. Middleware untuk Proteksi Rute Berdasarkan Role
- 4. Kontrol Akses di Komponen Client
- 5. Fitur Lanjutan: Redirect Setelah Login dan Logout
1.1 Pengembangan Komunikasi Antar ESP-Lora
- Koneksi ESP-Lora ke Wi-Fi: Satu ESP-Lora bertindak sebagai jembatan ke server dengan koneksi Wi-Fi, sementara yang lain berkomunikasi melalui LoRa. Jadi, semua data dari sensor tetap terhubung tanpa harus memiliki Wi-Fi di setiap perangkat!
- Protokol Komunikasi: Kita pakai MQTT atau HTTP untuk mengirim data dari gateway ke server. MQTT ideal untuk kirim data kecil secara berkala, cocok banget buat kebutuhan IoT seperti ini.
1.2 Pengembangan Server Kustom
- Desain Infrastruktur Server: Buat server khusus HortiLink yang menerima data dari gateway. Server ini bisa di cloud untuk skalabilitas atau di server lokal jika lebih aman dan sesuai kebutuhan internal.
- Database Time-Series (TSDB): Untuk menyimpan data historis sensor, kita gunakan TSDB seperti InfluxDB. Dengan ini, kita bisa melacak tren data, misalnya pola kelembapan dan suhu sepanjang waktu.
1.3 Pengembangan Dasbor dan Notifikasi Real-Time
- Antarmuka Dashboard: Bangun dasbor real-time yang user-friendly untuk memantau data dari sensor, kontrol perangkat, dan bisa diakses dari mana saja, baik lewat PC maupun mobile.
- Notifikasi dan Alarm: Fitur ini penting! Secara otomatis, sistem akan mengirimkan notifikasi saat ada parameter yang melewati batas yang telah ditentukan (misalnya pH atau kelembapan rendah), sehingga pengguna bisa cepat bertindak.
Alasan Pemilihan Sub-Bab 1.2 Pengembangan Server Kustom
Pada Tahap 3: Integrasi ESP-Gateway dengan Server Kustom, sub-bab 1.2 Pengembangan Server Kustom dipilih sebagai fokus utama karena fungsi server yang vital dalam manajemen data sensor, keamanan, dan analisis jangka panjang. Server kustom bukan hanya menjadi pusat pengumpulan data yang diterima dari ESP-Gateway, namun juga berperan dalam menyediakan fondasi untuk pemrosesan data, penyimpanan historis, dan kemudahan akses bagi pengguna akhir.
Sentralisasi Data dan Skalabilitas
- Server kustom dirancang untuk menerima dan menyimpan data secara terpusat, memberikan kontrol penuh atas akses, keamanan, dan performa sistem. Pilihan infrastruktur server—baik cloud maupun lokal—memastikan fleksibilitas dalam menyesuaikan kapasitas dengan kebutuhan proyek di masa depan, terutama jika ada peningkatan volume data atau penambahan perangkat sensor.
Penyimpanan Time-Series untuk Analisis Jangka Panjang
- Menggunakan Time Series Database (TSDB) seperti InfluxDB, server ini dapat menyimpan data sensor secara historis, memfasilitasi pemantauan tren serta analisis data jangka panjang. Penyimpanan yang berbasis waktu memungkinkan deteksi pola yang memengaruhi produktivitas, seperti tren kelembapan, suhu, atau konsentrasi nutrisi dalam tanah. Dengan pengelolaan data historis yang baik, sistem dapat menghasilkan laporan dan insight yang mendukung pengambilan keputusan berbasis data.
Ketersediaan Data untuk Layanan Real-Time
- Server ini menyediakan data yang terus diperbarui untuk mendukung fitur dasbor dan notifikasi real-time di tahap selanjutnya. Struktur dan penyimpanan data yang efisien memastikan integrasi yang lancar antara sistem dan antarmuka pengguna, memudahkan pemantauan dan pengaturan perangkat dari jarak jauh.
Oleh karena itu, fokus pada Pengembangan Server Kustom sebagai pusat penyimpanan dan pengelolaan data di Tahap 3 akan membangun fondasi teknis yang kuat, memungkinkan pengelolaan data yang optimal dan berkelanjutan untuk HortiLink.
Kreteria rancangan
- Platform : Next.js versi terbaru dengan typescript serta app-router
- Style : Tailwind css dan flowbite
- Server/hosting : netlify
- Database : MongoDB Atlas, mongose (ODM)
- Integrated Development Editor (IDE) : vs-code
- Memanfaatkan ESLint dan Prettier agar menjaga kode tetap konsisten
- Pattern menggunakan MVC dan atomic design
- 1. Platform: Next.js Versi Terbaru dengan TypeScript dan App Router
Pemilihan Next.js sebagai platform utama didasarkan pada keunggulannya dalam mengembangkan aplikasi web yang cepat, dinamis, dan skalabel. Next.js versi terbaru dilengkapi dengan dukungan TypeScript yang memungkinkan penulisan kode yang lebih aman dan bebas dari kesalahan tipe data, yang sangat penting untuk proyek berbasis data seperti HortiLink. Dengan menggunakan app-router sebagai pengatur rute utama, aplikasi ini mendapatkan manfaat penuh dari kemampuan server dan client routing yang mulus, mengoptimalkan kinerja dan pengalaman pengguna dalam menjelajahi berbagai fitur.
- 2. Style: Tailwind CSS dan Flowbite
Dalam hal styling dan antarmuka pengguna, Tailwind CSS dan Flowbite dipilih untuk memberikan pendekatan desain yang fleksibel dan cepat. Tailwind CSS adalah framework CSS yang berbasis utility-first, yang memungkinkan pengembang untuk membangun desain yang sepenuhnya responsif dengan efisiensi tinggi. Kombinasi dengan Flowbite, komponen UI berbasis Tailwind yang siap pakai, memungkinkan pengembangan antarmuka yang konsisten dan mudah digunakan. Solusi ini memberikan dasar desain yang modern dan responsif, memastikan tampilan yang profesional di berbagai perangkat, termasuk desktop dan mobile.
- 3. Server/Hosting: Netlify
Netlify dipilih sebagai server hosting untuk mendukung otomatisasi proses build dan deploy. Netlify menyediakan integrasi yang seamless dengan Git, memungkinkan pengembangan dan pengujian berkelanjutan setiap kali ada pembaruan kode. Kemampuan Netlify dalam menangani deployment dengan optimasi untuk static dan server-side rendering di Next.js juga menjadikannya pilihan ideal untuk aplikasi yang memerlukan kecepatan akses tinggi. Selain itu, Netlify mempermudah pengelolaan lingkungan variabel untuk menyimpan kredensial dan API key yang dibutuhkan aplikasi.
- 4. Database: MongoDB Atlas dengan Mongoose (ODM)
Untuk penyimpanan data, MongoDB Atlas digunakan sebagai basis data NoSQL yang dikelola di cloud, memberikan skalabilitas tinggi yang diperlukan untuk menangani volume data sensor yang terus meningkat. Mongoose sebagai Object Data Modeling (ODM) dipilih untuk memberikan struktur data yang lebih ketat pada MongoDB, sehingga memudahkan pengembang dalam memvalidasi, mengelola, dan memanipulasi data di dalam aplikasi. Dengan MongoDB Atlas, aplikasi dapat memanfaatkan keamanan bawaan, backup otomatis, dan replikasi data yang memastikan keandalan dan ketersediaan data secara konsisten.
- 5. Integrated Development Editor (IDE): Visual Studio Code
Visual Studio Code (VS Code) digunakan sebagai Integrated Development Environment (IDE) utama, karena kemudahan penggunaannya, kustomisasi yang tinggi, dan dukungan ekosistem ekstensi yang kaya. VS Code mendukung TypeScript, linting, debugging, dan fitur-fitur produktivitas lainnya, termasuk integrasi langsung dengan Git. Dengan ekstensi tambahan untuk linting dan format, seperti ESLint dan Prettier, serta konektivitas ke MongoDB, VS Code memberikan pengalaman pengembangan yang terintegrasi penuh untuk setiap aspek proyek HortiLink.
- 6. Menggunakan ESLint dan Prettier untuk Konsistensi Kode
Untuk menjaga kualitas dan konsistensi kode, ESLint dan Prettier diterapkan. ESLint berfungsi sebagai linter kode yang mendeteksi potensi kesalahan dan memastikan standar kode yang konsisten, sesuai dengan praktik terbaik. Prettier digunakan sebagai alat pemformatan otomatis yang memastikan kode tetap bersih, terstruktur, dan mengikuti gaya penulisan yang seragam. Dengan menerapkan kedua alat ini, proyek HortiLink akan memiliki kode yang lebih mudah dipelihara, dibaca, dan dikembangkan, serta meminimalisir kesalahan sintaks dan ketidakkonsistenan.
- 7. Pattern: Menggunakan MVC dan Atomic Design
Proyek ini menggunakan pendekatan Model-View-Controller (MVC) dan Atomic Design untuk arsitektur dan desain komponen. MVC adalah pola arsitektur yang memisahkan aplikasi menjadi tiga bagian utama:
- Model untuk mengelola data dan logika bisnis,
- View untuk antarmuka pengguna,
- Controller untuk menghubungkan Model dan View, menangani logika aplikasi, dan merespons tindakan pengguna.
Pola ini memudahkan dalam pengembangan modular dan memungkinkan tim untuk bekerja pada bagian aplikasi secara terpisah tanpa mengganggu keseluruhan sistem.
Atomic Design, di sisi lain, adalah pendekatan desain komponen yang memecah antarmuka pengguna menjadi bagian-bagian yang lebih kecil dan dapat digunakan kembali, mulai dari atom (elemen terkecil) hingga organisme (kumpulan komponen kompleks). Dengan menerapkan Atomic Design pada HortiLink, setiap elemen UI menjadi modular, dapat digunakan ulang, dan mudah dikelola, menjadikan antarmuka pengguna lebih konsisten dan fleksibel untuk dikembangkan.
Setup Project
Berikut ini adalah langkah-langkah setup awal proyek HortiLink dengan kriteria yang disebutkan, yaitu menggunakan Next.js (dengan TypeScript dan app-router), Tailwind CSS serta Flowbite, Netlify sebagai server hosting, dan MongoDB Atlas sebagai database.
- Langkah 1: Membuat Proyek Next.js dengan TypeScript
Instalasi Proyek Next.js dengan TypeScript Jalankan perintah berikut untuk membuat proyek Next.js baru dengan TypeScript:
npx create-next-app@latest hortilink --typescript cd hortilinkProyek ini akan terbuat dengan
app-routersecara default di Next.js versi terbaru (sejak versi 13).Menjalankan Server Pengembangan Pastikan semuanya berjalan dengan baik dengan menjalankan:
npm run devAkses aplikasi pada
http://localhost:3000untuk melihat halaman awal.
- Langkah 2: Konfigurasi Tailwind CSS dan Flowbite
Instalasi Tailwind CSS Instal Tailwind CSS dan dependensi yang diperlukan:
npm install -D tailwindcss postcss autoprefixer npx tailwindcss init -pKonfigurasi Tailwind Di dalam file
tailwind.config.js, tambahkan lokasi file yang akan digunakan untuk menulis CSS:/** @type {import('tailwindcss').Config} */ module.exports = { content: [ './app/**/*.{js,ts,jsx,tsx}', './pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}', './node_modules/flowbite-react/**/*.js', ], theme: { extend: {}, }, plugins: [require('flowbite/plugin')], };Buat File CSS Utama Buat file
globals.cssdi dalam folderstyles(gantilah isi file jika sudah ada) dan tambahkan konfigurasi berikut:@tailwind base; @tailwind components; @tailwind utilities;Instalasi Flowbite Tambahkan Flowbite dan Flowbite React:
npm install flowbite flowbite-reactIntegrasi Flowbite di Next.js Import
globals.cssdi fileapp/layout.tsxagar Tailwind CSS dan Flowbite bisa digunakan di seluruh halaman:import '../styles/globals.css';
- Langkah 3: Mengatur Koneksi ke MongoDB Atlas
Buat Database di MongoDB Atlas
- Buat akun di MongoDB Atlas jika belum punya.
- Buat kluster baru dan tambahkan database bernama
hortilink. - Buat user dan password untuk akses database, dan simpan connection string, misalnya:
mongodb+srv://<username>:<password>@clustername.mongodb.net/hortilink?retryWrites=true&w=majority
Instalasi Mongoose untuk Next.js Instal Mongoose sebagai ODM (Object Data Modeling) untuk menghubungkan aplikasi ke MongoDB:
npm install mongooseKonfigurasi Koneksi MongoDB Buat file
lib/mongodb.tsuntuk mengatur koneksi ke MongoDB:import mongoose from 'mongoose'; const MONGODB_URI = process.env.MONGODB_URI as string; if (!MONGODB_URI) { throw new Error( 'Please define the MONGODB_URI environment variable inside .env.local', ); } let cached = global.mongoose; if (!cached) { cached = global.mongoose = { conn: null, promise: null }; } async function dbConnect() { if (cached.conn) { return cached.conn; } if (!cached.promise) { const opts = { bufferCommands: false, }; cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => { return mongoose; }); } cached.conn = await cached.promise; return cached.conn; } export default dbConnect;Menambahkan Variabel Lingkungan Buat file
.env.localdi root proyek, lalu tambahkan connection string MongoDB:MONGODB_URI=mongodb+srv://<username>:<password>@clustername.mongodb.net/hortilink?retryWrites=true&w=majorityInisialisasi Database dalam Komponen Next.js Pada komponen atau halaman yang memerlukan akses ke MongoDB, import
dbConnectdan panggil di dalamgetServerSidePropsatau API route.Contoh di API route
app/api/sensordata/route.ts:import { NextResponse } from 'next/server'; import dbConnect from '../../../lib/mongodb'; import SensorData from '../../../models/sensorData'; // Buat model seperti pada langkah sebelumnya export async function GET() { await dbConnect(); const data = await SensorData.find({}); return NextResponse.json(data); }
- Langkah 4: Deploy ke Netlify
Instal Netlify CLI (Opsional) Instal Netlify CLI jika ingin mengelola deploy melalui terminal:
npm install -g netlify-cliPush Proyek ke GitHub atau GitLab Netlify akan mengambil source code dari repositori GitHub atau GitLab. Pastikan proyek di-push ke repositori.
Konfigurasi Build di Netlify
- Masuk ke akun Netlify dan buat proyek baru dengan menghubungkan ke repositori.
- Tambahkan variabel environment
MONGODB_URIdi pengaturan Netlify, sama seperti yang ada di.env.local.
Build Command dan Publish Directory
- Build Command:
npm run build - Publish Directory:
out
- Build Command:
Jalankan Deploy Netlify akan otomatis membangun dan menerbitkan aplikasi berdasarkan pengaturan ini.
- Langkah 5: Pengaturan VS Code
Instalasi Ekstensi VS Code
- Tailwind CSS IntelliSense: Menambahkan dukungan autocompletion untuk Tailwind CSS.
- ESLint dan Prettier: Untuk menjaga konsistensi dan kualitas kode.
- MongoDB for VS Code: Jika ingin mengelola MongoDB langsung dari VS Code.
Konfigurasi ESLint dan Prettier Jika ingin menjaga kode tetap konsisten, tambahkan konfigurasi ESLint dan Prettier dengan TypeScript di proyek Anda. Penggunaan ESLint dan Prettier sangat membantu dalam pengembangan proyek, terutama dalam menjaga kualitas dan konsistensi kode. Berikut ini fungsi dan keuntungan utama dari keduanya:
Lebih lanjut ESLint & Prettier
- 1. ESLint: Fungsi dan Keuntungan
Fungsi ESLint:
- Linting (Pengecekan Kode): ESLint memeriksa kode untuk menemukan dan menandai masalah potensial seperti kesalahan sintaks, penggunaan variabel yang tidak dideklarasikan, atau kode yang tidak digunakan.
- Enforcing Best Practices: ESLint membantu menjaga standar kode dengan memperingatkan jika ada pola yang tidak sesuai dengan praktik terbaik, misalnya, kesalahan penggunaan React Hooks atau penamaan yang tidak konsisten.
- Mendeteksi Bug Lebih Dini: Dengan linting otomatis, ESLint dapat mendeteksi bug atau kesalahan kecil sebelum mereka menjadi masalah besar. Misalnya, saat ada variabel yang typo atau tidak diimpor dengan benar.
Keuntungan ESLint:
Konsistensi Kode: Dengan aturan yang sama untuk semua pengembang di tim, kode menjadi konsisten dan mudah dibaca.
Efisiensi Review Kode: Mengurangi waktu untuk revisi manual saat review, karena banyak kesalahan sudah diperbaiki oleh ESLint.
Mengurangi Bug di Produksi: Deteksi dini terhadap kesalahan yang mungkin muncul di runtime, sehingga aplikasi lebih stabil dan andal.
2. Prettier: Fungsi dan Keuntungan
Fungsi Prettier:
- Formatter Kode: Prettier secara otomatis memformat kode agar rapi dan seragam. Ini mencakup aspek seperti penempatan kurung, indentasi, panjang baris, dan penggunaan tanda koma.
- Menghilangkan Debat Gaya Kode: Dengan Prettier, tim tidak perlu mendebatkan gaya penulisan kode, karena Prettier menerapkan gaya yang sama ke semua bagian kode secara otomatis.
Keuntungan Prettier:
- Meningkatkan Readability: Dengan format yang konsisten, kode menjadi lebih mudah dibaca dan dipahami, terutama dalam tim yang besar.
- Hemat Waktu: Developer tidak perlu memformat kode secara manual, sehingga fokus bisa dialihkan ke pengembangan fitur.
- Konsistensi Estetika: Mencegah ketidakkonsistenan estetika, seperti indentasi yang tidak seragam atau panjang baris yang berlebihan.
Mengapa Menggunakan Keduanya Bersamaan?
- Kolaborasi Optimal: ESLint menangani masalah kualitas dan praktik terbaik, sementara Prettier menjaga penampilan dan estetika kode. Bersama-sama, keduanya menciptakan standar yang konsisten dan mudah diterapkan oleh semua pengembang di tim.
- Integrasi Otomatis: Dengan konfigurasi yang tepat, Prettier dapat dijalankan sebagai bagian dari ESLint. Ini memastikan kode bebas dari kesalahan sekaligus tampak rapi secara otomatis, tanpa langkah tambahan.
Singkatnya, ESLint dan Prettier bersama-sama membuat proses pengembangan lebih rapi, konsisten, dan minim kesalahan. Ini sangat membantu menjaga kualitas kode di proyek yang berkembang, terutama dalam tim pengembang yang lebih besar.
Konfigurasi ESLint & Prettier
Konfigurasi ESLint dan Prettier bisa dilakukan dengan mudah, dan sebagian besar bisa diotomatisasi dengan beberapa perintah. Mari ikuti langkah-langkah berikut untuk menyiapkan ESLint dan Prettier secara otomatis di proyek Next.js dengan TypeScript.
1. Instalasi ESLint dan Prettier beserta Plugin Pendukung
Jalankan perintah berikut di terminal proyek untuk menginstal ESLint, Prettier, dan plugin yang dibutuhkan:
npm install -D eslint prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y @typescript-eslint/parser @typescript-eslint/eslint-pluginPenjelasan singkat tentang setiap package:
- eslint: Linter utama untuk mendeteksi dan memperbaiki masalah sintaks.
- prettier: Formatter untuk menjaga konsistensi kode.
- eslint-config-prettier: Menonaktifkan aturan ESLint yang bisa bertentangan dengan Prettier.
- eslint-plugin-prettier: Menjalankan Prettier sebagai aturan ESLint.
- @typescript-eslint/parser dan @typescript-eslint/eslint-plugin: Mendukung linting untuk TypeScript.
- eslint-plugin-react, eslint-plugin-react-hooks, dan eslint-plugin-jsx-a11y: Plugin yang disarankan untuk proyek berbasis React agar kodenya lebih sesuai dengan standar aksesibilitas dan praktik terbaik.
2. Inisialisasi Konfigurasi ESLint
Jalankan perintah berikut untuk membuat file konfigurasi
.eslintrc.json:npx eslint --initSaat diminta, pilih opsi berikut:
- How would you like to use ESLint?: Choose "To check syntax, find problems, and enforce code style".
- What type of modules does your project use?: Choose "JavaScript modules (import/export)".
- Which framework does your project use?: Choose "React".
- Does your project use TypeScript?: Choose "Yes".
- Where does your code run?: Choose "Browser".
- What format do you want your config file to be in?: Choose "JSON".
Setelah selesai, file
.eslintrc.jsonakan dihasilkan, dan kita akan menambahkan beberapa konfigurasi tambahan di langkah berikutnya.3. Menambahkan Konfigurasi ke
.eslintrc.jsonEdit file
.eslintrc.jsonagar kompatibel dengan Prettier dan TypeScript. Berikut adalah contoh konfigurasi yang lengkap:{ "env": { "browser": true, "es2021": true }, "extends": [ "eslint:recommended", "plugin:react/recommended", "plugin:@typescript-eslint/recommended", "plugin:react-hooks/recommended", "plugin:jsx-a11y/recommended", "plugin:prettier/recommended" ], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaFeatures": { "jsx": true }, "ecmaVersion": "latest", "sourceType": "module" }, "plugins": ["react", "@typescript-eslint", "prettier"], "rules": { "prettier/prettier": "error", "react/react-in-jsx-scope": "off", // Khusus Next.js, karena React diimpor otomatis "@typescript-eslint/explicit-module-boundary-types": "off" }, "settings": { "react": { "version": "detect" } } }Konfigurasi ini mengatur ESLint untuk:
- Menggunakan plugin Prettier dan menandai error jika kode tidak mengikuti format Prettier.
- Mendukung React, React Hooks, JSX Accessibility, dan TypeScript dengan linting yang tepat.
- Menonaktifkan beberapa aturan yang kurang relevan untuk proyek Next.js, seperti impor
Reactsecara eksplisit.
4. Buat Konfigurasi Prettier
Buat file konfigurasi Prettier dengan nama
.prettierrcdi root proyek Anda:{ "semi": true, "singleQuote": true, "tabWidth": 2, "printWidth": 80, "trailingComma": "es5", "arrowParens": "avoid" }File ini mengatur gaya format seperti penggunaan titik koma, tanda kutip tunggal, lebar baris, dan lain-lain sesuai preferensi umum.
5. Tambahkan Ignore Files
Agar ESLint dan Prettier tidak memeriksa file yang tidak diperlukan, buat file
.eslintignoredan.prettierignoredi root proyek. Contoh isi dari kedua file ini:.eslintignore
node_modules/ .next/ out/.prettierignore
node_modules/ .next/ out/6. Menambahkan Script di
package.jsonTambahkan script berikut di
package.jsonuntuk menjalankan ESLint dan Prettier secara otomatis:"scripts": { "lint": "eslint . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix", "format": "prettier --write ." }- lint: Mengecek kode menggunakan ESLint.
- lint:fix: Mengecek dan memperbaiki masalah ESLint secara otomatis.
- format: Menjalankan Prettier untuk memformat seluruh proyek.
7. Konfigurasi VS Code untuk Mendukung ESLint dan Prettier
Instal ekstensi ESLint dan Prettier - Code Formatter di VS Code untuk mendukung linting dan formatting secara otomatis.
Tambahkan juga pengaturan berikut di
settings.jsonVS Code untuk mengaktifkan format otomatis saat penyimpanan file:{ "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": true } }Langkah Tambahan: Memastikan Integrasi Otomatis
Sekarang Anda dapat menjalankan linting dan format secara otomatis setiap kali menyimpan file di VS Code. Anda juga bisa menjalankan perintah berikut untuk linting dan format manual:
npm run lint npm run format
Pattern MVC dan Atomic Design
Menggabungkan MVC dengan Atomic Design adalah pilihan yang sangat baik untuk membuat aplikasi seperti HortiLink menjadi lebih terstruktur dan terorganisir! Dengan pendekatan ini, kita bisa menjaga logika bisnis dan manajemen data dalam pola MVC, sambil merapikan antarmuka pengguna dengan menggunakan Atomic Design untuk komponen UI. Ini memberikan struktur kode yang lebih modular dan rapi, terutama ketika berhadapan dengan fitur kompleks dan antarmuka yang kaya.
Berikut cara mengimplementasikan kombinasi MVC dan Atomic Design di Next.js:
1. Konsep Penggabungan MVC dan Atomic Design
MVC (Model-View-Controller): Fokus pada pemisahan antara data (Model), logika bisnis (Controller), dan tampilan (View). MVC akan mengelola data dan logika aplikasi.
Atomic Design: Struktur desain UI dari komponen kecil (Atoms) hingga komponen kompleks (Molecules, Organisms, Templates, dan Pages). Atomic Design mengatur komponen UI agar reusable dan terstruktur.
2. Struktur Folder
Kita akan membagi proyek ke dalam dua bagian utama:
- MVC Structure: Di sini akan ada folder
models,controllers, danservices. - Atomic Design Structure: Folder
componentsakan dibagi menjadiatoms,molecules,organisms,templates, danpages.
Berikut adalah contoh struktur foldernya:
hortilink/
├── app/ # App Router untuk API dan halaman
│ ├── api/ # API routes Next.js
│ │ ├── farm/ # API untuk data kebun
│ │ └── sensor/ # API untuk data sensor
│ ├── farm/ # Halaman untuk fitur kebun
│ │ └── page.tsx # Halaman daftar kebun
│ └── sensor/ # Halaman untuk fitur sensor
│ └── page.tsx # Halaman daftar data sensor
├── components/ # Atomic Design untuk UI
│ ├── atoms/ # Komponen terkecil seperti button, input
│ ├── molecules/ # Gabungan atoms, misalnya form field
│ ├── organisms/ # Gabungan molecules, misalnya form atau tabel lengkap
│ ├── templates/ # Layout atau template halaman
│ └── pages/ # Halaman lengkap dengan layout dan komponen
├── controllers/ # Controller untuk logika bisnis
│ ├── FarmController.ts # Logika bisnis untuk kebun
│ └── SensorController.ts # Logika bisnis untuk data sensor
├── models/ # Model database menggunakan Mongoose
│ ├── FarmModel.ts # Model data untuk kebun
│ └── SensorDataModel.ts # Model data untuk sensor
├── services/ # Service untuk logika umum dan helper
│ └── db.ts # Koneksi database MongoDB
└── utils/ # Utilitas umum
└── apiHelpers.ts
3. Implementasi MVC
- a. Model
Model ini akan berfungsi sebagai representasi data di MongoDB. Misalnya, kita bisa membuat model
FarmModeluntuk kebun danSensorDataModeluntuk data sensor.// models/FarmModel.ts import mongoose, { Schema, model, models } from 'mongoose'; const farmSchema = new Schema({ name: { type: String, required: true }, luas: { type: Number, required: true }, lokasi: { type: String, required: true }, jenisSayuran: { type: String, required: true }, }); const FarmModel = models.Farm || model('Farm', farmSchema); export default FarmModel;// models/SensorDataModel.ts import mongoose, { Schema, model, models } from 'mongoose'; const sensorDataSchema = new Schema({ farmId: { type: mongoose.Schema.Types.ObjectId, ref: 'Farm', required: true, }, suhu: { type: Number, required: true }, humidity: { type: Number, required: true }, ec_tds: { type: Number, required: true }, timestamp: { type: Date, default: Date.now }, }); const SensorDataModel = models.SensorData || model('SensorData', sensorDataSchema); export default SensorDataModel;Lebih detail terkait data model dengan mongoDB-Mongose
- b. Controller
Controller akan mengelola logika bisnis. Di Next.js, kita akan memanggil controller ini di dalam API Routes di
app/api.// controllers/FarmController.ts import FarmModel from '../models/FarmModel'; import dbConnect from '../services/db'; export default class FarmController { static async getAllFarms() { await dbConnect(); return FarmModel.find({}); } static async createFarm(data: any) { await dbConnect(); return FarmModel.create(data); } }- c. Service Layer
Tambahkan service untuk koneksi database agar dapat digunakan di seluruh controller:
// services/db.ts import mongoose from 'mongoose'; const MONGODB_URI = process.env.MONGODB_URI || ''; if (!MONGODB_URI) { throw new Error('Please define the MONGODB_URI environment variable'); } let cached = global.mongoose; if (!cached) { cached = global.mongoose = { conn: null, promise: null }; } async function dbConnect() { if (cached.conn) return cached.conn; if (!cached.promise) { cached.promise = mongoose .connect(MONGODB_URI) .then((mongoose) => mongoose); } cached.conn = await cached.promise; return cached.conn; } export default dbConnect;4. Implementasi Atomic Design untuk Komponen UI
Di sini, kita akan membuat antarmuka pengguna berdasarkan prinsip Atomic Design:
Atoms: Komponen UI terkecil, seperti tombol atau input.
Molecules: Gabungan beberapa atoms, seperti field input lengkap dengan label.
Organisms: Gabungan beberapa molecules, misalnya tabel yang berisi data sensor atau data kebun.
Templates: Layout atau struktur halaman yang digunakan berulang kali.
Pages: Halaman penuh yang berisi semua komponen dan layout.
Contoh Implementasi Komponen Atomic Design
- a. Atoms
Misalnya, tombol sederhana
Button.tsxdicomponents/atoms:// components/atoms/Button.tsx import React from 'react'; type ButtonProps = { text: string; onClick: () => void; className?: string; }; const Button: React.FC<ButtonProps> = ({ text, onClick, className }) => ( <button onClick={onClick} className={`rounded bg-blue-500 px-4 py-2 text-white ${className}`} > {text} </button> ); export default Button;- b. Molecules
Field input yang lebih kompleks dengan label di
components/molecules/InputField.tsx:// components/molecules/InputField.tsx import React from 'react'; type InputFieldProps = { label: string; value: string; onChange: (e: React.ChangeEvent<HTMLInputElement>) => void; }; const InputField: React.FC<InputFieldProps> = ({ label, value, onChange, }) => ( <div> <label className="mb-2 block text-sm font-bold text-gray-700"> {label} </label> <input type="text" value={value} onChange={onChange} className="w-full rounded border px-3 py-2" /> </div> ); export default InputField;- c. Organisms
Tabel data sensor di
components/organisms/SensorTable.tsx:// components/organisms/SensorTable.tsx import React from 'react'; type SensorData = { _id: string; suhu: number; humidity: number; ec_tds: number; timestamp: string; }; const SensorTable: React.FC<{ data: SensorData[] }> = ({ data }) => ( <table className="w-full table-auto"> <thead> <tr> <th>Suhu</th> <th>Humidity</th> <th>EC/TDS</th> <th>Timestamp</th> </tr> </thead> <tbody> {data.map((sensor) => ( <tr key={sensor._id}> <td>{sensor.suhu}°C</td> <td>{sensor.humidity}%</td> <td>{sensor.ec_tds} µS/cm</td> <td>{new Date(sensor.timestamp).toLocaleString()}</td> </tr> ))} </tbody> </table> ); export default SensorTable;
- 5. Mengintegrasikan di Halaman
Di App Router, kita bisa menggunakan struktur ini dalam halaman app/farm/page.tsx atau app/sensor/page.tsx, menggabungkan komponen dari Atomic Design dan mengakses data melalui controller.
Tentu, berikut adalah kerangka bab beserta outline untuk bagian Otentikasi dan Otorisasi pada proyek situs HortiLink. Kerangka ini bisa membantu menyusun penjelasan yang sistematis dan komprehensif, mulai dari pengenalan hingga implementasi praktis dengan NextAuth, MongoDB, dan middleware untuk proteksi rute berdasarkan role.
Otentikasi dan Otorisasi
Dalam aplikasi modern, terutama yang melibatkan data sensitif atau fungsi penting seperti HortiLink, otentikasi dan otorisasi adalah dua pilar yang tak bisa diabaikan. Keduanya berfungsi sebagai gerbang keamanan utama yang memastikan siapa saja yang mengakses aplikasi memang benar-benar pengguna yang memiliki izin. Otentikasi dan otorisasi adalah kunci untuk menjaga data dan fungsionalitas tetap aman dari akses yang tidak sah. Mari kita bahas satu per satu kenapa ini penting dan bagaimana kita menerapkannya di HortiLink.
Mengapa Otentikasi dan Otorisasi Itu Penting?
Melindungi Data dan Fungsi Kritikal
- Bayangkan sebuah aplikasi tanpa kontrol keamanan. Semua orang bisa masuk dan melihat data penting tanpa batasan. Bagi HortiLink, aplikasi yang menangani data operasional dan teknis, membuka akses tanpa filter bisa menimbulkan risiko besar, baik dari segi keamanan maupun stabilitas aplikasi.
- Otentikasi memastikan bahwa yang masuk ke aplikasi hanyalah orang-orang yang memiliki akun yang valid, bukan sembarang orang. Sementara itu, otorisasi memastikan bahwa hanya pengguna tertentu yang bisa mengakses data atau fungsi tertentu, tergantung pada peran dan izin mereka.
Membatasi Akses Berdasarkan Peran
- Di HortiLink, kita akan memiliki berbagai jenis pengguna: Admin, Engineer, Teknisi/Operator, dan Guest. Setiap peran ini punya hak dan akses berbeda-beda.
- Misalnya, Admin mungkin bisa mengakses dan mengubah segala hal, dari data pengguna hingga pengaturan sistem. Engineer hanya bisa mengakses data teknis, sedangkan Operator mungkin hanya perlu mengakses data operasional tertentu. Dengan otorisasi berbasis role, kita bisa memastikan setiap pengguna memiliki akses yang sesuai, tanpa risiko membuka data kritikal kepada orang yang tidak berhak.
Tujuan Otentikasi dan Otorisasi di HortiLink
Di sinilah NextAuth masuk sebagai sistem otentikasi, yaitu proses yang memungkinkan pengguna login dengan akun masing-masing. Selain memudahkan proses login, NextAuth mendukung berbagai metode otentikasi, termasuk melalui akun sosial media (seperti Google dan GitHub) dan login berbasis email dan password. Dengan NextAuth, pengguna dapat dengan mudah masuk ke dalam aplikasi, dan kita bisa memastikan mereka benar-benar adalah pengguna yang terdaftar.
Kemudian, untuk otorisasi berbasis role, kita akan menggunakan middleware yang akan mengelola izin akses setiap pengguna berdasarkan perannya. Artinya, setelah pengguna berhasil login, middleware ini akan memeriksa apakah mereka memiliki hak yang sesuai untuk mengakses halaman atau fitur tertentu di aplikasi.
Manfaat Langsung bagi HortiLink
Keamanan Data yang Lebih Baik
- Dengan otentikasi yang kuat dan kontrol otorisasi, data sensitif, seperti data teknis perangkat atau informasi operasional yang spesifik, hanya dapat diakses oleh pengguna yang tepat. Ini memastikan bahwa data selalu aman dan mencegah akses yang tidak sah.
Pengalaman Pengguna yang Disesuaikan
- Dengan menerapkan otorisasi berbasis role, setiap pengguna akan merasa bahwa aplikasi disesuaikan untuk peran mereka. Seorang teknisi hanya akan melihat bagian yang relevan dengan tugas operasional mereka, sementara admin akan mendapatkan akses ke semua data dan pengaturan yang mereka perlukan.
Menghindari Beban Teknis di Server
- Membatasi akses tidak hanya soal keamanan, tetapi juga efisiensi. Dengan memastikan pengguna hanya bisa mengakses halaman dan fungsi yang mereka butuhkan, kita bisa mengurangi beban di server dan meningkatkan performa aplikasi secara keseluruhan.
Jadi, dengan otentikasi dan otorisasi yang diterapkan melalui NextAuth dan middleware, HortiLink tidak hanya menjadi aplikasi yang aman tetapi juga efisien dan ramah pengguna. Setiap pengguna dapat menjalankan tugas mereka dengan tepat dan cepat, tanpa harus berurusan dengan data atau fungsi yang tidak relevan.
1. Instalasi dan Setup NextAuth
Pada bagian ini, akan dijelaskan langkah-langkah menginstal dan melakukan konfigurasi dasar NextAuth pada aplikasi HortiLink. NextAuth akan digunakan sebagai sistem otentikasi utama untuk mengelola login pengguna dan sesi aplikasi.
1.1 Pengantar NextAuth NextAuth adalah library otentikasi yang dirancang khusus untuk aplikasi Next.js. NextAuth memberikan solusi otentikasi yang mudah dan fleksibel dengan mendukung berbagai metode otentikasi, termasuk OAuth (misalnya Google, Facebook, GitHub) dan login berbasis email dan password.
Kelebihan NextAuth:
- Dukungan Berbagai Provider: NextAuth mendukung banyak provider otentikasi (Google, Facebook, GitHub, dll.), sehingga memudahkan pengguna untuk login menggunakan akun sosial media mereka.
- Sistem Berbasis JSON Web Tokens (JWT): Menggunakan JWT, NextAuth mampu mengelola sesi secara aman di client-side dan server-side, yang sangat cocok untuk aplikasi yang membutuhkan otentikasi lintas platform.
- Fleksibilitas Callback: NextAuth menyediakan berbagai callback yang memungkinkan kita untuk menyesuaikan proses otentikasi dan manajemen sesi sesuai kebutuhan aplikasi, termasuk penambahan data khusus seperti peran (role) pengguna.
1.2 Instalasi NextAuth Untuk memulai penggunaan NextAuth, lakukan instalasi library
next-authpada proyek Next.js dengan menggunakan perintah berikut:
npm install next-auth
Setelah instalasi selesai, NextAuth siap untuk dikonfigurasi sebagai sistem otentikasi di HortiLink.
1.3 Konfigurasi Dasar NextAuth Konfigurasi dasar NextAuth mencakup beberapa hal: menentukan provider otentikasi, menambahkan dukungan login berbasis username/password, serta mengatur callback untuk pengelolaan sesi. Berikut adalah penjelasan detail dari setiap langkah konfigurasi.
a. Menentukan Provider OAuth Langkah pertama adalah menentukan provider yang akan digunakan dalam otentikasi. Pada contoh ini, kita akan mengkonfigurasi Google, Facebook, dan GitHub sebagai provider login.
- Buat file konfigurasi NextAuth di
app/api/auth/[...nextauth]/route.ts. Pastikan struktur file ini sesuai dengan arsitektur Next.js yang menggunakan App Router. - Tambahkan konfigurasi setiap provider sebagai berikut:
// app/api/auth/[...nextauth]/route.ts import NextAuth from 'next-auth'; import GoogleProvider from 'next-auth/providers/google'; import FacebookProvider from 'next-auth/providers/facebook'; import GitHubProvider from 'next-auth/providers/github'; import CredentialsProvider from 'next-auth/providers/credentials'; export const authOptions = { providers: [ GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, }), FacebookProvider({ clientId: process.env.FACEBOOK_CLIENT_ID!, clientSecret: process.env.FACEBOOK_CLIENT_SECRET!, }), GitHubProvider({ clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET!, }), ], // Konfigurasi tambahan untuk pengelolaan sesi akan ditambahkan pada langkah berikut }; const handler = NextAuth(authOptions); export { handler as GET, handler as POST };- Pastikan Anda telah menambahkan variabel
GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET,FACEBOOK_CLIENT_ID,FACEBOOK_CLIENT_SECRET,GITHUB_CLIENT_ID, danGITHUB_CLIENT_SECRETpada file.env.localdi root proyek Next.js.
- Buat file konfigurasi NextAuth di
b. Menambahkan Login Berbasis Username dan Password dengan Credentials Provider Selain menggunakan provider OAuth, NextAuth juga mendukung login berbasis username dan password menggunakan Credentials Provider. Ini sangat bermanfaat jika aplikasi HortiLink juga ingin menyediakan opsi login khusus dengan email dan password.
Tambahkan konfigurasi CredentialsProvider pada array providers di authOptions:
// Lanjutan dari kode sebelumnya di app/api/auth/[...nextauth]/route.ts
CredentialsProvider({
name: 'Credentials',
credentials: {
username: { label: 'Username', type: 'text' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
// Logika otentikasi username/password
const user = { id: 1, name: 'user', email: 'user@example.com' }; // Contoh pengguna statis
// Verifikasi user dari database atau layanan lain di sini
if (user) {
return Promise.resolve(user);
} else {
return Promise.resolve(null);
}
},
});
Dengan menambahkan CredentialsProvider, pengguna dapat melakukan login menggunakan kombinasi username dan password. Catatan: pada aplikasi produksi, logika authorize harus diubah agar memvalidasi username/password dari basis data atau sumber lain yang sesuai.
- c. Mengatur Callback NextAuth untuk Pengelolaan Sesi dengan JSON Web Tokens (JWT) NextAuth memungkinkan kita untuk mengelola sesi menggunakan JWT. Kita dapat menambahkan callback
jwtdansessionuntuk menyimpan informasi tambahan di token sesi pengguna, seperti peran (role) pengguna atau ID pengguna.
Tambahkan konfigurasi berikut pada authOptions:
// Lanjutan dari konfigurasi di app/api/auth/[...nextauth]/route.ts
callbacks: {
async jwt({ token, user }) {
// Tambahkan data user ke token saat login pertama kali
if (user) {
token.id = user.id;
token.role = user.role || 'user'; // Menambahkan role default jika tidak ada
}
return token;
},
async session({ session, token }) {
// Tambahkan data dari token ke dalam sesi
session.user.id = token.id;
session.user.role = token.role;
return session;
},
},
session: {
strategy: 'jwt', // Gunakan JWT untuk mengelola sesi
},
Penjelasan:
- Callback
jwt: Callback ini menambahkan data ke JWT setiap kali sesi pengguna diperbarui. Di sini, kita menambahkanuser.iddanuser.roleke dalam token. - Callback
session: Callback ini menambahkan data dari token JWT ke dalam sesi pengguna, sehingga data ini bisa diakses di seluruh aplikasi. strategy: 'jwt': Menginstruksikan NextAuth untuk menggunakan JWT sebagai strategi penyimpanan sesi.
Dengan konfigurasi ini, aplikasi HortiLink siap menggunakan NextAuth untuk otentikasi, mendukung beberapa provider, login berbasis username/password, dan manajemen sesi berbasis JWT.
2. Integrasi NextAuth dengan MongoDB Menggunakan Mongoose
Pada bagian ini, akan dibahas cara mengintegrasikan NextAuth dengan MongoDB menggunakan Mongoose sebagai ODM (Object-Document Mapping). Integrasi ini diperlukan untuk menyimpan data pengguna secara persisten, termasuk informasi penting seperti role pengguna, yang digunakan dalam otorisasi akses pada aplikasi HortiLink.
2.1 Setup Koneksi MongoDB
Agar aplikasi dapat terhubung dengan MongoDB, kita perlu membuat file koneksi dan mengatur variabel lingkungan yang diperlukan untuk akses ke database MongoDB.
- a. Membuat File
mongodb.tsuntuk Koneksi dengan Mongoose
Buat file mongodb.ts di dalam folder lib/ atau folder lain yang sesuai di dalam proyek. File ini akan mengelola koneksi MongoDB, memastikan hanya satu koneksi yang digunakan selama aplikasi berjalan. Pendekatan ini juga membantu menghindari masalah koneksi ulang yang tidak diperlukan, terutama saat menggunakan fitur Fast Refresh di Next.js.
Berikut adalah contoh implementasi file mongodb.ts:
// lib/mongodb.ts
import mongoose from 'mongoose';
const MONGODB_URI = process.env.MONGODB_URI as string;
if (!MONGODB_URI) {
throw new Error(
'Please define the MONGODB_URI environment variable inside .env.local',
);
}
// Caching koneksi agar tidak membuat koneksi baru saat Fast Refresh
let cached = global.mongoose;
if (!cached) {
cached = global.mongoose = { conn: null, promise: null };
}
async function connectToDatabase() {
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
cached.promise = mongoose.connect(MONGODB_URI).then((mongoose) => {
return mongoose;
});
}
cached.conn = await cached.promise;
return cached.conn;
}
export default connectToDatabase;
Penjelasan:
MONGODB_URIadalah variabel lingkungan yang berisi URL koneksi ke MongoDB.Fungsi
connectToDatabasememastikan koneksi hanya dibuat satu kali selama aplikasi berjalan, dan koneksi ini disimpan dalam cache (cached.conn).Pendekatan ini membantu mengurangi masalah koneksi yang berlebihan, terutama dalam lingkungan pengembangan.
b. Pengaturan Environment Variable untuk Koneksi MongoDB
Tambahkan variabel lingkungan MONGODB_URI di file .env.local pada root proyek. Nilainya adalah URI koneksi MongoDB yang sesuai dengan pengaturan database yang digunakan.
MONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/mydatabase
Gantilah username, password, dan mydatabase sesuai dengan kredensial dan nama database yang digunakan.
2.2 Membuat Model User dengan Role di MongoDB
Selanjutnya, kita perlu membuat model User di MongoDB yang mencakup informasi seperti nama, email, dan role pengguna. Model ini akan membantu mengelola dan mengakses data pengguna di seluruh aplikasi, khususnya untuk keperluan otorisasi.
- a. Membuat Skema User Menggunakan Mongoose
Buat file User.ts di dalam folder models untuk mendefinisikan skema Mongoose yang mencakup field role. Berikut ini adalah contoh skema pengguna dengan field dasar yang diperlukan.
// models/User.ts
import mongoose, { Schema, Document, Model } from 'mongoose';
export interface IUser extends Document {
name: string;
email: string;
role: 'admin' | 'engineer' | 'operator' | 'guest';
}
const userSchema: Schema<IUser> = new Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
role: {
type: String,
enum: ['admin', 'engineer', 'operator', 'guest'],
default: 'guest',
},
});
const User: Model<IUser> =
mongoose.models.User || mongoose.model<IUser>('User', userSchema);
export default User;
Penjelasan:
- Field
name: Menyimpan nama pengguna, bersifat wajib (required: true). - Field
email: Menyimpan email pengguna yang harus unik (unique: true) untuk setiap pengguna. - Field
role: Menyimpan role pengguna dengan opsienum, yaitu hanya menerima nilai 'admin', 'engineer', 'operator', atau 'guest'. Role default adalah 'guest'.
Dengan model ini, kita bisa menyimpan dan mengelola pengguna di MongoDB serta memberikan role tertentu yang akan digunakan untuk keperluan otorisasi.
2.3 Menyimpan dan Mengambil Role dari Database
Dalam otentikasi NextAuth, kita bisa menggunakan callback jwt dan session untuk menambahkan data role dari database MongoDB ke dalam token sesi. Ini memungkinkan kita untuk memanfaatkan role pengguna di seluruh aplikasi, baik untuk proteksi rute maupun pembatasan akses di bagian-bagian tertentu aplikasi.
- a. Konfigurasi
authOptionsdi NextAuth untuk Menyimpan Role
Agar data role dari MongoDB tersimpan di token dan sesi pengguna, tambahkan konfigurasi authOptions berikut:
// app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
import CredentialsProvider from 'next-auth/providers/credentials';
import connectToDatabase from '@/lib/mongodb';
import User from '@/models/User';
export const authOptions = {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
CredentialsProvider({
name: 'Credentials',
credentials: {
email: { label: 'Email', type: 'text' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
// Otentikasi username/password
await connectToDatabase();
const user = await User.findOne({ email: credentials?.email });
if (user) {
return user;
} else {
return null;
}
},
}),
],
callbacks: {
async jwt({ token, user }) {
await connectToDatabase();
// Ambil data user dari database saat user login
if (user) {
const dbUser = await User.findOne({ email: user.email });
token.role = dbUser?.role || 'guest';
}
return token;
},
async session({ session, token }) {
// Menyimpan role dari token ke session
session.user.role = token.role;
return session;
},
},
session: {
strategy: 'jwt', // Menggunakan JWT untuk manajemen sesi
},
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
Penjelasan Konfigurasi:
- Callback
authorizedi CredentialsProvider: Digunakan untuk memvalidasi pengguna yang login menggunakan username dan password. Fungsi ini memeriksa email pengguna di MongoDB, dan jika ditemukan, user tersebut dikembalikan untuk melanjutkan sesi login. - Callback
jwt: Setiap kali pengguna login atau sesi mereka diperbarui, callback ini akan menambahkan dataroledari database MongoDB ke dalam token JWT. - Callback
session: Callback ini menambahkan data role dari token ke dalam sesi. Dengan begitu, role pengguna dapat diakses di seluruh aplikasi melalui sesi pengguna.
Dengan konfigurasi ini, NextAuth dapat menyimpan role dari database MongoDB ke dalam sesi pengguna, memungkinkan aplikasi untuk menentukan hak akses pengguna secara otomatis.
3. Middleware untuk Proteksi Rute Berdasarkan Role
Bagian ini akan menjelaskan penerapan Role-Based Access Control (RBAC) di aplikasi HortiLink menggunakan middleware Next.js. RBAC memungkinkan kita untuk membatasi akses ke halaman-halaman tertentu berdasarkan role pengguna, memastikan hanya pengguna dengan izin yang sesuai yang dapat mengakses halaman atau fitur tertentu.
3.1 Konsep Role-Based Access Control (RBAC)
Role-Based Access Control (RBAC) adalah sistem manajemen akses yang menentukan hak akses pengguna berdasarkan peran (role) mereka. RBAC membantu memastikan bahwa setiap pengguna hanya dapat mengakses halaman atau fitur yang sesuai dengan peran mereka dalam aplikasi.
Dalam aplikasi HortiLink, ada empat peran utama:
- Admin: Memiliki akses penuh ke semua halaman dan fitur.
- Engineer: Memiliki akses ke halaman teknis dan manajemen perangkat, tetapi tidak ke halaman administrasi.
- Operator: Memiliki akses terbatas hanya ke halaman pengoperasian perangkat.
- Guest: Hanya memiliki akses ke halaman umum yang tidak memerlukan login.
Untuk implementasi RBAC, kita akan menggunakan middleware untuk memeriksa role pengguna dan membandingkannya dengan level akses yang dibutuhkan setiap halaman. Middleware akan membatasi akses secara otomatis sesuai dengan prioritas role pengguna.
3.2 Implementasi Middleware di Next.js
NextAuth menyediakan fungsi withAuth yang memungkinkan kita membuat middleware untuk proteksi rute. Middleware ini akan membaca token sesi pengguna, memeriksa role mereka, dan menentukan apakah mereka memiliki hak akses yang cukup untuk mengakses halaman yang diminta.
- a. Membuat Konstanta
ROLE_PRIORITYdanPROTECTED_ROUTES
Pertama, kita definisikan konstanta ROLE_PRIORITY dan PROTECTED_ROUTES untuk memudahkan middleware dalam menentukan hak akses setiap role terhadap halaman yang dilindungi.
// middleware.ts
// Definisikan prioritas untuk setiap role
const ROLE_PRIORITY = {
admin: 1, // Akses penuh
engineer: 2, // Akses teknis
operator: 3, // Akses operasional
guest: 4, // Akses umum
};
// Tentukan rute yang dilindungi dan minimum prioritas aksesnya
const PROTECTED_ROUTES = {
'/admin': ROLE_PRIORITY.admin, // Hanya admin
'/engineer': ROLE_PRIORITY.engineer, // Admin dan engineer
'/operator': ROLE_PRIORITY.operator, // Admin, engineer, dan operator
'/public': ROLE_PRIORITY.guest, // Semua pengguna, termasuk guest
};
Penjelasan:
ROLE_PRIORITYmendefinisikan hierarki prioritas setiap role, di mana nilai lebih rendah menunjukkan prioritas lebih tinggi.PROTECTED_ROUTESmenentukan halaman yang membutuhkan proteksi, dengan level akses minimum berdasarkan prioritas.b. Implementasi Middleware
withAuthuntuk Proteksi Rute
Selanjutnya, kita buat middleware withAuth untuk memeriksa apakah pengguna memiliki hak akses yang cukup untuk mengakses rute tertentu. Middleware ini akan memeriksa role pengguna dan, jika diperlukan, mengarahkan mereka ke halaman unauthorized jika mereka tidak memiliki izin yang cukup.
Berikut adalah implementasi lengkap middleware di file middleware.ts:
// middleware.ts
import { withAuth } from 'next-auth/middleware';
import { NextResponse } from 'next/server';
// Definisikan role dan rute yang dilindungi
const ROLE_PRIORITY = {
admin: 1,
engineer: 2,
operator: 3,
guest: 4,
};
const PROTECTED_ROUTES = {
'/admin': ROLE_PRIORITY.admin,
'/engineer': ROLE_PRIORITY.engineer,
'/operator': ROLE_PRIORITY.operator,
'/public': ROLE_PRIORITY.guest,
};
export default withAuth(
function middleware(req) {
// Dapatkan token dari NextAuth middleware
const { token } = req.nextauth;
// Redirect ke halaman login jika pengguna belum login
if (!token) {
return NextResponse.redirect(new URL('/login', req.url));
}
// Ambil role pengguna dari token dan tentukan prioritasnya
const userRolePriority =
ROLE_PRIORITY[token.role as keyof typeof ROLE_PRIORITY] ||
ROLE_PRIORITY.guest;
// Ambil path dari URL yang diminta
const requestedPath = req.nextUrl.pathname;
// Periksa akses berdasarkan rute yang dilindungi
for (const [path, minPriority] of Object.entries(PROTECTED_ROUTES)) {
if (requestedPath.startsWith(path)) {
// Jika prioritas user lebih tinggi dari yang dibutuhkan, redirect ke halaman unauthorized
if (userRolePriority > minPriority) {
return NextResponse.redirect(new URL('/unauthorized', req.url));
}
break;
}
}
// Jika pengguna memiliki akses, lanjutkan ke halaman yang diminta
return NextResponse.next();
},
{
callbacks: {
authorized: ({ token }) => !!token, // Pastikan pengguna telah login
},
},
);
// Tentukan rute yang menggunakan middleware
export const config = {
matcher: [
'/admin/:path*',
'/engineer/:path*',
'/operator/:path*',
'/public/:path*',
],
};
Penjelasan Implementasi:
- Pengecekan Token: Middleware ini menggunakan
withAuthuntuk mendapatkan token dari sesi pengguna. Jika token tidak ada, artinya pengguna belum login, dan akan diarahkan ke halaman login. - Pengecekan Role dan Prioritas: Middleware mengecek role pengguna berdasarkan
token.roledan mencocokkannya denganROLE_PRIORITY. - Akses ke Rute yang Dilindungi: Middleware membandingkan
userRolePrioritydenganminPriorityyang ditentukan untuk rute yang diminta. Jika role pengguna tidak memenuhi syarat, mereka akan diarahkan ke halaman/unauthorized. - Config
matcher: Middleware ini diterapkan hanya pada rute-rute tertentu yang membutuhkan proteksi, seperti/admin,/engineer,/operator, dan/public.
3.3 Menangani Akses Ditolak
Untuk pengguna yang mencoba mengakses halaman tanpa izin yang cukup, middleware akan mengarahkan mereka ke halaman unauthorized. Halaman ini memberikan informasi bahwa akses ditolak karena kurangnya hak akses.
- Membuat Halaman
unauthorized
Buat halaman unauthorized di app/unauthorized/page.tsx untuk menampilkan pesan akses ditolak.
// app/unauthorized/page.tsx
export default function UnauthorizedPage() {
return (
<div>
<h1>Access Denied</h1>
<p>You do not have permission to view this page.</p>
<a href="/">Back to Home</a>
</div>
);
}
Penjelasan:
- Halaman ini memberikan pesan sederhana bahwa akses ke halaman ini ditolak karena pengguna tidak memiliki izin yang sesuai.
- Terdapat tautan kembali ke halaman utama untuk membantu navigasi pengguna.
3.4 Testing Middleware
Setelah middleware selesai diimplementasikan, lakukan pengujian untuk memastikan proteksi rute berjalan dengan baik sesuai dengan role masing-masing pengguna. Berikut adalah skenario pengujian yang direkomendasikan:
- Admin: Pastikan pengguna dengan role
adminmemiliki akses penuh ke halaman/admin,/engineer,/operator, dan/public. - Engineer: Pastikan pengguna dengan role
engineermemiliki akses ke halaman/engineer,/operator, dan/public, tetapi diarahkan ke halamanunauthorizedsaat mencoba mengakses/admin. - Operator: Pastikan pengguna dengan role
operatorhanya bisa mengakses halaman/operatordan/public, tetapi diarahkan ke halamanunauthorizedsaat mencoba mengakses/adminatau/engineer. - Guest: Pastikan pengguna dengan role
guesthanya bisa mengakses halaman/public, dan diarahkan ke halamanunauthorizedjika mencoba mengakses halaman lain yang dilindungi.
4. Kontrol Akses di Komponen Client
Pada bagian ini, akan dibahas cara mengelola kontrol akses di sisi client menggunakan NextAuth dan hook useSession. Dengan memanfaatkan useSession, kita dapat melakukan pengecekan role pengguna pada komponen atau halaman tertentu di client-side, serta menyesuaikan tampilan elemen UI berdasarkan role pengguna. Pendekatan ini memungkinkan aplikasi memberikan pengalaman yang lebih disesuaikan untuk setiap jenis pengguna.
4.1 Menggunakan useSession untuk Kontrol Akses
useSession adalah hook yang disediakan oleh NextAuth untuk mendapatkan informasi sesi pengguna yang sedang aktif. Dengan hook ini, kita dapat memeriksa status login dan role pengguna di setiap komponen client, memungkinkan pengaturan akses ke halaman tertentu secara dinamis berdasarkan role yang dimiliki pengguna.
- a. Penggunaan Dasar
useSessionuntuk Memeriksa Role
Berikut adalah contoh penggunaan dasar useSession untuk memeriksa status sesi dan role pengguna. Implementasi ini akan menampilkan pesan akses ditolak jika pengguna tidak memiliki role yang diperlukan untuk mengakses halaman tertentu.
Misalnya, pada halaman Dashboard yang hanya bisa diakses oleh pengguna dengan role admin, engineer, dan operator, kita dapat menerapkan useSession sebagai berikut:
// app/dashboard/page.tsx
'use client';
import { useSession } from 'next-auth/react';
export default function DashboardPage() {
const { data: session, status } = useSession();
// Menampilkan loading sementara menunggu status sesi
if (status === 'loading') return <p>Loading...</p>;
// Cek apakah user memiliki role yang diperbolehkan mengakses halaman dashboard
if (
!session ||
(session.user.role !== 'admin' &&
session.user.role !== 'engineer' &&
session.user.role !== 'operator')
) {
return <p>Access Denied. You do not have permission to view this page.</p>;
}
// Jika pengguna memiliki izin, tampilkan konten dashboard
return (
<div>
<h1>Dashboard</h1>
<p>Welcome, {session.user.name}!</p>
<p>Your role: {session.user.role}</p>
</div>
);
}
Penjelasan:
status === 'loading'digunakan untuk menampilkan pesan loading sementara data sesi sedang diproses.Kondisi
if (!session || ...)mengecek apakah pengguna tidak memiliki sesi yang valid atau memiliki role yang tidak diizinkan. Jika demikian, pengguna akan diberikan pesan Access Denied.Jika pengguna memiliki role yang sesuai (admin, engineer, atau operator), halaman Dashboard akan ditampilkan dengan pesan selamat datang.
b. Contoh Kontrol Akses Berbasis Role di Client-Side
Untuk memberikan kontrol akses yang lebih fleksibel, kita bisa membuat fungsi khusus di komponen yang mengecek apakah role pengguna memenuhi persyaratan akses. Berikut adalah contoh fungsi hasAccess yang mengembalikan true jika pengguna memiliki role yang diizinkan, atau false jika sebaliknya:
function hasAccess(session: any, allowedRoles: string[]) {
return session && allowedRoles.includes(session.user.role);
}
// Menggunakan fungsi di komponen
if (!hasAccess(session, ['admin', 'engineer', 'operator'])) {
return <p>Access Denied. You do not have permission to view this page.</p>;
}
Fungsi hasAccess ini dapat digunakan untuk memudahkan kontrol akses di berbagai komponen client, tanpa harus menuliskan ulang logika pengecekan role setiap kali.
4.2 Menyembunyikan atau Menampilkan Fitur Berdasarkan Role
Selain mengatur akses ke halaman secara keseluruhan, useSession juga memungkinkan kita untuk menyembunyikan atau menampilkan elemen UI tertentu berdasarkan role pengguna. Hal ini bermanfaat untuk memberikan pengalaman pengguna yang lebih personal, dengan menampilkan hanya fitur-fitur yang relevan sesuai peran masing-masing pengguna.
- a. Menampilkan atau Menyembunyikan Komponen Berdasarkan Role
Misalkan, kita ingin menampilkan fitur manajemen pengguna hanya untuk pengguna dengan role admin, dan fitur pengelolaan perangkat untuk pengguna dengan role engineer. Kita bisa menambahkan logika pada komponen UI sebagai berikut:
// app/dashboard/page.tsx
'use client';
import { useSession } from 'next-auth/react';
export default function DashboardPage() {
const { data: session, status } = useSession();
if (status === 'loading') return <p>Loading...</p>;
if (
!session ||
(session.user.role !== 'admin' &&
session.user.role !== 'engineer' &&
session.user.role !== 'operator')
) {
return <p>Access Denied. You do not have permission to view this page.</p>;
}
return (
<div>
<h1>Dashboard</h1>
<p>Welcome, {session.user.name}!</p>
<p>Your role: {session.user.role}</p>
{/* Fitur hanya untuk Admin */}
{session.user.role === 'admin' && (
<div>
<h2>Admin Features</h2>
<button>Manage Users</button>
</div>
)}
{/* Fitur hanya untuk Engineer */}
{session.user.role === 'engineer' && (
<div>
<h2>Engineer Features</h2>
<button>Manage Devices</button>
</div>
)}
{/* Fitur untuk Admin dan Engineer */}
{(session.user.role === 'admin' || session.user.role === 'engineer') && (
<div>
<h2>Technical Reports</h2>
<button>View Reports</button>
</div>
)}
</div>
);
}
Penjelasan:
Fitur Admin: Hanya ditampilkan jika
session.user.roleadalahadmin. Bagian ini dapat berisi fitur-fitur khusus yang hanya boleh diakses oleh admin, seperti manajemen pengguna.Fitur Engineer: Ditampilkan jika
session.user.roleadalahengineer. Fitur ini bisa berisi elemen UI untuk manajemen perangkat atau fitur teknis lainnya.Fitur Gabungan untuk Admin dan Engineer: Dengan kondisi
(session.user.role === 'admin' || session.user.role === 'engineer'), bagian ini akan ditampilkan kepada admin dan engineer. Misalnya, fitur untuk melihat laporan teknis.b. Menggunakan Komponen Terkondisi Berdasarkan Role
Untuk membuat kode lebih rapi, kita juga bisa membuat komponen yang secara dinamis menampilkan elemen berdasarkan role pengguna. Komponen ini dapat digunakan untuk menyederhanakan kontrol akses pada berbagai fitur.
// components/RoleBasedFeature.tsx
import { useSession } from 'next-auth/react';
interface RoleBasedFeatureProps {
allowedRoles: string[];
children: React.ReactNode;
}
export default function RoleBasedFeature({
allowedRoles,
children,
}: RoleBasedFeatureProps) {
const { data: session } = useSession();
if (!session || !allowedRoles.includes(session.user.role)) {
return null; // Jangan tampilkan apapun jika role tidak sesuai
}
return <>{children}</>;
}
Penjelasan:
- Komponen
RoleBasedFeaturemenerima dua properti:allowedRoles, yang merupakan array role yang diizinkan, danchildren, yang berisi elemen atau komponen yang akan ditampilkan. - Jika
session.user.rolecocok dengan salah satu role diallowedRoles, makachildrenakan ditampilkan; jika tidak, elemen tidak akan ditampilkan sama sekali.
Dengan komponen ini, kontrol akses di halaman dapat disederhanakan sebagai berikut:
// app/dashboard/page.tsx
import RoleBasedFeature from '@/components/RoleBasedFeature';
export default function DashboardPage() {
return (
<div>
<h1>Dashboard</h1>
{/* Fitur hanya untuk Admin */}
<RoleBasedFeature allowedRoles={['admin']}>
<h2>Admin Features</h2>
<button>Manage Users</button>
</RoleBasedFeature>
{/* Fitur hanya untuk Engineer */}
<RoleBasedFeature allowedRoles={['engineer']}>
<h2>Engineer Features</h2>
<button>Manage Devices</button>
</RoleBasedFeature>
{/* Fitur untuk Admin dan Engineer */}
<RoleBasedFeature allowedRoles={['admin', 'engineer']}>
<h2>Technical Reports</h2>
<button>View Reports</button>
</RoleBasedFeature>
</div>
);
}
Dengan pendekatan ini, kontrol akses menjadi lebih modular dan mudah dipelihara.
5. Fitur Lanjutan: Redirect Setelah Login dan Logout
Pada bagian ini, akan dijelaskan cara menambahkan fitur redirect untuk mengarahkan pengguna ke halaman tertentu setelah proses login atau logout. Fitur ini penting untuk memberikan pengalaman pengguna yang lebih intuitif, memastikan mereka diarahkan ke halaman yang relevan sesuai konteks (misalnya, dashboard setelah login dan halaman utama setelah logout). Berikut adalah implementasi redirect setelah login dan logout di NextAuth.
5.1 Redirect Setelah Login
Setelah pengguna berhasil login, seringkali kita ingin mengarahkan mereka ke halaman yang sesuai dengan peran atau tujuan mereka, seperti halaman dashboard atau halaman beranda khusus. NextAuth memungkinkan kita untuk mengimplementasikan redirect setelah login melalui beberapa metode, salah satunya adalah dengan menggunakan properti callbackUrl saat memanggil fungsi signIn.
- a. Menggunakan
callbackUrluntuk Redirect Dinamis
Untuk mengarahkan pengguna ke halaman tertentu setelah login, kita dapat menggunakan properti callbackUrl pada signIn. Properti ini akan menentukan URL tujuan setelah proses login selesai.
Contoh implementasi signIn dengan callbackUrl pada halaman login:
// app/login/page.tsx
'use client';
import { signIn } from 'next-auth/react';
import { useRouter } from 'next/navigation';
export default function LoginPage() {
const router = useRouter();
const handleLogin = async (provider: string) => {
await signIn(provider, { callbackUrl: '/dashboard' });
};
return (
<div>
<h1>Login to HortiLink</h1>
<button onClick={() => handleLogin('google')}>Login with Google</button>
<button onClick={() => handleLogin('facebook')}>
Login with Facebook
</button>
<button onClick={() => handleLogin('github')}>Login with GitHub</button>
</div>
);
}
Penjelasan:
Fungsi
handleLoginmemanggilsignIndengan parametercallbackUrlyang ditentukan (/dashboard), sehingga setelah login, pengguna akan diarahkan ke halaman/dashboard.Jika login berhasil, pengguna akan otomatis diarahkan ke URL yang ditentukan.
b. Menentukan Redirect Berdasarkan Role Pengguna
Dalam beberapa kasus, kita mungkin ingin mengarahkan pengguna ke halaman berbeda berdasarkan role mereka. Misalnya, admin diarahkan ke halaman manajemen, sementara operator diarahkan ke halaman operasional. Ini bisa dicapai dengan memanfaatkan callback redirect di konfigurasi NextAuth.
Tambahkan callback redirect ke dalam konfigurasi authOptions di app/api/auth/[...nextauth]/route.ts:
// app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth';
import { authOptions } from '@/lib/auth';
export const authOptions = {
...otherAuthOptions, // Konfigurasi NextAuth lainnya
callbacks: {
async redirect({ url, baseUrl }) {
// Arahkan berdasarkan role pengguna
if (url === '/dashboard') {
const session = await getSession();
// Redirect admin ke halaman manajemen khusus
if (session?.user?.role === 'admin') return `${baseUrl}/admin`;
// Redirect engineer ke halaman teknis
if (session?.user?.role === 'engineer') return `${baseUrl}/engineer`;
// Default ke dashboard umum
return `${baseUrl}/dashboard`;
}
return baseUrl;
},
},
};
export default NextAuth(authOptions);
Penjelasan:
- Callback
redirect: Callback ini memungkinkan kita untuk mengatur URL redirect setelah login berdasarkan kondisi tertentu, dalam hal ini berdasarkanrolepengguna. - Setelah login, admin akan diarahkan ke
/admin, engineer ke/engineer, dan role lainnya ke/dashboard.
5.2 Redirect Setelah Logout
Untuk mengarahkan pengguna ke halaman tertentu setelah mereka logout, kita bisa menggunakan properti callbackUrl saat memanggil fungsi signOut. Secara default, NextAuth akan mengarahkan pengguna ke halaman utama aplikasi setelah logout. Namun, kita bisa menentukan URL tujuan khusus jika diinginkan, seperti halaman landing atau halaman informasi publik.
- a. Redirect dengan
callbackUrlpadasignOut
Berikut adalah contoh implementasi signOut dengan callbackUrl pada komponen client:
// app/components/LogoutButton.tsx
'use client';
import { signOut } from 'next-auth/react';
export default function LogoutButton() {
const handleLogout = () => {
signOut({ callbackUrl: '/' }); // Redirect ke halaman utama setelah logout
};
return <button onClick={handleLogout}>Logout</button>;
}
Penjelasan:
Fungsi
handleLogoutmemanggilsignOutdengancallbackUrlyang ditentukan sebagai'/', sehingga setelah logout, pengguna akan diarahkan ke halaman utama (/).Komponen
LogoutButtonini dapat digunakan di berbagai halaman atau komponen lain di aplikasi untuk memberikan opsi logout kepada pengguna.b. Menggunakan Redirect Default untuk Sign Out
Jika kita tidak menentukan callbackUrl saat memanggil signOut, NextAuth akan otomatis mengarahkan pengguna ke halaman utama aplikasi (URL dasar atau root) setelah logout. Untuk aplikasi dengan kebutuhan redirect yang lebih dinamis setelah logout, kita dapat menyesuaikan callbackUrl langsung dalam pengaturan NextAuth atau di bagian frontend menggunakan logika yang relevan sesuai konteks aplikasi.
Contoh implementasi redirect tanpa callbackUrl pada signOut:
// app/components/LogoutButton.tsx
import { signOut } from 'next-auth/react';
export default function LogoutButton() {
return <button onClick={() => signOut()}>Logout</button>;
}
Dengan implementasi ini, NextAuth akan mengarahkan pengguna ke halaman root (/) setelah logout tanpa perlu mengatur callbackUrl secara manual.
Catatan Penyusunan Artikel ini disusun sebagai materi edukasi dan referensi umum berdasarkan berbagai sumber pustaka, praktik lapangan, serta bantuan alat penulisan. Pembaca disarankan untuk melakukan verifikasi lanjutan dan penyesuaian sesuai dengan kondisi serta kebutuhan masing-masing sistem.