Skip to content

Commit fee65e5

Browse files
committed
feat(preview): Expire previews
Signed-off-by: provokateurin <kate@provokateurin.de>
1 parent b41d19a commit fee65e5

File tree

7 files changed

+78
-3
lines changed

7 files changed

+78
-3
lines changed

config/config.sample.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2775,4 +2775,12 @@
27752775
* Defaults to ``true``
27762776
*/
27772777
'enable_lazy_objects' => true,
2778+
2779+
/**
2780+
* Delete previews older than a certain number of days to reduce storage usage.
2781+
* Less than one day is not allowed, so set it to 0 to disable the deletion.
2782+
*
2783+
* Defaults to ``0``.
2784+
*/
2785+
'preview_expiration_days' => 0,
27782786
];
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OC\Core\BackgroundJobs;
11+
12+
use OC\Preview\PreviewService;
13+
use OCP\AppFramework\Utility\ITimeFactory;
14+
use OCP\BackgroundJob\IJob;
15+
use OCP\BackgroundJob\TimedJob;
16+
use OCP\IConfig;
17+
18+
class ExpirePreviewsJob extends TimedJob {
19+
public function __construct(
20+
ITimeFactory $time,
21+
private readonly IConfig $config,
22+
private readonly PreviewService $service,
23+
) {
24+
parent::__construct($time);
25+
26+
$this->setTimeSensitivity(IJob::TIME_INSENSITIVE);
27+
$this->setInterval(60 * 60 * 24);
28+
}
29+
30+
protected function run($argument): void {
31+
$days = $this->config->getSystemValueInt('preview_expiration_days');
32+
if ($days <= 0) {
33+
return;
34+
}
35+
36+
$this->service->deleteExpiredPreviews($days);
37+
}
38+
}

lib/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,7 @@
12481248
'OC\\Core\\BackgroundJobs\\BackgroundCleanupUpdaterBackupsJob' => $baseDir . '/core/BackgroundJobs/BackgroundCleanupUpdaterBackupsJob.php',
12491249
'OC\\Core\\BackgroundJobs\\CheckForUserCertificates' => $baseDir . '/core/BackgroundJobs/CheckForUserCertificates.php',
12501250
'OC\\Core\\BackgroundJobs\\CleanupLoginFlowV2' => $baseDir . '/core/BackgroundJobs/CleanupLoginFlowV2.php',
1251+
'OC\\Core\\BackgroundJobs\\ExpirePreviewsJob' => $baseDir . '/core/BackgroundJobs/ExpirePreviewsJob.php',
12511252
'OC\\Core\\BackgroundJobs\\GenerateMetadataJob' => $baseDir . '/core/BackgroundJobs/GenerateMetadataJob.php',
12521253
'OC\\Core\\BackgroundJobs\\LookupServerSendCheckBackgroundJob' => $baseDir . '/core/BackgroundJobs/LookupServerSendCheckBackgroundJob.php',
12531254
'OC\\Core\\BackgroundJobs\\MovePreviewJob' => $baseDir . '/core/BackgroundJobs/MovePreviewJob.php',

lib/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,6 +1289,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
12891289
'OC\\Core\\BackgroundJobs\\BackgroundCleanupUpdaterBackupsJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/BackgroundCleanupUpdaterBackupsJob.php',
12901290
'OC\\Core\\BackgroundJobs\\CheckForUserCertificates' => __DIR__ . '/../../..' . '/core/BackgroundJobs/CheckForUserCertificates.php',
12911291
'OC\\Core\\BackgroundJobs\\CleanupLoginFlowV2' => __DIR__ . '/../../..' . '/core/BackgroundJobs/CleanupLoginFlowV2.php',
1292+
'OC\\Core\\BackgroundJobs\\ExpirePreviewsJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/ExpirePreviewsJob.php',
12921293
'OC\\Core\\BackgroundJobs\\GenerateMetadataJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/GenerateMetadataJob.php',
12931294
'OC\\Core\\BackgroundJobs\\LookupServerSendCheckBackgroundJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/LookupServerSendCheckBackgroundJob.php',
12941295
'OC\\Core\\BackgroundJobs\\MovePreviewJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/MovePreviewJob.php',

lib/private/Preview/Db/PreviewMapper.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
namespace OC\Preview\Db;
1111

12+
use DateInterval;
13+
use DateTimeImmutable;
1214
use OCP\AppFramework\Db\Entity;
1315
use OCP\AppFramework\Db\QBMapper;
1416
use OCP\DB\Exception;
@@ -25,6 +27,7 @@ class PreviewMapper extends QBMapper {
2527
private const TABLE_NAME = 'previews';
2628
private const LOCATION_TABLE_NAME = 'preview_locations';
2729
private const VERSION_TABLE_NAME = 'preview_versions';
30+
public const MAX_CHUNK_SIZE = 1000;
2831

2932
public function __construct(
3033
IDBConnection $db,
@@ -171,11 +174,16 @@ public function getLocationId(string $bucket, string $objectStore): int {
171174
/**
172175
* @return \Generator<Preview>
173176
*/
174-
public function getPreviews(int $lastId, int $limit = 1000): \Generator {
177+
public function getPreviews(int $lastId, int $limit = self::MAX_CHUNK_SIZE, ?int $maxAgeDays = null): \Generator {
175178
$qb = $this->db->getQueryBuilder();
176179
$this->joinLocation($qb)
177180
->where($qb->expr()->gt('p.id', $qb->createNamedParameter($lastId, IQueryBuilder::PARAM_INT)))
178181
->setMaxResults($limit);
182+
183+
if ($maxAgeDays !== null) {
184+
$qb->andWhere($qb->expr()->lt('mtime', $qb->createNamedParameter((new DateTimeImmutable())->sub(new DateInterval('P' . $maxAgeDays . 'D'))->getTimestamp(), IQueryBuilder::PARAM_INT)));
185+
}
186+
179187
return $this->yieldEntities($qb);
180188

181189
}

lib/private/Preview/PreviewService.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,15 @@ public function getPreviewsForMimeTypes(array $mimeTypes): \Generator {
8585
public function deleteAll(): void {
8686
$lastId = 0;
8787
while (true) {
88-
$previews = $this->previewMapper->getPreviews($lastId, 1000);
88+
$previews = $this->previewMapper->getPreviews($lastId, PreviewMapper::MAX_CHUNK_SIZE);
8989
$i = 0;
9090
foreach ($previews as $preview) {
9191
$this->deletePreview($preview);
9292
$i++;
9393
$lastId = $preview->getId();
9494
}
9595

96-
if ($i !== 1000) {
96+
if ($i !== PreviewMapper::MAX_CHUNK_SIZE) {
9797
break;
9898
}
9999
}
@@ -106,4 +106,21 @@ public function deleteAll(): void {
106106
public function getAvailablePreviews(array $fileIds): array {
107107
return $this->previewMapper->getAvailablePreviews($fileIds);
108108
}
109+
110+
public function deleteExpiredPreviews(int $maxAgeDays): void {
111+
$lastId = 0;
112+
while (true) {
113+
$previews = $this->previewMapper->getPreviews($lastId, PreviewMapper::MAX_CHUNK_SIZE, $maxAgeDays);
114+
$i = 0;
115+
foreach ($previews as $preview) {
116+
$this->deletePreview($preview);
117+
$i++;
118+
$lastId = $preview->getId();
119+
}
120+
121+
if ($i !== PreviewMapper::MAX_CHUNK_SIZE) {
122+
break;
123+
}
124+
}
125+
}
109126
}

lib/private/Setup.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use InvalidArgumentException;
1515
use OC\Authentication\Token\PublicKeyTokenProvider;
1616
use OC\Authentication\Token\TokenCleanupJob;
17+
use OC\Core\BackgroundJobs\ExpirePreviewsJob;
1718
use OC\Core\BackgroundJobs\GenerateMetadataJob;
1819
use OC\Core\BackgroundJobs\MovePreviewJob;
1920
use OC\Log\Rotate;
@@ -507,6 +508,7 @@ public static function installBackgroundJobs(): void {
507508
$jobList->add(CleanupDeletedUsers::class);
508509
$jobList->add(GenerateMetadataJob::class);
509510
$jobList->add(MovePreviewJob::class);
511+
$jobList->add(ExpirePreviewsJob::class);
510512
}
511513

512514
/**

0 commit comments

Comments
 (0)