Cara Membuat Halaman Isolir Pelanggan di MikroTik Menggunakan WebProxy

Bagi pengelola jaringan seperti ISP atau RT/RW Net, menangani pelanggan yang telat membayar tagihan adalah hal yang rutin. Daripada mematikan koneksi mereka secara diam-diam yang sering berujung pada komplain “internet mati”, jauh lebih elegan jika kita mengalihkan (redirect) koneksi mereka ke sebuah Halaman Pemberitahuan Isolir.

Pada tutorial kali ini, kita akan membahas cara mengonfigurasi fitur isolir di MikroTik menggunakan kombinasi Firewall NAT dan WebProxy.

Prasyarat Sebelum Memulai

Sebelum mengeksekusi konfigurasi di router, pastikan Anda sudah menyiapkan hal-hal berikut:

  1. Hosting / Web Server: Anda sudah memiliki server atau hosting untuk menyimpan file halaman pemberitahuan isolir.
  2. Domain Halaman Isolir: Siapkan domain atau subdomain aktif, misalnya: isolir.namadomain.com.
  3. Range IP Pelanggan: Ketahui range IP pelanggan yang ingin diisolir (pada contoh ini kita menggunakan range 10.66.66.0-10.66.66.255).

Jika semua prasyarat sudah siap, mari kita mulai konfigurasinya! Buka terminal MikroTik Anda (via Winbox/SSH) dan ikuti langkah-langkah di bawah ini.

Langkah 1: Buat Address List untuk Domain Isolir

Langkah pertama adalah mendaftarkan domain halaman isolir kita ke dalam Address List. Ini berfungsi agar router mengenali ke mana pelanggan diizinkan untuk mengakses.

/ip firewall address-list
add address=isolir.namadomain.com list=isolir-page

Catatan: Sesuaikan isolir.namadomain.com dengan domain atau subdomain halaman isolir yang Anda gunakan.

Langkah 2: Buat Filter Rules (Blokir Akses Internet)

Selanjutnya, kita harus memblokir seluruh akses internet pelanggan yang diisolir, kecuali akses menuju halaman isolir dan akses ke server DNS (agar domain isolir tetap bisa di-resolve oleh perangkat pelanggan).

/ip firewall filter
add action=drop chain=forward comment="Isolir WebProxy" dst-address-list=!isolir-page protocol=tcp src-address=10.66.66.0-10.66.66.255
add action=drop chain=forward comment="Isolir WebProxy" dst-port=!53,5353 protocol=udp src-address=10.66.66.0-10.66.66.255

Penjelasan:

  • Baris pertama memblokir semua trafik TCP pelanggan, kecuali yang menuju ke isolir-page.
  • Baris kedua memblokir semua trafik UDP pelanggan, kecuali port 53 dan 5353 (DNS).

Langkah 3: Buat NAT Redirect ke WebProxy

Agar pelanggan yang mencoba membuka website lain (HTTP/HTTPS) otomatis terlempar ke proxy kita, gunakan rule NAT Redirect berikut:

/ip firewall nat
add action=redirect chain=dstnat comment="Isolir WebProxy" dst-address-list=!isolir-page dst-port=80,443 protocol=tcp src-address=10.66.66.0-10.66.66.255 to-ports=8080

Rule ini memaksa seluruh trafik web dari IP pelanggan yang terisolir untuk berbelok ke port 8080 (port WebProxy yang akan kita buat).

Langkah 4: Konfigurasi WebProxy (Pilih Sesuai Versi RouterOS)

MikroTik memiliki perbedaan perintah WebProxy antara RouterOS versi 6 dan versi 7. Gunakan script yang sesuai dengan OS di router Anda.

Untuk Pengguna RouterOS v6: Di ROS 6, kita menggunakan action deny yang digabungkan dengan fungsi redirect-to.

/ip proxy
set enabled=yes port=8080
/ip proxy access
add action=deny redirect-to="isolir.namadomain.com" src-address="10.66.66.0-10.66.66.255" comment="Isolir WebProxy"

Untuk Pengguna RouterOS v7: Di ROS 7, sintaksnya lebih rapi. Kita langsung menggunakan action redirect dengan action-data.

/ip proxy
set enabled=yes port=8080
/ip proxy access
add action=redirect action-data="isolir.namadomain.com" src-address=10.66.66.0-10.66.66.255 comment="Isolir WebProxy"

Langkah 5: Amankan WebProxy dari Akses Internet (Sangat Penting!)

Fitur WebProxy di MikroTik sangat rawan disalahgunakan oleh pihak luar (Open Proxy) jika diekspos ke publik internet. Kita harus menutup akses port 8080 dari sisi IP Publik/WAN kita menggunakan tabel RAW agar CPU router tidak terbebani serangan.

/ip firewall raw
add chain=prerouting dst-address=IP_PUBLIK_ANDA protocol=tcp dst-port=8080 action=drop comment="Isolir WebProxy - Secure Proxy"

Catatan: Ganti IP_PUBLIK_ANDA dengan IP Publik statis/WAN yang terpasang pada router Anda (contoh: 103.10.20.30).

💡 Tips Tambahan

Setelah menjalankan semua script di atas, buka menu IP > Firewall di Winbox Anda. Pastikan rule Filter dan NAT yang baru saja dibuat digeser (di-drag) posisinya ke urutan paling atas (Top Priority) agar rule ini dibaca lebih dulu oleh router mendahului rule Allow/Accept jaringan normal Anda.

Selamat mencoba! Dengan konfigurasi ini, kini setiap pelanggan yang IP-nya masuk ke dalam range 10.66.66.0-10.66.66.255 akan melihat halaman tagihan saat mereka mencoba berselancar di internet.

Tools:

Jika anda malas membaca dan meninginkan script generator agar tinggal copy paste, berikut bash scripting dan PHP.

Bash Scripting

#!/bin/bash

# Warna untuk output terminal
GREEN='\033[0;32m'
CYAN='\033[0;36m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color

# ==========================================
# FUNGSI VALIDASI
# ==========================================

is_valid_ip() {
    local ip=$1
    if [[ $ip =~ ^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$ ]]; then
        for i in 1 2 3 4; do
            if [ "${BASH_REMATCH[$i]}" -gt 255 ]; then return 1; fi
        done
        return 0
    fi
    return 1
}

is_valid_ip_range() {
    local input=$1
    if [[ $input =~ ^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/([0-9]{1,2})$ ]]; then
        local ip="${BASH_REMATCH[1]}"
        local cidr="${BASH_REMATCH[2]}"
        if ! is_valid_ip "$ip"; then return 1; fi
        if [ "$cidr" -gt 32 ]; then return 1; fi
        return 0
    elif [[ $input =~ ^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})-([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$ ]]; then
        local ip1="${BASH_REMATCH[1]}"
        local ip2="${BASH_REMATCH[2]}"
        if ! is_valid_ip "$ip1"; then return 1; fi
        if ! is_valid_ip "$ip2"; then return 1; fi
        return 0
    elif [[ $input =~ ^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$ ]]; then
        if ! is_valid_ip "$input"; then return 1; fi
        return 0
    fi
    return 1
}

is_valid_domain() {
    local domain=$1
    if [[ $domain =~ ^([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}(/.*)?$ ]]; then
        return 0
    fi
    return 1
}

# ==========================================
# MULAI PROGRAM
# ==========================================

clear
echo -e "${CYAN}======================================================${NC}"
echo -e "${CYAN}   MikroTik Isolir Configuration Generator (Strict)   ${NC}"
echo -e "${CYAN}======================================================${NC}"
echo ""

# 1. Validasi Domain Isolir
while true; do
    read -p "Masukkan Domain Halaman Isolir (contoh: isolir.domain.com atau isolir.domain.com/v1): " INPUT_DOMAIN
    if is_valid_domain "$INPUT_DOMAIN"; then break; else
        echo -e "${RED}[!] Format domain salah! (contoh valid: isolir.domain.com)${NC}"
    fi
done

# 2. Validasi Domain Billing/Client Area (Opsional, bisa di-skip)
while true; do
    read -p "(Jika ada) Masukkan Domain Billing/Client Area (pisahkan dengan koma jika lebih dari satu): " BILLING_INPUT

    # Jika di-skip/kosong
    if [ -z "$BILLING_INPUT" ]; then
        BILLING_ARRAY=()
        break
    fi

    # Memecah input berdasarkan koma menjadi array
    IFS=',' read -r -a BILLING_ARRAY <<< "$BILLING_INPUT"

    ALL_VALID=true
    for dom in "${BILLING_ARRAY[@]}"; do
        dom=$(echo "$dom" | xargs) # Menghapus spasi di awal/akhir
        if ! is_valid_domain "$dom"; then
            ALL_VALID=false
            echo -e "${RED}[!] Format domain salah pada: '$dom'${NC}"
        fi
    done

    if [ "$ALL_VALID" = true ]; then
        break
    fi
done

# 3. Validasi Port (1 - 65535)
while true; do
    read -p "Masukkan Port Web Proxy [Default: 8080]: " PORT
    PORT=${PORT:-8080}
    if [[ "$PORT" =~ ^[0-9]+$ ]] && [ "$PORT" -ge 1 ] && [ "$PORT" -le 65535 ]; then break; else
        echo -e "${RED}[!] Port harus berupa angka antara 1 - 65535!${NC}"
    fi
done

# 4 & 5. Nama Address List (Hardcoded) & Komentar
ADDR_LIST="isolir-component"
CUST_LIST="isolir-customer"
echo -e "\n${YELLOW}[i] Nama Address List diset otomatis:${NC}"
echo -e "    - Komponen (web isolir, billing, client area) : ${ADDR_LIST}"
echo -e "    - Pelanggan                                   : ${CUST_LIST}\n"

read -p "Masukkan Komentar Konfigurasi [Default: Isolir WebProxy]: " COMMENT
COMMENT=${COMMENT:-Isolir WebProxy}

# 6. Validasi Range IP Pelanggan
while true; do
    read -p "Masukkan Range IP/Prefix Pelanggan Isolir (contoh: 10.66.66.0/24): " IP_RANGE
    if is_valid_ip_range "$IP_RANGE"; then break; else
        echo -e "${RED}[!] Format IP tidak valid! Gunakan format IPv4, Prefix (Maks /32), atau Range IP (-)${NC}"
    fi
done

# 7. Validasi IP Publik
while true; do
    read -p "Masukkan IP Publik/WAN yang terpasang di Router (contoh: 103.x.x.x): " PUB_IP
    if is_valid_ip_range "$PUB_IP"; then break; else
        echo -e "${RED}[!] Format IP Publik tidak valid! Pastikan format IPv4 yang benar.${NC}"
    fi
done

# 8. Validasi Versi RouterOS
echo ""
while [[ "$ROS_VERSION" != "6" && "$ROS_VERSION" != "7" ]]; do
    read -p "Versi RouterOS yang digunakan? (Ketik 6 atau 7): " ROS_VERSION
    if [[ "$ROS_VERSION" != "6" && "$ROS_VERSION" != "7" ]]; then
        echo -e "${RED}[!] Harap masukkan angka 6 atau 7 saja!${NC}"
    fi
done

# 9. Validasi Nama File & Auto-Format .rsc
echo ""
read -p "Simpan output ke nama file apa? [Default: config-isolir.rsc]: " FILENAME
FILENAME=${FILENAME:-config-isolir.rsc}
if [[ ! "$FILENAME" =~ \.rsc$ ]]; then
    FILENAME="${FILENAME}.rsc"
fi
REMOVE_FILENAME="remove-${FILENAME}"

# Ekstrak host/domain (menghilangkan path)
DOMAIN_ONLY="${INPUT_DOMAIN%%/*}"

echo -e "\n${YELLOW}Generating configuration...${NC}\n"

# =========================================================
# MEMBUAT BLOK KONFIGURASI SETUP
# =========================================================
CONFIG=$(cat <<EOF
# =========================================================
# MikroTik Isolir Configuration - SETUP
# Generated by Script
# Date: $(date)
# =========================================================

# 0. Membersihkan konfigurasi lama (jika ada)
/ip firewall address-list remove [find comment="${COMMENT}"]
/ip firewall filter remove [find comment="${COMMENT}"]
/ip firewall nat remove [find comment="${COMMENT}"]
/ip firewall raw remove [find comment="${COMMENT} - Secure Proxy"]
/ip proxy access remove [find comment="${COMMENT}"]

# 1. Address List
/ip firewall address-list
# List untuk Komponen (Halaman Isolir)
add address=${DOMAIN_ONLY} list=${ADDR_LIST} comment="${COMMENT}"
EOF
)

# Loop untuk menambahkan semua Domain Billing ke Address List (jika ada)
if [ ${#BILLING_ARRAY[@]} -gt 0 ]; then
    CONFIG+=$'\n'"# List untuk Domain Billing/Client Area"
    for dom in "${BILLING_ARRAY[@]}"; do
        dom=$(echo "$dom" | xargs) # Menghapus spasi di awal/akhir
        BILLING_ONLY="${dom%%/*}"
        CONFIG+=$'\n'"add address=${BILLING_ONLY} list=${ADDR_LIST} comment=\"${COMMENT}\""
    done
fi

CONFIG+=$'\n'"# List untuk Pelanggan (Customer)"
CONFIG+=$'\n'"add address=${IP_RANGE} list=${CUST_LIST} comment=\"${COMMENT}\""

# Menambahkan blok Filter, NAT, & Proxy
CONFIG+=$'\n\n'$(cat <<EOF
# 2. Filter Rules
# Menambahkan rule accept agar akses ke komponen isolir (Isolir & Billing) tidak di-drop
/ip firewall filter
add action=accept chain=forward comment="${COMMENT}" dst-address-list=${ADDR_LIST} protocol=tcp src-address-list=${CUST_LIST}
# Rule drop untuk memblokir akses internet lainnya (kecuali DNS)
add action=drop chain=forward comment="${COMMENT}" dst-address-list=!${ADDR_LIST} protocol=tcp src-address-list=${CUST_LIST}
add action=drop chain=forward comment="${COMMENT}" dst-port=!53,5353 protocol=udp src-address-list=${CUST_LIST}

# 3. NAT Rules
/ip firewall nat
# Bypass NAT redirect khusus untuk trafik menuju komponen isolir (agar lolos dari proxy)
add action=accept chain=dstnat comment="${COMMENT}" dst-address-list=${ADDR_LIST} dst-port=80,443 protocol=tcp src-address-list=${CUST_LIST}
# Redirect traffic HTTP/HTTPS lainnya ke WebProxy
add action=redirect chain=dstnat comment="${COMMENT}" dst-address-list=!${ADDR_LIST} dst-port=80,443 protocol=tcp src-address-list=${CUST_LIST} to-ports=${PORT}
# SRCNAT agar pelanggan terisolir tetap bisa akses DNS di internet
add action=src-nat chain=srcnat comment="${COMMENT}" dst-port=53,5353 protocol=udp src-address-list=${CUST_LIST} to-addresses=${PUB_IP}
# SRCNAT agar pelanggan terisolir tetap bisa akses komponen isolir/billing di internet
add action=src-nat chain=srcnat comment="${COMMENT}" dst-address-list=${ADDR_LIST} src-address-list=${CUST_LIST} to-addresses=${PUB_IP}

# 4. Konfigurasi Web Proxy
/ip proxy
set enabled=yes port=${PORT}
/ip proxy access
EOF
)

if [ "$ROS_VERSION" == "7" ]; then
    CONFIG+=$'\n'"add action=redirect action-data=\"${INPUT_DOMAIN}\" src-address=${IP_RANGE} comment=\"${COMMENT}\""
else
    CONFIG+=$'\n'"add action=deny redirect-to=\"${INPUT_DOMAIN}\" src-address=\"${IP_RANGE}\" comment=\"${COMMENT}\""
fi

CONFIG+=$'\n\n'"# 5. Mengamankan WebProxy dari Internet (Block public access)"
CONFIG+=$'\n'"/ip firewall raw"
CONFIG+=$'\n'"add chain=prerouting dst-address=${PUB_IP} protocol=tcp dst-port=${PORT} action=drop comment=\"${COMMENT} - Secure Proxy\""

CONFIG+=$'\n\n'"# 6. Memastikan Rule Berada di Urutan Paling Atas (Top)"
CONFIG+=$'\n'"/ip firewall filter move [find comment=\"${COMMENT}\"] [:pick [find] 0]"
CONFIG+=$'\n'"/ip firewall nat move [find comment=\"${COMMENT}\"] [:pick [find] 0]"
CONFIG+=$'\n'"/ip firewall raw move [find comment=\"${COMMENT} - Secure Proxy\"] [:pick [find] 0]"
CONFIG+=$'\n'"/ip proxy access move [find comment=\"${COMMENT}\"] [:pick [find] 0]"

# =========================================================
# MEMBUAT BLOK KONFIGURASI REMOVE (HAPUS)
# =========================================================
REMOVE_CONFIG=$(cat <<EOF
# =========================================================
# MikroTik Isolir Configuration - REMOVE
# =========================================================
# Jika anda tidak yakin dengan konfigurasi ini atau ingin menghapus konfigurasi gunakan perintah dibawah ini:

/ip firewall address-list remove [find comment="${COMMENT}"]
/ip firewall filter remove [find comment="${COMMENT}"]
/ip firewall nat remove [find comment="${COMMENT}"]
/ip firewall raw remove [find comment="${COMMENT} - Secure Proxy"]
/ip proxy access remove [find comment="${COMMENT}"]

# (Opsional) Matikan fitur web proxy jika tidak ada rule lain yang menggunakannya di router ini:
# /ip proxy set enabled=no
EOF
)

# Menampilkan output REMOVE ke terminal (Output SETUP disembunyikan)
echo -e "${RED}================ SCRIPT REMOVE =======================${NC}"
echo "$REMOVE_CONFIG"
echo -e "${RED}======================================================${NC}\n"

# Menyimpan ke dalam file
echo "$CONFIG" > "$FILENAME"
echo "$REMOVE_CONFIG" > "$REMOVE_FILENAME"

echo -e "${GREEN}[+] Konfigurasi SETUP berhasil disimpan ke file : ${FILENAME}${NC}"
echo -e "${GREEN}[+] Konfigurasi REMOVE berhasil disimpan ke file: ${REMOVE_FILENAME}${NC}"
echo ""

PHP (Web UI)

<?php
$setup_config = '';
$remove_config = '';
$errors = [];

// ==========================================
// FUNGSI KEAMANAN & FILTERING
// ==========================================

// Mencegah celah XSS pada tampilan Form HTML
function e_html($string) {
    return htmlspecialchars($string ?? '', ENT_QUOTES, 'UTF-8');
}

// Mencegah RouterOS Command Injection (membersihkan tanda kutip)
function sanitize_ros($string) {
    return trim(str_replace(['"', "'", "\\", "`"], '', $string ?? ''));
}

// Validasi Domain (Format: sub.domain.tld atau sub.domain.tld/path)
function is_valid_domain($domain) {
    // Buang protokol HTTP/HTTPS jika user iseng memasukkannya
    $domain = preg_replace('#^https?://#', '', $domain);
    // Pisahkan path jika ada (misal isolir.com/v1 menjadi isolir.com)
    $domain_only = explode('/', $domain)[0];
    
    // Gunakan regex untuk memastikan format host valid
    if (preg_match('/^([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/', $domain_only)) {
        return true;
    }
    return false;
}

// Validasi IP / Prefix / Range
function is_valid_ip_range($input) {
    // Mode 1: CIDR (contoh: 192.168.1.0/24)
    if (preg_match('/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,2})$/', $input, $matches)) {
        $ip = $matches[1];
        $cidr = (int)$matches[2];
        return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false && $cidr >= 0 && $cidr <= 32;
    }
    // Mode 2: Range IP (contoh: 192.168.1.1-192.168.1.254)
    elseif (preg_match('/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})-(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/', $input, $matches)) {
        return filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false &&
               filter_var($matches[2], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
    }
    // Mode 3: Single IP
    else {
        return filter_var($input, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
    }
}


if ($_SERVER["REQUEST_METHOD"] == "POST") {
    // Ambil dan sanitasi data dari form secara ketat
    $isolir_input  = sanitize_ros($_POST['isolir_domain']);
    $billing_input = sanitize_ros($_POST['billing_domain']);
    $port          = sanitize_ros($_POST['proxy_port']);
    $comment       = sanitize_ros($_POST['comment']);
    $ip_range      = sanitize_ros($_POST['ip_range']);
    $pub_ip        = sanitize_ros($_POST['pub_ip']);
    $ros_version   = sanitize_ros($_POST['ros_version']);

    // ==========================================
    // PROSES VALIDASI INPUT
    // ==========================================

    if (empty($isolir_input)) {
        $errors[] = "Domain Halaman Isolir wajib diisi.";
    } elseif (!is_valid_domain($isolir_input)) {
        $errors[] = "Format Domain Isolir tidak valid! (Contoh yang benar: isolir.domain.com atau isolir.domain.com/v1)";
    }

    if (empty($ip_range)) {
        $errors[] = "Range IP Pelanggan wajib diisi.";
    } elseif (!is_valid_ip_range($ip_range)) {
        $errors[] = "Format Range IP Pelanggan tidak valid! Gunakan format IPv4, Prefix (/24), atau Range (-).";
    }

    if (empty($pub_ip)) {
        $errors[] = "IP Publik wajib diisi.";
    } elseif (!is_valid_ip_range($pub_ip)) {
        $errors[] = "Format IP Publik tidak valid! Pastikan menggunakan format IPv4 yang benar.";
    }

    // Proses dan Validasi Billing Domain jika diisi
    $billing_domains = [];
    if (!empty($billing_input)) {
        $arr = explode(',', $billing_input);
        foreach ($arr as $d) {
            $d = trim($d);
            if (!empty($d)) {
                if (!is_valid_domain($d)) {
                    $errors[] = "Format Domain Billing tidak valid pada: '{$d}'";
                } else {
                    $d = preg_replace('#^https?://#', '', $d);
                    $billing_domains[] = explode('/', $d)[0];
                }
            }
        }
    }

    // Nilai Default jika kosong/tidak valid
    if (!preg_match('/^[0-9]+$/', $port) || $port < 1 || $port > 65535) {
        if ($port !== '') $errors[] = "Port harus berupa angka antara 1 - 65535.";
        $port = '8080'; 
    }
    if ($comment === '') $comment = 'Isolir WebProxy';
    if ($ros_version !== '6' && $ros_version !== '7') $ros_version = '7';


    // JIKA TIDAK ADA ERROR, LANJUT GENERATE SCRIPT
    if (empty($errors)) {
        
        $addr_list = "isolir-component";
        $cust_list = "isolir-customer";

        $isolir_clean = preg_replace('#^https?://#', '', $isolir_input);
        $domain_only  = explode('/', $isolir_clean)[0];

        $date = date('Y-m-d H:i:s');

        // ==========================================
        // GENERATE SETUP SCRIPT
        // ==========================================
        $setup_config .= "# =========================================================\n";
        $setup_config .= "# MikroTik Isolir Configuration - SETUP\n";
        $setup_config .= "# Generated via Web Generator\n";
        $setup_config .= "# Date: {$date}\n";
        $setup_config .= "# =========================================================\n\n";

        $setup_config .= "# 0. Membersihkan konfigurasi lama (jika ada)\n";
        $setup_config .= "/ip firewall address-list remove [find comment=\"{$comment}\"]\n";
        $setup_config .= "/ip firewall filter remove [find comment=\"{$comment}\"]\n";
        $setup_config .= "/ip firewall nat remove [find comment=\"{$comment}\"]\n";
        $setup_config .= "/ip firewall raw remove [find comment=\"{$comment} - Secure Proxy\"]\n";
        $setup_config .= "/ip proxy access remove [find comment=\"{$comment}\"]\n\n";

        $setup_config .= "# 1. Address List\n";
        $setup_config .= "/ip firewall address-list\n";
        $setup_config .= "# List untuk Komponen (Halaman Isolir)\n";
        $setup_config .= "add address={$domain_only} list={$addr_list} comment=\"{$comment}\"\n";

        if (count($billing_domains) > 0) {
            $setup_config .= "# List untuk Domain Billing/Client Area\n";
            foreach ($billing_domains as $b_domain) {
                $setup_config .= "add address={$b_domain} list={$addr_list} comment=\"{$comment}\"\n";
            }
        }

        $setup_config .= "# List untuk Pelanggan (Customer)\n";
        $setup_config .= "add address={$ip_range} list={$cust_list} comment=\"{$comment}\"\n\n";

        $setup_config .= "# 2. Filter Rules\n";
        $setup_config .= "/ip firewall filter\n";
        $setup_config .= "add action=accept chain=forward comment=\"{$comment}\" dst-address-list={$addr_list} protocol=tcp src-address-list={$cust_list}\n";
        $setup_config .= "add action=drop chain=forward comment=\"{$comment}\" dst-address-list=!{$addr_list} protocol=tcp src-address-list={$cust_list}\n";
        $setup_config .= "add action=drop chain=forward comment=\"{$comment}\" dst-port=!53,5353 protocol=udp src-address-list={$cust_list}\n\n";

        $setup_config .= "# 3. NAT Rules\n";
        $setup_config .= "/ip firewall nat\n";
        $setup_config .= "add action=accept chain=dstnat comment=\"{$comment}\" dst-address-list={$addr_list} dst-port=80,443 protocol=tcp src-address-list={$cust_list}\n";
        $setup_config .= "add action=redirect chain=dstnat comment=\"{$comment}\" dst-address-list=!{$addr_list} dst-port=80,443 protocol=tcp src-address-list={$cust_list} to-ports={$port}\n";
        $setup_config .= "add action=src-nat chain=srcnat comment=\"{$comment}\" dst-port=53,5353 protocol=udp src-address-list={$cust_list} to-addresses={$pub_ip}\n";
        $setup_config .= "add action=src-nat chain=srcnat comment=\"{$comment}\" dst-address-list={$addr_list} src-address-list={$cust_list} to-addresses={$pub_ip}\n\n";

        $setup_config .= "# 4. Konfigurasi Web Proxy\n";
        $setup_config .= "/ip proxy\n";
        $setup_config .= "set enabled=yes port={$port}\n";
        $setup_config .= "/ip proxy access\n";

        if ($ros_version === '7') {
            $setup_config .= "add action=redirect action-data=\"{$isolir_input}\" src-address={$ip_range} comment=\"{$comment}\"\n";
        } else {
            $setup_config .= "add action=deny redirect-to=\"{$isolir_input}\" src-address=\"{$ip_range}\" comment=\"{$comment}\"\n";
        }

        $setup_config .= "\n# 5. Mengamankan WebProxy dari Internet (Block public access)\n";
        $setup_config .= "/ip firewall raw\n";
        $setup_config .= "add chain=prerouting dst-address={$pub_ip} protocol=tcp dst-port={$port} action=drop comment=\"{$comment} - Secure Proxy\"\n\n";

        $setup_config .= "# 6. Memastikan Rule Berada di Urutan Paling Atas (Top)\n";
        $setup_config .= "/ip firewall filter move [find comment=\"{$comment}\"] [:pick [find] 0]\n";
        $setup_config .= "/ip firewall nat move [find comment=\"{$comment}\"] [:pick [find] 0]\n";
        $setup_config .= "/ip firewall raw move [find comment=\"{$comment} - Secure Proxy\"] [:pick [find] 0]\n";
        $setup_config .= "/ip proxy access move [find comment=\"{$comment}\"] [:pick [find] 0]\n";

        // ==========================================
        // GENERATE REMOVE SCRIPT
        // ==========================================
        $remove_config .= "# =========================================================\n";
        $remove_config .= "# MikroTik Isolir Configuration - REMOVE\n";
        $remove_config .= "# =========================================================\n\n";
        $remove_config .= "/ip firewall address-list remove [find comment=\"{$comment}\"]\n";
        $remove_config .= "/ip firewall filter remove [find comment=\"{$comment}\"]\n";
        $remove_config .= "/ip firewall nat remove [find comment=\"{$comment}\"]\n";
        $remove_config .= "/ip firewall raw remove [find comment=\"{$comment} - Secure Proxy\"]\n";
        $remove_config .= "/ip proxy access remove [find comment=\"{$comment}\"]\n\n";
        $remove_config .= "# (Opsional) Matikan fitur web proxy jika tidak ada rule lain:\n";
        $remove_config .= "# /ip proxy set enabled=no\n";
    }
}
?>

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MikroTik Isolir Generator</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
    <style>
        body { font-family: 'Inter', sans-serif; background-color: #f8fafc; }
    </style>
</head>
<body class="text-gray-800 antialiased">

<div class="max-w-4xl mx-auto py-10 px-4 sm:px-6 lg:px-8">
    <div class="text-center mb-8">
        <h1 class="text-3xl font-bold text-slate-900 tracking-tight">MikroTik Isolir Generator</h1>
        <p class="text-slate-500 mt-2">Buat script otomatis untuk halaman isolir pelanggan dengan mudah dan aman.</p>
    </div>

    <!-- MAIN CARD -->
    <div class="bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden">
        
        <!-- NOTES / WARNING -->
        <div class="bg-yellow-50 border-l-4 border-yellow-400 p-4 m-6 mb-0 rounded-r-lg">
            <div class="flex">
                <div class="flex-shrink-0">
                    <svg class="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor">
                        <path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
                    </svg>
                </div>
                <div class="ml-3">
                    <h3 class="text-sm font-medium text-yellow-800">Catatan Penting</h3>
                    <div class="mt-2 text-sm text-yellow-700">
                        <p>Pastikan Anda <strong>memeriksa ulang (double-check)</strong> penulisan format IP, Prefix/CIDR, dan ejaan domain. Sistem akan menolak jika format yang dimasukkan salah.</p>
                    </div>
                </div>
            </div>
        </div>

        <!-- ERROR NOTIFICATIONS -->
        <?php if (!empty($errors)): ?>
        <div class="bg-red-50 border-l-4 border-red-500 p-4 m-6 mb-0 rounded-r-lg">
            <h3 class="text-sm font-bold text-red-800 mb-1">Ditemukan Kesalahan:</h3>
            <ul class="list-disc list-inside text-sm text-red-700">
                <?php foreach ($errors as $err): ?>
                    <li><?= e_html($err) ?></li>
                <?php endforeach; ?>
            </ul>
        </div>
        <?php endif; ?>

        <form method="POST" class="p-6 sm:p-8 space-y-6">
            
            <div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
                <!-- Domain Isolir -->
                <div class="sm:col-span-2">
                    <label class="block text-sm font-medium text-slate-700">Domain Halaman Isolir <span class="text-red-500">*</span></label>
                    <input type="text" name="isolir_domain" required placeholder="contoh: isolir.domain.com atau isolir.domain.com/v1" 
                           value="<?= e_html($_POST['isolir_domain'] ?? '') ?>"
                           class="mt-1 block w-full px-4 py-2 bg-slate-50 border border-slate-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 outline-none transition text-sm">
                </div>

                <!-- Domain Billing -->
                <div class="sm:col-span-2">
                    <label class="block text-sm font-medium text-slate-700">(Jika ada) Masukkan Domain Billing/Client Area</label>
                    <input type="text" name="billing_domain" placeholder="contoh: billing.domain.com, client.domain.com" 
                           value="<?= e_html($_POST['billing_domain'] ?? '') ?>"
                           class="mt-1 block w-full px-4 py-2 bg-slate-50 border border-slate-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 outline-none transition text-sm">
                    <p class="text-xs text-slate-500 mt-1">Pisahkan dengan koma jika lebih dari satu. Kosongkan jika tidak ada.</p>
                </div>

                <!-- Range IP -->
                <div>
                    <label class="block text-sm font-medium text-slate-700">Range IP/Prefix Pelanggan Isolir <span class="text-red-500">*</span></label>
                    <input type="text" name="ip_range" required placeholder="contoh: 10.66.66.0/24" 
                           value="<?= e_html($_POST['ip_range'] ?? '') ?>"
                           class="mt-1 block w-full px-4 py-2 bg-slate-50 border border-slate-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 outline-none transition text-sm">
                </div>

                <!-- IP Publik -->
                <div>
                    <label class="block text-sm font-medium text-slate-700">IP Publik/WAN yang terpasang di Router <span class="text-red-500">*</span></label>
                    <input type="text" name="pub_ip" required placeholder="contoh: 103.x.x.x" 
                           value="<?= e_html($_POST['pub_ip'] ?? '') ?>"
                           class="mt-1 block w-full px-4 py-2 bg-slate-50 border border-slate-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 outline-none transition text-sm">
                </div>

                <!-- Port -->
                <div>
                    <label class="block text-sm font-medium text-slate-700">Port Web Proxy</label>
                    <input type="number" name="proxy_port" placeholder="Default: 8080" 
                           value="<?= e_html($_POST['proxy_port'] ?? '8080') ?>"
                           class="mt-1 block w-full px-4 py-2 bg-slate-50 border border-slate-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 outline-none transition text-sm">
                </div>

                <!-- Komentar -->
                <div>
                    <label class="block text-sm font-medium text-slate-700">Komentar Konfigurasi</label>
                    <input type="text" name="comment" placeholder="Default: Isolir WebProxy" 
                           value="<?= e_html($_POST['comment'] ?? 'Isolir WebProxy') ?>"
                           class="mt-1 block w-full px-4 py-2 bg-slate-50 border border-slate-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 outline-none transition text-sm">
                </div>

                <!-- Versi RouterOS -->
                <div class="sm:col-span-2">
                    <label class="block text-sm font-medium text-slate-700 mb-2">Versi RouterOS <span class="text-red-500">*</span></label>
                    <div class="flex items-center space-x-6">
                        <label class="flex items-center cursor-pointer">
                            <input type="radio" name="ros_version" value="7" required <?= (($_POST['ros_version'] ?? '7') === '7') ? 'checked' : '' ?> class="w-4 h-4 text-blue-600 border-slate-300 focus:ring-blue-500">
                            <span class="ml-2 text-sm text-slate-700">RouterOS v7</span>
                        </label>
                        <label class="flex items-center cursor-pointer">
                            <input type="radio" name="ros_version" value="6" required <?= (($_POST['ros_version'] ?? '') === '6') ? 'checked' : '' ?> class="w-4 h-4 text-blue-600 border-slate-300 focus:ring-blue-500">
                            <span class="ml-2 text-sm text-slate-700">RouterOS v6</span>
                        </label>
                    </div>
                </div>
            </div>

            <div class="pt-4 border-t border-slate-100 flex items-center justify-between">
                <div class="text-xs text-slate-500">
                    <span class="font-semibold text-slate-700">Address-List otomatis dibuat:</span> <br>
                    <code class="bg-slate-100 px-1 rounded text-blue-600">isolir-component</code>, <code class="bg-slate-100 px-1 rounded text-blue-600">isolir-customer</code>
                </div>
                <button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-6 rounded-lg shadow-sm transition-colors focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
                    Generate Script
                </button>
            </div>
        </form>
    </div>

    <!-- RESULT SECTION -->
    <?php if (empty($errors) && !empty($setup_config)): ?>
    <div class="mt-8 space-y-6" id="result-section">
        
        <!-- SETUP SCRIPT -->
        <div class="bg-slate-900 rounded-2xl shadow-sm overflow-hidden">
            <div class="px-4 py-3 bg-slate-800 border-b border-slate-700 flex justify-between items-center">
                <h3 class="text-sm font-semibold text-slate-200">Konfigurasi SETUP</h3>
                <div class="flex space-x-2">
                    <button onclick="copyCode('setupCode')" class="text-xs bg-slate-700 hover:bg-slate-600 text-slate-200 px-3 py-1.5 rounded transition">Copy</button>
                    <button onclick="downloadCode('setupCode', 'setup-isolir.rsc')" class="text-xs bg-blue-600 hover:bg-blue-500 text-white px-3 py-1.5 rounded transition">Download .rsc</button>
                </div>
            </div>
            <div class="p-4 overflow-x-auto">
                <pre id="setupCode" class="text-xs text-green-400 font-mono leading-relaxed"><?= htmlspecialchars($setup_config) ?></pre>
            </div>
        </div>

        <!-- REMOVE SCRIPT -->
        <div class="bg-slate-900 rounded-2xl shadow-sm overflow-hidden">
            <div class="px-4 py-3 bg-red-900/40 border-b border-red-900/50 flex justify-between items-center">
                <h3 class="text-sm font-semibold text-red-400">Konfigurasi REMOVE (Hapus)</h3>
                <div class="flex space-x-2">
                    <button onclick="copyCode('removeCode')" class="text-xs bg-slate-700 hover:bg-slate-600 text-slate-200 px-3 py-1.5 rounded transition">Copy</button>
                    <button onclick="downloadCode('removeCode', 'remove-isolir.rsc')" class="text-xs bg-red-600 hover:bg-red-500 text-white px-3 py-1.5 rounded transition">Download .rsc</button>
                </div>
            </div>
            <div class="p-4 overflow-x-auto">
                <pre id="removeCode" class="text-xs text-red-300 font-mono leading-relaxed"><?= htmlspecialchars($remove_config) ?></pre>
            </div>
        </div>

    </div>
    <script>
        document.getElementById('result-section').scrollIntoView({ behavior: 'smooth' });
    </script>
    <?php endif; ?>

    <div class="mt-12 text-center text-sm text-slate-400">
        &copy; <?= date('Y') ?> MikroTik Isolir Generator. Created securely.
    </div>
</div>

<script>
    function copyCode(elementId) {
        const text = document.getElementById(elementId).innerText;
        navigator.clipboard.writeText(text).then(() => {
            alert("Script berhasil di-copy ke clipboard!");
        });
    }

    function downloadCode(elementId, filename) {
        const text = document.getElementById(elementId).innerText;
        const blob = new Blob([text], { type: 'text/plain' });
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        window.URL.revokeObjectURL(url);
        document.body.removeChild(a);
    }
</script>

</body>
</html>