CleanTempFiles.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. <?php
  2. namespace App\Service\Cron\Job;
  3. use App\Repository\Access\AccessRepository;
  4. use App\Repository\Core\FileRepository;
  5. use App\Service\Cron\BaseCronJob;
  6. use App\Service\Cron\CronjobInterface;
  7. use App\Service\Storage\LocalStorage;
  8. use Doctrine\Common\Collections\Collection;
  9. use Doctrine\Common\Collections\Criteria;
  10. use Doctrine\ORM\EntityManagerInterface;
  11. use JetBrains\PhpStorm\Pure;
  12. /**
  13. * Cronjob that delete temporary files older than N days
  14. */
  15. class CleanTempFiles extends BaseCronJob implements CronjobInterface
  16. {
  17. /**
  18. * Delay before removing a temporary file (in days)
  19. */
  20. private const DELETE_OLDER_THAN = 7;
  21. /**
  22. * ID of the Access who author the file deletion
  23. */
  24. private const AUTHOR = 10984;
  25. /**
  26. * Delay before deleting the record of the temporary file from the DB
  27. */
  28. private const PURGE_RECORDS_OLDER_THAN = 365;
  29. /**
  30. * @param FileRepository $fileRepository
  31. * @param EntityManagerInterface $em
  32. * @param AccessRepository $accessRepository
  33. * @param LocalStorage $storage
  34. */
  35. #[Pure]
  36. public function __construct(
  37. private FileRepository $fileRepository,
  38. private EntityManagerInterface $em,
  39. private AccessRepository $accessRepository,
  40. private LocalStorage $storage
  41. ) {
  42. parent::__construct();
  43. }
  44. /**
  45. * Preview the result of the execution, without actually deleting anything
  46. */
  47. public function preview(): void
  48. {
  49. $files = $this->listFilesToDelete();
  50. $total = count($files);
  51. $this->ui->print($total . " temporary files to be removed");
  52. $this->ui->print("> Printing the first and last 50 :");
  53. $i = 0;
  54. foreach ($files as $file) {
  55. $i++;
  56. if ($i > 50 && ($total - $i) > 50) { continue; }
  57. if (($total - $i) === 50) {
  58. $this->ui->print(' (...)');
  59. }
  60. $this->ui->print(' * ' . $file->getPath());
  61. }
  62. $this->purgeDb(false);
  63. }
  64. /**
  65. * Proceed to the deletion of the files and the purge of the DB
  66. */
  67. public function execute(): void
  68. {
  69. $files = $this->listFilesToDelete();
  70. $this->deleteFiles($files);
  71. $this->purgeDb();
  72. }
  73. /**
  74. * List the files to delete in the DB
  75. *
  76. * @return Collection
  77. * @throws \Exception
  78. */
  79. private function listFilesToDelete(): Collection {
  80. $maxDate = new \DateTime(self::DELETE_OLDER_THAN . ' days ago');
  81. $this->ui->print('List temporary files created before ' . $maxDate->format('c'));
  82. $criteria = new Criteria();
  83. $criteria->where(
  84. Criteria::expr()?->andX(
  85. Criteria::expr()?->eq('isTemporaryFile', 1),
  86. Criteria::expr()?->orX(
  87. Criteria::expr()?->lt('createDate', $maxDate),
  88. Criteria::expr()?->isNull('createDate')
  89. )
  90. )
  91. );
  92. return $this->fileRepository->matching($criteria);
  93. }
  94. /**
  95. * Delete the files
  96. *
  97. * @param Collection $files
  98. */
  99. private function deleteFiles(Collection $files): void {
  100. $total = count($files);
  101. $this->ui->print($total . " temporary files to be removed");
  102. $author = $this->accessRepository->find(self::AUTHOR);
  103. if ($author === null) {
  104. throw new \RuntimeException('Access ' . self::AUTHOR . ' could not be found');
  105. }
  106. $this->ui->print('Deleting files...');
  107. $i = 0;
  108. $this->ui->progress(0, $total);
  109. foreach ($files as $file) {
  110. try {
  111. $i++;
  112. $this->ui->progress($i, $total);
  113. $this->storage->delete($file, $author);
  114. } catch (\RuntimeException $e) {
  115. $this->ui->print('ERROR : ' . $e->getMessage());
  116. }
  117. }
  118. $this->ui->print($i . ' files deleted');
  119. }
  120. /**
  121. * Purge the DB from temporary file records older than N days
  122. *
  123. * @param bool $commit
  124. * @throws \Exception
  125. */
  126. private function purgeDb(bool $commit = true): void {
  127. $maxDate = new \DateTime(self::PURGE_RECORDS_OLDER_THAN . ' days ago');
  128. $this->ui->print('Purge DB from records of files deleted before ' . $maxDate->format('c'));
  129. $this->em->beginTransaction();
  130. $queryBuilder = $this->em->createQueryBuilder();
  131. $q = $queryBuilder
  132. ->delete('File', 'f')
  133. ->where($queryBuilder->expr()->eq('f.isTemporaryFile', 1))
  134. ->andWhere($queryBuilder->expr()->lt('f.updateDate', ':maxDate'))
  135. ->setParameter('maxDate', $maxDate)
  136. ->getQuery();
  137. $purged = $q->getResult();
  138. if ($commit) {
  139. $this->em->commit();
  140. $this->ui->print('DB purged - ' . $purged . ' records permanently deleted');
  141. return;
  142. }
  143. $this->em->rollback();
  144. $this->ui->print('DB purged - ' . $purged . ' records would be permanently deleted');
  145. }
  146. }