diff --git a/system/controllers/home.php b/system/controllers/home.php
index a71d340e..20e1d1b6 100644
--- a/system/controllers/home.php
+++ b/system/controllers/home.php
@@ -317,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/devices/MikrotikVpn.php b/system/devices/MikrotikVpn.php
new file mode 100644
index 00000000..6edeed2c
--- /dev/null
+++ b/system/devices/MikrotikVpn.php
@@ -0,0 +1,504 @@
+ 'Mikrotik Vpn',
+ 'description' => 'To handle connection between PHPNuxBill with Mikrotik VPN',
+ 'author' => 'agstr',
+ 'url' => [
+ 'Github' => 'https://github.com/agstrxyz',
+ 'Telegram' => 'https://t.me/agstrxyz',
+ 'Youtube' => 'https://www.youtube.com/@agstrxyz',
+ 'Donate' => 'https://paypal.me/ibnux'
+ ]
+ ];
+ }
+
+ function add_customer($customer, $plan)
+ {
+ global $isChangePlan;
+ $mikrotik = $this->info($plan['routers']);
+ $client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
+ $cid = self::getIdByCustomer($customer, $client);
+ if (empty($cid)) {
+ $this->addVpnUser($client, $plan, $customer);
+ }else{
+ $setRequest = new RouterOS\Request('/ppp/secret/set');
+ $setRequest->setArgument('numbers', $cid);
+ if (!empty($customer['pppoe_password'])) {
+ $setRequest->setArgument('password', $customer['pppoe_password']);
+ } else {
+ $setRequest->setArgument('password', $customer['password']);
+ }
+ if (!empty($customer['pppoe_username'])) {
+ $setRequest->setArgument('name', $customer['pppoe_username']);
+ } else {
+ $setRequest->setArgument('name', $customer['username']);
+ }
+ 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'])));
+ $client->sendSync($setRequest);
+ if(isset($isChangePlan) && $isChangePlan){
+ $this->removeVpnActive($client, $customer['username']);
+ if (!empty($customer['pppoe_username'])) {
+ $this->removeVpnActive($client, $customer['pppoe_username']);
+ }
+ }
+ }
+ }
+
+ function remove_customer($customer, $plan)
+ {
+ $mikrotik = $this->info($plan['routers']);
+ $client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
+ if (!empty($plan['plan_expired'])) {
+ $p = ORM::for_table("tbl_plans")->find_one($plan['plan_expired']);
+ if($p){
+ $this->add_customer($customer, $p);
+ $this->removeVpnActive($client, $customer['username']);
+ if (!empty($customer['pppoe_username'])) {
+ $this->removeVpnActive($client, $customer['pppoe_username']);
+ }
+ return;
+ }
+ }
+ $this->removeVpnUser($client, $customer['username'], $customer['id']);
+ if (!empty($customer['pppoe_username'])) {
+ $this->removeVpnUser($client, $customer['pppoe_username'], $customer['id']);
+ }
+ $this->removeVpnActive($client, $customer['username']);
+ if (!empty($customer['pppoe_username'])) {
+ $this->removeVpnActive($client, $customer['pppoe_username']);
+ }
+ }
+
+ public function change_username($plan, $from, $to)
+ {
+ $mikrotik = $this->info($plan['routers']);
+ $client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
+ $printRequest = new RouterOS\Request('/ppp/secret/print');
+ $printRequest->setQuery(RouterOS\Query::where('name', $from));
+ $cid = $client->sendSync($printRequest)->getProperty('.id');
+ if (!empty($cid)) {
+ $setRequest = new RouterOS\Request('/ppp/secret/set');
+ $setRequest->setArgument('numbers', $cid);
+ $setRequest->setArgument('name', $to);
+ $client->sendSync($setRequest);
+ $this->removeVpnActive($client, $from);
+ }
+ }
+
+ function add_plan($plan)
+ {
+ $mikrotik = $this->info($plan['routers']);
+ $client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
+
+
+ $bw = ORM::for_table("tbl_bandwidth")->find_one($plan['id_bw']);
+ if ($bw['rate_down_unit'] == 'Kbps') {
+ $unitdown = 'K';
+ } else {
+ $unitdown = 'M';
+ }
+ if ($bw['rate_up_unit'] == 'Kbps') {
+ $unitup = 'K';
+ } else {
+ $unitup = 'M';
+ }
+ $rate = $bw['rate_up'] . $unitup . "/" . $bw['rate_down'] . $unitdown;
+ if(!empty(trim($bw['burst']))){
+ $rate .= ' '.$bw['burst'];
+ }
+ $pool = ORM::for_table("tbl_pool")->where("pool_name", $plan['pool'])->find_one();
+ $addRequest = new RouterOS\Request('/ppp/profile/add');
+ $client->sendSync(
+ $addRequest
+ ->setArgument('name', $plan['name_plan'])
+ ->setArgument('local-address', (!empty($pool['local_ip'])) ? $pool['local_ip']: $pool['pool_name'])
+ ->setArgument('remote-address', $pool['pool_name'])
+ ->setArgument('rate-limit', $rate)
+ );
+ }
+
+
+ function getIdByCustomer($customer, $client){
+ $printRequest = new RouterOS\Request('/ppp/secret/print');
+ $printRequest->setQuery(RouterOS\Query::where('name', $customer['username']));
+ $id = $client->sendSync($printRequest)->getProperty('.id');
+ if(empty($id)){
+ if (!empty($customer['pppoe_username'])) {
+ $printRequest = new RouterOS\Request('/ppp/secret/print');
+ $printRequest->setQuery(RouterOS\Query::where('name', $customer['pppoe_username']));
+ $id = $client->sendSync($printRequest)->getProperty('.id');
+ }
+ }
+ return $id;
+ }
+
+ function update_plan($old_name, $new_plan)
+ {
+ $mikrotik = $this->info($new_plan['routers']);
+ $client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
+
+ $printRequest = new RouterOS\Request(
+ '/ppp profile print .proplist=.id',
+ RouterOS\Query::where('name', $old_name['name_plan'])
+ );
+ $profileID = $client->sendSync($printRequest)->getProperty('.id');
+ if (empty($profileID)) {
+ $this->add_plan($new_plan);
+ } else {
+ $bw = ORM::for_table("tbl_bandwidth")->find_one($new_plan['id_bw']);
+ if ($bw['rate_down_unit'] == 'Kbps') {
+ $unitdown = 'K';
+ } else {
+ $unitdown = 'M';
+ }
+ if ($bw['rate_up_unit'] == 'Kbps') {
+ $unitup = 'K';
+ } else {
+ $unitup = 'M';
+ }
+ $rate = $bw['rate_up'] . $unitup . "/" . $bw['rate_down'] . $unitdown;
+ if(!empty(trim($bw['burst']))){
+ $rate .= ' '.$bw['burst'];
+ }
+ $pool = ORM::for_table("tbl_pool")->where("pool_name", $new_plan['pool'])->find_one();
+ $setRequest = new RouterOS\Request('/ppp/profile/set');
+ $client->sendSync(
+ $setRequest
+ ->setArgument('numbers', $profileID)
+ ->setArgument('local-address', (!empty($pool['local_ip'])) ? $pool['local_ip']: $pool['pool_name'])
+ ->setArgument('remote-address', $pool['pool_name'])
+ ->setArgument('rate-limit', $rate)
+ ->setArgument('on-up', $new_plan['on_login'])
+ ->setArgument('on-down', $new_plan['on_logout'])
+ );
+ }
+ }
+
+ function remove_plan($plan)
+ {
+ $mikrotik = $this->info($plan['routers']);
+ $client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
+ $printRequest = new RouterOS\Request(
+ '/ppp profile print .proplist=.id',
+ RouterOS\Query::where('name', $plan['name_plan'])
+ );
+ $profileID = $client->sendSync($printRequest)->getProperty('.id');
+
+ $removeRequest = new RouterOS\Request('/ppp/profile/remove');
+ $client->sendSync(
+ $removeRequest
+ ->setArgument('numbers', $profileID)
+ );
+ }
+
+ function add_pool($pool){
+ global $_app_stage;
+ if ($_app_stage == 'demo') {
+ return null;
+ }
+ $mikrotik = $this->info($pool['routers']);
+ $client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
+ $addRequest = new RouterOS\Request('/ip/pool/add');
+ $client->sendSync(
+ $addRequest
+ ->setArgument('name', $pool['pool_name'])
+ ->setArgument('ranges', $pool['range_ip'])
+ );
+ }
+
+ function update_pool($old_pool, $new_pool){
+ global $_app_stage;
+ if ($_app_stage == 'demo') {
+ return null;
+ }
+ $mikrotik = $this->info($new_pool['routers']);
+ $client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
+ $printRequest = new RouterOS\Request(
+ '/ip pool print .proplist=.id',
+ RouterOS\Query::where('name', $old_pool['pool_name'])
+ );
+ $poolID = $client->sendSync($printRequest)->getProperty('.id');
+ if (empty($poolID)) {
+ $this->add_pool($new_pool);
+ } else {
+ $setRequest = new RouterOS\Request('/ip/pool/set');
+ $client->sendSync(
+ $setRequest
+ ->setArgument('numbers', $poolID)
+ ->setArgument('name', $new_pool['pool_name'])
+ ->setArgument('ranges', $new_pool['range_ip'])
+ );
+ }
+ }
+
+ function remove_pool($pool){
+ global $_app_stage;
+ if ($_app_stage == 'demo') {
+ return null;
+ }
+ $mikrotik = $this->info($pool['routers']);
+ $client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
+ $printRequest = new RouterOS\Request(
+ '/ip pool print .proplist=.id',
+ RouterOS\Query::where('name', $pool['pool_name'])
+ );
+ $poolID = $client->sendSync($printRequest)->getProperty('.id');
+ $removeRequest = new RouterOS\Request('/ip/pool/remove');
+ $client->sendSync(
+ $removeRequest
+ ->setArgument('numbers', $poolID)
+ );
+ }
+
+
+ function online_customer($customer, $router_name)
+ {
+ $mikrotik = $this->info($router_name);
+ $client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
+ $printRequest = new RouterOS\Request(
+ '/ppp active print',
+ RouterOS\Query::where('user', $customer['username'])
+ );
+ return $client->sendSync($printRequest)->getProperty('.id');
+ }
+
+ function info($name)
+ {
+ return ORM::for_table('tbl_routers')->where('name', $name)->find_one();
+ }
+
+ function getClient($ip, $user, $pass)
+ {
+ global $_app_stage;
+ if ($_app_stage == 'demo') {
+ return null;
+ }
+ $iport = explode(":", $ip);
+ return new RouterOS\Client($iport[0], $user, $pass, ($iport[1]) ? $iport[1] : null);
+ }
+
+ function removeVpnUser($client, $username, $cstid)
+ {
+ global $_app_stage;
+ if ($_app_stage == 'demo') {
+ return null;
+ }
+ $printRequest = new RouterOS\Request('/ppp/secret/print');
+ //$printRequest->setArgument('.proplist', '.id');
+ $printRequest->setQuery(RouterOS\Query::where('name', $username));
+ $id = $client->sendSync($printRequest)->getProperty('.id');
+ $removeRequest = new RouterOS\Request('/ppp/secret/remove');
+ $removeRequest->setArgument('numbers', $id);
+ $client->sendSync($removeRequest);
+ $this->rmNat($client, $cstid);
+ }
+
+ function addVpnUser($client, $plan, $customer)
+ {
+ $setRequest = new RouterOS\Request('/ppp/secret/add');
+ $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'])) {
+ $setRequest->setArgument('password', $customer['pppoe_password']);
+ } else {
+ $setRequest->setArgument('password', $customer['password']);
+ }
+ if (!empty($customer['pppoe_username'])) {
+ $setRequest->setArgument('name', $customer['pppoe_username']);
+ } else {
+ $setRequest->setArgument('name', $customer['username']);
+ }
+ if (!empty($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 removeVpnActive($client, $username)
+ {
+ global $_app_stage;
+ if ($_app_stage == 'demo') {
+ return null;
+ }
+ $onlineRequest = new RouterOS\Request('/ppp/active/print');
+ $onlineRequest->setArgument('.proplist', '.id');
+ $onlineRequest->setQuery(RouterOS\Query::where('name', $username));
+ $id = $client->sendSync($onlineRequest)->getProperty('.id');
+
+ $removeRequest = new RouterOS\Request('/ppp/active/remove');
+ $removeRequest->setArgument('numbers', $id);
+ $client->sendSync($removeRequest);
+ }
+
+
+ function addIpToAddressList($client, $ip, $listName, $comment = '')
+ {
+ global $_app_stage;
+ if ($_app_stage == 'demo') {
+ return null;
+ }
+ $addRequest = new RouterOS\Request('/ip/firewall/address-list/add');
+ $client->sendSync(
+ $addRequest
+ ->setArgument('address', $ip)
+ ->setArgument('comment', $comment)
+ ->setArgument('list', $listName)
+ );
+
+ }
+
+ function removeIpFromAddressList($client, $ip)
+ {
+ global $_app_stage;
+ if ($_app_stage == 'demo') {
+ return null;
+ }
+ $printRequest = new RouterOS\Request(
+ '/ip firewall address-list print .proplist=.id',
+ RouterOS\Query::where('address', $ip)
+ );
+ $id = $client->sendSync($printRequest)->getProperty('.id');
+ $removeRequest = new RouterOS\Request('/ip/firewall/address-list/remove');
+ $client->sendSync(
+ $removeRequest
+ ->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();
+
+ 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('address', $ip)
+ ->setArgument('comment', $cmnt.' || '.$cust['username'])
+ );
+ }
+ }
+ }
+
+ function rmNat($client, $cstid)
+ {
+ global $_app_stage;
+ if ($_app_stage == 'demo') {
+ return null;
+ }
+ $tcf = ORM::for_table('tbl_customers_fields')
+ ->where('customer_id', $cstid)
+ ->find_many();
+ $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')}
+
diff --git a/ui/ui/plan.tpl b/ui/ui/plan.tpl
index 3cca5861..32111f4a 100644
--- a/ui/ui/plan.tpl
+++ b/ui/ui/plan.tpl
@@ -95,9 +95,13 @@
{/if}
{if $ds['type'] == 'Hotspot'}
- {$ds['namebp']} |
- {else}
- {$ds['namebp']} |
+ {$ds['namebp']} |
+ {/if}
+ {if $ds['type'] == 'PPPOE'}
+ {$ds['namebp']} |
+ {/if}
+ {if $ds['type'] == 'VPN'}
+ {$ds['namebp']} |
{/if}
{$ds['type']} |
{Lang::dateAndTimeFormat($ds['recharged_on'],$ds['recharged_time'])} |
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')}
+
+
+
+
+
+{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')}
+
+
+
+
+
+{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"}
+
+
+
+
+
+
+ {Lang::T('Port Pool')} - VPN Tunnels
+
+
+
+
+
+
+
+ {Lang::T('Port Name')} |
+ {Lang::T('Public IP')} |
+ {Lang::T('Range Port')} |
+ {Lang::T('Routers')} |
+ {Lang::T('Manage')} |
+ ID |
+
+
+
+ {foreach $d as $ds}
+
+ {$ds['port_name']} |
+ {$ds['public_ip']} |
+ {$ds['range_port']} |
+ {$ds['routers']} |
+
+ {Lang::T('Edit')}
+
+ |
+ {$ds['id']} |
+
+ {/foreach}
+
+
+
+ {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/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 @@
+
@@ -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/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'])}
@@ -338,9 +339,62 @@
{/if}
{/foreach}
+ {/if}
+ {if $_user['service_type'] == 'VPN' && Validator::countRouterPlan($plans_vpn,$router['name'])>0}
+
+
+ {foreach $plans_vpn as $plan}
+ {if $router['name'] eq $plan['routers']}
+
+
+
+
+
+
+
+
+ {Lang::T('Type')} |
+ {$plan['type']} |
+
+
+ {Lang::T('Price')} |
+ {Lang::moneyFormat($plan['price'])} |
+
+
+ {Lang::T('Validity')} |
+ {$plan['validity']} {$plan['validity_unit']} |
+
+
+
+
+
+
+
+ {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)}
@@ -443,6 +497,56 @@
{/if}
{/foreach}
+
+
+ {foreach $plans_vpn as $plan}
+ {if $router['name'] eq $plan['routers']}
+
+
+
+
+
+
+
+
+ {Lang::T('Type')} |
+ {$plan['type']} |
+
+
+ {Lang::T('Price')} |
+ {Lang::moneyFormat($plan['price'])} |
+
+
+ {Lang::T('Validity')} |
+ {$plan['validity']} {$plan['validity_unit']} |
+
+
+
+
+
+
+
+ {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/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')}
+
+
+
+
+
+{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"}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{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"}
+
+
+
+
+
+
{Lang::T('VPN Package')}
+
+
+
+
+
+
+ |
+ {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}
+
+ {$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']} |
+
+ {/foreach}
+
+
+
+
+
+
+
+
+{include file="sections/footer.tpl"}