@silexlabs/silex 3.0.0-alpha.151 → 3.0.0-alpha.152
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/client/grapesjs/project-bar.d.ts +3 -0
- package/dist/client/css/admin.css +74 -6
- package/dist/client/js/main.js +105 -104
- package/dist/plugins/server/plugins/server/GitlabConnector.d.ts +1 -0
- package/dist/plugins/server/plugins/server/GitlabConnector.js +7 -5
- package/dist/plugins/server/plugins/server/GitlabConnector.js.map +1 -1
- package/package.json +2 -1
- package/src/css/grapesjs-plugins.scss +34 -0
- package/src/css/grapesjs.scss +31 -0
- package/src/css/project-bar.scss +5 -7
- package/src/ts/client/grapesjs/css-props.ts +608 -257
- package/src/ts/client/grapesjs/index.ts +61 -11
- package/src/ts/client/grapesjs/internal-links.ts +11 -1
- package/src/ts/client/grapesjs/project-bar.ts +12 -3
- package/src/ts/client/grapesjs/settings.ts +44 -20
- package/src/ts/plugins/server/GitlabConnector.ts +8 -5
|
@@ -23,6 +23,8 @@ import openImport from './openImport'
|
|
|
23
23
|
* Handle plugins, options and initialization of the editor
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
+
const notificationContainer = document.createElement('div')
|
|
27
|
+
|
|
26
28
|
// ////////////////////
|
|
27
29
|
// Plugins
|
|
28
30
|
// ////////////////////
|
|
@@ -45,10 +47,11 @@ import rateLimitPlugin from '@silexlabs/grapesjs-storage-rate-limit'
|
|
|
45
47
|
import borderPugin from 'grapesjs-style-border'
|
|
46
48
|
import backgroundPlugin from 'grapesjs-style-bg'
|
|
47
49
|
import resizePanelPlugin from './resize-panel'
|
|
50
|
+
import notificationsPlugin, { NotificationEditor } from '@silexlabs/grapesjs-notifications'
|
|
48
51
|
|
|
49
52
|
import { pagePanelPlugin, cmdTogglePages, cmdAddPage } from './page-panel'
|
|
50
53
|
import { newPageDialog, cmdOpenNewPageDialog } from './new-page-dialog'
|
|
51
|
-
import { projectBarPlugin } from './project-bar'
|
|
54
|
+
import { PROJECT_BAR_PANEL_ID, projectBarPlugin } from './project-bar'
|
|
52
55
|
import { settingsDialog, cmdOpenSettings } from './settings'
|
|
53
56
|
import { blocksPlugin } from './blocks'
|
|
54
57
|
import { semanticPlugin } from './semantic'
|
|
@@ -90,7 +93,8 @@ const plugins = [
|
|
|
90
93
|
{name: './footer', value: footerPlugin},
|
|
91
94
|
{name: '@silexlabs/grapesjs-storage-rate-limit', value: rateLimitPlugin},
|
|
92
95
|
{name: 'grapesjs-style-border', value: borderPugin},
|
|
93
|
-
{name: './resize-panel', value: resizePanelPlugin}
|
|
96
|
+
{name: './resize-panel', value: resizePanelPlugin},
|
|
97
|
+
{name: '@silexlabs/grapesjs-notifications', value: notificationsPlugin},
|
|
94
98
|
]
|
|
95
99
|
// Check that all plugins are loaded correctly
|
|
96
100
|
plugins
|
|
@@ -201,9 +205,9 @@ export function getEditorConfig(config: ClientConfig): EditorConfig {
|
|
|
201
205
|
buttons: [
|
|
202
206
|
{
|
|
203
207
|
id: 'symbol-create-button',
|
|
204
|
-
className: '
|
|
205
|
-
label: 'Create symbol from selection',
|
|
208
|
+
className: 'gjs-pn-btn',
|
|
206
209
|
command: cmdPromptAddSymbol,
|
|
210
|
+
text: '\u271A',
|
|
207
211
|
},
|
|
208
212
|
],
|
|
209
213
|
}, {
|
|
@@ -212,9 +216,9 @@ export function getEditorConfig(config: ClientConfig): EditorConfig {
|
|
|
212
216
|
attributes: { title: 'Pages', containerClassName: 'page-panel-container', },
|
|
213
217
|
command: cmdTogglePages,
|
|
214
218
|
buttons: [{
|
|
215
|
-
className: '
|
|
219
|
+
className: 'gjs-pn-btn',
|
|
216
220
|
command: cmdAddPage,
|
|
217
|
-
text: '
|
|
221
|
+
text: '\u271A',
|
|
218
222
|
}],
|
|
219
223
|
}, {
|
|
220
224
|
id: 'layer-manager-btn',
|
|
@@ -237,6 +241,16 @@ export function getEditorConfig(config: ClientConfig): EditorConfig {
|
|
|
237
241
|
id: 'spacer',
|
|
238
242
|
attributes: {},
|
|
239
243
|
className: 'project-bar-spacer',
|
|
244
|
+
}, {
|
|
245
|
+
id: 'notifications-btn',
|
|
246
|
+
className: 'notifications-btn fa-regular fa-bell',
|
|
247
|
+
attributes: { title: 'Notifications', containerClassName: 'notifications-container', },
|
|
248
|
+
command: 'open-notifications',
|
|
249
|
+
buttons: [{
|
|
250
|
+
className: 'gjs-pn-btn',
|
|
251
|
+
command: 'notifications:clear',
|
|
252
|
+
text: '\u2716',
|
|
253
|
+
}],
|
|
240
254
|
}, {
|
|
241
255
|
id: 'dash2',
|
|
242
256
|
className: 'fa-solid fa-house',
|
|
@@ -275,10 +289,6 @@ export function getEditorConfig(config: ClientConfig): EditorConfig {
|
|
|
275
289
|
[filterStyles]: {
|
|
276
290
|
appendBefore: '.gjs-sm-sectors',
|
|
277
291
|
},
|
|
278
|
-
[internalLinksPlugin.toString()]: {
|
|
279
|
-
// FIXME: warn the user about links in error
|
|
280
|
-
onError: (errors) => console.warn('Links errors:', errors),
|
|
281
|
-
},
|
|
282
292
|
[codePlugin.toString()]: {
|
|
283
293
|
blockLabel: 'HTML',
|
|
284
294
|
blockCustomCode: {
|
|
@@ -308,6 +318,10 @@ export function getEditorConfig(config: ClientConfig): EditorConfig {
|
|
|
308
318
|
[imgPlugin.toString()]: {
|
|
309
319
|
replacedElements: config.replacedElements,
|
|
310
320
|
},
|
|
321
|
+
[notificationsPlugin.toString()]: {
|
|
322
|
+
container: notificationContainer,
|
|
323
|
+
reverse: true,
|
|
324
|
+
},
|
|
311
325
|
},
|
|
312
326
|
}
|
|
313
327
|
}
|
|
@@ -344,6 +358,26 @@ export async function initEditor(config: EditorConfig) {
|
|
|
344
358
|
modalImportTitle: 'Import from website',
|
|
345
359
|
}))
|
|
346
360
|
|
|
361
|
+
// Detect loading errors
|
|
362
|
+
// Display a useful notification
|
|
363
|
+
const typeConfig = {
|
|
364
|
+
view: {
|
|
365
|
+
onRender({editor, el, model}) {
|
|
366
|
+
const src = model.getAttributes().src
|
|
367
|
+
el.addEventListener('error', () => {
|
|
368
|
+
editor.runCommand('notifications:add', {
|
|
369
|
+
type: 'error',
|
|
370
|
+
group: 'Image loading error',
|
|
371
|
+
message: `Error loading image: ${src}`,
|
|
372
|
+
componentId: model.getId(),
|
|
373
|
+
})
|
|
374
|
+
})
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
}
|
|
378
|
+
editor.DomComponents.addType('image', typeConfig)
|
|
379
|
+
editor.DomComponents.addType('iframe', typeConfig)
|
|
380
|
+
|
|
347
381
|
// Adjustments to do when the editor is ready
|
|
348
382
|
editor.on('load', () => {
|
|
349
383
|
const views = editor.Panels.getPanel('views')
|
|
@@ -360,9 +394,25 @@ export async function initEditor(config: EditorConfig) {
|
|
|
360
394
|
// Render the block manager, otherwise it is empty
|
|
361
395
|
editor.BlockManager.render(null)
|
|
362
396
|
|
|
363
|
-
//
|
|
397
|
+
// Use the style filter plugin
|
|
364
398
|
editor.StyleManager.addProperty('extra', { extend: 'filter' })
|
|
365
399
|
|
|
400
|
+
// Add a class to the Style Manager's sticky top section
|
|
401
|
+
editor.SelectorManager.selectorTags.el.parentElement.classList.add('top-style-section')
|
|
402
|
+
|
|
403
|
+
// Add the notifications container
|
|
404
|
+
document.body.querySelector('.notifications-container').appendChild(notificationContainer)
|
|
405
|
+
// Mark the button as dirty when there are notifications
|
|
406
|
+
// TODO: move this in the notifications plugin options
|
|
407
|
+
editor.on(
|
|
408
|
+
'notifications:changed',
|
|
409
|
+
() => {
|
|
410
|
+
const notificationButton = editor.Panels.getPanel(PROJECT_BAR_PANEL_ID).view?.el.querySelector('.notifications-btn')
|
|
411
|
+
;(editor as NotificationEditor)
|
|
412
|
+
.NotificationManager.length ? notificationButton.classList.add('project-bar__dirty') : notificationButton.classList.remove('project-bar__dirty')
|
|
413
|
+
}
|
|
414
|
+
)
|
|
415
|
+
|
|
366
416
|
// GrapesJs editor is ready
|
|
367
417
|
resolve(editor)
|
|
368
418
|
})
|
|
@@ -60,7 +60,17 @@ export const internalLinksPlugin = (editor, opts) => {
|
|
|
60
60
|
issues.push(component)
|
|
61
61
|
}
|
|
62
62
|
})
|
|
63
|
-
|
|
63
|
+
// Add a notification for the issues
|
|
64
|
+
if(issues.length) {
|
|
65
|
+
issues.forEach(component => {
|
|
66
|
+
editor.runCommand('notifications:add', {
|
|
67
|
+
type: 'error',
|
|
68
|
+
message: `Page ${page.previous('name')} was removed, please fix the following links:`,
|
|
69
|
+
componentId: component.getId(),
|
|
70
|
+
group: 'Broken internal links',
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
}
|
|
64
74
|
break
|
|
65
75
|
}
|
|
66
76
|
})
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
import { Editor, Panel } from 'grapesjs'
|
|
19
19
|
import {html, render} from 'lit-html'
|
|
20
20
|
|
|
21
|
-
const
|
|
22
|
-
const containerPanelId = 'project-bar-container'
|
|
21
|
+
export const PROJECT_BAR_PANEL_ID = 'project-bar-panel'
|
|
22
|
+
export const containerPanelId = 'project-bar-container'
|
|
23
23
|
|
|
24
24
|
export interface PanelObject {
|
|
25
25
|
command: string | ((editor: Editor) => void)
|
|
@@ -34,6 +34,7 @@ export interface PanelObject {
|
|
|
34
34
|
text: string
|
|
35
35
|
className: string
|
|
36
36
|
}[]
|
|
37
|
+
onClick?: (editor: Editor) => void
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export const projectBarPlugin = (editor, opts) => {
|
|
@@ -44,7 +45,7 @@ export const projectBarPlugin = (editor, opts) => {
|
|
|
44
45
|
})
|
|
45
46
|
// create the project bar panel in grapesjs
|
|
46
47
|
editor.Panels.addPanel({
|
|
47
|
-
id:
|
|
48
|
+
id: PROJECT_BAR_PANEL_ID,
|
|
48
49
|
buttons: opts.panels,
|
|
49
50
|
visible : true,
|
|
50
51
|
})
|
|
@@ -113,6 +114,10 @@ export function addButton(editor: Editor, panel: PanelObject) {
|
|
|
113
114
|
document.body.classList.add('silex-squeeze-left')
|
|
114
115
|
editor.refresh()
|
|
115
116
|
}
|
|
117
|
+
// Call the onClick function if it exists
|
|
118
|
+
if(panel.onClick) panel.onClick(editor)
|
|
119
|
+
// Remove the dirty flag
|
|
120
|
+
el.classList.remove('project-bar__dirty')
|
|
116
121
|
},
|
|
117
122
|
stop() {
|
|
118
123
|
if(panel.attributes.containerClassName) {
|
|
@@ -121,6 +126,10 @@ export function addButton(editor: Editor, panel: PanelObject) {
|
|
|
121
126
|
document.body.classList.remove('silex-squeeze-left')
|
|
122
127
|
editor.refresh()
|
|
123
128
|
}
|
|
129
|
+
// Call the onClick function if it exists
|
|
130
|
+
if(panel.onClick) panel.onClick(editor)
|
|
131
|
+
// Remove the dirty flag
|
|
132
|
+
el.classList.remove('project-bar__dirty')
|
|
124
133
|
},
|
|
125
134
|
})
|
|
126
135
|
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import {html, render} from 'lit-html'
|
|
19
|
-
import {defaultSections, idCodeWrapper, isSite} from './settings-sections'
|
|
19
|
+
import {defaultSections, idCodeWrapper, isSite, SettingsSection} from './settings-sections'
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* @fileoverview This file contains the settings dialog. The config API lets plugins add sections to the settings dialog.
|
|
@@ -39,6 +39,7 @@ export const settingsDialog = (editor, opts) => {
|
|
|
39
39
|
console.info('Silex version:', SILEX_VERSION)
|
|
40
40
|
editor.Commands.add(cmdOpenSettings, {
|
|
41
41
|
run: (_, sender, {page}) => {
|
|
42
|
+
console.log('open settings', page)
|
|
42
43
|
modal = editor.Modal.open({
|
|
43
44
|
title: page ? 'Page settings' : 'Site Settings',
|
|
44
45
|
content: '',
|
|
@@ -101,6 +102,37 @@ export const settingsDialog = (editor, opts) => {
|
|
|
101
102
|
})
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
function showSection(item: SettingsSection) {
|
|
106
|
+
const aside = el.querySelector('aside.silex-bar') as HTMLElement
|
|
107
|
+
const ul = aside.querySelector('ul.silex-list--menu') as HTMLUListElement
|
|
108
|
+
const li = ul.querySelector('li#settings-' + item.id) as HTMLLIElement
|
|
109
|
+
currentSection = item
|
|
110
|
+
console.log('show section', item.id, {aside, ul, li})
|
|
111
|
+
if(!li) {
|
|
112
|
+
console.warn('Cannot find section', item.id, 'in the side bar, fallback to the first section')
|
|
113
|
+
showSection(defaultSections[0]) // Fallback to the first section
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
// Update active
|
|
117
|
+
Array.from(ul.querySelectorAll('.active')).forEach(el => el.classList.remove('active'))
|
|
118
|
+
li.classList.add('active')
|
|
119
|
+
const main = el.querySelector('main#settings__main') as HTMLElement
|
|
120
|
+
const mainItem = el.querySelector(`div#settings-${item.id}`)
|
|
121
|
+
if(!mainItem) {
|
|
122
|
+
console.warn('Cannot find section', item.id, 'in the settings dialog, fallback to the first section')
|
|
123
|
+
showSection(defaultSections[0]) // Fallback to the first section
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
// Update hidden
|
|
127
|
+
Array.from(main.querySelectorAll('.silex-hideable')).forEach(el => el.classList.add('silex-hidden'))
|
|
128
|
+
mainItem.classList.remove('silex-hidden')
|
|
129
|
+
// This messes up with the save / cancel mechanism
|
|
130
|
+
// displaySettings(editor, config, model)
|
|
131
|
+
// Refresh the code editor just in case it went from hidden to visible
|
|
132
|
+
// This makes it ready to be used when the user clicks on the tab
|
|
133
|
+
headEditor.refresh()
|
|
134
|
+
}
|
|
135
|
+
|
|
104
136
|
export function addSection(section, siteOrPage: 'site' | 'page', position: 'first' | 'last' | number) {
|
|
105
137
|
const sections = siteOrPage === 'site' ? sectionsSite : sectionsPage
|
|
106
138
|
if (position === 'first') {
|
|
@@ -122,6 +154,7 @@ export function removeSection(id, siteOrPage: 'site' | 'page') {
|
|
|
122
154
|
}
|
|
123
155
|
|
|
124
156
|
|
|
157
|
+
let currentSection
|
|
125
158
|
function displaySettings(editor, config, model = editor.getModel()) {
|
|
126
159
|
// Update the model with the current settings
|
|
127
160
|
const settings = model.get('settings') || {} as WebsiteSettings
|
|
@@ -129,7 +162,8 @@ function displaySettings(editor, config, model = editor.getModel()) {
|
|
|
129
162
|
// Get the current sections for page or site
|
|
130
163
|
const menuItemsCurrent = isSite(model) ? sectionsSite : sectionsPage
|
|
131
164
|
// Init the current section selection
|
|
132
|
-
|
|
165
|
+
currentSection = currentSection || menuItemsCurrent[0]
|
|
166
|
+
// Render the settings dialog
|
|
133
167
|
render(html`
|
|
134
168
|
<form class="silex-form">
|
|
135
169
|
<div class="silex-help">
|
|
@@ -146,25 +180,12 @@ function displaySettings(editor, config, model = editor.getModel()) {
|
|
|
146
180
|
<ul class="silex-list silex-list--menu">
|
|
147
181
|
${menuItemsCurrent.map(item => html`
|
|
148
182
|
<li
|
|
149
|
-
|
|
183
|
+
id="settings-${item.id}"
|
|
184
|
+
class=${item.id === currentSection.id ? 'active' : ''}
|
|
150
185
|
@click=${e => {
|
|
151
186
|
e.preventDefault()
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const ul = li.closest('ul')
|
|
155
|
-
const section = li.closest('section')
|
|
156
|
-
const mainItem = section.querySelector(`#settings-${item.id}`)
|
|
157
|
-
// Update active
|
|
158
|
-
Array.from(ul.querySelectorAll('.active')).forEach(el => el.classList.remove('active'))
|
|
159
|
-
li.classList.add('active')
|
|
160
|
-
// Update hidden
|
|
161
|
-
Array.from(section.querySelectorAll('.silex-hideable')).forEach(el => el.classList.add('silex-hidden'))
|
|
162
|
-
mainItem.classList.remove('silex-hidden')
|
|
163
|
-
// This messes up with the save / cancel mechanism
|
|
164
|
-
// displaySettings(editor, config, model)
|
|
165
|
-
// Refresh the code editor just in case it went from hidden to visible
|
|
166
|
-
// This makes it ready to be used when the user clicks on the tab
|
|
167
|
-
headEditor.refresh()
|
|
187
|
+
// Show the new section
|
|
188
|
+
showSection(item)
|
|
168
189
|
// Notify other plugins
|
|
169
190
|
editor.trigger(ClientEvent.SETTINGS_SECTION_CHANGE, item.id)
|
|
170
191
|
}}
|
|
@@ -174,7 +195,7 @@ function displaySettings(editor, config, model = editor.getModel()) {
|
|
|
174
195
|
`)}
|
|
175
196
|
</ul>
|
|
176
197
|
</aside>
|
|
177
|
-
<main>
|
|
198
|
+
<main id="settings__main">
|
|
178
199
|
${menuItemsCurrent.map(item => item.render(settings, model))}
|
|
179
200
|
</main>
|
|
180
201
|
</section>
|
|
@@ -185,6 +206,9 @@ function displaySettings(editor, config, model = editor.getModel()) {
|
|
|
185
206
|
</footer>
|
|
186
207
|
</form>
|
|
187
208
|
`, el)
|
|
209
|
+
// Display the current section
|
|
210
|
+
showSection(currentSection)
|
|
211
|
+
// Init the code editor
|
|
188
212
|
el.querySelector(`#${idCodeWrapper}`)?.appendChild(headEditor.getElement())
|
|
189
213
|
headEditor.setContent(settings.head || '')
|
|
190
214
|
}
|
|
@@ -38,6 +38,7 @@ export interface GitlabOptions {
|
|
|
38
38
|
//metaRepoFile: string
|
|
39
39
|
repoPrefix: string
|
|
40
40
|
scope: string
|
|
41
|
+
domain: string
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
interface GitlabToken {
|
|
@@ -189,6 +190,8 @@ export default class GitlabConnector implements StorageConnector {
|
|
|
189
190
|
} as GitlabOptions
|
|
190
191
|
if(!this.options.clientId) throw new Error('Missing Gitlab client ID')
|
|
191
192
|
if(!this.options.clientSecret) throw new Error('Missing Gitlab client secret')
|
|
193
|
+
if(!this.options.domain) throw new Error('Missing Gitlab domain')
|
|
194
|
+
|
|
192
195
|
}
|
|
193
196
|
|
|
194
197
|
// **
|
|
@@ -224,7 +227,7 @@ export default class GitlabConnector implements StorageConnector {
|
|
|
224
227
|
async readFile(session: GitlabSession, websiteId: string, fileName: string): Promise<Buffer> {
|
|
225
228
|
const safePath = fileName.replace(/^\//, '')
|
|
226
229
|
// Call the API
|
|
227
|
-
const url =
|
|
230
|
+
const url = `${this.options.domain}/api/v4/projects/${websiteId}/repository/files/${safePath}?ref=${this.options.branch}&access_token=${session.gitlab?.token?.access_token}`
|
|
228
231
|
const response = await fetch(url, {
|
|
229
232
|
method: 'GET',
|
|
230
233
|
headers: {
|
|
@@ -273,7 +276,7 @@ export default class GitlabConnector implements StorageConnector {
|
|
|
273
276
|
const token = session?.gitlab?.token
|
|
274
277
|
const tokenParam = token ? `access_token=${token.access_token}&` : ''
|
|
275
278
|
const paramsStr = Object.entries(params).map(([k, v]) => `${k}=${encodeURIComponent((v as any).toString())}`).join('&')
|
|
276
|
-
const url =
|
|
279
|
+
const url = `${this.options.domain}/${path}?${tokenParam}${paramsStr}`
|
|
277
280
|
const headers = {
|
|
278
281
|
'Content-Type': 'application/json',
|
|
279
282
|
}
|
|
@@ -304,7 +307,7 @@ export default class GitlabConnector implements StorageConnector {
|
|
|
304
307
|
client_id: this.options.clientId,
|
|
305
308
|
client_secret: this.options.clientSecret,
|
|
306
309
|
}
|
|
307
|
-
const response = await fetch('
|
|
310
|
+
const response = await fetch(this.options.domain + '/oauth/token', {
|
|
308
311
|
method: 'POST',
|
|
309
312
|
headers: {
|
|
310
313
|
'Content-Type': 'application/json',
|
|
@@ -391,7 +394,7 @@ export default class GitlabConnector implements StorageConnector {
|
|
|
391
394
|
codeVerifier,
|
|
392
395
|
codeChallenge,
|
|
393
396
|
}
|
|
394
|
-
return
|
|
397
|
+
return `${this.options.domain}/oauth/authorize?client_id=${this.options.clientId}&redirect_uri=${redirect_uri}&response_type=code&state=${session.gitlab.state}&scope=${this.options.scope}&code_challenge=${codeChallenge}&code_challenge_method=S256`
|
|
395
398
|
}
|
|
396
399
|
|
|
397
400
|
getOptions(formData: object): object {
|
|
@@ -420,7 +423,7 @@ export default class GitlabConnector implements StorageConnector {
|
|
|
420
423
|
if(!session.gitlab?.codeVerifier) throw new ApiError('Missing code verifier', 401)
|
|
421
424
|
if(!session.gitlab?.codeChallenge) throw new ApiError('Missing code challenge', 401)
|
|
422
425
|
|
|
423
|
-
const response = await fetch('
|
|
426
|
+
const response = await fetch(this.options.domain + '/oauth/token', {
|
|
424
427
|
method: 'POST',
|
|
425
428
|
headers: {
|
|
426
429
|
'Content-Type': 'application/json',
|