Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use OCA\AppAPI\Notifications\ExAppNotifier;
use OCA\AppAPI\PublicCapabilities;
use OCA\AppAPI\SetupChecks\DaemonCheck;
use OCA\AppAPI\SetupChecks\HarpVersionCheck;
use OCA\DAV\Events\SabrePluginAddEvent;
use OCA\DAV\Events\SabrePluginAuthInitEvent;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
Expand All @@ -42,6 +43,7 @@ class Application extends App implements IBootstrap {
public const APP_ID = 'app_api';
public const TEST_DEPLOY_APPID = 'test-deploy';
public const TEST_DEPLOY_INFO_XML = 'https://raw.githubusercontent.com/nextcloud/test-deploy/main/appinfo/info.xml';
public const MINIMUM_HARP_VERSION = '0.3';

public function __construct(array $urlParams = []) {
parent::__construct(self::APP_ID, $urlParams);
Expand Down Expand Up @@ -69,6 +71,7 @@ public function register(IRegistrationContext $context): void {
$context->registerEventListener(DeclarativeSettingsSetValueEvent::class, SetValueListener::class);

$context->registerSetupCheck(DaemonCheck::class);
$context->registerSetupCheck(HarpVersionCheck::class);
}

public function boot(IBootContext $context): void {
Expand Down
25 changes: 25 additions & 0 deletions lib/Service/HarpService.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,29 @@ public function harpExAppUpdate(DaemonConfig $daemonConfig, ExApp $exApp, bool $
$this->logger->error("HarpService: harpExAppUpdate ($addedStr) failed: " . $e->getMessage());
}
}

public function getHarpVersion(DaemonConfig $daemonConfig): ?string {
if (!self::isHarp($daemonConfig->getDeployConfig())) {
return null;
}
$this->initGuzzleClient($daemonConfig);
$url = $this->buildHarpUrl($daemonConfig, '/info');
$this->logger->info("HarpService: getHarpVersion: " . $url);

try {
$response = $this->guzzleClient->get($url);
$data = json_decode($response->getBody()->getContents(), true);
if (isset($data['version'])) {
if (gettype($data['version']) === 'double') {
// Locale-independent float to string conversion
return rtrim(number_format($data['version'], 10, '.', ''), '0');
}
return (string) $data['version'];
}
return null;
} catch (\Exception $e) {
$this->logger->error("HarpService: getHarpVersion failed: " . $e->getMessage());
return null;
}
}
}
103 changes: 103 additions & 0 deletions lib/SetupChecks/HarpVersionCheck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\AppAPI\SetupChecks;

use OCA\AppAPI\AppInfo\Application;
use OCA\AppAPI\Db\DaemonConfig;
use OCA\AppAPI\Service\DaemonConfigService;
use OCA\AppAPI\Service\HarpService;
use OCP\ICacheFactory;
use OCP\IL10N;
use OCP\SetupCheck\ISetupCheck;
use OCP\SetupCheck\SetupResult;
use Psr\Log\LoggerInterface;

class HarpVersionCheck implements ISetupCheck {
private \OCP\ICache $cache;

public function __construct(
private readonly IL10N $l10n,
private readonly LoggerInterface $logger,
private readonly DaemonConfigService $daemonConfigService,
private readonly HarpService $harpService,
ICacheFactory $cacheFactory,
) {
if ($cacheFactory->isAvailable()) {
$this->cache = $cacheFactory->createDistributed(Application::APP_ID . '/harp_version_check');
}
}

public function getName(): string {
return $this->l10n->t('AppAPI HaRP version check');
}

public function getCategory(): string {
return 'system';
}

/**
* @return DaemonConfig[]
*/
public function getHaRPDaemonConfigs(): array {
$allDaemons = $this->daemonConfigService->getRegisteredDaemonConfigs();
return array_filter($allDaemons, function (DaemonConfig $daemon) {
return HarpService::isHarp($daemon->getDeployConfig());
});
}

public function run(): SetupResult {
$harpDaemons = $this->getHaRPDaemonConfigs();

if (empty($harpDaemons)) {
return SetupResult::success();
}

$issues = [];
foreach ($harpDaemons as $daemonConfig) {
try {
$versionString = $this->getHarpVersion($daemonConfig);
if ($versionString === null) {
$issues[] = $this->l10n->t('Could not retrieve HaRP version from daemon "%s"', [$daemonConfig->getName()]);
continue;
}
if (!$this->fulfillsMinimumVersionRequirement($versionString)) {
$issues[] = $this->l10n->t('HaRP version for daemon "%s" is "%s", which is too old. The minimum required version is "%s". Please update the daemon to the latest version. ', [$daemonConfig->getName(), $versionString, Application::MINIMUM_HARP_VERSION]);
}
} catch (\Exception $e) {
$this->logger->error('Failed to check HaRP version for daemon ' . $daemonConfig->getName() . ': ' . $e->getMessage(), ['exception' => $e]);
$issues[] = $this->l10n->t('Failed to check HaRP version for daemon "%s": %s', [$daemonConfig->getName(), $e->getMessage()]);
}
}

if (!empty($issues)) {
return SetupResult::warning(
implode("\n", $issues),
"https://github.com/nextcloud/HaRP/",
);
}

return SetupResult::success();
}

private function fulfillsMinimumVersionRequirement(string $version): bool {
return version_compare($version, Application::MINIMUM_HARP_VERSION, ">=");
}

private function getHarpVersion(DaemonConfig $daemonConfig): ?string {
$cacheKey = $daemonConfig->getName() . "_" . (string)crc32(json_encode($daemonConfig));
$version = $this->cache->get($cacheKey);
if ($version === null) {
$version = $this->harpService->getHarpVersion($daemonConfig);
$oneWeek = 60 * 60 * 24 * 7;
$this->cache->set($cacheKey, $version, $oneWeek);
}
return $version;
}
}