src/Controller/Client/MarzbanController.php line 41

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Client;
  3. use App\Entity\VPN\Service\Service;
  4. use App\Entity\VPN\Service\ServiceUsage;
  5. use App\Entity\VPN\V2ray\Server;
  6. use App\Repository\VPN\Service\ServiceRepository;
  7. use App\Repository\VPN\Service\ServiceUsageRepository;
  8. use App\Repository\VPN\V2ray\ServerRepository;
  9. use App\Service\Util\MarzbanNewAPI;
  10. use App\Service\Util\NotificationService;
  11. use App\Service\Util\ServersAnalyticsUsageService;
  12. use App\Service\Util\Telegram;
  13. use DateTime;
  14. use Doctrine\ORM\EntityManagerInterface;
  15. use Exception;
  16. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  17. use Symfony\Component\HttpFoundation\JsonResponse;
  18. use Symfony\Component\HttpFoundation\Request;
  19. use Symfony\Component\Routing\Annotation\Route;
  20. use Symfony\Component\Stopwatch\Stopwatch;
  21. class MarzbanController extends AbstractController
  22. {
  23.     public $registry;
  24.     private Telegram $telegram;
  25.     public EntityManagerInterface $entityManager;
  26.     public ServiceUsageRepository $serviceUsageRepository;
  27.     public function __construct(Telegram $telegramEntityManagerInterface $entityManager ServiceUsageRepository $serviceUsageRepository)
  28.     {
  29.         $this->telegram $telegram;
  30.         $this->serviceUsageRepository $serviceUsageRepository;
  31.         $this->entityManager $entityManager;
  32.     }
  33.     #[Route('/client/marzban'name'app_client_marzban')]
  34.     public function index(
  35.         Request           $request,
  36.         ServiceRepository $serviceRepository,
  37.         ServerRepository $serverRepository,
  38.         NotificationService $notificationService
  39.     ): JsonResponse
  40.     {
  41.         $entityManager $this->getDoctrine()->getManager();
  42.         $this->registry $entityManager;
  43.         $now = new DateTime();
  44.         $currentHour = (int)$now->format('H');
  45.         $data json_decode($request->getContent(), true);
  46.         if (json_last_error() !== JSON_ERROR_NONE || !isset($data['ip'], $data['list'])) {
  47.             return new JsonResponse('Invalid JSON or missing data'JsonResponse::HTTP_BAD_REQUEST);
  48.         }
  49.         sleep(random_int(60 300));
  50.         $stopwatch = new Stopwatch();
  51.         // Start the stopwatch
  52.         $stopwatch->start('duration');
  53.         $users $data['list'];
  54.         $usersCount count($users);
  55.         $ip $data['ip'];
  56.         $totalServerUsage 0;
  57.         foreach ($users as $key => $userItem) {
  58.             if ($userItem['used_traffic'] > 0) {
  59.                 $totalServerUsage $totalServerUsage $userItem['used_traffic'];
  60.             }
  61.         }
  62.         $analytics = new ServersAnalyticsUsageService($ip$this->entityManager);
  63.         $analytics->addServerUsage($ip$totalServerUsage);
  64.         $server $this->getServer($ip$serverRepository);
  65.         if (!$server) {
  66.             $this->telegram->sendRawMessage($ip);
  67.             return new JsonResponse('Server not found'JsonResponse::HTTP_BAD_REQUEST);
  68.         }
  69.         $server->setLastScriptUpdate(new \DateTimeImmutable());
  70.         $this->entityManager->flush();
  71.         // Split users into batches of 50 to handle them in smaller transactions
  72.         $arrayData array_chunk($users50);
  73.         foreach ($arrayData as $arrayDatum) {
  74.             try {
  75.                 // Start a transaction for this batch
  76.                 $entityManager->beginTransaction();
  77.                 foreach ($arrayDatum as $userArray) {
  78.                     try {
  79.                         $username $userArray['username'];
  80.                         $usedTraffic $userArray['used_traffic'] ?? 0;
  81.                         // Only handle users with traffic usage
  82.                         if ($usedTraffic 0) {
  83.                             $service $this->findService($username,$serviceRepository);
  84.                             if ($service) {
  85.                                 $this->handleServiceUsage($server$service$usedTraffic);
  86.                                 if (!$service->getExpireAt())
  87.                                 {
  88.                                     $now = new \DateTimeImmutable();
  89.                                     $expiredAt =  $now->add(new \DateInterval("P{$service->getPlan()->getDays()}D"));
  90.                                     $service->setExpireAt($expiredAt);
  91.                                     $entityManager->flush();
  92.                                 }
  93.                                 if ($service->getPlan()->getServiceType() === 'unlimited' && $service->getCountry() !== $server->getCountry()->getCode()){
  94.                                     $this->removeServiceUser($server$username);
  95.                                 }
  96.                             }else{
  97.                                 $this->removeServiceUser($server$username);
  98.                             }
  99.                         }
  100.                     } catch (Exception $exception) {
  101.                         // Log the exception without affecting the entire transaction
  102. //                        $this->telegram->exception($exception);
  103.                         $notificationService->exception($exception);
  104.                     }
  105.                 }
  106.                 // Commit the current transaction for the batch
  107.                 $entityManager->flush();
  108.                 $entityManager->commit();
  109.                 // Clear the entity manager to free up memory
  110.                 $entityManager->clear();
  111.             } catch (Exception $e) {
  112.                 // Catch any exception and log it
  113. //                $this->telegram->exception($e);
  114. $notificationService->exception($e);
  115.                 // If EntityManager is closed due to an error, reset it
  116.                 if (!$entityManager->isOpen()) {
  117.                     $entityManager $this->getDoctrine()->getManager();
  118.                 }
  119.             } finally {
  120.                 // Clear the entity manager to free up memory after each batch
  121.                 $entityManager->clear();
  122.             }
  123.         }
  124.         // Stop the stopwatch and get the event duration
  125.         $event $stopwatch->stop('duration');
  126.         // Get elapsed time in milliseconds
  127.         $duration $event->getDuration();  // Time in milliseconds
  128. //        $this->telegram->sendRawMessage(
  129. //            'Duration : '. $duration .' ms'.PHP_EOL.
  130. //            'Ip : '. $ip .PHP_EOL.
  131. //            'Users Trafical : '. count($users).PHP_EOL.
  132. //            'Total Users : '.$usersCount
  133. //
  134. //        );
  135.         return new JsonResponse('Success'JsonResponse::HTTP_OK);
  136.     }
  137.     private function getServer(
  138.         string           $ip,
  139.         ServerRepository $v2rayMultiServerRepository,
  140.     ): Server|null
  141.     {
  142.         return $v2rayMultiServerRepository->findOneBy(['ip' => $ip'active' => 1'deletedAt' => null]);
  143.     }
  144.     private function findService(
  145.         string            $username,
  146.         ServiceRepository $v2rayMultiServiceRepository,
  147.     ): Service|null
  148.     {
  149.         return $v2rayMultiServiceRepository->findOneBy(['clientId' => $username]);
  150.     }
  151.     private function handleServiceUsage(Server $serverService $serviceint $usedTraffic): void
  152.     {
  153.         if ($usedTraffic 0) {
  154.             $trafficToAdd $usedTraffic;
  155.             $service->setV2rayUsage($service->getV2rayUsage() + $trafficToAdd);
  156.             $now = new \DateTime();
  157.             $todayUsageObject $this->serviceUsageRepository->findOneBy(['service' => $service 'date' => $now]);
  158.             if (!$todayUsageObject) {
  159.                 $todayUsageObject = new ServiceUsage();
  160.                 $todayUsageObject->setService($service);
  161.                 $todayUsageObject->setDate(new \DateTime());
  162.                 $this->entityManager->persist($todayUsageObject);
  163.             }
  164.             $todayUsageObject->setV2rayUsage(
  165.                 $todayUsageObject->getV2rayUsage() +
  166.                 (int)$trafficToAdd
  167.             );
  168.             $this->entityManager->flush();
  169.             if ((!$service->isEnabled() || !$service->isActive())) {
  170.                 $this->removeServiceUser($server$service);
  171.             }
  172.         }
  173.     }
  174.     private function removeServiceUser(Server $server$service): void
  175.     {
  176.         $apiObject = new MarzbanNewAPI($server);
  177.         if ($service instanceof Service){
  178.             $apiObject->removeUser($service->getClientId());
  179.         }else{
  180.             $apiObject->removeUser($service);
  181.         }
  182.     }
  183. }