@fleetbase/registry-bridge-engine 0.0.1
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/.php-cs-fixer.php +29 -0
- package/LICENSE.md +651 -0
- package/README.md +122 -0
- package/addon/adapters/registry-bridge.js +5 -0
- package/addon/adapters/registry-extension-bundle.js +1 -0
- package/addon/adapters/registry-extension.js +1 -0
- package/addon/components/extension-card.hbs +12 -0
- package/addon/components/extension-card.js +235 -0
- package/addon/components/extension-form.hbs +237 -0
- package/addon/components/extension-form.js +123 -0
- package/addon/components/extension-modal-title.hbs +14 -0
- package/addon/components/extension-modal-title.js +20 -0
- package/addon/components/extension-monetize-form.hbs +56 -0
- package/addon/components/extension-monetize-form.js +7 -0
- package/addon/components/extension-pending-publish-viewer.hbs +52 -0
- package/addon/components/extension-pending-publish-viewer.js +37 -0
- package/addon/components/extension-reviewer-control.hbs +68 -0
- package/addon/components/extension-reviewer-control.js +68 -0
- package/addon/components/modals/confirm-extension-purchase.hbs +5 -0
- package/addon/components/modals/confirm-extension-purchase.js +3 -0
- package/addon/components/modals/extension-details.hbs +69 -0
- package/addon/components/modals/extension-details.js +33 -0
- package/addon/components/modals/extension-purchase-form.hbs +5 -0
- package/addon/components/modals/extension-purchase-form.js +3 -0
- package/addon/components/modals/extension-uninstall.hbs +25 -0
- package/addon/components/modals/extension-uninstall.js +11 -0
- package/addon/components/modals/select-extension-bundle.hbs +43 -0
- package/addon/components/modals/select-extension-bundle.js +31 -0
- package/addon/components/progress-bar.hbs +12 -0
- package/addon/components/progress-bar.js +8 -0
- package/addon/controllers/application.js +6 -0
- package/addon/controllers/developers/analytics.js +26 -0
- package/addon/controllers/developers/extensions/edit/bundles.js +70 -0
- package/addon/controllers/developers/extensions/edit/index.js +3 -0
- package/addon/controllers/developers/extensions/edit/monetize.js +7 -0
- package/addon/controllers/developers/extensions/edit.js +107 -0
- package/addon/controllers/developers/extensions/index.js +3 -0
- package/addon/controllers/developers/extensions/new.js +32 -0
- package/addon/controllers/developers/payments/index.js +39 -0
- package/addon/controllers/developers/payments/onboard.js +67 -0
- package/addon/controllers/explore/category.js +22 -0
- package/addon/controllers/explore/index.js +15 -0
- package/addon/controllers/installed.js +86 -0
- package/addon/controllers/purchased.js +18 -0
- package/addon/engine.js +44 -0
- package/addon/models/registry-extension-bundle.js +62 -0
- package/addon/models/registry-extension.js +215 -0
- package/addon/routes/application.js +12 -0
- package/addon/routes/developers/analytics.js +10 -0
- package/addon/routes/developers/credentials.js +3 -0
- package/addon/routes/developers/extensions/edit/bundles.js +21 -0
- package/addon/routes/developers/extensions/edit/details.js +3 -0
- package/addon/routes/developers/extensions/edit/index.js +3 -0
- package/addon/routes/developers/extensions/edit/monetize.js +3 -0
- package/addon/routes/developers/extensions/edit.js +18 -0
- package/addon/routes/developers/extensions/index.js +10 -0
- package/addon/routes/developers/extensions/new.js +3 -0
- package/addon/routes/developers/extensions.js +3 -0
- package/addon/routes/developers/payments/index.js +26 -0
- package/addon/routes/developers/payments/onboard.js +21 -0
- package/addon/routes/developers/payments.js +3 -0
- package/addon/routes/developers.js +3 -0
- package/addon/routes/explore/category.js +27 -0
- package/addon/routes/explore/index.js +17 -0
- package/addon/routes/explore.js +3 -0
- package/addon/routes/installed.js +10 -0
- package/addon/routes/purchased.js +10 -0
- package/addon/routes.js +28 -0
- package/addon/serializers/registry-extension-bundle.js +15 -0
- package/addon/serializers/registry-extension.js +21 -0
- package/addon/services/stripe.js +83 -0
- package/addon/styles/registry-bridge-engine.css +142 -0
- package/addon/templates/application.hbs +26 -0
- package/addon/templates/developers/analytics.hbs +83 -0
- package/addon/templates/developers/credentials.hbs +1 -0
- package/addon/templates/developers/extensions/edit/bundles.hbs +71 -0
- package/addon/templates/developers/extensions/edit/details.hbs +16 -0
- package/addon/templates/developers/extensions/edit/index.hbs +1 -0
- package/addon/templates/developers/extensions/edit/monetize.hbs +3 -0
- package/addon/templates/developers/extensions/edit.hbs +48 -0
- package/addon/templates/developers/extensions/index.hbs +27 -0
- package/addon/templates/developers/extensions/new.hbs +39 -0
- package/addon/templates/developers/extensions.hbs +1 -0
- package/addon/templates/developers/payments/index.hbs +33 -0
- package/addon/templates/developers/payments/onboard.hbs +48 -0
- package/addon/templates/developers/payments.hbs +1 -0
- package/addon/templates/developers.hbs +1 -0
- package/addon/templates/explore/category.hbs +12 -0
- package/addon/templates/explore/index.hbs +12 -0
- package/addon/templates/explore.hbs +1 -0
- package/addon/templates/installed.hbs +32 -0
- package/addon/templates/purchased.hbs +34 -0
- package/app/adapters/registry-bridge.js +1 -0
- package/app/adapters/registry-extension-bundle.js +1 -0
- package/app/adapters/registry-extension.js +1 -0
- package/app/components/extension-card.js +1 -0
- package/app/components/extension-form.js +1 -0
- package/app/components/extension-modal-title.js +1 -0
- package/app/components/extension-monetize-form.js +1 -0
- package/app/components/extension-pending-publish-viewer.js +1 -0
- package/app/components/extension-reviewer-control.js +1 -0
- package/app/components/modals/confirm-extension-purchase.js +1 -0
- package/app/components/modals/extension-details.js +1 -0
- package/app/components/modals/extension-purchase-form.js +1 -0
- package/app/components/modals/extension-uninstall.js +1 -0
- package/app/components/modals/select-extension-bundle.js +1 -0
- package/app/components/progress-bar.js +1 -0
- package/app/controllers/application.js +1 -0
- package/app/controllers/developers/analytics.js +1 -0
- package/app/controllers/developers/extensions/edit/bundles.js +1 -0
- package/app/controllers/developers/extensions/edit/index.js +1 -0
- package/app/controllers/developers/extensions/edit/monetize.js +1 -0
- package/app/controllers/developers/extensions/edit.js +1 -0
- package/app/controllers/developers/extensions/index.js +1 -0
- package/app/controllers/developers/extensions/new.js +1 -0
- package/app/controllers/developers/payments/index.js +1 -0
- package/app/controllers/developers/payments/onboard.js +1 -0
- package/app/controllers/explore/category.js +1 -0
- package/app/controllers/explore/index.js +1 -0
- package/app/controllers/installed.js +1 -0
- package/app/controllers/purchased.js +1 -0
- package/app/models/registry-extension-bundle.js +1 -0
- package/app/models/registry-extension.js +1 -0
- package/app/routes/developers/analytics.js +1 -0
- package/app/routes/developers/credentials.js +1 -0
- package/app/routes/developers/extensions/edit/bundles.js +1 -0
- package/app/routes/developers/extensions/edit/details.js +1 -0
- package/app/routes/developers/extensions/edit/index.js +1 -0
- package/app/routes/developers/extensions/edit/monetize.js +1 -0
- package/app/routes/developers/extensions/edit.js +1 -0
- package/app/routes/developers/extensions/index.js +1 -0
- package/app/routes/developers/extensions/new.js +1 -0
- package/app/routes/developers/extensions.js +1 -0
- package/app/routes/developers/payments/index.js +1 -0
- package/app/routes/developers/payments/onboard.js +1 -0
- package/app/routes/developers/payments.js +1 -0
- package/app/routes/developers.js +1 -0
- package/app/routes/explore/category.js +1 -0
- package/app/routes/explore/index.js +1 -0
- package/app/routes/explore.js +1 -0
- package/app/routes/installed.js +1 -0
- package/app/routes/purchased.js +1 -0
- package/app/serializers/registry-extension-bundle.js +1 -0
- package/app/serializers/registry-extension.js +1 -0
- package/app/services/stripe.js +1 -0
- package/app/templates/developers/analytics.js +1 -0
- package/app/templates/developers/credentials.js +1 -0
- package/app/templates/developers/extensions/edit/bundles.js +1 -0
- package/app/templates/developers/extensions/edit/details.js +1 -0
- package/app/templates/developers/extensions/edit/index.js +1 -0
- package/app/templates/developers/extensions/edit/monetize.js +1 -0
- package/app/templates/developers/extensions/edit.js +1 -0
- package/app/templates/developers/extensions/index.js +1 -0
- package/app/templates/developers/extensions/new.js +1 -0
- package/app/templates/developers/extensions.js +1 -0
- package/app/templates/developers/payments/index.js +1 -0
- package/app/templates/developers/payments/onboard.js +1 -0
- package/app/templates/developers/payments.js +1 -0
- package/app/templates/developers.js +1 -0
- package/app/templates/explore/category.js +1 -0
- package/app/templates/explore/index.js +1 -0
- package/app/templates/explore.js +1 -0
- package/app/templates/installed.js +1 -0
- package/app/templates/purchased.js +1 -0
- package/composer.json +95 -0
- package/config/environment.js +28 -0
- package/extension.json +10 -0
- package/index.js +26 -0
- package/package.json +129 -0
- package/phpstan.neon.dist +8 -0
- package/phpunit.xml.dist +16 -0
- package/server/.gitattributes +14 -0
- package/server/config/registry-bridge.php +32 -0
- package/server/migrations/2024_03_19_060627_create_registry_users_table.php +42 -0
- package/server/migrations/2024_03_21_051614_create_registry_extensions_table.php +76 -0
- package/server/migrations/2024_03_25_044537_create_registry_extension_bundles_table.php +54 -0
- package/server/migrations/2024_03_29_072101_registry_extension_installs.php +35 -0
- package/server/migrations/2024_07_16_155000_create_registry_extension_purchases.php +41 -0
- package/server/seeders/ExtensionsCategorySeeder.php +359 -0
- package/server/src/Console/Commands/Initialize.php +35 -0
- package/server/src/Console/Commands/PostInstallExtension.php +84 -0
- package/server/src/Exceptions/InstallFailedException.php +21 -0
- package/server/src/Expansions/CategoryExpansion.php +30 -0
- package/server/src/Http/Controllers/Internal/v1/ExtensionInstallerController.php +153 -0
- package/server/src/Http/Controllers/Internal/v1/RegistryAuthController.php +230 -0
- package/server/src/Http/Controllers/Internal/v1/RegistryController.php +54 -0
- package/server/src/Http/Controllers/Internal/v1/RegistryExtensionBundleController.php +112 -0
- package/server/src/Http/Controllers/Internal/v1/RegistryExtensionController.php +257 -0
- package/server/src/Http/Controllers/Internal/v1/RegistryPaymentsController.php +227 -0
- package/server/src/Http/Controllers/RegistryBridgeController.php +13 -0
- package/server/src/Http/Filter/RegistryExtensionFilter.php +80 -0
- package/server/src/Http/Requests/AddRegistryUserRequest.php +47 -0
- package/server/src/Http/Requests/AuthenticateRegistryUserRequest.php +47 -0
- package/server/src/Http/Requests/CreateRegistryExtensionBundleRequest.php +42 -0
- package/server/src/Http/Requests/CreateRegistryExtensionRequest.php +31 -0
- package/server/src/Http/Requests/InstallExtensionRequest.php +30 -0
- package/server/src/Http/Requests/RegistryAuthRequest.php +46 -0
- package/server/src/Http/Requests/RegistryExtensionActionRequest.php +30 -0
- package/server/src/Http/Resources/RegistryUser.php +40 -0
- package/server/src/Models/RegistryExtension.php +656 -0
- package/server/src/Models/RegistryExtensionBundle.php +1015 -0
- package/server/src/Models/RegistryExtensionInstall.php +76 -0
- package/server/src/Models/RegistryExtensionPurchase.php +87 -0
- package/server/src/Models/RegistryUser.php +140 -0
- package/server/src/Providers/RegistryBridgeServiceProvider.php +117 -0
- package/server/src/Support/Bridge.php +53 -0
- package/server/src/Support/Utils.php +19 -0
- package/server/src/routes.php +58 -0
- package/server/tests/Feature.php +5 -0
- package/translations/en-us.yaml +119 -0
- package/tsconfig.declarations.json +10 -0
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\RegistryBridge\Models;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\Casts\Json;
|
|
6
|
+
use Fleetbase\Casts\Money;
|
|
7
|
+
use Fleetbase\Models\Category;
|
|
8
|
+
use Fleetbase\Models\Company;
|
|
9
|
+
use Fleetbase\Models\File;
|
|
10
|
+
use Fleetbase\Models\Model;
|
|
11
|
+
use Fleetbase\Models\User;
|
|
12
|
+
use Fleetbase\RegistryBridge\Support\Utils;
|
|
13
|
+
use Fleetbase\Support\Utils as SupportUtils;
|
|
14
|
+
use Fleetbase\Traits\HasApiModelBehavior;
|
|
15
|
+
use Fleetbase\Traits\HasMetaAttributes;
|
|
16
|
+
use Fleetbase\Traits\HasPublicId;
|
|
17
|
+
use Fleetbase\Traits\HasUuid;
|
|
18
|
+
use Fleetbase\Traits\Searchable;
|
|
19
|
+
use Spatie\Sluggable\HasSlug;
|
|
20
|
+
use Spatie\Sluggable\SlugOptions;
|
|
21
|
+
|
|
22
|
+
class RegistryExtension extends Model
|
|
23
|
+
{
|
|
24
|
+
use HasUuid;
|
|
25
|
+
use HasPublicId;
|
|
26
|
+
use HasMetaAttributes;
|
|
27
|
+
use HasApiModelBehavior;
|
|
28
|
+
use HasSlug;
|
|
29
|
+
use Searchable;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The database table used by the model.
|
|
33
|
+
*
|
|
34
|
+
* @var string
|
|
35
|
+
*/
|
|
36
|
+
protected $table = 'registry_extensions';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The type of public Id to generate.
|
|
40
|
+
*
|
|
41
|
+
* @var string
|
|
42
|
+
*/
|
|
43
|
+
protected $publicIdType = 'extension';
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The attributes that are mass assignable.
|
|
47
|
+
*/
|
|
48
|
+
protected $fillable = [
|
|
49
|
+
'uuid',
|
|
50
|
+
'company_uuid',
|
|
51
|
+
'created_by_uuid',
|
|
52
|
+
'category_uuid',
|
|
53
|
+
'registry_user_uuid',
|
|
54
|
+
'current_bundle_uuid',
|
|
55
|
+
'next_bundle_uuid',
|
|
56
|
+
'icon_uuid',
|
|
57
|
+
'public_id',
|
|
58
|
+
'stripe_product_id',
|
|
59
|
+
'name',
|
|
60
|
+
'subtitle',
|
|
61
|
+
'payment_required',
|
|
62
|
+
'price',
|
|
63
|
+
'sale_price',
|
|
64
|
+
'on_sale',
|
|
65
|
+
'subscription_required',
|
|
66
|
+
'subscription_billing_period',
|
|
67
|
+
'subscription_model',
|
|
68
|
+
'subscription_amount',
|
|
69
|
+
'subscription_tiers',
|
|
70
|
+
'currency',
|
|
71
|
+
'slug',
|
|
72
|
+
'version',
|
|
73
|
+
'fa_icon',
|
|
74
|
+
'description',
|
|
75
|
+
'promotional_text',
|
|
76
|
+
'website_url',
|
|
77
|
+
'repo_url',
|
|
78
|
+
'support_url',
|
|
79
|
+
'privacy_policy_url',
|
|
80
|
+
'tos_url',
|
|
81
|
+
'copyright',
|
|
82
|
+
'primary_language',
|
|
83
|
+
'tags',
|
|
84
|
+
'languages',
|
|
85
|
+
'meta',
|
|
86
|
+
'core_extension',
|
|
87
|
+
'status',
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The attributes that should be cast to native types.
|
|
92
|
+
*/
|
|
93
|
+
protected $casts = [
|
|
94
|
+
'payment_required' => 'boolean',
|
|
95
|
+
'on_sale' => 'boolean',
|
|
96
|
+
'subscription_required' => 'boolean',
|
|
97
|
+
'subscription_tiers' => Json::class,
|
|
98
|
+
'tags' => Json::class,
|
|
99
|
+
'languages' => Json::class,
|
|
100
|
+
'meta' => Json::class,
|
|
101
|
+
'core_extension' => 'boolean',
|
|
102
|
+
'price' => Money::class,
|
|
103
|
+
'sale_price' => Money::class,
|
|
104
|
+
'subscription_amount' => Money::class,
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Dynamic attributes that are appended to object.
|
|
109
|
+
*
|
|
110
|
+
* @var array
|
|
111
|
+
*/
|
|
112
|
+
protected $appends = [
|
|
113
|
+
'icon_url',
|
|
114
|
+
'current_bundle_filename',
|
|
115
|
+
'current_bundle_id',
|
|
116
|
+
'current_bundle_public_id',
|
|
117
|
+
'next_bundle_filename',
|
|
118
|
+
'next_bundle_id',
|
|
119
|
+
'next_bundle_public_id',
|
|
120
|
+
'category_name',
|
|
121
|
+
'publisher_name',
|
|
122
|
+
'is_purchased',
|
|
123
|
+
'is_installed',
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Relations that should be loaded with model.
|
|
128
|
+
*
|
|
129
|
+
* @var array
|
|
130
|
+
*/
|
|
131
|
+
protected $with = [
|
|
132
|
+
'category',
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Relations that should not be loaded.
|
|
137
|
+
*
|
|
138
|
+
* @var array
|
|
139
|
+
*/
|
|
140
|
+
protected $without = [
|
|
141
|
+
'current_bundle',
|
|
142
|
+
'next_bundle',
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Searchable columns.
|
|
147
|
+
*
|
|
148
|
+
* @var array
|
|
149
|
+
*/
|
|
150
|
+
protected $searchableColumns = ['name'];
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The "booting" method of the model.
|
|
154
|
+
*
|
|
155
|
+
* This method is called on the model boot and sets up
|
|
156
|
+
* event listeners, such as creating a unique bundle ID
|
|
157
|
+
* when a new model instance is being created.
|
|
158
|
+
*/
|
|
159
|
+
protected static function boot()
|
|
160
|
+
{
|
|
161
|
+
parent::boot();
|
|
162
|
+
|
|
163
|
+
static::saving(function ($model) {
|
|
164
|
+
if ($model->isDirty('price')) {
|
|
165
|
+
$model->updateOrCreateStripePrice();
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get the options for generating the slug.
|
|
172
|
+
*/
|
|
173
|
+
public function getSlugOptions(): SlugOptions
|
|
174
|
+
{
|
|
175
|
+
return SlugOptions::create()
|
|
176
|
+
->generateSlugsFrom('name')
|
|
177
|
+
->saveSlugsTo('slug');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
182
|
+
*/
|
|
183
|
+
public function company()
|
|
184
|
+
{
|
|
185
|
+
return $this->belongsTo(Company::class, 'company_uuid', 'uuid');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
190
|
+
*/
|
|
191
|
+
public function createdBy()
|
|
192
|
+
{
|
|
193
|
+
return $this->belongsTo(User::class, 'created_by_uuid', 'uuid');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
198
|
+
*/
|
|
199
|
+
public function registryUser()
|
|
200
|
+
{
|
|
201
|
+
return $this->belongsTo(RegistryUser::class, 'registry_user_uuid', 'uuid');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
206
|
+
*/
|
|
207
|
+
public function currentBundle()
|
|
208
|
+
{
|
|
209
|
+
return $this->belongsTo(RegistryExtensionBundle::class, 'current_bundle_uuid', 'uuid');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
214
|
+
*/
|
|
215
|
+
public function nextBundle()
|
|
216
|
+
{
|
|
217
|
+
return $this->belongsTo(RegistryExtensionBundle::class, 'next_bundle_uuid', 'uuid');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
222
|
+
*/
|
|
223
|
+
public function icon()
|
|
224
|
+
{
|
|
225
|
+
return $this->belongsTo(File::class, 'icon_uuid', 'uuid');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
230
|
+
*/
|
|
231
|
+
public function category()
|
|
232
|
+
{
|
|
233
|
+
return $this->belongsTo(Category::class, 'category_uuid', 'uuid');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
238
|
+
*/
|
|
239
|
+
public function screenshots()
|
|
240
|
+
{
|
|
241
|
+
return $this->hasMany(File::class, 'subject_uuid', 'uuid')->where('type', 'extension_screenshot');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
246
|
+
*/
|
|
247
|
+
public function bundles()
|
|
248
|
+
{
|
|
249
|
+
return $this->hasMany(RegistryExtensionBundle::class, 'extension_uuid', 'uuid');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
254
|
+
*/
|
|
255
|
+
public function bundleFiles()
|
|
256
|
+
{
|
|
257
|
+
return $this->hasMany(File::class, 'subject_uuid', 'uuid')->where('type', 'extension_bundle');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
262
|
+
*/
|
|
263
|
+
public function installs()
|
|
264
|
+
{
|
|
265
|
+
return $this->hasMany(RegistryExtensionInstall::class, 'extension_uuid', 'uuid');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
270
|
+
*/
|
|
271
|
+
public function purchases()
|
|
272
|
+
{
|
|
273
|
+
return $this->hasMany(RegistryExtensionPurchase::class, 'extension_uuid', 'uuid');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Check if current session has purchased extension.
|
|
278
|
+
*/
|
|
279
|
+
public function getIsPurchasedAttribute(): bool
|
|
280
|
+
{
|
|
281
|
+
return $this->purchases->contains(function ($purchase) {
|
|
282
|
+
return $purchase->company_uuid === session('company');
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Check if current session has installed extension.
|
|
288
|
+
*/
|
|
289
|
+
public function getIsInstalledAttribute(): bool
|
|
290
|
+
{
|
|
291
|
+
return $this->installs->contains(function ($purchase) {
|
|
292
|
+
return $purchase->company_uuid === session('company');
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Get avatar URL attribute.
|
|
298
|
+
*/
|
|
299
|
+
public function getIconUrlAttribute(): ?string
|
|
300
|
+
{
|
|
301
|
+
if ($this->icon instanceof File) {
|
|
302
|
+
return $this->icon->url;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return data_get($this, 'icon.url', 'https://flb-assets.s3.ap-southeast-1.amazonaws.com/static/default-extension-icon.svg');
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Get the current bundle public ID.
|
|
310
|
+
*/
|
|
311
|
+
public function getCurrentBundlePublicIdAttribute(): ?string
|
|
312
|
+
{
|
|
313
|
+
if ($this->currentBundle instanceof RegistryExtensionBundle) {
|
|
314
|
+
return $this->currentBundle->public_id;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return data_get($this, 'currentBundle.public_id');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Get the current bundle ID.
|
|
322
|
+
*/
|
|
323
|
+
public function getCurrentBundleIdAttribute(): ?string
|
|
324
|
+
{
|
|
325
|
+
if ($this->currentBundle instanceof RegistryExtensionBundle) {
|
|
326
|
+
return $this->currentBundle->bundle_id;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return data_get($this, 'currentBundle.bundle_id');
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Get the current bundle original filename.
|
|
334
|
+
*/
|
|
335
|
+
public function getCurrentBundleFilenameAttribute(): ?string
|
|
336
|
+
{
|
|
337
|
+
if ($this->currentBundle instanceof RegistryExtensionBundle) {
|
|
338
|
+
return $this->currentBundle->bundle_filename;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return data_get($this, 'currentBundle.bundle_filename');
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Get the next bundle public ID.
|
|
346
|
+
*/
|
|
347
|
+
public function getNextBundlePublicIdAttribute(): ?string
|
|
348
|
+
{
|
|
349
|
+
if ($this->nextBundle instanceof RegistryExtensionBundle) {
|
|
350
|
+
return $this->nextBundle->public_id;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return data_get($this, 'nextBundle.public_id');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Get the next bundle ID.
|
|
358
|
+
*/
|
|
359
|
+
public function getNextBundleIdAttribute(): ?string
|
|
360
|
+
{
|
|
361
|
+
if ($this->nextBundle instanceof RegistryExtensionBundle) {
|
|
362
|
+
return $this->nextBundle->bundle_id;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return data_get($this, 'nextBundle.bundle_id');
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Get the current bundle original filename.
|
|
370
|
+
*/
|
|
371
|
+
public function getNextBundleFilenameAttribute(): ?string
|
|
372
|
+
{
|
|
373
|
+
if ($this->nextBundle instanceof RegistryExtensionBundle) {
|
|
374
|
+
return $this->nextBundle->bundle_filename;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return data_get($this, 'nextBundle.bundle_filename');
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Get the extension's category.
|
|
382
|
+
*/
|
|
383
|
+
public function getCategoryNameAttribute(): ?string
|
|
384
|
+
{
|
|
385
|
+
if ($this->category instanceof Category) {
|
|
386
|
+
return $this->category->name;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return data_get($this, 'category.name');
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Get the extension's category.
|
|
394
|
+
*/
|
|
395
|
+
public function getPublisherNameAttribute(): ?string
|
|
396
|
+
{
|
|
397
|
+
if ($this->company instanceof Company) {
|
|
398
|
+
return $this->company->name;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return data_get($this, 'company.name');
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Finds a RegistryExtension by package name in the associated currentBundle.
|
|
406
|
+
*
|
|
407
|
+
* This method searches for a RegistryExtension where the associated currentBundle's
|
|
408
|
+
* 'meta' JSON column contains the specified package name either in the 'api' field
|
|
409
|
+
* or the 'engine' field. It returns the first matching RegistryExtension or null
|
|
410
|
+
* if no matches are found. The method leverages Eloquent's relationship querying
|
|
411
|
+
* capabilities to efficiently filter the results.
|
|
412
|
+
*
|
|
413
|
+
* @param string $packageName the name of the package to search for in the 'api' or 'engine' fields
|
|
414
|
+
*
|
|
415
|
+
* @return RegistryExtension|null the first RegistryExtension that matches the search criteria, or null if no match is found
|
|
416
|
+
*/
|
|
417
|
+
public static function findByPackageName(string $packageName): ?RegistryExtension
|
|
418
|
+
{
|
|
419
|
+
return static::whereHas('currentBundle', function ($query) use ($packageName) {
|
|
420
|
+
$query->where('meta->package.json->name', $packageName)->orWhere('meta->composer.json->name', $packageName);
|
|
421
|
+
})->first();
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Determines if the current extension instance is ready for submission.
|
|
426
|
+
*
|
|
427
|
+
* This method is an instance method that internally calls the static method
|
|
428
|
+
* `isExtensionReadyForSubmission` to perform the validation on the current instance.
|
|
429
|
+
* It checks various fields of the extension for certain criteria such as minimum
|
|
430
|
+
* string lengths, presence of necessary fields, and URL validation.
|
|
431
|
+
*
|
|
432
|
+
* @return bool returns true if the extension instance passes all validations, false otherwise
|
|
433
|
+
*/
|
|
434
|
+
public function isReadyForSubmission(): bool
|
|
435
|
+
{
|
|
436
|
+
return static::isExtensionReadyForSubmission($this);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Validates if an extension, identified by its ID or instance, is ready for submission.
|
|
441
|
+
*
|
|
442
|
+
* This method accepts either an extension ID or an instance of `RegistryExtension`. It then
|
|
443
|
+
* performs various validations on fields like 'name', 'description', 'tags', etc., to
|
|
444
|
+
* determine if the extension meets the criteria for submission. URL fields are validated
|
|
445
|
+
* to ensure they contain proper URLs. The method is designed to be flexible, handling
|
|
446
|
+
* validation for both an extension ID and an extension object.
|
|
447
|
+
*
|
|
448
|
+
* @param int|RegistryExtension $extensionId the ID of the extension or an instance of `RegistryExtension`
|
|
449
|
+
*
|
|
450
|
+
* @return bool returns true if the extension passes all validations, false otherwise
|
|
451
|
+
*
|
|
452
|
+
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException if the extension with the given ID is not found
|
|
453
|
+
*/
|
|
454
|
+
public static function isExtensionReadyForSubmission($extensionId): bool
|
|
455
|
+
{
|
|
456
|
+
if ($extensionId instanceof RegistryExtension) {
|
|
457
|
+
$extension = $extensionId;
|
|
458
|
+
} else {
|
|
459
|
+
$extension = self::find($extensionId);
|
|
460
|
+
}
|
|
461
|
+
if (!$extension) {
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
$validations = [
|
|
466
|
+
'name' => function ($value) {
|
|
467
|
+
return is_string($value) && strlen($value) > 3;
|
|
468
|
+
},
|
|
469
|
+
'description' => function ($value) {
|
|
470
|
+
return is_string($value) && strlen($value) > 12;
|
|
471
|
+
},
|
|
472
|
+
'tags' => function ($value) {
|
|
473
|
+
return !empty($value);
|
|
474
|
+
},
|
|
475
|
+
'promotional_text' => function ($value) {
|
|
476
|
+
return !empty($value);
|
|
477
|
+
},
|
|
478
|
+
'subtitle' => function ($value) {
|
|
479
|
+
return !empty($value);
|
|
480
|
+
},
|
|
481
|
+
'copyright' => function ($value) {
|
|
482
|
+
return !empty($value);
|
|
483
|
+
},
|
|
484
|
+
'website_url' => function ($value) {
|
|
485
|
+
return filter_var($value, FILTER_VALIDATE_URL) !== false;
|
|
486
|
+
},
|
|
487
|
+
'support_url' => function ($value) {
|
|
488
|
+
return filter_var($value, FILTER_VALIDATE_URL) !== false;
|
|
489
|
+
},
|
|
490
|
+
'privacy_policy_url' => function ($value) {
|
|
491
|
+
return filter_var($value, FILTER_VALIDATE_URL) !== false;
|
|
492
|
+
},
|
|
493
|
+
'icon_uuid' => function ($value) {
|
|
494
|
+
return !empty($value);
|
|
495
|
+
},
|
|
496
|
+
'category_uuid' => function ($value) {
|
|
497
|
+
return !empty($value);
|
|
498
|
+
},
|
|
499
|
+
'next_bundle_uuid' => function ($value) {
|
|
500
|
+
return !empty($value);
|
|
501
|
+
},
|
|
502
|
+
];
|
|
503
|
+
|
|
504
|
+
// Should have a new bundle for submission
|
|
505
|
+
$isNewBundle = $extension->next_bundle_uuid !== $extension->current_bundle_uuid;
|
|
506
|
+
if (!$isNewBundle) {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Check validations
|
|
511
|
+
foreach ($validations as $field => $validationFunction) {
|
|
512
|
+
if (isset($extension->$field)) {
|
|
513
|
+
$value = $extension->$field;
|
|
514
|
+
if (!$validationFunction($value)) {
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
} else {
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return true;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Retrieves or creates a Stripe product associated with this model instance.
|
|
527
|
+
*
|
|
528
|
+
* This method attempts to fetch a Stripe product based on an existing 'stripe_product_id'.
|
|
529
|
+
* If no product is found or if 'stripe_product_id' is not set, it creates a new Stripe product
|
|
530
|
+
* with the current model's name and description and updates the model's 'stripe_product_id'.
|
|
531
|
+
*
|
|
532
|
+
* @return \Stripe\Product|null returns the Stripe Product object if successful, or null if the Stripe client is not available
|
|
533
|
+
*/
|
|
534
|
+
public function getStripeProduct(): ?\Stripe\Product
|
|
535
|
+
{
|
|
536
|
+
$stripe = Utils::getStripeClient();
|
|
537
|
+
if ($stripe) {
|
|
538
|
+
$product = $this->stripe_product_id ? $stripe->products->retrieve($this->stripe_product_id, []) : null;
|
|
539
|
+
if (!$product) {
|
|
540
|
+
$product = $stripe->products->create(['name' => $this->name, 'description' => $this->description, 'metadata' => ['fleetbase_id' => $this->public_id]]);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Update stripe product id
|
|
544
|
+
$this->update(['stripe_product_id' => $product->id]);
|
|
545
|
+
|
|
546
|
+
return $product;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Retrieves the active Stripe price for the associated product.
|
|
554
|
+
*
|
|
555
|
+
* This method first ensures the product exists in Stripe by calling getStripeProduct().
|
|
556
|
+
* It then fetches the current active price for the product. If no active prices are found,
|
|
557
|
+
* it returns null.
|
|
558
|
+
*
|
|
559
|
+
* @return \Stripe\Price|null returns the Stripe Price object if available, or null otherwise
|
|
560
|
+
*/
|
|
561
|
+
public function getStripePrice(): ?\Stripe\Price
|
|
562
|
+
{
|
|
563
|
+
if (!$this->stripe_product_id) {
|
|
564
|
+
$this->getStripeProduct();
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
$stripe = Utils::getStripeClient();
|
|
568
|
+
$price = null;
|
|
569
|
+
if ($stripe) {
|
|
570
|
+
$prices = $stripe->prices->all(['product' => $this->stripe_product_id, 'limit' => 1, 'active' => true]);
|
|
571
|
+
$price = is_array($prices->data) && count($prices->data) ? $prices->data[0] : null;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
return $price;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Updates the existing active Stripe price to inactive and creates a new Stripe price.
|
|
579
|
+
*
|
|
580
|
+
* This method first retrieves the current active price and deactivates it if it exists.
|
|
581
|
+
* It then creates a new price with the current model's price and currency, associated with
|
|
582
|
+
* the Stripe product.
|
|
583
|
+
*
|
|
584
|
+
* @return \Stripe\Price|null returns the newly created Stripe Price object
|
|
585
|
+
*/
|
|
586
|
+
public function updateOrCreateStripePrice(): ?\Stripe\Price
|
|
587
|
+
{
|
|
588
|
+
$stripe = Utils::getStripeClient();
|
|
589
|
+
$price = $this->getStripePrice();
|
|
590
|
+
if ($price instanceof \Stripe\Price) {
|
|
591
|
+
// update stripe price
|
|
592
|
+
$stripe->prices->update($price->id, ['active' => false]);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// create new stripe price
|
|
596
|
+
$price = $stripe->prices->create(['unit_amount' => $this->price, 'currency' => $this->currency, 'product' => $this->stripe_product_id]);
|
|
597
|
+
|
|
598
|
+
return $price;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Creates a Stripe Checkout session for purchasing this model's associated product.
|
|
603
|
+
*
|
|
604
|
+
* This method ensures that the model has a valid company (extension author) and an active price.
|
|
605
|
+
* It calculates the facilitator fee based on the total amount and creates a checkout session with
|
|
606
|
+
* the necessary Stripe configurations, including the return URI with query parameters.
|
|
607
|
+
*
|
|
608
|
+
* @param string $returnUri the URI to which the user should be returned after the checkout process
|
|
609
|
+
*
|
|
610
|
+
* @return \Stripe\Checkout\Session returns the Stripe Checkout Session object
|
|
611
|
+
*
|
|
612
|
+
* @throws \Exception throws an exception if the model does not have an associated company or price
|
|
613
|
+
*/
|
|
614
|
+
public function createStripeCheckoutSession(string $returnUri): \Stripe\Checkout\Session
|
|
615
|
+
{
|
|
616
|
+
// Get extension author
|
|
617
|
+
$extensionAuthor = $this->company;
|
|
618
|
+
if (!$extensionAuthor) {
|
|
619
|
+
throw new \Exception('The extension you attempted to purchase is not available for purchase at this time.');
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Get the extension price from stripe
|
|
623
|
+
$price = $this->getStripePrice();
|
|
624
|
+
if (!$price) {
|
|
625
|
+
throw new \Exception('The extension you attempted to purchase is not available for purchase at this time.');
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Calculate the fee fleetbase takes for faciliation of extension
|
|
629
|
+
$totalAmount = $price->unit_amount;
|
|
630
|
+
$facilitatorFee = SupportUtils::calculatePercentage(config('registry-bridge.facilitator_fee', 10), $totalAmount);
|
|
631
|
+
|
|
632
|
+
// Get the stripe client to create the checkout session
|
|
633
|
+
$stripe = Utils::getStripeClient();
|
|
634
|
+
|
|
635
|
+
// Create the stripe checkout session
|
|
636
|
+
$checkoutSession = $stripe->checkout->sessions->create([
|
|
637
|
+
'ui_mode' => 'embedded',
|
|
638
|
+
'line_items' => [
|
|
639
|
+
[
|
|
640
|
+
'price' => $price->id,
|
|
641
|
+
'quantity' => 1,
|
|
642
|
+
],
|
|
643
|
+
],
|
|
644
|
+
'mode' => 'payment',
|
|
645
|
+
'return_url' => SupportUtils::consoleUrl($returnUri) . '?extension_id=' . $this->uuid . '&checkout_session_id={CHECKOUT_SESSION_ID}',
|
|
646
|
+
'payment_intent_data' => [
|
|
647
|
+
'application_fee_amount' => $facilitatorFee,
|
|
648
|
+
'transfer_data' => [
|
|
649
|
+
'destination' => $extensionAuthor->stripe_connect_id,
|
|
650
|
+
],
|
|
651
|
+
],
|
|
652
|
+
]);
|
|
653
|
+
|
|
654
|
+
return $checkoutSession;
|
|
655
|
+
}
|
|
656
|
+
}
|