saasufy-components 2.1.0 → 2.3.0

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.
@@ -32,6 +32,7 @@ class CollectionAdder extends SocketConsumer {
32
32
  return [
33
33
  'collection-type',
34
34
  'collection-fields',
35
+ 'field-labels',
35
36
  'model-values',
36
37
  'submit-button-label',
37
38
  'hide-submit-button',
@@ -166,6 +167,10 @@ class CollectionAdder extends SocketConsumer {
166
167
  fieldValues: modelFieldValues
167
168
  } = this.getFieldDetails(this.getAttribute('model-values'));
168
169
 
170
+ let {
171
+ fieldValues: modelFieldLabels
172
+ } = this.getFieldDetails(this.getAttribute('field-labels'));
173
+
169
174
  this.modelFieldValues = modelFieldValues;
170
175
 
171
176
  if (this.collection) this.collection.destroy();
@@ -186,11 +191,12 @@ class CollectionAdder extends SocketConsumer {
186
191
  inputType = inputTypeParts[1];
187
192
  inputTypeParams = inputTypeParts[3] ? inputTypeParts[3].split(',') : [];
188
193
  }
194
+ let inputLabel = modelFieldLabels[field] == null ? field : modelFieldLabels[field];
189
195
  if (inputType === 'checkbox') {
190
196
  items.push(
191
197
  `<label>
192
198
  ${
193
- field
199
+ inputLabel
194
200
  }
195
201
  <input class="collection-adder-input" type="${
196
202
  inputType
@@ -205,7 +211,7 @@ class CollectionAdder extends SocketConsumer {
205
211
  <input class="collection-adder-input" type="text" name="${
206
212
  field
207
213
  }" placeholder="${
208
- field
214
+ inputLabel
209
215
  }" />
210
216
  <select class="collection-adder-select">
211
217
  <option value="" selected disabled hidden>Options</option>
@@ -218,7 +224,7 @@ class CollectionAdder extends SocketConsumer {
218
224
  `<textarea class="collection-adder-input" name="${
219
225
  field
220
226
  }" placeholder="${
221
- field
227
+ inputLabel
222
228
  }"></textarea>`
223
229
  );
224
230
  } else {
@@ -228,7 +234,7 @@ class CollectionAdder extends SocketConsumer {
228
234
  }" name="${
229
235
  field
230
236
  }" placeholder="${
231
- field
237
+ inputLabel
232
238
  }" />`
233
239
  );
234
240
  }
@@ -0,0 +1,98 @@
1
+ import { SocketConsumer } from './socket.js';
2
+ import { generateRandomHexString } from './utils.js';
3
+
4
+ const DEFAULT_AUTH_TIMEOUT = 10000;
5
+
6
+ class OAuthHandler extends SocketConsumer {
7
+ static get observedAttributes() {
8
+ return [
9
+ 'provider',
10
+ 'code-param-name',
11
+ 'state-param-name',
12
+ 'state-session-storage-key',
13
+ 'success-url',
14
+ 'success-location-hash',
15
+ 'navigate-event-path',
16
+ 'auth-timeout'
17
+ ];
18
+ }
19
+
20
+ successRedirect() {
21
+ let successURL = this.getAttribute('success-url');
22
+ let successLocationHash = this.getAttribute('success-location-hash');
23
+ let navigateEventPath = this.getAttribute('navigate-event-path');
24
+
25
+ if (successLocationHash) {
26
+ location.hash = successLocationHash;
27
+ } else if (successURL) {
28
+ location.href = successURL;
29
+ } else {
30
+ this.dispatchEvent(
31
+ new CustomEvent('navigate', {
32
+ detail: {
33
+ path: navigateEventPath
34
+ },
35
+ bubbles: true
36
+ })
37
+ );
38
+ }
39
+ }
40
+
41
+ async connectedCallback() {
42
+ this.innerHTML = '';
43
+
44
+ let provider = this.getAttribute('provider');
45
+ if (!provider) {
46
+ throw new Error('The provider attribute of oauth-handler was not specified');
47
+ }
48
+
49
+ let sessionStorageKey = this.getAttribute('state-session-storage-key') || `oauth.${provider}.state`;
50
+ let expectedOAuthState = sessionStorage.getItem(sessionStorageKey);
51
+ if (!expectedOAuthState) return;
52
+
53
+ let codeParamName = this.getAttribute('code-param-name') || 'code';
54
+ let stateParamName = this.getAttribute('state-param-name') || 'state';
55
+
56
+ let authTimeout = Number(this.getAttribute('auth-timeout') || DEFAULT_AUTH_TIMEOUT);
57
+
58
+ let urlSearchParams = new URLSearchParams(location.search);
59
+ let code = urlSearchParams.get(codeParamName);
60
+
61
+ if (code) {
62
+ let state = urlSearchParams.get(stateParamName);
63
+ let socket = this.getSocket();
64
+
65
+ history.replaceState({}, document.title, location.pathname);
66
+
67
+ if (state === expectedOAuthState) {
68
+ try {
69
+ await socket.invoke('log-in-oauth', { provider, data: { code } });
70
+ } catch (error) {
71
+ this.innerHTML = `
72
+ <div class="error">OAuth authentication failed. ${error.message}</div>
73
+ `;
74
+ return;
75
+ }
76
+ if (socket.authState === 'authenticated') {
77
+ this.successRedirect();
78
+ } else {
79
+ try {
80
+ await socket.listener('authenticate').once(authTimeout);
81
+ } catch (error) {
82
+ this.innerHTML = `
83
+ <div class="error">OAuth socket authentication timed out</div>
84
+ `;
85
+ return;
86
+ }
87
+ this.successRedirect();
88
+ }
89
+ } else {
90
+ this.innerHTML = `
91
+ <div class="error">OAuth state query parameter was invalid</div>
92
+ `;
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ window.customElements.define('oauth-handler', OAuthHandler);
package/oauth-link.js ADDED
@@ -0,0 +1,84 @@
1
+ import { renderTemplate, generateRandomHexString } from './utils.js';
2
+
3
+ class OAuthLink extends HTMLElement {
4
+ constructor() {
5
+ super();
6
+ this.isReady = false;
7
+ this.attachShadow({ mode: 'open' });
8
+
9
+ this.handleSlotChangeEvent = () => {
10
+ this.renderItem();
11
+ };
12
+ }
13
+
14
+ static get observedAttributes() {
15
+ return [
16
+ 'provider',
17
+ 'client-id',
18
+ 'state-size',
19
+ 'state-session-storage-key'
20
+ ];
21
+ }
22
+
23
+ attributeChangedCallback(name, oldValue, newValue) {
24
+ if (!this.isReady) return;
25
+ this.render();
26
+ }
27
+
28
+ connectedCallback() {
29
+ this.isReady = true;
30
+ this.render();
31
+ this.shadowRoot.addEventListener('slotchange', this.handleSlotChangeEvent);
32
+ }
33
+
34
+ disconnectedCallback() {
35
+ this.shadowRoot.removeEventListener('slotchange', this.handleSlotChangeEvent);
36
+ }
37
+
38
+ renderItem() {
39
+ let provider = this.getAttribute('provider');
40
+ if (!provider) {
41
+ throw new Error('The provider attribute of oauth-link was not specified');
42
+ }
43
+ let viewportNode = this.shadowRoot.querySelector('slot[name="viewport"]').assignedNodes()[0];
44
+ if (!viewportNode) return;
45
+ let itemTemplate = this.shadowRoot.querySelector('slot[name="item"]').assignedNodes()[0];
46
+ if (!itemTemplate) return;
47
+
48
+ let clientId = this.getAttribute('client-id');
49
+ let stateSize = Number(this.getAttribute('state-size') || 20);
50
+ let sessionStorageKey = this.getAttribute('state-session-storage-key') || `oauth.${provider}.state`;
51
+ let state = generateRandomHexString(stateSize);
52
+ sessionStorage.setItem(sessionStorageKey, state);
53
+
54
+ let itemString = renderTemplate(
55
+ itemTemplate.innerHTML,
56
+ {
57
+ oauth: {
58
+ clientId,
59
+ state
60
+ }
61
+ }
62
+ );
63
+ viewportNode.innerHTML = itemString;
64
+ }
65
+
66
+ render() {
67
+ let urlSearchParams = new URLSearchParams(location.search);
68
+ let code = urlSearchParams.get('code');
69
+
70
+ if (code) {
71
+ this.shadowRoot.innerHTML = '';
72
+ return;
73
+ }
74
+
75
+ this.shadowRoot.innerHTML = `
76
+ <slot name="item"></slot>
77
+ <slot name="viewport"></slot>
78
+ `;
79
+
80
+ this.renderItem();
81
+ }
82
+ }
83
+
84
+ window.customElements.define('oauth-link', OAuthLink);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "saasufy-components",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "Components for Saasufy.",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/utils.js CHANGED
@@ -268,6 +268,18 @@ export function updateConsumerElements(parentElement, consumers, value) {
268
268
  }
269
269
  }
270
270
 
271
+ export function generateRandomHexString(byteLength) {
272
+ let byteArray = new Uint8Array(byteLength);
273
+ crypto.getRandomValues(byteArray);
274
+ return Array.from(byteArray, byte => {
275
+ let firstChar = byte >> 4;
276
+ firstChar = firstChar < 10 ? String(firstChar) : String.fromCharCode(firstChar + 87);
277
+ let secondChar = byte & 0x0f;
278
+ secondChar = secondChar < 10 ? String(secondChar) : String.fromCharCode(secondChar + 87);
279
+ return `${firstChar}${secondChar}`;
280
+ }).join('');
281
+ }
282
+
271
283
  export function wait(duration) {
272
284
  return new Promise((resolve) => setTimeout(resolve, duration));
273
285
  }