@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.
Files changed (106) hide show
  1. package/.dockerignore +5 -0
  2. package/.editorconfig +14 -0
  3. package/.eslintrc.json +43 -0
  4. package/.github/FUNDING.yml +13 -0
  5. package/.github/workflows/docker-hub-publish.yml +36 -0
  6. package/.github/workflows/npm-publish.yml +19 -0
  7. package/.github/workflows/test.yml +26 -0
  8. package/.nvmrc +1 -0
  9. package/.silex.js +19 -0
  10. package/CODE_OF_CONDUCT.md +128 -0
  11. package/CONTRIBUTING.md +108 -0
  12. package/Dockerfile +15 -0
  13. package/LICENSE +203 -0
  14. package/app.json +106 -0
  15. package/directus.silex.js +7 -0
  16. package/dist/client/css/admin.css +4350 -0
  17. package/dist/client/index.html +9 -0
  18. package/dist/client/js/main.js +282 -0
  19. package/dist/client/js/main.js.LICENSE.txt +52 -0
  20. package/dist/server/config.js +50 -0
  21. package/dist/server/events.js +5 -0
  22. package/dist/server/index.js +29 -0
  23. package/dist/server/start.js +18 -0
  24. package/jest.config.js +10 -0
  25. package/logos/logo_by_ceubri/silex-icon-2018@200px.png +0 -0
  26. package/logos/logo_by_ceubri/silex-icon-2018@4x.png +0 -0
  27. package/logos/logo_by_ceubri/silex-logo-2018-white-transp.pdf +0 -0
  28. package/logos/logo_by_ceubri/silex-logo-2018-white.jpg +0 -0
  29. package/logos/logo_by_ceubri/silex-logo-2018-white.pdf +0 -0
  30. package/logos/logo_by_ceubri/silex-logo-2018-white.png +0 -0
  31. package/logos/logo_by_ceubri/silex-logo-2018-white@0.5x.png +0 -0
  32. package/logos/logo_by_ceubri/silex-logo-2018-white@0.75x.png +0 -0
  33. package/logos/logo_by_ceubri/silex-logo-2018-white@4x.jpg +0 -0
  34. package/logos/logo_by_ceubri/silex-logo-2018-white@4x.png +0 -0
  35. package/logos/logo_by_ceubri/silex-logo-2018.ai +2520 -6
  36. package/logos/logo_by_ceubri/silex-logo-2018.jpg +0 -0
  37. package/logos/logo_by_ceubri/silex-logo-2018.pdf +0 -0
  38. package/logos/logo_by_ceubri/silex-logo-2018.png +0 -0
  39. package/logos/logo_by_ceubri/silex-logo-2018@0.5x.png +0 -0
  40. package/logos/logo_by_ceubri/silex-logo-2018@0.75x.png +0 -0
  41. package/logos/logo_by_ceubri/silex-logo-2018@4x.jpg +0 -0
  42. package/logos/logo_by_ceubri/silex-logo-2018@4x.png +0 -0
  43. package/logos/silexlabs.png +0 -0
  44. package/package.json +170 -0
  45. package/public/assets/container.png +0 -0
  46. package/public/assets/favicon.png +0 -0
  47. package/public/assets/logo-silex-small.png +0 -0
  48. package/public/example-config.js +31 -0
  49. package/public/robots.txt +2 -0
  50. package/readme.md +51 -0
  51. package/src/css/_mixins.scss +8 -0
  52. package/src/css/_variables.scss +33 -0
  53. package/src/css/dialog.scss +34 -0
  54. package/src/css/grapesjs.scss +44 -0
  55. package/src/css/index.scss +22 -0
  56. package/src/css/loader.scss +10 -0
  57. package/src/css/page-panel.scss +65 -0
  58. package/src/css/project-bar.scss +102 -0
  59. package/src/css/silex.scss +183 -0
  60. package/src/html/debug.pug +8 -0
  61. package/src/html/index.pug +39 -0
  62. package/src/html/templates/includes.pug +2 -0
  63. package/src/html/templates/workspace.pug +2 -0
  64. package/src/plugins/AuthPlugin.js +21 -0
  65. package/src/plugins/Cache.js +11 -0
  66. package/src/plugins/DefaultBackend.js +113 -0
  67. package/src/plugins/ExpressPlugin.js +30 -0
  68. package/src/plugins/HooksPlugin.js +58 -0
  69. package/src/plugins/PublishPlugin.js +69 -0
  70. package/src/plugins/SslPlugin.js +66 -0
  71. package/src/plugins/StaticPlugin.js +48 -0
  72. package/src/plugins/WebsitePlugin.js +149 -0
  73. package/src/ts/client/config.ts +21 -0
  74. package/src/ts/client/expose.ts +4 -0
  75. package/src/ts/client/grapesjs/blocks.ts +88 -0
  76. package/src/ts/client/grapesjs/eleventy.ts +21 -0
  77. package/src/ts/client/grapesjs/index.ts +255 -0
  78. package/src/ts/client/grapesjs/internal-links.test.ts +87 -0
  79. package/src/ts/client/grapesjs/internal-links.ts +210 -0
  80. package/src/ts/client/grapesjs/new-page-dialog.ts +73 -0
  81. package/src/ts/client/grapesjs/openImport.ts +59 -0
  82. package/src/ts/client/grapesjs/page-panel.ts +101 -0
  83. package/src/ts/client/grapesjs/project-bar.ts +92 -0
  84. package/src/ts/client/grapesjs/publish.ts +386 -0
  85. package/src/ts/client/grapesjs/rich-text.ts +16 -0
  86. package/src/ts/client/grapesjs/semantic.ts +96 -0
  87. package/src/ts/client/grapesjs/settings.ts +190 -0
  88. package/src/ts/client/grapesjs/symbolDialogs.ts +21 -0
  89. package/src/ts/client/grapesjs/template.ts +166 -0
  90. package/src/ts/client/index.ts +38 -0
  91. package/src/ts/client/utils.ts +9 -0
  92. package/src/ts/externs.ts +1 -0
  93. package/src/ts/page.ts +14 -0
  94. package/src/ts/server/cli.ts +23 -0
  95. package/src/ts/server/config.ts +62 -0
  96. package/src/ts/server/events.ts +2 -0
  97. package/src/ts/server/index.ts +35 -0
  98. package/src/ts/server/start.ts +18 -0
  99. package/src/ts/server/utils/BackwardCompat.ts +534 -0
  100. package/src/ts/types.ts +138 -0
  101. package/tsconfig-server-debug.json +20 -0
  102. package/tsconfig-server-release.json +19 -0
  103. package/tsconfig.jest.json +9 -0
  104. package/tsconfig.json +27 -0
  105. package/webpack.config.js +29 -0
  106. 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,8 @@
1
+ extends index.pug
2
+
3
+ block silex-start
4
+ script.
5
+ document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')
6
+ window.silex.start({
7
+ debug: true,
8
+ })
@@ -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,2 @@
1
+ link(rel='stylesheet', type='text/css', href='css/admin.css')
2
+
@@ -0,0 +1,2 @@
1
+ main.silex-workspace
2
+ #gjs.silex-main.silex-dialog-hide.silex-ui-full
@@ -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
+ }