@silexlabs/silex 3.0.0-alpha.30 → 3.0.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.silex/default/.silex.data.json +1 -0
- package/.silex.js +1 -8
- package/CONTRIBUTING.md +48 -25
- package/README.md +96 -0
- package/dist/client/css/admin.css +4 -66
- package/dist/client/js/main.js +53 -103
- package/dist/client/js/main.js.LICENSE.txt +3 -19
- package/dist/constants.js +4 -0
- package/dist/page.js +15 -0
- package/dist/server/cli.js +9 -0
- package/dist/server/config.js +31 -11
- package/dist/server/{start.js → express.js} +1 -1
- package/dist/server/index.js +9 -3
- package/dist/types.js +34 -0
- package/package.json +35 -34
- package/public/silex.js +25 -0
- package/src/example-client-config.js +47 -0
- package/src/plugins/DefaultBackend.js +40 -4
- package/src/plugins/ExpressPlugin.js +9 -0
- package/src/plugins/PublishPlugin.js +4 -4
- package/src/plugins/WebsitePlugin.js +77 -18
- package/src/ts/client/config.ts +20 -2
- package/src/ts/client/grapesjs/blocks.ts +18 -86
- package/src/ts/client/grapesjs/index.ts +27 -9
- package/src/ts/client/grapesjs/internal-links.test.ts +1 -0
- package/src/ts/client/grapesjs/publish.ts +4 -4
- package/src/ts/client/grapesjs/semantic.ts +1 -0
- package/src/ts/client/index.ts +21 -8
- package/src/ts/constants.ts +5 -0
- package/src/ts/server/cli.ts +6 -0
- package/src/ts/server/config.ts +45 -16
- package/src/ts/server/{start.ts → express.ts} +1 -1
- package/src/ts/server/index.ts +9 -2
- package/src/ts/types.ts +6 -12
- package/tsconfig-server-debug.json +2 -3
- package/tsconfig-server-release.json +2 -3
- package/tsconfig.json +2 -3
- package/webpack.config.js +0 -1
- package/public/example-config.js +0 -31
- package/readme.md +0 -51
- /package/dist/{server/events.js → events.js} +0 -0
- /package/src/ts/{server/events.ts → events.ts} +0 -0
|
@@ -3,9 +3,12 @@ const { resolve } = require('path')
|
|
|
3
3
|
const formidable = require('formidable')
|
|
4
4
|
|
|
5
5
|
const { noCache } = require('./Cache')
|
|
6
|
+
const { defaultSite } = require('@silexlabs/silex').default.types
|
|
6
7
|
|
|
7
8
|
const EVENT_READ_START = 'EVENT_READ_START'
|
|
8
9
|
const EVENT_READ_END = 'EVENT_READ_END'
|
|
10
|
+
const EVENT_DELETE_START = 'EVENT_WRITE_START'
|
|
11
|
+
const EVENT_DELETE_END = 'EVENT_WRITE_END'
|
|
9
12
|
const EVENT_WRITE_START = 'EVENT_WRITE_START'
|
|
10
13
|
const EVENT_WRITE_END = 'EVENT_WRITE_END'
|
|
11
14
|
const EVENT_ASSET_READ_START = 'EVENT_ASSET_READ_START'
|
|
@@ -18,13 +21,15 @@ module.exports = async function(config, opts = {}) {
|
|
|
18
21
|
const options = {
|
|
19
22
|
// Defaults
|
|
20
23
|
backend: 'src/plugins/DefaultBackend.js',
|
|
24
|
+
// Default data for new websites
|
|
25
|
+
defaultSite,
|
|
21
26
|
// Options
|
|
22
27
|
...opts
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
// Link to the desired backend
|
|
26
31
|
const backend = resolve(__dirname, '../..', options.backend)
|
|
27
|
-
const { assetsDir, assetUrl, init, writeData, readData } = require(backend)
|
|
32
|
+
const { assetsDir, assetUrl, init, writeData, readData, list, del } = require(backend)
|
|
28
33
|
|
|
29
34
|
config.on('silex:startup:start', ({app}) => {
|
|
30
35
|
const router = Router()
|
|
@@ -32,6 +37,7 @@ module.exports = async function(config, opts = {}) {
|
|
|
32
37
|
// website specials
|
|
33
38
|
router.get('/website', readWebsite)
|
|
34
39
|
router.post('/website', writeWebsite)
|
|
40
|
+
router.delete('/website', deleteWebsite)
|
|
35
41
|
router.get(/\/assets\/(.*)/, readAsset)
|
|
36
42
|
router.post('/assets', writeAsset)
|
|
37
43
|
|
|
@@ -39,21 +45,40 @@ module.exports = async function(config, opts = {}) {
|
|
|
39
45
|
})
|
|
40
46
|
|
|
41
47
|
async function readWebsite(req, res) {
|
|
42
|
-
const id = req.query
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
const { id } = req.query
|
|
49
|
+
if(id) {
|
|
50
|
+
// Returns a website data
|
|
51
|
+
try {
|
|
52
|
+
config.emit(EVENT_READ_START, { req, id })
|
|
53
|
+
const data = await readData(id)
|
|
54
|
+
config.emit(EVENT_READ_END, { req, res, data, id })
|
|
55
|
+
res
|
|
56
|
+
.type('application/json')
|
|
57
|
+
.send({
|
|
58
|
+
...options.defaultSite,
|
|
59
|
+
...data,
|
|
60
|
+
})
|
|
61
|
+
} catch (err) {
|
|
62
|
+
if(err.code === 'ENOENT') {
|
|
63
|
+
console.error('Read file error, website does not exist', err)
|
|
64
|
+
res.status(404).json({ message: 'Read file error, website does not exist: ' + err.message, code: err.code})
|
|
65
|
+
} else {
|
|
66
|
+
console.error('Read file error', err)
|
|
67
|
+
res.status(400).json({ message: err.message, code: err.code})
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
// list websites
|
|
72
|
+
try {
|
|
73
|
+
config.emit(EVENT_READ_START, { req, id })
|
|
74
|
+
const data = await list()
|
|
75
|
+
config.emit(EVENT_READ_END, { req, res, data, id })
|
|
76
|
+
res
|
|
77
|
+
.type('application/json')
|
|
78
|
+
.send(data)
|
|
79
|
+
} catch (err) {
|
|
80
|
+
console.error('List sites error', err)
|
|
81
|
+
res.status(400).json({ message: err.message, code: err.code })
|
|
57
82
|
}
|
|
58
83
|
}
|
|
59
84
|
}
|
|
@@ -79,8 +104,6 @@ module.exports = async function(config, opts = {}) {
|
|
|
79
104
|
|
|
80
105
|
async function writeWebsite(req, res) {
|
|
81
106
|
const id = req.query.id
|
|
82
|
-
await init(id)
|
|
83
|
-
|
|
84
107
|
const [data, err] = fromBody(req.body)
|
|
85
108
|
if(err) {
|
|
86
109
|
res.status(400).json({ message: 'Error writing data file, could not parse the provided body: ' + err.message, code: err.code})
|
|
@@ -88,6 +111,7 @@ module.exports = async function(config, opts = {}) {
|
|
|
88
111
|
}
|
|
89
112
|
config.emit(EVENT_WRITE_START, { req, id, data })
|
|
90
113
|
try {
|
|
114
|
+
await init(id)
|
|
91
115
|
await writeData(id, data)
|
|
92
116
|
} catch (err) {
|
|
93
117
|
console.error('Error writing data file', err)
|
|
@@ -102,6 +126,41 @@ module.exports = async function(config, opts = {}) {
|
|
|
102
126
|
})
|
|
103
127
|
}
|
|
104
128
|
|
|
129
|
+
async function deleteWebsite(req, res) {
|
|
130
|
+
const { id } = req.query
|
|
131
|
+
if(id) {
|
|
132
|
+
try {
|
|
133
|
+
config.emit(EVENT_DELETE_START, { req, id })
|
|
134
|
+
const data = await del(id)
|
|
135
|
+
config.emit(EVENT_DELETE_END, { req, res, data, id })
|
|
136
|
+
res
|
|
137
|
+
.type('application/json')
|
|
138
|
+
.send(data)
|
|
139
|
+
} catch (err) {
|
|
140
|
+
if(err.code === 'ENOENT') {
|
|
141
|
+
console.error('Delete file error, website does not exist', err)
|
|
142
|
+
res.status(404).json({ message: 'Delete file error, website does not exist: ' + err.message, code: err.code})
|
|
143
|
+
} else {
|
|
144
|
+
console.error('Delete file error', err)
|
|
145
|
+
res.status(400).json({ message: err.message, code: err.code})
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
// list websites
|
|
150
|
+
try {
|
|
151
|
+
config.emit(EVENT_DELETE_START, { req, id })
|
|
152
|
+
const data = await list()
|
|
153
|
+
config.emit(EVENT_DELETE_END, { req, res, data, id })
|
|
154
|
+
res
|
|
155
|
+
.type('application/json')
|
|
156
|
+
.send(data)
|
|
157
|
+
} catch (err) {
|
|
158
|
+
console.error('List sites error', err)
|
|
159
|
+
res.status(400).json({ message: err.message, code: err.code })
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
105
164
|
async function readAsset(req, res) {
|
|
106
165
|
const id = req.query.id
|
|
107
166
|
const fileName = req.params[0]
|
package/src/ts/client/config.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { Config } from '@silexlabs/silex-plugins'
|
|
1
|
+
import { Config, Plugin } from '@silexlabs/silex-plugins'
|
|
2
2
|
import { getEditorConfig } from './grapesjs'
|
|
3
|
+
import { CLIENT_CONFIG_FILE_NAME } from '../constants'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* @fileoverview Silex client side config
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
const id = new URL(location.href).searchParams.get('id') || 'default'
|
|
9
|
-
const
|
|
10
|
+
const lang = new URL(location.href).searchParams.get('lang') || 'en'
|
|
11
|
+
const rootUrl = `${location.protocol}//${location.host}${location.pathname}`
|
|
10
12
|
|
|
11
13
|
export class SilexConfig extends Config {
|
|
12
14
|
/**
|
|
@@ -14,8 +16,24 @@ export class SilexConfig extends Config {
|
|
|
14
16
|
*/
|
|
15
17
|
debug = false
|
|
16
18
|
|
|
19
|
+
/**
|
|
20
|
+
* language for I18n module
|
|
21
|
+
*/
|
|
22
|
+
lang = lang
|
|
23
|
+
|
|
17
24
|
/**
|
|
18
25
|
* Grapesjs config
|
|
19
26
|
*/
|
|
20
27
|
editor = getEditorConfig(id, rootUrl)
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Client config url
|
|
31
|
+
* This is the url of the config file which is a plugin
|
|
32
|
+
*/
|
|
33
|
+
clientConfigUrl = `${rootUrl}${CLIENT_CONFIG_FILE_NAME}`
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* GrapesJs plugins
|
|
37
|
+
*/
|
|
38
|
+
grapesJsPlugins: Plugin[] = []
|
|
21
39
|
}
|
|
@@ -1,88 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
;[
|
|
17
|
-
// {
|
|
18
|
-
// id: 'section',
|
|
19
|
-
// def: {
|
|
20
|
-
// label: 'Section',
|
|
21
|
-
// category: 'Containers',
|
|
22
|
-
// attributes: { class: 'fa fa-list-alt' },
|
|
23
|
-
// draggable: 'body',
|
|
24
|
-
// content: {
|
|
25
|
-
// tagName: 'section',
|
|
26
|
-
// styles: `
|
|
27
|
-
// .${clsSectionBackground} {
|
|
28
|
-
// display: flex;
|
|
29
|
-
// justify-content: center;
|
|
30
|
-
// }
|
|
31
|
-
// `,
|
|
32
|
-
// components: [
|
|
33
|
-
// {
|
|
34
|
-
// tagName: 'div',
|
|
35
|
-
// name: 'Section container',
|
|
36
|
-
// attributes: { class: ['website-width'] },
|
|
37
|
-
// draggable: false,
|
|
38
|
-
// removable: false,
|
|
39
|
-
// resizable: true,
|
|
40
|
-
// styles: `
|
|
41
|
-
// .${clsSection} {
|
|
42
|
-
// min-height: 100px;
|
|
43
|
-
// width: 1200px;
|
|
44
|
-
// }
|
|
45
|
-
// `,
|
|
46
|
-
// },
|
|
47
|
-
// ],
|
|
48
|
-
// },
|
|
49
|
-
// },
|
|
50
|
-
// },
|
|
51
|
-
{
|
|
52
|
-
id: 'container',
|
|
53
|
-
def: {
|
|
54
|
-
label: 'Container',
|
|
55
|
-
category: 'Basics',
|
|
56
|
-
attributes: { class: 'container-png' },
|
|
57
|
-
content: {
|
|
58
|
-
type: 'container',
|
|
59
|
-
style: {
|
|
60
|
-
'min-height': '100px',
|
|
61
|
-
},
|
|
62
|
-
},
|
|
1
|
+
export const blocksPlugin = (editor, opts) => {
|
|
2
|
+
// Defaults
|
|
3
|
+
const options = {
|
|
4
|
+
...opts,
|
|
5
|
+
}
|
|
6
|
+
// Container block
|
|
7
|
+
const containerId = 'container'
|
|
8
|
+
editor.BlockManager.add(containerId, {
|
|
9
|
+
label: 'Container',
|
|
10
|
+
category: 'Basics',
|
|
11
|
+
attributes: { class: 'container-png' },
|
|
12
|
+
content: {
|
|
13
|
+
type: containerId, // display as "Container"
|
|
14
|
+
style: {
|
|
15
|
+
'min-height': '100px',
|
|
63
16
|
},
|
|
64
17
|
},
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
// category: 'Containers',
|
|
69
|
-
// label: 'Link Block',
|
|
70
|
-
// attributes: { class: 'fa fa-link' },
|
|
71
|
-
// content: {
|
|
72
|
-
// type: 'link',
|
|
73
|
-
// editable: false,
|
|
74
|
-
// droppable: true,
|
|
75
|
-
// resizable: true,
|
|
76
|
-
// style:{
|
|
77
|
-
// display: 'inline-block',
|
|
78
|
-
// padding: '5px',
|
|
79
|
-
// 'min-height': '100px',
|
|
80
|
-
// 'width': '100px'
|
|
81
|
-
// }
|
|
82
|
-
// },
|
|
83
|
-
// },
|
|
84
|
-
// },
|
|
85
|
-
]
|
|
86
|
-
.forEach(block => editor.BlockManager.add(block.id, block.def))
|
|
87
|
-
})
|
|
88
|
-
|
|
18
|
+
})
|
|
19
|
+
editor.DomComponents.addType(containerId, {})
|
|
20
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import grapesjs
|
|
1
|
+
import grapesjs from 'grapesjs'
|
|
2
2
|
import openImport from './openImport'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -14,7 +14,7 @@ import styleFilterPlugin from 'grapesjs-style-filter'
|
|
|
14
14
|
import formPlugin from 'grapesjs-plugin-forms'
|
|
15
15
|
import codePlugin from 'grapesjs-custom-code'
|
|
16
16
|
import uiSuggestClasses from '@silexlabs/grapesjs-ui-suggest-classes'
|
|
17
|
-
import symbolsPlugin from '@silexlabs/grapesjs-symbols
|
|
17
|
+
import * as symbolsPlugin from '@silexlabs/grapesjs-symbols' // Why is this not working without *?
|
|
18
18
|
import loadingPlugin from '@silexlabs/grapesjs-loading'
|
|
19
19
|
import fontsDialogPlugin, { cmdOpenFonts } from '@silexlabs/grapesjs-fonts'
|
|
20
20
|
import symbolDialogsPlugin, { cmdPromptAddSymbol } from './symbolDialogs'
|
|
@@ -60,8 +60,6 @@ plugins
|
|
|
60
60
|
throw new Error(`Plugin ${p.name} could not be loaded correctly`)
|
|
61
61
|
})
|
|
62
62
|
|
|
63
|
-
export function getPlugins() { return plugins }
|
|
64
|
-
|
|
65
63
|
// ////////////////////
|
|
66
64
|
// Config
|
|
67
65
|
// ////////////////////
|
|
@@ -86,7 +84,7 @@ export function getEditorConfig(id: string, rootUrl: string) {
|
|
|
86
84
|
},
|
|
87
85
|
|
|
88
86
|
assetManager: {
|
|
89
|
-
upload: `${rootUrl}
|
|
87
|
+
upload: `${rootUrl}assets/`,
|
|
90
88
|
},
|
|
91
89
|
|
|
92
90
|
storageManager: {
|
|
@@ -94,8 +92,8 @@ export function getEditorConfig(id: string, rootUrl: string) {
|
|
|
94
92
|
type: 'remote',
|
|
95
93
|
options: {
|
|
96
94
|
remote: {
|
|
97
|
-
urlStore: `${rootUrl}
|
|
98
|
-
urlLoad: `${rootUrl}
|
|
95
|
+
urlStore: `${rootUrl}website/?id=${id}`,
|
|
96
|
+
urlLoad: `${rootUrl}website/?id=${id}`,
|
|
99
97
|
},
|
|
100
98
|
},
|
|
101
99
|
},
|
|
@@ -214,10 +212,16 @@ export function getEditorConfig(id: string, rootUrl: string) {
|
|
|
214
212
|
// Keep a ref to the editor singleton
|
|
215
213
|
let editor
|
|
216
214
|
|
|
217
|
-
export function initEditor(config
|
|
215
|
+
export function initEditor(config, grapesJsPlugins) {
|
|
218
216
|
if(editor) throw new Error('Grapesjs editor already created')
|
|
219
217
|
|
|
220
|
-
editor = grapesjs.init(
|
|
218
|
+
editor = grapesjs.init({
|
|
219
|
+
plugins: [
|
|
220
|
+
...grapesJsPlugins,
|
|
221
|
+
...config.plugins,
|
|
222
|
+
],
|
|
223
|
+
...config,
|
|
224
|
+
})
|
|
221
225
|
|
|
222
226
|
// customize the editor
|
|
223
227
|
;['text']
|
|
@@ -253,6 +257,20 @@ export function initEditor(config: EditorConfig) {
|
|
|
253
257
|
// use the style filter plugin
|
|
254
258
|
editor.StyleManager.addProperty('extra',{ extend: 'filter' })
|
|
255
259
|
})
|
|
260
|
+
|
|
261
|
+
// Handle errors
|
|
262
|
+
editor.on('storage:error:load', (err) => {
|
|
263
|
+
editor.Modal.open({
|
|
264
|
+
title: 'Error loading website',
|
|
265
|
+
content: `This website could not be loaded.<br><hr>${err}`,
|
|
266
|
+
})
|
|
267
|
+
})
|
|
268
|
+
editor.on('storage:error:store', (err) => {
|
|
269
|
+
editor.Modal.open({
|
|
270
|
+
title: 'Error saving website',
|
|
271
|
+
content: `This website could not be saved.<br><hr>${err}`,
|
|
272
|
+
})
|
|
273
|
+
})
|
|
256
274
|
}
|
|
257
275
|
|
|
258
276
|
export function getEditor() {
|
|
@@ -8,7 +8,7 @@ import { onAll } from '../utils'
|
|
|
8
8
|
const pluginName = 'publish'
|
|
9
9
|
export const cmdPublish = 'publish-open-dialog'
|
|
10
10
|
let _token = null
|
|
11
|
-
let
|
|
11
|
+
let id
|
|
12
12
|
let rootUrl
|
|
13
13
|
|
|
14
14
|
// plugin code
|
|
@@ -19,7 +19,7 @@ export const publishPlugin = grapesjs.plugins.add(pluginName, (editor, opts) =>
|
|
|
19
19
|
}
|
|
20
20
|
// Global config
|
|
21
21
|
rootUrl = opts.rootUrl
|
|
22
|
-
|
|
22
|
+
id = opts.id
|
|
23
23
|
// Keep track of the token
|
|
24
24
|
editor.on('login:success', async ({ getUser, getToken }) => {
|
|
25
25
|
_token = await getToken()
|
|
@@ -248,7 +248,7 @@ export async function startPublication(editor) {
|
|
|
248
248
|
...projectData,
|
|
249
249
|
settings: siteSettings,
|
|
250
250
|
publication: publicationSettings,
|
|
251
|
-
|
|
251
|
+
id,
|
|
252
252
|
files,
|
|
253
253
|
}
|
|
254
254
|
// Reset asset URLs
|
|
@@ -282,7 +282,7 @@ export async function startPublication(editor) {
|
|
|
282
282
|
let res
|
|
283
283
|
let json
|
|
284
284
|
try {
|
|
285
|
-
res = await fetch(`${ rootUrl }
|
|
285
|
+
res = await fetch(`${ rootUrl }publish`, {
|
|
286
286
|
method: 'POST',
|
|
287
287
|
body: JSON.stringify({
|
|
288
288
|
data,
|
package/src/ts/client/index.ts
CHANGED
|
@@ -4,11 +4,9 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
// Get env vars from webpack (see plugin in webpack.config.js)
|
|
8
|
-
declare const CONFIG_URL: string
|
|
9
|
-
|
|
10
7
|
import { SilexConfig } from './config'
|
|
11
8
|
import { initEditor, getEditor } from './grapesjs/index'
|
|
9
|
+
import { EVENT_STARTUP_START, EVENT_STARTUP_END } from '../events'
|
|
12
10
|
|
|
13
11
|
/**
|
|
14
12
|
* Start Silex, called from host HTML page with window.silex.start()
|
|
@@ -17,22 +15,37 @@ export async function start(options = {}) {
|
|
|
17
15
|
const config = new SilexConfig()
|
|
18
16
|
Object.assign(config, options)
|
|
19
17
|
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
// Config file in root folder which can be generated by the server
|
|
19
|
+
console.log('Loading config', config.clientConfigUrl)
|
|
20
|
+
//import(/* webpackIgnore: true */ config.clientConfigUrl)
|
|
21
|
+
//.then((configFile) => console.log('Config loaded', configFile))
|
|
22
|
+
//.catch((e) => console.error('Config error', config.clientConfigUrl, e))
|
|
23
|
+
await config.addPlugin(config.clientConfigUrl, {})
|
|
24
24
|
|
|
25
25
|
// Debug mode
|
|
26
26
|
if (config.debug) {
|
|
27
27
|
console.warn('Silex starting in debug mode.', {config})
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
// Notify plugins that loading is over and Silex is starting
|
|
31
|
+
config.emit(EVENT_STARTUP_START)
|
|
32
|
+
|
|
30
33
|
// Start grapesjs
|
|
31
|
-
|
|
34
|
+
console.log('start', {config})
|
|
35
|
+
try{
|
|
36
|
+
initEditor(config.editor, config.grapesJsPlugins)
|
|
37
|
+
} catch(e) {
|
|
38
|
+
console.error('Error starting Silex', e)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Init internationalization module
|
|
42
|
+
getEditor().I18n.setLocale(config.lang)
|
|
32
43
|
|
|
33
44
|
// End of loading
|
|
34
45
|
getEditor().on('load', () => {
|
|
46
|
+
console.log('Silex started')
|
|
35
47
|
document.querySelector('.silex-loader').classList.add('silex-dialog-hide')
|
|
36
48
|
document.querySelector('#gjs').classList.remove('silex-dialog-hide')
|
|
49
|
+
config.emit(EVENT_STARTUP_END)
|
|
37
50
|
})
|
|
38
51
|
}
|
package/src/ts/server/cli.ts
CHANGED
|
@@ -12,6 +12,9 @@ const options = parse({
|
|
|
12
12
|
'ssl-certificate': [ false, 'Path to SSL certificate', 'file'],
|
|
13
13
|
'force-https-trust-xfp-header': [ false, 'Sets the trustXFPHeader param of the express module "cookie-session". Use only with --force-https', 'boolean'],
|
|
14
14
|
'session-secret': ['s', 'Session secret', 'string'],
|
|
15
|
+
'cors-url': ['', 'Enable CORS for URL (can be "*")', 'string'],
|
|
16
|
+
'client-config': ['', 'Path to client config file to be served on .silex-client.js', 'string'],
|
|
17
|
+
'fs-root': ['', 'Path to the root folder where to store websites. Used by the default backend (fs).', 'string'],
|
|
15
18
|
}, {})
|
|
16
19
|
|
|
17
20
|
if(options.config) process.env.CONFIG = options.config
|
|
@@ -23,5 +26,8 @@ if(options['ssl-private-key']) process.env.SILEX_SSL_PRIVATE_KEY = options['ssl-
|
|
|
23
26
|
if(options['ssl-certificate']) process.env.SILEX_SSL_CERTIFICATE = options['ssl-certificate']
|
|
24
27
|
if(options['force-https-trust-xfp-header']) process.env.SILEX_FORCE_HTTPS_TRUST_XFP_HEADER = options['force-https-trust-xfp-header']
|
|
25
28
|
if(options['session-secret']) process.env.SILEX_SESSION_SECRET = options['session-secret']
|
|
29
|
+
if(options['cors-url']) process.env.SILEX_CORS_URL = options['cors-url']
|
|
30
|
+
if(options['client-config']) process.env.SILEX_CLIENT_CONFIG = options['client-config']
|
|
31
|
+
if(options['fs-root']) process.env.FS_ROOT = options['fs-root']
|
|
26
32
|
|
|
27
33
|
import './index'
|
package/src/ts/server/config.ts
CHANGED
|
@@ -9,16 +9,20 @@
|
|
|
9
9
|
|
|
10
10
|
import { Config, Plugin } from '@silexlabs/silex-plugins'
|
|
11
11
|
import { resolve } from 'path'
|
|
12
|
+
import { CLIENT_CONFIG_FILE_NAME } from '../constants'
|
|
13
|
+
import { Router } from 'express'
|
|
14
|
+
import { readFile } from 'fs/promises'
|
|
15
|
+
import { EVENT_STARTUP_START } from '../events'
|
|
12
16
|
|
|
13
17
|
/**
|
|
14
18
|
* Config types definitions
|
|
15
19
|
*/
|
|
16
20
|
export class ServerConfig extends Config {
|
|
17
21
|
constructor(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
public url: string,
|
|
23
|
+
public debug: boolean,
|
|
24
|
+
public port: string,
|
|
25
|
+
public apiPath: string,
|
|
22
26
|
) {
|
|
23
27
|
super()
|
|
24
28
|
}
|
|
@@ -28,13 +32,49 @@ export class ServerConfig extends Config {
|
|
|
28
32
|
export default async function(): Promise<ServerConfig> {
|
|
29
33
|
const port = process.env.PORT || '6805' // 6805 is the date of sexual revolution started in paris france 8-)
|
|
30
34
|
const debug = process.env.SILEX_DEBUG === 'true'
|
|
35
|
+
const clientConfigPath = process.env.SILEX_CLIENT_CONFIG
|
|
31
36
|
|
|
32
37
|
const url = process.env.SERVER_URL || `http://localhost:${port}`
|
|
33
38
|
const config = new ServerConfig(url, debug, port, '/api')
|
|
34
39
|
|
|
40
|
+
// Serve the client side config file
|
|
41
|
+
if (clientConfigPath) {
|
|
42
|
+
config.on(EVENT_STARTUP_START, async ({ app }) => {
|
|
43
|
+
console.log(`> Serving client side config ${clientConfigPath} at ${CLIENT_CONFIG_FILE_NAME}`)
|
|
44
|
+
// Attach the route immediately
|
|
45
|
+
const router = Router()
|
|
46
|
+
app.use(router)
|
|
47
|
+
// Load the config file and serve it
|
|
48
|
+
let clientConfig = (await readFile(clientConfigPath as string)).toString()
|
|
49
|
+
router.get(`/${CLIENT_CONFIG_FILE_NAME}`, async (res, req, next) => {
|
|
50
|
+
// Reload each time in debug mode
|
|
51
|
+
if (config.debug) {
|
|
52
|
+
console.log('[Debug mode] Reloading config file', clientConfigPath)
|
|
53
|
+
clientConfig = (await readFile(clientConfigPath)).toString()
|
|
54
|
+
}
|
|
55
|
+
// Send the config file
|
|
56
|
+
req.header('Content-Type', 'application/javascript').send(clientConfig)
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Optional config file
|
|
62
|
+
// Load this config file before the main config file so that routes can be overriden
|
|
63
|
+
// Or listen for event silex:startup:end to add routes after the default ones
|
|
64
|
+
if (process.env.CONFIG) {
|
|
65
|
+
const userConfigPath: Plugin = process.env.CONFIG
|
|
66
|
+
console.log('> Loading user config', userConfigPath)
|
|
67
|
+
try {
|
|
68
|
+
// Initiate the process with the config file which is just another plugin
|
|
69
|
+
await config.addPlugin(userConfigPath, {})
|
|
70
|
+
} catch (e) {
|
|
71
|
+
throw new Error(`User config file ${userConfigPath} error: ${e.message}`)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
35
75
|
// Config file in root folder
|
|
36
76
|
const configFilePath: Plugin = resolve(__dirname, '../../.silex.js')
|
|
37
|
-
console.log('Loading config', configFilePath)
|
|
77
|
+
console.log('> Loading config', configFilePath)
|
|
38
78
|
try {
|
|
39
79
|
// Initiate the process with the config file which is just another plugin
|
|
40
80
|
await config.addPlugin(configFilePath, {})
|
|
@@ -45,17 +85,6 @@ export default async function(): Promise<ServerConfig> {
|
|
|
45
85
|
throw new Error(`Error in config file ${configFilePath}: ${e.message}`)
|
|
46
86
|
}
|
|
47
87
|
}
|
|
48
|
-
// Optional config file
|
|
49
|
-
if (process.env.CONFIG) {
|
|
50
|
-
const userConfigPath: Plugin = process.env.CONFIG
|
|
51
|
-
console.log('Loading user config', userConfigPath)
|
|
52
|
-
try {
|
|
53
|
-
// Initiate the process with the config file which is just another plugin
|
|
54
|
-
await config.addPlugin(userConfigPath, {})
|
|
55
|
-
} catch (e) {
|
|
56
|
-
throw new Error(`User config file ${userConfigPath} not found: ${e.message}`)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
88
|
|
|
60
89
|
// Return the config file
|
|
61
90
|
return config
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import express, { Application } from 'express'
|
|
2
2
|
|
|
3
3
|
import { ServerConfig } from './config'
|
|
4
|
-
import { EVENT_STARTUP_START, EVENT_STARTUP_END } from '
|
|
4
|
+
import { EVENT_STARTUP_START, EVENT_STARTUP_END } from '../events'
|
|
5
5
|
|
|
6
6
|
export default async function(config: ServerConfig): Promise<Application> {
|
|
7
7
|
// Plugins hook to create API routes
|
package/src/ts/server/index.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import getConfig from './config'
|
|
2
|
-
import start from './
|
|
2
|
+
import start from './express'
|
|
3
|
+
|
|
4
|
+
import * as config from './config'
|
|
5
|
+
import * as types from '../types'
|
|
6
|
+
import * as events from '../events'
|
|
7
|
+
import * as constants from '../constants'
|
|
8
|
+
import * as page from '../page'
|
|
9
|
+
export default { config, types, events, constants, page }
|
|
3
10
|
|
|
4
11
|
// Main app
|
|
5
12
|
async function main() {
|
|
@@ -30,7 +37,7 @@ async function startLiverReload() {
|
|
|
30
37
|
const server = createServer({
|
|
31
38
|
delay: 0,
|
|
32
39
|
}, () => {
|
|
33
|
-
console.info(`\
|
|
40
|
+
console.info(`\n> Debug mode\n> Live reload server is running.\n> Watching ${dist}`)
|
|
34
41
|
})
|
|
35
42
|
server.watch(dist)
|
|
36
43
|
}
|