@fleetbase/registry-bridge-engine 0.0.11 → 0.0.13

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.
Files changed (42) hide show
  1. package/addon/components/extension-card.hbs +5 -0
  2. package/addon/components/extension-card.js +54 -3
  3. package/addon/components/extension-form.hbs +20 -2
  4. package/addon/components/extension-form.js +57 -0
  5. package/addon/components/modals/extension-details.hbs +40 -18
  6. package/addon/components/modals/self-managed-install-instructions.hbs +40 -0
  7. package/addon/controllers/installed.js +2 -0
  8. package/addon/engine.js +1 -1
  9. package/addon/models/registry-extension.js +2 -0
  10. package/addon/routes/application.js +11 -0
  11. package/addon/routes/developers/analytics.js +11 -0
  12. package/addon/routes/developers/credentials.js +11 -0
  13. package/addon/routes/developers/extensions/edit.js +11 -0
  14. package/addon/routes/developers/extensions/index.js +11 -0
  15. package/addon/routes/developers/extensions/new.js +14 -1
  16. package/addon/routes/developers/extensions.js +14 -1
  17. package/addon/routes/developers/payments/index.js +11 -0
  18. package/addon/routes/developers/payments/onboard.js +8 -1
  19. package/addon/routes/developers/payments.js +14 -1
  20. package/addon/routes/explore/category.js +11 -0
  21. package/addon/routes/explore/index.js +11 -0
  22. package/addon/routes/installed.js +11 -0
  23. package/addon/routes/purchased.js +11 -0
  24. package/addon/styles/registry-bridge-engine.css +137 -0
  25. package/addon/templates/application.hbs +9 -15
  26. package/addon/templates/developers/analytics.hbs +2 -0
  27. package/addon/templates/developers/credentials.hbs +1 -1
  28. package/addon/templates/developers/extensions/edit.hbs +2 -1
  29. package/addon/templates/developers/extensions/index.hbs +1 -1
  30. package/addon/templates/developers/extensions/new.hbs +1 -1
  31. package/addon/templates/developers/payments/index.hbs +1 -1
  32. package/app/components/modals/self-managed-install-instructions.js +1 -0
  33. package/composer.json +2 -2
  34. package/extension.json +1 -1
  35. package/package.json +4 -3
  36. package/server/migrations/2024_08_02_072214_add_self_managed_column_to_registry_extensions_table.php +28 -0
  37. package/server/src/Auth/Schemas/RegistryBridge.php +215 -0
  38. package/server/src/Http/Controllers/Internal/v1/RegistryController.php +46 -0
  39. package/server/src/Models/RegistryExtension.php +45 -0
  40. package/server/src/Models/RegistryExtensionBundle.php +2 -2
  41. package/server/src/routes.php +2 -1
  42. package/translations/en-us.yaml +14 -1
@@ -6,6 +6,11 @@
6
6
  <div class="flex flex-col">
7
7
  <div class="font-semibold text-sm block {{@nameTextClass}}">{{@extension.name}}</div>
8
8
  <div class="text-xs {{@descriptionTextClass}}">{{n-a @extension.description}}</div>
9
+ {{#if @extension.payment_required}}
10
+ <Badge @status="success" @hideStatusDot={{true}}>{{format-currency @extension.price @extension.currency}}</Badge>
11
+ {{else}}
12
+ <Badge @status="success" @hideStatusDot={{true}}>Free</Badge>
13
+ {{/if}}
9
14
  </div>
10
15
  {{yield @extension}}
11
16
  </div>
@@ -26,6 +26,7 @@ export default class ExtensionCardComponent extends Component {
26
26
  @service fetch;
27
27
  @service stripe;
28
28
  @service urlSearchParams;
29
+ @service abilities;
29
30
  @tracked extension;
30
31
 
31
32
  constructor(owner, { extension }) {
@@ -40,9 +41,26 @@ export default class ExtensionCardComponent extends Component {
40
41
 
41
42
  @action onClick(options = {}) {
42
43
  const installChannel = `install.${this.currentUser.companyId}.${this.extension.id}`;
44
+ const isAuthor = this.extension.is_author === true;
45
+ const isSelfManaged = this.extension.self_managed === true;
43
46
  const isAlreadyPurchased = this.extension.is_purchased === true;
44
47
  const isAlreadyInstalled = this.extension.is_installed === true;
45
- const isPaymentRequired = this.extension.payment_required === true && isAlreadyPurchased === false;
48
+ const isPaymentRequired = !isAuthor && this.extension.payment_required === true && isAlreadyPurchased === false;
49
+ const userCannotPurchase = isPaymentRequired && this.abilities.cannot('registry-bridge purchase extension');
50
+ let acceptButtonText = isPaymentRequired ? `Purchase for ${formatCurrency(this.extension.price, this.extension.currency)}` : isAlreadyInstalled ? 'Installed' : 'Install';
51
+ if (this.abilities.cannot('registry-bridge install extension')) {
52
+ acceptButtonText = 'Unauthorized to Install';
53
+ }
54
+ const goBack = async (modal) => {
55
+ await modal.done();
56
+ later(
57
+ this,
58
+ () => {
59
+ this.onClick();
60
+ },
61
+ 100
62
+ );
63
+ };
46
64
 
47
65
  if (typeof this.args.onClick === 'function') {
48
66
  this.args.onClick(this.extension);
@@ -53,9 +71,9 @@ export default class ExtensionCardComponent extends Component {
53
71
  titleComponent: 'extension-modal-title',
54
72
  modalClass: 'flb--extension-modal modal-lg',
55
73
  modalHeaderClass: 'flb--extension-modal-header',
56
- acceptButtonText: isPaymentRequired ? `Purchase for ${formatCurrency(this.extension.price, this.extension.currency)}` : isAlreadyInstalled ? 'Installed' : 'Install',
74
+ acceptButtonText,
57
75
  acceptButtonIcon: isPaymentRequired ? 'credit-card' : isAlreadyInstalled ? 'check' : 'download',
58
- acceptButtonDisabled: isAlreadyInstalled,
76
+ acceptButtonDisabled: this.abilities.cannot('registry-bridge install extension') || isAlreadyInstalled || userCannotPurchase,
59
77
  acceptButtonScheme: isPaymentRequired ? 'success' : 'primary',
60
78
  declineButtonText: 'Done',
61
79
  process: null,
@@ -63,6 +81,13 @@ export default class ExtensionCardComponent extends Component {
63
81
  stepDescription: 'Awaiting install to begin...',
64
82
  progress: 0,
65
83
  extension: this.extension,
84
+ viewSelfManagesInstallInstructions: () => {
85
+ this.selfManagedInstallInstructions({
86
+ extension: this.extension,
87
+ confirm: goBack,
88
+ decline: goBack,
89
+ });
90
+ },
66
91
  confirm: async (modal) => {
67
92
  modal.startLoading();
68
93
 
@@ -71,6 +96,22 @@ export default class ExtensionCardComponent extends Component {
71
96
  return this.startCheckoutSession();
72
97
  }
73
98
 
99
+ // If self managed just prompt instructions
100
+ if (isSelfManaged) {
101
+ await modal.done();
102
+ return later(
103
+ this,
104
+ () => {
105
+ return this.selfManagedInstallInstructions({
106
+ extension: this.extension,
107
+ confirm: goBack,
108
+ decline: goBack,
109
+ });
110
+ },
111
+ 100
112
+ );
113
+ }
114
+
74
115
  // Listen for install progress
75
116
  this.socket.listen(installChannel, ({ process, step, progress }) => {
76
117
  let stepDescription;
@@ -121,6 +162,16 @@ export default class ExtensionCardComponent extends Component {
121
162
  });
122
163
  }
123
164
 
165
+ async selfManagedInstallInstructions(options = {}) {
166
+ await this.modalsManager.done();
167
+ this.modalsManager.show('modals/self-managed-install-instructions', {
168
+ title: 'Install a Self Managed Extension',
169
+ hideDeclineButton: true,
170
+ acceptButtonText: 'Done',
171
+ ...options,
172
+ });
173
+ }
174
+
124
175
  async startCheckoutSession() {
125
176
  const checkout = await this.stripe.initEmbeddedCheckout({
126
177
  fetchClientSecret: this.fetchClientSecret.bind(this),
@@ -69,7 +69,6 @@
69
69
 
70
70
  <InputGroup
71
71
  @name={{t "registry-bridge.developers.extensions.extension-form.extension-tags"}}
72
- @wrapperClass="mb-0i"
73
72
  @helpText={{t "registry-bridge.developers.extensions.extension-form.extension-tags-help-text"}}
74
73
  >
75
74
  <TagInput
@@ -85,6 +84,19 @@
85
84
  {{tag}}
86
85
  </TagInput>
87
86
  </InputGroup>
87
+
88
+ <InputGroup @wrapperClass="mb-0i">
89
+ <Toggle
90
+ @isToggled={{@extension.self_managed}}
91
+ @onToggle={{fn (mut @extension.self_managed)}}
92
+ @helpText={{t "registry-bridge.developers.extensions.extension-form.self-managed-help-text"}}
93
+ >
94
+ <span class="dark:text-gray-100 text-sm mx-2">{{t "registry-bridge.developers.extensions.extension-form.self-managed"}}</span>
95
+ </Toggle>
96
+ <p class="mt-2 text-xs bg-blue-800 border border-blue-600 px-2 py-2 rounded-md text-blue-100">
97
+ {{t "registry-bridge.developers.extensions.extension-form.self-managed-help-text"}}
98
+ </p>
99
+ </InputGroup>
88
100
  </ContentPanel>
89
101
  <ContentPanel @title={{t "registry-bridge.developers.extensions.extension-form.extension-bundle"}} @open={{true}} @pad={{false}} @panelBodyClass="bg-white dark:bg-gray-800">
90
102
  <div class="px-4 pb-4 pt-3 flex flex-col flex-grow-0">
@@ -101,7 +113,13 @@
101
113
  {{/if}}
102
114
  </div>
103
115
  </ContentPanel>
104
- <ContentPanel @title={{t "registry-bridge.developers.extensions.extension-form.extension-listing-details"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
116
+ <ContentPanel
117
+ @title={{t "registry-bridge.developers.extensions.extension-form.extension-listing-details"}}
118
+ @open={{true}}
119
+ @pad={{true}}
120
+ @panelBodyClass="bg-white dark:bg-gray-800"
121
+ @actionButtons={{this.listingDetailsPanelActions}}
122
+ >
105
123
  <InputGroup
106
124
  @name={{t "registry-bridge.developers.extensions.extension-form.extension-promotional-text"}}
107
125
  @helpText={{t "registry-bridge.developers.extensions.extension-form.extension-promotional-text-help-text"}}
@@ -3,6 +3,8 @@ import { tracked } from '@glimmer/tracking';
3
3
  import { inject as service } from '@ember/service';
4
4
  import { action } from '@ember/object';
5
5
  import { task } from 'ember-concurrency';
6
+ import { later } from '@ember/runloop';
7
+ import formatCurrency from '@fleetbase/ember-ui/utils/format-currency';
6
8
 
7
9
  export default class ExtensionFormComponent extends Component {
8
10
  @service store;
@@ -13,6 +15,14 @@ export default class ExtensionFormComponent extends Component {
13
15
  @tracked subscriptionModelOptions = ['flat_rate', 'tiered', 'usage'];
14
16
  @tracked billingPeriodOptions = ['daily', 'weekly', 'monthly', 'quarterly', 'yearly'];
15
17
  @tracked uploadQueue = [];
18
+ listingDetailsPanelActions = [
19
+ {
20
+ type: 'link',
21
+ size: 'xs',
22
+ text: 'Preview Listing',
23
+ onClick: this.previewListing,
24
+ },
25
+ ];
16
26
  acceptedImageTypes = ['image/jpeg', 'image/png', 'image/gif'];
17
27
  acceptedBundleTypes = [
18
28
  'application/zip',
@@ -120,4 +130,51 @@ export default class ExtensionFormComponent extends Component {
120
130
  @action removeFile(file) {
121
131
  return file.destroyRecord();
122
132
  }
133
+
134
+ @action previewListing(options = {}) {
135
+ const extension = this.args.extension;
136
+ const isAlreadyPurchased = extension.is_purchased === true;
137
+ const isAlreadyInstalled = extension.is_installed === true;
138
+ const isPaymentRequired = extension.payment_required === true && isAlreadyPurchased === false;
139
+ const goBack = async (modal) => {
140
+ await modal.done();
141
+ later(
142
+ this,
143
+ () => {
144
+ this.previewListing();
145
+ },
146
+ 100
147
+ );
148
+ };
149
+
150
+ this.modalsManager.show('modals/extension-details', {
151
+ titleComponent: 'extension-modal-title',
152
+ modalClass: 'flb--extension-modal modal-lg',
153
+ modalHeaderClass: 'flb--extension-modal-header',
154
+ acceptButtonText: isPaymentRequired ? `Purchase for ${formatCurrency(extension.price, extension.currency)}` : isAlreadyInstalled ? 'Installed' : 'Install',
155
+ acceptButtonIcon: isPaymentRequired ? 'credit-card' : isAlreadyInstalled ? 'check' : 'download',
156
+ acceptButtonDisabled: true,
157
+ acceptButtonScheme: isPaymentRequired ? 'success' : 'primary',
158
+ declineButtonText: 'Done',
159
+ viewSelfManagesInstallInstructions: () => {
160
+ this.selfManagedInstallInstructions({
161
+ extension,
162
+ confirm: goBack,
163
+ decline: goBack,
164
+ });
165
+ },
166
+ extension,
167
+ ...options,
168
+ });
169
+ }
170
+
171
+ async selfManagedInstallInstructions(options = {}) {
172
+ await this.modalsManager.done();
173
+ this.modalsManager.show('modals/self-managed-install-instructions', {
174
+ title: 'Install a Self Managed Extension',
175
+ hideDeclineButton: true,
176
+ acceptButtonText: 'Done',
177
+ ...options,
178
+ });
179
+ }
123
180
  }
@@ -2,7 +2,7 @@
2
2
  <div class="modal-body-container space-y-3">
3
3
  <div class="flex flex-row items-center space-x-2">
4
4
  <div class="flb--extension-tag shadow-sm border dark:bg-gray-800 dark:border-gray-800 dark:text-gray-200">
5
- Extension
5
+ {{t "registry-bridge.component.extension-details-modal.extension"}}
6
6
  </div>
7
7
  <div class="flb--extension-tag shadow-sm border dark:bg-gray-800 dark:border-gray-800 dark:text-gray-200">
8
8
  {{this.extension.category_name}}
@@ -27,41 +27,63 @@
27
27
  </EmberWormhole>
28
28
  {{/if}}
29
29
  {{/if}}
30
- <div>
31
- <h3 class="dark:text-white font-semibold mb-1">Overview</h3>
32
- <div class="space-y-1">
30
+ <div class="space-y-3">
31
+ <div>
32
+ <h3 class="dark:text-white font-semibold mb-1">{{t "registry-bridge.component.extension-details-modal.description"}}</h3>
33
33
  <p class="dark:text-gray-200 text-sm">{{this.extension.description}}</p>
34
- <p class="dark:text-gray-200 text-sm">{{html-safe this.extension.promotional_text}}</p>
34
+ </div>
35
+ <div>
36
+ <h3 class="dark:text-white font-semibold mb-1">{{t "registry-bridge.component.extension-details-modal.overview"}}</h3>
37
+ <div class="extension-details-promo-content dark:text-gray-200 text-sm">{{html-safe this.extension.promotional_text}}</div>
35
38
  </div>
36
39
  </div>
37
40
  <div>
38
- <h3 class="dark:text-white font-semibold mb-1">Details</h3>
41
+ <h3 class="dark:text-white font-semibold mb-1">{{t "registry-bridge.component.extension-details-modal.details"}}</h3>
39
42
  <div class="space-y-2">
40
43
  <div class="grid grid-cols-4 gap-2">
41
44
  <div class="space-y-2">
42
45
  <div>
43
- <div class="text-sm font-semibold dark:text-gray-100">Version</div>
44
- <div class="text-sm dark:text-gray-200">{{this.extension.current_bundle_version}}</div>
46
+ <div class="text-sm font-semibold dark:text-gray-100">{{t "registry-bridge.component.extension-details-modal.version"}}</div>
47
+ <div class="text-sm dark:text-gray-200">{{n-a this.extension.current_bundle_version}}</div>
45
48
  </div>
46
49
  <div>
47
- <div class="text-sm font-semibold dark:text-gray-100">Updated</div>
48
- <div class="text-sm dark:text-gray-200">{{this.extension.updatedAt}}</div>
50
+ <div class="text-sm font-semibold dark:text-gray-100">{{t "registry-bridge.component.extension-details-modal.updated"}}</div>
51
+ <div class="text-sm dark:text-gray-200">{{n-a this.extension.updatedAt}}</div>
49
52
  </div>
50
53
  </div>
51
54
  <div class="space-y-2">
52
55
  <div>
53
- <div class="text-sm font-semibold dark:text-gray-100">Author</div>
54
- <div class="text-sm dark:text-gray-200">{{this.extension.publisher_name}}</div>
56
+ <div class="text-sm font-semibold dark:text-gray-100">{{t "registry-bridge.component.extension-details-modal.author"}}</div>
57
+ <div class="text-sm dark:text-gray-200">{{n-a this.extension.publisher_name}}</div>
55
58
  </div>
56
59
  <div>
57
- <div class="text-sm font-semibold dark:text-gray-100">Website</div>
58
- <a
59
- class="text-sm dark:text-gray-200"
60
- href={{this.extension.website_url}}
61
- target={{dasherize (concat this.extension.name "-website")}}
62
- >{{this.extension.website_url}}</a>
60
+ <div class="text-sm font-semibold dark:text-gray-100">{{t "registry-bridge.component.extension-details-modal.website"}}</div>
61
+ {{#if this.extension.website_url}}
62
+ <a
63
+ class="text-sm text-blue-400 hover:text-blue-300"
64
+ href={{this.extension.website_url}}
65
+ target={{dasherize (concat this.extension.name "-website")}}
66
+ >{{this.extension.website_url}}</a>
67
+ {{else}}
68
+ <div class="text-sm dark:text-gray-200">-</div>
69
+ {{/if}}
63
70
  </div>
64
71
  </div>
72
+ <div class="space-y-2">
73
+ {{#if this.extension.self_managed}}
74
+ <div>
75
+ <Badge @status="info" @hideStatusDot={{true}} @helpText={{t "registry-bridge.component.extension-details-modal.self-managed-help-text"}}>{{t
76
+ "registry-bridge.component.extension-details-modal.self-managed"
77
+ }}</Badge>
78
+ {{#if @options.viewSelfManagesInstallInstructions}}
79
+ <a href="#" class="text-xs text-blue-400 hover:opacity-50" {{on "click" @options.viewSelfManagesInstallInstructions}}><FaIcon
80
+ @icon="circle-info"
81
+ class="mr-1"
82
+ />How to install</a>
83
+ {{/if}}
84
+ </div>
85
+ {{/if}}
86
+ </div>
65
87
  </div>
66
88
  </div>
67
89
  </div>
@@ -0,0 +1,40 @@
1
+ <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
+ <div class="modal-body-container">
3
+ <p class="text-sm mb-4 text-gray-900 dark:text-gray-200">
4
+ Begin the installation from the base directory of your Fleetbase instance. For comprehensive instructions, visit the
5
+ <a href="https://github.com/fleetbase/fleetbase-cli" target="fleetbase-cli" class="text-blue-400 hover:opacity-50">
6
+ <FaIcon @icon="arrow-up-right-from-square" class="text-xs" />
7
+ Fleetbase CLI GitHub page</a>.
8
+ </p>
9
+ <ol class="self-managed-install-instructions">
10
+ <li>
11
+ <div class="flex">
12
+ First,
13
+ <LinkTo @route="console.extensions.developers.credentials" class="ml-1 text-blue-400 hover:opacity-50">generate a Fleetbase registry credentials token.</LinkTo>
14
+ </div>
15
+ </li>
16
+ <li>
17
+ <div class="flex flex-col">
18
+ <div class="mb-2">Install the Fleetbase CLI globally using npm:</div>
19
+ <ClickToCopy @value={{concat "npm i -g @fleetbase/cli"}}><code>npm i -g @fleetbase/cli</code></ClickToCopy>
20
+ </div>
21
+ </li>
22
+ <li>
23
+ <div class="flex flex-col">
24
+ <div class="mb-2">Set your authentication token with the CLI:</div>
25
+ <ClickToCopy @value={{concat "flb set-auth {YOUR_TOKEN}"}}><code>flb set-auth {YOUR_TOKEN}</code></ClickToCopy>
26
+ </div>
27
+ </li>
28
+ <li>
29
+ <div class="flex flex-col">
30
+ <div class="mb-2">Finally, install the extension using one of the following commands:</div>
31
+ <div>
32
+ <ClickToCopy @value={{concat "flb install fleetbase/" @options.extension.slug}}><code>flb install fleetbase/{{@options.extension.slug}}</code></ClickToCopy>
33
+ <div class="my-1 pl-2">or</div>
34
+ <ClickToCopy @value={{concat "flb install " @options.extension.public_id}}><code>flb install {{@options.extension.public_id}}</code></ClickToCopy>
35
+ </div>
36
+ </div>
37
+ </li>
38
+ </ol>
39
+ </div>
40
+ </Modal::Default>
@@ -9,6 +9,7 @@ export default class InstalledController extends Controller {
9
9
  @service notifications;
10
10
  @service socket;
11
11
  @service hostRouter;
12
+ @service abilities;
12
13
 
13
14
  @action about(extension) {
14
15
  this.modalsManager.show('modals/extension-details', {
@@ -31,6 +32,7 @@ export default class InstalledController extends Controller {
31
32
  acceptButtonText: 'Uninstall',
32
33
  acceptButtonIcon: 'trash',
33
34
  acceptButtonScheme: 'danger',
35
+ acceptButtonDisabled: this.abilities.cannot('registry-bridge uninstall extension'),
34
36
  process: null,
35
37
  step: null,
36
38
  stepDescription: 'Awaiting uninstall to begin...',
package/addon/engine.js CHANGED
@@ -19,7 +19,7 @@ export default class RegistryBridgeEngine extends Engine {
19
19
  };
20
20
  setupExtension = function (app, engine, universe) {
21
21
  // Register menu item in header
22
- universe.registerHeaderMenuItem('Extensions', 'console.extensions', { icon: 'shapes', priority: 99 });
22
+ universe.registerHeaderMenuItem('Extensions', 'console.extensions', { icon: 'shapes', priority: 99, slug: 'registry-bridge' });
23
23
  // Register admin controls
24
24
  universe.registerAdminMenuPanel(
25
25
  'Extensions Registry',
@@ -63,8 +63,10 @@ export default class RegistryExtensionModel extends Model {
63
63
  @attr('array') languages;
64
64
  @attr('object') meta;
65
65
  @attr('boolean') core_service;
66
+ @attr('boolean') self_managed;
66
67
  @attr('boolean') is_purchased;
67
68
  @attr('boolean') is_installed;
69
+ @attr('boolean') is_author;
68
70
  @attr('string', { defaultValue: 'pending' }) status;
69
71
 
70
72
  /** @dates */
@@ -3,6 +3,17 @@ import { inject as service } from '@ember/service';
3
3
 
4
4
  export default class ApplicationRoute extends Route {
5
5
  @service fetch;
6
+ @service notifications;
7
+ @service hostRouter;
8
+ @service abilities;
9
+ @service intl;
10
+
11
+ beforeModel() {
12
+ if (this.abilities.cannot('registry-bridge see extension')) {
13
+ this.notifications.warning(this.intl.t('common.unauthorized-access'));
14
+ return this.hostRouter.transitionTo('console');
15
+ }
16
+ }
6
17
 
7
18
  async setupController(controller) {
8
19
  super.setupController(...arguments);
@@ -3,6 +3,17 @@ import { inject as service } from '@ember/service';
3
3
 
4
4
  export default class DevelopersAnalyticsRoute extends Route {
5
5
  @service store;
6
+ @service notifications;
7
+ @service hostRouter;
8
+ @service abilities;
9
+ @service intl;
10
+
11
+ beforeModel() {
12
+ if (this.abilities.cannot('registry-bridge list extension-analytic')) {
13
+ this.notifications.warning(this.intl.t('common.unauthorized-access'));
14
+ return this.hostRouter.transitionTo('console');
15
+ }
16
+ }
6
17
 
7
18
  model() {
8
19
  return this.store.query('registry-extension', { is_author: 1 });
@@ -3,6 +3,17 @@ import { inject as service } from '@ember/service';
3
3
 
4
4
  export default class DevelopersCredentialsRoute extends Route {
5
5
  @service fetch;
6
+ @service notifications;
7
+ @service hostRouter;
8
+ @service abilities;
9
+ @service intl;
10
+
11
+ beforeModel() {
12
+ if (this.abilities.cannot('registry-bridge list registry-token')) {
13
+ this.notifications.warning(this.intl.t('common.unauthorized-access'));
14
+ return this.hostRouter.transitionTo('console');
15
+ }
16
+ }
6
17
 
7
18
  model() {
8
19
  return this.fetch.get('auth/registry-tokens', {}, { namespace: '~registry/v1' });
@@ -3,6 +3,17 @@ import { inject as service } from '@ember/service';
3
3
 
4
4
  export default class DevelopersExtensionsEditRoute extends Route {
5
5
  @service store;
6
+ @service notifications;
7
+ @service hostRouter;
8
+ @service abilities;
9
+ @service intl;
10
+
11
+ beforeModel() {
12
+ if (this.abilities.cannot('registry-bridge update extension-bundle')) {
13
+ this.notifications.warning(this.intl.t('common.unauthorized-access'));
14
+ return this.hostRouter.transitionTo('console');
15
+ }
16
+ }
6
17
 
7
18
  model(params) {
8
19
  return this.store.queryRecord('registry-extension', { public_id: params.public_id, single: true });
@@ -3,6 +3,17 @@ import { inject as service } from '@ember/service';
3
3
 
4
4
  export default class DevelopersExtensionsIndexRoute extends Route {
5
5
  @service store;
6
+ @service notifications;
7
+ @service hostRouter;
8
+ @service abilities;
9
+ @service intl;
10
+
11
+ beforeModel() {
12
+ if (this.abilities.cannot('registry-bridge list extension-bundle')) {
13
+ this.notifications.warning(this.intl.t('common.unauthorized-access'));
14
+ return this.hostRouter.transitionTo('console');
15
+ }
16
+ }
6
17
 
7
18
  model() {
8
19
  return this.store.query('registry-extension', { is_author: 1 });
@@ -1,3 +1,16 @@
1
1
  import Route from '@ember/routing/route';
2
+ import { inject as service } from '@ember/service';
2
3
 
3
- export default class DevelopersExtensionsNewRoute extends Route {}
4
+ export default class DevelopersExtensionsNewRoute extends Route {
5
+ @service notifications;
6
+ @service hostRouter;
7
+ @service abilities;
8
+ @service intl;
9
+
10
+ beforeModel() {
11
+ if (this.abilities.cannot('registry-bridge create extension-bundle')) {
12
+ this.notifications.warning(this.intl.t('common.unauthorized-access'));
13
+ return this.hostRouter.transitionTo('console');
14
+ }
15
+ }
16
+ }
@@ -1,3 +1,16 @@
1
1
  import Route from '@ember/routing/route';
2
+ import { inject as service } from '@ember/service';
2
3
 
3
- export default class DevelopersExtensionsRoute extends Route {}
4
+ export default class DevelopersExtensionsRoute extends Route {
5
+ @service notifications;
6
+ @service hostRouter;
7
+ @service abilities;
8
+ @service intl;
9
+
10
+ beforeModel() {
11
+ if (this.abilities.cannot('registry-bridge list extension-bundle')) {
12
+ this.notifications.warning(this.intl.t('common.unauthorized-access'));
13
+ return this.hostRouter.transitionTo('console');
14
+ }
15
+ }
16
+ }
@@ -3,6 +3,10 @@ import { inject as service } from '@ember/service';
3
3
 
4
4
  export default class DevelopersPaymentsIndexRoute extends Route {
5
5
  @service fetch;
6
+ @service notifications;
7
+ @service hostRouter;
8
+ @service abilities;
9
+ @service intl;
6
10
 
7
11
  queryParams = {
8
12
  page: { refreshModel: true },
@@ -11,6 +15,13 @@ export default class DevelopersPaymentsIndexRoute extends Route {
11
15
  query: { refreshModel: true },
12
16
  };
13
17
 
18
+ beforeModel() {
19
+ if (this.abilities.cannot('registry-bridge list extension-payment')) {
20
+ this.notifications.warning(this.intl.t('common.unauthorized-access'));
21
+ return this.hostRouter.transitionTo('console');
22
+ }
23
+ }
24
+
14
25
  model() {
15
26
  return this.fetch.get('payments/author-received', {}, { namespace: '~registry/v1' });
16
27
  }
@@ -3,10 +3,17 @@ import { inject as service } from '@ember/service';
3
3
 
4
4
  export default class DevelopersPaymentsOnboardRoute extends Route {
5
5
  @service fetch;
6
- @service hostRouter;
7
6
  @service notifications;
7
+ @service hostRouter;
8
+ @service abilities;
9
+ @service intl;
8
10
 
9
11
  async beforeModel() {
12
+ if (this.abilities.cannot('registry-bridge onboard extension-payment')) {
13
+ this.notifications.warning(this.intl.t('common.unauthorized-access'));
14
+ return this.hostRouter.transitionTo('console');
15
+ }
16
+
10
17
  try {
11
18
  const { hasStripeConnectAccount } = await this.fetch.get('payments/has-stripe-connect-account', {}, { namespace: '~registry/v1' });
12
19
  if (hasStripeConnectAccount) {
@@ -1,3 +1,16 @@
1
1
  import Route from '@ember/routing/route';
2
+ import { inject as service } from '@ember/service';
2
3
 
3
- export default class DevelopersPaymentsRoute extends Route {}
4
+ export default class DevelopersPaymentsRoute extends Route {
5
+ @service notifications;
6
+ @service hostRouter;
7
+ @service abilities;
8
+ @service intl;
9
+
10
+ beforeModel() {
11
+ if (this.abilities.cannot('registry-bridge list extension-payment')) {
12
+ this.notifications.warning(this.intl.t('common.unauthorized-access'));
13
+ return this.hostRouter.transitionTo('console');
14
+ }
15
+ }
16
+ }
@@ -3,6 +3,10 @@ import { inject as service } from '@ember/service';
3
3
 
4
4
  export default class ExploreCategoryRoute extends Route {
5
5
  @service store;
6
+ @service notifications;
7
+ @service hostRouter;
8
+ @service abilities;
9
+ @service intl;
6
10
 
7
11
  queryParams = {
8
12
  query: {
@@ -10,6 +14,13 @@ export default class ExploreCategoryRoute extends Route {
10
14
  },
11
15
  };
12
16
 
17
+ beforeModel() {
18
+ if (this.abilities.cannot('registry-bridge list extension')) {
19
+ this.notifications.warning(this.intl.t('common.unauthorized-access'));
20
+ return this.hostRouter.transitionTo('console');
21
+ }
22
+ }
23
+
13
24
  model({ slug }) {
14
25
  return this.store.queryRecord('category', { slug, for: 'extension_category', core_category: 1, single: 1 });
15
26
  }
@@ -3,6 +3,10 @@ import { inject as service } from '@ember/service';
3
3
 
4
4
  export default class ExploreIndexRoute extends Route {
5
5
  @service store;
6
+ @service notifications;
7
+ @service hostRouter;
8
+ @service abilities;
9
+ @service intl;
6
10
 
7
11
  queryParams = {
8
12
  query: {
@@ -10,6 +14,13 @@ export default class ExploreIndexRoute extends Route {
10
14
  },
11
15
  };
12
16
 
17
+ beforeModel() {
18
+ if (this.abilities.cannot('registry-bridge list extension')) {
19
+ this.notifications.warning(this.intl.t('common.unauthorized-access'));
20
+ return this.hostRouter.transitionTo('console');
21
+ }
22
+ }
23
+
13
24
  model(params) {
14
25
  const { query } = params;
15
26
  return this.store.query('registry-extension', { explore: 1, query });