This commit is contained in:
Harivansh Rathi 2026-04-01 02:28:21 +00:00
parent a4cb20d228
commit 12c53f3515
20 changed files with 70 additions and 70 deletions

View file

@ -1,13 +1,13 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<info xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <info xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd"> xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
<id>ainascontrolplane</id> <id>betternascontrolplane</id>
<name>aiNAS Control Plane</name> <name>aiNAS Control Plane</name>
<summary>Thin aiNAS shell app for Nextcloud integration</summary> <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> <description>Provides aiNAS-branded entry points inside Nextcloud while delegating business logic to the aiNAS control plane.</description>
<version>0.1.0</version> <version>0.1.0</version>
<licence>AGPL-3.0-or-later</licence> <licence>AGPL-3.0-or-later</licence>
<author homepage="https://ainas.local">aiNAS</author> <author homepage="https://betternas.local">aiNAS</author>
<namespace>AinasControlplane</namespace> <namespace>AinasControlplane</namespace>
<category>integration</category> <category>integration</category>
<dependencies> <dependencies>
@ -15,9 +15,9 @@
</dependencies> </dependencies>
<navigations> <navigations>
<navigation> <navigation>
<id>ainascontrolplane</id> <id>betternascontrolplane</id>
<name>aiNAS</name> <name>aiNAS</name>
<route>ainascontrolplane.page.index</route> <route>betternascontrolplane.page.index</route>
<icon>app.svg</icon> <icon>app.svg</icon>
<type>link</type> <type>link</type>
</navigation> </navigation>

View file

@ -1,5 +1,5 @@
{ {
"name": "ainas/ainascontrolplane", "name": "betternas/betternascontrolplane",
"description": "aiNAS Nextcloud shell app", "description": "aiNAS Nextcloud shell app",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"autoload": { "autoload": {

View file

@ -1,10 +1,10 @@
.ainas-shell { .betternas-shell {
max-width: 1100px; max-width: 1100px;
margin: 0 auto; margin: 0 auto;
padding: 32px; padding: 32px;
} }
.ainas-shell__hero { .betternas-shell__hero {
margin-bottom: 28px; margin-bottom: 28px;
padding: 28px; padding: 28px;
border-radius: 24px; border-radius: 24px;
@ -12,7 +12,7 @@
color: #f6fafc; color: #f6fafc;
} }
.ainas-shell__eyebrow { .betternas-shell__eyebrow {
margin: 0 0 12px; margin: 0 0 12px;
font-size: 12px; font-size: 12px;
letter-spacing: 0.12em; letter-spacing: 0.12em;
@ -20,26 +20,26 @@
opacity: 0.8; opacity: 0.8;
} }
.ainas-shell__title { .betternas-shell__title {
margin: 0 0 12px; margin: 0 0 12px;
font-size: 32px; font-size: 32px;
line-height: 1.1; line-height: 1.1;
} }
.ainas-shell__copy { .betternas-shell__copy {
margin: 0; margin: 0;
max-width: 70ch; max-width: 70ch;
font-size: 15px; font-size: 15px;
line-height: 1.6; line-height: 1.6;
} }
.ainas-shell__grid { .betternas-shell__grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px; gap: 20px;
} }
.ainas-shell__card { .betternas-shell__card {
padding: 24px; padding: 24px;
border: 1px solid rgba(16, 33, 45, 0.12); border: 1px solid rgba(16, 33, 45, 0.12);
border-radius: 20px; border-radius: 20px;
@ -47,38 +47,38 @@
box-shadow: 0 20px 40px rgba(16, 33, 45, 0.06); box-shadow: 0 20px 40px rgba(16, 33, 45, 0.06);
} }
.ainas-shell__card h2 { .betternas-shell__card h2 {
margin-top: 0; margin-top: 0;
} }
.ainas-shell__card dl { .betternas-shell__card dl {
display: grid; display: grid;
grid-template-columns: minmax(120px, 160px) 1fr; grid-template-columns: minmax(120px, 160px) 1fr;
gap: 8px 16px; gap: 8px 16px;
margin: 0; margin: 0;
} }
.ainas-shell__card dt { .betternas-shell__card dt {
font-weight: 600; font-weight: 600;
} }
.ainas-shell__card dd { .betternas-shell__card dd {
margin: 0; margin: 0;
} }
.ainas-shell__card code { .betternas-shell__card code {
display: inline-block; display: inline-block;
padding: 4px 8px; padding: 4px 8px;
border-radius: 999px; border-radius: 999px;
background: #eef4f7; background: #eef4f7;
} }
.ainas-shell__card ul { .betternas-shell__card ul {
margin: 0; margin: 0;
padding-left: 20px; padding-left: 20px;
} }
.ainas-shell__error { .betternas-shell__error {
margin-top: 16px; margin-top: 16px;
color: #b42318; color: #b42318;
} }

View file

@ -10,7 +10,7 @@ use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\AppFramework\Bootstrap\IRegistrationContext;
class Application extends App implements IBootstrap { class Application extends App implements IBootstrap {
public const APP_ID = 'ainascontrolplane'; public const APP_ID = 'betternascontrolplane';
public function __construct() { public function __construct() {
parent::__construct(self::APP_ID); parent::__construct(self::APP_ID);

View file

@ -5,24 +5,24 @@ declare(strict_types=1);
use OCA\AinasControlplane\AppInfo\Application; use OCA\AinasControlplane\AppInfo\Application;
use OCP\Util; use OCP\Util;
Util::addStyle(Application::APP_ID, 'ainascontrolplane'); Util::addStyle(Application::APP_ID, 'betternascontrolplane');
$snapshot = $_['snapshot']; $snapshot = $_['snapshot'];
$reachable = !empty($snapshot['available']) ? 'yes' : 'no'; $reachable = !empty($snapshot['available']) ? 'yes' : 'no';
$version = $snapshot['version']['version'] ?? 'unreachable'; $version = $snapshot['version']['version'] ?? 'unreachable';
?> ?>
<div class="ainas-shell ainas-shell--admin"> <div class="betternas-shell betternas-shell--admin">
<div class="ainas-shell__hero"> <div class="betternas-shell__hero">
<p class="ainas-shell__eyebrow">Admin settings</p> <p class="betternas-shell__eyebrow">Admin settings</p>
<h1 class="ainas-shell__title">aiNAS control-plane wiring</h1> <h1 class="betternas-shell__title">aiNAS control-plane wiring</h1>
<p class="ainas-shell__copy"> <p class="betternas-shell__copy">
The local scaffold wires this app to the control plane through the <code>AINAS_CONTROL_PLANE_URL</code> environment variable in the Nextcloud container. The local scaffold wires this app to the control plane through the <code>AINAS_CONTROL_PLANE_URL</code> environment variable in the Nextcloud container.
</p> </p>
</div> </div>
<div class="ainas-shell__grid"> <div class="betternas-shell__grid">
<section class="ainas-shell__card"> <section class="betternas-shell__card">
<h2>Current wiring</h2> <h2>Current wiring</h2>
<dl> <dl>
<dt>Control-plane URL</dt> <dt>Control-plane URL</dt>
@ -34,7 +34,7 @@ $version = $snapshot['version']['version'] ?? 'unreachable';
</dl> </dl>
</section> </section>
<section class="ainas-shell__card"> <section class="betternas-shell__card">
<h2>Next step</h2> <h2>Next step</h2>
<p>Keep storage policy, sharing logic, and orchestration in the control-plane service. This page should remain a thin integration surface.</p> <p>Keep storage policy, sharing logic, and orchestration in the control-plane service. This page should remain a thin integration surface.</p>
</section> </section>

View file

@ -5,7 +5,7 @@ declare(strict_types=1);
use OCA\AinasControlplane\AppInfo\Application; use OCA\AinasControlplane\AppInfo\Application;
use OCP\Util; use OCP\Util;
Util::addStyle(Application::APP_ID, 'ainascontrolplane'); Util::addStyle(Application::APP_ID, 'betternascontrolplane');
$snapshot = $_['snapshot']; $snapshot = $_['snapshot'];
$version = $snapshot['version']['version'] ?? 'unreachable'; $version = $snapshot['version']['version'] ?? 'unreachable';
@ -13,17 +13,17 @@ $status = !empty($snapshot['available']) ? 'Connected' : 'Unavailable';
$error = $snapshot['error'] ?? null; $error = $snapshot['error'] ?? null;
?> ?>
<div class="ainas-shell"> <div class="betternas-shell">
<div class="ainas-shell__hero"> <div class="betternas-shell__hero">
<p class="ainas-shell__eyebrow">aiNAS inside Nextcloud</p> <p class="betternas-shell__eyebrow">aiNAS inside Nextcloud</p>
<h1 class="ainas-shell__title"><?php p($_['appName']); ?></h1> <h1 class="betternas-shell__title"><?php p($_['appName']); ?></h1>
<p class="ainas-shell__copy"> <p class="betternas-shell__copy">
This shell app stays intentionally thin. It exposes aiNAS entry points inside Nextcloud and delegates business logic to the external control-plane service. This shell app stays intentionally thin. It exposes aiNAS entry points inside Nextcloud and delegates business logic to the external control-plane service.
</p> </p>
</div> </div>
<div class="ainas-shell__grid"> <div class="betternas-shell__grid">
<section class="ainas-shell__card"> <section class="betternas-shell__card">
<h2>Control plane</h2> <h2>Control plane</h2>
<dl> <dl>
<dt>Configured URL</dt> <dt>Configured URL</dt>
@ -34,11 +34,11 @@ $error = $snapshot['error'] ?? null;
<dd><?php p($version); ?></dd> <dd><?php p($version); ?></dd>
</dl> </dl>
<?php if ($error !== null): ?> <?php if ($error !== null): ?>
<p class="ainas-shell__error"><?php p($error); ?></p> <p class="betternas-shell__error"><?php p($error); ?></p>
<?php endif; ?> <?php endif; ?>
</section> </section>
<section class="ainas-shell__card"> <section class="betternas-shell__card">
<h2>Boundary</h2> <h2>Boundary</h2>
<ul> <ul>
<li>Nextcloud provides file and client primitives.</li> <li>Nextcloud provides file and client primitives.</li>

View file

@ -6,7 +6,7 @@ aiNAS treats Nextcloud as an upstream backend, not as the place where aiNAS prod
That leads to three explicit boundaries: That leads to three explicit boundaries:
1. `apps/ainas-controlplane/` is a thin shell inside Nextcloud. 1. `apps/betternas-controlplane/` is a thin shell inside Nextcloud.
2. `exapps/control-plane/` owns aiNAS business logic and internal APIs. 2. `exapps/control-plane/` owns aiNAS business logic and internal APIs.
3. `packages/contracts/` defines the interface between the shell app and the control plane. 3. `packages/contracts/` defines the interface between the shell app and the control plane.

View file

@ -1,5 +1,5 @@
{ {
"name": "@ainas/control-plane", "name": "@betternas/control-plane",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"type": "module", "type": "module",
@ -11,7 +11,7 @@
"typecheck": "tsc --noEmit -p tsconfig.json" "typecheck": "tsc --noEmit -p tsconfig.json"
}, },
"dependencies": { "dependencies": {
"@ainas/contracts": "file:../../packages/contracts" "@betternas/contracts": "file:../../packages/contracts"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.18.6", "@types/node": "^22.18.6",

View file

@ -1,4 +1,4 @@
import type { NextcloudBackendStatus } from "@ainas/contracts"; import type { NextcloudBackendStatus } from "@betternas/contracts";
export class NextcloudBackendAdapter { export class NextcloudBackendAdapter {
constructor(private readonly baseUrl: string) {} constructor(private readonly baseUrl: string) {}

View file

@ -3,7 +3,7 @@ import {
CONTROL_PLANE_ROUTES, CONTROL_PLANE_ROUTES,
type ControlPlaneHealthResponse, type ControlPlaneHealthResponse,
type ControlPlaneVersionResponse type ControlPlaneVersionResponse
} from "@ainas/contracts"; } from "@betternas/contracts";
import type { ControlPlaneConfig } from "./config.js"; import type { ControlPlaneConfig } from "./config.js";
import { NextcloudBackendAdapter } from "./adapters/nextcloud-backend.js"; import { NextcloudBackendAdapter } from "./adapters/nextcloud-backend.js";

View file

@ -17,7 +17,7 @@ docker/
nextcloud/ nextcloud/
CADDY/ CADDY/
apps/ apps/
ainas-controlplane/ #generate from nextcloud tempalte betternas-controlplane/ #generate from nextcloud tempalte
exapps/ exapps/
sharing-service/ #golang/python sharing-service/ #golang/python
policy-service/ policy-service/

View file

@ -65,7 +65,7 @@ The initial scaffold will create clear top-level directories for infrastructure,
Initial structure: Initial structure:
- `docker/`: local orchestration and container assets - `docker/`: local orchestration and container assets
- `apps/ainas-controlplane/`: generated Nextcloud shell app - `apps/betternas-controlplane/`: generated Nextcloud shell app
- `exapps/control-plane/`: aiNAS control-plane service, packaged for Nextcloud-compatible dev flows - `exapps/control-plane/`: aiNAS control-plane service, packaged for Nextcloud-compatible dev flows
- `packages/contracts/`: shared schemas and API contracts - `packages/contracts/`: shared schemas and API contracts
- `docs/`: architecture and product model notes - `docs/`: architecture and product model notes
@ -105,7 +105,7 @@ Alternatives considered:
1. Add the proposal artifacts that establish the architecture and scaffold requirements. 1. Add the proposal artifacts that establish the architecture and scaffold requirements.
2. Create the top-level repository layout and a Docker Compose development environment. 2. Create the top-level repository layout and a Docker Compose development environment.
3. Generate the Nextcloud shell app into `apps/ainas-controlplane/`. 3. Generate the Nextcloud shell app into `apps/betternas-controlplane/`.
4. Scaffold the control-plane service and shared contracts package. 4. Scaffold the control-plane service and shared contracts package.
5. Verify local startup, service discovery, and basic health paths before implementing product features. 5. Verify local startup, service discovery, and basic health paths before implementing product features.

View file

@ -7,7 +7,7 @@
## 2. Nextcloud shell app scaffold ## 2. Nextcloud shell app scaffold
- [x] 2.1 Generate the aiNAS Nextcloud app scaffold into `apps/ainas-controlplane/` - [x] 2.1 Generate the aiNAS Nextcloud app scaffold into `apps/betternas-controlplane/`
- [x] 2.2 Configure the shell app with aiNAS branding, navigation entry points, and basic settings surface - [x] 2.2 Configure the shell app with aiNAS branding, navigation entry points, and basic settings surface
- [x] 2.3 Add an adapter layer in the shell app for calling aiNAS-owned service endpoints - [x] 2.3 Add an adapter layer in the shell app for calling aiNAS-owned service endpoints
- [x] 2.4 Verify the shell app installs and loads in the local Nextcloud runtime - [x] 2.4 Verify the shell app installs and loads in the local Nextcloud runtime

14
package-lock.json generated
View file

@ -1,20 +1,20 @@
{ {
"name": "ainas", "name": "betternas",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ainas", "name": "betternas",
"workspaces": [ "workspaces": [
"packages/*", "packages/*",
"exapps/*" "exapps/*"
] ]
}, },
"exapps/control-plane": { "exapps/control-plane": {
"name": "@ainas/control-plane", "name": "@betternas/control-plane",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@ainas/contracts": "file:../../packages/contracts" "@betternas/contracts": "file:../../packages/contracts"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.18.6", "@types/node": "^22.18.6",
@ -22,11 +22,11 @@
"typescript": "^5.9.3" "typescript": "^5.9.3"
} }
}, },
"node_modules/@ainas/contracts": { "node_modules/@betternas/contracts": {
"resolved": "packages/contracts", "resolved": "packages/contracts",
"link": true "link": true
}, },
"node_modules/@ainas/control-plane": { "node_modules/@betternas/control-plane": {
"resolved": "exapps/control-plane", "resolved": "exapps/control-plane",
"link": true "link": true
}, },
@ -604,7 +604,7 @@
"license": "MIT" "license": "MIT"
}, },
"packages/contracts": { "packages/contracts": {
"name": "@ainas/contracts", "name": "@betternas/contracts",
"version": "0.1.0" "version": "0.1.0"
} }
} }

View file

@ -1,5 +1,5 @@
{ {
"name": "ainas", "name": "betternas",
"private": true, "private": true,
"packageManager": "npm@10.9.7", "packageManager": "npm@10.9.7",
"workspaces": [ "workspaces": [
@ -7,9 +7,9 @@
"exapps/*" "exapps/*"
], ],
"scripts": { "scripts": {
"build": "npm run build --workspace @ainas/contracts && npm run build --workspace @ainas/control-plane", "build": "npm run build --workspace @betternas/contracts && npm run build --workspace @betternas/control-plane",
"typecheck": "npm run typecheck --workspace @ainas/contracts && npm run typecheck --workspace @ainas/control-plane", "typecheck": "npm run typecheck --workspace @betternas/contracts && npm run typecheck --workspace @betternas/control-plane",
"dev:control-plane": "npm run dev --workspace @ainas/control-plane" "dev:control-plane": "npm run dev --workspace @betternas/control-plane"
} }
} }

View file

@ -1,5 +1,5 @@
{ {
"name": "@ainas/contracts", "name": "@betternas/contracts",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"type": "module", "type": "module",

View file

@ -1,6 +1,6 @@
{ {
"$schema": "https://json-schema.org/draft/2020-12/schema", "$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ainas.local/schemas/control-plane-health.schema.json", "$id": "https://betternas.local/schemas/control-plane-health.schema.json",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": [ "required": [

View file

@ -1,6 +1,6 @@
{ {
"$schema": "https://json-schema.org/draft/2020-12/schema", "$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ainas.local/schemas/control-plane-version.schema.json", "$id": "https://betternas.local/schemas/control-plane-version.schema.json",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": [ "required": [

View file

@ -1,9 +1,9 @@
aiNAS repo aiNAS repo
│ apps/ainas-controlplane │ apps/betternas-controlplane
│ PHP Nextcloud app │ PHP Nextcloud app
│ - nav entry: "aiNAS" │ - nav entry: "aiNAS"
│ - page route: /index.php/apps/ainascontrolplane/ │ - page route: /index.php/apps/betternascontrolplane/
│ - OCS route: /ocs/v2.php/apps/ainascontrolplane/api/status │ - OCS route: /ocs/v2.php/apps/betternascontrolplane/api/status
│ - job today: call control-plane and render a thin shell │ - job today: call control-plane and render a thin shell
│ exapps/control-plane │ exapps/control-plane

View file

@ -4,7 +4,7 @@ set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
compose_file="$repo_root/docker/compose.dev.yml" compose_file="$repo_root/docker/compose.dev.yml"
app_source_dir="$repo_root/apps/ainas-controlplane" app_source_dir="$repo_root/apps/betternas-controlplane"
nextcloud_occ() { nextcloud_occ() {
docker compose -f "$compose_file" exec -T --user www-data --workdir /var/www/html nextcloud php occ "$@" docker compose -f "$compose_file" exec -T --user www-data --workdir /var/www/html nextcloud php occ "$@"
@ -17,13 +17,13 @@ nextcloud_is_installed() {
docker compose -f "$compose_file" up -d --build docker compose -f "$compose_file" up -d --build
docker compose -f "$compose_file" exec -T --user root nextcloud sh -lc ' docker compose -f "$compose_file" exec -T --user root nextcloud sh -lc '
mkdir -p /var/www/html/custom_apps/ainascontrolplane mkdir -p /var/www/html/custom_apps/betternascontrolplane
chown -R www-data:www-data /var/www/html/custom_apps chown -R www-data:www-data /var/www/html/custom_apps
' '
docker compose -f "$compose_file" cp "$app_source_dir/." nextcloud:/var/www/html/custom_apps/ainascontrolplane docker compose -f "$compose_file" cp "$app_source_dir/." nextcloud:/var/www/html/custom_apps/betternascontrolplane
docker compose -f "$compose_file" exec -T --user root nextcloud sh -lc ' docker compose -f "$compose_file" exec -T --user root nextcloud sh -lc '
chown -R www-data:www-data /var/www/html/custom_apps/ainascontrolplane chown -R www-data:www-data /var/www/html/custom_apps/betternascontrolplane
' '
echo "Waiting for Nextcloud command interface..." echo "Waiting for Nextcloud command interface..."
@ -60,7 +60,7 @@ if ! nextcloud_is_installed; then
fi fi
fi fi
nextcloud_occ app:enable --force ainascontrolplane >/dev/null nextcloud_occ app:enable --force betternascontrolplane >/dev/null
echo "Nextcloud: http://localhost:8080" echo "Nextcloud: http://localhost:8080"
echo "aiNAS control plane: http://localhost:3001" echo "aiNAS control plane: http://localhost:3001"