mirror of
https://github.com/harivansh-afk/betterNAS.git
synced 2026-04-19 21:00:29 +00:00
add nextcloud shell
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
parent
57f221fb72
commit
eea46f28ad
9 changed files with 337 additions and 0 deletions
30
apps/ainas-controlplane/appinfo/info.xml
Normal file
30
apps/ainas-controlplane/appinfo/info.xml
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<info xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
|
||||||
|
<id>ainascontrolplane</id>
|
||||||
|
<name>aiNAS Control Plane</name>
|
||||||
|
<summary>Thin aiNAS shell app for Nextcloud integration</summary>
|
||||||
|
<description>Provides aiNAS-branded entry points inside Nextcloud while delegating business logic to the aiNAS control plane.</description>
|
||||||
|
<version>0.1.0</version>
|
||||||
|
<licence>AGPL-3.0-or-later</licence>
|
||||||
|
<author homepage="https://ainas.local">aiNAS</author>
|
||||||
|
<namespace>AinasControlplane</namespace>
|
||||||
|
<category>integration</category>
|
||||||
|
<dependencies>
|
||||||
|
<nextcloud min-version="31" max-version="33"/>
|
||||||
|
</dependencies>
|
||||||
|
<navigations>
|
||||||
|
<navigation>
|
||||||
|
<id>ainascontrolplane</id>
|
||||||
|
<name>aiNAS</name>
|
||||||
|
<route>ainascontrolplane.page.index</route>
|
||||||
|
<icon>app.svg</icon>
|
||||||
|
<type>link</type>
|
||||||
|
</navigation>
|
||||||
|
</navigations>
|
||||||
|
<settings>
|
||||||
|
<admin>OCA\AinasControlplane\Settings\Admin</admin>
|
||||||
|
<admin-section>OCA\AinasControlplane\Settings\AdminSection</admin-section>
|
||||||
|
</settings>
|
||||||
|
</info>
|
||||||
|
|
||||||
17
apps/ainas-controlplane/composer.json
Normal file
17
apps/ainas-controlplane/composer.json
Normal file
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
25
apps/ainas-controlplane/lib/AppInfo/Application.php
Normal file
25
apps/ainas-controlplane/lib/AppInfo/Application.php
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\AinasControlplane\AppInfo;
|
||||||
|
|
||||||
|
use OCP\AppFramework\App;
|
||||||
|
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||||
|
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||||
|
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
||||||
|
|
||||||
|
class Application extends App implements IBootstrap {
|
||||||
|
public const APP_ID = 'ainascontrolplane';
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct(self::APP_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register(IRegistrationContext $context): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function boot(IBootContext $context): void {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
29
apps/ainas-controlplane/lib/Controller/ApiController.php
Normal file
29
apps/ainas-controlplane/lib/Controller/ApiController.php
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\AinasControlplane\Controller;
|
||||||
|
|
||||||
|
use OCA\AinasControlplane\AppInfo\Application;
|
||||||
|
use OCA\AinasControlplane\Service\ControlPlaneClient;
|
||||||
|
use OCP\AppFramework\Http\Attribute\ApiRoute;
|
||||||
|
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
||||||
|
use OCP\AppFramework\Http\DataResponse;
|
||||||
|
use OCP\AppFramework\OCSController;
|
||||||
|
use OCP\IRequest;
|
||||||
|
|
||||||
|
class ApiController extends OCSController {
|
||||||
|
public function __construct(
|
||||||
|
IRequest $request,
|
||||||
|
private readonly ControlPlaneClient $controlPlaneClient,
|
||||||
|
) {
|
||||||
|
parent::__construct(Application::APP_ID, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[NoAdminRequired]
|
||||||
|
#[ApiRoute(verb: 'GET', url: '/api/status')]
|
||||||
|
public function status(): DataResponse {
|
||||||
|
return new DataResponse($this->controlPlaneClient->fetchSnapshot());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
43
apps/ainas-controlplane/lib/Controller/PageController.php
Normal file
43
apps/ainas-controlplane/lib/Controller/PageController.php
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\AinasControlplane\Controller;
|
||||||
|
|
||||||
|
use OCA\AinasControlplane\AppInfo\Application;
|
||||||
|
use OCA\AinasControlplane\Service\ControlPlaneClient;
|
||||||
|
use OCA\AinasControlplane\Service\ControlPlaneConfig;
|
||||||
|
use OCP\AppFramework\Controller;
|
||||||
|
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
|
||||||
|
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
||||||
|
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
||||||
|
use OCP\AppFramework\Http\Attribute\OpenAPI;
|
||||||
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
|
use OCP\IRequest;
|
||||||
|
|
||||||
|
class PageController extends Controller {
|
||||||
|
public function __construct(
|
||||||
|
IRequest $request,
|
||||||
|
private readonly ControlPlaneClient $controlPlaneClient,
|
||||||
|
private readonly ControlPlaneConfig $controlPlaneConfig,
|
||||||
|
) {
|
||||||
|
parent::__construct(Application::APP_ID, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[NoCSRFRequired]
|
||||||
|
#[NoAdminRequired]
|
||||||
|
#[OpenAPI(OpenAPI::SCOPE_IGNORE)]
|
||||||
|
#[FrontpageRoute(verb: 'GET', url: '/')]
|
||||||
|
public function index(): TemplateResponse {
|
||||||
|
return new TemplateResponse(
|
||||||
|
Application::APP_ID,
|
||||||
|
'index',
|
||||||
|
[
|
||||||
|
'appName' => 'aiNAS Control Plane',
|
||||||
|
'controlPlaneUrl' => $this->controlPlaneConfig->getBaseUrl(),
|
||||||
|
'snapshot' => $this->controlPlaneClient->fetchSnapshot(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
88
apps/ainas-controlplane/lib/Service/ControlPlaneClient.php
Normal file
88
apps/ainas-controlplane/lib/Service/ControlPlaneClient.php
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\AinasControlplane\Service;
|
||||||
|
|
||||||
|
use OCP\Http\Client\IClientService;
|
||||||
|
use OCP\Http\Client\IResponse;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
class ControlPlaneClient {
|
||||||
|
public function __construct(
|
||||||
|
private readonly IClientService $clientService,
|
||||||
|
private readonly ControlPlaneConfig $controlPlaneConfig,
|
||||||
|
private readonly LoggerInterface $logger,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
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<string, mixed>}
|
||||||
|
*/
|
||||||
|
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<string, mixed>
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
31
apps/ainas-controlplane/lib/Service/ControlPlaneConfig.php
Normal file
31
apps/ainas-controlplane/lib/Service/ControlPlaneConfig.php
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\AinasControlplane\Service;
|
||||||
|
|
||||||
|
use OCA\AinasControlplane\AppInfo\Application;
|
||||||
|
use OCP\IAppConfig;
|
||||||
|
|
||||||
|
class ControlPlaneConfig {
|
||||||
|
public function __construct(
|
||||||
|
private readonly IAppConfig $appConfig,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBaseUrl(): string {
|
||||||
|
$environmentUrl = getenv('AINAS_CONTROL_PLANE_URL');
|
||||||
|
if (is_string($environmentUrl) && $environmentUrl !== '') {
|
||||||
|
return rtrim($environmentUrl, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
$configuredUrl = $this->appConfig->getValueString(
|
||||||
|
Application::APP_ID,
|
||||||
|
'control_plane_url',
|
||||||
|
'http://control-plane:3000',
|
||||||
|
);
|
||||||
|
|
||||||
|
return rtrim($configuredUrl, '/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
39
apps/ainas-controlplane/lib/Settings/Admin.php
Normal file
39
apps/ainas-controlplane/lib/Settings/Admin.php
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\AinasControlplane\Settings;
|
||||||
|
|
||||||
|
use OCA\AinasControlplane\AppInfo\Application;
|
||||||
|
use OCA\AinasControlplane\Service\ControlPlaneClient;
|
||||||
|
use OCA\AinasControlplane\Service\ControlPlaneConfig;
|
||||||
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
|
use OCP\Settings\ISettings;
|
||||||
|
|
||||||
|
class Admin implements ISettings {
|
||||||
|
public function __construct(
|
||||||
|
private readonly ControlPlaneConfig $controlPlaneConfig,
|
||||||
|
private readonly ControlPlaneClient $controlPlaneClient,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getForm(): TemplateResponse {
|
||||||
|
return new TemplateResponse(
|
||||||
|
Application::APP_ID,
|
||||||
|
'admin',
|
||||||
|
[
|
||||||
|
'controlPlaneUrl' => $this->controlPlaneConfig->getBaseUrl(),
|
||||||
|
'snapshot' => $this->controlPlaneClient->fetchSnapshot(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSection(): string {
|
||||||
|
return Application::APP_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPriority(): int {
|
||||||
|
return 50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
35
apps/ainas-controlplane/lib/Settings/AdminSection.php
Normal file
35
apps/ainas-controlplane/lib/Settings/AdminSection.php
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\AinasControlplane\Settings;
|
||||||
|
|
||||||
|
use OCA\AinasControlplane\AppInfo\Application;
|
||||||
|
use OCP\IL10N;
|
||||||
|
use OCP\IURLGenerator;
|
||||||
|
use OCP\Settings\IIconSection;
|
||||||
|
|
||||||
|
class AdminSection implements IIconSection {
|
||||||
|
public function __construct(
|
||||||
|
private readonly IURLGenerator $urlGenerator,
|
||||||
|
private readonly IL10N $l,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getID(): string {
|
||||||
|
return Application::APP_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string {
|
||||||
|
return $this->l->t('aiNAS');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPriority(): int {
|
||||||
|
return 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIcon(): ?string {
|
||||||
|
return $this->urlGenerator->imagePath(Application::APP_ID, 'app-dark.svg');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue