src/ApplicationBundle/Modules/Api/Controller/ActivityLogController.php line 25

Open in your IDE?
  1. <?php
  2. namespace ApplicationBundle\Modules\Api\Controller;
  3. use ApplicationBundle\Modules\Authentication\Constants\UserConstants;
  4. use ApplicationBundle\Controller\GenericController;
  5. use ApplicationBundle\Modules\Api\Service\ActivityLogService;
  6. use InvalidArgumentException;
  7. use Symfony\Component\HttpFoundation\JsonResponse;
  8. use Symfony\Component\HttpFoundation\Request;
  9. class ActivityLogController extends GenericController
  10. {
  11.     public function logAction(Request $request): JsonResponse
  12.     {
  13.         $payload $this->extractPayload($request);
  14.         // Route the write to the LOGGED-IN TENANT's DB. SessionListener switches the
  15.         // default connection with reset=false, which only rewrites the params and does
  16.         // NOT move an already-open connection — so an /api write can land in the box
  17.         // DEFAULT database (every tenant's activity funneled there, its own Activity
  18.         // Dashboard read empty). Force a reconnect (reset=true) to the session tenant.
  19.         $this->switchToSessionTenantDb($request);
  20.         $service = new ActivityLogService($this->getDoctrine()->getManager());
  21.         try {
  22.             $activityLog $service->log($payload);
  23.         } catch (InvalidArgumentException $exception) {
  24.             return new JsonResponse([
  25.                 'success' => false,
  26.                 'code' => 'HB_VALIDATION_ERROR',
  27.                 'message' => 'Validation failed',
  28.                 'errors' => [$exception->getMessage()],
  29.             ], 400);
  30.         } catch (\Throwable $exception) {
  31.             return new JsonResponse([
  32.                 'success' => false,
  33.                 'code' => 'HB_INTERNAL_ERROR',
  34.                 'message' => 'Activity log could not be saved',
  35.             ], 500);
  36.         }
  37.         return new JsonResponse([
  38.             'success' => true,
  39.             'code' => 'HB_OK',
  40.             'message' => 'Activity logged successfully',
  41.             'data' => [
  42.                 'id' => $activityLog->getId(),
  43.                 'user_id' => $activityLog->getUserId(),
  44.                 'session_id' => $activityLog->getSessionId(),
  45.                 'route' => $activityLog->getRoute(),
  46.                 'action_type' => $activityLog->getActionType(),
  47.                 'duration_seconds' => $activityLog->getDurationSeconds(),
  48.                 'created_at' => $activityLog->getCreatedAt() ? $activityLog->getCreatedAt()->format('Y-m-d H:i:s') : null,
  49.             ],
  50.         ], 201);
  51.     }
  52.     /**
  53.      * Force-reconnect the default EM to the session tenant's DB (reset=true) so the
  54.      * activity row is written to the right tenant, not the box default. Best-effort:
  55.      * any failure leaves the existing connection and never breaks logging.
  56.      */
  57.     private function switchToSessionTenantDb(Request $request): void
  58.     {
  59.         try {
  60.             $appId = (int) $request->getSession()->get(UserConstants::USER_APP_ID);
  61.             if ($appId <= 0) {
  62.                 return;
  63.             }
  64.             $emGoc $this->getDoctrine()->getManager('company_group');
  65.             $goc $emGoc->getRepository('CompanyGroupBundle\\Entity\\CompanyGroup')
  66.                 ->findOneBy(['appId' => $appId]);
  67.             if (!$goc || !$goc->getDbName()) {
  68.                 return;
  69.             }
  70.             $this->container->get('application_connector')->resetConnection(
  71.                 'default',
  72.                 $goc->getDbName(),
  73.                 $goc->getDbUser(),
  74.                 $goc->getDbPass(),
  75.                 $goc->getDbHost(),
  76.                 true
  77.             );
  78.         } catch (\Throwable $e) {
  79.             // never break activity logging on the tenant-switch path
  80.         }
  81.     }
  82.     private function extractPayload(Request $request): array
  83.     {
  84.         $contentType strtolower((string) $request->headers->get('Content-Type'''));
  85.         $rawContent trim((string) $request->getContent());
  86.         if ($rawContent !== '' && strpos($contentType'application/json') !== false) {
  87.             $decoded json_decode($rawContenttrue);
  88.             return is_array($decoded) ? $decoded : [];
  89.         }
  90.         return $request->request->all();
  91.     }
  92. }