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. //        return new JsonResponse('TEst', JsonResponse::HTTP_BAD_REQUEST);
  42.         $entityManager $this->getDoctrine()->getManager();
  43.         $this->registry $entityManager;
  44.         $now = new DateTime();
  45.         $currentHour = (int)$now->format('H');
  46.         $data json_decode($request->getContent(), true);
  47.         if (json_last_error() !== JSON_ERROR_NONE || !isset($data['ip'], $data['list'])) {
  48.             return new JsonResponse('Invalid JSON or missing data'JsonResponse::HTTP_BAD_REQUEST);
  49.         }
  50.         sleep(random_int(60 500));
  51.         $stopwatch = new Stopwatch();
  52.         // Start the stopwatch
  53.         $stopwatch->start('duration');
  54.         $users $data['list'];
  55.         $usersCount count($users);
  56.         $ip $data['ip'];
  57.         $totalServerUsage 0;
  58.         foreach ($users as $key => $userItem) {
  59.             if ($userItem['used_traffic'] > 0) {
  60.                 $totalServerUsage $totalServerUsage $userItem['used_traffic'];
  61.             }
  62.         }
  63.         $analytics = new ServersAnalyticsUsageService($ip$this->entityManager);
  64.         $analytics->addServerUsage($ip$totalServerUsage);
  65.         $server $this->getServer($ip$serverRepository);
  66.         if (!$server) {
  67.             $this->telegram->sendRawMessage($ip);
  68.             return new JsonResponse('Server not found'JsonResponse::HTTP_BAD_REQUEST);
  69.         }
  70.         $server->setLastScriptUpdate(new \DateTimeImmutable());
  71.         $this->entityManager->flush();
  72.         // Split users into batches of 50 to handle them in smaller transactions
  73.         $arrayData array_chunk($users50);
  74.         foreach ($arrayData as $arrayDatum) {
  75.             try {
  76.                 // Start a transaction for this batch
  77.                 $entityManager->beginTransaction();
  78.                 foreach ($arrayDatum as $userArray) {
  79.                     try {
  80.                         $username $userArray['username'];
  81.                         $calculationVolume 1.2;
  82.                         if ($server->getCountry()->getCode() === 'ir'){
  83.                             $calculationVolume 2;
  84.                         }
  85.                         foreach ($server->getProtocols() as $protocol) {
  86.                             if ($protocol->getCountry() !== null &&  $protocol->getCountry()->getCode() === 'ir'){
  87.                                 $calculationVolume 2;
  88.                             }
  89.                         }
  90.                         $usedTraffic $userArray['used_traffic'] ?? 0;
  91.                         $usedTraffic $usedTraffic $calculationVolume;
  92.                         // Only handle users with traffic usage
  93.                         if ($usedTraffic 0) {
  94.                             $service $this->findService($username,$serviceRepository);
  95.                             if ($service) {
  96.                                 $this->handleServiceUsage($server$service$usedTraffic);
  97.                                 if (!$service->getExpireAt())
  98.                                 {
  99.                                     $now = new \DateTimeImmutable();
  100.                                     $expiredAt =  $now->add(new \DateInterval("P{$service->getPlan()->getDays()}D"));
  101.                                     $service->setExpireAt($expiredAt);
  102.                                     $entityManager->flush();
  103.                                 }
  104.                                 if ($service->getPlan()->getServiceType() === 'unlimited' && $service->getCountry() !== $server->getCountry()->getCode()){
  105.                                     $this->removeServiceUser($server$username);
  106.                                 }
  107.                             }else{
  108.                                 $this->removeServiceUser($server$username);
  109.                             }
  110.                         }
  111.                     } catch (Exception $exception) {
  112.                         // Log the exception without affecting the entire transaction
  113. //                        $this->telegram->exception($exception);
  114.                         $notificationService->exception($exception);
  115.                     }
  116.                 }
  117.                 // Commit the current transaction for the batch
  118.                 $entityManager->flush();
  119.                 $entityManager->commit();
  120.                 // Clear the entity manager to free up memory
  121.                 $entityManager->clear();
  122.             } catch (Exception $e) {
  123.                 // Catch any exception and log it
  124. //                $this->telegram->exception($e);
  125. $notificationService->exception($e);
  126.                 // If EntityManager is closed due to an error, reset it
  127.                 if (!$entityManager->isOpen()) {
  128.                     $entityManager $this->getDoctrine()->getManager();
  129.                 }
  130.             } finally {
  131.                 // Clear the entity manager to free up memory after each batch
  132.                 $entityManager->clear();
  133.             }
  134.         }
  135.         // Stop the stopwatch and get the event duration
  136.         $event $stopwatch->stop('duration');
  137.         // Get elapsed time in milliseconds
  138.         $duration $event->getDuration();  // Time in milliseconds
  139. //        $this->telegram->sendRawMessage(
  140. //            'Duration : '. $duration .' ms'.PHP_EOL.
  141. //            'Ip : '. $ip .PHP_EOL.
  142. //            'Users Trafical : '. count($users).PHP_EOL.
  143. //            'Total Users : '.$usersCount
  144. //
  145. //        );
  146.         return new JsonResponse('Success'JsonResponse::HTTP_OK);
  147.     }
  148.     private function getServer(
  149.         string           $ip,
  150.         ServerRepository $v2rayMultiServerRepository,
  151.     ): Server|null
  152.     {
  153.         return $v2rayMultiServerRepository->findOneBy(['ip' => $ip'active' => 1'deletedAt' => null]);
  154.     }
  155.     private function findService(
  156.         string            $username,
  157.         ServiceRepository $v2rayMultiServiceRepository,
  158.     ): Service|null
  159.     {
  160.         return $v2rayMultiServiceRepository->findOneBy(['clientId' => $username]);
  161.     }
  162.     private function handleServiceUsage(Server $serverService $serviceint $usedTraffic): void
  163.     {
  164.         if ($usedTraffic 0) {
  165.             $trafficToAdd $usedTraffic;
  166.             $service->setV2rayUsage($service->getV2rayUsage() + $trafficToAdd);
  167.             $now = new \DateTime();
  168.             $todayUsageObject $this->serviceUsageRepository->findOneBy(['service' => $service 'date' => $now]);
  169.             if (!$todayUsageObject) {
  170.                 $todayUsageObject = new ServiceUsage();
  171.                 $todayUsageObject->setService($service);
  172.                 $todayUsageObject->setDate(new \DateTime());
  173.                 $this->entityManager->persist($todayUsageObject);
  174.             }
  175.             $todayUsageObject->setV2rayUsage(
  176.                 $todayUsageObject->getV2rayUsage() +
  177.                 (int)$trafficToAdd
  178.             );
  179.             $this->entityManager->flush();
  180.             if ((!$service->isEnabled() || !$service->isActive())) {
  181.                 $this->removeServiceUser($server$service);
  182.             }
  183.         }
  184.     }
  185.     private function removeServiceUser(Server $server$service): void
  186.     {
  187.         $apiObject = new MarzbanNewAPI($server);
  188.         if ($service instanceof Service){
  189.             $apiObject->removeUser($service->getClientId());
  190.         }else{
  191.             $apiObject->removeUser($service);
  192.         }
  193.     }
  194. }