From de89a9e3b4f6fd10e2d821c793fc10b0e98e3e39 Mon Sep 17 00:00:00 2001 From: Pinga <121483313+getpinga@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:59:10 +0300 Subject: [PATCH] Added ability to manage custom registrar pricing from panel; fixed #163 --- cp/app/Controllers/RegistrarsController.php | 116 ++++++- cp/bootstrap/app.php | 3 + cp/bootstrap/helper.php | 5 + .../views/admin/registrars/customPricing.twig | 284 ++++++++++++------ cp/routes/web.php | 3 +- 5 files changed, 314 insertions(+), 97 deletions(-) diff --git a/cp/app/Controllers/RegistrarsController.php b/cp/app/Controllers/RegistrarsController.php index 6cf7496..ea5c184 100644 --- a/cp/app/Controllers/RegistrarsController.php +++ b/cp/app/Controllers/RegistrarsController.php @@ -1245,24 +1245,16 @@ class RegistrarsController extends Controller } } - public function updateCustomPricing(Request $request, Response $response, $args) + public function customPricingView(Request $request, Response $response, $args) { if ($_SESSION["auth_roles"] != 0) { return $response->withHeader('Location', '/dashboard')->withStatus(302); } - //TODO - if ($request->getMethod() === 'POST') { - // Retrieve POST data - $data = $request->getParsedBody(); - $db = $this->container->get('db'); - - var_dump ($data);die(); - } - + $db = $this->container->get('db'); // Get the current URI $uri = $request->getUri()->getPath(); - + if ($args) { $args = trim($args); @@ -1300,6 +1292,108 @@ class RegistrarsController extends Controller } } + public function updateCustomPricing(Request $request, Response $response, $args) + { + if ($_SESSION["auth_roles"] != 0) { + return $response->withHeader('Location', '/dashboard')->withStatus(302); + } + + if (!$args) { + return $response->withHeader('Location', '/registrars')->withStatus(302); + } + + $method = $request->getMethod(); + $body = $request->getBody()->__toString(); + $data = json_decode($body, true); + $db = $this->container->get('db'); + $clid = getClid($db, $args); + + $tld = $data['tld'] ?? null; + $action = $data['action'] ?? null; + + if (!$tld || !$action || !in_array($action, ['create', 'renew', 'transfer', 'restore'])) { + $response->getBody()->write(json_encode(['error' => 'Invalid input'])); + return $response->withHeader('Content-Type', 'application/json')->withStatus(400); + } + + $tldId = $db->selectValue('SELECT id FROM domain_tld WHERE tld = ?', [$tld]); + if (!$tldId) { + $response->getBody()->write(json_encode(['error' => 'TLD not found'])); + return $response->withHeader('Content-Type', 'application/json')->withStatus(404); + } + + if ($method === 'POST') { + $prices = $data['prices'] ?? []; + + if ($action === 'restore') { + $price = $prices['restore'] ?? null; + if ($price === null) { + $response->getBody()->write(json_encode(['error' => 'Missing restore price'])); + return $response->withHeader('Content-Type', 'application/json')->withStatus(400); + } + + $db->exec(' + INSERT INTO domain_restore_price (tldid, registrar_id, price) + VALUES (?, ?, ?) + ON DUPLICATE KEY UPDATE price = VALUES(price) + ', [$tldId, $clid, $price]); + + } else { + $columns = []; + foreach ($prices as $key => $val) { + if (preg_match('/^y(\d{1,2})$/', $key, $matches)) { + $year = (int)$matches[1]; + $months = $year * 12; + $col = 'm' . $months; + $columns[$col] = $val; + } + } + + if (!empty($columns)) { + $columns['tldid'] = $tldId; + $columns['registrar_id'] = $clid; + $columns['command'] = $action; + + $colNames = array_keys($columns); + $placeholders = array_fill(0, count($columns), '?'); + $values = array_values($columns); + + $updateClause = implode(', ', array_map(function ($col) { + return "$col = VALUES($col)"; + }, $colNames)); + + $sql = 'INSERT INTO domain_price (' . implode(', ', $colNames) . ') + VALUES (' . implode(', ', $placeholders) . ') + ON DUPLICATE KEY UPDATE ' . $updateClause; + + $db->exec($sql, $values); + } + } + + $response->getBody()->write(json_encode(['success' => true])); + return $response->withHeader('Content-Type', 'application/json')->withStatus(200); + } + + elseif ($method === 'DELETE') { + if ($action === 'restore') { + $db->delete('domain_restore_price', [ + 'tldid' => $tldId, + 'registrar_id' => $clid + ]); + } else { + $db->exec('DELETE FROM domain_price WHERE tldid = ? AND registrar_id = ? AND command = ?', [ + $tldId, $clid, $action + ]); + } + + $response->getBody()->write(json_encode(['success' => true])); + return $response->withHeader('Content-Type', 'application/json')->withStatus(200); + } + + $response->getBody()->write(json_encode(['error' => 'Method not allowed'])); + return $response->withHeader('Content-Type', 'application/json')->withStatus(405); + } + public function transferRegistrar(Request $request, Response $response) { if ($_SESSION["auth_roles"] != 0) { diff --git a/cp/bootstrap/app.php b/cp/bootstrap/app.php index 021b0fc..ea1d936 100644 --- a/cp/bootstrap/app.php +++ b/cp/bootstrap/app.php @@ -304,6 +304,9 @@ $csrfMiddleware = function ($request, $handler) use ($container) { if ($path && $path === '/clear-cache') { return $handler->handle($request); } + if (str_starts_with($path, '/registrar/updatepricing/')) { + return $handler->handle($request); + } if ($path && $path === '/token-well') { $csrf->generateToken(); return $handler->handle($request); diff --git a/cp/bootstrap/helper.php b/cp/bootstrap/helper.php index ad28100..be1c655 100644 --- a/cp/bootstrap/helper.php +++ b/cp/bootstrap/helper.php @@ -1154,4 +1154,9 @@ function isValidHostname($hostname) { function sign($ts, $method, $path, $body, $secret_key) { $stringToSign = $ts . strtoupper($method) . $path . $body; return hash_hmac('sha256', $stringToSign, $secret_key); +} + +function getClid($db, string $clid): ?int { + $result = $db->selectValue('SELECT id FROM registrar WHERE clid = ? LIMIT 1', [$clid]); + return $result !== false ? (int)$result : null; } \ No newline at end of file diff --git a/cp/resources/views/admin/registrars/customPricing.twig b/cp/resources/views/admin/registrars/customPricing.twig index ece320f..3cd3345 100644 --- a/cp/resources/views/admin/registrars/customPricing.twig +++ b/cp/resources/views/admin/registrars/customPricing.twig @@ -25,107 +25,221 @@
{% include 'partials/flash.twig' %} -
- {{ csrf.field | raw }}

{{ __('Registrar') }} {{ name }}

-
- -
-
- +
- - - - - - - + + + + + + + - - {% for tld in tlds %} - - - {% if tld.createPrices or tld.renewPrices or tld.transferPrices or tld.tld_restore %} - - - - - {% else %} - - {% endif %} - + + {% for tld in tlds %} + + + + {% for action in ['create', 'renew', 'transfer'] %} + {% endfor %} - + + + + {% endfor %} +
{{ __('TLD') }} / {{ __('Command') }}{{ __('Create') }}{{ __('Renew') }}{{ __('Transfer') }}{{ __('Restore Price') }}
{{ __('TLD') }}{{ __('Create') }}{{ __('Renew') }}{{ __('Transfer') }}{{ __('Restore Price') }}
{{ tld.tld }} - {% if tld.createPrices %} - {% for year in [12, 24, 36, 48, 60, 72, 84, 96, 108, 120] %} -
-
- -
-
- -
-
- {% endfor %} - {% else %} - N/A - {% endif %} -
- {% if tld.renewPrices %} - {% for year in [12, 24, 36, 48, 60, 72, 84, 96, 108, 120] %} -
-
- -
-
- -
-
- {% endfor %} - {% else %} - N/A - {% endif %} -
- {% if tld.transferPrices %} - {% for year in [12, 24, 36, 48, 60, 72, 84, 96, 108, 120] %} -
-
- -
-
- -
-
- {% endfor %} - {% else %} - N/A - {% endif %} -
- {% if tld.tld_restore %} - - {% else %} - N/A - {% endif %} - {{ __('Registrar does not have custom prices for') }} {{ tld.tld }}
{{ tld.tld }} + + {% set prices = attribute(tld, action ~ 'Prices') %} + {% if prices %} +
+
+ {% for year in 1..10 %} + {% set key = 'm' ~ (year * 12) %} + {% if attribute(prices, key) is defined %} + {{ year }}y: ${{ attribute(prices, key) }}{% if not loop.last %}, {% endif %} + {% endif %} + {% endfor %} +
+
+ + +
+
+ {% else %} + {{ __('No custom price') }}
+ + {% endif %} +
+ {% if tld.tld_restore %} +
${{ tld.tld_restore.price }}
+
+ + +
+ {% else %} + {{ __('No custom price') }}
+ + {% endif %} +
-
{% include 'partials/footer.twig' %} + {% endblock %} \ No newline at end of file diff --git a/cp/routes/web.php b/cp/routes/web.php index 66da832..e26d024 100644 --- a/cp/routes/web.php +++ b/cp/routes/web.php @@ -94,7 +94,8 @@ $app->group('', function ($route) { $route->map(['GET', 'POST'], '/registrar/create', RegistrarsController::class . ':create')->setName('registrarcreate'); $route->get('/registrar/view/{registrar}', RegistrarsController::class . ':viewRegistrar')->setName('viewRegistrar'); $route->get('/registrar/update/{registrar}', RegistrarsController::class . ':updateRegistrar')->setName('updateRegistrar'); - $route->map(['GET', 'POST'], '/registrar/pricing/{registrar}', RegistrarsController::class . ':updateCustomPricing')->setName('updateCustomPricing'); + $route->get('/registrar/pricing/{registrar}', RegistrarsController::class . ':customPricingView')->setName('customPricingView'); + $route->map(['POST', 'DELETE'], '/registrar/updatepricing/{registrar}', RegistrarsController::class . ':updateCustomPricing')->setName('updateCustomPricing'); $route->post('/registrar/update', RegistrarsController::class . ':updateRegistrarProcess')->setName('updateRegistrarProcess'); $route->get('/registrar', RegistrarsController::class .':registrar')->setName('registrar'); $route->map(['GET', 'POST'], '/registrar/edit', RegistrarsController::class .':editRegistrar')->setName('editRegistrar');