diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..600d2d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode \ No newline at end of file diff --git a/monipel.png b/monipel.png new file mode 100644 index 0000000..f291306 Binary files /dev/null and b/monipel.png differ diff --git a/monipel/README.md b/monipel/README.md new file mode 100644 index 0000000..c14975c --- /dev/null +++ b/monipel/README.md @@ -0,0 +1,121 @@ +# Monipel | Mikmoni Template Login Hotspot + +Template login hotspot RouterOS ini dirancang untuk pelanggan mikmoni, meskipun demikian pengguna umum juga bisa menggunakan template ini, namun mungkin perlu beberapa penyesuaian. + +Template ini juga dibuat agar lebih mudah diatur oleh pengguna awam, pengguna hanya perlu mengubah beberapa nilai untuk menyesuaikan halaman login. + +## Konfigurasi + +Buka file `config.js`, ubah sesuai kebutuhan. + +### Mengaktifkan/Menonaktifkan Metode Login + +Untuk mengaktifkan metode login, ubah bagian metode login di dalam blok `loginMethod` menjadi `true`, untuk menonaktifkan ubah menjadi `false`. + +Untuk menentukan default metode login, dimana ketika pengguna membuka halaman login dan metode ini yang akan ditampil terlebih dahulu, ubah bagian `default` ke metode yang diinginkan (`member`, `voucher`, `qrCode`) pasti metode tersebut disetel ke `true`. + +Contoh di bawah ini akan mengaktifkan metode login Member dan Voucher, QR Code dimatikan, sementara metode login defaultnya disetel menggunakan voucher. + +```javascript +//... +loginMethod: { + member: true, + voucher: true, + qrCode: false, + default: "voucher", // member, voucher, qrCode +} +//... +``` + +### QR Code Scanner + +Jika Anda mengaktifkan metode login `qrCode` masukkan URL tempat pengguna memindai QR Code voucher, jika tidak abaikan. + +```javascript + // ... + qrCodeScannerURL: "https://example.com/qrcode-scanner", + // .. +``` + +Pengguna tentunya harus bisa mengunjungi URL/domain tersebut meskipun mereka belum login, oleh karena itu silahkan buka Terminal di Winbox kemudian copy paste kode di bawah kemudian enter. + +``` +/ip hotspot walled-garden ip add action=accept comment="Mikmoni" disabled=no dst-host=*.mikmoni.com +``` + +**Apa Artinya Perintah di Atas?** + +Pengguna hotspot termasuk yang tidak login akan bisa mengakses situs yang ditujukan pada nilai `dst-host` dalam hal ini `mikmoni.com` termasuk subdomainnya. + +Selain bisa dimanfaatkan untuk tujuan pemindaian QR Code, fitur ini juga bisa berguna jika Anda ingin menyematkan video/audio berukuran besar untuk menghiasi halaman login, alih-alih menyimpannya di penyimpanan RouterOS yang dapat mempersempit ruang penyimpanan, Anda bisa menyematkan video/audio ke halaman login melalui URL. + +**Aktifkan HTTP PAP** + +Agar metode login melalui QR Code ini dapat berjalan dengan baik kita perlu mengaktifkan HTTP PAP di RouterOS. + +1. Buka **Winbox** +2. **IP** +3. **Hotspot** +4. **Server Profile** +5. _pilih server profile yang digunakan_ +6. **Login** +7. ✔ **HTTP PAP** + +### Mengaktifkan Pemeriksa Expired Voucher + +Secara default beberapa informasi yang ditampilkan pada halaman status dihitung dalam satu sesi, misalnya nilai Uptime, ketika pengguna keluar itu akan dihitung mulai dari awal lagi. + +Oleh karena itu mikmoni menyediakan route/URL khusus untuk memeriksa informasi voucher tanpa batas sesi termasuk masa aktif (expired), URL ini bisa kita dapat melalui halaman dashboard daftar Router. + +URL ini akan mengembalikan data pengguna hotspot dalam format JSON: + +```json +{ + "data": { + ".id": "*5", + "server": "hotspot1", + "name": "OD32N", + "password": "5DQMQ", + "mac-address": "XX:XX:XX:XX:XX:XX", + "profile": "1Hari", + "uptime": "22s", + "bytes-in": "23937", + "bytes-out": "24147", + "packets-in": "186", + "packets-out": "156", + "dynamic": "false", + "disabled": "false", + "comment": "apr/04/2021 20:25:02" + } +} +``` + +Di template ini kita hanya menggunakan masa aktif voucher yang diambil dari nilai comment, namun Anda bisa memanfaatkan data yang dikembalikan untuk menampilkan data yang lain di halaman status jika dibutuhkan. + +Mari kita lihat pada file `config.js`: + +```javascript +//... +expiredChecker: { + active: false, + URL: "https://example.com/v1/expired", + token: "69ee773d4xxxxxxxx:00628cdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", +} +//... +``` + +Jika Anda tidak memerlukan fitur ini, atur nilai `active` menjadi `false` dan abaikan bagian yang lain. Namun jika Anda ingin mengaktifkan fitur ini, atur nilainya menjadi `true`. + +Ubah nilai `URL` dan `token` dengan nilai yang digenerate dari halaman daftar Router. + +Sama seperti QR Code Scanner, Ini juga memerlukan konfigurasi Walled Garden karena nantinya pengguna akan mengirim permintaan ke luar. Jika Anda sebelumnya sudah menjalankan perintah di bawah di Terminal Winbox tidak perlu lagi menjalankan perintah ini, kalau belum silahkan salin dan tempelkan. + +Perintahnya sama seperti QR Code Scanner, biar Anda tidak perlu scroll saya sisipkan lagi: + +``` +/ip hotspot walled-garden ip add action=accept comment="Mikmoni" disabled=no dst-host=*.mikmoni.com +``` + +## Laporkan Masalah + +Jika Anda menemukan masalah silahkan laporkan. diff --git a/monipel/alogin.html b/monipel/alogin.html new file mode 100644 index 0000000..641c7d5 --- /dev/null +++ b/monipel/alogin.html @@ -0,0 +1,35 @@ + + + + + + + + + + + Redirect | Hotspot + + + +
+
+
+

Berhasil Terhubung!

+

Jika belum, klik di sini

+
+
+
+ + + + diff --git a/monipel/config.js b/monipel/config.js new file mode 100644 index 0000000..c8b6dc2 --- /dev/null +++ b/monipel/config.js @@ -0,0 +1,16 @@ +const config = { + loginMethod: { + member: true, + voucher: true, + qrCode: true, + default: "member", // member, voucher, qrCode + }, + qrCodeScannerURL: "https://example.com/qrcode-scanner", + expiredChecker: { + active: false, + URL: "https://example.com/v1/expired", + token: "69ee773d4xxxxxxxx:00628cdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + }, +}; + +export default config; diff --git a/monipel/error.html b/monipel/error.html new file mode 100644 index 0000000..2f57a51 --- /dev/null +++ b/monipel/error.html @@ -0,0 +1,28 @@ + + + + + + + + + + + Error | Hotspot + + +
+
+
+

Error

+

$(error)

+

Kembali ke halaman login

+
+
+
+ + + + diff --git a/monipel/errors-en.txt b/monipel/errors-en.txt new file mode 100644 index 0000000..7ff037f --- /dev/null +++ b/monipel/errors-en.txt @@ -0,0 +1,104 @@ +# This file contains error messages which are shown to user, when http/https +# login is used. +# These messages can be changed to make user interface more friendly, including +# translations to different languages. +# +# Various variables can be used here as well. Most frequently used ones are: +# $(error-orig) - original error message from hotspot +# $(ip) - ip address of a client +# $(username) - username of client trying to log in + +# internal-error +# It should never happen. If it will, error page will be shown +# displaying this error message (error-orig will describe what has happened) + +internal-error = internal error ($(error-orig)) + +# config-error +# Should never happen if hotspot is configured properly. + +config-error = configuration error ($(error-orig)) + +# not-logged-in +# Will happen, if status or logout page is requested by user, +# which actually is not logged in + +not-logged-in = you are not logged in (ip $(ip)) + +# ippool-empty +# IP address for user is to be assigned from ip pool, but there are no more +# addresses in that pool + +ippool-empty = cannot assign ip address - no more free addresses from pool + +# shutting-down +# When shutdown is executed, new clients are not accepted + +shutting-down = hotspot service is shutting down + +# user-session-limit +# If user profile has limit of shared-users, then this error will be shown +# after reaching this limit + +user-session-limit = no more sessions are allowed for user $(username) + +# license-session-limit +# Depending on licence number of active hotspot clients is limited to +# one or another amount. If this limit is reached, following error is displayed. + +license-session-limit = session limit reached ($(error-orig)) + +# wrong-mac-username +# If username looks like MAC address (12:34:56:78:9a:bc), but is not +# a MAC address of this client, login is rejected + +wrong-mac-username = invalid username ($(username)): this MAC address is not yours + +# chap-missing +# If http-chap login method is used, but hotspot program does not receive +# back encrypted password, this error message is shown. +# Possible reasons of failure: +# - JavaScript is not enabled in web browser; +# - login.html page is not valid; +# - challenge value has expired on server (more than 1h of inactivity); +# - http-chap login method is recently removed; +# If JavaScript is enabled and login.html page is valid, +# then retrying to login usually fixes this problem. + +chap-missing = web browser did not send challenge response (try again, enable JavaScript) + +# invalid-username +# Most general case of invalid username or password. If RADIUS server +# has sent an error string with Access-Reject message, then it will +# override this setting. + +invalid-username = invalid username or password + +# invalid-mac +# Local users (on hotspot server) can be bound to some MAC address. If login +# from different MAC is tried, this error message will be shown. + +invalid-mac = user $(username) is not allowed to log in from this MAC address + +# uptime-limit, traffic-limit +# For local hotspot users in case if limits are reached + +uptime-limit = user $(username) has reached uptime limit +traffic-limit = user $(username) has reached traffic limit + +# radius-timeout +# User is authenticated by RADIUS server, but no response is received from it, +# following error will be shown. + +radius-timeout = RADIUS server is not responding + +# auth-in-progress +# Authorization in progress. Client already has issued an authorization request +# which is not yet complete. + +auth-in-progress = already authorizing, retry later + +# radius-reply +# Radius server returned some custom error message + +radius-reply = $(error-orig) \ No newline at end of file diff --git a/monipel/errors.txt b/monipel/errors.txt new file mode 100644 index 0000000..7112b7e --- /dev/null +++ b/monipel/errors.txt @@ -0,0 +1,74 @@ +# internal-error +# Ini tidak akan ditampilkan jika memiliki halaman kesalahan, halaman ini akan ditampilkan. +# menampilkan pesan kesalahan ini (error-orig akan menjelaskan apa yang telah terjadi) +internal-error = Kesalahan internal ($(error-orig)). + +# config-error +# Seharusnya tidak terjadi jika hotspot dikonfigurasi dengan benar. +config-error = Kesalahan konfigurasi ($(error-orig)). + +# not-logged-in +# Akan tampil jika status atau halaman logout diminta oleh pengguna, +# yang sebenarnya belum masuk +not-logged-in = Anda belum masuk (ip $(ip)). + +# ippool-empty +# Alamat IP pengguna akan ditetapkan dari kumpulan ip (IP Pool), tetapi tidak ada lagi alamat di IP Pool. +ippool-empty = Tidak dapat menetapkan alamat IP - Alamat IP dari Pool habis. + +# shutting-down +# Saat shutdown dijalankan, klien baru tidak diterima +shutting-down = Layanan hotspot sedang ditutup. + +# user-session-limit +# Jika profil pengguna memiliki batas pengguna bersama, maka kesalahan ini akan ditampilkan setelah mencapai batas +user-session-limit = Voucher $(username) sedang digunakan . + +# license-session-limit +# Bergantung pada nomor lisensi dari klien hotspot aktif yang dibatasi +# satu atau jumlah lain. Jika batas ini tercapai, kesalahan berikut akan ditampilkan. +license-session-limit = Batas durasi penggunaan voucher habis ($(error-orig)). + +# wrong-mac-username +# Jika nama pengguna terlihat seperti alamat MAC (12:34:56:78:9a:bc), tetapi bukan +# alamat MAC klien ini, login ditolak +wrong-mac-username = Voucher tidak sesuai ($(username)): MAC address ini bukan milik Anda. + +# chap-missing +# Jika metode login http-chap digunakan, tetapi program hotspot tidak menerima kembalian sandi terenkripsi, pesan kesalahan ini ditampilkan. +# Kemungkinan penyebab kegagalan: +# - JavaScript tidak diaktifkan di browser web; +# - halaman login.html tidak valid; +# - nilai tantangan telah kedaluwarsa di server (lebih dari 1 jam tidak aktif); +# - Metode login http-chap baru saja dihapus; +# Jika JavaScript diaktifkan dan halaman login.html valid, coba masuk kembali biasanya memperbaiki masalah ini. +chap-missing = Browser tidak mengirimkan tanggapanan tantangan (Coba lagi, pastikan JavaScript browser aktif). + +# invalid-username +# Kasus paling umum dari nama pengguna atau kata sandi tidak valid. Jika server RADIUS +# telah mengirim string kesalahan dengan pesan Access-Reject, maka itu akan menimpa pengaturan ini. +invalid-username = Voucher tidak sesuai, masukkan kembali dengan benar. + +# invalid-mac +# Pengguna lokal (di server hotspot) dapat terikat ke beberapa alamat MAC. Jika login +# dari MAC yang berbeda, pesan kesalahan ini akan ditampilkan. +invalid-mac = Voucher $(username) tidak diizinkan untuk masuk dari alamat MAC ini. + +# uptime-limit, traffic-limit +# Untuk pengguna hotspot lokal jika batas tercapai +uptime-limit = Voucher $(username) telah mencapai batas waktu. +traffic-limit = Voucher $(username) telah mencapai batas kuota. + +# radius-timeout +# Pengguna diautentikasi oleh server RADIUS, tetapi tidak ada tanggapan yang diterima darinya, +# kesalahan berikut akan ditampilkan. +radius-timeout = RADIUS server tidak merespon. + +# auth-in-progress +# Otorisasi sedang berlangsung. Klien sudah mengeluarkan permintaan otorisasi +# yang belum selesai. +auth-in-progress = Otorisasi sedang berlangung, coba lagi beberapa saat. + +# radius-reply +# Server Radius mengembalikan beberapa pesan kesalahan khusus +radius-reply = Kesalahan dari server Radius $(error-orig). diff --git a/monipel/favicon.ico b/monipel/favicon.ico new file mode 100644 index 0000000..88c3945 Binary files /dev/null and b/monipel/favicon.ico differ diff --git a/monipel/login.html b/monipel/login.html new file mode 100644 index 0000000..e218182 --- /dev/null +++ b/monipel/login.html @@ -0,0 +1,98 @@ + + + + + + + + + + Hotspot | Login + + + $(if chap-id) +
+ + + + +
+ + + + $(endif) + + +
+
+
+

Hello!

+

Login untuk menggunakan hotspot.

+
+ + + +
+ + + +
+
+ + 😀 +
+
+
+
+ + 🔑 +
+ + Show + +
+ + + + $(if error) +

$(error)

+ $(endif) + + +
+ + +
+
+ + + + diff --git a/monipel/loginScript.js b/monipel/loginScript.js new file mode 100644 index 0000000..a13fa5e --- /dev/null +++ b/monipel/loginScript.js @@ -0,0 +1,95 @@ +import config from "./config.js"; + +// set title with DNS +document.title = `Login - ${window.location.hostname}`; + +// focus input username/voucher +document.getElementById("username").focus(); + +// DOM var +const inputUsername = document.getElementById("username"); +const inputPassword = document.getElementById("password"); +const inputGroupUsername = document.getElementById("input-group-username"); +const inputGroupPassword = document.getElementById("input-group-password"); +const loginButton = document.querySelector("button[type=submit]"); +const qrCodeInfo = document.getElementById("qrCodeInfo"); +const qrCodeScannerURL = document.getElementById("qrCodeScannerURL"); + +// set URL QR Code Scanner +qrCodeScannerURL.addEventListener("click", (e) => { + e.preventDefault(); + window.location = `intent:${config.qrCodeScannerURL}#Intent;end`; +}); + +// set default menu active +const defaultActiveMenu = document.getElementById(config.loginMethod.default); +defaultActiveMenu.classList.add("login-method-active"); + +// hide menu with value false +const activeMenu = Object.keys(config.loginMethod).filter( + (key) => config.loginMethod[key] === false +); +activeMenu.forEach((el) => { + document.getElementById(el).style.display = "none"; +}); + +// form formatter +function inputChangeHandler(e) { + inputPassword.value = e.target.value; +} +const formFormatter = (menuName) => { + if (menuName === "member") { + inputGroupPassword.style.display = "block"; + inputGroupUsername.style.display = "block"; + inputUsername.placeholder = "Username"; + loginButton.style.display = "block"; + qrCodeInfo.style.display = "none"; + inputUsername.removeEventListener("input", inputChangeHandler); + } else if (menuName === "voucher") { + inputGroupUsername.style.display = "block"; + inputGroupPassword.style.display = "none"; + inputUsername.placeholder = "Voucher"; + loginButton.style.display = "block"; + qrCodeInfo.style.display = "none"; + inputUsername.addEventListener("input", inputChangeHandler); + } else { + qrCodeInfo.style.display = "block"; + inputGroupUsername.style.display = "none"; + inputGroupPassword.style.display = "none"; + loginButton.style.display = "none"; + inputUsername.removeEventListener("input", inputChangeHandler); + } +}; + +/// init form based config default menu +formFormatter(config.loginMethod.default); + +// Show Hide Input Password +const toggleShowPassword = document.getElementById("showPassword"); +toggleShowPassword.addEventListener("click", () => { + const toggleShowPasswordText = document.getElementById("showPasswordText"); + if (inputPassword.type === "text") { + inputPassword.type = "password"; + toggleShowPasswordText.innerHTML = "Show"; + } else { + inputPassword.type = "text"; + toggleShowPasswordText.innerHTML = "Hide"; + } +}); + +// Menu Login Method +const menus = document.querySelectorAll(".login-method-menu"); +menus.forEach((menu) => { + menu.addEventListener("click", function () { + const currentActive = document.querySelectorAll(".login-method-active"); + currentActive[0].className = currentActive[0].className.replace( + " login-method-active", + "" + ); + this.className += " login-method-active"; + + // change form input based method + const activeMenuId = this.id; + formFormatter(activeMenuId); + }); +}); diff --git a/monipel/logout.html b/monipel/logout.html new file mode 100644 index 0000000..ecd99ec --- /dev/null +++ b/monipel/logout.html @@ -0,0 +1,84 @@ + + + + + + + + + + + Logout | Hotspot + + + +
+
+
+

Anda Baru Saja Keluar!

+

Informasi/status voucher $(username).

+
+ + + + + + + + + + + + + + + $(if session-time-left) + + + + + $(else) + + + + + $(endif) + + $(if remain-bytes-total) + + + + + $(endif) +
IP Address:$(ip)
MAC Address:$(mac)
Bytes U/D:$(bytes-in-nice) / $(bytes-out-nice)
Uptime / Left:$(uptime) / $(session-time-left)
Uptime:$(uptime)
Sisa kuota:$(remain-bytes-total)
+ + +
+ +
+ + +
+
+ + + diff --git a/monipel/md5.js b/monipel/md5.js new file mode 100644 index 0000000..9dbd031 --- /dev/null +++ b/monipel/md5.js @@ -0,0 +1,217 @@ +/* + * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message + * Digest Algorithm, as defined in RFC 1321. + * Version 1.1 Copyright (C) Paul Johnston 1999 - 2002. + * Code also contributed by Greg Holt + * See http://pajhome.org.uk/site/legal.html for details. + */ + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function safe_add(x, y) +{ + var lsw = (x & 0xFFFF) + (y & 0xFFFF) + var msw = (x >> 16) + (y >> 16) + (lsw >> 16) + return (msw << 16) | (lsw & 0xFFFF) +} + +/* + * Bitwise rotate a 32-bit number to the left. + */ +function rol(num, cnt) +{ + return (num << cnt) | (num >>> (32 - cnt)) +} + +/* + * These functions implement the four basic operations the algorithm uses. + */ +function cmn(q, a, b, x, s, t) +{ + return safe_add(rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b) +} +function ff(a, b, c, d, x, s, t) +{ + return cmn((b & c) | ((~b) & d), a, b, x, s, t) +} +function gg(a, b, c, d, x, s, t) +{ + return cmn((b & d) | (c & (~d)), a, b, x, s, t) +} +function hh(a, b, c, d, x, s, t) +{ + return cmn(b ^ c ^ d, a, b, x, s, t) +} +function ii(a, b, c, d, x, s, t) +{ + return cmn(c ^ (b | (~d)), a, b, x, s, t) +} + +/* + * Calculate the MD5 of an array of little-endian words, producing an array + * of little-endian words. + */ +function coreMD5(x) +{ + var a = 1732584193 + var b = -271733879 + var c = -1732584194 + var d = 271733878 + + for(i = 0; i < x.length; i += 16) + { + var olda = a + var oldb = b + var oldc = c + var oldd = d + + a = ff(a, b, c, d, x[i+ 0], 7 , -680876936) + d = ff(d, a, b, c, x[i+ 1], 12, -389564586) + c = ff(c, d, a, b, x[i+ 2], 17, 606105819) + b = ff(b, c, d, a, x[i+ 3], 22, -1044525330) + a = ff(a, b, c, d, x[i+ 4], 7 , -176418897) + d = ff(d, a, b, c, x[i+ 5], 12, 1200080426) + c = ff(c, d, a, b, x[i+ 6], 17, -1473231341) + b = ff(b, c, d, a, x[i+ 7], 22, -45705983) + a = ff(a, b, c, d, x[i+ 8], 7 , 1770035416) + d = ff(d, a, b, c, x[i+ 9], 12, -1958414417) + c = ff(c, d, a, b, x[i+10], 17, -42063) + b = ff(b, c, d, a, x[i+11], 22, -1990404162) + a = ff(a, b, c, d, x[i+12], 7 , 1804603682) + d = ff(d, a, b, c, x[i+13], 12, -40341101) + c = ff(c, d, a, b, x[i+14], 17, -1502002290) + b = ff(b, c, d, a, x[i+15], 22, 1236535329) + + a = gg(a, b, c, d, x[i+ 1], 5 , -165796510) + d = gg(d, a, b, c, x[i+ 6], 9 , -1069501632) + c = gg(c, d, a, b, x[i+11], 14, 643717713) + b = gg(b, c, d, a, x[i+ 0], 20, -373897302) + a = gg(a, b, c, d, x[i+ 5], 5 , -701558691) + d = gg(d, a, b, c, x[i+10], 9 , 38016083) + c = gg(c, d, a, b, x[i+15], 14, -660478335) + b = gg(b, c, d, a, x[i+ 4], 20, -405537848) + a = gg(a, b, c, d, x[i+ 9], 5 , 568446438) + d = gg(d, a, b, c, x[i+14], 9 , -1019803690) + c = gg(c, d, a, b, x[i+ 3], 14, -187363961) + b = gg(b, c, d, a, x[i+ 8], 20, 1163531501) + a = gg(a, b, c, d, x[i+13], 5 , -1444681467) + d = gg(d, a, b, c, x[i+ 2], 9 , -51403784) + c = gg(c, d, a, b, x[i+ 7], 14, 1735328473) + b = gg(b, c, d, a, x[i+12], 20, -1926607734) + + a = hh(a, b, c, d, x[i+ 5], 4 , -378558) + d = hh(d, a, b, c, x[i+ 8], 11, -2022574463) + c = hh(c, d, a, b, x[i+11], 16, 1839030562) + b = hh(b, c, d, a, x[i+14], 23, -35309556) + a = hh(a, b, c, d, x[i+ 1], 4 , -1530992060) + d = hh(d, a, b, c, x[i+ 4], 11, 1272893353) + c = hh(c, d, a, b, x[i+ 7], 16, -155497632) + b = hh(b, c, d, a, x[i+10], 23, -1094730640) + a = hh(a, b, c, d, x[i+13], 4 , 681279174) + d = hh(d, a, b, c, x[i+ 0], 11, -358537222) + c = hh(c, d, a, b, x[i+ 3], 16, -722521979) + b = hh(b, c, d, a, x[i+ 6], 23, 76029189) + a = hh(a, b, c, d, x[i+ 9], 4 , -640364487) + d = hh(d, a, b, c, x[i+12], 11, -421815835) + c = hh(c, d, a, b, x[i+15], 16, 530742520) + b = hh(b, c, d, a, x[i+ 2], 23, -995338651) + + a = ii(a, b, c, d, x[i+ 0], 6 , -198630844) + d = ii(d, a, b, c, x[i+ 7], 10, 1126891415) + c = ii(c, d, a, b, x[i+14], 15, -1416354905) + b = ii(b, c, d, a, x[i+ 5], 21, -57434055) + a = ii(a, b, c, d, x[i+12], 6 , 1700485571) + d = ii(d, a, b, c, x[i+ 3], 10, -1894986606) + c = ii(c, d, a, b, x[i+10], 15, -1051523) + b = ii(b, c, d, a, x[i+ 1], 21, -2054922799) + a = ii(a, b, c, d, x[i+ 8], 6 , 1873313359) + d = ii(d, a, b, c, x[i+15], 10, -30611744) + c = ii(c, d, a, b, x[i+ 6], 15, -1560198380) + b = ii(b, c, d, a, x[i+13], 21, 1309151649) + a = ii(a, b, c, d, x[i+ 4], 6 , -145523070) + d = ii(d, a, b, c, x[i+11], 10, -1120210379) + c = ii(c, d, a, b, x[i+ 2], 15, 718787259) + b = ii(b, c, d, a, x[i+ 9], 21, -343485551) + + a = safe_add(a, olda) + b = safe_add(b, oldb) + c = safe_add(c, oldc) + d = safe_add(d, oldd) + } + return [a, b, c, d] +} + +/* + * Convert an array of little-endian words to a hex string. + */ +function binl2hex(binarray) +{ + var hex_tab = "0123456789abcdef" + var str = "" + for(var i = 0; i < binarray.length * 4; i++) + { + str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8)) & 0xF) + } + return str +} + +/* + * Convert an array of little-endian words to a base64 encoded string. + */ +function binl2b64(binarray) +{ + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + var str = "" + for(var i = 0; i < binarray.length * 32; i += 6) + { + str += tab.charAt(((binarray[i>>5] << (i%32)) & 0x3F) | + ((binarray[i>>5+1] >> (32-i%32)) & 0x3F)) + } + return str +} + +/* + * Convert an 8-bit character string to a sequence of 16-word blocks, stored + * as an array, and append appropriate padding for MD4/5 calculation. + * If any of the characters are >255, the high byte is silently ignored. + */ +function str2binl(str) +{ + var nblk = ((str.length + 8) >> 6) + 1 // number of 16-word blocks + var blks = new Array(nblk * 16) + for(var i = 0; i < nblk * 16; i++) blks[i] = 0 + for(var i = 0; i < str.length; i++) + blks[i>>2] |= (str.charCodeAt(i) & 0xFF) << ((i%4) * 8) + blks[i>>2] |= 0x80 << ((i%4) * 8) + blks[nblk*16-2] = str.length * 8 + return blks +} + +/* + * Convert a wide-character string to a sequence of 16-word blocks, stored as + * an array, and append appropriate padding for MD4/5 calculation. + */ +function strw2binl(str) +{ + var nblk = ((str.length + 4) >> 5) + 1 // number of 16-word blocks + var blks = new Array(nblk * 16) + for(var i = 0; i < nblk * 16; i++) blks[i] = 0 + for(var i = 0; i < str.length; i++) + blks[i>>1] |= str.charCodeAt(i) << ((i%2) * 16) + blks[i>>1] |= 0x80 << ((i%2) * 16) + blks[nblk*16-2] = str.length * 16 + return blks +} + +/* + * External interface + */ +function hexMD5 (str) { return binl2hex(coreMD5( str2binl(str))) } +function hexMD5w(str) { return binl2hex(coreMD5(strw2binl(str))) } +function b64MD5 (str) { return binl2b64(coreMD5( str2binl(str))) } +function b64MD5w(str) { return binl2b64(coreMD5(strw2binl(str))) } +/* Backward compatibility */ +function calcMD5(str) { return binl2hex(coreMD5( str2binl(str))) } diff --git a/monipel/radvert.html b/monipel/radvert.html new file mode 100644 index 0000000..25a9e80 --- /dev/null +++ b/monipel/radvert.html @@ -0,0 +1,81 @@ + + + + + + + + + + Ads | Hotspot + + + + + + + + +
+ Advertisement. +

+ If nothing happens, open + advertisement + manually. +
+ + diff --git a/monipel/redirect.html b/monipel/redirect.html new file mode 100644 index 0000000..7834082 --- /dev/null +++ b/monipel/redirect.html @@ -0,0 +1,12 @@ + +$(if http-status == 302)Hotspot redirect$(endif) +$(if http-header == "Location")$(link-redirect)$(endif) + + + ... + + + + + + diff --git a/monipel/rlogin.html b/monipel/rlogin.html new file mode 100644 index 0000000..9b1033c --- /dev/null +++ b/monipel/rlogin.html @@ -0,0 +1,12 @@ + +$(if http-status == 302)Hotspot login required$(endif) +$(if http-header == "Location")$(link-redirect)$(endif) + + + ... + + + + + + diff --git a/monipel/status.html b/monipel/status.html new file mode 100644 index 0000000..d53ca45 --- /dev/null +++ b/monipel/status.html @@ -0,0 +1,135 @@ + + + + + + + + + + + Status | Hotspot + $(if refresh-timeout) + + $(endif) + + + + +
+
+
+

Status

+ +

Informasi/status voucher $(if login-by == 'trial')Trial$(elif login-by != 'mac')$(username)$(endif).

+
+ + + + + + + + + + + $(if session-time-left) + + + + + $(else) + + + + + $(endif) + + $(if blocked == 'yes') + + + + + $(elif refresh-timeout) + + + + + $(endif) + + + + + +
IP Address:$(ip)
Bytes U/D:$(bytes-in-nice) / $(bytes-out-nice)
Uptime / Left:$(uptime) / $(session-time-left)
Uptime:$(uptime)
Status: advertisement required
Refresh:$(refresh-timeout)
Expired:
+ + $(if login-by-mac != 'yes') +
+ +
+ $(endif) + + +
+
+ + + + diff --git a/monipel/style.css b/monipel/style.css new file mode 100644 index 0000000..6a149eb --- /dev/null +++ b/monipel/style.css @@ -0,0 +1,248 @@ +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, + Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-size: 15px; + background: #f1f1f1; + margin-top: 0; + margin-bottom: 0; +} +/* body, */ +html, +h1, +p { + margin: 0; + padding: 0; +} +*, +:after, +:before { + box-sizing: border-box; + outline: 0; + appearance: none; +} +a { + color: #06f; + text-decoration: none; + font-weight: bold; +} +a:hover { + color: #005ce6; +} +.container { + display: grid; + place-items: center; + height: 100vh; +} +form { + display: grid; + padding: 5px 24px 24px; +} +.wrapper { + transition: transform 0.3s cubic-bezier(0.6, 0.4, 0, 1), + opacity 0.3s cubic-bezier(0.6, 0.4, 0, 1); + will-change: transform; + border-radius: 14px; + background: #fff; + width: 100%; +} +.title-wrapper { + text-align: center; + padding: 24px; +} +.title-wrapper > h1 { + color: #000; + line-height: 1.1em; + letter-spacing: -0.035em; + margin-bottom: 10px; +} +.title-wrapper > p { + color: #7d7c83; + line-height: 1.4em; + font-size: 16px; +} +.input-group { + margin-bottom: 20px; + position: relative; +} +.input-icon { + position: relative; +} +.input { + border-radius: 10px; + height: 46px; + font-size: 15px; + transition: box-shadow 0.15s ease; + box-shadow: inset #d8d8da 0 0 0 1px, inset #fff 0 0 0 100px !important; + border: none; + width: 100%; + outline: 0; + padding: 0 14px; + padding-left: 52px; +} +.btn { + cursor: pointer; + width: 100%; + font-weight: 600; + text-align: center; + white-space: nowrap; + border: none; + text-decoration: none; + transition: all 0.15s ease; +} +.btn-primary { + background: #06f; + color: #fff; + border-radius: 10px; + font-size: 15px; + padding: 0 21px; + height: 46px; + line-height: 45px; +} +.btn-primary:not(:disabled):not(.is-disabled):hover { + background: #005ce6; +} +.btn-show-hide { + position: absolute; + top: 13%; + width: auto; + right: 6px; + border-radius: 8px; + font-size: 14px; + padding: 0 12px; + height: 34px; + line-height: 34px; + color: #000; + box-shadow: inset rgb(0 0 0 / 15%) 0 0 0 1px; +} +.input:hover:not(:disabled) { + box-shadow: inset #b1b0b5 0 0 0 1px, inset #fff 0 0 0 100px !important; +} +.input:focus:not(:disabled) { + box-shadow: inset #06f 0 0 0 1px, inset #fff 0 0 0 100px !important; +} +.btn-show-hide:not(:disabled):not(.is-disabled):hover { + box-shadow: inset rgb(0 0 0 / 30%) 0 0 0 1px; +} +.icon { + left: 15px; + font-size: 18px; + position: absolute; + top: 50%; + transition: all 0.15s ease; + transform: translate3d(0, -50%, 0); + font-style: normal; +} +.form-footer { + flex-basis: 100%; + flex-grow: 1; + box-shadow: rgb(0 0 0 / 11%) 0 -1px; + text-align: center; + line-height: 1.7em; + padding: 24px; +} +.copy-right { + color: #7d7c83; + font-size: 13px; +} +.copy-right a { + font-weight: normal; +} + +.login-method { + display: flex; + justify-content: center; +} +.login-method ul { + list-style: none; + padding: 0; + margin-top: 0; + border-bottom: 1px solid #d8d8da; +} +.login-method li { + display: inline-block; + padding: 10px; + font-weight: bold; + cursor: pointer; +} + +.login-method li:hover { + color: #0066ff; +} + +.login-method-active { + border-bottom: 2px solid #0066ff; + color: #0066ff; +} +.error { + padding: 0 10px 20px; + color: #ed1245; +} + +table { + border-collapse: collapse; + margin: 0 auto 20px; + text-align: left; + min-width: 300px; +} +td, +th { + border: 1px solid #e3e3e3; + padding: 10px; +} + +@media (min-width: 576px) { + .wrapper { + width: 468px; + } +} + +/* spinner */ +.spinner { + margin: 0; + text-align: left; +} + +.spinner > div { + width: 10px; + height: 10px; + background-color: #9e9e9e; + + border-radius: 100%; + display: inline-block; + -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both; + animation: sk-bouncedelay 1.4s infinite ease-in-out both; +} + +.spinner .bounce1 { + -webkit-animation-delay: -0.32s; + animation-delay: -0.32s; +} + +.spinner .bounce2 { + -webkit-animation-delay: -0.16s; + animation-delay: -0.16s; +} + +@-webkit-keyframes sk-bouncedelay { + 0%, + 80%, + 100% { + -webkit-transform: scale(0); + } + 40% { + -webkit-transform: scale(1); + } +} + +@keyframes sk-bouncedelay { + 0%, + 80%, + 100% { + -webkit-transform: scale(0); + transform: scale(0); + } + 40% { + -webkit-transform: scale(1); + transform: scale(1); + } +} diff --git a/monipel/success.html b/monipel/success.html new file mode 100644 index 0000000..c63e8e4 --- /dev/null +++ b/monipel/success.html @@ -0,0 +1,46 @@ + + + + + + + + + + Success | Hotspot + $(if refresh-timeout) + + $(endif) + + +
+
+
+ $(if login-by == 'trial') +

Welcome Trial User!

+ $(elif login-by != 'mac') +

Welcome $(username)!

+ $(endif) +

+ Anda berhasil terhubung, kunjungi + halaman status +

+ . +
+ + +
+
+ + + diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..9ea30af --- /dev/null +++ b/readme.md @@ -0,0 +1,7 @@ +# Mikmoni Hotspot Template + +Template login hotspot RouterOS ini dirancang untuk pelanggan mikmoni, meskipun demikian pengguna umum juga bisa menggunakan template ini, mungkin perlu beberapa penyesuaian. + +> Panduan konfigurasi disertakan dalam file readme masing-masing template. + +## Monipel