diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 8782c2ca..a47c1be0 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -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; @@ -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.0'; public function __construct(array $urlParams = []) { parent::__construct(self::APP_ID, $urlParams); @@ -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 { diff --git a/lib/Service/HarpService.php b/lib/Service/HarpService.php index 1e5fc753..979fe44c 100644 --- a/lib/Service/HarpService.php +++ b/lib/Service/HarpService.php @@ -106,7 +106,7 @@ public static function getExAppHost(ExApp $exApp): string { return $exApp->getAppid(); } } - return "127.0.0.1"; + return '127.0.0.1'; } public function getHarpExApp(ExApp $exApp): array { @@ -157,4 +157,34 @@ 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') { + return $this->versionFloatToString($data['version']); + } + return (string) $data['version']; + } + return null; + } catch (\Exception $e) { + $this->logger->error('HarpService: getHarpVersion failed: ' . $e->getMessage()); + return null; + } + } + + private function versionFloatToString(float $version): string { + // If the Harp version was reported as a float, e.g. `4.2`, convert it to the string "4.2.0". + // We use number_format to avoid potential issues from locale-dependent float to string conversions. + return rtrim(number_format($version, 10, '.', ''), '0') . '.0'; + } } diff --git a/lib/SetupChecks/HarpVersionCheck.php b/lib/SetupChecks/HarpVersionCheck.php new file mode 100644 index 00000000..fd7afc4b --- /dev/null +++ b/lib/SetupChecks/HarpVersionCheck.php @@ -0,0 +1,103 @@ +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[] + */ + private 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("
", $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; + } +}