Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
CleanupStaleSigningStatus
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
2 / 2
3
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 run
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4/**
5 * SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
6 * SPDX-License-Identifier: AGPL-3.0-or-later
7 */
8
9namespace OCA\Libresign\BackgroundJob;
10
11use DateTime;
12use OCA\Libresign\Db\File;
13use OCA\Libresign\Db\FileMapper;
14use OCP\AppFramework\Utility\ITimeFactory;
15use OCP\BackgroundJob\IJob;
16use OCP\BackgroundJob\TimedJob;
17use Psr\Log\LoggerInterface;
18
19/**
20 * Report stale signing operations for reconciliation.
21 *
22 * Keep the registered class name for upgrade compatibility. A timeout cannot
23 * establish whether a cryptographic operation wrote a PDF, so resetting the
24 * status automatically would permit an unsafe second signature.
25 */
26class CleanupStaleSigningStatus extends TimedJob {
27    private const int STALE_TIMEOUT_MINUTES = 10;
28    private const int MAX_FILE_IDS_IN_LOG = 10;
29
30    public function __construct(
31        ITimeFactory $time,
32        private FileMapper $fileMapper,
33        private LoggerInterface $logger,
34    ) {
35        parent::__construct($time);
36        $this->setInterval(60 * 5);
37        $this->setTimeSensitivity(IJob::TIME_INSENSITIVE);
38    }
39
40    #[\Override]
41    public function run($argument): void {
42        $staleFiles = $this->fileMapper->findStaleSigningInProgress(
43            new DateTime('-' . self::STALE_TIMEOUT_MINUTES . ' minutes'),
44        );
45        if ($staleFiles === []) {
46            return;
47        }
48
49        // One diagnostic per cron run, not one warning for every affected file.
50        $this->logger->warning('Stale signing statuses need manual review before retry', [
51            'count' => count($staleFiles),
52            'fileIds' => array_map(
53                static fn (File $file): int => $file->getId(),
54                array_slice($staleFiles, 0, self::MAX_FILE_IDS_IN_LOG),
55            ),
56        ]);
57    }
58}