CleanTempFiles.php 5.2 KB

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