From eea46f28adfdace0c767d914ce4c5ec6ce6f5dd4 Mon Sep 17 00:00:00 2001 From: Harivansh Rathi Date: Tue, 31 Mar 2026 21:25:48 +0000 Subject: [PATCH] add nextcloud shell Co-authored-by: Codex --- apps/ainas-controlplane/appinfo/info.xml | 30 +++++++ apps/ainas-controlplane/composer.json | 17 ++++ .../lib/AppInfo/Application.php | 25 ++++++ .../lib/Controller/ApiController.php | 29 ++++++ .../lib/Controller/PageController.php | 43 +++++++++ .../lib/Service/ControlPlaneClient.php | 88 +++++++++++++++++++ .../lib/Service/ControlPlaneConfig.php | 31 +++++++ .../ainas-controlplane/lib/Settings/Admin.php | 39 ++++++++ .../lib/Settings/AdminSection.php | 35 ++++++++ 9 files changed, 337 insertions(+) create mode 100644 apps/ainas-controlplane/appinfo/info.xml create mode 100644 apps/ainas-controlplane/composer.json create mode 100644 apps/ainas-controlplane/lib/AppInfo/Application.php create mode 100644 apps/ainas-controlplane/lib/Controller/ApiController.php create mode 100644 apps/ainas-controlplane/lib/Controller/PageController.php create mode 100644 apps/ainas-controlplane/lib/Service/ControlPlaneClient.php create mode 100644 apps/ainas-controlplane/lib/Service/ControlPlaneConfig.php create mode 100644 apps/ainas-controlplane/lib/Settings/Admin.php create mode 100644 apps/ainas-controlplane/lib/Settings/AdminSection.php diff --git a/apps/ainas-controlplane/appinfo/info.xml b/apps/ainas-controlplane/appinfo/info.xml new file mode 100644 index 0000000..d36430e --- /dev/null +++ b/apps/ainas-controlplane/appinfo/info.xml @@ -0,0 +1,30 @@ + + + ainascontrolplane + aiNAS Control Plane + Thin aiNAS shell app for Nextcloud integration + Provides aiNAS-branded entry points inside Nextcloud while delegating business logic to the aiNAS control plane. + 0.1.0 + AGPL-3.0-or-later + aiNAS + AinasControlplane + integration + + + + + + ainascontrolplane + aiNAS + ainascontrolplane.page.index + app.svg + link + + + + OCA\AinasControlplane\Settings\Admin + OCA\AinasControlplane\Settings\AdminSection + + + diff --git a/apps/ainas-controlplane/composer.json b/apps/ainas-controlplane/composer.json new file mode 100644 index 0000000..6f316d5 --- /dev/null +++ b/apps/ainas-controlplane/composer.json @@ -0,0 +1,17 @@ +{ + "name": "ainas/ainascontrolplane", + "description": "aiNAS Nextcloud shell app", + "license": "AGPL-3.0-or-later", + "autoload": { + "psr-4": { + "OCA\\AinasControlplane\\": "lib/" + } + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "nextcloud/ocp": "dev-stable31" + } +} + diff --git a/apps/ainas-controlplane/lib/AppInfo/Application.php b/apps/ainas-controlplane/lib/AppInfo/Application.php new file mode 100644 index 0000000..6a6dbd2 --- /dev/null +++ b/apps/ainas-controlplane/lib/AppInfo/Application.php @@ -0,0 +1,25 @@ +controlPlaneClient->fetchSnapshot()); + } +} + diff --git a/apps/ainas-controlplane/lib/Controller/PageController.php b/apps/ainas-controlplane/lib/Controller/PageController.php new file mode 100644 index 0000000..f8c3d92 --- /dev/null +++ b/apps/ainas-controlplane/lib/Controller/PageController.php @@ -0,0 +1,43 @@ + 'aiNAS Control Plane', + 'controlPlaneUrl' => $this->controlPlaneConfig->getBaseUrl(), + 'snapshot' => $this->controlPlaneClient->fetchSnapshot(), + ], + ); + } +} + diff --git a/apps/ainas-controlplane/lib/Service/ControlPlaneClient.php b/apps/ainas-controlplane/lib/Service/ControlPlaneClient.php new file mode 100644 index 0000000..da6e916 --- /dev/null +++ b/apps/ainas-controlplane/lib/Service/ControlPlaneClient.php @@ -0,0 +1,88 @@ + + */ + public function fetchSnapshot(): array { + $baseUrl = $this->controlPlaneConfig->getBaseUrl(); + + try { + $healthResponse = $this->request($baseUrl . '/health'); + $versionResponse = $this->request($baseUrl . '/version'); + + return [ + 'available' => $healthResponse['statusCode'] === 200, + 'url' => $baseUrl, + 'health' => $healthResponse['body'], + 'version' => $versionResponse['body'], + ]; + } catch (\Throwable $exception) { + $this->logger->warning('Failed to reach aiNAS control plane', [ + 'exception' => $exception, + 'url' => $baseUrl, + ]); + + return [ + 'available' => false, + 'url' => $baseUrl, + 'error' => $exception->getMessage(), + ]; + } + } + + /** + * @return array{statusCode: int, body: array} + */ + private function request(string $url): array { + $client = $this->clientService->newClient(); + $response = $client->get($url, [ + 'headers' => [ + 'Accept' => 'application/json', + ], + 'http_errors' => false, + 'timeout' => 2, + 'nextcloud' => [ + 'allow_local_address' => true, + ], + ]); + + return [ + 'statusCode' => $response->getStatusCode(), + 'body' => $this->decodeBody($response), + ]; + } + + /** + * @return array + */ + private function decodeBody(IResponse $response): array { + $body = $response->getBody(); + if ($body === '') { + return []; + } + + $decoded = json_decode($body, true, 512, JSON_THROW_ON_ERROR); + if (!is_array($decoded)) { + return []; + } + + return $decoded; + } +} + diff --git a/apps/ainas-controlplane/lib/Service/ControlPlaneConfig.php b/apps/ainas-controlplane/lib/Service/ControlPlaneConfig.php new file mode 100644 index 0000000..0b443d0 --- /dev/null +++ b/apps/ainas-controlplane/lib/Service/ControlPlaneConfig.php @@ -0,0 +1,31 @@ +appConfig->getValueString( + Application::APP_ID, + 'control_plane_url', + 'http://control-plane:3000', + ); + + return rtrim($configuredUrl, '/'); + } +} + diff --git a/apps/ainas-controlplane/lib/Settings/Admin.php b/apps/ainas-controlplane/lib/Settings/Admin.php new file mode 100644 index 0000000..927bfb4 --- /dev/null +++ b/apps/ainas-controlplane/lib/Settings/Admin.php @@ -0,0 +1,39 @@ + $this->controlPlaneConfig->getBaseUrl(), + 'snapshot' => $this->controlPlaneClient->fetchSnapshot(), + ], + ); + } + + public function getSection(): string { + return Application::APP_ID; + } + + public function getPriority(): int { + return 50; + } +} + diff --git a/apps/ainas-controlplane/lib/Settings/AdminSection.php b/apps/ainas-controlplane/lib/Settings/AdminSection.php new file mode 100644 index 0000000..caab8b9 --- /dev/null +++ b/apps/ainas-controlplane/lib/Settings/AdminSection.php @@ -0,0 +1,35 @@ +l->t('aiNAS'); + } + + public function getPriority(): int { + return 50; + } + + public function getIcon(): ?string { + return $this->urlGenerator->imagePath(Application::APP_ID, 'app-dark.svg'); + } +} +