diff --git a/CHANGELOG.md b/CHANGELOG.md index 21df89e6..8f9e0862 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ # CHANGELOG +## 2024.9.13 + +- Add Selling Mikrotik VPN By @agstrxyz +- Theme Redesign by @Focuslinkstech +- Fix That and this + + ## 2024.8.28 - add Router Status Offline/Online by @Focuslinkstech diff --git a/system/autoload/Admin.php b/system/autoload/Admin.php index 66052cda..18d016c6 100644 --- a/system/autoload/Admin.php +++ b/system/autoload/Admin.php @@ -13,25 +13,27 @@ class Admin { global $db_pass, $config; $enable_session_timeout = $config['enable_session_timeout']; - if ($enable_session_timeout) { - $timeout = 60; - if ($config['session_timeout_duration']) { - $timeout = intval($config['session_timeout_duration']); + $session_timeout_duration = $config['session_timeout_duration'] ? intval($config['session_timeout_duration'] * 60) : intval(60 * 60); // Convert minutes to seconds + + // Check if the session is active and valid + if (isset($_SESSION['aid']) && isset($_SESSION['aid_expiration'])) { + if ($_SESSION['aid_expiration'] > time()) { + if ($enable_session_timeout) { + $_SESSION['aid_expiration'] = time() + $session_timeout_duration; + } + return $_SESSION['aid']; + } + // Session expired, log out the user + elseif ($enable_session_timeout && $_SESSION['aid_expiration'] <= time()) { + self::removeCookie(); + session_destroy(); + _alert(Lang::T('Session has expired. Please log in again.'), 'danger', "admin"); + return 0; } - $session_timeout_duration = $timeout * 60; // Convert minutes to seconds } - if (isset($_SESSION['aid']) && isset($_SESSION['aid_expiration']) && $_SESSION['aid_expiration'] > time()) { - return $_SESSION['aid']; - } elseif ($enable_session_timeout && isset($_SESSION['aid']) && isset($_SESSION['aid_expiration']) && $_SESSION['aid_expiration'] <= time()) { - self::removeCookie(); - session_destroy(); - _alert(Lang::T('Session has expired. Please log in again.'), 'danger', "admin"); - return 0; - } - // Check if cookie is set and valid + // Check if the cookie is set and valid elseif (isset($_COOKIE['aid'])) { - // id.time.sha1 $tmp = explode('.', $_COOKIE['aid']); if (sha1($tmp[0] . '.' . $tmp[1] . '.' . $db_pass) == $tmp[2]) { if (time() - $tmp[1] < 86400 * 7) { diff --git a/system/autoload/Message.php b/system/autoload/Message.php index a7fbae9b..cdc04525 100644 --- a/system/autoload/Message.php +++ b/system/autoload/Message.php @@ -154,6 +154,9 @@ class Message $mail->Body = $body; } $mail->send(); + if (!$mail->send()) { + _log(Lang::T("Email not sent, Mailer Error: ") . $mail->ErrorInfo); + } //

} @@ -201,10 +204,10 @@ class Message return "$via: $msg"; } - public static function sendBalanceNotification($cust, $balance, $balance_now, $message, $via) + public static function sendBalanceNotification($cust, $target, $balance, $balance_now, $message, $via) { global $config; - $msg = str_replace('[[name]]', $cust['fullname'] . ' (' . $cust['username'] . ')', $message); + $msg = str_replace('[[name]]', $target['fullname'] . ' (' . $target['username'] . ')', $message); $msg = str_replace('[[current_balance]]', Lang::moneyFormat($balance_now), $msg); $msg = str_replace('[[balance]]', Lang::moneyFormat($balance), $msg); $phone = $cust['phonenumber']; @@ -219,6 +222,7 @@ class Message } else if ($via == 'wa') { Message::sendWhatsapp($phone, $msg); } + self::addToInbox($cust['id'], Lang::T('Balance Notification'), $msg); } return "$via: $msg"; } @@ -258,4 +262,15 @@ class Message Message::sendWhatsapp($cust['phonenumber'], $textInvoice); } } + + + public static function addToInbox($to_customer_id, $subject, $body, $from = 'System'){ + $v = ORM::for_table('tbl_customers_inbox')->create(); + $v->from = $from; + $v->customer_id = $to_customer_id; + $v->subject = $subject; + $v->date_created = date('Y-m-d H:i:s'); + $v->body = nl2br($body); + $v->save(); + } } diff --git a/system/controllers/customers.php b/system/controllers/customers.php index c38d3461..c2c9c68c 100644 --- a/system/controllers/customers.php +++ b/system/controllers/customers.php @@ -673,10 +673,10 @@ switch ($action) { default: run_hook('list_customers'); #HOOK - $search = _post('search'); - $order = _post('order', 'username'); - $filter = _post('filter', 'Active'); - $orderby = _post('orderby', 'asc'); + $search = _req('search'); + $order = _req('order', 'username'); + $filter = _req('filter', 'Active'); + $orderby = _req('orderby', 'asc'); $order_pos = [ 'username' => 0, 'created_at' => 8, diff --git a/system/controllers/dashboard.php b/system/controllers/dashboard.php index 4d1c9849..9f1185b0 100644 --- a/system/controllers/dashboard.php +++ b/system/controllers/dashboard.php @@ -214,6 +214,12 @@ if ($config['router_check']) { $ui->assign('routeroffs', $routeroffs); } +$timestampFile = "$UPLOAD_PATH/cron_last_run.txt"; +if (file_exists($timestampFile)) { + $lastRunTime = file_get_contents($timestampFile); + $ui->assign('run_date', date('Y-m-d h:i:s A', $lastRunTime)); +} + // Assign the monthly sales data to Smarty $ui->assign('start_date', $start_date); $ui->assign('current_date', $current_date); diff --git a/system/controllers/home.php b/system/controllers/home.php index 147476b5..20e1d1b6 100644 --- a/system/controllers/home.php +++ b/system/controllers/home.php @@ -71,8 +71,9 @@ if (_post('send') == 'balance') { $d->pg_url_payment = 'balance'; $d->status = 2; $d->save(); - Message::sendBalanceNotification($user, $balance, ($user['balance'] - $balance), Lang::getNotifText('balance_send'), $config['user_notification_payment']); - Message::sendBalanceNotification($target, $balance, ($target['balance'] + $balance), Lang::getNotifText('balance_received'), $config['user_notification_payment']); + // + Message::sendBalanceNotification($user, $target, $balance, ($user['balance'] - $balance), Lang::getNotifText('balance_send'), $config['user_notification_payment']); + Message::sendBalanceNotification($target, $user, $balance, ($target['balance'] + $balance), Lang::getNotifText('balance_received'), $config['user_notification_payment']); Message::sendTelegram("#u$user[username] send balance to #u$target[username] \n" . Lang::moneyFormat($balance)); r2(U . 'home', 's', Lang::T('Sending balance success')); } @@ -316,7 +317,15 @@ if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'] && !empty($_SESSI } } } - + +$tcf = ORM::for_table('tbl_customers_fields') + ->where('customer_id', $user['id']) + ->find_many(); +$vpn = ORM::for_table('tbl_port_pool') + ->find_one(); +$ui->assign('cf', $tcf); +$ui->assign('vpn', $vpn); + $ui->assign('unpaid', ORM::for_table('tbl_payment_gateway') ->where('username', $user['username']) ->where('status', 1) diff --git a/system/controllers/order.php b/system/controllers/order.php index 0d4b2a3c..988db104 100644 --- a/system/controllers/order.php +++ b/system/controllers/order.php @@ -113,12 +113,19 @@ switch ($action) { ->where('type', 'Hotspot') ->where('prepaid', 'yes') ->find_many(); + $plans_vpn = ORM::for_table('tbl_plans') + ->where('plan_type', $account_type) + ->where('enabled', '1')->where('is_radius', 0) + ->where('type', 'VPN') + ->where('prepaid', 'yes') + ->find_many(); } $ui->assign('routers', $routers); $ui->assign('radius_pppoe', $radius_pppoe); $ui->assign('radius_hotspot', $radius_hotspot); $ui->assign('plans_pppoe', $plans_pppoe); $ui->assign('plans_hotspot', $plans_hotspot); + $ui->assign('plans_vpn', $plans_vpn); run_hook('customer_view_order_plan'); #HOOK $ui->display('user-ui/orderPlan.tpl'); break; diff --git a/system/controllers/pool.php b/system/controllers/pool.php index 5528e751..ba599f22 100644 --- a/system/controllers/pool.php +++ b/system/controllers/pool.php @@ -147,6 +147,132 @@ switch ($action) { } else { r2(U . 'pool/edit/' . $id, 'e', $msg); } + + case 'port': + $ui->assign('xfooter', ''); + + $name = _post('name'); + if ($name != '') { + $query = ORM::for_table('tbl_port_pool')->where_like('pool_name', '%' . $name . '%')->order_by_desc('id'); + $d = Paginator::findMany($query, ['name' => $name]); + } else { + $query = ORM::for_table('tbl_port_pool')->order_by_desc('id'); + $d = Paginator::findMany($query); + } + + $ui->assign('d', $d); + run_hook('view_port'); #HOOK + $ui->display('port.tpl'); + break; + + case 'add-port': + $r = ORM::for_table('tbl_routers')->find_many(); + $ui->assign('r', $r); + run_hook('view_add_port'); #HOOK + $ui->display('port-add.tpl'); + break; + + case 'edit-port': + $id = $routes['2']; + $d = ORM::for_table('tbl_port_pool')->find_one($id); + if ($d) { + $ui->assign('d', $d); + run_hook('view_edit_port'); #HOOK + $ui->display('port-edit.tpl'); + } else { + r2(U . 'pool/port', 'e', Lang::T('Account Not Found')); + } + break; + + case 'delete-port': + $id = $routes['2']; + run_hook('delete_port'); #HOOK + $d = ORM::for_table('tbl_port_pool')->find_one($id); + if ($d) { + $d->delete(); + + r2(U . 'pool/port', 's', Lang::T('Data Deleted Successfully')); + } + break; + + case 'sync': + $pools = ORM::for_table('tbl_port_pool')->find_many(); + $log = ''; + foreach ($pools as $pool) { + if ($pool['routers'] != 'radius') { + (new MikrotikPppoe())->update_pool($pool, $pool); + $log .= 'DONE: ' . $pool['port_name'] . ': ' . $pool['range_port'] . '
'; + } + } + r2(U . 'pool/list', 's', $log); + break; + case 'add-port-post': + $name = _post('name'); + $port_range = _post('port_range'); + $public_ip = _post('public_ip'); + $routers = _post('routers'); + run_hook('add_pool'); #HOOK + $msg = ''; + if (Validator::Length($name, 30, 2) == false) { + $msg .= 'Name should be between 3 to 30 characters' . '
'; + } + if ($port_range == '' or $routers == '') { + $msg .= Lang::T('All field is required') . '
'; + } + + $d = ORM::for_table('tbl_port_pool')->where('routers', $routers)->find_one(); + if ($d) { + $msg .= Lang::T('Routers already have ports, each router can only have 1 port range!') . '
'; + } + if ($msg == '') { + $b = ORM::for_table('tbl_port_pool')->create(); + $b->public_ip = $public_ip; + $b->port_name = $name; + $b->range_port = $port_range; + $b->routers = $routers; + $b->save(); + r2(U . 'pool/port', 's', Lang::T('Data Created Successfully')); + } else { + r2(U . 'pool/add-port', 'e', $msg); + } + break; + + + case 'edit-port-post': + $name = _post('name'); + $public_ip = _post('public_ip'); + $range_port = _post('range_port'); + $routers = _post('routers'); + run_hook('edit_port'); #HOOK + $msg = ''; + $msg = ''; + if (Validator::Length($name, 30, 2) == false) { + $msg .= 'Name should be between 3 to 30 characters' . '
'; + } + if ($range_port == '' or $routers == '') { + $msg .= Lang::T('All field is required') . '
'; + } + + $id = _post('id'); + $d = ORM::for_table('tbl_port_pool')->find_one($id); + $old = ORM::for_table('tbl_port_pool')->find_one($id); + if (!$d) { + $msg .= Lang::T('Data Not Found') . '
'; + } + + if ($msg == '') { + $d->port_name = $name; + $d->public_ip = $public_ip; + $d->range_port = $range_port; + $d->routers = $routers; + $d->save(); + + + + r2(U . 'pool/port', 's', Lang::T('Data Updated Successfully')); + } else { + r2(U . 'pool/edit-port/' . $id, 'e', $msg); + } break; default: diff --git a/system/controllers/services.php b/system/controllers/services.php index aaa69635..9bc32e5e 100644 --- a/system/controllers/services.php +++ b/system/controllers/services.php @@ -901,6 +901,380 @@ switch ($action) { r2(U . 'services/balance-add', 'e', $msg); } break; + case 'vpn': + $ui->assign('_title', Lang::T('VPN Plans')); + $ui->assign('xfooter', ''); + + $name = _post('name'); + $name = _req('name'); + $type1 = _req('type1'); + $type2 = _req('type2'); + $type3 = _req('type3'); + $bandwidth = _req('bandwidth'); + $valid = _req('valid'); + $device = _req('device'); + $status = _req('status'); + $router = _req('router'); + $ui->assign('type1', $type1); + $ui->assign('type2', $type2); + $ui->assign('type3', $type3); + $ui->assign('bandwidth', $bandwidth); + $ui->assign('valid', $valid); + $ui->assign('device', $device); + $ui->assign('status', $status); + $ui->assign('router', $router); + + $append_url = "&type1=" . urlencode($type1) + . "&type2=" . urlencode($type2) + . "&type3=" . urlencode($type3) + . "&bandwidth=" . urlencode($bandwidth) + . "&valid=" . urlencode($valid) + . "&device=" . urlencode($device) + . "&status=" . urlencode($status) + . "&router=" . urlencode($router); + + $bws = ORM::for_table('tbl_plans')->distinct()->select("id_bw")->where('tbl_plans.type', 'VPN')->findArray(); + $ids = array_column($bws, 'id_bw'); + if(count($ids)){ + $ui->assign('bws', ORM::for_table('tbl_bandwidth')->select("id")->select('name_bw')->where_id_in($ids)->findArray()); + }else{ + $ui->assign('bws', []); + } + $ui->assign('type2s', ORM::for_table('tbl_plans')->getEnum("plan_type")); + $ui->assign('type3s', ORM::for_table('tbl_plans')->getEnum("typebp")); + $ui->assign('valids', ORM::for_table('tbl_plans')->getEnum("validity_unit")); + $ui->assign('routers', array_column(ORM::for_table('tbl_plans')->distinct()->select("routers")->whereNotEqual('routers', '')->findArray(), 'routers')); + $devices = []; + $files = scandir($DEVICE_PATH); + foreach ($files as $file) { + $ext = pathinfo($file, PATHINFO_EXTENSION); + if ($ext == 'php') { + $devices[] = pathinfo($file, PATHINFO_FILENAME); + } + } + $ui->assign('devices', $devices); + $query = ORM::for_table('tbl_bandwidth') + ->left_outer_join('tbl_plans', array('tbl_bandwidth.id', '=', 'tbl_plans.id_bw')) + ->where('tbl_plans.type', 'VPN'); + if (!empty($type1)) { + $query->where('tbl_plans.prepaid', $type1); + } + if (!empty($type2)) { + $query->where('tbl_plans.plan_type', $type2); + } + if (!empty($type3)) { + $query->where('tbl_plans.typebp', $type3); + } + if (!empty($bandwidth)) { + $query->where('tbl_plans.id_bw', $bandwidth); + } + if (!empty($valid)) { + $query->where('tbl_plans.validity_unit', $valid); + } + if (!empty($router)) { + if ($router == 'radius') { + $query->where('tbl_plans.is_radius', '1'); + } else { + $query->where('tbl_plans.routers', $router); + } + } + if (!empty($device)) { + $query->where('tbl_plans.device', $device); + } + if (in_array($status, ['0', '1'])) { + $query->where('tbl_plans.enabled', $status); + } + if ($name != '') { + $query->where_like('tbl_plans.name_plan', '%' . $name . '%'); + } + $d = Paginator::findMany($query, ['name' => $name], 20, $append_url); + + $ui->assign('d', $d); + run_hook('view_list_vpn'); #HOOK + $ui->display('vpn.tpl'); + break; + + case 'vpn-add': + $ui->assign('_title', Lang::T('VPN Plans')); + $d = ORM::for_table('tbl_bandwidth')->find_many(); + $ui->assign('d', $d); + $r = ORM::for_table('tbl_routers')->find_many(); + $ui->assign('r', $r); + $devices = []; + $files = scandir($DEVICE_PATH); + foreach ($files as $file) { + $ext = pathinfo($file, PATHINFO_EXTENSION); + if ($ext == 'php') { + $devices[] = pathinfo($file, PATHINFO_FILENAME); + } + } + $ui->assign('devices', $devices); + run_hook('view_add_vpn'); #HOOK + $ui->display('vpn-add.tpl'); + break; + + case 'vpn-edit': + $ui->assign('_title', Lang::T('VPN Plans')); + $id = $routes['2']; + $d = ORM::for_table('tbl_plans')->find_one($id); + if ($d) { + if (empty($d['device'])) { + if ($d['is_radius']) { + $d->device = 'Radius'; + } else { + $d->device = 'MikrotikVpn'; + } + $d->save(); + } + $ui->assign('d', $d); + $p = ORM::for_table('tbl_pool')->where('routers', ($d['is_radius']) ? 'radius' : $d['routers'])->find_many(); + $ui->assign('p', $p); + $b = ORM::for_table('tbl_bandwidth')->find_many(); + $ui->assign('b', $b); + $r = []; + if ($d['is_radius']) { + $r = ORM::for_table('tbl_routers')->find_many(); + } + $ui->assign('r', $r); + $devices = []; + $files = scandir($DEVICE_PATH); + foreach ($files as $file) { + $ext = pathinfo($file, PATHINFO_EXTENSION); + if ($ext == 'php') { + $devices[] = pathinfo($file, PATHINFO_FILENAME); + } + } + $ui->assign('devices', $devices); + //select expired plan + if ($d['is_radius']) { + $exps = ORM::for_table('tbl_plans')->selects('id', 'name_plan')->where('type', 'VPN')->where("is_radius", 1)->findArray(); + } else { + $exps = ORM::for_table('tbl_plans')->selects('id', 'name_plan')->where('type', 'VPN')->where("routers", $d['routers'])->findArray(); + } + $ui->assign('exps', $exps); + run_hook('view_edit_vpn'); #HOOK + $ui->display('vpn-edit.tpl'); + } else { + r2(U . 'services/vpn', 'e', Lang::T('Account Not Found')); + } + break; + + case 'vpn-delete': + $id = $routes['2']; + + $d = ORM::for_table('tbl_plans')->find_one($id); + if ($d) { + run_hook('delete_vpn'); #HOOK + + $dvc = Package::getDevice($d); + if ($_app_stage != 'demo') { + if (file_exists($dvc)) { + require_once $dvc; + (new $d['device'])->remove_plan($d); + } else { + new Exception(Lang::T("Devices Not Found")); + } + } + $d->delete(); + + r2(U . 'services/vpn', 's', Lang::T('Data Deleted Successfully')); + } + break; + + case 'vpn-add-post': + $name = _post('name_plan'); + $plan_type = _post('plan_type'); + $radius = _post('radius'); + $id_bw = _post('id_bw'); + $price = _post('price'); + $validity = _post('validity'); + $validity_unit = _post('validity_unit'); + $routers = _post('routers'); + $device = _post('device'); + $pool = _post('pool_name'); + $enabled = _post('enabled'); + $prepaid = _post('prepaid'); + $expired_date = _post('expired_date'); + + + $msg = ''; + if (Validator::UnsignedNumber($validity) == false) { + $msg .= 'The validity must be a number' . '
'; + } + if (Validator::UnsignedNumber($price) == false) { + $msg .= 'The price must be a number' . '
'; + } + if ($name == '' or $id_bw == '' or $price == '' or $validity == '' or $pool == '') { + $msg .= Lang::T('All field is required') . '
'; + } + if (empty($radius)) { + if ($routers == '') { + $msg .= Lang::T('All field is required') . '
'; + } + } + + $d = ORM::for_table('tbl_plans')->where('name_plan', $name)->find_one(); + if ($d) { + $msg .= Lang::T('Name Plan Already Exist') . '
'; + } + run_hook('add_vpn'); #HOOK + if ($msg == '') { + $b = ORM::for_table('tbl_bandwidth')->where('id', $id_bw)->find_one(); + if ($b['rate_down_unit'] == 'Kbps') { + $unitdown = 'K'; + $raddown = '000'; + } else { + $unitdown = 'M'; + $raddown = '000000'; + } + if ($b['rate_up_unit'] == 'Kbps') { + $unitup = 'K'; + $radup = '000'; + } else { + $unitup = 'M'; + $radup = '000000'; + } + $rate = $b['rate_up'] . $unitup . "/" . $b['rate_down'] . $unitdown; + $radiusRate = $b['rate_up'] . $radup . '/' . $b['rate_down'] . $raddown . '/' . $b['burst']; + $rate = trim($rate . " " . $b['burst']); + $d = ORM::for_table('tbl_plans')->create(); + $d->type = 'VPN'; + $d->name_plan = $name; + $d->id_bw = $id_bw; + $d->price = $price; + $d->plan_type = $plan_type; + $d->validity = $validity; + $d->validity_unit = $validity_unit; + $d->pool = $pool; + if (!empty($radius)) { + $d->is_radius = 1; + $d->routers = ''; + } else { + $d->is_radius = 0; + $d->routers = $routers; + } + if ($prepaid == 'no') { + if ($expired_date > 28 && $expired_date < 1) { + $expired_date = 20; + } + $d->expired_date = $expired_date; + } else { + $d->expired_date = 0; + } + $d->enabled = $enabled; + $d->prepaid = $prepaid; + $d->device = $device; + $d->save(); + + $dvc = Package::getDevice($d); + if ($_app_stage != 'demo') { + if (file_exists($dvc)) { + require_once $dvc; + (new $d['device'])->add_plan($d); + } else { + new Exception(Lang::T("Devices Not Found")); + } + } + r2(U . 'services/vpn', 's', Lang::T('Data Created Successfully')); + } else { + r2(U . 'services/vpn-add', 'e', $msg); + } + break; + + case 'edit-vpn-post': + $id = _post('id'); + $plan_type = _post('plan_type'); + $name = _post('name_plan'); + $id_bw = _post('id_bw'); + $price = _post('price'); + $validity = _post('validity'); + $validity_unit = _post('validity_unit'); + $routers = _post('routers'); + $device = _post('device'); + $pool = _post('pool_name'); + $plan_expired = _post('plan_expired'); + $enabled = _post('enabled'); + $prepaid = _post('prepaid'); + $expired_date = _post('expired_date'); + $on_login = _post('on_login'); + $on_logout = _post('on_logout'); + + $msg = ''; + if (Validator::UnsignedNumber($validity) == false) { + $msg .= 'The validity must be a number' . '
'; + } + if (Validator::UnsignedNumber($price) == false) { + $msg .= 'The price must be a number' . '
'; + } + if ($name == '' or $id_bw == '' or $price == '' or $validity == '' or $pool == '') { + $msg .= Lang::T('All field is required') . '
'; + } + + $d = ORM::for_table('tbl_plans')->where('id', $id)->find_one(); + $old = ORM::for_table('tbl_plans')->where('id', $id)->find_one(); + if ($d) { + } else { + $msg .= Lang::T('Data Not Found') . '
'; + } + run_hook('edit_vpn'); #HOOK + if ($msg == '') { + $b = ORM::for_table('tbl_bandwidth')->where('id', $id_bw)->find_one(); + if ($b['rate_down_unit'] == 'Kbps') { + $unitdown = 'K'; + $raddown = '000'; + } else { + $unitdown = 'M'; + $raddown = '000000'; + } + if ($b['rate_up_unit'] == 'Kbps') { + $unitup = 'K'; + $radup = '000'; + } else { + $unitup = 'M'; + $radup = '000000'; + } + $rate = $b['rate_up'] . $unitup . "/" . $b['rate_down'] . $unitdown; + $radiusRate = $b['rate_up'] . $radup . '/' . $b['rate_down'] . $raddown . '/' . $b['burst']; + $rate = trim($rate . " " . $b['burst']); + + $d->name_plan = $name; + $d->id_bw = $id_bw; + $d->price = $price; + $d->plan_type = $plan_type; + $d->validity = $validity; + $d->validity_unit = $validity_unit; + $d->routers = $routers; + $d->pool = $pool; + $d->plan_expired = $plan_expired; + $d->enabled = $enabled; + $d->prepaid = $prepaid; + $d->device = $device; + $d->on_login = $on_login; + $d->on_logout = $on_logout; + if ($prepaid == 'no') { + if ($expired_date > 28 && $expired_date < 1) { + $expired_date = 20; + } + $d->expired_date = $expired_date; + } else { + $d->expired_date = 0; + } + $d->save(); + + $dvc = Package::getDevice($d); + if ($_app_stage != 'demo') { + if (file_exists($dvc)) { + require_once $dvc; + (new $d['device'])->update_plan($old, $d); + } else { + new Exception(Lang::T("Devices Not Found")); + } + } + r2(U . 'services/vpn', 's', Lang::T('Data Updated Successfully')); + } else { + r2(U . 'services/vpn-edit/' . $id, 'e', $msg); + } + break; default: $ui->display('a404.tpl'); } diff --git a/system/controllers/settings.php b/system/controllers/settings.php index 33e4abe9..96cb497b 100644 --- a/system/controllers/settings.php +++ b/system/controllers/settings.php @@ -295,6 +295,16 @@ switch ($action) { $d->value = _post('pppoe_plan'); $d->save(); } + $d = ORM::for_table('tbl_appconfig')->where('setting', 'vpn_plan')->find_one(); + if ($d) { + $d->value = _post('vpn_plan'); + $d->save(); + } else { + $d = ORM::for_table('tbl_appconfig')->create(); + $d->setting = 'vpn_plan'; + $d->value = _post('vpn_plan'); + $d->save(); + } $currency_code = $_POST['currency_code']; $d = ORM::for_table('tbl_appconfig')->where('setting', 'currency_code')->find_one(); diff --git a/system/cron.php b/system/cron.php index e4e6719c..81d9f423 100644 --- a/system/cron.php +++ b/system/cron.php @@ -1,6 +1,27 @@ where('enabled', '1')->find_many(); if (!$routers) { echo "No active routers found in the database.\n"; @@ -186,14 +187,20 @@ if ($config['router_check']) { Message::SendEmail($adminEmail, $subject, $message); sendTelegram($message); } - - if (defined('PHP_SAPI') && PHP_SAPI === 'cli') { - echo "Cronjob finished\n"; - } else { - echo ""; - } - - flock($lock, LOCK_UN); - fclose($lock); - unlink($lockFile); + echo "Router monitoring finished\n"; } + + +if (defined('PHP_SAPI') && PHP_SAPI === 'cli') { + echo "Cronjob finished\n"; +} else { + echo ""; +} + +flock($lock, LOCK_UN); +fclose($lock); +unlink($lockFile); + +$timestampFile = "$UPLOAD_PATH/cron_last_run.txt"; +file_put_contents($timestampFile, time()); + diff --git a/system/devices/MikrotikPppoe.php b/system/devices/MikrotikPppoe.php index f9d46dd0..71ac800a 100644 --- a/system/devices/MikrotikPppoe.php +++ b/system/devices/MikrotikPppoe.php @@ -10,14 +10,14 @@ use PEAR2\Net\RouterOS; -class MikrotikPppoeCustom +class MikrotikPppoe { // show Description function description() { return [ 'title' => 'Mikrotik PPPOE', - 'description' => 'To handle connection between PHPNuxBill with Mikrotik PPPOE using Custom Username, IP and Password', + 'description' => 'To handle connection between PHPNuxBill with Mikrotik PPPOE', 'author' => 'ibnux', 'url' => [ 'Github' => 'https://github.com/hotspotbilling/phpnuxbill/', @@ -51,6 +51,8 @@ class MikrotikPppoeCustom } if (!empty($customer['pppoe_ip'])) { $setRequest->setArgument('remote-address', $customer['pppoe_ip']); + }else{ + $setRequest->setArgument('remote-address', '0.0.0.0'); } $setRequest->setArgument('profile', $plan['name_plan']); $setRequest->setArgument('comment', $customer['fullname'] . ' | ' . $customer['email'] . ' | ' . implode(', ', User::getBillNames($customer['id']))); diff --git a/system/devices/MikrotikPppoeCustom.php b/system/devices/MikrotikVpn.php similarity index 72% rename from system/devices/MikrotikPppoeCustom.php rename to system/devices/MikrotikVpn.php index 83406144..a8f41fec 100644 --- a/system/devices/MikrotikPppoeCustom.php +++ b/system/devices/MikrotikVpn.php @@ -1,27 +1,20 @@ 'Mikrotik PPPOE Custom', - 'description' => 'To handle connection between PHPNuxBill with Mikrotik PPPOE using Custom Username, IP and Password', - 'author' => 'ibnux', + 'title' => 'Mikrotik Vpn', + 'description' => 'To handle connection between PHPNuxBill with Mikrotik VPN', + 'author' => 'agstr', 'url' => [ - 'Github' => 'https://github.com/hotspotbilling/phpnuxbill/', - 'Telegram' => 'https://t.me/phpnuxbill', + 'Github' => 'https://github.com/agstrxyz', + 'Telegram' => 'https://t.me/agstrxyz', + 'Youtube' => 'https://www.youtube.com/@agstrxyz', 'Donate' => 'https://paypal.me/ibnux' ] ]; @@ -34,8 +27,7 @@ class MikrotikPppoeCustom $client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); $cid = self::getIdByCustomer($customer, $client); if (empty($cid)) { - //customer not exists, add it - $this->addPpoeUser($client, $plan, $customer); + $this->addVpnUser($client, $plan, $customer); }else{ $setRequest = new RouterOS\Request('/ppp/secret/set'); $setRequest->setArgument('numbers', $cid); @@ -50,18 +42,17 @@ class MikrotikPppoeCustom $setRequest->setArgument('name', $customer['username']); } if (!empty($customer['pppoe_ip'])) { - $setRequest->setArgument('local-address', $customer['pppoe_ip']); + $setRequest->setArgument('remote-address', $customer['pppoe_ip']); }else{ - $setRequest->setArgument('local-address', '0.0.0.0'); + $setRequest->setArgument('remote-address', '0.0.0.0'); } $setRequest->setArgument('profile', $plan['name_plan']); $setRequest->setArgument('comment', $customer['fullname'] . ' | ' . $customer['email'] . ' | ' . implode(', ', User::getBillNames($customer['id']))); $client->sendSync($setRequest); - //disconnect then if(isset($isChangePlan) && $isChangePlan){ - $this->removePpoeActive($client, $customer['username']); + $this->removeVpnActive($client, $customer['username']); if (!empty($customer['pppoe_username'])) { - $this->removePpoeActive($client, $customer['pppoe_username']); + $this->removeVpnActive($client, $customer['pppoe_username']); } } } @@ -75,29 +66,27 @@ class MikrotikPppoeCustom $p = ORM::for_table("tbl_plans")->find_one($plan['plan_expired']); if($p){ $this->add_customer($customer, $p); - $this->removePpoeActive($client, $customer['username']); + $this->removeVpnActive($client, $customer['username']); if (!empty($customer['pppoe_username'])) { - $this->removePpoeActive($client, $customer['pppoe_username']); + $this->removeVpnActive($client, $customer['pppoe_username']); } return; } } - $this->removePpoeUser($client, $customer['username']); + $this->removeVpnUser($client, $customer['username'], $customer['id']); if (!empty($customer['pppoe_username'])) { - $this->removePpoeUser($client, $customer['pppoe_username']); + $this->removeVpnUser($client, $customer['pppoe_username'], $customer['id']); } - $this->removePpoeActive($client, $customer['username']); + $this->removeVpnActive($client, $customer['username']); if (!empty($customer['pppoe_username'])) { - $this->removePpoeActive($client, $customer['pppoe_username']); + $this->removeVpnActive($client, $customer['pppoe_username']); } } - // customer change username public function change_username($plan, $from, $to) { $mikrotik = $this->info($plan['routers']); $client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); - //check if customer exists $printRequest = new RouterOS\Request('/ppp/secret/print'); $printRequest->setQuery(RouterOS\Query::where('name', $from)); $cid = $client->sendSync($printRequest)->getProperty('.id'); @@ -106,8 +95,7 @@ class MikrotikPppoeCustom $setRequest->setArgument('numbers', $cid); $setRequest->setArgument('name', $to); $client->sendSync($setRequest); - //disconnect then - $this->removePpoeActive($client, $from); + $this->removeVpnActive($client, $from); } } @@ -116,7 +104,6 @@ class MikrotikPppoeCustom $mikrotik = $this->info($plan['routers']); $client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); - //Add Pool $bw = ORM::for_table("tbl_bandwidth")->find_one($plan['id_bw']); if ($bw['rate_down_unit'] == 'Kbps') { @@ -144,9 +131,7 @@ class MikrotikPppoeCustom ); } - /** - * Function to ID by username from Mikrotik - */ + function getIdByCustomer($customer, $client){ $printRequest = new RouterOS\Request('/ppp/secret/print'); $printRequest->setQuery(RouterOS\Query::where('name', $customer['username'])); @@ -306,7 +291,7 @@ class MikrotikPppoeCustom return new RouterOS\Client($iport[0], $user, $pass, ($iport[1]) ? $iport[1] : null); } - function removePpoeUser($client, $username) + function removeVpnUser($client, $username, $cstid) { global $_app_stage; if ($_app_stage == 'demo') { @@ -319,12 +304,13 @@ class MikrotikPppoeCustom $removeRequest = new RouterOS\Request('/ppp/secret/remove'); $removeRequest->setArgument('numbers', $id); $client->sendSync($removeRequest); + $this->rmNat($client, $cstid); } - function addPpoeUser($client, $plan, $customer) + function addVpnUser($client, $plan, $customer) { $setRequest = new RouterOS\Request('/ppp/secret/add'); - $setRequest->setArgument('service', 'pppoe'); + $setRequest->setArgument('service', 'any'); $setRequest->setArgument('profile', $plan['name_plan']); $setRequest->setArgument('comment', $customer['fullname'] . ' | ' . $customer['email'] . ' | ' . implode(', ', User::getBillNames($customer['id']))); if (!empty($customer['pppoe_password'])) { @@ -338,12 +324,21 @@ class MikrotikPppoeCustom $setRequest->setArgument('name', $customer['username']); } if (!empty($customer['pppoe_ip'])) { - $setRequest->setArgument('local-address', $customer['pppoe_ip']); - } + $ips = $customer['pppoe_ip']; + $setRequest->setArgument('remote-address', $customer['pppoe_ip']); + } else { + $ips = $this->checkIpAddr($plan['pool'], $customer['id']); + $setRequest->setArgument('remote-address', $ips); + + } + $this->addNat($client, $plan, $customer, $ips); $client->sendSync($setRequest); + $customer->service_type = 'VPN'; + $customer->pppoe_ip = $ips; + $customer->save(); } - function removePpoeActive($client, $username) + function removeVpnActive($client, $username) { global $_app_stage; if ($_app_stage == 'demo') { @@ -359,18 +354,6 @@ class MikrotikPppoeCustom $client->sendSync($removeRequest); } - function getIpHotspotUser($client, $username) - { - global $_app_stage; - if ($_app_stage == 'demo') { - return null; - } - $printRequest = new RouterOS\Request( - '/ip hotspot active print', - RouterOS\Query::where('user', $username) - ); - return $client->sendSync($printRequest)->getProperty('address'); - } function addIpToAddressList($client, $ip, $listName, $comment = '') { @@ -385,6 +368,7 @@ class MikrotikPppoeCustom ->setArgument('comment', $comment) ->setArgument('list', $listName) ); + } function removeIpFromAddressList($client, $ip) @@ -404,4 +388,117 @@ class MikrotikPppoeCustom ->setArgument('numbers', $id) ); } + + function addNat($client, $plan, $cust, $ips) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $this->checkPort($cust['id'], 'Winbox', $plan['routers']); + $this->checkPort($cust['id'], 'Api', $plan['routers']); + $this->checkPort($cust['id'], 'Web', $plan['routers']); + $tcf = ORM::for_table('tbl_customers_fields') + ->where('customer_id', $cust['id']) + ->find_many(); + $ip = ORM::for_table('tbl_port_pool') + ->where('routers', $plan['routers']) + ->find_one(); + foreach ($tcf as $cf) { + $dst = $cf['field_value']; + $cmnt = $cf['field_name']; + if ($cmnt == 'Winbox') { + $tp = '8291'; } + if ($cmnt == 'Web') { + $tp = '80'; } + if ($cmnt == 'Api') { + $tp = '8728'; } + if ($cmnt == 'Winbox' || $cmnt == 'Web' || $cmnt == 'Api') { + $addRequest = new RouterOS\Request('/ip/firewall/nat/add'); + $client->sendSync( + $addRequest + ->setArgument('chain', 'dstnat') + ->setArgument('protocol', 'tcp') + ->setArgument('dst-port', $dst) + ->setArgument('action', 'dst-nat') + ->setArgument('to-addresses', $ips) + ->setArgument('to-ports', $tp) + ->setArgument('dst-address', $ip['public_ip']) + ->setArgument('comment', $cmnt.' || '.$cust['username']) + ); + } + } + } + + function rmNat($client, $cstid) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + + $cst = ORM::for_table('tbl_customers')->find_one($cstid); + $printRequest = new RouterOS\Request('/ip/firewall/nat/print'); + $printRequest->setQuery(RouterOS\Query::where('to-addresses', $cst['pppoe_ip'])); + $nats = $client->sendSync($printRequest); + foreach ($nats as $nat) { + $id = $client->sendSync($printRequest)->getProperty('.id'); + $removeRequest = new RouterOS\Request('/ip/firewall/nat/remove'); + $removeRequest->setArgument('numbers', $id); + $client->sendSync($removeRequest); + } + } + + + function checkPort($id, $portn, $router) + { + $tcf = ORM::for_table('tbl_customers_fields') + ->where('customer_id', $id) + ->where('field_name', $portn) + ->find_one(); + $ports = ORM::for_table('tbl_port_pool') + ->where('routers', $router) + ->find_one(); + $port = explode('-',$ports['range_port']); + if (empty($tcf) && !empty($ports)) { + repeat: + $portr = rand($port['0'], $port['1']); + if (ORM::for_table('tbl_customers_fields')->where('field_value', $portr)->find_one()) { + if($portr == $port['1']) + { + return; + } + goto repeat; + } + $cf = ORM::for_table('tbl_customers_fields')->create(); + $cf->customer_id = $id; + $cf->field_name = $portn; + $cf->field_value = $portr; + $cf->save(); + } + } + + function checkIpAddr($pname, $id) { + $c = ORM::for_table('tbl_customers')->find_one($id); + $ipp = ORM::for_table('tbl_pool') + ->where('pool_name', $pname) + ->find_one(); + $ip_r = explode('-',$ipp['range_ip']); + $ip_1 = explode('.',$ip_r['0']); + $ip_2 = explode('.',$ip_r['1']); + repeat: + $ipt = rand($ip_1['3'], $ip_2['3']); + $ips = $ip_1['0'].'.'.$ip_1['1'].'.'.$ip_1['2'].'.'.$ipt; + if (empty($c['pppoe_ip'])) { + if (ORM::for_table('tbl_customers')->where('pppoe_ip' ,$ips)->find_one()) { + if ($ip_2['3'] == $ipt) + { + return; + } + goto repeat; + } + return $ips; + } + } + } diff --git a/system/updates.json b/system/updates.json index bce2f4ef..8d66fc0c 100644 --- a/system/updates.json +++ b/system/updates.json @@ -155,5 +155,11 @@ "2024.8.28" : [ "ALTER TABLE `tbl_routers` ADD `status` ENUM('Online', 'Offline') DEFAULT 'Online' AFTER `coordinates`;", "ALTER TABLE `tbl_routers` ADD `last_seen` DATETIME AFTER `status`;" + ], + "2024.9.13" : [ + "ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','VPN','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;", + "ALTER TABLE `tbl_customers` CHANGE `service_type` `service_type` ENUM('Hotspot','PPPoE','VPN','Others') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'Others' COMMENT 'For selecting user type';", + "ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','VPN','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;", + "CREATE TABLE IF NOT EXISTS `tbl_port_pool` ( `id` int(10) NOT NULL AUTO_INCREMENT , `public_ip` varchar(40) NOT NULL, `port_name` varchar(40) NOT NULL, `range_port` varchar(40) NOT NULL, `routers` varchar(40) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;" ] } \ No newline at end of file diff --git a/ui/ui/app-localisation.tpl b/ui/ui/app-localisation.tpl index 936c3abb..571d5588 100644 --- a/ui/ui/app-localisation.tpl +++ b/ui/ui/app-localisation.tpl @@ -128,6 +128,14 @@ {Lang::T('Change title in user Plan order')} +

+ +
+ +
+ {Lang::T('Change title in user Plan order')} +
diff --git a/ui/ui/customers-edit.tpl b/ui/ui/customers-edit.tpl index e0f670c2..05b8b1bd 100644 --- a/ui/ui/customers-edit.tpl +++ b/ui/ui/customers-edit.tpl @@ -75,6 +75,7 @@ +
diff --git a/ui/ui/customers.tpl b/ui/ui/customers.tpl index f4434ccb..7829ffcd 100644 --- a/ui/ui/customers.tpl +++ b/ui/ui/customers.tpl @@ -29,7 +29,7 @@
- Order    + Order  
{foreach $type2s as $t} - + {/foreach}
@@ -44,9 +44,9 @@
@@ -54,8 +54,8 @@
@@ -63,8 +63,8 @@
@@ -72,7 +72,7 @@ @@ -81,7 +81,7 @@
@@ -104,38 +104,40 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - {foreach $d as $ds} +
+
{Lang::T('Internet Package')}{Lang::T('Limit')} - {Lang::T('Expired')}
{Lang::T('Name')}{Lang::T('Type')}Bandwidth{Lang::T('Category')}{Lang::T('Price')}{Lang::T('Validity')}{Lang::T('Time')}{Lang::T('Data')}{Lang::T('Location')}{Lang::T('Device')}{Lang::T('Internet Package')}{Lang::T('Date')}{Lang::T('ID')}{Lang::T('Manage')}
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + {foreach $d as $ds} + }class="warning" title="Postpaid" {/if}> @@ -146,11 +148,11 @@ @@ -163,13 +165,14 @@ {Lang::T('Edit')} + onclick="return confirm('{Lang::T('Delete')}?')" + class="btn btn-danger btn-xs"> - {/foreach} - -
{Lang::T('Internet Package')} + {Lang::T('Limit')} + {Lang::T('Expired')}
{Lang::T('Name')}{Lang::T('Type')}Bandwidth{Lang::T('Category')}{Lang::T('Price')}{Lang::T('Validity')}{Lang::T('Time')}{Lang::T('Data')}{Lang::T('Location')}{Lang::T('Device')}{Lang::T('Internet Package')}{Lang::T('Date')}{Lang::T('ID')}{Lang::T('Manage')}
{$ds['name_plan']} {if $ds['prepaid'] == no}Postpaid{else}Prepaid{/if} {$ds['plan_type']} {$ds['name_bw']}{$ds['data_limit']} {$ds['data_unit']} {if $ds['is_radius']} - RADIUS + RADIUS {else} - {if $ds['routers']!=''} - {$ds['routers']} - {/if} + {if $ds['routers']!=''} + {$ds['routers']} + {/if} {/if} {$ds['device']}
+ {/foreach} + + +
-{include file="sections/footer.tpl"} +{include file="sections/footer.tpl"} \ No newline at end of file diff --git a/ui/ui/plan.tpl b/ui/ui/plan.tpl index 4cf71280..32111f4a 100644 --- a/ui/ui/plan.tpl +++ b/ui/ui/plan.tpl @@ -5,19 +5,19 @@
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])} -
- sync -
- {*
- CSV -
*} +
+ sync +
+ {*
+ CSV +
*} {/if}   -{Lang::T('Active Customers')} + {Lang::T('Active Customers')}
@@ -70,34 +70,39 @@
- - - - - - - - - - - - - - - {foreach $d as $ds} - +
  +
{Lang::T('Username')}{Lang::T('Plan Name')}{Lang::T('Type')}{Lang::T('Created On')}{Lang::T('Expires On')}{Lang::T('Method')}{Lang::T('Location')}{Lang::T('Manage')}
+ + + + + + + + + + + + + + {foreach $d as $ds} + {if $ds['type'] == 'Hotspot'} - {else} + {/if} + {if $ds['type'] == 'PPPOE'} {/if} + {if $ds['type'] == 'VPN'} + + {/if} @@ -107,19 +112,20 @@ {Lang::T('Edit')} {if in_array($_admin['user_type'],['SuperAdmin','Admin'])} - + {/if} {if $ds['status']=='off' && $_c['extend_expired']} - {Lang::T('Extend')} + {Lang::T('Extend')} {/if} - {/foreach} - -
{Lang::T('Username')}{Lang::T('Plan Name')}{Lang::T('Type')}{Lang::T('Created On')}{Lang::T('Expires On')}{Lang::T('Method')}{Lang::T('Location')}{Lang::T('Manage')}
{if $ds['customer_id'] == '0'} - {$ds['username']} + {$ds['username']} {else} - {$ds['username']} + {$ds['username']} {/if} {$ds['namebp']}{$ds['namebp']}{$ds['namebp']}{$ds['type']} {Lang::dateAndTimeFormat($ds['recharged_on'],$ds['recharged_time'])} {Lang::dateAndTimeFormat($ds['expiration'],$ds['time'])}
+ {/foreach} + + +
{include file="pagination.tpl"} @@ -131,10 +137,10 @@ var res = prompt("Extend for many days?", "3"); if (res) { if (confirm("Extend for " + res + " days?")) { - window.location.href = "{$_url}plan/extend/"+idP+"/"+res+"&stoken={App::getToken()}"; + window.location.href = "{$_url}plan/extend/" + idP + "/" + res + "&stoken={App::getToken()}"; } } } -{include file="sections/footer.tpl"} +{include file="sections/footer.tpl"} \ No newline at end of file diff --git a/ui/ui/port-add.tpl b/ui/ui/port-add.tpl new file mode 100644 index 00000000..b689a896 --- /dev/null +++ b/ui/ui/port-add.tpl @@ -0,0 +1,51 @@ +{include file="sections/header.tpl"} + +
+
+
+
{Lang::T('Add Port Pool')}
+
+ +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + Or {Lang::T('Cancel')} +
+
+
+ +
+
+
+
+ +{include file="sections/footer.tpl"} diff --git a/ui/ui/port-edit.tpl b/ui/ui/port-edit.tpl new file mode 100644 index 00000000..46413314 --- /dev/null +++ b/ui/ui/port-edit.tpl @@ -0,0 +1,49 @@ +{include file="sections/header.tpl"} + +
+
+
+
{Lang::T('Edit Port')}
+
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ + Or {Lang::T('Cancel')} +
+
+
+ +
+
+
+
+ +{include file="sections/footer.tpl"} diff --git a/ui/ui/port.tpl b/ui/ui/port.tpl new file mode 100644 index 00000000..77fd2661 --- /dev/null +++ b/ui/ui/port.tpl @@ -0,0 +1,76 @@ +{include file="sections/header.tpl"} + +
+
+
+
+
+ sync +
+ {Lang::T('Port Pool')} - VPN Tunnels +
+
+
+
+ +
+   +
+
+ + + + + + + + + + + + + {foreach $d as $ds} + + + + + + + + + {/foreach} + +
{Lang::T('Port Name')}{Lang::T('Public IP')}{Lang::T('Range Port')}{Lang::T('Routers')}{Lang::T('Manage')}ID
{$ds['port_name']}{$ds['public_ip']}{$ds['range_port']}{$ds['routers']} + {Lang::T('Edit')} + + {$ds['id']}
+
+ {include file="pagination.tpl"} +
+

{Lang::T('Create expired Internet Plan')}

+

{Lang::T('When customer expired, you can move it to Expired Internet Plan')}

+
+
+
+
+
+ +{include file="sections/footer.tpl"} diff --git a/ui/ui/pppoe.tpl b/ui/ui/pppoe.tpl index 3c17d6b1..9dfc8711 100644 --- a/ui/ui/pppoe.tpl +++ b/ui/ui/pppoe.tpl @@ -35,8 +35,8 @@ @@ -44,9 +44,9 @@ @@ -54,8 +54,8 @@ @@ -63,8 +63,8 @@ @@ -72,7 +72,7 @@ @@ -81,7 +81,7 @@ @@ -104,36 +104,38 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - {foreach $d as $ds} - +
+
{Lang::T('Internet Plan')} - {Lang::T('Expired')}
{Lang::T('Name')}{Lang::T('Type')}{Lang::T('Bandwidth')}{Lang::T('Price')}{Lang::T('Validity')}{Lang::T('IP Pool')}{Lang::T('Internet Plan')}{Lang::T('Date')}{Lang::T('Location')}{Lang::T('Device')}{Lang::T('Manage')}ID
+ + + + + + + + + + + + + + + + + + + + + + + + + {foreach $d as $ds} + - + @@ -144,11 +146,11 @@ @@ -161,9 +163,10 @@ - {/foreach} - -
{Lang::T('Internet Plan')} + {Lang::T('Expired')}
{Lang::T('Name')}{Lang::T('Type')}{Lang::T('Bandwidth')}{Lang::T('Price')}{Lang::T('Validity')}{Lang::T('IP Pool')}{Lang::T('Internet Plan')}{Lang::T('Date')}{Lang::T('Location')}{Lang::T('Device')}{Lang::T('Manage')}ID
{$ds['name_plan']}{$ds['plan_type']} {if $ds['prepaid'] != 'yes'}{Lang::T('Postpaid')}{else}{Lang::T('Prepaid')}{/if}{$ds['plan_type']} {if $ds['prepaid'] != + 'yes'}{Lang::T('Postpaid')}{else}{Lang::T('Prepaid')}{/if} {$ds['name_bw']} {Lang::moneyFormat($ds['price'])} {$ds['validity']} {$ds['validity_unit']}{if $ds['prepaid'] == no}{$ds['expired_date']}{/if} {if $ds['is_radius']} - RADIUS + RADIUS {else} - {if $ds['routers']!=''} - {$ds['routers']} - {/if} + {if $ds['routers']!=''} + {$ds['routers']} + {/if} {/if} {$ds['device']} {$ds['id']}
+ {/foreach} + + +
-{include file="sections/footer.tpl"} +{include file="sections/footer.tpl"} \ No newline at end of file diff --git a/ui/ui/recharge.tpl b/ui/ui/recharge.tpl index b896ab50..96370d58 100644 --- a/ui/ui/recharge.tpl +++ b/ui/ui/recharge.tpl @@ -22,6 +22,7 @@
+
diff --git a/ui/ui/scripts/custom.js b/ui/ui/scripts/custom.js index 844f5ffc..87734583 100644 --- a/ui/ui/scripts/custom.js +++ b/ui/ui/scripts/custom.js @@ -96,7 +96,7 @@ $(function() { }); }; - }else{ + } else if ($('#POE').is(':checked')) { $.ajax({ type: "POST", dataType: "html", @@ -117,6 +117,27 @@ $(function() { } }); }); + } else { + $.ajax({ + type: "POST", + dataType: "html", + url: "index.php?_route=autoload/server", + success: function(msg){ + $("#server").html(msg); + } + }); + $("#server").change(function(){ + var server = $("#server").val(); + $.ajax({ + type: "POST", + dataType: "html", + url: "index.php?_route=autoload/plan", + data: "jenis=VPN&server="+server, + success: function(msg){ + $("#plan").html(msg); + } + }); + }); } }); }); diff --git a/ui/ui/sections/header.tpl b/ui/ui/sections/header.tpl index e19b8073..89c354d6 100644 --- a/ui/ui/sections/header.tpl +++ b/ui/ui/sections/header.tpl @@ -148,12 +148,45 @@ color: inherit; background-color: transparent; border-color: transparent; + border-bottom-right-radius: 21px; + border-bottom-left-radius: 21px; } - .panel-primary>.panel-heading { - color: inherit; - background-color: transparent; - border-color: transparent; + .panel-success>.panel-heading { + border-bottom-right-radius: 21px; + border-bottom-left-radius: 21px; + } + + .panel-cron-success>.panel-heading { + border-bottom-right-radius: 21px; + border-bottom-left-radius: 21px; + color: #fff; + background-color: #169210; + border-color: #25e01c; + + } + + .panel-cron-warning>.panel-heading { + border-bottom-right-radius: 21px; + border-bottom-left-radius: 21px; + color: #350808; + background-color: #efeb0a; + border-color: #efeb0a; + } + .panel-cron-danger>.panel-heading { + border-bottom-right-radius: 21px; + border-bottom-left-radius: 21px; + color: #fff; + background-color: #e61212; + border-color: #df1335; + } + + .panel-danger>.panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; + border-bottom-right-radius: 21px; + border-bottom-left-radius: 21px; } .panel-heading { @@ -656,9 +689,6 @@ } .toggle-container { - position: absolute; - top: 17px; - right: 15px; cursor: pointer; } @@ -668,19 +698,6 @@ transition: color 0.5s ease; } - @media (max-width: 600px) { - .toggle-container { - top: 15px; - right: 60px; - } - - .toggle-container .toggle-icon { - font-size: 20px; - color: rgb(100 116 139); - transition: color 0.5s ease; - } - } - .dark-mode .toggle-container .toggle-icon { color: #ffdd57; } @@ -1157,9 +1174,9 @@
  • -
    + 🌞 -
    +
  • PPPOE
  • +
  • VPN
  • Bandwidth
  • {if $_c['enable_balance'] == 'yes'} @@ -1327,6 +1346,8 @@ href="{$_url}routers">Routers
  • IP Pool
  • +
  • Port Pool
  • {Lang::T('Routers Maps')}
  • {$_MENU_NETWORK} diff --git a/ui/ui/user-ui/dashboard.tpl b/ui/ui/user-ui/dashboard.tpl index 361e6487..65e24c39 100644 --- a/ui/ui/user-ui/dashboard.tpl +++ b/ui/ui/user-ui/dashboard.tpl @@ -88,6 +88,8 @@ Hotspot {elseif $_user.service_type == 'PPPoE'} PPPoE + {elseif $_user.service_type == 'VPN'} + VPN {elseif $_user.service_type == 'Others' || $_user.service_type == null} Others {/if} @@ -165,8 +167,10 @@
    {if $_bill['type'] == 'Hotspot'} {if $_c['hotspot_plan']==''}Hotspot Plan{else}{$_c['hotspot_plan']}{/if} - {else} + {else if $_bill['type'] == 'PPPOE'} {if $_c['pppoe_plan']==''}PPPOE Plan{else}{$_c['pppoe_plan']}{/if} + {else if $_bill['type'] == 'VPN'} + {if $_c['pppoe_plan']==''}VPN Plan{else}{$_c['vpn_plan']}{/if} {/if}
    @@ -215,6 +219,25 @@ {$_bill['plan_type']} + {if $_bill['type'] == 'VPN' && $_bill['routers'] == $vpn['routers']} + + {Lang::T('Public IP')} + {$vpn['public_ip']} / {$vpn['port_name']} + + + {Lang::T('Private IP')} + {$_user['pppoe_ip']} + + {foreach $cf as $tcf} + + {if $tcf['field_name'] == 'Winbox' or $tcf['field_name'] == 'Api' or $tcf['field_name'] == 'Web'} + {$tcf['field_name']} - Port + {$tcf['field_value']} + + {/if} + {/foreach} + {/if} + {if $nux_ip neq ''} {Lang::T('Current IP')} diff --git a/ui/ui/user-ui/header.tpl b/ui/ui/user-ui/header.tpl index a2be9f04..4298c03a 100644 --- a/ui/ui/user-ui/header.tpl +++ b/ui/ui/user-ui/header.tpl @@ -333,9 +333,6 @@ .toggle-container { - position: absolute; - top: 17px; - right: 15px; cursor: pointer; } @@ -346,10 +343,6 @@ } @media (max-width: 600px) { - .toggle-container { - top: 15px; - right: 200px; - } .toggle-container .toggle-icon { font-size: 20px; @@ -781,13 +774,13 @@ @@ -29,10 +29,11 @@ {Lang::T("Next")} {/if} + {Lang::T("Back")} {Lang::T("Delete")} - {Lang::T("Share")} + {Lang::T("Share")} diff --git a/ui/ui/user-ui/orderPlan.tpl b/ui/ui/user-ui/orderPlan.tpl index f79ed4dd..9db514ff 100644 --- a/ui/ui/user-ui/orderPlan.tpl +++ b/ui/ui/user-ui/orderPlan.tpl @@ -226,6 +226,7 @@ {/if} {foreach $routers as $router} {if Validator::isRouterHasPlan($plans_hotspot, $router['name']) || Validator::isRouterHasPlan($plans_pppoe, + $router['name']) || Validator::isRouterHasPlan($plans_vpn, $router['name'])}
    {$router['name']}
    @@ -338,9 +339,62 @@ {/if} {/foreach}
    + {/if} + {if $_user['service_type'] == 'VPN' && Validator::countRouterPlan($plans_vpn,$router['name'])>0} +
    {if $_c['vpn_plan']==''}VPN Plan{else}{$_c['vpn_plan']}{/if}
    +
    + {foreach $plans_vpn as $plan} + {if $router['name'] eq $plan['routers']} +
    +
    +
    {$plan['name_plan']}
    +
    +
    + + + + + + + + + + + + + + + +
    {Lang::T('Type')}{$plan['type']}
    {Lang::T('Price')}{Lang::moneyFormat($plan['price'])}
    {Lang::T('Validity')}{$plan['validity']} {$plan['validity_unit']}
    +
    +
    +
    +
    + {Lang::T('Buy')} + {if $_c['enable_balance'] == 'yes' && $_user['balance']>=$plan['price']} + {Lang::T('Pay With Balance')} + {/if} +
    + {if $_c['enable_balance'] == 'yes' && $_c['allow_balance_transfer'] == 'yes' && + $_user['balance']>=$plan['price']} + {Lang::T('Buy for friend')} + {/if} +
    +
    +
    + {/if} + {/foreach} +
    {/if} {if $_user['service_type'] == 'Others' || $_user['service_type'] == '' && (Validator::countRouterPlan($plans_hotspot, $router['name'])>0 || Validator::countRouterPlan($plans_pppoe, + $router['name'])>0 || Validator::countRouterPlan($plans_vpn, $router['name'])>0)}
    {if $_c['hotspot_plan']==''}Hotspot Plan{else}{$_c['hotspot_plan']}{/if}
    @@ -443,6 +497,56 @@ {/if} {/foreach} +
    {if $_c['vpn_plan']==''}VPN Plan{else}{$_c['vpn_plan']}{/if}
    +
    + {foreach $plans_vpn as $plan} + {if $router['name'] eq $plan['routers']} +
    +
    +
    {$plan['name_plan']}
    +
    +
    + + + + + + + + + + + + + + + +
    {Lang::T('Type')}{$plan['type']}
    {Lang::T('Price')}{Lang::moneyFormat($plan['price'])}
    {Lang::T('Validity')}{$plan['validity']} {$plan['validity_unit']}
    +
    +
    +
    +
    + {Lang::T('Buy')} + {if $_c['enable_balance'] == 'yes' && $_user['balance']>=$plan['price']} + {Lang::T('Pay With Balance')} + {/if} +
    + {if $_c['enable_balance'] == 'yes' && $_c['allow_balance_transfer'] == 'yes' && + $_user['balance']>=$plan['price']} + {Lang::T('Buy for friend')} + {/if} +
    +
    +
    + {/if} + {/foreach} +
    {/if} {/if} diff --git a/ui/ui/voucher.tpl b/ui/ui/voucher.tpl index 6f357b48..abf652f0 100644 --- a/ui/ui/voucher.tpl +++ b/ui/ui/voucher.tpl @@ -17,11 +17,12 @@
    {if in_array($_admin['user_type'],['SuperAdmin','Admin'])} - + {/if}  
    @@ -41,8 +42,8 @@
    @@ -50,7 +51,7 @@ @@ -65,7 +66,7 @@ @@ -76,8 +77,8 @@ class="fa fa-search">
    - +
    @@ -85,24 +86,25 @@
    - - - - - - - - - - - - - - - - - {foreach $d as $ds} - +
      +
    ID{Lang::T('Type')}{Lang::T('Routers')}{Lang::T('Plan Name')}{Lang::T('Code Voucher')}{Lang::T('Status Voucher')}{Lang::T('Customer')}{Lang::T('Used Date')}{Lang::T('Generated By')}{Lang::T('Manage')}
    + + + + + + + + + + + + + + + + {foreach $d as $ds} + @@ -112,33 +114,35 @@ onmouseenter="this.style.backgroundColor = 'white';"> {$ds['code']} - {/foreach} - -
    ID{Lang::T('Type')}{Lang::T('Routers')}{Lang::T('Plan Name')}{Lang::T('Code Voucher')}{Lang::T('Status Voucher')}{Lang::T('Customer')}{Lang::T('Used Date')}{Lang::T('Generated By')}{Lang::T('Manage')}
    {$ds['id']} {$ds['type']} {$ds['routers']} {if $ds['status'] eq '0'} {else} + {else} {/if} {if $ds['user'] eq '0'} - {else}{$ds['user']} {/if} {if $ds['used_date']}{Lang::dateTimeFormat($ds['used_date'])}{/if} {if $ds['generated_by']} - {$admins[$ds['generated_by']]} + {$admins[$ds['generated_by']]} {else} - {/if} {if $ds['status'] neq '1'} -   {Lang::T('View')}   +   {Lang::T('View')}   {/if} {if in_array($_admin['user_type'],['SuperAdmin','Admin'])} - + {/if}
    + {/foreach} + + +
    {include file="pagination.tpl"} -{include file="sections/footer.tpl"} +{include file="sections/footer.tpl"} \ No newline at end of file diff --git a/ui/ui/vpn-add.tpl b/ui/ui/vpn-add.tpl new file mode 100644 index 00000000..ae26a8ac --- /dev/null +++ b/ui/ui/vpn-add.tpl @@ -0,0 +1,188 @@ +{include file="sections/header.tpl"} + +
    +
    +
    +
    {Lang::T('Add Service Plan')}
    +
    +
    +
    + +
    + {Lang::T('Enable')} + {Lang::T('Disable')} +
    +
    +
    + +
    + {Lang::T('Prepaid')} + {Lang::T('Postpaid')} +
    +
    +
    + +
    + {Lang::T('Personal')} + {Lang::T('Business')} +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    + {$_c['currency_code']} + +
    +
    + {if $_c['enable_tax'] == 'yes'} + {if $_c['tax_rate'] == 'custom'} +

    {number_format($_c['custom_tax_rate'], 2)} % {Lang::T('Tax Rates + will be added')}

    + {else} +

    {number_format($_c['tax_rate'] * 100, 2)} % {Lang::T('Tax Rates + will be added')}

    + {/if} + {/if} +
    +
    + +
    + +
    +
    + +
    +

    {Lang::T('1 Period = 1 Month, Expires the 20th of each month')} +

    +
    + +
    + +
    + +

    {Lang::T('Cannot be change after saved')}

    +
    +
    +
    + +
    + +
    +
    +
    +
    + + Or {Lang::T('Cancel')} +
    +
    +
    +
    +
    +
    +
    + +{if $_c['radius_enable']} + {literal} + + {/literal} +{/if} +{include file="sections/footer.tpl"} diff --git a/ui/ui/vpn-edit.tpl b/ui/ui/vpn-edit.tpl new file mode 100644 index 00000000..53428605 --- /dev/null +++ b/ui/ui/vpn-edit.tpl @@ -0,0 +1,255 @@ +{include file="sections/header.tpl"} + +
    +
    +
    +
    +
    {Lang::T('Edit Service Plan')} || {$d['name_plan']}
    +
    + +
    + +
    + {Lang::T('Enable')} + {Lang::T('Disable')} +
    +
    +
    + +
    + + {Lang::T('Prepaid')} + {Lang::T('Postpaid')} +
    +
    +
    + +
    + + {Lang::T('Personal')} + {Lang::T('Business')} +
    +
    + {if $_c['radius_enable'] and $d['is_radius']} +
    + +
    + +
    +
    + {/if} +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    + {$_c['currency_code']} + +
    +
    + {if $_c['enable_tax'] == 'yes'} + {if $_c['tax_rate'] == 'custom'} +

    {number_format($_c['custom_tax_rate'], 2)} % {Lang::T('Tax Rates + will be added')}

    + {else} +

    {number_format($_c['tax_rate'] * 100, 2)} % {Lang::T('Tax Rates + will be added')}

    + {/if} + {/if} + +
    +
    + +
    + +
    +
    + +

    {Lang::T('1 Period = 1 Month, Expires the 20th of each month')}

    +
    + +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    + {Lang::T('Expired Action')} {Lang::T('Optional')} +
    + +
    + +

    + {Lang::T('When Expired, customer will be move to selected internet plan')}

    +
    +
    +
    +
    +
    + {if !$d['is_radius']} +
    +
    +
    {Lang::T('on-login / on-up')}
    +
    + +
    +
    +
    +
    {Lang::T('on-logout / on-down')}
    +
    + +
    +
    +
    + {/if} +
    +
    +
    + + Or {Lang::T('Cancel')} +
    +
    +
    + + + + + + + + + + + + + + +{include file="sections/footer.tpl"} diff --git a/ui/ui/vpn.tpl b/ui/ui/vpn.tpl new file mode 100644 index 00000000..6047e446 --- /dev/null +++ b/ui/ui/vpn.tpl @@ -0,0 +1,179 @@ +{include file="sections/header.tpl"} + +
    +
    +
    +
    +
    + sync +
    {Lang::T('VPN Package')} +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + {foreach $d as $ds} + + + + + + + + + + + + + + + {/foreach} + +
    {Lang::T('Internet Plan')} + {Lang::T('Expired')}
    {Lang::T('Name')}{Lang::T('Type')}{Lang::T('Bandwidth')}{Lang::T('Price')}{Lang::T('Validity')}{Lang::T('IP Pool')}{Lang::T('Internet Plan')}{Lang::T('Date')}{Lang::T('Location')}{Lang::T('Device')}{Lang::T('Manage')}ID
    {$ds['name_plan']}{$ds['plan_type']} {if $ds['prepaid'] != 'yes'}{Lang::T('Postpaid')}{else}{Lang::T('Prepaid')}{/if}{$ds['name_bw']}{Lang::moneyFormat($ds['price'])}{$ds['validity']} {$ds['validity_unit']}{$ds['pool']}{if $ds['plan_expired']}{Lang::T('Yes')}{else}{Lang::T('No')} + {/if}{if $ds['prepaid'] == no}{$ds['expired_date']}{/if} + {if $ds['is_radius']} + RADIUS + {else} + {if $ds['routers']!=''} + {$ds['routers']} + {/if} + {/if} + {$ds['device']} + {Lang::T('Edit')} + + {$ds['id']}
    +
    + +
    +
    +
    + +{include file="sections/footer.tpl"} diff --git a/version.json b/version.json index 1bd146b2..13f748ef 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "2024.9.6" + "version": "2024.9.13" } \ No newline at end of file