diff --git a/system/controllers/accounts.php b/system/controllers/accounts.php
new file mode 100644
index 0000000..ca46741
--- /dev/null
+++ b/system/controllers/accounts.php
@@ -0,0 +1,254 @@
+assign('_title', Lang::T('My Account'));
+$ui->assign('_system_menu', 'accounts');
+
+$action = $routes['1'];
+$user = User::_info();
+$ui->assign('_user', $user);
+
+switch ($action) {
+
+ case 'change-password':
+ run_hook('customer_view_change_password'); #HOOK
+ $ui->display('user-change-password.tpl');
+ break;
+
+ case 'change-password-post':
+ $password = _post('password');
+ run_hook('customer_change_password'); #HOOK
+ if ($password != '') {
+ $d = ORM::for_table('tbl_customers')->where('username', $user['username'])->find_one();
+ if ($d) {
+ $d_pass = $d['password'];
+ $npass = _post('npass');
+ $cnpass = _post('cnpass');
+
+ if (Password::_uverify($password, $d_pass) == true) {
+ if (!Validator::Length($npass, 15, 2)) {
+ r2(U . 'accounts/change-password', 'e', 'New Password must be 3 to 14 character');
+ }
+ if ($npass != $cnpass) {
+ r2(U . 'accounts/change-password', 'e', 'Both Password should be same');
+ }
+
+ $c = ORM::for_table('tbl_user_recharges')->where('username', $user['username'])->find_one();
+ if ($c) {
+ $p = ORM::for_table('tbl_plans')->where('id', $c['plan_id'])->find_one();
+ if ($p['is_radius']) {
+ if ($c['type'] == 'Hotspot' || ($c['type'] == 'PPPOE' && empty($d['pppoe_password']))) {
+ Radius::customerUpsert($d, $p);
+ }
+ } else {
+ $mikrotik = Mikrotik::info($c['routers']);
+ $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
+ if ($c['type'] == 'Hotspot') {
+ Mikrotik::setHotspotUser($client, $c['username'], $npass);
+ Mikrotik::removeHotspotActiveUser($client, $user['username']);
+ } else if (empty($d['pppoe_password'])) {
+ // only change when pppoe_password empty
+ Mikrotik::setPpoeUser($client, $c['username'], $npass);
+ Mikrotik::removePpoeActive($client, $user['username']);
+ }
+ }
+ }
+ $d->password = $npass;
+ $d->save();
+
+ _msglog('s', Lang::T('Password changed successfully, Please login again'));
+ _log('[' . $user['username'] . ']: Password changed successfully', 'User', $user['id']);
+
+ r2(U . 'login');
+ } else {
+ r2(U . 'accounts/change-password', 'e', Lang::T('Incorrect Current Password'));
+ }
+ } else {
+ r2(U . 'accounts/change-password', 'e', Lang::T('Incorrect Current Password'));
+ }
+ } else {
+ r2(U . 'accounts/change-password', 'e', Lang::T('Incorrect Current Password'));
+ }
+ break;
+
+ case 'profile':
+ $d = ORM::for_table('tbl_customers')->find_one($user['id']);
+ if ($d) {
+ run_hook('customer_view_edit_profile'); #HOOK
+ $ui->assign('d', $d);
+ $ui->display('user-profile.tpl');
+ } else {
+ r2(U . 'home', 'e', Lang::T('Account Not Found'));
+ }
+ break;
+
+ case 'edit-profile-post':
+ $fullname = _post('fullname');
+ $address = _post('address');
+ $email = _post('email');
+ $phonenumber = _post('phonenumber');
+ run_hook('customer_edit_profile'); #HOOK
+ $msg = '';
+ if (Validator::Length($fullname, 31, 2) == false) {
+ $msg .= 'Full Name should be between 3 to 30 characters' . '
';
+ }
+ if (Validator::UnsignedNumber($phonenumber) == false) {
+ $msg .= 'Phone Number must be a number' . '
';
+ }
+
+ $d = ORM::for_table('tbl_customers')->find_one($user['id']);
+ if ($d) {
+ } else {
+ $msg .= Lang::T('Data Not Found') . '
';
+ }
+
+ if ($msg == '') {
+ $d->fullname = $fullname;
+ $d->address = $address;
+ $d->email = $email;
+ $d->phonenumber = $phonenumber;
+ $d->save();
+
+ _log('[' . $user['username'] . ']: ' . Lang::T('User Updated Successfully'), 'User', $user['id']);
+ r2(U . 'accounts/profile', 's', Lang::T('User Updated Successfully'));
+ } else {
+ r2(U . 'accounts/profile', 'e', $msg);
+ }
+ break;
+
+
+ case 'phone-update':
+
+ $d = ORM::for_table('tbl_customers')->find_one($user['id']);
+ if ($d) {
+ //run_hook('customer_view_edit_profile'); #HOOK
+ $ui->assign('d', $d);
+ $ui->display('user-phone-update.tpl');
+ } else {
+ r2(U . 'home', 'e', Lang::T('Account Not Found'));
+ }
+ break;
+
+ case 'phone-update-otp':
+ $phone = _post('phone');
+ $username = $user['username'];
+ $otpPath = $CACHE_PATH . '/sms/';
+
+ // Validate the phone number format
+ if (!preg_match('/^[0-9]{10,}$/', $phone)) {
+ r2(U . 'accounts/phone-update', 'e', Lang::T('Invalid phone number format'));
+ }
+
+ if (empty($config['sms_url'])) {
+ r2(U . 'accounts/phone-update', 'e', Lang::T('SMS server not Available, Please try again later'));
+ }
+
+ if (!empty($config['sms_url'])) {
+ if (!empty($phone)) {
+ $d = ORM::for_table('tbl_customers')->where('username', $username)->where('phonenumber', $phone)->find_one();
+ if ($d) {
+ r2(U . 'accounts/phone-update', 'e', Lang::T('You cannot use your current phone number'));
+ }
+ if (!file_exists($otpPath)) {
+ mkdir($otpPath);
+ touch($otpPath . 'index.html');
+ }
+ $otpFile = $otpPath . sha1($username . $db_password) . ".txt";
+ $phoneFile = $otpPath . sha1($username . $db_password) . "_phone.txt";
+
+ // expired 10 minutes
+ if (file_exists($otpFile) && time() - filemtime($otpFile) < 1200) {
+ r2(U . 'accounts/phone-update', 'e', Lang::T('Please wait ' . (1200 - (time() - filemtime($otpFile))) . ' seconds before sending another SMS'));
+ } else {
+ $otp = rand(100000, 999999);
+ file_put_contents($otpFile, $otp);
+ file_put_contents($phoneFile, $phone);
+ // send send OTP to user
+ if ($config['phone_otp_type'] === 'sms') {
+ Message::sendSMS($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
+ } elseif ($config['phone_otp_type'] === 'whatsapp') {
+ Message::sendWhatsapp($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
+ } elseif ($config['phone_otp_type'] === 'both') {
+ Message::sendSMS($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
+ Message::sendWhatsapp($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
+ }
+ //redirect after sending OTP
+ r2(U . 'accounts/phone-update', 'e', Lang::T('Verification code has been sent to your phone'));
+ }
+ }
+ }
+
+ break;
+
+ case 'phone-update-post':
+ $phone = _post('phone');
+ $otp_code = _post('otp');
+ $username = $user['username'];
+ $otpPath = $CACHE_PATH . '/sms/';
+
+ // Validate the phone number format
+ if (!preg_match('/^[0-9]{10,}$/', $phone)) {
+ r2(U . 'accounts/phone-update', 'e', Lang::T('Invalid phone number format'));
+ exit();
+ }
+
+ if (!empty($config['sms_url'])) {
+ $otpFile = $otpPath . sha1($username . $db_password) . ".txt";
+ $phoneFile = $otpPath . sha1($username . $db_password) . "_phone.txt";
+
+ // Check if OTP file exists
+ if (!file_exists($otpFile)) {
+ r2(U . 'accounts/phone-update', 'e', Lang::T('Please request OTP first'));
+ exit();
+ }
+
+ // expired 10 minutes
+ if (time() - filemtime($otpFile) > 1200) {
+ unlink($otpFile);
+ unlink($phoneFile);
+ r2(U . 'accounts/phone-update', 'e', Lang::T('Verification code expired'));
+ exit();
+ } else {
+ $code = file_get_contents($otpFile);
+
+ // Check if OTP code matches
+ if ($code != $otp_code) {
+ r2(U . 'accounts/phone-update', 'e', Lang::T('Wrong Verification code'));
+ exit();
+ }
+
+ // Check if the phone number matches the one that requested the OTP
+ $savedPhone = file_get_contents($phoneFile);
+ if ($savedPhone !== $phone) {
+ r2(U . 'accounts/phone-update', 'e', Lang::T('The phone number does not match the one that requested the OTP'));
+ exit();
+ }
+
+ // OTP verification successful, delete OTP and phone number files
+ unlink($otpFile);
+ unlink($phoneFile);
+ }
+ } else {
+ r2(U . 'accounts/phone-update', 'e', Lang::T('SMS server not available'));
+ exit();
+ }
+
+ // Update the phone number in the database
+ $d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
+ if ($d) {
+ $d->phonenumber = Lang::phoneFormat($phone);
+ $d->save();
+ }
+
+ r2(U . 'accounts/profile', 's', Lang::T('Phone number updated successfully'));
+ break;
+
+ default:
+ $ui->display('a404.tpl');
+}
diff --git a/system/controllers/admin.php b/system/controllers/admin.php
new file mode 100644
index 0000000..e8064bd
--- /dev/null
+++ b/system/controllers/admin.php
@@ -0,0 +1,57 @@
+where('username', $username)->find_one();
+ if ($d) {
+ $d_pass = $d['password'];
+ if (Password::_verify($password, $d_pass) == true) {
+ $_SESSION['aid'] = $d['id'];
+ $token = Admin::setCookie($d['id']);
+ $d->last_login = date('Y-m-d H:i:s');
+ $d->save();
+ _log($username . ' ' . Lang::T('Login Successful'), $d['user_type'], $d['id']);
+ if ($isApi) {
+ if ($token) {
+ showResult(true, Lang::T('Login Successful'), ['token' => "a.".$token]);
+ } else {
+ showResult(false, Lang::T('Invalid Username or Password'));
+ }
+ }
+ _alert(Lang::T('Login Successful'),'success', "dashboard");
+ } else {
+ _log($username . ' ' . Lang::T('Failed Login'), $d['user_type']);
+ _alert(Lang::T('Invalid Username or Password').".",'danger', "admin");
+ }
+ } else {
+ _alert(Lang::T('Invalid Username or Password')."..",'danger', "admin");
+ }
+ } else {
+ _alert(Lang::T('Invalid Username or Password')."...",'danger', "admin");
+ }
+
+ break;
+ default:
+ run_hook('view_login'); #HOOK
+ $ui->display('admin-login.tpl');
+ break;
+}
diff --git a/system/controllers/autoload.php b/system/controllers/autoload.php
new file mode 100644
index 0000000..e24af34
--- /dev/null
+++ b/system/controllers/autoload.php
@@ -0,0 +1,89 @@
+assign('_title', Lang::T('Network'));
+$ui->assign('_system_menu', 'network');
+
+$action = $routes['1'];
+$ui->assign('_admin', $admin);
+
+switch ($action) {
+ case 'pool':
+ $routers = _get('routers');
+ if(empty($routers)){
+ $d = ORM::for_table('tbl_pool')->find_many();
+ }else{
+ $d = ORM::for_table('tbl_pool')->where('routers', $routers)->find_many();
+ }
+ $ui->assign('routers', $routers);
+ $ui->assign('d', $d);
+ $ui->display('autoload-pool.tpl');
+ break;
+
+ case 'server':
+ $d = ORM::for_table('tbl_routers')->where('enabled', '1')->find_many();
+ $ui->assign('d', $d);
+
+ $ui->display('autoload-server.tpl');
+ break;
+
+ case 'plan':
+ $server = _post('server');
+ $jenis = _post('jenis');
+ if(in_array($admin['user_type'], array('SuperAdmin', 'Admin'))){
+ if($server=='radius'){
+ $d = ORM::for_table('tbl_plans')->where('is_radius', 1)->where('type', $jenis)->find_many();
+ }else{
+ $d = ORM::for_table('tbl_plans')->where('routers', $server)->where('type', $jenis)->find_many();
+ }
+ }else{
+ if($server=='radius'){
+ $d = ORM::for_table('tbl_plans')->where('is_radius', 1)->where('type', $jenis)->where('enabled', '1')->find_many();
+ }else{
+ $d = ORM::for_table('tbl_plans')->where('routers', $server)->where('type', $jenis)->where('enabled', '1')->find_many();
+ }
+ }
+ $ui->assign('d', $d);
+
+ $ui->display('autoload.tpl');
+ break;
+ case 'customer_is_active':
+ $d = ORM::for_table('tbl_user_recharges')->where('customer_id', $routes['2'])->findOne();
+ if ($d) {
+ if ($d['status'] == 'on') {
+ die(''.$d['namebp'].'');
+ } else {
+ die(''.$d['namebp'].'');
+ }
+ } else {
+ die('•');
+ }
+ break;
+ case 'customer_select2':
+
+ $s = addslashes(_get('s'));
+ if (empty($s)) {
+ $c = ORM::for_table('tbl_customers')->limit(30)->find_many();
+ } else {
+ $c = ORM::for_table('tbl_customers')->where_raw("(`username` LIKE '%$s%' OR `fullname` LIKE '%$s%' OR `phonenumber` LIKE '%$s%' OR `email` LIKE '%$s%')")->limit(30)->find_many();
+ }
+ header('Content-Type: application/json');
+ foreach ($c as $cust) {
+ $json[] = [
+ 'id' => $cust['id'],
+ 'text' => $cust['username'] . ' - ' . $cust['fullname'] . ' - ' . $cust['email']
+ ];
+ }
+ echo json_encode(['results' => $json]);
+ die();
+ default:
+ $ui->display('a404.tpl');
+}
diff --git a/system/controllers/autoload_user.php b/system/controllers/autoload_user.php
new file mode 100644
index 0000000..8e50773
--- /dev/null
+++ b/system/controllers/autoload_user.php
@@ -0,0 +1,37 @@
+where('id', $routes['2'])->where('username', $user['username'])->findOne();
+ if ($bill['type'] == 'Hotspot' && $bill['status'] == 'on') {
+ $m = Mikrotik::info($bill['routers']);
+ $client = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
+ if (Mikrotik::isUserLogin($client, $user['username'])) {
+ die(''.Lang::T('You are Online, Logout?').'');
+ } else {
+ if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'])) {
+ die(''.Lang::T('Not Online, Login now?').'');
+ }else{
+ die(Lang::T('Your account not connected to internet'));
+ }
+ }
+ } else {
+ die('--');
+ }
+ break;
+ default:
+ $ui->display('404.tpl');
+}
diff --git a/system/controllers/bandwidth.php b/system/controllers/bandwidth.php
new file mode 100644
index 0000000..2d49061
--- /dev/null
+++ b/system/controllers/bandwidth.php
@@ -0,0 +1,190 @@
+assign('_title', Lang::T('Bandwidth Plans'));
+$ui->assign('_system_menu', 'services');
+
+$action = $routes['1'];
+$ui->assign('_admin', $admin);
+
+if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
+ r2(U . "dashboard", 'e', Lang::T('You do not have permission to access this page'));
+}
+
+switch ($action) {
+ case 'list':
+ $ui->assign('xfooter', '');
+ run_hook('view_list_bandwidth'); #HOOK
+ $name = _post('name');
+ if ($name != '') {
+ $query = ORM::for_table('tbl_bandwidth')->where_like('name_bw', '%' . $name . '%')->order_by_desc('id');
+ $d = Paginator::findMany($query, ['name' => $name]);
+ } else {
+ $query = ORM::for_table('tbl_bandwidth')->order_by_desc('id');
+ $d = Paginator::findMany($query);
+ }
+
+ $ui->assign('d', $d);
+ $ui->display('bandwidth.tpl');
+ break;
+
+ case 'add':
+ if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
+ _alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
+ }
+ run_hook('view_add_bandwidth'); #HOOK
+ $ui->display('bandwidth-add.tpl');
+ break;
+
+ case 'edit':
+ if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
+ _alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
+ }
+ $id = $routes['2'];
+ run_hook('view_edit_bandwith'); #HOOK
+ $d = ORM::for_table('tbl_bandwidth')->find_one($id);
+ if ($d) {
+ $ui->assign('burst', explode(" ", $d['burst']));
+ $ui->assign('d', $d);
+ $ui->display('bandwidth-edit.tpl');
+ } else {
+ r2(U . 'bandwidth/list', 'e', Lang::T('Account Not Found'));
+ }
+ break;
+
+ case 'delete':
+ if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
+ _alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
+ }
+ $id = $routes['2'];
+ run_hook('delete_bandwidth'); #HOOK
+ $d = ORM::for_table('tbl_bandwidth')->find_one($id);
+ if ($d) {
+ $d->delete();
+ r2(U . 'bandwidth/list', 's', Lang::T('Data Deleted Successfully'));
+ }
+ break;
+
+ case 'add-post':
+ if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
+ _alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
+ }
+ $name = _post('name');
+ $rate_down = _post('rate_down');
+ $rate_down_unit = _post('rate_down_unit');
+ $rate_up = _post('rate_up');
+ $rate_up_unit = _post('rate_up_unit');
+ run_hook('add_bandwidth'); #HOOK
+ $isBurst = true;
+ $burst = "";
+ if (isset($_POST['burst'])) {
+ foreach ($_POST['burst'] as $b) {
+ if (empty($b)) {
+ $isBurst = false;
+ }
+ }
+ if ($isBurst) {
+ $burst = implode(' ', $_POST['burst']);
+ };
+ }
+ $msg = '';
+ if (Validator::Length($name, 16, 4) == false) {
+ $msg .= 'Name should be between 5 to 15 characters' . '
';
+ }
+
+ if ($rate_down_unit == 'Kbps') {
+ $unit_rate_down = $rate_down * 1024;
+ } else {
+ $unit_rate_down = $rate_down * 1048576;
+ }
+ if ($rate_up_unit == 'Kbps') {
+ $unit_rate_up = $min_up * 1024;
+ } else {
+ $unit_rate_up = $min_up * 1048576;
+ }
+
+ $d = ORM::for_table('tbl_bandwidth')->where('name_bw', $name)->find_one();
+ if ($d) {
+ $msg .= Lang::T('Name Bandwidth Already Exist') . '
';
+ }
+
+ if ($msg == '') {
+ $d = ORM::for_table('tbl_bandwidth')->create();
+ $d->name_bw = $name;
+ $d->rate_down = $rate_down;
+ $d->rate_down_unit = $rate_down_unit;
+ $d->rate_up = $rate_up;
+ $d->rate_up_unit = $rate_up_unit;
+ $d->burst = $burst;
+ $d->save();
+
+ r2(U . 'bandwidth/list', 's', Lang::T('Data Created Successfully'));
+ } else {
+ r2(U . 'bandwidth/add', 'e', $msg);
+ }
+ break;
+
+ case 'edit-post':
+ if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
+ _alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
+ }
+ $name = _post('name');
+ $rate_down = _post('rate_down');
+ $rate_down_unit = _post('rate_down_unit');
+ $rate_up = _post('rate_up');
+ $rate_up_unit = _post('rate_up_unit');
+ run_hook('edit_bandwidth'); #HOOK
+ $isBurst = true;
+ $burst = "";
+ if (isset($_POST['burst'])) {
+ foreach ($_POST['burst'] as $b) {
+ if (empty($b)) {
+ $isBurst = false;
+ }
+ }
+ if ($isBurst) {
+ $burst = implode(' ', $_POST['burst']);
+ };
+ }
+ $msg = '';
+ if (Validator::Length($name, 16, 4) == false) {
+ $msg .= 'Name should be between 5 to 15 characters' . '
';
+ }
+
+ $id = _post('id');
+ $d = ORM::for_table('tbl_bandwidth')->find_one($id);
+ if ($d) {
+ } else {
+ $msg .= Lang::T('Data Not Found') . '
';
+ }
+
+ if ($d['name_bw'] != $name) {
+ $c = ORM::for_table('tbl_bandwidth')->where('name_bw', $name)->find_one();
+ if ($c) {
+ $msg .= Lang::T('Name Bandwidth Already Exist') . '
';
+ }
+ }
+
+ if ($msg == '') {
+ $d->name_bw = $name;
+ $d->rate_down = $rate_down;
+ $d->rate_down_unit = $rate_down_unit;
+ $d->rate_up = $rate_up;
+ $d->rate_up_unit = $rate_up_unit;
+ $d->burst = $burst;
+ $d->save();
+
+ r2(U . 'bandwidth/list', 's', Lang::T('Data Updated Successfully'));
+ } else {
+ r2(U . 'bandwidth/edit/' . $id, 'e', $msg);
+ }
+ break;
+
+ default:
+ $ui->display('a404.tpl');
+}