@silexlabs/silex 3.0.0-alpha.39 → 3.0.0-alpha.41
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/.silex.js +38 -8
- package/dist/client/client/config.d.ts +11 -41
- package/dist/client/client/grapesjs/LoginDialog.d.ts +13 -0
- package/dist/client/client/grapesjs/PublicationManager.d.ts +69 -0
- package/dist/client/client/grapesjs/PublicationUi.d.ts +58 -0
- package/dist/client/client/grapesjs/index.d.ts +18 -44
- package/dist/client/client/grapesjs/storage.d.ts +1 -0
- package/dist/client/client/services.d.ts +35 -0
- package/dist/client/constants.d.ts +17 -0
- package/dist/client/css/admin.css +86 -3
- package/dist/client/js/main.js +201 -90
- package/dist/client/js/main.js.LICENSE.txt +6 -0
- package/dist/client/types.d.ts +200 -29
- package/dist/constants.d.ts +21 -0
- package/dist/constants.js +38 -1
- package/dist/constants.js.map +1 -0
- package/dist/events.d.ts +2 -0
- package/dist/events.js +17 -0
- package/dist/events.js.map +1 -0
- package/dist/plugins/AuthPlugin.d.ts +1 -0
- package/dist/plugins/AuthPlugin.js +38 -0
- package/dist/plugins/AuthPlugin.js.map +1 -0
- package/dist/plugins/Cache.d.ts +2 -0
- package/dist/plugins/Cache.js +32 -0
- package/dist/plugins/Cache.js.map +1 -0
- package/dist/plugins/FtpBackend.d.ts +92 -0
- package/dist/plugins/FtpBackend.js +604 -0
- package/dist/plugins/FtpBackend.js.map +1 -0
- package/dist/plugins/HooksPlugin.d.ts +7 -0
- package/dist/plugins/HooksPlugin.js +76 -0
- package/dist/plugins/HooksPlugin.js.map +1 -0
- package/dist/plugins/PublicationPlugin.d.ts +17 -0
- package/dist/plugins/PublicationPlugin.js +170 -0
- package/dist/plugins/PublicationPlugin.js.map +1 -0
- package/dist/plugins/SslPlugin.d.ts +1 -0
- package/dist/plugins/SslPlugin.js +86 -0
- package/dist/plugins/SslPlugin.js.map +1 -0
- package/dist/plugins/StaticPlugin.d.ts +1 -0
- package/dist/plugins/StaticPlugin.js +66 -0
- package/dist/plugins/StaticPlugin.js.map +1 -0
- package/dist/plugins/WebsitePlugin.d.ts +14 -0
- package/dist/plugins/WebsitePlugin.js +302 -0
- package/dist/plugins/WebsitePlugin.js.map +1 -0
- package/dist/server/backends/FsBackend.d.ts +42 -0
- package/dist/server/backends/FsBackend.js +269 -0
- package/dist/server/backends/FsBackend.js.map +1 -0
- package/dist/server/backends/index.d.ts +95 -0
- package/dist/server/backends/index.js +255 -0
- package/dist/server/backends/index.js.map +1 -0
- package/dist/server/cli.js +0 -1
- package/dist/server/config.d.ts +58 -0
- package/dist/server/config.js +128 -24
- package/dist/server/config.js.map +1 -0
- package/dist/server/express.js +31 -3
- package/dist/server/index.js +9 -20
- package/dist/server/jobs.d.ts +6 -0
- package/dist/server/jobs.js +75 -0
- package/dist/server/jobs.js.map +1 -0
- package/dist/server/utils/validation.d.ts +7 -0
- package/dist/server/utils/validation.js +37 -0
- package/dist/server/utils/validation.js.map +1 -0
- package/dist/types.d.ts +270 -0
- package/dist/types.js +35 -28
- package/dist/types.js.map +1 -0
- package/package.json +19 -8
- package/public/silex.js +17 -0
- package/src/css/dialog.scss +9 -2
- package/src/css/grapesjs.scss +79 -0
- package/src/css/project-bar.scss +2 -2
- package/src/example-client-config.js +17 -0
- package/src/html/templates/includes.pug +0 -1
- package/src/ts/client/config.ts +7 -6
- package/src/ts/client/grapesjs/LoginDialog.ts +190 -0
- package/src/ts/client/grapesjs/PublicationManager.ts +333 -0
- package/src/ts/client/grapesjs/PublicationUi.ts +320 -0
- package/src/ts/client/grapesjs/index.ts +88 -85
- package/src/ts/client/grapesjs/storage.ts +110 -0
- package/src/ts/client/index.ts +5 -7
- package/src/ts/client/services.ts +129 -0
- package/src/ts/constants.ts +21 -0
- package/src/ts/plugins/AuthPlugin.ts +38 -0
- package/src/ts/plugins/Cache.ts +28 -0
- package/src/ts/plugins/FtpBackend.ts +689 -0
- package/src/ts/plugins/HooksPlugin.ts +79 -0
- package/src/ts/plugins/PublicationPlugin.ts +170 -0
- package/src/{plugins/SslPlugin.js → ts/plugins/SslPlugin.ts} +24 -6
- package/src/ts/plugins/StaticPlugin.ts +68 -0
- package/src/ts/plugins/WebsitePlugin.ts +318 -0
- package/src/ts/server/backends/FsBackend.ts +266 -0
- package/src/ts/server/backends/index.ts +309 -0
- package/src/ts/server/cli.ts +0 -2
- package/src/ts/server/config.ts +103 -33
- package/src/ts/server/express.ts +37 -3
- package/src/ts/server/index.ts +24 -14
- package/src/ts/server/jobs.test.ts +74 -0
- package/src/ts/server/jobs.ts +77 -0
- package/src/ts/server/utils/validation.ts +33 -0
- package/src/ts/types.ts +167 -59
- package/dist/client/client/grapesjs/publish.d.ts +0 -18
- package/dist/page.js +0 -15
- package/src/plugins/AuthPlugin.js +0 -21
- package/src/plugins/Cache.js +0 -11
- package/src/plugins/DefaultBackend.js +0 -149
- package/src/plugins/ExpressPlugin.js +0 -39
- package/src/plugins/HooksPlugin.js +0 -58
- package/src/plugins/PublishPlugin.js +0 -69
- package/src/plugins/StaticPlugin.js +0 -48
- package/src/plugins/WebsitePlugin.js +0 -207
- package/src/ts/client/grapesjs/publish.ts +0 -403
- /package/.silex/default/{.silex.data.json → website.json} +0 -0
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import grapesjs from 'grapesjs'
|
|
18
|
+
import grapesjs, { Editor } from 'grapesjs'
|
|
19
19
|
import openImport from './openImport'
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -35,6 +35,7 @@ import symbolsPlugin from '@silexlabs/grapesjs-symbols'
|
|
|
35
35
|
import loadingPlugin from '@silexlabs/grapesjs-loading'
|
|
36
36
|
import fontsDialogPlugin, { cmdOpenFonts } from '@silexlabs/grapesjs-fonts'
|
|
37
37
|
import symbolDialogsPlugin, { cmdPromptAddSymbol } from './symbolDialogs'
|
|
38
|
+
import loginDialogPlugin, { LoginDialogOptions } from './LoginDialog'
|
|
38
39
|
|
|
39
40
|
import { pagePanelPlugin, cmdTogglePages, cmdAddPage } from './page-panel'
|
|
40
41
|
import { newPageDialog, cmdOpenNewPageDialog } from './new-page-dialog'
|
|
@@ -44,30 +45,36 @@ import { blocksPlugin } from './blocks'
|
|
|
44
45
|
import { semanticPlugin } from './semantic'
|
|
45
46
|
import { richTextPlugin } from './rich-text'
|
|
46
47
|
import { internalLinksPlugin } from './internal-links'
|
|
47
|
-
import {
|
|
48
|
+
import publicationManagerPlugin, { PublicationManagerOptions } from './PublicationManager'
|
|
48
49
|
import { templatePlugin } from './template'
|
|
49
50
|
import { eleventyPlugin } from './eleventy'
|
|
51
|
+
import { storagePlugin } from './storage'
|
|
52
|
+
import { API_WEBSITE_READ, API_WEBSITE_WRITE } from '../../constants'
|
|
53
|
+
import { BackendId, WebsiteId } from '../../types'
|
|
54
|
+
import { ApiError } from '../services'
|
|
50
55
|
|
|
51
56
|
const plugins = [
|
|
52
|
-
{name: './
|
|
53
|
-
{name: './
|
|
57
|
+
{name: './project-bar', value: projectBarPlugin}, // has to be before panels and dialogs
|
|
58
|
+
{name: './settings', value: settingsDialog},
|
|
54
59
|
{name: '@silexlabs/grapesjs-fonts', value: fontsDialogPlugin},
|
|
55
|
-
{name: './
|
|
56
|
-
{name: './
|
|
60
|
+
{name: './new-page-dialog', value: newPageDialog},
|
|
61
|
+
{name: './page-panel', value: pagePanelPlugin},
|
|
57
62
|
{name: 'grapesjs-blocks-basic', value: blocksBasicPlugin},
|
|
58
|
-
{name: './
|
|
59
|
-
{name: './
|
|
60
|
-
{name: './
|
|
63
|
+
{name: './blocks', value: blocksPlugin},
|
|
64
|
+
{name: './semantic', value: semanticPlugin},
|
|
65
|
+
{name: './rich-text', value: richTextPlugin},
|
|
61
66
|
{name: 'grapesjs-style-filter', value: styleFilterPlugin},
|
|
62
67
|
{name: 'grapesjs-plugin-forms', value: formPlugin},
|
|
63
68
|
{name: 'grapesjs-custom-code', value: codePlugin},
|
|
64
|
-
{name: './
|
|
69
|
+
{name: './internal-links', value: internalLinksPlugin},
|
|
65
70
|
{name: '@silexlabs/grapesjs-ui-suggest-classes', value: uiSuggestClasses},
|
|
66
|
-
{name: './
|
|
71
|
+
{name: './symbolDialogs', value: symbolDialogsPlugin},
|
|
67
72
|
{name: '@silexlabs/grapesjs-symbols', value: symbolsPlugin},
|
|
68
|
-
{name: './
|
|
69
|
-
{name: './
|
|
70
|
-
{name: './
|
|
73
|
+
{name: './PublicationManager', value: publicationManagerPlugin},
|
|
74
|
+
{name: './template', value: templatePlugin},
|
|
75
|
+
{name: './eleventy', value: eleventyPlugin},
|
|
76
|
+
{name: './storage', value: storagePlugin},
|
|
77
|
+
{name: './LoginDialog', value: loginDialogPlugin},
|
|
71
78
|
{name: '@silexlabs/grapesjs-loading', value: loadingPlugin},
|
|
72
79
|
]
|
|
73
80
|
// Check that all plugins are loaded correctly
|
|
@@ -83,7 +90,7 @@ plugins
|
|
|
83
90
|
const catBasic = 'Containers'
|
|
84
91
|
const catComponents = 'Components'
|
|
85
92
|
|
|
86
|
-
export function getEditorConfig(id:
|
|
93
|
+
export function getEditorConfig(id: WebsiteId, backendId: BackendId, rootUrl: string) {
|
|
87
94
|
return {
|
|
88
95
|
container: '#gjs',
|
|
89
96
|
height: '100%',
|
|
@@ -101,20 +108,27 @@ export function getEditorConfig(id: string, rootUrl: string) {
|
|
|
101
108
|
},
|
|
102
109
|
|
|
103
110
|
assetManager: {
|
|
104
|
-
upload: `${rootUrl}assets
|
|
111
|
+
upload: `${rootUrl}/assets/?id=${id}`,
|
|
105
112
|
},
|
|
106
113
|
|
|
107
114
|
storageManager: {
|
|
108
115
|
autoload: true,
|
|
109
|
-
type: 'remote',
|
|
116
|
+
//type: 'remote',
|
|
117
|
+
type: 'backend',
|
|
110
118
|
options: {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
119
|
+
backend: {
|
|
120
|
+
id,
|
|
121
|
+
backendId,
|
|
114
122
|
},
|
|
123
|
+
//remote: {
|
|
124
|
+
// urlStore: `${rootUrl}/${API_WEBSITE_WRITE}?id=${id}`,
|
|
125
|
+
// urlLoad: `${rootUrl}/${API_WEBSITE_READ}?id=${id}`,
|
|
126
|
+
//},
|
|
115
127
|
},
|
|
116
128
|
},
|
|
117
129
|
|
|
130
|
+
cssIcons: '/css/all.min.css',
|
|
131
|
+
|
|
118
132
|
plugins: plugins.map(p => p.value),
|
|
119
133
|
|
|
120
134
|
pluginsOpts: {
|
|
@@ -138,13 +152,13 @@ export function getEditorConfig(id: string, rootUrl: string) {
|
|
|
138
152
|
command: 'open-blocks',
|
|
139
153
|
}, {
|
|
140
154
|
id: 'symbols-btn',
|
|
141
|
-
className: 'symbols-btn fa
|
|
155
|
+
className: 'symbols-btn fa-regular fa-gem',
|
|
142
156
|
attributes: { title: 'Symbols', containerClassName: 'symbols-list-container', },
|
|
143
157
|
command: 'open-symbols',
|
|
144
158
|
buttons: [
|
|
145
159
|
{
|
|
146
160
|
id: 'symbol-create-button',
|
|
147
|
-
className: 'pages__add-page fa fa-plus',
|
|
161
|
+
className: 'pages__add-page fa-solid fa-plus',
|
|
148
162
|
label: 'Create symbol from selection',
|
|
149
163
|
command: cmdPromptAddSymbol,
|
|
150
164
|
},
|
|
@@ -161,17 +175,17 @@ export function getEditorConfig(id: string, rootUrl: string) {
|
|
|
161
175
|
}],
|
|
162
176
|
}, {
|
|
163
177
|
id: 'layer-manager-btn',
|
|
164
|
-
className: 'layer-manager-btn fa
|
|
178
|
+
className: 'layer-manager-btn fa-solid fa-layer-group',
|
|
165
179
|
attributes: { title: 'Layers', containerClassName: 'layer-manager-container', },
|
|
166
180
|
command: 'open-layers',
|
|
167
181
|
}, {
|
|
168
182
|
id: 'font-dialog-btn',
|
|
169
|
-
className: 'font-manager-btn fa
|
|
183
|
+
className: 'font-manager-btn fa-solid fa-font',
|
|
170
184
|
attributes: { title: 'Fonts' },
|
|
171
185
|
command: cmdOpenFonts,
|
|
172
186
|
}, {
|
|
173
187
|
id: 'settings-dialog-btn',
|
|
174
|
-
className: 'page-panel-btn fa
|
|
188
|
+
className: 'page-panel-btn fa-solid fa-gears',
|
|
175
189
|
attributes: { title: 'Settings' },
|
|
176
190
|
command: cmdOpenSettings,
|
|
177
191
|
}, {
|
|
@@ -186,11 +200,10 @@ export function getEditorConfig(id: string, rootUrl: string) {
|
|
|
186
200
|
},
|
|
187
201
|
],
|
|
188
202
|
},
|
|
189
|
-
[
|
|
203
|
+
[publicationManagerPlugin as any]: {
|
|
190
204
|
appendTo: 'options',
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
},
|
|
205
|
+
websiteId: id,
|
|
206
|
+
} as PublicationManagerOptions,
|
|
194
207
|
[pagePanelPlugin as any]: {
|
|
195
208
|
cmdOpenNewPageDialog,
|
|
196
209
|
cmdOpenSettings,
|
|
@@ -216,6 +229,9 @@ export function getEditorConfig(id: string, rootUrl: string) {
|
|
|
216
229
|
[fontsDialogPlugin as any]: {
|
|
217
230
|
api_key: 'AIzaSyAdJTYSLPlKz4w5Iqyy-JAF2o8uQKd1FKc',
|
|
218
231
|
},
|
|
232
|
+
[loginDialogPlugin as any]: {
|
|
233
|
+
id,
|
|
234
|
+
} as LoginDialogOptions,
|
|
219
235
|
},
|
|
220
236
|
}
|
|
221
237
|
}
|
|
@@ -223,74 +239,61 @@ export function getEditorConfig(id: string, rootUrl: string) {
|
|
|
223
239
|
// ////////////////////
|
|
224
240
|
// Initialize editor
|
|
225
241
|
// ////////////////////
|
|
226
|
-
// Expose grapes for plugins
|
|
227
|
-
// window['grapesjs'] = grapesjs
|
|
228
|
-
|
|
229
242
|
// Keep a ref to the editor singleton
|
|
230
|
-
let editor
|
|
231
|
-
|
|
232
|
-
export function initEditor(config, grapesJsPlugins) {
|
|
243
|
+
let editor: Editor
|
|
244
|
+
export async function initEditor(config, grapesJsPlugins) {
|
|
233
245
|
if(editor) throw new Error('Grapesjs editor already created')
|
|
246
|
+
return new Promise<Editor>((resolve, reject) => {
|
|
247
|
+
try {
|
|
248
|
+
editor = grapesjs.init({
|
|
249
|
+
plugins: [
|
|
250
|
+
...grapesJsPlugins,
|
|
251
|
+
...config.plugins,
|
|
252
|
+
],
|
|
253
|
+
...config,
|
|
254
|
+
})
|
|
255
|
+
} catch(e) {
|
|
256
|
+
console.error('Error initializing GrapesJs with plugins:', plugins, e)
|
|
257
|
+
reject(e)
|
|
258
|
+
}
|
|
234
259
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
} catch(e) {
|
|
244
|
-
console.error('Error initializing GrapesJs with plugins:', plugins, e)
|
|
245
|
-
throw e
|
|
246
|
-
}
|
|
260
|
+
// customize the editor
|
|
261
|
+
['text']
|
|
262
|
+
.forEach(id => editor.Blocks.get(id)?.set('category', 'Basics'))
|
|
263
|
+
;['image', 'video']
|
|
264
|
+
.forEach(id => editor.Blocks.get(id)?.set('category', 'Media'))
|
|
265
|
+
;['map']
|
|
266
|
+
.forEach(id => editor.Blocks.get(id)?.set('category', 'Components'))
|
|
267
|
+
editor.Blocks.render([])
|
|
247
268
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
.forEach(id => editor.Blocks.get(id)?.set('category', 'Components'))
|
|
255
|
-
editor.Blocks.render()
|
|
269
|
+
editor.Commands.add('gjs-open-import-webpage', openImport(editor, {
|
|
270
|
+
modalImportLabel: '',
|
|
271
|
+
modalImportContent: 'Paste a web page HTML code here.',
|
|
272
|
+
modalImportButton: 'Import',
|
|
273
|
+
modalImportTitle: 'Import from website',
|
|
274
|
+
}))
|
|
256
275
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
276
|
+
editor.on('load', () => {
|
|
277
|
+
// // move the options panel to the sidebar
|
|
278
|
+
// const optionsEl = editor.Panels.getPanel('options').view.el
|
|
279
|
+
// editor.Panels.getPanel('project-bar-panel').view.el
|
|
280
|
+
// .appendChild(options)
|
|
281
|
+
// options.style.width = 0
|
|
282
|
+
// options.style.position = 'static'
|
|
263
283
|
|
|
264
|
-
|
|
265
|
-
// // move the options panel to the sidebar
|
|
266
|
-
// const optionsEl = editor.Panels.getPanel('options').view.el
|
|
267
|
-
// editor.Panels.getPanel('project-bar-panel').view.el
|
|
268
|
-
// .appendChild(options)
|
|
269
|
-
// options.style.width = 0
|
|
270
|
-
// options.style.position = 'static'
|
|
271
|
-
// remove blocks and layers buttons from the properties
|
|
272
|
-
setTimeout(() => {
|
|
284
|
+
// remove blocks and layers buttons from the properties
|
|
273
285
|
editor.Panels.getPanel('views').buttons.remove('open-blocks')
|
|
274
286
|
editor.Panels.getPanel('views').buttons.remove('open-layers')
|
|
275
287
|
editor.Panels.getPanel('views').view.el.firstChild.style.justifyContent = 'initial' // align left
|
|
276
288
|
editor.Panels.getPanel('options').buttons.remove('export-template')
|
|
277
|
-
})
|
|
278
289
|
|
|
279
|
-
|
|
280
|
-
editor.StyleManager.addProperty('extra',{ extend: 'filter' })
|
|
281
|
-
})
|
|
290
|
+
editor.BlockManager.render(null)
|
|
282
291
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
})
|
|
289
|
-
})
|
|
290
|
-
editor.on('storage:error:store', (err) => {
|
|
291
|
-
editor.Modal.open({
|
|
292
|
-
title: 'Error saving website',
|
|
293
|
-
content: `This website could not be saved.<br><hr>${err}`,
|
|
292
|
+
// use the style filter plugin
|
|
293
|
+
editor.StyleManager.addProperty('extra',{ extend: 'filter' })
|
|
294
|
+
|
|
295
|
+
// GrapesJs editor is ready
|
|
296
|
+
resolve(editor)
|
|
294
297
|
})
|
|
295
298
|
})
|
|
296
299
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Silex website builder, free/libre no-code tool for makers.
|
|
3
|
+
* Copyright (c) 2023 lexoyo and Silex Labs foundation
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
* the Free Software Foundation, either version 3 of the License, or any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU Affero General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { BackendId, WebsiteId, WebsiteData } from '../../types'
|
|
19
|
+
import { ApiError, LoginStatus, websiteLoad, websiteSave } from '../services'
|
|
20
|
+
import { cmdLogin, eventLoggedIn, eventLoggedOut, getCurrentLoginStatus } from './LoginDialog'
|
|
21
|
+
|
|
22
|
+
async function wait(ms: number) {
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
setTimeout(resolve, ms)
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
export const storagePlugin = (editor) => {
|
|
28
|
+
editor.Storage.add('backend', {
|
|
29
|
+
async load(options: { id: WebsiteId, backendId: BackendId }): Promise<WebsiteData> {
|
|
30
|
+
try {
|
|
31
|
+
const status = await getCurrentLoginStatus(editor)
|
|
32
|
+
if(status?.backend?.isLoggedIn) {
|
|
33
|
+
const data = await websiteLoad(options.id, options.backendId)
|
|
34
|
+
return data
|
|
35
|
+
} else {
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
editor.once(eventLoggedIn, async () => {
|
|
38
|
+
try {
|
|
39
|
+
const data = await editor.Storage.load(options)
|
|
40
|
+
//editor.loadProjectData(data)
|
|
41
|
+
resolve(data)
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.error('backendPlugin load error', err)
|
|
44
|
+
reject(err)
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
editor.Commands.run(cmdLogin)
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error('backendPlugin load error', err)
|
|
52
|
+
throw err
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
async store(data: WebsiteData, options: { id: WebsiteId, backendId: BackendId }) {
|
|
57
|
+
try {
|
|
58
|
+
if(await getCurrentLoginStatus(editor)) {
|
|
59
|
+
await websiteSave(options.id, options.backendId, data)
|
|
60
|
+
} else {
|
|
61
|
+
editor.once(eventLoggedIn, () => {
|
|
62
|
+
return editor.Storage.save(options)
|
|
63
|
+
})
|
|
64
|
+
editor.Commands.run(cmdLogin)
|
|
65
|
+
}
|
|
66
|
+
} catch (err) {
|
|
67
|
+
console.error('backendPlugin store error', err)
|
|
68
|
+
throw err
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
// Handle errors
|
|
73
|
+
editor.on('storage:error:load', (err) => {
|
|
74
|
+
handleError(err)
|
|
75
|
+
})
|
|
76
|
+
editor.on('storage:error:store', (err) => {
|
|
77
|
+
handleError(err)
|
|
78
|
+
})
|
|
79
|
+
function handleError(err: Error) {
|
|
80
|
+
console.error('Error with loading / saving', err, err instanceof ApiError)
|
|
81
|
+
if (err instanceof ApiError) {
|
|
82
|
+
console.error('ApiError', err.code, err.message)
|
|
83
|
+
switch (err.code) {
|
|
84
|
+
case 404:
|
|
85
|
+
return editor.Modal.open({
|
|
86
|
+
title: 'Website not found',
|
|
87
|
+
content: `This website could not be found.<br><hr>${err.message}`,
|
|
88
|
+
})
|
|
89
|
+
case 403:
|
|
90
|
+
return editor.Modal.open({
|
|
91
|
+
title: 'Access denied',
|
|
92
|
+
content: `You are not allowed to access this website.<br><hr>${err.message}`,
|
|
93
|
+
})
|
|
94
|
+
case 401:
|
|
95
|
+
editor.trigger(eventLoggedOut)
|
|
96
|
+
return
|
|
97
|
+
default:
|
|
98
|
+
return editor.Modal.open({
|
|
99
|
+
title: 'Error',
|
|
100
|
+
content: `An error occured. ${err.message}`,
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
return editor.Modal.open({
|
|
105
|
+
title: 'Error',
|
|
106
|
+
content: `An error occured. ${err.message}`,
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
package/src/ts/client/index.ts
CHANGED
|
@@ -50,7 +50,7 @@ export async function start(options = {}) {
|
|
|
50
50
|
// Start grapesjs
|
|
51
51
|
console.log('start', {config})
|
|
52
52
|
try{
|
|
53
|
-
initEditor(config.editor, config.grapesJsPlugins)
|
|
53
|
+
await initEditor(config.editor, config.grapesJsPlugins)
|
|
54
54
|
} catch(e) {
|
|
55
55
|
console.error('Error starting Silex', e)
|
|
56
56
|
throw e
|
|
@@ -60,10 +60,8 @@ export async function start(options = {}) {
|
|
|
60
60
|
getEditor().I18n.setLocale(config.lang)
|
|
61
61
|
|
|
62
62
|
// End of loading
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
config.emit(EVENT_STARTUP_END)
|
|
68
|
-
})
|
|
63
|
+
console.log('Silex started')
|
|
64
|
+
document.querySelector('.silex-loader').classList.add('silex-dialog-hide')
|
|
65
|
+
document.querySelector('#gjs').classList.remove('silex-dialog-hide')
|
|
66
|
+
config.emit(EVENT_STARTUP_END)
|
|
69
67
|
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Silex website builder, free/libre no-code tool for makers.
|
|
3
|
+
* Copyright (c) 2023 lexoyo and Silex Labs foundation
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
* the Free Software Foundation, either version 3 of the License, or any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU Affero General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { API_BACKEND_LIST, API_BACKEND_LOGIN_STATUS, API_BACKEND_LOGOUT, API_PUBLICATION_PUBLISH, API_PUBLICATION_STATUS, API_WEBSITE_ASSETS_READ, API_WEBSITE_ASSETS_WRITE, API_WEBSITE_DELETE, API_WEBSITE_LIST, API_WEBSITE_READ, API_WEBSITE_WRITE } from '../constants'
|
|
19
|
+
import { ApiPublicationPublishRequestBody, ApiPublicationPublishRequestQuery, ApiPublicationPublishResponse, ApiPublicationStatusRequestQuery, ApiPublicationStatusResponse, BackendId, JobData, JobId, PublicationJobData, WebsiteId, ApiBackendListResponse, ApiBackendListRequestQuery, BackendData, BackendType, ApiWebsiteReadRequestQuery, ApiWebsiteReadResponse, WebsiteData, ApiWebsiteWriteRequestQuery, ApiWebsiteWriteRequestBody, ApiWebsiteWriteResponse, ApiWebsiteDeleteRequestQuery, ApiWebsiteAssetsReadRequestQuery, ApiWebsiteAssetsReadResponse, ApiWebsiteAssetsWriteRequestQuery, ApiWebsiteAssetsWriteRequestBody, ApiWebsiteAssetsWriteResponse, ClientSideFile, PublicationData, ApiBackendLoginStatusRequestQuery, ApiBackendLoginStatusResponse, BackendUser, WebsiteMeta, ApiBackendLogoutRequestQuery } from '../types'
|
|
20
|
+
|
|
21
|
+
export enum ApiRoute {
|
|
22
|
+
PUBLICATION_PUBLISH = API_PUBLICATION_PUBLISH,
|
|
23
|
+
PUBLICATION_STATUS = API_PUBLICATION_STATUS,
|
|
24
|
+
BACKEND_LOGOUT = API_BACKEND_LOGOUT,
|
|
25
|
+
BACKEND_LIST = API_BACKEND_LIST,
|
|
26
|
+
BACKEND_LOGIN_STATUS = API_BACKEND_LOGIN_STATUS,
|
|
27
|
+
WEBSITE_READ = API_WEBSITE_READ,
|
|
28
|
+
WEBSITE_WRITE = API_WEBSITE_WRITE,
|
|
29
|
+
WEBSITE_DELETE = API_WEBSITE_DELETE,
|
|
30
|
+
WEBSITE_LIST = API_WEBSITE_LIST,
|
|
31
|
+
WEBSITE_ASSETS_READ = API_WEBSITE_ASSETS_READ,
|
|
32
|
+
WEBSITE_ASSETS_WRITE = API_WEBSITE_ASSETS_WRITE,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface LoginStatus {
|
|
36
|
+
backend: BackendData,
|
|
37
|
+
user: BackendUser,
|
|
38
|
+
websiteMeta?: WebsiteMeta,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class ApiError extends Error {
|
|
42
|
+
constructor(message: string, public readonly code: number) {
|
|
43
|
+
super(message)
|
|
44
|
+
console.error({ message, code })
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const ROOT_URL = window.location.origin
|
|
49
|
+
|
|
50
|
+
export async function loginStatus(backendId: BackendId, type: BackendType, id?: WebsiteId): Promise<LoginStatus> {
|
|
51
|
+
return api<ApiBackendLoginStatusRequestQuery, null, ApiBackendLoginStatusResponse>(ApiRoute.BACKEND_LOGIN_STATUS, 'GET', {
|
|
52
|
+
backendId,
|
|
53
|
+
type,
|
|
54
|
+
id,
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function logout(backendId: BackendId, type: BackendType): Promise<void> {
|
|
59
|
+
return api<ApiBackendLogoutRequestQuery, null, null>(ApiRoute.BACKEND_LOGOUT, 'POST', { backendId, type })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function publish(websiteId: WebsiteId, data: PublicationData): Promise<[url: string, job: PublicationJobData]> {
|
|
63
|
+
const { url, job } = await api<ApiPublicationPublishRequestQuery, ApiPublicationPublishRequestBody, ApiPublicationPublishResponse>(
|
|
64
|
+
ApiRoute.PUBLICATION_PUBLISH,
|
|
65
|
+
'POST',
|
|
66
|
+
{ id: websiteId },
|
|
67
|
+
data
|
|
68
|
+
)
|
|
69
|
+
return [url, job]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function publicationStatus(jobId: JobId): Promise<PublicationJobData> {
|
|
73
|
+
return api<ApiPublicationStatusRequestQuery, null, ApiPublicationStatusResponse>(
|
|
74
|
+
ApiRoute.PUBLICATION_STATUS,
|
|
75
|
+
'GET',
|
|
76
|
+
{ jobId },
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function backendList(type: BackendType): Promise<BackendData[]> {
|
|
81
|
+
const list = await api<ApiBackendListRequestQuery, null, ApiBackendListResponse>(ApiRoute.BACKEND_LIST, 'GET', { type })
|
|
82
|
+
return list as BackendData[]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
export async function websiteList(backendId: BackendId): Promise<WebsiteMeta[]> {
|
|
87
|
+
return api<ApiWebsiteReadRequestQuery, null, ApiWebsiteReadResponse>(ApiRoute.WEBSITE_LIST, 'GET', { backendId }) as Promise<WebsiteMeta[]>
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function websiteLoad(id: WebsiteId, backendId: BackendId): Promise<WebsiteData> {
|
|
91
|
+
return api<ApiWebsiteReadRequestQuery, null, ApiWebsiteReadResponse>(ApiRoute.WEBSITE_READ, 'GET', { id, backendId }) as Promise<WebsiteData>
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function websiteSave(id: WebsiteId, backendId: BackendId, data: WebsiteData): Promise<void> {
|
|
95
|
+
const { message } = await api<ApiWebsiteWriteRequestQuery, ApiWebsiteWriteRequestBody, ApiWebsiteWriteResponse>(ApiRoute.WEBSITE_WRITE, 'POST', { id, backendId }, data)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function websiteDelete(id: WebsiteId, backendId: BackendId): Promise<void> {
|
|
99
|
+
await api<ApiWebsiteDeleteRequestQuery, null, null>(ApiRoute.WEBSITE_DELETE, 'DELETE', { id, backendId })
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function websiteAssetsLoad(path: string, id: WebsiteId, backendId: BackendId): Promise<string> {
|
|
103
|
+
return api<ApiWebsiteAssetsReadRequestQuery, null, ApiWebsiteAssetsReadResponse>(`${ApiRoute.WEBSITE_ASSETS_READ}/${path}`, 'GET', { id, backendId })
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function websiteAssetsSave(id: WebsiteId, backendId: BackendId, files: ClientSideFile[]): Promise<string[]> {
|
|
107
|
+
const { data } = await api<ApiWebsiteAssetsWriteRequestQuery, ApiWebsiteAssetsWriteRequestBody, ApiWebsiteAssetsWriteResponse>(ApiRoute.WEBSITE_ASSETS_WRITE, 'POST', { id, backendId }, files)
|
|
108
|
+
return data
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function api<ReqQuery, ReqBody, ResBody>(route: ApiRoute | string, method: string, query?: ReqQuery, payload?: ReqBody): Promise<ResBody> {
|
|
112
|
+
const url = `${ROOT_URL}/${route.toString()}?${new URLSearchParams(Object.entries(query)).toString()}`
|
|
113
|
+
const response = await fetch(url, {
|
|
114
|
+
method,
|
|
115
|
+
headers: {
|
|
116
|
+
'Content-Type': 'application/json',
|
|
117
|
+
},
|
|
118
|
+
body: payload && JSON.stringify(payload), // assuming that the payload needs to be stringified
|
|
119
|
+
credentials: 'include', // sends the cookies with the request
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
const json = await response.json()
|
|
124
|
+
console.error('There was a problem calling the API', response, json)
|
|
125
|
+
throw new ApiError(json.message, response.status)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return response.json()
|
|
129
|
+
}
|
package/src/ts/constants.ts
CHANGED
|
@@ -19,4 +19,25 @@
|
|
|
19
19
|
* @fileoverview define constants for Silex client and server
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
export const WEBSITE_DATA_FILE_NAME = 'website.json'
|
|
23
|
+
|
|
24
|
+
export const DEFAULT_WEBSITE_ID = 'default'
|
|
25
|
+
export const DEFAULT_BACKEND_ID = ''
|
|
26
|
+
export const DEFAULT_LANGUAGE = 'en'
|
|
22
27
|
export const CLIENT_CONFIG_FILE_NAME = 'silex.js'
|
|
28
|
+
|
|
29
|
+
export const API_BACKEND_LOGIN_STATUS = 'api/backend/login/status/'
|
|
30
|
+
export const API_BACKEND_LIST = 'api/backend/'
|
|
31
|
+
export const API_BACKEND_LOGOUT = 'api/backend/logout/'
|
|
32
|
+
export const API_BACKEND_LOGIN = 'api/backend/login/'
|
|
33
|
+
export const API_BACKEND_LOGIN_CALLBACK = 'api/backend/login/'
|
|
34
|
+
|
|
35
|
+
export const API_PUBLICATION_PUBLISH = 'api/publication/'
|
|
36
|
+
export const API_PUBLICATION_STATUS = '/api/publication/status/'
|
|
37
|
+
|
|
38
|
+
export const API_WEBSITE_READ = 'api/website/'
|
|
39
|
+
export const API_WEBSITE_WRITE = 'api/website/'
|
|
40
|
+
export const API_WEBSITE_DELETE = 'api/website/'
|
|
41
|
+
export const API_WEBSITE_LIST = 'api/website/'
|
|
42
|
+
export const API_WEBSITE_ASSETS_READ = 'assets/:path'
|
|
43
|
+
export const API_WEBSITE_ASSETS_WRITE = 'assets/'
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Silex website builder, free/libre no-code tool for makers.
|
|
3
|
+
* Copyright (c) 2023 lexoyo and Silex Labs foundation
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
* the Free Software Foundation, either version 3 of the License, or any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU Affero General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import express from 'express'
|
|
19
|
+
import { Directus } from '@directus/sdk'
|
|
20
|
+
|
|
21
|
+
export default async function(config, opts = {}) {
|
|
22
|
+
// config.on('silex:startup:start', ({app}) => {
|
|
23
|
+
// const router = express.Router()
|
|
24
|
+
// router.post(['/publish', '/website', '/assets'], async function(req, res, next) {
|
|
25
|
+
// try {
|
|
26
|
+
// const token = req.body.token || opts.directusToken
|
|
27
|
+
// const directus = new Directus(opts.directusUrl)
|
|
28
|
+
// directus.storage.auth_token = token
|
|
29
|
+
// const me = await directus.users.me.read()
|
|
30
|
+
// next()
|
|
31
|
+
// } catch(err) {
|
|
32
|
+
// console.error('Publish failed in auth module. Did you provide a directus token?', err.message)
|
|
33
|
+
// res.status(403).json({ message: err.message })
|
|
34
|
+
// }
|
|
35
|
+
// })
|
|
36
|
+
// app.use(router)
|
|
37
|
+
// })
|
|
38
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Silex website builder, free/libre no-code tool for makers.
|
|
3
|
+
* Copyright (c) 2023 lexoyo and Silex Labs foundation
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
* the Free Software Foundation, either version 3 of the License, or any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU Affero General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export function noCache (req, res, next) {
|
|
19
|
+
res.header('Cache-Control', 'private,no-cache,no-store,must-revalidate,proxy-revalidate')
|
|
20
|
+
res.header('Expires', '-1')
|
|
21
|
+
res.header('Pragma', 'no-cache')
|
|
22
|
+
next()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function withCache(req, res, next) {
|
|
26
|
+
res.header('Cache-Control', 'public,max-age=86400,immutable') // 24h
|
|
27
|
+
next()
|
|
28
|
+
}
|