- Extension
+ {{t "registry-bridge.component.extension-details-modal.extension"}}
{{this.extension.category_name}}
@@ -28,40 +28,59 @@
{{/if}}
{{/if}}
-
Overview
+
{{t "registry-bridge.component.extension-details-modal.overview"}}
{{this.extension.description}}
{{html-safe this.extension.promotional_text}}
-
Details
+
{{t "registry-bridge.component.extension-details-modal.details"}}
-
Version
-
{{this.extension.current_bundle_version}}
+
{{t "registry-bridge.component.extension-details-modal.version"}}
+
{{n-a this.extension.current_bundle_version}}
-
Updated
-
{{this.extension.updatedAt}}
+
{{t "registry-bridge.component.extension-details-modal.updated"}}
+
{{n-a this.extension.updatedAt}}
-
Author
-
{{this.extension.publisher_name}}
+
{{t "registry-bridge.component.extension-details-modal.author"}}
+
{{n-a this.extension.publisher_name}}
+
+ {{#if this.extension.self_managed}}
+
+
{{t
+ "registry-bridge.component.extension-details-modal.self-managed"
+ }}
+ {{#if @options.viewSelfManagesInstallInstructions}}
+
How to install
+ {{/if}}
+
+ {{/if}}
+
diff --git a/addon/components/modals/self-managed-install-instructions.hbs b/addon/components/modals/self-managed-install-instructions.hbs
new file mode 100644
index 0000000..d606d7c
--- /dev/null
+++ b/addon/components/modals/self-managed-install-instructions.hbs
@@ -0,0 +1,40 @@
+
+
+
+ Begin the installation from the base directory of your Fleetbase instance. For comprehensive instructions, visit the
+
+
+ Fleetbase CLI GitHub page.
+
+
+ -
+
+ First,
+ generate a Fleetbase registry credentials token.
+
+
+ -
+
+
Install the Fleetbase CLI globally using npm:
+
npm i -g @fleetbase/cli
+
+
+ -
+
+
Set your authentication token with the CLI:
+
flb set-auth {YOUR_TOKEN}
+
+
+ -
+
+
Finally, install the extension using one of the following commands:
+
+
flb install fleetbase/{{@options.extension.slug}}
+
or
+
flb install {{@options.extension.public_id}}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addon/models/registry-extension.js b/addon/models/registry-extension.js
index f42b62e..8c18edc 100644
--- a/addon/models/registry-extension.js
+++ b/addon/models/registry-extension.js
@@ -63,8 +63,10 @@ export default class RegistryExtensionModel extends Model {
@attr('array') languages;
@attr('object') meta;
@attr('boolean') core_service;
+ @attr('boolean') self_managed;
@attr('boolean') is_purchased;
@attr('boolean') is_installed;
+ @attr('boolean') is_author;
@attr('string', { defaultValue: 'pending' }) status;
/** @dates */
diff --git a/addon/styles/registry-bridge-engine.css b/addon/styles/registry-bridge-engine.css
index 72e8c18..fc919df 100644
--- a/addon/styles/registry-bridge-engine.css
+++ b/addon/styles/registry-bridge-engine.css
@@ -140,3 +140,34 @@ body[data-theme='dark'] .extension-card-container > .extension-card-body-contain
margin-left: 2rem;
margin-bottom: 2rem;
}
+
+.self-managed-install-instructions {
+ list-style: decimal;
+ color: #000;
+ padding-left: 30px;
+ font-size: 0.85rem;
+ line-height: 1rem;
+}
+
+.self-managed-install-instructions > li {
+ padding-bottom: 1.5rem;
+}
+
+body[data-theme='dark'] .self-managed-install-instructions {
+ list-style: decimal;
+ color: #fff;
+ padding-left: 30px;
+}
+
+.self-managed-install-instructions code {
+ display: flex;
+ font-family: monospace;
+ padding: 0.25rem 0.75rem;
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 5%);
+ background-color: #1f2937;
+ border: 1px #374151 solid;
+ color: #86efac;
+ border-radius: 0.5rem;
+ font-size: 0.75rem;
+ line-height: 1rem;
+}
diff --git a/app/components/modals/self-managed-install-instructions.js b/app/components/modals/self-managed-install-instructions.js
new file mode 100644
index 0000000..3ec45df
--- /dev/null
+++ b/app/components/modals/self-managed-install-instructions.js
@@ -0,0 +1 @@
+export { default } from '@fleetbase/registry-bridge-engine/components/modals/self-managed-install-instructions';
diff --git a/composer.json b/composer.json
index 8fdc025..b55517e 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,6 @@
{
"name": "fleetbase/registry-bridge",
- "version": "0.0.11",
+ "version": "0.0.12",
"description": "Internal Bridge between Fleetbase API and Extensions Registry",
"keywords": [
"fleetbase-extension",
diff --git a/extension.json b/extension.json
index b7f7a57..1077ba6 100644
--- a/extension.json
+++ b/extension.json
@@ -1,6 +1,6 @@
{
"name": "Registry Bridge",
- "version": "0.0.11",
+ "version": "0.0.12",
"description": "Internal Bridge between Fleetbase API and Extensions Registry",
"repository": "https://github.com/fleetbase/registry-bridge",
"license": "AGPL-3.0-or-later",
diff --git a/package.json b/package.json
index d464f52..01c1ca5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@fleetbase/registry-bridge-engine",
- "version": "0.0.11",
+ "version": "0.0.12",
"description": "Internal Bridge between Fleetbase API and Extensions Registry",
"fleetbase": {
"route": "extensions"
@@ -40,7 +40,7 @@
"dependencies": {
"@babel/core": "^7.23.2",
"@fleetbase/ember-core": "^0.2.14",
- "@fleetbase/ember-ui": "^0.2.20",
+ "@fleetbase/ember-ui": "^0.2.21",
"@fortawesome/ember-fontawesome": "^2.0.0",
"@fortawesome/fontawesome-svg-core": "6.4.0",
"@fortawesome/free-solid-svg-icons": "6.4.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 32ffb2e..92e2c92 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -15,8 +15,8 @@ importers:
specifier: ^0.2.14
version: 0.2.14(@ember/test-helpers@3.2.0(ember-source@5.4.0(@babel/core@7.23.2)(@glimmer/component@1.1.2(@babel/core@7.23.2))(rsvp@4.8.5)(webpack@5.89.0))(webpack@5.89.0))(ember-source@5.4.0(@babel/core@7.23.2)(@glimmer/component@1.1.2(@babel/core@7.23.2))(rsvp@4.8.5)(webpack@5.89.0))(webpack@5.89.0)
'@fleetbase/ember-ui':
- specifier: ^0.2.20
- version: 0.2.20(@ember/test-helpers@3.2.0(ember-source@5.4.0(@babel/core@7.23.2)(@glimmer/component@1.1.2(@babel/core@7.23.2))(rsvp@4.8.5)(webpack@5.89.0))(webpack@5.89.0))(@glimmer/component@1.1.2(@babel/core@7.23.2))(@glimmer/tracking@1.1.2)(ember-source@5.4.0(@babel/core@7.23.2)(@glimmer/component@1.1.2(@babel/core@7.23.2))(rsvp@4.8.5)(webpack@5.89.0))(postcss@8.4.39)(rollup@2.79.1)(tracked-built-ins@3.3.0)(webpack@5.89.0)
+ specifier: ^0.2.21
+ version: 0.2.21(@ember/test-helpers@3.2.0(ember-source@5.4.0(@babel/core@7.23.2)(@glimmer/component@1.1.2(@babel/core@7.23.2))(rsvp@4.8.5)(webpack@5.89.0))(webpack@5.89.0))(@glimmer/component@1.1.2(@babel/core@7.23.2))(@glimmer/tracking@1.1.2)(ember-source@5.4.0(@babel/core@7.23.2)(@glimmer/component@1.1.2(@babel/core@7.23.2))(rsvp@4.8.5)(webpack@5.89.0))(postcss@8.4.39)(rollup@2.79.1)(tracked-built-ins@3.3.0)(webpack@5.89.0)
'@fortawesome/ember-fontawesome':
specifier: ^2.0.0
version: 2.0.0(ember-source@5.4.0(@babel/core@7.23.2)(@glimmer/component@1.1.2(@babel/core@7.23.2))(rsvp@4.8.5)(webpack@5.89.0))(rollup@2.79.1)(webpack@5.89.0)
@@ -1334,8 +1334,8 @@ packages:
resolution: {integrity: sha512-OdFY1QGG6NewnL51halLlxVQW7s93uY4lz3bk4rL8dDfoxQaPi3knXSu9yL9TrT3x3H+1z34ZseksVfUv6Wzyg==}
engines: {node: '>= 18'}
- '@fleetbase/ember-ui@0.2.20':
- resolution: {integrity: sha512-adbwNVV8huOurRiF+fJJB9JqJO+PdZc6v/zq16UC42PcZvXxncA94PqrL45jYBAhLWtrxJQih+rPVF+L5WnuhA==}
+ '@fleetbase/ember-ui@0.2.21':
+ resolution: {integrity: sha512-Ia2V4yjy0qk8p8HelACM0SFJdpi1HBwqyyr4e0g1lFUxrKLceGCHgDFXYoqNG+Q4lecFVZO0n2oFbFdYrh9hpg==}
engines: {node: '>= 18'}
'@floating-ui/core@1.6.4':
@@ -9712,7 +9712,7 @@ snapshots:
- utf-8-validate
- webpack
- '@fleetbase/ember-ui@0.2.20(@ember/test-helpers@3.2.0(ember-source@5.4.0(@babel/core@7.23.2)(@glimmer/component@1.1.2(@babel/core@7.23.2))(rsvp@4.8.5)(webpack@5.89.0))(webpack@5.89.0))(@glimmer/component@1.1.2(@babel/core@7.23.2))(@glimmer/tracking@1.1.2)(ember-source@5.4.0(@babel/core@7.23.2)(@glimmer/component@1.1.2(@babel/core@7.23.2))(rsvp@4.8.5)(webpack@5.89.0))(postcss@8.4.39)(rollup@2.79.1)(tracked-built-ins@3.3.0)(webpack@5.89.0)':
+ '@fleetbase/ember-ui@0.2.21(@ember/test-helpers@3.2.0(ember-source@5.4.0(@babel/core@7.23.2)(@glimmer/component@1.1.2(@babel/core@7.23.2))(rsvp@4.8.5)(webpack@5.89.0))(webpack@5.89.0))(@glimmer/component@1.1.2(@babel/core@7.23.2))(@glimmer/tracking@1.1.2)(ember-source@5.4.0(@babel/core@7.23.2)(@glimmer/component@1.1.2(@babel/core@7.23.2))(rsvp@4.8.5)(webpack@5.89.0))(postcss@8.4.39)(rollup@2.79.1)(tracked-built-ins@3.3.0)(webpack@5.89.0)':
dependencies:
'@babel/core': 7.23.2
'@ember/render-modifiers': 2.1.0(@babel/core@7.23.2)(ember-source@5.4.0(@babel/core@7.23.2)(@glimmer/component@1.1.2(@babel/core@7.23.2))(rsvp@4.8.5)(webpack@5.89.0))
diff --git a/server/migrations/2024_08_02_072214_add_self_managed_column_to_registry_extensions_table.php b/server/migrations/2024_08_02_072214_add_self_managed_column_to_registry_extensions_table.php
new file mode 100644
index 0000000..488516d
--- /dev/null
+++ b/server/migrations/2024_08_02_072214_add_self_managed_column_to_registry_extensions_table.php
@@ -0,0 +1,28 @@
+boolean('self_managed')->after('subtitle')->default(0);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('registry_extensions', function (Blueprint $table) {
+ $table->dropColumn('self_managed');
+ });
+ }
+};
diff --git a/server/src/Http/Controllers/Internal/v1/RegistryController.php b/server/src/Http/Controllers/Internal/v1/RegistryController.php
index 124f2e6..4b563cf 100644
--- a/server/src/Http/Controllers/Internal/v1/RegistryController.php
+++ b/server/src/Http/Controllers/Internal/v1/RegistryController.php
@@ -6,6 +6,7 @@
use Fleetbase\Http\Resources\Category as CategoryResource;
use Fleetbase\Models\Category;
use Fleetbase\RegistryBridge\Models\RegistryExtension;
+use Illuminate\Http\Request;
class RegistryController extends Controller
{
@@ -51,4 +52,49 @@ public function getInstalledEngines()
return response()->json($installedExtensions);
}
+
+ /**
+ * Lookup and retrieve package information based on the provided package name.
+ *
+ * This method handles a request to lookup a package by its name. It utilizes the `RegistryExtension::lookup` method to find the
+ * corresponding registry extension. If no extension is found or if the extension does not have valid package or composer data,
+ * an error response is returned.
+ *
+ * If a valid extension and its associated bundle are found, the function extracts the package and composer names from the
+ * `package.json` and `composer.json` metadata. These names are then returned in a JSON response.
+ *
+ * @param Request $request the incoming HTTP request containing the 'package' input parameter
+ *
+ * @return \Illuminate\Http\JsonResponse a JSON response containing the package and composer names if found, or an error message otherwise
+ */
+ public function lookupPackage(Request $request)
+ {
+ $packageName = $request->input('package');
+ $registryExtension = RegistryExtension::lookup($packageName);
+ if (!$registryExtension) {
+ return response()->error('No extension found by this name for install');
+ }
+
+ if (!$registryExtension->currentBundle) {
+ return response()->error('No valid package data found for this extension install');
+ }
+
+ $packageJson = $registryExtension->currentBundle->meta['package.json'];
+ if (!$packageJson) {
+ return response()->error('No valid package data found for this extension install');
+ }
+
+ $composerJson = $registryExtension->currentBundle->meta['composer.json'];
+ if (!$composerJson) {
+ return response()->error('No valid package data found for this extension install');
+ }
+
+ $packageJsonName = data_get($packageJson, 'name');
+ $composerJsonName = data_get($composerJson, 'name');
+
+ return response()->json([
+ 'npm' => $packageJsonName,
+ 'composer' => $composerJsonName,
+ ]);
+ }
}
diff --git a/server/src/Models/RegistryExtension.php b/server/src/Models/RegistryExtension.php
index 63893f2..604d1ff 100644
--- a/server/src/Models/RegistryExtension.php
+++ b/server/src/Models/RegistryExtension.php
@@ -15,6 +15,7 @@
use Fleetbase\Traits\HasPublicId;
use Fleetbase\Traits\HasUuid;
use Fleetbase\Traits\Searchable;
+use Illuminate\Support\Str;
use Spatie\Sluggable\HasSlug;
use Spatie\Sluggable\SlugOptions;
@@ -57,6 +58,7 @@ class RegistryExtension extends Model
'stripe_product_id',
'name',
'subtitle',
+ 'self_managed',
'payment_required',
'price',
'sale_price',
@@ -90,6 +92,7 @@ class RegistryExtension extends Model
* The attributes that should be cast to native types.
*/
protected $casts = [
+ 'self_managed' => 'boolean',
'payment_required' => 'boolean',
'on_sale' => 'boolean',
'subscription_required' => 'boolean',
@@ -121,6 +124,7 @@ class RegistryExtension extends Model
'publisher_name',
'is_purchased',
'is_installed',
+ 'is_author',
];
/**
@@ -413,6 +417,14 @@ public function getPublisherNameAttribute(): ?string
return data_get($this, 'company.name');
}
+ /**
+ * Determines if the current company session is the author of the extension.
+ */
+ public function getIsAuthorAttribute(): bool
+ {
+ return $this->company_uuid === session('company');
+ }
+
/**
* Finds a RegistryExtension by package name in the associated currentBundle.
*
@@ -433,6 +445,39 @@ public static function findByPackageName(string $packageName): ?RegistryExtensio
})->first();
}
+ /**
+ * Lookup a registry extension based on the given package name.
+ *
+ * This method attempts to find a `RegistryExtension` that matches the provided package name. It checks multiple fields including
+ * `uuid`, `public_id`, and `slug`. If the package name starts with 'fleetbase/', it also attempts to match the slug extracted from the package name.
+ *
+ * Additionally, the method checks for the existence of a related `currentBundle` where the `package.json` or `composer.json` metadata
+ * matches the provided package name.
+ *
+ * @param string $packageName the name, UUID, public ID, or slug of the package to lookup
+ *
+ * @return RegistryExtension|null returns the found `RegistryExtension` instance or `null` if no match is found
+ */
+ public static function lookup(string $packageName): ?RegistryExtension
+ {
+ return static::where('status', 'published')->where(function ($query) use ($packageName) {
+ $query->where('uuid', $packageName)
+ ->orWhere('public_id', $packageName)
+ ->orWhere('slug', $packageName);
+
+ // Check for fleetbase/ prefix and match slug
+ if (Str::startsWith($packageName, 'fleetbase/')) {
+ $packageSlug = explode('/', $packageName)[1] ?? null;
+ if ($packageSlug) {
+ $query->orWhere('slug', $packageSlug);
+ }
+ }
+ })->orWhereHas('currentBundle', function ($query) use ($packageName) {
+ $query->where('meta->package.json->name', $packageName)
+ ->orWhere('meta->composer.json->name', $packageName);
+ })->with(['currentBundle'])->first();
+ }
+
/**
* Determines if the current extension instance is ready for submission.
*
diff --git a/server/src/Models/RegistryExtensionBundle.php b/server/src/Models/RegistryExtensionBundle.php
index 8a4dcd9..d76832c 100644
--- a/server/src/Models/RegistryExtensionBundle.php
+++ b/server/src/Models/RegistryExtensionBundle.php
@@ -688,7 +688,7 @@ public function runInstallerProgress(): void
]);
// minimal latency
- usleep(500 * rand(2, 4));
+ usleep(50 * rand(1, 3));
}
}
}
@@ -708,7 +708,7 @@ public function runUninstallerProgress(): void
]);
// minimal latency
- usleep(500 * rand(2, 4));
+ usleep(50 * rand(1, 3));
}
}
}
diff --git a/server/src/routes.php b/server/src/routes.php
index b475c5a..034becc 100644
--- a/server/src/routes.php
+++ b/server/src/routes.php
@@ -12,7 +12,8 @@
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
-
+// Lookup package endpoint
+Route::get(config('internals.api.routing.prefix', '~registry') . '/v1/lookup', 'Fleetbase\RegistryBridge\Http\Controllers\Internal\v1\RegistryController@lookupPackage');
Route::prefix(config('internals.api.routing.prefix', '~registry'))->middleware(['fleetbase.registry'])->namespace('Fleetbase\RegistryBridge\Http\Controllers')->group(
function ($router) {
/*
diff --git a/tests/integration/components/modals/self-managed-install-instructions-test.js b/tests/integration/components/modals/self-managed-install-instructions-test.js
new file mode 100644
index 0000000..67288ca
--- /dev/null
+++ b/tests/integration/components/modals/self-managed-install-instructions-test.js
@@ -0,0 +1,26 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'dummy/tests/helpers';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+module('Integration | Component | modals/self-managed-install-instructions', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('it renders', async function (assert) {
+ // Set any properties with this.set('myProperty', 'value');
+ // Handle any actions with this.set('myAction', function(val) { ... });
+
+ await render(hbs`
`);
+
+ assert.dom().hasText('');
+
+ // Template block usage:
+ await render(hbs`
+
+ template block text
+
+ `);
+
+ assert.dom().hasText('template block text');
+ });
+});
diff --git a/translations/en-us.yaml b/translations/en-us.yaml
index 7aa39fc..5c90b37 100644
--- a/translations/en-us.yaml
+++ b/translations/en-us.yaml
@@ -9,6 +9,16 @@ registry-bridge:
about: About
about-extension: About {extensionName}
component:
+ extension-details-modal:
+ extension: Extension
+ author: Author
+ overview: Overview
+ details: Details
+ version: Version
+ updated: Updated
+ website: Website
+ self-managed: Self Managed
+ self-managed-help-text: A self-managed extension is designed for users who host Fleetbase on their own servers, outside of the cloud/SaaS environment. These extensions require manual installation and configuration. If you are using Fleetbase as a self-hosted instance, you can use these extensions to add additional features. However, they are not available for cloud/SaaS users.
extension-pending-publish-viewer:
content-panel-title: Extensions Pending Publish
focused-extension-title: >
@@ -116,4 +126,6 @@ registry-bridge:
extension-id: Extension ID
extension-id-help-text: The unique identifier for this extension.
bundles: Bundles
- upload-new-bundle: Upload new Bundle
\ No newline at end of file
+ upload-new-bundle: Upload new Bundle
+ self-managed: Self Managed
+ self-managed-help-text: Enable this option if your module is intended for self-hosted instances only. By selecting this, the module will not be available for installation on the cloud/SaaS version of Fleetbase and must be manually installed by the user on their own server.
\ No newline at end of file