@silexlabs/silex 3.0.0-alpha.21
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/.dockerignore +5 -0
- package/.editorconfig +14 -0
- package/.eslintrc.json +43 -0
- package/.github/FUNDING.yml +13 -0
- package/.github/workflows/docker-hub-publish.yml +36 -0
- package/.github/workflows/npm-publish.yml +19 -0
- package/.github/workflows/test.yml +26 -0
- package/.nvmrc +1 -0
- package/.silex.js +19 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +108 -0
- package/Dockerfile +15 -0
- package/LICENSE +203 -0
- package/app.json +106 -0
- package/directus.silex.js +7 -0
- package/dist/client/css/admin.css +4350 -0
- package/dist/client/index.html +9 -0
- package/dist/client/js/main.js +282 -0
- package/dist/client/js/main.js.LICENSE.txt +52 -0
- package/dist/server/config.js +50 -0
- package/dist/server/events.js +5 -0
- package/dist/server/index.js +29 -0
- package/dist/server/start.js +18 -0
- package/jest.config.js +10 -0
- package/logos/logo_by_ceubri/silex-icon-2018@200px.png +0 -0
- package/logos/logo_by_ceubri/silex-icon-2018@4x.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white-transp.pdf +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white.jpg +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white.pdf +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white@0.5x.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white@0.75x.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white@4x.jpg +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white@4x.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018.ai +2520 -6
- package/logos/logo_by_ceubri/silex-logo-2018.jpg +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018.pdf +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018@0.5x.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018@0.75x.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018@4x.jpg +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018@4x.png +0 -0
- package/logos/silexlabs.png +0 -0
- package/package.json +170 -0
- package/public/assets/container.png +0 -0
- package/public/assets/favicon.png +0 -0
- package/public/assets/logo-silex-small.png +0 -0
- package/public/example-config.js +31 -0
- package/public/robots.txt +2 -0
- package/readme.md +51 -0
- package/src/css/_mixins.scss +8 -0
- package/src/css/_variables.scss +33 -0
- package/src/css/dialog.scss +34 -0
- package/src/css/grapesjs.scss +44 -0
- package/src/css/index.scss +22 -0
- package/src/css/loader.scss +10 -0
- package/src/css/page-panel.scss +65 -0
- package/src/css/project-bar.scss +102 -0
- package/src/css/silex.scss +183 -0
- package/src/html/debug.pug +8 -0
- package/src/html/index.pug +39 -0
- package/src/html/templates/includes.pug +2 -0
- package/src/html/templates/workspace.pug +2 -0
- package/src/plugins/AuthPlugin.js +21 -0
- package/src/plugins/Cache.js +11 -0
- package/src/plugins/DefaultBackend.js +113 -0
- package/src/plugins/ExpressPlugin.js +30 -0
- package/src/plugins/HooksPlugin.js +58 -0
- package/src/plugins/PublishPlugin.js +69 -0
- package/src/plugins/SslPlugin.js +66 -0
- package/src/plugins/StaticPlugin.js +48 -0
- package/src/plugins/WebsitePlugin.js +149 -0
- package/src/ts/client/config.ts +21 -0
- package/src/ts/client/expose.ts +4 -0
- package/src/ts/client/grapesjs/blocks.ts +88 -0
- package/src/ts/client/grapesjs/eleventy.ts +21 -0
- package/src/ts/client/grapesjs/index.ts +255 -0
- package/src/ts/client/grapesjs/internal-links.test.ts +87 -0
- package/src/ts/client/grapesjs/internal-links.ts +210 -0
- package/src/ts/client/grapesjs/new-page-dialog.ts +73 -0
- package/src/ts/client/grapesjs/openImport.ts +59 -0
- package/src/ts/client/grapesjs/page-panel.ts +101 -0
- package/src/ts/client/grapesjs/project-bar.ts +92 -0
- package/src/ts/client/grapesjs/publish.ts +386 -0
- package/src/ts/client/grapesjs/rich-text.ts +16 -0
- package/src/ts/client/grapesjs/semantic.ts +96 -0
- package/src/ts/client/grapesjs/settings.ts +190 -0
- package/src/ts/client/grapesjs/symbolDialogs.ts +21 -0
- package/src/ts/client/grapesjs/template.ts +166 -0
- package/src/ts/client/index.ts +38 -0
- package/src/ts/client/utils.ts +9 -0
- package/src/ts/externs.ts +1 -0
- package/src/ts/page.ts +14 -0
- package/src/ts/server/cli.ts +23 -0
- package/src/ts/server/config.ts +62 -0
- package/src/ts/server/events.ts +2 -0
- package/src/ts/server/index.ts +35 -0
- package/src/ts/server/start.ts +18 -0
- package/src/ts/server/utils/BackwardCompat.ts +534 -0
- package/src/ts/types.ts +138 -0
- package/tsconfig-server-debug.json +20 -0
- package/tsconfig-server-release.json +19 -0
- package/tsconfig.jest.json +9 -0
- package/tsconfig.json +27 -0
- package/webpack.config.js +29 -0
- package/webpack.debug.config.js +8 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
@use "variables" as *;
|
|
2
|
+
@use "mixins";
|
|
3
|
+
|
|
4
|
+
.silex-workspace {
|
|
5
|
+
@include mixins.silex-ui-full;
|
|
6
|
+
display: flex;
|
|
7
|
+
a {
|
|
8
|
+
color: inherit;
|
|
9
|
+
}
|
|
10
|
+
h2 {
|
|
11
|
+
font-size: 18px;
|
|
12
|
+
background: $lighterPrimaryColor;
|
|
13
|
+
border-radius: 8px 8px 0 0;
|
|
14
|
+
padding: 8px;
|
|
15
|
+
margin: 0;
|
|
16
|
+
font-weight: normal;
|
|
17
|
+
}
|
|
18
|
+
h3 {
|
|
19
|
+
margin: 10px 0;
|
|
20
|
+
font-size: 18px;
|
|
21
|
+
font-weight: normal;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.full-width {
|
|
26
|
+
width: 100%;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// full screen settings
|
|
30
|
+
.settings-dialog > div {
|
|
31
|
+
max-width: none;
|
|
32
|
+
width: 100%;
|
|
33
|
+
max-height: 100vh;
|
|
34
|
+
overflow: auto;
|
|
35
|
+
form.silex-form {
|
|
36
|
+
padding-bottom: 100px; // leave space for fixed footer
|
|
37
|
+
}
|
|
38
|
+
.silex-form__element {
|
|
39
|
+
padding: 10px 5px;
|
|
40
|
+
border: 1px solid $lighterPrimaryColor;
|
|
41
|
+
margin: 10px 0;
|
|
42
|
+
width: 98%;
|
|
43
|
+
}
|
|
44
|
+
footer {
|
|
45
|
+
position: fixed;
|
|
46
|
+
bottom: 0;
|
|
47
|
+
left: 0;
|
|
48
|
+
width: 100%;
|
|
49
|
+
padding: 20px 0;
|
|
50
|
+
background-color: $primaryColor;
|
|
51
|
+
&>*:last-child {
|
|
52
|
+
margin-right: 30px;
|
|
53
|
+
}
|
|
54
|
+
.silex-version {
|
|
55
|
+
float: left;
|
|
56
|
+
padding: 0 15px;
|
|
57
|
+
font-size: x-small;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
button.silex-button,
|
|
63
|
+
input.silex-button[type="button"],
|
|
64
|
+
input.silex-button[type="submit"] {
|
|
65
|
+
cursor: pointer;
|
|
66
|
+
font-family: inherit;
|
|
67
|
+
color: #fff;
|
|
68
|
+
display: inline-flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
justify-content: center;
|
|
71
|
+
border: 1px solid #e9ebeb;
|
|
72
|
+
border-bottom-color: #e1e3e3;
|
|
73
|
+
border-radius: 4px;
|
|
74
|
+
box-shadow: 0 2px 4px 0 rgba(14, 30, 37, .12);
|
|
75
|
+
background-color: $lighterPrimaryColor;
|
|
76
|
+
border-color: transparent;
|
|
77
|
+
padding: 5px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.silex-button[disabled] {
|
|
81
|
+
opacity: .8;
|
|
82
|
+
cursor: unset;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.silex-button.silex-button--primary {
|
|
86
|
+
background-color: $lighterPrimaryColor;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.silex-button.silex-button--secondary {
|
|
90
|
+
background-color: $primaryColor;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.silex-button--size {
|
|
94
|
+
max-height: 30px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.silex-button--small {
|
|
98
|
+
font-size: .7em;
|
|
99
|
+
padding: 0 5px;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.silex-input {
|
|
103
|
+
appearance: initial;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.silex-label {
|
|
107
|
+
display: block;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.silex-bar {
|
|
111
|
+
display: flex;
|
|
112
|
+
&>* {
|
|
113
|
+
margin: 5px;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
ol.silex-list,
|
|
118
|
+
ul.silex-list {
|
|
119
|
+
list-style: none;
|
|
120
|
+
margin: 0;
|
|
121
|
+
padding: 0;
|
|
122
|
+
background-color: #575757;
|
|
123
|
+
&>li {
|
|
124
|
+
padding: 5px;
|
|
125
|
+
border-bottom: 1px solid #575757;
|
|
126
|
+
background-color: #444;
|
|
127
|
+
margin: 5px;
|
|
128
|
+
}
|
|
129
|
+
fieldset {
|
|
130
|
+
border-color: #575757;
|
|
131
|
+
}
|
|
132
|
+
h4 { // badge-like
|
|
133
|
+
display: inline-block;
|
|
134
|
+
padding: 5px;
|
|
135
|
+
padding-bottom: 2px;
|
|
136
|
+
margin: 10px 5px;
|
|
137
|
+
font-weight: normal;
|
|
138
|
+
color: #3d3d3d;
|
|
139
|
+
background-color: #c4c4c4;
|
|
140
|
+
}
|
|
141
|
+
.silex-list__item__body {
|
|
142
|
+
display: flex;
|
|
143
|
+
}
|
|
144
|
+
.silex-list__item__footer {
|
|
145
|
+
display: flex;
|
|
146
|
+
justify-content: flex-end;
|
|
147
|
+
button {
|
|
148
|
+
padding: 5px;
|
|
149
|
+
margin: 5px;
|
|
150
|
+
padding-top: 8px;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
form.silex-form {
|
|
156
|
+
display: flex;
|
|
157
|
+
flex-direction: column;
|
|
158
|
+
footer {
|
|
159
|
+
text-align: right;
|
|
160
|
+
border-top: 1px solid $lighterPrimaryColor;
|
|
161
|
+
padding: 20px 0 10px;
|
|
162
|
+
margin: 20px 0 0;
|
|
163
|
+
}
|
|
164
|
+
button, input, optgroup, select, textarea {
|
|
165
|
+
background-color: $darkerPrimaryColor;
|
|
166
|
+
color: $secondaryColor;
|
|
167
|
+
padding: 5px;
|
|
168
|
+
border: none;
|
|
169
|
+
}
|
|
170
|
+
input[type=checkbox] {
|
|
171
|
+
margin: 5px;
|
|
172
|
+
}
|
|
173
|
+
textarea {
|
|
174
|
+
width: 98% !important;
|
|
175
|
+
}
|
|
176
|
+
.silex-form__element {
|
|
177
|
+
display: inline-flex;
|
|
178
|
+
flex-direction: column;
|
|
179
|
+
}
|
|
180
|
+
.silex-form__group.col2 {
|
|
181
|
+
column-count: 2;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
doctype html
|
|
2
|
+
html(lang="en", translate="no")
|
|
3
|
+
head
|
|
4
|
+
block head-start
|
|
5
|
+
|
|
6
|
+
block head
|
|
7
|
+
title='Silex'
|
|
8
|
+
meta(name='description', content='This is a public instance of Silex website builder. Build websites in the browser')
|
|
9
|
+
link(rel="shortcut icon", href="assets/favicon.png")
|
|
10
|
+
|
|
11
|
+
block loading
|
|
12
|
+
style.
|
|
13
|
+
body {
|
|
14
|
+
background-color: #444444;
|
|
15
|
+
font-family: Helvetica,sans-serif;
|
|
16
|
+
}
|
|
17
|
+
.silex-loader {
|
|
18
|
+
display: none;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
block head-end
|
|
22
|
+
|
|
23
|
+
body.silex-loading
|
|
24
|
+
.silex-loader.silex-dialog.
|
|
25
|
+
Loading...
|
|
26
|
+
|
|
27
|
+
block includes
|
|
28
|
+
include ./templates/includes.pug
|
|
29
|
+
|
|
30
|
+
block body-start
|
|
31
|
+
include ./templates/workspace.pug
|
|
32
|
+
|
|
33
|
+
block body-end
|
|
34
|
+
script(src='js/main.js')
|
|
35
|
+
|
|
36
|
+
block silex-start
|
|
37
|
+
// Here you can use silex.config to hook into silex options before silex starts
|
|
38
|
+
script.
|
|
39
|
+
window.silex.start()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const express = require('express')
|
|
2
|
+
const { Directus } = require('@directus/sdk')
|
|
3
|
+
|
|
4
|
+
module.exports = async function(config, opts = {}) {
|
|
5
|
+
config.on('silex:startup:start', ({app}) => {
|
|
6
|
+
const router = express.Router()
|
|
7
|
+
router.post(['/publish', '/website', '/assets'], async function(req, res, next) {
|
|
8
|
+
try {
|
|
9
|
+
const token = req.body.token || opts.directusToken
|
|
10
|
+
const directus = new Directus(opts.directusUrl)
|
|
11
|
+
directus.storage.auth_token = token
|
|
12
|
+
const me = await directus.users.me.read()
|
|
13
|
+
next()
|
|
14
|
+
} catch(err) {
|
|
15
|
+
console.error('Publish failed in auth module. Did you provide a directus token?', err.message)
|
|
16
|
+
res.status(403).json({ message: err.message })
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
app.use(router)
|
|
20
|
+
})
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
exports.noCache = function (req, res, next) {
|
|
2
|
+
res.header('Cache-Control', 'private,no-cache,no-store,must-revalidate,proxy-revalidate')
|
|
3
|
+
res.header('Expires', '-1')
|
|
4
|
+
res.header('Pragma', 'no-cache')
|
|
5
|
+
next()
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
exports.withCache = function (req, res, next) {
|
|
9
|
+
res.header('Cache-Control', 'public,max-age=86400,immutable') // 24h
|
|
10
|
+
next()
|
|
11
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const { readFile, writeFile, mkdir, copyFile } = require('node:fs/promises')
|
|
2
|
+
const { join, dirname, basename } = require('path')
|
|
3
|
+
const { homedir } = require('os')
|
|
4
|
+
|
|
5
|
+
//const { URLRewriter, URLTranslator } = require('cssurl')
|
|
6
|
+
|
|
7
|
+
async function mkdirIfNotExists(path, options = null) {
|
|
8
|
+
try {
|
|
9
|
+
return await mkdir(path, options)
|
|
10
|
+
} catch (err) {
|
|
11
|
+
if (err.code === 'EEXIST') {
|
|
12
|
+
return
|
|
13
|
+
} else {
|
|
14
|
+
throw err
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Paths
|
|
20
|
+
const FS_ROOT = process.env.FS_ROOT || join(homedir(), '.silex')
|
|
21
|
+
const ASSETS_PATH = 'assets'
|
|
22
|
+
const ASSETS_URL = 'assets'
|
|
23
|
+
const HTML_PATH = ''
|
|
24
|
+
const CSS_PATH = ''
|
|
25
|
+
const DATA_FILE_NAME = '/.silex.data.json'
|
|
26
|
+
const PUBLICATION_PATH = 'publication'
|
|
27
|
+
|
|
28
|
+
function path(id = 'default') {
|
|
29
|
+
return join(FS_ROOT, id)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
exports.assetsDir = async function (id) {
|
|
33
|
+
return join(path(id), ASSETS_PATH)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
exports.assetUrl = async function (id, fileName) {
|
|
37
|
+
return `${ASSETS_URL}/${fileName}`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Init files for projects and each project
|
|
41
|
+
exports.init = async function (id) {
|
|
42
|
+
await mkdirIfNotExists(FS_ROOT)
|
|
43
|
+
const folder = path(id)
|
|
44
|
+
const uploadDir = join(folder, ASSETS_PATH)
|
|
45
|
+
await mkdirIfNotExists(uploadDir, { recursive: true, })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Read project data
|
|
49
|
+
exports.readData = async function (id) {
|
|
50
|
+
return readFile(path(id) + DATA_FILE_NAME)
|
|
51
|
+
}
|
|
52
|
+
// Write project data
|
|
53
|
+
exports.writeData = async function (id, data) {
|
|
54
|
+
return writeFile(path(id) + DATA_FILE_NAME, JSON.stringify(data))
|
|
55
|
+
}
|
|
56
|
+
function required(prop, name) {
|
|
57
|
+
if (!prop) throw new Error(`Error missing required param ${name}`)
|
|
58
|
+
}
|
|
59
|
+
exports.publish = async function (id, files, data) {
|
|
60
|
+
const { assets, settings, publication } = data
|
|
61
|
+
required(assets, 'assets')
|
|
62
|
+
required(files, 'files')
|
|
63
|
+
required(id, 'id')
|
|
64
|
+
const folder = path(id)
|
|
65
|
+
const publicationDir = join(folder, publication?.path || PUBLICATION_PATH)
|
|
66
|
+
|
|
67
|
+
// HTML and CSS
|
|
68
|
+
await Promise.all(files
|
|
69
|
+
.map(async (file) => {
|
|
70
|
+
// // Process the page CSS to have correct relative URLs
|
|
71
|
+
// let css = file.css
|
|
72
|
+
// try {
|
|
73
|
+
// if(settings?.html?.path != settings?.css?.path) {
|
|
74
|
+
// const rewriter = new URLRewriter(function(url) {
|
|
75
|
+
// const translator = new URLTranslator()
|
|
76
|
+
// return translator.translate(url, settings?.html?.path, settings?.css?.path)
|
|
77
|
+
// })
|
|
78
|
+
// css = rewriter.rewrite(css)
|
|
79
|
+
// }
|
|
80
|
+
// } catch (err) {
|
|
81
|
+
// console.error('Error processing CSS', err, file)
|
|
82
|
+
// throw new Error(`Error processing CSS. ${err.message}`)
|
|
83
|
+
// }
|
|
84
|
+
try {
|
|
85
|
+
// Paths
|
|
86
|
+
const htmlPath = join(publicationDir, file.htmlPath)
|
|
87
|
+
const cssPath = join(publicationDir, file.cssPath)
|
|
88
|
+
// Init
|
|
89
|
+
await mkdirIfNotExists(dirname(htmlPath), { recursive: true, })
|
|
90
|
+
await mkdirIfNotExists(dirname(cssPath), { recursive: true, })
|
|
91
|
+
// Write
|
|
92
|
+
await writeFile(htmlPath, file.html)
|
|
93
|
+
await writeFile(cssPath, file.css)
|
|
94
|
+
} catch (err) {
|
|
95
|
+
throw new Error(`Publication error: could not write files ${file.cssPath} and ${file.htmlPath}. ${err.message}`)
|
|
96
|
+
}
|
|
97
|
+
}))
|
|
98
|
+
// Assets
|
|
99
|
+
const assetsFolder = join(publicationDir, publication?.assets?.path || ASSETS_PATH)
|
|
100
|
+
await mkdirIfNotExists(assetsFolder, { recursive: true, })
|
|
101
|
+
|
|
102
|
+
assets.forEach(async asset => {
|
|
103
|
+
required(asset.src, 'asset\'s "src" attribute')
|
|
104
|
+
const src = join(folder, asset.src)
|
|
105
|
+
const dst = join(assetsFolder, basename(asset.src))
|
|
106
|
+
try {
|
|
107
|
+
await copyFile(src, dst)
|
|
108
|
+
} catch (err) {
|
|
109
|
+
throw new Error(`Publication error: could not copy asset file ${src} to ${dst}. ${err.message}`)
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
return publication?.url
|
|
113
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const bodyParser = require('body-parser')
|
|
2
|
+
const compression = require('compression')
|
|
3
|
+
const cookieParser = require('cookie-parser')
|
|
4
|
+
const session = require('cookie-session')
|
|
5
|
+
|
|
6
|
+
module.exports = async function(config, opts = {}) {
|
|
7
|
+
// Options with defaults
|
|
8
|
+
const options = {
|
|
9
|
+
jsonLimit: process.env.SILEX_EXPRESS_JSON_LIMIT || '1mb',
|
|
10
|
+
textLimit: process.env.SILEX_EXPRESS_TEXT_LIMIT || '10mb',
|
|
11
|
+
sessionName: process.env.SILEX_SESSION_NAME || 'silex-session',
|
|
12
|
+
sessionSecret: process.env.SILEX_SESSION_SECRET || 'replace this session secret in env vars',
|
|
13
|
+
...opts,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
config.on('silex:startup:start', ({app}) => {
|
|
17
|
+
// compress gzip when possible
|
|
18
|
+
app.use(compression())
|
|
19
|
+
|
|
20
|
+
// cookie & session
|
|
21
|
+
app.use(bodyParser.json({limit: options.jsonLimit}))
|
|
22
|
+
app.use(bodyParser.text({limit: options.textLimit}))
|
|
23
|
+
app.use(cookieParser())
|
|
24
|
+
app.use(session({
|
|
25
|
+
name: options.sessionName,
|
|
26
|
+
secret: options.sessionSecret,
|
|
27
|
+
}))
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Config } from '../config'
|
|
2
|
+
import { EVENT_WRITE_END, EVENT_ASSET_WRITE_END } from './WebsitePlugin'
|
|
3
|
+
import { EVENT_PUBLISH_END } from './PublishPlugin'
|
|
4
|
+
import fetch from 'node-fetch'
|
|
5
|
+
import { projectPath, assetsDir } from '../project'
|
|
6
|
+
|
|
7
|
+
// type HooksOptions = {
|
|
8
|
+
// gitUrl?: string
|
|
9
|
+
// buildUrl?: string
|
|
10
|
+
// }
|
|
11
|
+
|
|
12
|
+
const rootPath = process.env.DATA_FOLDER
|
|
13
|
+
|
|
14
|
+
module.exports = async function(config, opts = {}) {
|
|
15
|
+
// Options with defaults
|
|
16
|
+
const options = {
|
|
17
|
+
gitUrl: process.env.SILEX_HOOK_GIT,
|
|
18
|
+
buildUrl: process.env.SILEX_HOOK_BUILD,
|
|
19
|
+
...opts
|
|
20
|
+
}
|
|
21
|
+
config.on(EVENT_WRITE_END, async ({ res, req, projectId, data }) => {
|
|
22
|
+
if(options.gitUrl) {
|
|
23
|
+
await hook(options.gitUrl, {
|
|
24
|
+
path: projectPath(projectId),
|
|
25
|
+
message: 'Change from Silex',
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
config.on(EVENT_ASSET_WRITE_END, async ({ res, req, projectId, uploadDir, form, data }) => {
|
|
30
|
+
if(options.gitUrl) {
|
|
31
|
+
await hook(options.gitUrl, {
|
|
32
|
+
path: projectPath(projectId),
|
|
33
|
+
message: 'Change from Silex',
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
config.on(EVENT_PUBLISH_END, async ({ projectId, files, req, res }) => {
|
|
38
|
+
if(options.buildUrl) {
|
|
39
|
+
await hook(options.buildUrl)
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function hook(url, params = {}) {
|
|
45
|
+
if(url) {
|
|
46
|
+
const urlObj = new URL(url)
|
|
47
|
+
console.log('Calling hook', {url})
|
|
48
|
+
try {
|
|
49
|
+
Object.keys(params)
|
|
50
|
+
.forEach(key => urlObj.searchParams.append(key, params[key]))
|
|
51
|
+
return fetch(urlObj.toString())
|
|
52
|
+
} catch(error) {
|
|
53
|
+
console.log('Hook call failed', {url, error})
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const express = require('express')
|
|
2
|
+
const { resolve } = require('path')
|
|
3
|
+
|
|
4
|
+
const { noCache } = require('./Cache')
|
|
5
|
+
const { minify } = require('html-minifier')
|
|
6
|
+
|
|
7
|
+
const router = express.Router()
|
|
8
|
+
|
|
9
|
+
// type PublishOptions = {
|
|
10
|
+
// statusUrl?: string
|
|
11
|
+
// }
|
|
12
|
+
|
|
13
|
+
module.exports = async function(config, opts = {}) {
|
|
14
|
+
const options = {
|
|
15
|
+
// Defaults
|
|
16
|
+
statusUrl: process.env.SILEX_PUBLICATION_STATUS_URL,
|
|
17
|
+
backend: 'src/plugins/DefaultBackend.js',
|
|
18
|
+
// Options
|
|
19
|
+
...opts,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Link to the desired backend
|
|
23
|
+
const backend = resolve(__dirname, '../..', options.backend)
|
|
24
|
+
const { publish } = require(backend)
|
|
25
|
+
|
|
26
|
+
config.on('silex:startup:start', ({app}) => {
|
|
27
|
+
// Start publication
|
|
28
|
+
router.post('/publish', async function(req, res) {
|
|
29
|
+
const { pages, files, id } = req.body.data
|
|
30
|
+
if (!pages || !id) {
|
|
31
|
+
console.error('Error in the request, pages and id parmas required', {id})
|
|
32
|
+
res.status(400).send({
|
|
33
|
+
message: 'Error in the request, pages and id parmas required',
|
|
34
|
+
})
|
|
35
|
+
} else {
|
|
36
|
+
// Optim
|
|
37
|
+
files.forEach(file => {
|
|
38
|
+
file.html = minify(file.html, {
|
|
39
|
+
continueOnParseError: true,
|
|
40
|
+
collapseInlineTagWhitespace: true,
|
|
41
|
+
collapseWhitespace: true,
|
|
42
|
+
minifyCSS: true,
|
|
43
|
+
minifyJS: true,
|
|
44
|
+
removeScriptTypeAttributes: true,
|
|
45
|
+
removeStyleLinkTypeAttributes: true,
|
|
46
|
+
}).trim()
|
|
47
|
+
})
|
|
48
|
+
// Publication
|
|
49
|
+
await config.emit(EVENT_PUBLISH_START, { id, files, req})
|
|
50
|
+
let url
|
|
51
|
+
try {
|
|
52
|
+
url = await publish(id, files, req.body.data)
|
|
53
|
+
} catch (err) {
|
|
54
|
+
console.error('Error publishing the website', err)
|
|
55
|
+
res.status(500).json({ message: `Error publishing the website. ${err.message}`})
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
const mutable = { id, files, req, res, url, statusUrl: options.statusUrl }
|
|
59
|
+
await config.emit(EVENT_PUBLISH_END, mutable)
|
|
60
|
+
res.json({
|
|
61
|
+
url: mutable.url,
|
|
62
|
+
statusUrl: mutable.statusUrl,
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
app.use(noCache, router)
|
|
68
|
+
})
|
|
69
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const express = require('express')
|
|
2
|
+
const { readFileSync } = require('fs')
|
|
3
|
+
const { createServer } = require('https')
|
|
4
|
+
const fromorceSSL = require('express-force-ssl')
|
|
5
|
+
|
|
6
|
+
// interface SslOptions {
|
|
7
|
+
// forceHttps?: boolean
|
|
8
|
+
// trustXFPHeader?: boolean
|
|
9
|
+
// privateKey?: string
|
|
10
|
+
// certificate?: string
|
|
11
|
+
// sslPort?: string
|
|
12
|
+
// }
|
|
13
|
+
|
|
14
|
+
module.exports = async function(config, opts = {}) {
|
|
15
|
+
// Options with defaults
|
|
16
|
+
const options = {
|
|
17
|
+
forceHttps: process.env.SILEX_FORCE_HTTPS === 'true',
|
|
18
|
+
trustXFPHeader: process.env.SILEX_FORCE_HTTPS_TRUST_XFP_HEADER === 'true',
|
|
19
|
+
privateKey: process.env.SILEX_SSL_PRIVATE_KEY,
|
|
20
|
+
certificate: process.env.SILEX_SSL_CERTIFICATE,
|
|
21
|
+
sslPort: process.env.SSL_PORT || '443',
|
|
22
|
+
...opts,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Add routes on silex startup
|
|
26
|
+
config.on('silex:startup:start', ({app}) => {
|
|
27
|
+
const router = express.Router()
|
|
28
|
+
|
|
29
|
+
// SSL
|
|
30
|
+
// force ssl if the env var SILEX_FORCE_HTTPS is set
|
|
31
|
+
if (options.forceHttps) {
|
|
32
|
+
console.log('> Force SSL option is enabled')
|
|
33
|
+
app.set('forceSSLOptions', {
|
|
34
|
+
trustXFPHeader: !!options.trustXFPHeader,
|
|
35
|
+
})
|
|
36
|
+
router.use(forceSSL)
|
|
37
|
+
} else {
|
|
38
|
+
console.log('> Force SSL option is disabled, env var SILEX_FORCE_HTTPS not set')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// SSL certificate
|
|
42
|
+
if (options.privateKey && options.certificate) {
|
|
43
|
+
console.log('> SSL certificate is enabled, found certificate:', options.certificate)
|
|
44
|
+
try {
|
|
45
|
+
const privateKey = readFileSync(options.privateKey).toString()
|
|
46
|
+
const certificate = readFileSync(options.certificate).toString()
|
|
47
|
+
|
|
48
|
+
const sslServerOptions = {
|
|
49
|
+
key: privateKey,
|
|
50
|
+
cert: certificate,
|
|
51
|
+
requestCert: true,
|
|
52
|
+
rejectUnauthorized: false,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
createServer(sslServerOptions, this).listen(options.sslPort, () => {
|
|
56
|
+
console.log('SSL: listening on port ', options.sslPort)
|
|
57
|
+
})
|
|
58
|
+
} catch (e) {
|
|
59
|
+
console.error('SSL: load certificate failed.', e)
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
console.log('> SSL certificate disabled, env vars SILEX_SSL_CERTIFICATE and SILEX_SSL_PRIVATE_KEY not set')
|
|
63
|
+
}
|
|
64
|
+
app.use(router)
|
|
65
|
+
})
|
|
66
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const express = require('express')
|
|
2
|
+
const nodeModules = require('node_modules-path')
|
|
3
|
+
const serveStatic = require('serve-static')
|
|
4
|
+
const { withCache } = require('./Cache')
|
|
5
|
+
|
|
6
|
+
const Path = require('path')
|
|
7
|
+
|
|
8
|
+
// type StaticOptions = {
|
|
9
|
+
// routes?: {
|
|
10
|
+
// path?: string
|
|
11
|
+
// route?: string
|
|
12
|
+
// module?: string
|
|
13
|
+
// }[]
|
|
14
|
+
// }
|
|
15
|
+
|
|
16
|
+
module.exports = async function(config, opts = {}) {
|
|
17
|
+
// Options with defaults
|
|
18
|
+
const options = {
|
|
19
|
+
rootPath: Path.join(__dirname, '../..'),
|
|
20
|
+
routes: [
|
|
21
|
+
{
|
|
22
|
+
route: '/',
|
|
23
|
+
path: 'public',
|
|
24
|
+
}, {
|
|
25
|
+
route: '/',
|
|
26
|
+
path: 'dist/client',
|
|
27
|
+
},
|
|
28
|
+
]
|
|
29
|
+
// add project route for source maps
|
|
30
|
+
.concat(config.debug ? [{
|
|
31
|
+
route: '/',
|
|
32
|
+
path: './',
|
|
33
|
+
}] : []),
|
|
34
|
+
...opts,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
config.on('silex:startup:start', ({app}) => {
|
|
38
|
+
const router = express.Router()
|
|
39
|
+
options.routes
|
|
40
|
+
.forEach(folder => {
|
|
41
|
+
// either the module root folder or silex root folder
|
|
42
|
+
const path = Path.join(options.rootPath, folder.path || '')
|
|
43
|
+
if (!folder.route) throw new Error('The config for static module has no `route` attribute: ' + folder)
|
|
44
|
+
router.use(folder.route, serveStatic(path))
|
|
45
|
+
})
|
|
46
|
+
app.use(withCache, router)
|
|
47
|
+
})
|
|
48
|
+
}
|