generated from Nodarx/template
c20dde6633
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
182 lines
6.5 KiB
PHP
182 lines
6.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
namespace KTXM\ProviderImap\Console;
|
|
|
|
use KTXC\Context\TenantContext;
|
|
use KTXM\ProviderImap\Providers\Provider;
|
|
use KTXM\ProviderImap\Providers\Service;
|
|
use KTXM\ProviderImap\Service\Cache\HarmonizationService;
|
|
use KTXM\ProviderImap\Stores\MailboxStore;
|
|
use KTXM\ProviderImap\Stores\MessageStore;
|
|
use Symfony\Component\Console\Attribute\AsCommand;
|
|
use Symfony\Component\Console\Command\Command;
|
|
use Symfony\Component\Console\Input\InputArgument;
|
|
use Symfony\Component\Console\Input\InputInterface;
|
|
use Symfony\Component\Console\Input\InputOption;
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
|
|
|
/**
|
|
* Harmonize the cache of a stored IMAP service with its server.
|
|
*
|
|
* Read-only towards the server (EXAMINE, BODY.PEEK); safe to run repeatedly.
|
|
*/
|
|
#[AsCommand(
|
|
name: 'provider_imap_mail:cache:harmonize',
|
|
description: 'Harmonize the message cache of an IMAP service with its server',
|
|
)]
|
|
class HarmonizeCommand extends Command
|
|
{
|
|
public function __construct(
|
|
private readonly Provider $provider,
|
|
private readonly TenantContext $tenantContext,
|
|
private readonly HarmonizationService $harmonizer,
|
|
private readonly MailboxStore $mailboxStore,
|
|
private readonly MessageStore $messageStore,
|
|
) {
|
|
parent::__construct();
|
|
}
|
|
|
|
protected function configure(): void
|
|
{
|
|
$this
|
|
->addArgument('service-id', InputArgument::REQUIRED, 'Stored service ID')
|
|
->addOption('tenant', 't', InputOption::VALUE_REQUIRED, 'Tenant ID')
|
|
->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'User ID')
|
|
->addOption('mailbox', 'm', InputOption::VALUE_REQUIRED, 'Mailbox to harmonize (default: INBOX)')
|
|
->addOption('all', 'a', InputOption::VALUE_NONE, 'Harmonize every selectable mailbox')
|
|
->setHelp(<<<'HELP'
|
|
The <info>provider_imap_mail:cache:harmonize</info> command brings the cache of a
|
|
stored IMAP service in line with its server.
|
|
|
|
It will:
|
|
1. Create the cache indexes (idempotent)
|
|
2. Harmonize the mailbox list
|
|
3. Harmonize the messages of one mailbox, or of every selectable mailbox with --all
|
|
|
|
Server access is read-only: nothing is marked as read.
|
|
|
|
Examples:
|
|
|
|
Harmonize INBOX:
|
|
<info>provider_imap_mail:cache:harmonize abc123 --tenant=t1 --user=u1</info>
|
|
|
|
Harmonize a specific mailbox:
|
|
<info>provider_imap_mail:cache:harmonize abc123 --tenant=t1 --user=u1 --mailbox=Sent</info>
|
|
|
|
Harmonize every mailbox:
|
|
<info>provider_imap_mail:cache:harmonize abc123 --tenant=t1 --user=u1 --all</info>
|
|
HELP);
|
|
}
|
|
|
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
|
{
|
|
$io = new SymfonyStyle($input, $output);
|
|
|
|
$tenantId = (string) ($input->getOption('tenant') ?? '');
|
|
$userId = (string) ($input->getOption('user') ?? '');
|
|
$serviceId = (string) $input->getArgument('service-id');
|
|
$mailbox = trim((string) ($input->getOption('mailbox') ?? 'INBOX'));
|
|
$all = (bool) $input->getOption('all');
|
|
|
|
$errors = [];
|
|
if ($tenantId === '') {
|
|
$errors[] = 'Tenant ID is required (--tenant).';
|
|
}
|
|
if ($userId === '') {
|
|
$errors[] = 'User ID is required (--user).';
|
|
}
|
|
if ($all && $input->getOption('mailbox') !== null) {
|
|
$errors[] = 'Use either --mailbox or --all, not both.';
|
|
}
|
|
if ($errors !== []) {
|
|
$io->error($errors);
|
|
return Command::FAILURE;
|
|
}
|
|
|
|
$this->tenantContext->resolveIdentifier($tenantId);
|
|
$service = $this->provider->serviceFetch($tenantId, $userId, $serviceId);
|
|
if (!$service instanceof Service) {
|
|
$io->error(sprintf("Service '%s' not found.", $serviceId));
|
|
return Command::FAILURE;
|
|
}
|
|
|
|
$this->mailboxStore->ensureIndexes();
|
|
$this->messageStore->ensureIndexes();
|
|
|
|
$harmonizer = $this->harmonizer->for($service);
|
|
$startedAt = microtime(true);
|
|
|
|
try {
|
|
if ($all) {
|
|
$result = $harmonizer->harmonizeAll();
|
|
$mailboxes = $result['mailboxes'];
|
|
$messages = $result['messages'];
|
|
} else {
|
|
$mailboxes = $harmonizer->harmonizeMailboxes();
|
|
$messages = [$mailbox => $harmonizer->harmonizeMessages($mailbox)];
|
|
}
|
|
} catch (\Throwable $e) {
|
|
$io->error('Harmonization failed: ' . $e->getMessage());
|
|
return Command::FAILURE;
|
|
}
|
|
|
|
$io->section('Mailboxes');
|
|
$io->definitionList(
|
|
['Added' => self::names($mailboxes['added'])],
|
|
['Updated' => count($mailboxes['updated'])],
|
|
['Removed' => self::names($mailboxes['removed'])],
|
|
);
|
|
|
|
$io->section('Messages');
|
|
$rows = [];
|
|
$failed = 0;
|
|
foreach ($messages as $name => $outcome) {
|
|
if (is_string($outcome)) {
|
|
$failed++;
|
|
$rows[] = [$name, '<error>failed</error>', '', '', '', '', $outcome];
|
|
continue;
|
|
}
|
|
$rows[] = [
|
|
$name,
|
|
$outcome['status'],
|
|
$outcome['added'],
|
|
$outcome['updated'],
|
|
$outcome['removed'],
|
|
$outcome['complete'] ? 'yes' : 'no',
|
|
$outcome['reset'] ? 'UIDVALIDITY changed, cache reset' : '',
|
|
];
|
|
}
|
|
$io->table(['Mailbox', 'Status', 'Added', 'Updated', 'Removed', 'Complete', 'Note'], $rows);
|
|
|
|
$io->writeln(sprintf(
|
|
'Cache: <info>storage/%s/provider_imap/%s</info>, MongoDB <info>provider_imap_mail_mailboxes</info> / <info>provider_imap_mail_messages</info>',
|
|
$tenantId,
|
|
$serviceId,
|
|
));
|
|
$io->writeln(sprintf('Completed in <info>%.2fs</info>', microtime(true) - $startedAt));
|
|
|
|
if ($failed > 0) {
|
|
$io->warning(sprintf('%d mailbox(es) failed.', $failed));
|
|
return Command::FAILURE;
|
|
}
|
|
|
|
return Command::SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @param string[] $names
|
|
*/
|
|
private static function names(array $names): string
|
|
{
|
|
return $names === [] ? '-' : implode(', ', $names);
|
|
}
|
|
}
|