@fleetbase/registry-bridge-engine 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/controllers/application.js +6 -1
- package/addon/controllers/developers/payments/settings.js +58 -0
- package/addon/routes/developers/payments/settings.js +23 -0
- package/addon/routes.js +1 -0
- package/addon/templates/application.hbs +28 -26
- package/addon/templates/developers/payments/index.hbs +9 -3
- package/addon/templates/developers/payments/onboard.hbs +1 -1
- package/addon/templates/developers/payments/settings.hbs +30 -0
- package/composer.json +1 -1
- package/config/environment.js +23 -0
- package/extension.json +1 -1
- package/package.json +3 -3
- package/server/migrations/2026_02_15_000001_create_registry_developer_accounts_table.php +44 -0
- package/server/migrations/2026_02_15_000002_add_account_type_to_registry_users_table.php +48 -0
- package/server/migrations/2026_02_15_000003_add_publisher_fields_to_registry_extensions_table.php +34 -0
- package/server/migrations/2026_02_15_100001_convert_purchases_to_polymorphic.php +45 -0
- package/server/migrations/2026_02_15_100002_add_purchaser_indexes.php +35 -0
- package/server/src/Http/Controllers/Internal/v1/RegistryAuthController.php +79 -29
- package/server/src/Http/Controllers/Internal/v1/RegistryDeveloperAccountController.php +355 -0
- package/server/src/Http/Controllers/Internal/v1/RegistryExtensionController.php +28 -0
- package/server/src/Http/Controllers/Internal/v1/RegistryPaymentsController.php +74 -3
- package/server/src/Models/RegistryDeveloperAccount.php +147 -0
- package/server/src/Models/RegistryExtensionPurchase.php +17 -0
- package/server/src/Models/RegistryUser.php +85 -14
- package/server/src/routes.php +19 -5
- package/translations/en-us.yaml +7 -0
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import Controller from '@ember/controller';
|
|
2
|
+
import config from '../config/environment';
|
|
2
3
|
|
|
3
|
-
export default class ApplicationController extends Controller {
|
|
4
|
+
export default class ApplicationController extends Controller {
|
|
5
|
+
get selfHostedRegistry() {
|
|
6
|
+
return config.registry.selfHosted === true;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import Controller from '@ember/controller';
|
|
2
|
+
import { inject as service } from '@ember/service';
|
|
3
|
+
import { tracked } from '@glimmer/tracking';
|
|
4
|
+
import { action } from '@ember/object';
|
|
5
|
+
import { loadConnectAndInitialize } from '@stripe/connect-js';
|
|
6
|
+
import config from '../../../config/environment';
|
|
7
|
+
|
|
8
|
+
export default class DevelopersPaymentsSettingsController extends Controller {
|
|
9
|
+
@service fetch;
|
|
10
|
+
@service notifications;
|
|
11
|
+
|
|
12
|
+
@tracked connectInstance;
|
|
13
|
+
@tracked accountManagementComponent;
|
|
14
|
+
@tracked isLoading = true;
|
|
15
|
+
|
|
16
|
+
@action
|
|
17
|
+
async setupStripe(element) {
|
|
18
|
+
try {
|
|
19
|
+
await this.initializeStripe();
|
|
20
|
+
if (this.accountManagementComponent) {
|
|
21
|
+
element.appendChild(this.accountManagementComponent);
|
|
22
|
+
}
|
|
23
|
+
this.isLoading = false;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
this.notifications.serverError(error);
|
|
26
|
+
this.isLoading = false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async fetchClientSecret() {
|
|
31
|
+
try {
|
|
32
|
+
const { clientSecret } = await this.fetch.post('payments/account-management-session', {}, { namespace: '~registry/v1' });
|
|
33
|
+
return clientSecret;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
this.notifications.serverError(error);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async initializeStripe() {
|
|
41
|
+
if (this.connectInstance) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this.connectInstance = loadConnectAndInitialize({
|
|
46
|
+
publishableKey: config.stripe.publishableKey,
|
|
47
|
+
fetchClientSecret: this.fetchClientSecret.bind(this),
|
|
48
|
+
appearance: {
|
|
49
|
+
overlays: 'dialog',
|
|
50
|
+
variables: {
|
|
51
|
+
colorPrimary: '#635BFF',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
this.accountManagementComponent = this.connectInstance.create('account-management');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import Route from '@ember/routing/route';
|
|
2
|
+
import { inject as service } from '@ember/service';
|
|
3
|
+
|
|
4
|
+
export default class DevelopersPaymentsSettingsRoute extends Route {
|
|
5
|
+
@service notifications;
|
|
6
|
+
@service hostRouter;
|
|
7
|
+
@service fetch;
|
|
8
|
+
@service intl;
|
|
9
|
+
|
|
10
|
+
async beforeModel() {
|
|
11
|
+
// Check if user has a Stripe Connect account before allowing access
|
|
12
|
+
try {
|
|
13
|
+
const { hasStripeConnectAccount } = await this.fetch.get('payments/has-stripe-connect-account', {}, { namespace: '~registry/v1' });
|
|
14
|
+
if (!hasStripeConnectAccount) {
|
|
15
|
+
this.notifications.warning(this.intl.t('registry-bridge.developers.payments.no-account-warning'));
|
|
16
|
+
return this.hostRouter.transitionTo('console.extensions.payments.onboard');
|
|
17
|
+
}
|
|
18
|
+
} catch (error) {
|
|
19
|
+
this.notifications.serverError(error);
|
|
20
|
+
return this.hostRouter.transitionTo('console.extensions.payments');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
package/addon/routes.js
CHANGED
|
@@ -23,32 +23,34 @@
|
|
|
23
23
|
</LinkTo>
|
|
24
24
|
{{/each}}
|
|
25
25
|
</Layout::Sidebar::Panel>
|
|
26
|
-
|
|
27
|
-
<Layout::Sidebar::
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
26
|
+
{{#if this.selfHostedRegistry}}
|
|
27
|
+
<Layout::Sidebar::Panel @open={{true}} @title="Developers">
|
|
28
|
+
<Layout::Sidebar::Item
|
|
29
|
+
@route="console.extensions.developers.extensions"
|
|
30
|
+
@icon="box-archive"
|
|
31
|
+
@permission="registry-bridge list extension-bundle"
|
|
32
|
+
@visible={{can "registry-bridge see extension-bundle"}}
|
|
33
|
+
>Extensions</Layout::Sidebar::Item>
|
|
34
|
+
<Layout::Sidebar::Item
|
|
35
|
+
@route="console.extensions.developers.analytics"
|
|
36
|
+
@icon="chart-simple"
|
|
37
|
+
@permission="registry-bridge list extension-analytic"
|
|
38
|
+
@visible={{can "registry-bridge see extension-analytic"}}
|
|
39
|
+
>Analytics</Layout::Sidebar::Item>
|
|
40
|
+
<Layout::Sidebar::Item
|
|
41
|
+
@route="console.extensions.developers.payments"
|
|
42
|
+
@icon="cash-register"
|
|
43
|
+
@permission="registry-bridge list extension-payment"
|
|
44
|
+
@visible={{can "registry-bridge see extension-payment"}}
|
|
45
|
+
>Payments</Layout::Sidebar::Item>
|
|
46
|
+
<Layout::Sidebar::Item
|
|
47
|
+
@route="console.extensions.developers.credentials"
|
|
48
|
+
@icon="key"
|
|
49
|
+
@permission="registry-bridge list registry-token"
|
|
50
|
+
@visible={{can "registry-bridge see registry-token"}}
|
|
51
|
+
>Credentials</Layout::Sidebar::Item>
|
|
52
|
+
</Layout::Sidebar::Panel>
|
|
53
|
+
{{/if}}
|
|
52
54
|
<Spacer @height="200px" />
|
|
53
55
|
</EmberWormhole>
|
|
54
56
|
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
<Layout::Section::Header @title="Payments">
|
|
2
2
|
{{#if this.hasStripeConnectAccount}}
|
|
3
|
-
<div class="flex
|
|
4
|
-
<
|
|
5
|
-
|
|
3
|
+
<div class="flex items-center space-x-4">
|
|
4
|
+
<LinkTo @route="developers.payments.settings" class="btn btn-primary">
|
|
5
|
+
<FaIcon @icon="cog" class="mr-1" />
|
|
6
|
+
{{t "registry-bridge.developers.payments.manage-account"}}
|
|
7
|
+
</LinkTo>
|
|
8
|
+
<div class="flex flex-row space-x-1">
|
|
9
|
+
<span class="text-sm text-black dark:text-white font-bold">Total Amount:</span>
|
|
10
|
+
<span class="text-sm text-black dark:text-white">{{format-currency @model.total_amount "USD"}}</span>
|
|
11
|
+
</div>
|
|
6
12
|
</div>
|
|
7
13
|
{{/if}}
|
|
8
14
|
</Layout::Section::Header>
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
</div>
|
|
39
39
|
<div
|
|
40
40
|
id="embedded-onboarding-container"
|
|
41
|
-
class="min-h-20 bg-gray-50 dark:bg-gray-100 shadow-md rounded-lg border border-gray-300 dark:border-gray-900 {{unless this.onboardInProgress 'hidden'}}"
|
|
41
|
+
class="p-4 min-h-20 bg-gray-50 dark:bg-gray-100 shadow-md rounded-lg border border-gray-300 dark:border-gray-900 {{unless this.onboardInProgress 'hidden'}}"
|
|
42
42
|
{{did-insert (fn this.createTrackedElement "embeddedOnboardingContainer")}}
|
|
43
43
|
>
|
|
44
44
|
</div>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<Layout::Section::Header @title={{t "registry-bridge.developers.payments.settings.title"}} />
|
|
2
|
+
|
|
3
|
+
<Layout::Section::Body class="overflow-y-scroll h-full">
|
|
4
|
+
<div class="container">
|
|
5
|
+
<div class="max-w-5xl mx-auto mt-4">
|
|
6
|
+
<div class="content">
|
|
7
|
+
<div class="mb-4">
|
|
8
|
+
<p class="text-sm text-gray-700 dark:text-gray-300">
|
|
9
|
+
{{t "registry-bridge.developers.payments.settings.description"}}
|
|
10
|
+
</p>
|
|
11
|
+
</div>
|
|
12
|
+
{{#if this.isLoading}}
|
|
13
|
+
<div class="flex items-center justify-center p-8">
|
|
14
|
+
<Spinner
|
|
15
|
+
@loadingMessage={{t "registry-bridge.developers.payments.settings.loading"}}
|
|
16
|
+
@loadingMessageClass="ml-2 text-black dark:text-white"
|
|
17
|
+
@wrapperClass="flex flex-row items-center"
|
|
18
|
+
/>
|
|
19
|
+
</div>
|
|
20
|
+
{{/if}}
|
|
21
|
+
<div
|
|
22
|
+
id="account-management-container"
|
|
23
|
+
{{did-insert this.setupStripe}}
|
|
24
|
+
class="p-4 min-h-20 bg-gray-50 dark:bg-gray-100 shadow-md rounded-lg border border-gray-300 dark:border-gray-900"
|
|
25
|
+
>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</Layout::Section::Body>
|
package/composer.json
CHANGED
package/config/environment.js
CHANGED
|
@@ -13,6 +13,9 @@ module.exports = function (environment) {
|
|
|
13
13
|
stripe: {
|
|
14
14
|
publishableKey: getenv('STRIPE_KEY'),
|
|
15
15
|
},
|
|
16
|
+
registry: {
|
|
17
|
+
selfHosted: toBoolean(getenv('SELF_HOSTED_REGISTRY', false)),
|
|
18
|
+
},
|
|
16
19
|
};
|
|
17
20
|
|
|
18
21
|
return ENV;
|
|
@@ -26,3 +29,23 @@ function getMountedEngineRoutePrefix() {
|
|
|
26
29
|
|
|
27
30
|
return `console.${mountedEngineRoutePrefix}.`;
|
|
28
31
|
}
|
|
32
|
+
|
|
33
|
+
function toBoolean(value) {
|
|
34
|
+
switch (value) {
|
|
35
|
+
case 'true':
|
|
36
|
+
case '1':
|
|
37
|
+
case 1:
|
|
38
|
+
case true:
|
|
39
|
+
return true;
|
|
40
|
+
case 'false':
|
|
41
|
+
case '0':
|
|
42
|
+
case 0:
|
|
43
|
+
case false:
|
|
44
|
+
case null:
|
|
45
|
+
case undefined:
|
|
46
|
+
case '':
|
|
47
|
+
return false;
|
|
48
|
+
default:
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
package/extension.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fleetbase/registry-bridge-engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Internal Bridge between Fleetbase API and Extensions Registry",
|
|
5
5
|
"fleetbase": {
|
|
6
6
|
"route": "extensions"
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@babel/core": "^7.23.2",
|
|
42
|
-
"@fleetbase/ember-core": "^0.3.
|
|
43
|
-
"@fleetbase/ember-ui": "^0.3.
|
|
42
|
+
"@fleetbase/ember-core": "^0.3.12",
|
|
43
|
+
"@fleetbase/ember-ui": "^0.3.21",
|
|
44
44
|
"@fortawesome/ember-fontawesome": "^2.0.0",
|
|
45
45
|
"@fortawesome/fontawesome-svg-core": "6.4.0",
|
|
46
46
|
"@fortawesome/free-brands-svg-icons": "6.4.0",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Illuminate\Database\Migrations\Migration;
|
|
4
|
+
use Illuminate\Database\Schema\Blueprint;
|
|
5
|
+
use Illuminate\Support\Facades\Schema;
|
|
6
|
+
|
|
7
|
+
return new class extends Migration
|
|
8
|
+
{
|
|
9
|
+
/**
|
|
10
|
+
* Run the migrations.
|
|
11
|
+
*/
|
|
12
|
+
public function up(): void
|
|
13
|
+
{
|
|
14
|
+
Schema::create('registry_developer_accounts', function (Blueprint $table) {
|
|
15
|
+
$table->increments('id');
|
|
16
|
+
$table->uuid('uuid')->unique()->index();
|
|
17
|
+
$table->string('username')->unique();
|
|
18
|
+
$table->string('email')->unique();
|
|
19
|
+
$table->string('password');
|
|
20
|
+
$table->string('name')->nullable();
|
|
21
|
+
$table->string('avatar_url')->nullable();
|
|
22
|
+
$table->string('github_username')->nullable();
|
|
23
|
+
$table->string('website')->nullable();
|
|
24
|
+
$table->text('bio')->nullable();
|
|
25
|
+
$table->timestamp('email_verified_at')->nullable();
|
|
26
|
+
$table->string('verification_token')->nullable();
|
|
27
|
+
$table->enum('status', ['active', 'suspended', 'pending_verification'])->default('pending_verification');
|
|
28
|
+
$table->timestamps();
|
|
29
|
+
$table->softDeletes();
|
|
30
|
+
|
|
31
|
+
$table->index('email');
|
|
32
|
+
$table->index('username');
|
|
33
|
+
$table->index('status');
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Reverse the migrations.
|
|
39
|
+
*/
|
|
40
|
+
public function down(): void
|
|
41
|
+
{
|
|
42
|
+
Schema::dropIfExists('registry_developer_accounts');
|
|
43
|
+
}
|
|
44
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Illuminate\Database\Migrations\Migration;
|
|
4
|
+
use Illuminate\Database\Schema\Blueprint;
|
|
5
|
+
use Illuminate\Support\Facades\Schema;
|
|
6
|
+
|
|
7
|
+
return new class extends Migration
|
|
8
|
+
{
|
|
9
|
+
/**
|
|
10
|
+
* Run the migrations.
|
|
11
|
+
*/
|
|
12
|
+
public function up(): void
|
|
13
|
+
{
|
|
14
|
+
Schema::table('registry_users', function (Blueprint $table) {
|
|
15
|
+
$table->enum('account_type', ['cloud', 'developer'])->default('cloud')->after('uuid');
|
|
16
|
+
$table->uuid('developer_account_uuid')->nullable()->after('account_type');
|
|
17
|
+
|
|
18
|
+
$table->foreign('developer_account_uuid')
|
|
19
|
+
->references('uuid')
|
|
20
|
+
->on('registry_developer_accounts')
|
|
21
|
+
->onDelete('cascade');
|
|
22
|
+
|
|
23
|
+
$table->index(['account_type', 'developer_account_uuid']);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Make company_uuid and user_uuid nullable since developer accounts won't have them
|
|
27
|
+
Schema::table('registry_users', function (Blueprint $table) {
|
|
28
|
+
$table->uuid('company_uuid')->nullable()->change();
|
|
29
|
+
$table->uuid('user_uuid')->nullable()->change();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Reverse the migrations.
|
|
35
|
+
*/
|
|
36
|
+
public function down(): void
|
|
37
|
+
{
|
|
38
|
+
Schema::table('registry_users', function (Blueprint $table) {
|
|
39
|
+
$table->dropForeign(['developer_account_uuid']);
|
|
40
|
+
$table->dropColumn(['account_type', 'developer_account_uuid']);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
Schema::table('registry_users', function (Blueprint $table) {
|
|
44
|
+
$table->uuid('company_uuid')->nullable(false)->change();
|
|
45
|
+
$table->uuid('user_uuid')->nullable(false)->change();
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
};
|
package/server/migrations/2026_02_15_000003_add_publisher_fields_to_registry_extensions_table.php
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Illuminate\Database\Migrations\Migration;
|
|
4
|
+
use Illuminate\Database\Schema\Blueprint;
|
|
5
|
+
use Illuminate\Support\Facades\Schema;
|
|
6
|
+
|
|
7
|
+
return new class extends Migration
|
|
8
|
+
{
|
|
9
|
+
/**
|
|
10
|
+
* Run the migrations.
|
|
11
|
+
*/
|
|
12
|
+
public function up(): void
|
|
13
|
+
{
|
|
14
|
+
Schema::table('registry_extensions', function (Blueprint $table) {
|
|
15
|
+
$table->enum('publisher_type', ['cloud', 'developer'])->default('cloud')->after('company_uuid');
|
|
16
|
+
$table->uuid('publisher_uuid')->nullable()->after('publisher_type');
|
|
17
|
+
|
|
18
|
+
$table->index(['publisher_type', 'publisher_uuid']);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Backfill existing extensions with publisher data from company_uuid
|
|
22
|
+
DB::statement('UPDATE registry_extensions SET publisher_type = "cloud", publisher_uuid = company_uuid WHERE publisher_uuid IS NULL');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Reverse the migrations.
|
|
27
|
+
*/
|
|
28
|
+
public function down(): void
|
|
29
|
+
{
|
|
30
|
+
Schema::table('registry_extensions', function (Blueprint $table) {
|
|
31
|
+
$table->dropColumn(['publisher_type', 'publisher_uuid']);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Illuminate\Database\Migrations\Migration;
|
|
4
|
+
use Illuminate\Database\Schema\Blueprint;
|
|
5
|
+
use Illuminate\Support\Facades\Schema;
|
|
6
|
+
|
|
7
|
+
return new class extends Migration
|
|
8
|
+
{
|
|
9
|
+
/**
|
|
10
|
+
* Run the migrations.
|
|
11
|
+
*
|
|
12
|
+
* @return void
|
|
13
|
+
*/
|
|
14
|
+
public function up()
|
|
15
|
+
{
|
|
16
|
+
Schema::table('registry_extension_purchases', function (Blueprint $table) {
|
|
17
|
+
// Add polymorphic columns using Fleetbase convention (subject)
|
|
18
|
+
$table->string('purchaser_uuid')->nullable()->after('uuid');
|
|
19
|
+
$table->string('purchaser_type')->nullable()->after('purchaser_uuid');
|
|
20
|
+
|
|
21
|
+
// Keep company_uuid for backward compatibility during migration
|
|
22
|
+
// Will be removed in a future migration after data migration
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Migrate existing data: convert company_uuid to polymorphic relationship
|
|
26
|
+
DB::table('registry_extension_purchases')
|
|
27
|
+
->whereNotNull('company_uuid')
|
|
28
|
+
->update([
|
|
29
|
+
'purchaser_uuid' => DB::raw('company_uuid'),
|
|
30
|
+
'purchaser_type' => 'Fleetbase\\Models\\Company'
|
|
31
|
+
]);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Reverse the migrations.
|
|
36
|
+
*
|
|
37
|
+
* @return void
|
|
38
|
+
*/
|
|
39
|
+
public function down()
|
|
40
|
+
{
|
|
41
|
+
Schema::table('registry_extension_purchases', function (Blueprint $table) {
|
|
42
|
+
$table->dropColumn(['purchaser_uuid', 'purchaser_type']);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Illuminate\Database\Migrations\Migration;
|
|
4
|
+
use Illuminate\Database\Schema\Blueprint;
|
|
5
|
+
use Illuminate\Support\Facades\Schema;
|
|
6
|
+
|
|
7
|
+
return new class extends Migration
|
|
8
|
+
{
|
|
9
|
+
/**
|
|
10
|
+
* Run the migrations.
|
|
11
|
+
*
|
|
12
|
+
* @return void
|
|
13
|
+
*/
|
|
14
|
+
public function up()
|
|
15
|
+
{
|
|
16
|
+
Schema::table('registry_extension_purchases', function (Blueprint $table) {
|
|
17
|
+
// Add indexes for polymorphic relationship queries
|
|
18
|
+
$table->index(['purchaser_uuid', 'purchaser_type'], 'purchases_purchaser_index');
|
|
19
|
+
$table->index(['extension_uuid', 'purchaser_uuid', 'purchaser_type'], 'purchases_extension_purchaser_index');
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Reverse the migrations.
|
|
25
|
+
*
|
|
26
|
+
* @return void
|
|
27
|
+
*/
|
|
28
|
+
public function down()
|
|
29
|
+
{
|
|
30
|
+
Schema::table('registry_extension_purchases', function (Blueprint $table) {
|
|
31
|
+
$table->dropIndex('purchases_purchaser_index');
|
|
32
|
+
$table->dropIndex('purchases_extension_purchaser_index');
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
};
|