@things-factory/auth-ui 10.1.17 → 10.1.18
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/dist-client/components/app-binding-detail.d.ts +46 -0
- package/dist-client/components/app-binding-detail.js +384 -0
- package/dist-client/components/app-binding-detail.js.map +1 -0
- package/dist-client/components/appliance-editor.d.ts +37 -0
- package/dist-client/components/appliance-editor.js +489 -0
- package/dist-client/components/appliance-editor.js.map +1 -0
- package/dist-client/components/application-editor.d.ts +31 -0
- package/dist-client/components/application-editor.js +491 -0
- package/dist-client/components/application-editor.js.map +1 -0
- package/dist-client/components/profile-component.js +28 -11
- package/dist-client/components/profile-component.js.map +1 -1
- package/dist-client/constants/application-type-parity.test.js +62 -0
- package/dist-client/constants/application-type-parity.test.js.map +1 -0
- package/dist-client/constants/application.d.ts +17 -7
- package/dist-client/constants/application.js +18 -8
- package/dist-client/constants/application.js.map +1 -1
- package/dist-client/pages/app-binding/app-bindings.d.ts +2 -0
- package/dist-client/pages/app-binding/app-bindings.js +19 -1
- package/dist-client/pages/app-binding/app-bindings.js.map +1 -1
- package/dist-client/pages/appliance/home.d.ts +2 -0
- package/dist-client/pages/appliance/home.js +21 -3
- package/dist-client/pages/appliance/home.js.map +1 -1
- package/dist-client/pages/application/applications.d.ts +2 -0
- package/dist-client/pages/application/applications.js +21 -3
- package/dist-client/pages/application/applications.js.map +1 -1
- package/dist-client/pages/secrets-are-not-printed.test.js +21 -3
- package/dist-client/pages/secrets-are-not-printed.test.js.map +1 -1
- package/dist-client/route.js +0 -15
- package/dist-client/route.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/form-values.d.ts +29 -0
- package/dist-client/utils/form-values.js +45 -0
- package/dist-client/utils/form-values.js.map +1 -1
- package/dist-client/utils/form-values.test.js +59 -1
- package/dist-client/utils/form-values.test.js.map +1 -1
- package/dist-client/utils/webauthn-verification.d.ts +56 -0
- package/dist-client/utils/webauthn-verification.js +79 -0
- package/dist-client/utils/webauthn-verification.js.map +1 -0
- package/dist-client/utils/webauthn-verification.test.js +79 -0
- package/dist-client/utils/webauthn-verification.test.js.map +1 -0
- package/package.json +3 -3
- package/things-factory.config.js +0 -5
- package/translations/en.json +25 -0
- package/translations/ja.json +26 -0
- package/translations/ko.json +25 -0
- package/translations/ms.json +26 -0
- package/translations/zh.json +26 -0
- package/dist-client/pages/app-binding/app-binding.d.ts +0 -2
- package/dist-client/pages/app-binding/app-binding.js +0 -371
- package/dist-client/pages/app-binding/app-binding.js.map +0 -1
- package/dist-client/pages/appliance/appliance.d.ts +0 -2
- package/dist-client/pages/appliance/appliance.js +0 -390
- package/dist-client/pages/appliance/appliance.js.map +0 -1
- package/dist-client/pages/appliance/register.js +0 -166
- package/dist-client/pages/appliance/register.js.map +0 -1
- package/dist-client/pages/application/application.d.ts +0 -2
- package/dist-client/pages/application/application.js +0 -384
- package/dist-client/pages/application/application.js.map +0 -1
- package/dist-client/pages/application/register.js +0 -246
- package/dist-client/pages/application/register.js.map +0 -1
- /package/dist-client/{pages/appliance/register.d.ts → constants/application-type-parity.test.d.ts} +0 -0
- /package/dist-client/{pages/application/register.d.ts → utils/webauthn-verification.test.d.ts} +0 -0
|
@@ -1,384 +0,0 @@
|
|
|
1
|
-
import { __decorate, __metadata } from "tslib";
|
|
2
|
-
import '@material/web/icon/icon.js';
|
|
3
|
-
import Clipboard from 'clipboard';
|
|
4
|
-
import gql from 'graphql-tag';
|
|
5
|
-
import { css, html } from 'lit';
|
|
6
|
-
import { customElement, property, queryAll } from 'lit/decorators.js';
|
|
7
|
-
import { APPLICATION_TYPES } from '../../constants/application.js';
|
|
8
|
-
import { client } from '@operato/graphql';
|
|
9
|
-
import { i18next } from '@operato/i18n';
|
|
10
|
-
import { navigate, PageView } from '@operato/shell';
|
|
11
|
-
import '../../components/secret-field.js';
|
|
12
|
-
let Application = class Application extends PageView {
|
|
13
|
-
static { this.styles = [
|
|
14
|
-
css `
|
|
15
|
-
:host { display: flex;
|
|
16
|
-
flex-direction: column;
|
|
17
|
-
overflow-y: auto;
|
|
18
|
-
position: relative;
|
|
19
|
-
|
|
20
|
-
background-color: var(--md-sys-color-background);
|
|
21
|
-
padding: var(--spacing-large);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
h2 { margin: var(--title-margin);
|
|
25
|
-
font: var(--title-font);
|
|
26
|
-
color: var(--title-text-color);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
[page-description] { margin: var(--page-description-margin);
|
|
30
|
-
font: var(--page-description-font);
|
|
31
|
-
color: var(--page-description-color);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
[icon] { position: absolute;
|
|
35
|
-
top: 10px;
|
|
36
|
-
right: 10px;
|
|
37
|
-
max-width: 80px;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
[icon] img { max-width: 100%;
|
|
41
|
-
max-height: 100%;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
[button-primary] { background-color: var(--button-primary-background-color);
|
|
45
|
-
border: var(--button-border);
|
|
46
|
-
border-radius: var(--button-border-radius);
|
|
47
|
-
margin: var(--button-margin);
|
|
48
|
-
padding: var(--button-primary-padding);
|
|
49
|
-
color: var(--button-primary-color);
|
|
50
|
-
font: var(--button-primary-font);
|
|
51
|
-
text-transform: var(--button-text-transform);
|
|
52
|
-
|
|
53
|
-
text-decoration: none;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
[button-primary]:hover { background-color: var(--button-primary-active-background-color);
|
|
57
|
-
box-shadow: var(--button-active-box-shadow);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
[fieldset-container] { background-color: var(--md-sys-color-surface);
|
|
61
|
-
margin: var(--spacing-large) 0 var(--spacing-medium) 0;
|
|
62
|
-
padding: var(--spacing-medium);
|
|
63
|
-
border-radius: var(--border-radius);
|
|
64
|
-
box-shadow: var(--box-shadow);
|
|
65
|
-
|
|
66
|
-
label { font: var(--label-font);
|
|
67
|
-
color: var(--label-color, var(--md-sys-color-on-surface));
|
|
68
|
-
text-transform: var(--label-text-transform);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
input,
|
|
72
|
-
select {
|
|
73
|
-
background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));
|
|
74
|
-
color: var(--input-field-color, var(--md-sys-color-on-surface));
|
|
75
|
-
border: var(--border-dim-color);
|
|
76
|
-
border-radius: var(--border-radius);
|
|
77
|
-
margin: var(--input-margin);
|
|
78
|
-
padding: var(--input-padding);
|
|
79
|
-
font: var(--input-font);
|
|
80
|
-
|
|
81
|
-
flex: 1;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
select:focus,
|
|
85
|
-
input:focus,
|
|
86
|
-
button { outline: none;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
form { max-width: var(--content-container-max-width);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
[fieldset-container] fieldset { margin: 0;
|
|
94
|
-
margin-top: -15px;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
fieldset { border-radius: var(--border-radius);
|
|
98
|
-
border: var(--border-dim-color);
|
|
99
|
-
margin: var(--fieldset-margin);
|
|
100
|
-
padding: var(--fieldset-padding);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
legend { padding: var(--legend-padding);
|
|
104
|
-
font-weight: bold;
|
|
105
|
-
color: var(--legend-color);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
[field-2column] { display: grid;
|
|
109
|
-
grid-template-columns: 1fr 1fr;
|
|
110
|
-
grid-gap: 15px;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
[field] { display: flex;
|
|
114
|
-
flex-direction: column;
|
|
115
|
-
position: relative;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
[grid-span] { grid-column: span 2;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
button { display: flex;
|
|
122
|
-
align-items: center;
|
|
123
|
-
gap: var(--spacing-small);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
button,
|
|
127
|
-
input[type='submit'],
|
|
128
|
-
[button-in-field] { background-color: var(--button-background-color);
|
|
129
|
-
border: var(--button-border);
|
|
130
|
-
border-radius: var(--button-border-radius);
|
|
131
|
-
padding: var(--button-padding);
|
|
132
|
-
color: var(--button-color);
|
|
133
|
-
font: var(--button-font);
|
|
134
|
-
text-transform: var(--button-text-transform);
|
|
135
|
-
|
|
136
|
-
margin: var(--spacing-medium) 0 var(--spacing-medium) var(--spacing-medium);
|
|
137
|
-
float: right;
|
|
138
|
-
text-decoration: none;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
button:hover,
|
|
142
|
-
input[type='submit']:hover { border: var(--button-activ-border);
|
|
143
|
-
box-shadow: var(--button-active-box-shadow);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
[button-in-field] { border-radius: 0 var(--button-border-radius) var(--button-border-radius) 0;
|
|
147
|
-
position: absolute;
|
|
148
|
-
top: 12px;
|
|
149
|
-
right: 0;
|
|
150
|
-
max-height: 36px;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
[input-hint] { font: var(--input-hint-font);
|
|
154
|
-
color: var(--input-hint-color);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
@media screen and (max-width: 480px) { [field] { grid-column: span 2;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
`
|
|
161
|
-
]; }
|
|
162
|
-
get context() {
|
|
163
|
-
return { title: { icon: 'apps',
|
|
164
|
-
text: this.application.name
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
render() {
|
|
169
|
-
var app = this.application || {};
|
|
170
|
-
return html `
|
|
171
|
-
<div>
|
|
172
|
-
<h2><md-icon>apps</md-icon> ${app.name}</h2>
|
|
173
|
-
<p page-description>${app.description}</p>
|
|
174
|
-
</div>
|
|
175
|
-
|
|
176
|
-
<div icon>
|
|
177
|
-
<img src=${app.icon} />
|
|
178
|
-
</div>
|
|
179
|
-
|
|
180
|
-
<form>
|
|
181
|
-
<div fieldset-container>
|
|
182
|
-
<fieldset>
|
|
183
|
-
<legend>application</legend>
|
|
184
|
-
<div field-2column>
|
|
185
|
-
<div field grid-span>
|
|
186
|
-
<label for="name">app name</label>
|
|
187
|
-
<input id="name" type="text" name="name" .value=${app.name} />
|
|
188
|
-
</div>
|
|
189
|
-
|
|
190
|
-
<div field grid-span>
|
|
191
|
-
<label for="description">description</label>
|
|
192
|
-
<input id="description" type="text" name="description" .value=${app.description} />
|
|
193
|
-
</div>
|
|
194
|
-
|
|
195
|
-
<div field grid-span>
|
|
196
|
-
<label for="email">contact email</label>
|
|
197
|
-
<input id="email" type="text" name="email" .value=${app.email} />
|
|
198
|
-
</div>
|
|
199
|
-
|
|
200
|
-
<div field grid-span>
|
|
201
|
-
<label for="type-selector">type</label>
|
|
202
|
-
<select id="type-selector" name="type">
|
|
203
|
-
${APPLICATION_TYPES.map(type => html `<option value="${app.type}" ?selected="${type === app.type ? true : false}">${type}</option>`)}
|
|
204
|
-
</select>
|
|
205
|
-
</div>
|
|
206
|
-
|
|
207
|
-
<div field grid-span>
|
|
208
|
-
<label for="url">application URL</label>
|
|
209
|
-
<input id="url" type="text" name="url" .value=${app.url} />
|
|
210
|
-
</div>
|
|
211
|
-
|
|
212
|
-
<div field grid-span>
|
|
213
|
-
<label for="icon">icon</label>
|
|
214
|
-
<input id="icon" type="text" name="icon" .value=${app.icon} />
|
|
215
|
-
</div>
|
|
216
|
-
|
|
217
|
-
<div field grid-span>
|
|
218
|
-
<label for="redirect-url">redirect URL</label>
|
|
219
|
-
<input id="redirect-url" type="text" name="redirectUrl" .value=${app.redirectUrl} />
|
|
220
|
-
</div>
|
|
221
|
-
|
|
222
|
-
<div field grid-span>
|
|
223
|
-
<label for="webhook">webhook</label>
|
|
224
|
-
<input id="webhook" type="text" name="webhook" .value=${app.webhook} />
|
|
225
|
-
</div>
|
|
226
|
-
|
|
227
|
-
<div field grid-span>
|
|
228
|
-
<label for="available-scopes">available-scopes</label>
|
|
229
|
-
<input id="available-scopes" type="text" .value=${app.availableScopes} readonly />
|
|
230
|
-
</div>
|
|
231
|
-
|
|
232
|
-
<div field grid-span>
|
|
233
|
-
<label for="auth-url">auth URL</label>
|
|
234
|
-
<input id="auth-url" type="text" .value=${app.authUrl} readonly />
|
|
235
|
-
<button dense button-in-field clipboard-copy @click=${e => e.preventDefault()}>copy</button>
|
|
236
|
-
The endpoint for authorization server. This is used to get the authorization code.
|
|
237
|
-
</div>
|
|
238
|
-
|
|
239
|
-
<div field grid-span>
|
|
240
|
-
<label for="access-token-url">access token URL</label>
|
|
241
|
-
<input id="access-token-url" type="text" .value=${app.accessTokenUrl} readonly />
|
|
242
|
-
<button dense button-in-field clipboard-copy @click=${e => e.preventDefault()}>copy</button>
|
|
243
|
-
The endpoint for authentication server. This is used to exchange the authorization code for an access
|
|
244
|
-
token.
|
|
245
|
-
</div>
|
|
246
|
-
</div>
|
|
247
|
-
</fieldset>
|
|
248
|
-
</div>
|
|
249
|
-
|
|
250
|
-
<div fieldset-container>
|
|
251
|
-
<fieldset>
|
|
252
|
-
<legend>credentials</legend>
|
|
253
|
-
<div field-2column>
|
|
254
|
-
<div field grid-span>
|
|
255
|
-
<label for="app-key">appKey</label>
|
|
256
|
-
<input id="app-key" type="text" .value=${app.appKey} readonly />
|
|
257
|
-
<button dense button-in-field clipboard-copy @click=${e => e.preventDefault()}>copy</button>
|
|
258
|
-
</div>
|
|
259
|
-
|
|
260
|
-
<!--
|
|
261
|
-
appKey above stays a plain field: it is the client identifier, meant to be
|
|
262
|
-
read and quoted. appSecret is the other half — it carries its own
|
|
263
|
-
@privilege for security:query, and it is the value an integration signs
|
|
264
|
-
with. It was printed in the open from the moment the page loaded.
|
|
265
|
-
-->
|
|
266
|
-
<div field grid-span>
|
|
267
|
-
<label for="app-secret">${i18next.t('field.client-secret')}</label>
|
|
268
|
-
<secret-field id="app-secret" .value=${app.appSecret || ''}></secret-field>
|
|
269
|
-
</div>
|
|
270
|
-
</div>
|
|
271
|
-
</fieldset>
|
|
272
|
-
</div>
|
|
273
|
-
|
|
274
|
-
<div class="button-container">
|
|
275
|
-
<button danger @click=${this.deleteApplication.bind(this)}>
|
|
276
|
-
<md-icon>delete_forever</md-icon>delete this app
|
|
277
|
-
</button>
|
|
278
|
-
<button @click=${this.updateApplication.bind(this)}><md-icon>save</md-icon>update</button>
|
|
279
|
-
<button><md-icon>passkey</md-icon>generate new secret</button>
|
|
280
|
-
</div>
|
|
281
|
-
</form>
|
|
282
|
-
`;
|
|
283
|
-
}
|
|
284
|
-
firstUpdated() {
|
|
285
|
-
this.clipboard = new Clipboard(this.copybuttons, { target: (trigger => trigger.parentElement.querySelector('input'))
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
async pageUpdated(changes, lifecycle, before) {
|
|
289
|
-
if (this.active) {
|
|
290
|
-
await this.fetchApplication();
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
async fetchApplication() {
|
|
294
|
-
const response = await client.query({ query: gql `
|
|
295
|
-
query ($id: String!) { application(id: $id) { id
|
|
296
|
-
name
|
|
297
|
-
description
|
|
298
|
-
email
|
|
299
|
-
url
|
|
300
|
-
icon
|
|
301
|
-
redirectUrl
|
|
302
|
-
authUrl
|
|
303
|
-
accessTokenUrl
|
|
304
|
-
webhook
|
|
305
|
-
availableScopes
|
|
306
|
-
appKey
|
|
307
|
-
appSecret
|
|
308
|
-
type
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
`,
|
|
312
|
-
variables: { id: this.lifecycle.resourceId
|
|
313
|
-
}
|
|
314
|
-
});
|
|
315
|
-
this.application = response.data.application;
|
|
316
|
-
}
|
|
317
|
-
async updateApplication(e) {
|
|
318
|
-
e.preventDefault();
|
|
319
|
-
const form = this.renderRoot.querySelector('form');
|
|
320
|
-
const formData = new FormData(form);
|
|
321
|
-
const patch = Array.from(formData.entries()).reduce((patch, [key, value]) => {
|
|
322
|
-
patch[key] = value;
|
|
323
|
-
return patch;
|
|
324
|
-
}, {});
|
|
325
|
-
const id = this.lifecycle.resourceId;
|
|
326
|
-
const response = await client.mutate({ mutation: gql `
|
|
327
|
-
mutation ($id: String!, $patch: ApplicationPatch!) { updateApplication(id: $id, patch: $patch) { id
|
|
328
|
-
name
|
|
329
|
-
description
|
|
330
|
-
email
|
|
331
|
-
url
|
|
332
|
-
icon
|
|
333
|
-
redirectUrl
|
|
334
|
-
webhook
|
|
335
|
-
appKey
|
|
336
|
-
appSecret
|
|
337
|
-
type
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
`,
|
|
341
|
-
variables: { id,
|
|
342
|
-
patch
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
if (response.errors) {
|
|
346
|
-
console.error('update fail');
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
this.application = { ...this.application, ...response.data.updateApplication };
|
|
350
|
-
console.log('update sucess');
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
async deleteApplication(e) {
|
|
354
|
-
e.preventDefault();
|
|
355
|
-
const id = this.lifecycle.resourceId;
|
|
356
|
-
const response = await client.mutate({ mutation: gql `
|
|
357
|
-
mutation ($id: String!) { deleteApplication(id: $id)
|
|
358
|
-
}
|
|
359
|
-
`,
|
|
360
|
-
variables: { id
|
|
361
|
-
}
|
|
362
|
-
});
|
|
363
|
-
const result = response.data.deleteApplication;
|
|
364
|
-
if (result) {
|
|
365
|
-
console.log('delete sucess');
|
|
366
|
-
navigate('applications');
|
|
367
|
-
}
|
|
368
|
-
else {
|
|
369
|
-
console.error('delete fail');
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
};
|
|
373
|
-
__decorate([
|
|
374
|
-
property({ type: Object }),
|
|
375
|
-
__metadata("design:type", Object)
|
|
376
|
-
], Application.prototype, "application", void 0);
|
|
377
|
-
__decorate([
|
|
378
|
-
queryAll('[clipboard-copy]'),
|
|
379
|
-
__metadata("design:type", Object)
|
|
380
|
-
], Application.prototype, "copybuttons", void 0);
|
|
381
|
-
Application = __decorate([
|
|
382
|
-
customElement('application-page')
|
|
383
|
-
], Application);
|
|
384
|
-
//# sourceMappingURL=application.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"application.js","sourceRoot":"","sources":["../../../client/pages/application/application.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport Clipboard from 'clipboard'\nimport gql from 'graphql-tag'\nimport { css, html } from 'lit'\nimport { customElement, property, queryAll } from 'lit/decorators.js'\nimport { APPLICATION_TYPES } from '../../constants/application.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next } from '@operato/i18n'\nimport { navigate, PageView } from '@operato/shell'\n\nimport '../../components/secret-field.js'\n\n@customElement('application-page')\nclass Application extends PageView { static styles = [\n css`\n :host { display: flex;\n flex-direction: column;\n overflow-y: auto;\n position: relative;\n\n background-color: var(--md-sys-color-background);\n padding: var(--spacing-large);\n }\n\n h2 { margin: var(--title-margin);\n font: var(--title-font);\n color: var(--title-text-color);\n }\n\n [page-description] { margin: var(--page-description-margin);\n font: var(--page-description-font);\n color: var(--page-description-color);\n }\n\n [icon] { position: absolute;\n top: 10px;\n right: 10px;\n max-width: 80px;\n }\n\n [icon] img { max-width: 100%;\n max-height: 100%;\n }\n\n [button-primary] { background-color: var(--button-primary-background-color);\n border: var(--button-border);\n border-radius: var(--button-border-radius);\n margin: var(--button-margin);\n padding: var(--button-primary-padding);\n color: var(--button-primary-color);\n font: var(--button-primary-font);\n text-transform: var(--button-text-transform);\n\n text-decoration: none;\n }\n\n [button-primary]:hover { background-color: var(--button-primary-active-background-color);\n box-shadow: var(--button-active-box-shadow);\n }\n\n [fieldset-container] { background-color: var(--md-sys-color-surface);\n margin: var(--spacing-large) 0 var(--spacing-medium) 0;\n padding: var(--spacing-medium);\n border-radius: var(--border-radius);\n box-shadow: var(--box-shadow);\n\n label { font: var(--label-font);\n color: var(--label-color, var(--md-sys-color-on-surface));\n text-transform: var(--label-text-transform);\n }\n\n input,\n select {\n background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n color: var(--input-field-color, var(--md-sys-color-on-surface));\n border: var(--border-dim-color);\n border-radius: var(--border-radius);\n margin: var(--input-margin);\n padding: var(--input-padding);\n font: var(--input-font);\n\n flex: 1;\n }\n\n select:focus,\n input:focus,\n button { outline: none;\n }\n\n form { max-width: var(--content-container-max-width);\n }\n }\n\n [fieldset-container] fieldset { margin: 0;\n margin-top: -15px;\n }\n\n fieldset { border-radius: var(--border-radius);\n border: var(--border-dim-color);\n margin: var(--fieldset-margin);\n padding: var(--fieldset-padding);\n }\n\n legend { padding: var(--legend-padding);\n font-weight: bold;\n color: var(--legend-color);\n }\n\n [field-2column] { display: grid;\n grid-template-columns: 1fr 1fr;\n grid-gap: 15px;\n }\n\n [field] { display: flex;\n flex-direction: column;\n position: relative;\n }\n\n [grid-span] { grid-column: span 2;\n }\n\n button { display: flex;\n align-items: center;\n gap: var(--spacing-small);\n }\n\n button,\n input[type='submit'],\n [button-in-field] { background-color: var(--button-background-color);\n border: var(--button-border);\n border-radius: var(--button-border-radius);\n padding: var(--button-padding);\n color: var(--button-color);\n font: var(--button-font);\n text-transform: var(--button-text-transform);\n\n margin: var(--spacing-medium) 0 var(--spacing-medium) var(--spacing-medium);\n float: right;\n text-decoration: none;\n }\n\n button:hover,\n input[type='submit']:hover { border: var(--button-activ-border);\n box-shadow: var(--button-active-box-shadow);\n }\n\n [button-in-field] { border-radius: 0 var(--button-border-radius) var(--button-border-radius) 0;\n position: absolute;\n top: 12px;\n right: 0;\n max-height: 36px;\n }\n\n [input-hint] { font: var(--input-hint-font);\n color: var(--input-hint-color);\n }\n\n @media screen and (max-width: 480px) { [field] { grid-column: span 2;\n }\n }\n `\n ]\n\n @property({ type: Object }) application: any\n @queryAll('[clipboard-copy]') copybuttons\n private clipboard?: Clipboard\n\n get context() { return { title: { icon: 'apps',\n text: this.application.name\n }\n }\n }\n\n render() { var app = this.application || {}\n\n return html`\n <div>\n <h2><md-icon>apps</md-icon> ${app.name}</h2>\n <p page-description>${app.description}</p>\n </div>\n\n <div icon>\n <img src=${app.icon} />\n </div>\n\n <form>\n <div fieldset-container>\n <fieldset>\n <legend>application</legend>\n <div field-2column>\n <div field grid-span>\n <label for=\"name\">app name</label>\n <input id=\"name\" type=\"text\" name=\"name\" .value=${app.name} />\n </div>\n\n <div field grid-span>\n <label for=\"description\">description</label>\n <input id=\"description\" type=\"text\" name=\"description\" .value=${app.description} />\n </div>\n\n <div field grid-span>\n <label for=\"email\">contact email</label>\n <input id=\"email\" type=\"text\" name=\"email\" .value=${app.email} />\n </div>\n\n <div field grid-span>\n <label for=\"type-selector\">type</label>\n <select id=\"type-selector\" name=\"type\">\n ${APPLICATION_TYPES.map(\n type =>\n html`<option value=\"${app.type}\" ?selected=\"${type === app.type ? true : false}\">${type}</option>`\n )}\n </select>\n </div>\n\n <div field grid-span>\n <label for=\"url\">application URL</label>\n <input id=\"url\" type=\"text\" name=\"url\" .value=${app.url} />\n </div>\n\n <div field grid-span>\n <label for=\"icon\">icon</label>\n <input id=\"icon\" type=\"text\" name=\"icon\" .value=${app.icon} />\n </div>\n\n <div field grid-span>\n <label for=\"redirect-url\">redirect URL</label>\n <input id=\"redirect-url\" type=\"text\" name=\"redirectUrl\" .value=${app.redirectUrl} />\n </div>\n\n <div field grid-span>\n <label for=\"webhook\">webhook</label>\n <input id=\"webhook\" type=\"text\" name=\"webhook\" .value=${app.webhook} />\n </div>\n\n <div field grid-span>\n <label for=\"available-scopes\">available-scopes</label>\n <input id=\"available-scopes\" type=\"text\" .value=${app.availableScopes} readonly />\n </div>\n\n <div field grid-span>\n <label for=\"auth-url\">auth URL</label>\n <input id=\"auth-url\" type=\"text\" .value=${app.authUrl} readonly />\n <button dense button-in-field clipboard-copy @click=${e => e.preventDefault()}>copy</button>\n The endpoint for authorization server. This is used to get the authorization code.\n </div>\n\n <div field grid-span>\n <label for=\"access-token-url\">access token URL</label>\n <input id=\"access-token-url\" type=\"text\" .value=${app.accessTokenUrl} readonly />\n <button dense button-in-field clipboard-copy @click=${e => e.preventDefault()}>copy</button>\n The endpoint for authentication server. This is used to exchange the authorization code for an access\n token.\n </div>\n </div>\n </fieldset>\n </div>\n\n <div fieldset-container>\n <fieldset>\n <legend>credentials</legend>\n <div field-2column>\n <div field grid-span>\n <label for=\"app-key\">appKey</label>\n <input id=\"app-key\" type=\"text\" .value=${app.appKey} readonly />\n <button dense button-in-field clipboard-copy @click=${e => e.preventDefault()}>copy</button>\n </div>\n\n <!--\n appKey above stays a plain field: it is the client identifier, meant to be\n read and quoted. appSecret is the other half — it carries its own\n @privilege for security:query, and it is the value an integration signs\n with. It was printed in the open from the moment the page loaded.\n -->\n <div field grid-span>\n <label for=\"app-secret\">${i18next.t('field.client-secret')}</label>\n <secret-field id=\"app-secret\" .value=${app.appSecret || ''}></secret-field>\n </div>\n </div>\n </fieldset>\n </div>\n\n <div class=\"button-container\">\n <button danger @click=${this.deleteApplication.bind(this)}>\n <md-icon>delete_forever</md-icon>delete this app\n </button>\n <button @click=${this.updateApplication.bind(this)}><md-icon>save</md-icon>update</button>\n <button><md-icon>passkey</md-icon>generate new secret</button>\n </div>\n </form>\n `\n }\n\n firstUpdated() { this.clipboard = new Clipboard(this.copybuttons, { target: (trigger => trigger.parentElement.querySelector('input')) as any\n })\n }\n\n async pageUpdated(changes, lifecycle, before) { if (this.active) { await this.fetchApplication()\n }\n }\n\n async fetchApplication() { const response = await client.query({ query: gql`\n query ($id: String!) { application(id: $id) { id\n name\n description\n email\n url\n icon\n redirectUrl\n authUrl\n accessTokenUrl\n webhook\n availableScopes\n appKey\n appSecret\n type\n }\n }\n `,\n variables: { id: this.lifecycle.resourceId\n }\n })\n\n this.application = response.data.application\n }\n\n async updateApplication(e) { e.preventDefault()\n\n const form = this.renderRoot.querySelector('form') as HTMLFormElement\n const formData = new FormData(form)\n\n const patch = Array.from(formData.entries()).reduce((patch, [key, value]) => { patch[key] = value\n return patch\n }, {})\n\n const id = this.lifecycle.resourceId\n\n const response = await client.mutate({ mutation: gql`\n mutation ($id: String!, $patch: ApplicationPatch!) { updateApplication(id: $id, patch: $patch) { id\n name\n description\n email\n url\n icon\n redirectUrl\n webhook\n appKey\n appSecret\n type\n }\n }\n `,\n variables: { id,\n patch\n }\n })\n\n if (response.errors) { console.error('update fail')\n } else { this.application = { ...this.application, ...response.data.updateApplication }\n console.log('update sucess')\n }\n }\n\n async deleteApplication(e) { e.preventDefault()\n\n const id = this.lifecycle.resourceId\n\n const response = await client.mutate({ mutation: gql`\n mutation ($id: String!) { deleteApplication(id: $id)\n }\n `,\n variables: { id\n }\n })\n\n const result = response.data.deleteApplication\n if (result) { console.log('delete sucess')\n navigate('applications')\n } else { console.error('delete fail')\n }\n }\n}\n"]}
|
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
import { __decorate, __metadata } from "tslib";
|
|
2
|
-
import gql from 'graphql-tag';
|
|
3
|
-
import { css, html } from 'lit';
|
|
4
|
-
import { customElement, property, query, state } from 'lit/decorators.js';
|
|
5
|
-
import { client } from '@operato/graphql';
|
|
6
|
-
import { i18next, localize } from '@operato/i18n';
|
|
7
|
-
import { navigate, PageView } from '@operato/shell';
|
|
8
|
-
import { FormFieldStyles } from '@operato/styles';
|
|
9
|
-
import { filledValues, missingRequired } from '../../utils/form-values.js';
|
|
10
|
-
let ApplicationRegister = class ApplicationRegister extends localize(i18next)(PageView) {
|
|
11
|
-
constructor() {
|
|
12
|
-
super(...arguments);
|
|
13
|
-
/** Required fields the person has not filled in yet. Empty until they press register. */
|
|
14
|
-
this.missing = [];
|
|
15
|
-
/** The mutation was refused. The link already said why; this says the press landed. */
|
|
16
|
-
this.failed = false;
|
|
17
|
-
}
|
|
18
|
-
static { this.styles = [
|
|
19
|
-
FormFieldStyles,
|
|
20
|
-
css `
|
|
21
|
-
:host { background-color: var(--md-sys-color-background);
|
|
22
|
-
padding: var(--spacing-large);
|
|
23
|
-
|
|
24
|
-
position: relative;
|
|
25
|
-
|
|
26
|
-
overflow: auto;
|
|
27
|
-
}
|
|
28
|
-
h2 { margin: var(--title-margin);
|
|
29
|
-
font: var(--title-font);
|
|
30
|
-
color: var(--title-text-color);
|
|
31
|
-
}
|
|
32
|
-
[page-description] { margin: var(--page-description-margin);
|
|
33
|
-
font: var(--page-description-font);
|
|
34
|
-
color: var(--page-description-color);
|
|
35
|
-
}
|
|
36
|
-
[field-error],
|
|
37
|
-
[form-error] { color: var(--md-sys-color-error);
|
|
38
|
-
font: var(--input-hint-font, inherit);
|
|
39
|
-
margin-top: var(--spacing-tiny, 2px);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
input[invalid] { border-color: var(--md-sys-color-error);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
[icon] { position: absolute;
|
|
46
|
-
top: 10px;
|
|
47
|
-
right: 10px;
|
|
48
|
-
|
|
49
|
-
max-width: 80px;
|
|
50
|
-
}
|
|
51
|
-
[icon] img { max-width: 100%;
|
|
52
|
-
max-height: 100%;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
img[pagedeco] { width: 150px;
|
|
56
|
-
margin: 0 var(--spacing-large);
|
|
57
|
-
float: left;
|
|
58
|
-
position: relative;
|
|
59
|
-
top: -20px;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
[form-container] { background-color: var(--md-sys-color-surface);
|
|
63
|
-
padding: var(--spacing-large);
|
|
64
|
-
border-radius: var(--border-radius);
|
|
65
|
-
box-shadow: var(--box-shadow);
|
|
66
|
-
max-width: var(--input-container-max-width);
|
|
67
|
-
position: relative;
|
|
68
|
-
top: -50px;
|
|
69
|
-
clear: both;
|
|
70
|
-
}
|
|
71
|
-
label { font: var(--label-font);
|
|
72
|
-
color: var(--label-color, var(--md-sys-color-on-surface));
|
|
73
|
-
text-transform: var(--label-text-transform);
|
|
74
|
-
}
|
|
75
|
-
input {
|
|
76
|
-
background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));
|
|
77
|
-
color: var(--input-field-color, var(--md-sys-color-on-surface));
|
|
78
|
-
border: var(--border-dim-color);
|
|
79
|
-
border-radius: var(--border-radius);
|
|
80
|
-
margin: var(--input-margin);
|
|
81
|
-
padding: var(--input-padding);
|
|
82
|
-
font: var(--input-font);
|
|
83
|
-
|
|
84
|
-
flex: 1;
|
|
85
|
-
}
|
|
86
|
-
select:focus,
|
|
87
|
-
input:focus { outline: none;
|
|
88
|
-
}
|
|
89
|
-
[field-2column] { display: grid;
|
|
90
|
-
grid-template-columns: 1fr 1fr;
|
|
91
|
-
grid-gap: 15px;
|
|
92
|
-
}
|
|
93
|
-
[field] { display: flex;
|
|
94
|
-
flex-direction: column;
|
|
95
|
-
}
|
|
96
|
-
[grid-span] { grid-column: span 2;
|
|
97
|
-
}
|
|
98
|
-
@media screen and (max-width: 480px) { img[pagedeco] { display: none;
|
|
99
|
-
}
|
|
100
|
-
[form-container] { position: initial;
|
|
101
|
-
}
|
|
102
|
-
[field] { grid-column: span 2;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
`
|
|
106
|
-
]; }
|
|
107
|
-
get context() {
|
|
108
|
-
return { title: `application registration`,
|
|
109
|
-
help: 'auth/application'
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
render() {
|
|
113
|
-
return html `
|
|
114
|
-
<img src="/assets/images/image-api.png" pagedeco />
|
|
115
|
-
<h2>${i18next.t('text.register new application')}</h2>
|
|
116
|
-
<p page-description>${i18next.t('text.an application signs in on its own behalf')}</p>
|
|
117
|
-
|
|
118
|
-
${this._icon
|
|
119
|
-
? html `
|
|
120
|
-
<div icon>
|
|
121
|
-
<img src=${this._icon} />
|
|
122
|
-
</div>
|
|
123
|
-
`
|
|
124
|
-
: html ``}
|
|
125
|
-
|
|
126
|
-
<div form-container>
|
|
127
|
-
<form>
|
|
128
|
-
<div field-2column>
|
|
129
|
-
<div field grid-span>
|
|
130
|
-
<label for="name">${i18next.t('field.name')}</label>
|
|
131
|
-
<input id="name" type="text" name="name" required ?invalid=${this.missing.includes('name')} />
|
|
132
|
-
${this.missing.includes('name')
|
|
133
|
-
? html `<div field-error>${i18next.t('text.this field is required')}</div>`
|
|
134
|
-
: ''}
|
|
135
|
-
</div>
|
|
136
|
-
|
|
137
|
-
<div field grid-span>
|
|
138
|
-
<label for="description">${i18next.t('field.description')}</label>
|
|
139
|
-
<input id="description" type="text" name="description" />
|
|
140
|
-
</div>
|
|
141
|
-
|
|
142
|
-
<div field grid-span>
|
|
143
|
-
<label for="app-url">${i18next.t('field.application url')}</label>
|
|
144
|
-
<input id="app-url" type="url" name="url" />
|
|
145
|
-
</div>
|
|
146
|
-
|
|
147
|
-
<div field grid-span>
|
|
148
|
-
<label for="redirect-url">${i18next.t('field.redirect url')}</label>
|
|
149
|
-
<input id="redirect-url" type="url" name="redirectUrl" />
|
|
150
|
-
</div>
|
|
151
|
-
|
|
152
|
-
<div field grid-span>
|
|
153
|
-
<label for="icon">${i18next.t('field.application icon')}</label>
|
|
154
|
-
<input id="icon" type="url" name="icon" @change=${e => (this._icon = e.target.value)} />
|
|
155
|
-
</div>
|
|
156
|
-
|
|
157
|
-
<div field grid-span>
|
|
158
|
-
<!-- type=email so the browser checks the shape the server's scalar will check. -->
|
|
159
|
-
<label for="email">${i18next.t('field.contact email')}</label>
|
|
160
|
-
<input id="email" type="email" name="email" />
|
|
161
|
-
</div>
|
|
162
|
-
|
|
163
|
-
${this.failed ? html `<div form-error grid-span>${i18next.t('text.could not register')}</div>` : ''}
|
|
164
|
-
|
|
165
|
-
<button grid-span type="submit" @click=${this.createApplication.bind(this)}>
|
|
166
|
-
${i18next.t('button.register')}
|
|
167
|
-
</button>
|
|
168
|
-
</div>
|
|
169
|
-
</form>
|
|
170
|
-
</div>
|
|
171
|
-
`;
|
|
172
|
-
}
|
|
173
|
-
async createApplication(e) {
|
|
174
|
-
e.preventDefault();
|
|
175
|
-
const formData = new FormData(this.form);
|
|
176
|
-
/*
|
|
177
|
-
`name` is the only field NewApplication declares non-null. The list comes from the
|
|
178
|
-
schema rather than from taste — requiring more here would invent a rule the product
|
|
179
|
-
does not have, and the person would be stopped by this screen alone.
|
|
180
|
-
*/
|
|
181
|
-
const missing = missingRequired(formData, ['name']);
|
|
182
|
-
if (missing.length) {
|
|
183
|
-
this.missing = missing;
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
this.missing = [];
|
|
187
|
-
/*
|
|
188
|
-
This used to send every input, including the untouched ones, each as an empty string.
|
|
189
|
-
`NewApplication.email` is a GraphQLEmailAddress and rejects '' — so registering without
|
|
190
|
-
a contact email failed, on a field the schema marks optional, and the screen printed
|
|
191
|
-
"creation fail." to the console where nobody would see it.
|
|
192
|
-
|
|
193
|
-
`filledValues` leaves an empty field out instead, which is what optional means on the
|
|
194
|
-
wire.
|
|
195
|
-
*/
|
|
196
|
-
const application = filledValues(formData);
|
|
197
|
-
const response = await client.mutate({ mutation: gql `
|
|
198
|
-
mutation ($application: NewApplication!) { createApplication(application: $application) { id
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
`,
|
|
202
|
-
variables: { application
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
if (response.errors) {
|
|
206
|
-
/*
|
|
207
|
-
The shared client link already raises a notify event carrying the server's reason, so
|
|
208
|
-
the person sees what went wrong. What was missing is that the screen stayed silent
|
|
209
|
-
and looked idle — the form is still filled in, and nothing said the press had landed.
|
|
210
|
-
*/
|
|
211
|
-
this.failed = true;
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
this.failed = false;
|
|
215
|
-
navigate(`application/${response.data.createApplication.id}`);
|
|
216
|
-
}
|
|
217
|
-
pageUpdated(changes, lifecycle, before) {
|
|
218
|
-
if (this.active) {
|
|
219
|
-
this.form.reset();
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
__decorate([
|
|
224
|
-
property({ type: Object }),
|
|
225
|
-
__metadata("design:type", Object)
|
|
226
|
-
], ApplicationRegister.prototype, "application", void 0);
|
|
227
|
-
__decorate([
|
|
228
|
-
query('form'),
|
|
229
|
-
__metadata("design:type", HTMLFormElement)
|
|
230
|
-
], ApplicationRegister.prototype, "form", void 0);
|
|
231
|
-
__decorate([
|
|
232
|
-
state(),
|
|
233
|
-
__metadata("design:type", String)
|
|
234
|
-
], ApplicationRegister.prototype, "_icon", void 0);
|
|
235
|
-
__decorate([
|
|
236
|
-
state(),
|
|
237
|
-
__metadata("design:type", Array)
|
|
238
|
-
], ApplicationRegister.prototype, "missing", void 0);
|
|
239
|
-
__decorate([
|
|
240
|
-
state(),
|
|
241
|
-
__metadata("design:type", Boolean)
|
|
242
|
-
], ApplicationRegister.prototype, "failed", void 0);
|
|
243
|
-
ApplicationRegister = __decorate([
|
|
244
|
-
customElement('application-register')
|
|
245
|
-
], ApplicationRegister);
|
|
246
|
-
//# sourceMappingURL=register.js.map
|