@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
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\RegistryBridge\Models;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\Models\Model;
|
|
6
|
+
use Fleetbase\Traits\HasUuid;
|
|
7
|
+
use Illuminate\Support\Str;
|
|
8
|
+
|
|
9
|
+
class RegistryDeveloperAccount extends Model
|
|
10
|
+
{
|
|
11
|
+
use HasUuid;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The database table used by the model.
|
|
15
|
+
*
|
|
16
|
+
* @var string
|
|
17
|
+
*/
|
|
18
|
+
protected $table = 'registry_developer_accounts';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The attributes that are mass assignable.
|
|
22
|
+
*
|
|
23
|
+
* @var array
|
|
24
|
+
*/
|
|
25
|
+
protected $fillable = [
|
|
26
|
+
'username',
|
|
27
|
+
'email',
|
|
28
|
+
'password',
|
|
29
|
+
'name',
|
|
30
|
+
'avatar_url',
|
|
31
|
+
'github_username',
|
|
32
|
+
'website',
|
|
33
|
+
'bio',
|
|
34
|
+
'email_verified_at',
|
|
35
|
+
'verification_token',
|
|
36
|
+
'status',
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The attributes that should be cast to native types.
|
|
41
|
+
*
|
|
42
|
+
* @var array
|
|
43
|
+
*/
|
|
44
|
+
protected $casts = [
|
|
45
|
+
'email_verified_at' => 'datetime',
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The attributes excluded from the model's JSON form.
|
|
50
|
+
*
|
|
51
|
+
* @var array
|
|
52
|
+
*/
|
|
53
|
+
protected $hidden = [
|
|
54
|
+
'password',
|
|
55
|
+
'verification_token',
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The "booting" method of the model.
|
|
60
|
+
*/
|
|
61
|
+
protected static function boot()
|
|
62
|
+
{
|
|
63
|
+
parent::boot();
|
|
64
|
+
|
|
65
|
+
static::creating(function ($model) {
|
|
66
|
+
if (empty($model->verification_token)) {
|
|
67
|
+
$model->verification_token = Str::random(64);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get the registry users associated with this developer account.
|
|
74
|
+
*
|
|
75
|
+
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
76
|
+
*/
|
|
77
|
+
public function registryUsers()
|
|
78
|
+
{
|
|
79
|
+
return $this->hasMany(RegistryUser::class, 'developer_account_uuid', 'uuid');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get the extensions published by this developer account.
|
|
84
|
+
*
|
|
85
|
+
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
86
|
+
*/
|
|
87
|
+
public function extensions()
|
|
88
|
+
{
|
|
89
|
+
return $this->hasMany(RegistryExtension::class, 'publisher_uuid', 'uuid')
|
|
90
|
+
->where('publisher_type', 'developer');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check if the account is active.
|
|
95
|
+
*/
|
|
96
|
+
public function isActive(): bool
|
|
97
|
+
{
|
|
98
|
+
return $this->status === 'active';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Check if the account is suspended.
|
|
103
|
+
*/
|
|
104
|
+
public function isSuspended(): bool
|
|
105
|
+
{
|
|
106
|
+
return $this->status === 'suspended';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Check if the account is pending verification.
|
|
111
|
+
*/
|
|
112
|
+
public function isPendingVerification(): bool
|
|
113
|
+
{
|
|
114
|
+
return $this->status === 'pending_verification';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Check if the email is verified.
|
|
119
|
+
*/
|
|
120
|
+
public function isEmailVerified(): bool
|
|
121
|
+
{
|
|
122
|
+
return $this->email_verified_at !== null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Mark the email as verified.
|
|
127
|
+
*/
|
|
128
|
+
public function markEmailAsVerified(): bool
|
|
129
|
+
{
|
|
130
|
+
return $this->update([
|
|
131
|
+
'email_verified_at' => now(),
|
|
132
|
+
'status' => 'active',
|
|
133
|
+
'verification_token' => null,
|
|
134
|
+
]);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Generate a new verification token.
|
|
139
|
+
*/
|
|
140
|
+
public function generateVerificationToken(): string
|
|
141
|
+
{
|
|
142
|
+
$token = Str::random(64);
|
|
143
|
+
$this->update(['verification_token' => $token]);
|
|
144
|
+
|
|
145
|
+
return $token;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -29,6 +29,8 @@ class RegistryExtensionPurchase extends Model
|
|
|
29
29
|
protected $fillable = [
|
|
30
30
|
'uuid',
|
|
31
31
|
'company_uuid',
|
|
32
|
+
'purchaser_uuid',
|
|
33
|
+
'purchaser_type',
|
|
32
34
|
'extension_uuid',
|
|
33
35
|
'stripe_checkout_session_id',
|
|
34
36
|
'stripe_payment_intent_id',
|
|
@@ -70,7 +72,22 @@ class RegistryExtensionPurchase extends Model
|
|
|
70
72
|
protected $without = ['company', 'extension'];
|
|
71
73
|
|
|
72
74
|
/**
|
|
75
|
+
* Get the purchaser (polymorphic relationship).
|
|
76
|
+
* Can be either a Company or RegistryDeveloperAccount.
|
|
77
|
+
*
|
|
78
|
+
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
|
|
79
|
+
*/
|
|
80
|
+
public function purchaser()
|
|
81
|
+
{
|
|
82
|
+
return $this->morphTo('purchaser', 'purchaser_type', 'purchaser_uuid', 'uuid');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Legacy relationship for backward compatibility.
|
|
87
|
+
*
|
|
73
88
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
89
|
+
*
|
|
90
|
+
* @deprecated Use purchaser() instead
|
|
74
91
|
*/
|
|
75
92
|
public function company()
|
|
76
93
|
{
|
|
@@ -40,6 +40,8 @@ class RegistryUser extends Model
|
|
|
40
40
|
protected $fillable = [
|
|
41
41
|
'company_uuid',
|
|
42
42
|
'user_uuid',
|
|
43
|
+
'account_type',
|
|
44
|
+
'developer_account_uuid',
|
|
43
45
|
'token',
|
|
44
46
|
'registry_token',
|
|
45
47
|
'scope',
|
|
@@ -115,6 +117,14 @@ class RegistryUser extends Model
|
|
|
115
117
|
return $this->belongsTo(User::class);
|
|
116
118
|
}
|
|
117
119
|
|
|
120
|
+
/**
|
|
121
|
+
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
122
|
+
*/
|
|
123
|
+
public function developerAccount()
|
|
124
|
+
{
|
|
125
|
+
return $this->belongsTo(RegistryDeveloperAccount::class, 'developer_account_uuid', 'uuid');
|
|
126
|
+
}
|
|
127
|
+
|
|
118
128
|
/**
|
|
119
129
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
120
130
|
*/
|
|
@@ -132,11 +142,15 @@ class RegistryUser extends Model
|
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
/**
|
|
135
|
-
*
|
|
145
|
+
* Get the is_admin attribute.
|
|
136
146
|
*/
|
|
137
147
|
public function getIsAdminAttribute(): bool
|
|
138
148
|
{
|
|
139
|
-
|
|
149
|
+
if ($this->account_type === 'developer') {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return $this->user && $this->user->is_admin === true;
|
|
140
154
|
}
|
|
141
155
|
|
|
142
156
|
/**
|
|
@@ -190,9 +204,8 @@ class RegistryUser extends Model
|
|
|
190
204
|
/**
|
|
191
205
|
* Find a registry user by their username.
|
|
192
206
|
*
|
|
193
|
-
* This method
|
|
194
|
-
*
|
|
195
|
-
* corresponding registry user is returned.
|
|
207
|
+
* This method searches for a registry user by email or username,
|
|
208
|
+
* supporting both cloud and developer account types.
|
|
196
209
|
*
|
|
197
210
|
* @param string $username the username to search for
|
|
198
211
|
*
|
|
@@ -200,16 +213,32 @@ class RegistryUser extends Model
|
|
|
200
213
|
*/
|
|
201
214
|
public static function findFromUsername(string $username): ?RegistryUser
|
|
202
215
|
{
|
|
216
|
+
// First try to find a cloud user
|
|
217
|
+
$cloudUser = static::select('registry_users.*')
|
|
218
|
+
->join('users', function ($join) use ($username) {
|
|
219
|
+
$join->on('users.uuid', '=', 'registry_users.user_uuid')
|
|
220
|
+
->on('users.company_uuid', '=', 'registry_users.company_uuid')
|
|
221
|
+
->where(function ($query) use ($username) {
|
|
222
|
+
$query->where('users.email', $username)
|
|
223
|
+
->orWhere('users.username', $username);
|
|
224
|
+
});
|
|
225
|
+
})
|
|
226
|
+
->where('registry_users.account_type', 'cloud')
|
|
227
|
+
->first();
|
|
228
|
+
|
|
229
|
+
if ($cloudUser) {
|
|
230
|
+
return $cloudUser;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// If not found, try to find a developer account
|
|
203
234
|
return static::select('registry_users.*')
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
})
|
|
212
|
-
->first();
|
|
235
|
+
->join('registry_developer_accounts', 'registry_developer_accounts.uuid', '=', 'registry_users.developer_account_uuid')
|
|
236
|
+
->where('registry_users.account_type', 'developer')
|
|
237
|
+
->where(function ($query) use ($username) {
|
|
238
|
+
$query->where('registry_developer_accounts.email', $username)
|
|
239
|
+
->orWhere('registry_developer_accounts.username', $username);
|
|
240
|
+
})
|
|
241
|
+
->first();
|
|
213
242
|
}
|
|
214
243
|
|
|
215
244
|
public static function findFromToken(string $token): ?RegistryUser
|
|
@@ -252,4 +281,46 @@ class RegistryUser extends Model
|
|
|
252
281
|
{
|
|
253
282
|
return [$this->public_id];
|
|
254
283
|
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Check if this is a developer account.
|
|
287
|
+
*/
|
|
288
|
+
public function isDeveloperAccount(): bool
|
|
289
|
+
{
|
|
290
|
+
return $this->account_type === 'developer';
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Check if this is a cloud account.
|
|
295
|
+
*/
|
|
296
|
+
public function isCloudAccount(): bool
|
|
297
|
+
{
|
|
298
|
+
return $this->account_type === 'cloud';
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Get permissions for this registry user.
|
|
303
|
+
*/
|
|
304
|
+
public function getPermissions(): array
|
|
305
|
+
{
|
|
306
|
+
if ($this->isDeveloperAccount()) {
|
|
307
|
+
return [
|
|
308
|
+
'can_install_free_extensions' => true,
|
|
309
|
+
'can_install_paid_extensions' => false,
|
|
310
|
+
'can_publish_extensions' => true,
|
|
311
|
+
'can_purchase_extensions' => false,
|
|
312
|
+
'can_access_console' => false,
|
|
313
|
+
'can_manage_company' => false,
|
|
314
|
+
];
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return [
|
|
318
|
+
'can_install_free_extensions' => true,
|
|
319
|
+
'can_install_paid_extensions' => true,
|
|
320
|
+
'can_publish_extensions' => true,
|
|
321
|
+
'can_purchase_extensions' => true,
|
|
322
|
+
'can_access_console' => true,
|
|
323
|
+
'can_manage_company' => true,
|
|
324
|
+
];
|
|
325
|
+
}
|
|
255
326
|
}
|
package/server/src/routes.php
CHANGED
|
@@ -12,15 +12,22 @@ use Illuminate\Support\Facades\Route;
|
|
|
12
12
|
| is assigned the "api" middleware group. Enjoy building your API!
|
|
13
13
|
|
|
|
14
14
|
*/
|
|
15
|
-
//
|
|
16
|
-
Route::get(config('
|
|
17
|
-
Route::
|
|
18
|
-
Route::
|
|
15
|
+
// Public endpoints (no authentication required)
|
|
16
|
+
Route::get(config('registry-bridge.api.routing.prefix', '~registry') . '/v1/extensions', 'Fleetbase\RegistryBridge\Http\Controllers\Internal\v1\RegistryExtensionController@listPublicExtensions');
|
|
17
|
+
Route::get(config('registry-bridge.api.routing.prefix', '~registry') . '/v1/lookup', 'Fleetbase\RegistryBridge\Http\Controllers\Internal\v1\RegistryController@lookupPackage');
|
|
18
|
+
Route::post(config('registry-bridge.api.routing.prefix', '~registry') . '/v1/bundle-upload', 'Fleetbase\RegistryBridge\Http\Controllers\Internal\v1\RegistryController@bundleUpload');
|
|
19
|
+
|
|
20
|
+
// Developer account registration (public, no auth required)
|
|
21
|
+
Route::post(config('registry-bridge.api.routing.prefix', '~registry') . '/v1/developer-account/register', 'Fleetbase\RegistryBridge\Http\Controllers\Internal\v1\RegistryDeveloperAccountController@register');
|
|
22
|
+
Route::post(config('registry-bridge.api.routing.prefix', '~registry') . '/v1/developer-account/verify', 'Fleetbase\RegistryBridge\Http\Controllers\Internal\v1\RegistryDeveloperAccountController@verifyEmail');
|
|
23
|
+
Route::post(config('registry-bridge.api.routing.prefix', '~registry') . '/v1/developer-account/resend-verification', 'Fleetbase\RegistryBridge\Http\Controllers\Internal\v1\RegistryDeveloperAccountController@resendVerification');
|
|
24
|
+
Route::post(config('registry-bridge.api.routing.prefix', '~registry') . '/v1/developer-account/generate-token', 'Fleetbase\RegistryBridge\Http\Controllers\Internal\v1\RegistryDeveloperAccountController@generateToken');
|
|
25
|
+
Route::prefix(config('registry-bridge.api.routing.prefix', '~registry'))->middleware(['fleetbase.registry'])->namespace('Fleetbase\RegistryBridge\Http\Controllers')->group(
|
|
19
26
|
function ($router) {
|
|
20
27
|
/*
|
|
21
28
|
* Internal Routes v1
|
|
22
29
|
*/
|
|
23
|
-
$router->group(['prefix' => config('
|
|
30
|
+
$router->group(['prefix' => config('registry-bridge.api.routing.internal_prefix', 'v1'), 'namespace' => 'Internal\v1'], function ($router) {
|
|
24
31
|
$router->group(['prefix' => 'auth'], function ($router) {
|
|
25
32
|
$router->group(['middleware' => ['fleetbase.protected']], function ($router) {
|
|
26
33
|
$router->get('registry-tokens', 'RegistryAuthController@getRegistryTokens');
|
|
@@ -35,6 +42,12 @@ Route::prefix(config('internals.api.routing.prefix', '~registry'))->middleware([
|
|
|
35
42
|
$router->post('check-publish', 'RegistryAuthController@checkPublishAllowed');
|
|
36
43
|
});
|
|
37
44
|
|
|
45
|
+
// Developer account profile routes (require authentication)
|
|
46
|
+
$router->group(['prefix' => 'developer-account'], function ($router) {
|
|
47
|
+
$router->get('profile', 'RegistryDeveloperAccountController@profile');
|
|
48
|
+
$router->post('profile', 'RegistryDeveloperAccountController@updateProfile');
|
|
49
|
+
});
|
|
50
|
+
|
|
38
51
|
$router->group(['middleware' => ['fleetbase.protected']], function ($router) {
|
|
39
52
|
$router->get('categories', 'RegistryController@categories');
|
|
40
53
|
$router->get('engines', 'RegistryController@getInstalledEngines');
|
|
@@ -48,6 +61,7 @@ Route::prefix(config('internals.api.routing.prefix', '~registry'))->middleware([
|
|
|
48
61
|
$router->group(['prefix' => 'payments'], function ($router) {
|
|
49
62
|
$router->post('account', 'RegistryPaymentsController@getStripeAccount');
|
|
50
63
|
$router->post('account-session', 'RegistryPaymentsController@getStripeAccountSession');
|
|
64
|
+
$router->post('account-management-session', 'RegistryPaymentsController@createAccountManagementSession');
|
|
51
65
|
$router->get('has-stripe-connect-account', 'RegistryPaymentsController@hasStripeConnectAccount');
|
|
52
66
|
$router->post('create-checkout-session', 'RegistryPaymentsController@createStripeCheckoutSession');
|
|
53
67
|
$router->post('get-checkout-session', 'RegistryPaymentsController@getStripeCheckoutSessionStatus');
|
package/translations/en-us.yaml
CHANGED
|
@@ -49,6 +49,13 @@ registry-bridge:
|
|
|
49
49
|
purchased:
|
|
50
50
|
title: Purchased Extensions
|
|
51
51
|
developers:
|
|
52
|
+
payments:
|
|
53
|
+
manage-account: Manage Payout Account
|
|
54
|
+
no-account-warning: You must complete the Stripe onboarding process before accessing account settings.
|
|
55
|
+
settings:
|
|
56
|
+
title: Manage Payout Account
|
|
57
|
+
description: Manage your Stripe account details, including your business information and bank accounts for receiving payouts.
|
|
58
|
+
loading: Loading account management...
|
|
52
59
|
extensions:
|
|
53
60
|
extensions: Extensions
|
|
54
61
|
create-new-extension: Create new Extension
|