@silexlabs/silex 3.0.0-alpha.100

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 (282) hide show
  1. package/.env.default +19 -0
  2. package/.silex.js +37 -0
  3. package/LICENSE +661 -0
  4. package/README.md +96 -0
  5. package/dist/client/client/api.d.ts +85 -0
  6. package/dist/client/client/assetUrl.d.ts +51 -0
  7. package/dist/client/client/config.d.ts +58 -0
  8. package/dist/client/client/events.d.ts +14 -0
  9. package/dist/client/client/expose.d.ts +8 -0
  10. package/dist/client/client/grapesjs/LoginDialog.d.ts +15 -0
  11. package/dist/client/client/grapesjs/PublicationManager.d.ts +72 -0
  12. package/dist/client/client/grapesjs/PublicationUi.d.ts +62 -0
  13. package/dist/client/client/grapesjs/blocks.d.ts +1 -0
  14. package/dist/client/client/grapesjs/breadcrumbs.d.ts +1 -0
  15. package/dist/client/client/grapesjs/footer.d.ts +2 -0
  16. package/dist/client/client/grapesjs/index.d.ts +5 -0
  17. package/dist/client/client/grapesjs/internal-links.d.ts +1 -0
  18. package/dist/client/client/grapesjs/new-page-dialog.d.ts +2 -0
  19. package/dist/client/client/grapesjs/openImport.d.ts +9 -0
  20. package/dist/client/client/grapesjs/page-panel.d.ts +3 -0
  21. package/dist/client/client/grapesjs/project-bar.d.ts +1 -0
  22. package/dist/client/client/grapesjs/rich-text.d.ts +1 -0
  23. package/dist/client/client/grapesjs/semantic.d.ts +1 -0
  24. package/dist/client/client/grapesjs/settings.d.ts +2 -0
  25. package/dist/client/client/grapesjs/storage.d.ts +1 -0
  26. package/dist/client/client/grapesjs/symbolDialogs.d.ts +3 -0
  27. package/dist/client/client/index.d.ts +5 -0
  28. package/dist/client/client/publication-transformers.d.ts +41 -0
  29. package/dist/client/client/utils.d.ts +2 -0
  30. package/dist/client/constants.d.ts +29 -0
  31. package/dist/client/css/admin.css +4491 -0
  32. package/dist/client/index.html +9 -0
  33. package/dist/client/js/main.js +321 -0
  34. package/dist/client/js/main.js.LICENSE.txt +24 -0
  35. package/dist/client/page.d.ts +2 -0
  36. package/dist/client/types.d.ts +334 -0
  37. package/dist/plugins/client/client/api.d.ts +85 -0
  38. package/dist/plugins/client/client/api.js +114 -0
  39. package/dist/plugins/client/client/api.js.map +1 -0
  40. package/dist/plugins/client/client/assetUrl.d.ts +51 -0
  41. package/dist/plugins/client/client/assetUrl.js +224 -0
  42. package/dist/plugins/client/client/assetUrl.js.map +1 -0
  43. package/dist/plugins/client/client/config.d.ts +58 -0
  44. package/dist/plugins/client/client/config.js +90 -0
  45. package/dist/plugins/client/client/config.js.map +1 -0
  46. package/dist/plugins/client/client/events.d.ts +14 -0
  47. package/dist/plugins/client/client/events.js +34 -0
  48. package/dist/plugins/client/client/events.js.map +1 -0
  49. package/dist/plugins/client/client/grapesjs/LoginDialog.d.ts +15 -0
  50. package/dist/plugins/client/client/grapesjs/LoginDialog.js +195 -0
  51. package/dist/plugins/client/client/grapesjs/LoginDialog.js.map +1 -0
  52. package/dist/plugins/client/client/grapesjs/PublicationManager.d.ts +72 -0
  53. package/dist/plugins/client/client/grapesjs/PublicationManager.js +356 -0
  54. package/dist/plugins/client/client/grapesjs/PublicationManager.js.map +1 -0
  55. package/dist/plugins/client/client/grapesjs/PublicationUi.d.ts +62 -0
  56. package/dist/plugins/client/client/grapesjs/PublicationUi.js +336 -0
  57. package/dist/plugins/client/client/grapesjs/PublicationUi.js.map +1 -0
  58. package/dist/plugins/client/client/grapesjs/blocks.d.ts +1 -0
  59. package/dist/plugins/client/client/grapesjs/blocks.js +37 -0
  60. package/dist/plugins/client/client/grapesjs/blocks.js.map +1 -0
  61. package/dist/plugins/client/client/grapesjs/breadcrumbs.d.ts +1 -0
  62. package/dist/plugins/client/client/grapesjs/breadcrumbs.js +75 -0
  63. package/dist/plugins/client/client/grapesjs/breadcrumbs.js.map +1 -0
  64. package/dist/plugins/client/client/grapesjs/footer.d.ts +2 -0
  65. package/dist/plugins/client/client/grapesjs/footer.js +28 -0
  66. package/dist/plugins/client/client/grapesjs/footer.js.map +1 -0
  67. package/dist/plugins/client/client/grapesjs/index.d.ts +5 -0
  68. package/dist/plugins/client/client/grapesjs/index.js +313 -0
  69. package/dist/plugins/client/client/grapesjs/index.js.map +1 -0
  70. package/dist/plugins/client/client/grapesjs/internal-links.d.ts +1 -0
  71. package/dist/plugins/client/client/grapesjs/internal-links.js +219 -0
  72. package/dist/plugins/client/client/grapesjs/internal-links.js.map +1 -0
  73. package/dist/plugins/client/client/grapesjs/new-page-dialog.d.ts +2 -0
  74. package/dist/plugins/client/client/grapesjs/new-page-dialog.js +83 -0
  75. package/dist/plugins/client/client/grapesjs/new-page-dialog.js.map +1 -0
  76. package/dist/plugins/client/client/grapesjs/openImport.d.ts +9 -0
  77. package/dist/plugins/client/client/grapesjs/openImport.js +74 -0
  78. package/dist/plugins/client/client/grapesjs/openImport.js.map +1 -0
  79. package/dist/plugins/client/client/grapesjs/page-panel.d.ts +3 -0
  80. package/dist/plugins/client/client/grapesjs/page-panel.js +113 -0
  81. package/dist/plugins/client/client/grapesjs/page-panel.js.map +1 -0
  82. package/dist/plugins/client/client/grapesjs/project-bar.d.ts +1 -0
  83. package/dist/plugins/client/client/grapesjs/project-bar.js +104 -0
  84. package/dist/plugins/client/client/grapesjs/project-bar.js.map +1 -0
  85. package/dist/plugins/client/client/grapesjs/rich-text.d.ts +1 -0
  86. package/dist/plugins/client/client/grapesjs/rich-text.js +31 -0
  87. package/dist/plugins/client/client/grapesjs/rich-text.js.map +1 -0
  88. package/dist/plugins/client/client/grapesjs/semantic.d.ts +1 -0
  89. package/dist/plugins/client/client/grapesjs/semantic.js +110 -0
  90. package/dist/plugins/client/client/grapesjs/semantic.js.map +1 -0
  91. package/dist/plugins/client/client/grapesjs/settings.d.ts +2 -0
  92. package/dist/plugins/client/client/grapesjs/settings.js +198 -0
  93. package/dist/plugins/client/client/grapesjs/settings.js.map +1 -0
  94. package/dist/plugins/client/client/grapesjs/storage.d.ts +1 -0
  95. package/dist/plugins/client/client/grapesjs/storage.js +126 -0
  96. package/dist/plugins/client/client/grapesjs/storage.js.map +1 -0
  97. package/dist/plugins/client/client/grapesjs/symbolDialogs.d.ts +3 -0
  98. package/dist/plugins/client/client/grapesjs/symbolDialogs.js +36 -0
  99. package/dist/plugins/client/client/grapesjs/symbolDialogs.js.map +1 -0
  100. package/dist/plugins/client/client/publication-transformers.d.ts +41 -0
  101. package/dist/plugins/client/client/publication-transformers.js +238 -0
  102. package/dist/plugins/client/client/publication-transformers.js.map +1 -0
  103. package/dist/plugins/client/client/utils.d.ts +2 -0
  104. package/dist/plugins/client/client/utils.js +25 -0
  105. package/dist/plugins/client/client/utils.js.map +1 -0
  106. package/dist/plugins/client/constants.d.ts +29 -0
  107. package/dist/plugins/client/constants.js +46 -0
  108. package/dist/plugins/client/constants.js.map +1 -0
  109. package/dist/plugins/client/page.d.ts +2 -0
  110. package/dist/plugins/client/page.js +31 -0
  111. package/dist/plugins/client/page.js.map +1 -0
  112. package/dist/plugins/client/plugins/client/11ty.d.ts +17 -0
  113. package/dist/plugins/client/plugins/client/11ty.js +86 -0
  114. package/dist/plugins/client/plugins/client/11ty.js.map +1 -0
  115. package/dist/plugins/client/plugins/client/publicationRenderer.d.ts +2 -0
  116. package/dist/plugins/client/plugins/client/publicationRenderer.js +170 -0
  117. package/dist/plugins/client/plugins/client/publicationRenderer.js.map +1 -0
  118. package/dist/plugins/client/plugins/client/template.d.ts +2 -0
  119. package/dist/plugins/client/plugins/client/template.js +183 -0
  120. package/dist/plugins/client/plugins/client/template.js.map +1 -0
  121. package/dist/plugins/client/plugins/client/website-info.d.ts +1 -0
  122. package/dist/plugins/client/plugins/client/website-info.js +72 -0
  123. package/dist/plugins/client/plugins/client/website-info.js.map +1 -0
  124. package/dist/plugins/client/types.d.ts +334 -0
  125. package/dist/plugins/client/types.js +74 -0
  126. package/dist/plugins/client/types.js.map +1 -0
  127. package/dist/plugins/server/constants.d.ts +29 -0
  128. package/dist/plugins/server/constants.js +49 -0
  129. package/dist/plugins/server/constants.js.map +1 -0
  130. package/dist/plugins/server/plugins/server/AuthPlugin.d.ts +1 -0
  131. package/dist/plugins/server/plugins/server/AuthPlugin.js +40 -0
  132. package/dist/plugins/server/plugins/server/AuthPlugin.js.map +1 -0
  133. package/dist/plugins/server/plugins/server/Cache.d.ts +2 -0
  134. package/dist/plugins/server/plugins/server/Cache.js +32 -0
  135. package/dist/plugins/server/plugins/server/Cache.js.map +1 -0
  136. package/dist/plugins/server/plugins/server/DownloadConnector.d.ts +29 -0
  137. package/dist/plugins/server/plugins/server/DownloadConnector.js +129 -0
  138. package/dist/plugins/server/plugins/server/DownloadConnector.js.map +1 -0
  139. package/dist/plugins/server/plugins/server/FtpConnector.d.ts +86 -0
  140. package/dist/plugins/server/plugins/server/FtpConnector.js +526 -0
  141. package/dist/plugins/server/plugins/server/FtpConnector.js.map +1 -0
  142. package/dist/plugins/server/plugins/server/GitlabConnector.d.ts +87 -0
  143. package/dist/plugins/server/plugins/server/GitlabConnector.js +564 -0
  144. package/dist/plugins/server/plugins/server/GitlabConnector.js.map +1 -0
  145. package/dist/plugins/server/plugins/server/PublicationOptimizer.d.ts +0 -0
  146. package/dist/plugins/server/plugins/server/PublicationOptimizer.js +14 -0
  147. package/dist/plugins/server/plugins/server/PublicationOptimizer.js.map +1 -0
  148. package/dist/plugins/server/plugins/server/SslPlugin.d.ts +1 -0
  149. package/dist/plugins/server/plugins/server/SslPlugin.js +86 -0
  150. package/dist/plugins/server/plugins/server/SslPlugin.js.map +1 -0
  151. package/dist/plugins/server/plugins/server/StaticPlugin.d.ts +1 -0
  152. package/dist/plugins/server/plugins/server/StaticPlugin.js +78 -0
  153. package/dist/plugins/server/plugins/server/StaticPlugin.js.map +1 -0
  154. package/dist/plugins/server/server/config.d.ts +59 -0
  155. package/dist/plugins/server/server/config.js +181 -0
  156. package/dist/plugins/server/server/config.js.map +1 -0
  157. package/dist/plugins/server/server/connectors/FsHosting.d.ts +13 -0
  158. package/dist/plugins/server/server/connectors/FsHosting.js +54 -0
  159. package/dist/plugins/server/server/connectors/FsHosting.js.map +1 -0
  160. package/dist/plugins/server/server/connectors/FsStorage.d.ts +43 -0
  161. package/dist/plugins/server/server/connectors/FsStorage.js +257 -0
  162. package/dist/plugins/server/server/connectors/FsStorage.js.map +1 -0
  163. package/dist/plugins/server/server/connectors/connectors.d.ts +87 -0
  164. package/dist/plugins/server/server/connectors/connectors.js +127 -0
  165. package/dist/plugins/server/server/connectors/connectors.js.map +1 -0
  166. package/dist/plugins/server/server/events.d.ts +26 -0
  167. package/dist/plugins/server/server/events.js +31 -0
  168. package/dist/plugins/server/server/events.js.map +1 -0
  169. package/dist/plugins/server/server/jobs.d.ts +12 -0
  170. package/dist/plugins/server/server/jobs.js +77 -0
  171. package/dist/plugins/server/server/jobs.js.map +1 -0
  172. package/dist/plugins/server/server/utils/validation.d.ts +7 -0
  173. package/dist/plugins/server/server/utils/validation.js +38 -0
  174. package/dist/plugins/server/server/utils/validation.js.map +1 -0
  175. package/dist/plugins/server/types.d.ts +334 -0
  176. package/dist/plugins/server/types.js +78 -0
  177. package/dist/plugins/server/types.js.map +1 -0
  178. package/dist/server/constants.js +29 -0
  179. package/dist/server/plugins/server/Cache.js +15 -0
  180. package/dist/server/server/api/api.js +20 -0
  181. package/dist/server/server/api/connectorApi.js +216 -0
  182. package/dist/server/server/api/publicationApi.js +108 -0
  183. package/dist/server/server/api/websiteApi.js +296 -0
  184. package/dist/server/server/cli.js +68 -0
  185. package/dist/server/server/config.js +120 -0
  186. package/dist/server/server/connectors/FsHosting.js +35 -0
  187. package/dist/server/server/connectors/FsStorage.js +225 -0
  188. package/dist/server/server/connectors/connectors.js +89 -0
  189. package/dist/server/server/events.js +14 -0
  190. package/dist/server/server/expose.js +27 -0
  191. package/dist/server/server/express.js +47 -0
  192. package/dist/server/server/index.js +90 -0
  193. package/dist/server/server/jobs.js +48 -0
  194. package/dist/server/server/utils/validation.js +15 -0
  195. package/dist/server/types.js +34 -0
  196. package/package.json +209 -0
  197. package/public/assets/container.png +0 -0
  198. package/public/assets/favicon.png +0 -0
  199. package/public/assets/logo-silex-small.png +0 -0
  200. package/public/robots.txt +2 -0
  201. package/public/silex.js +24 -0
  202. package/src/css/_mixins.scss +8 -0
  203. package/src/css/_variables.scss +33 -0
  204. package/src/css/dialog.scss +41 -0
  205. package/src/css/footer.scss +12 -0
  206. package/src/css/grapesjs-light.scss +180 -0
  207. package/src/css/grapesjs.scss +152 -0
  208. package/src/css/index.scss +32 -0
  209. package/src/css/light.scss +7 -0
  210. package/src/css/loader.scss +12 -0
  211. package/src/css/page-panel.scss +65 -0
  212. package/src/css/project-bar.scss +102 -0
  213. package/src/css/silex.scss +189 -0
  214. package/src/css/website-info.scss +14 -0
  215. package/src/html/debug.pug +8 -0
  216. package/src/html/index.pug +39 -0
  217. package/src/html/templates/includes.pug +1 -0
  218. package/src/html/templates/workspace.pug +2 -0
  219. package/src/ts/client/api.ts +144 -0
  220. package/src/ts/client/assetUrl.test.ts +125 -0
  221. package/src/ts/client/assetUrl.ts +235 -0
  222. package/src/ts/client/config.ts +105 -0
  223. package/src/ts/client/events.ts +34 -0
  224. package/src/ts/client/expose.ts +30 -0
  225. package/src/ts/client/grapesjs/LoginDialog.ts +204 -0
  226. package/src/ts/client/grapesjs/PublicationManager.ts +370 -0
  227. package/src/ts/client/grapesjs/PublicationUi.ts +342 -0
  228. package/src/ts/client/grapesjs/blocks.ts +37 -0
  229. package/src/ts/client/grapesjs/breadcrumbs.ts +79 -0
  230. package/src/ts/client/grapesjs/eleventy.ts +47 -0
  231. package/src/ts/client/grapesjs/footer.ts +27 -0
  232. package/src/ts/client/grapesjs/index.ts +335 -0
  233. package/src/ts/client/grapesjs/internal-links.test.ts +105 -0
  234. package/src/ts/client/grapesjs/internal-links.ts +225 -0
  235. package/src/ts/client/grapesjs/new-page-dialog.ts +90 -0
  236. package/src/ts/client/grapesjs/openImport.ts +81 -0
  237. package/src/ts/client/grapesjs/page-panel.ts +118 -0
  238. package/src/ts/client/grapesjs/project-bar.ts +107 -0
  239. package/src/ts/client/grapesjs/rich-text.ts +33 -0
  240. package/src/ts/client/grapesjs/semantic.ts +114 -0
  241. package/src/ts/client/grapesjs/settings.ts +208 -0
  242. package/src/ts/client/grapesjs/storage.ts +121 -0
  243. package/src/ts/client/grapesjs/symbolDialogs.ts +38 -0
  244. package/src/ts/client/index.ts +98 -0
  245. package/src/ts/client/publication-transformers.test.ts +199 -0
  246. package/src/ts/client/publication-transformers.ts +263 -0
  247. package/src/ts/client/utils.ts +28 -0
  248. package/src/ts/constants.ts +52 -0
  249. package/src/ts/externs.ts +18 -0
  250. package/src/ts/page.ts +31 -0
  251. package/src/ts/plugins/client/11ty.ts +104 -0
  252. package/src/ts/plugins/client/publicationRenderer.ts +174 -0
  253. package/src/ts/plugins/client/template.ts +192 -0
  254. package/src/ts/plugins/client/website-info.ts +84 -0
  255. package/src/ts/plugins/server/AuthPlugin.ts +38 -0
  256. package/src/ts/plugins/server/Cache.ts +28 -0
  257. package/src/ts/plugins/server/DownloadConnector.ts +148 -0
  258. package/src/ts/plugins/server/FtpConnector.test.ts +82 -0
  259. package/src/ts/plugins/server/FtpConnector.ts +609 -0
  260. package/src/ts/plugins/server/GitlabConnector.ts +644 -0
  261. package/src/ts/plugins/server/PublicationOptimizer.ts +14 -0
  262. package/src/ts/plugins/server/SslPlugin.ts +84 -0
  263. package/src/ts/plugins/server/StaticPlugin.ts +79 -0
  264. package/src/ts/server/api/api.ts +19 -0
  265. package/src/ts/server/api/connectorApi.ts +345 -0
  266. package/src/ts/server/api/publicationApi.ts +159 -0
  267. package/src/ts/server/api/websiteApi.ts +432 -0
  268. package/src/ts/server/cli.ts +65 -0
  269. package/src/ts/server/config.ts +189 -0
  270. package/src/ts/server/connectors/FsHosting.ts +60 -0
  271. package/src/ts/server/connectors/FsStorage.test.ts +51 -0
  272. package/src/ts/server/connectors/FsStorage.ts +261 -0
  273. package/src/ts/server/connectors/connectors.ts +203 -0
  274. package/src/ts/server/events.ts +37 -0
  275. package/src/ts/server/expose.ts +19 -0
  276. package/src/ts/server/express.ts +70 -0
  277. package/src/ts/server/index.ts +100 -0
  278. package/src/ts/server/jobs.test.ts +75 -0
  279. package/src/ts/server/jobs.ts +87 -0
  280. package/src/ts/server/utils/BackwardCompat.ts +552 -0
  281. package/src/ts/server/utils/validation.ts +34 -0
  282. package/src/ts/types.ts +339 -0
@@ -0,0 +1,84 @@
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 { readFileSync } from 'fs'
20
+ import { createServer } from 'https'
21
+ import forceSSL from 'express-force-ssl'
22
+ import { ServerEvent } from '../../server/events'
23
+
24
+ // interface SslOptions {
25
+ // forceHttps?: boolean
26
+ // trustXFPHeader?: boolean
27
+ // privateKey?: string
28
+ // certificate?: string
29
+ // sslPort?: string
30
+ // }
31
+
32
+ export default async function(config, opts = {}) {
33
+ // Options with defaults
34
+ const options = {
35
+ forceHttps: process.env.SILEX_FORCE_HTTPS === 'true',
36
+ trustXFPHeader: process.env.SILEX_FORCE_HTTPS_TRUST_XFP_HEADER === 'true',
37
+ privateKey: process.env.SILEX_SSL_PRIVATE_KEY,
38
+ certificate: process.env.SILEX_SSL_CERTIFICATE,
39
+ sslPort: process.env.SSL_PORT || '443',
40
+ ...opts,
41
+ }
42
+
43
+ // Add routes on silex startup
44
+ config.on(ServerEvent.STARTUP_START, ({app}) => {
45
+ const router = express.Router()
46
+
47
+ // SSL
48
+ // force ssl if the env var SILEX_FORCE_HTTPS is set
49
+ if (options.forceHttps) {
50
+ console.info('> [SslPlugin] Force SSL option is enabled')
51
+ app.set('forceSSLOptions', {
52
+ trustXFPHeader: !!options.trustXFPHeader,
53
+ })
54
+ router.use(forceSSL)
55
+ } else {
56
+ console.info('> [SslPlugin] Force SSL option is disabled, env var SILEX_FORCE_HTTPS not set')
57
+ }
58
+
59
+ // SSL certificate
60
+ if (options.privateKey && options.certificate) {
61
+ console.info('> [SslPlugin] SSL certificate is enabled, found certificate:', options.certificate)
62
+ try {
63
+ const privateKey = readFileSync(options.privateKey).toString()
64
+ const certificate = readFileSync(options.certificate).toString()
65
+
66
+ const sslServerOptions = {
67
+ key: privateKey,
68
+ cert: certificate,
69
+ requestCert: true,
70
+ rejectUnauthorized: false,
71
+ }
72
+
73
+ createServer(sslServerOptions, this).listen(options.sslPort, () => {
74
+ console.info('> [SslPlugin] Listening on port ', options.sslPort)
75
+ })
76
+ } catch (e) {
77
+ console.error('> [SslPlugin] Load certificate failed.', e)
78
+ }
79
+ } else {
80
+ console.info('> [SslPlugin] Certificate disabled, env vars SILEX_SSL_CERTIFICATE and SILEX_SSL_PRIVATE_KEY not set')
81
+ }
82
+ app.use(router)
83
+ })
84
+ }
@@ -0,0 +1,79 @@
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 nodeModules from 'node_modules-path'
20
+ import serveStatic from 'serve-static'
21
+ import { withCache } from './Cache'
22
+ import { join } from 'path'
23
+ import { ServerEvent } from '../../server/events'
24
+
25
+ type StaticOptions = {
26
+ routes: {
27
+ path: string
28
+ route: string
29
+ }[]
30
+ }
31
+
32
+ export default async function(config, opts = {}) {
33
+ // Options with defaults
34
+ const options: StaticOptions = {
35
+ routes: [
36
+ {
37
+ route: '/',
38
+ path: join(__dirname, '../../../../..', 'public'),
39
+ }, {
40
+ route: '/css/',
41
+ path: nodeModules('@fortawesome/fontawesome-free') + '/@fortawesome/fontawesome-free/css/',
42
+ }, {
43
+ route: '/webfonts/',
44
+ path: nodeModules('@fortawesome/fontawesome-free') + '/@fortawesome/fontawesome-free/webfonts/',
45
+ }, {
46
+ route: '/css/files/',
47
+ path: nodeModules('@fontsource/ubuntu') + '/@fontsource/ubuntu/files/',
48
+ //}, {
49
+ // route: '/node_modules/',
50
+ // path: nodeModules(),
51
+ }, {
52
+ route: '/',
53
+ path: join(__dirname, '../../../../..', 'dist', 'client'),
54
+ }, {
55
+ route: '/',
56
+ path: join(__dirname, '../../../../..', 'dist', 'plugins', 'client'),
57
+ },
58
+ ]
59
+ // add project route for source maps
60
+ .concat(config.debug ? [{
61
+ route: '/',
62
+ path: './',
63
+ }] : []),
64
+ ...opts,
65
+ }
66
+ console.info('> [StaticPlugin] Serving static files')
67
+
68
+ config.on(ServerEvent.STARTUP_START, ({app}) => {
69
+ const router = express.Router()
70
+ options.routes
71
+ .forEach(folder => {
72
+ if (!folder.route) throw new Error('The config for static module has no `route` attribute')
73
+ if (!folder.path) throw new Error('The config for static module has no `path` attribute')
74
+ console.info(`> [StaticPlugin] Serving static files from ${folder.path} on ${folder.route}`)
75
+ router.use(folder.route, serveStatic(folder.path))
76
+ })
77
+ app.use(withCache, router)
78
+ })
79
+ }
@@ -0,0 +1,19 @@
1
+ import { Router } from 'express'
2
+ import { ServerConfig } from '../config'
3
+ import { noCache } from '../../plugins/server/Cache'
4
+
5
+ import connectorApi from './connectorApi'
6
+ import websiteApi from './websiteApi'
7
+ import publicationApi from './publicationApi'
8
+ import { API_CONNECTOR_PATH, API_PUBLICATION_PATH, API_WEBSITE_PATH } from '../../constants'
9
+
10
+ export default function(config: ServerConfig): Router {
11
+ const router = Router()
12
+ router.use(noCache)
13
+
14
+ router.use(API_CONNECTOR_PATH, connectorApi(config))
15
+ router.use(API_WEBSITE_PATH, websiteApi(config))
16
+ router.use(API_PUBLICATION_PATH, publicationApi(config))
17
+
18
+ return router
19
+ }
@@ -0,0 +1,345 @@
1
+ import { Request, Response, Router } from 'express'
2
+ import { API_CONNECTOR_LIST, API_CONNECTOR_LOGIN_CALLBACK, API_CONNECTOR_USER, API_CONNECTOR_LOGOUT, API_CONNECTOR_LOGIN, API_CONNECTOR_SETTINGS, API_PATH, API_CONNECTOR_PATH } from '../../constants'
3
+ import { ServerConfig } from '../config'
4
+ import { requiredParam } from '../utils/validation'
5
+ import { Connector, getConnector, toConnectorData, toConnectorEnum } from '../connectors/connectors'
6
+ import { ApiConnectorListQuery, ApiConnectorListResponse, ApiConnectorLoggedInPostMessage, ApiConnectorLoginQuery, ApiConnectorUserQuery, ApiConnectorUserResponse, ApiConnectorLogoutQuery, ConnectorId, ApiConnectorLoginCbkQuery, ApiConnectorLoginCbkBody, ConnectorOptions, ApiResponseError } from '../../types'
7
+
8
+ /**
9
+ * @fileoverview The connector API adds routes to handle the connectors and the methods they implement, this includes authentication and user data.
10
+ *
11
+ * About authentication
12
+ *
13
+ * There are 2 types of connectors, for storage of website data and for publication to a hosting service. The authentication process is the same for both types of connectors
14
+ *
15
+ * Here is a typical authentication flow, which can be for a storage or a publication connector:
16
+ *
17
+ * 1. Client app calls the `user` route which returns a 401 as there is no auth in the session
18
+ * 1. Client app lists all connectors (either for publication or storage) and display a button for each connector
19
+ * 1. The user clicks a button, it opens a page with either the connector's `getOAuthUrl` for OAuth connectors (A) or the login route for basic auth connectors (B). This can be done in a popup (C) or as a redirect in the client app window (D)
20
+ * 1. OAuth (A): the page displays the OAuth page which ends up going back to the callback page
21
+ * 1. Basic auth (B): the page displays the login form `connector.getLoginForm()` which ends up going back to the callback page
22
+ * 1. The callback page stores the auth data in the session (for storage), or in the website publication settings (for publication)
23
+ * 1. Popup (C): The callback page will post a message to the parent window which closes the popup and goes on with the flow
24
+ * 1. Redirect (D): The callback page will redirect to the client app
25
+ * 1. The client side will call the `user` route again, which will return the user data
26
+ *
27
+ */
28
+ /**
29
+ * Add routes to the express app
30
+ */
31
+ export default function(config: ServerConfig) {
32
+ // Create the router
33
+ const router = Router()
34
+
35
+ // Connector routes
36
+ router.get(API_CONNECTOR_USER, routeUser)
37
+ router.get(API_CONNECTOR_LIST, routeListConnectors)
38
+ router.get(API_CONNECTOR_LOGIN, routeLogin)
39
+ // router.get(API_CONNECTOR_SETTINGS, routeSettings)
40
+ // router.post(API_CONNECTOR_SETTINGS, routeSettingsPost)
41
+ router.post(API_CONNECTOR_LOGIN_CALLBACK, routeLoginSuccess)
42
+ router.get(API_CONNECTOR_LOGIN_CALLBACK, routeLoginSuccess)
43
+ router.post(API_CONNECTOR_LOGOUT, routeLogout)
44
+
45
+ return router
46
+ }
47
+
48
+ /**
49
+ * Method to validate HTTP status codes
50
+ * Returns a suitable error code if it is not valid
51
+ */
52
+ function validateStatus(status: number, _default = 500): number {
53
+ if(!status) {
54
+ console.warn(`Status code is undefined, returning default ${_default}`)
55
+ return _default
56
+ }
57
+ // Make sure it is a string
58
+ status = parseInt(status.toString())
59
+ // Check the status code
60
+ if (status >= 100 && status < 600) return status
61
+ // Invalid status code
62
+ console.warn(`Invalid status code ${status}, returning default ${_default}}`)
63
+ return _default
64
+ }
65
+
66
+ /**
67
+ * Express route to check if the user is logged in
68
+ * Returns user data and connector data
69
+ */
70
+ async function routeUser(req: Request, res: Response) {
71
+ try {
72
+ const config = requiredParam(req.app.get('config') as ServerConfig, 'Config object on express js APP')
73
+ const session = requiredParam(req['session'], 'Session object')
74
+ const query = req.query as ApiConnectorUserQuery
75
+ const type = toConnectorEnum(requiredParam(query.type, 'Connector type'))
76
+ const connector = await getConnector<Connector>(config, session, type, query.connectorId)
77
+ if (!connector) {
78
+ res
79
+ .status(500)
80
+ .json({
81
+ error: true,
82
+ message: `Connector not found: ${type} ${query.connectorId}`,
83
+ } as ApiResponseError)
84
+ return
85
+ }
86
+ if(!await connector.isLoggedIn(session)) {
87
+ res
88
+ .status(401)
89
+ .json({
90
+ error: true,
91
+ message: 'Not logged in',
92
+ } as ApiResponseError)
93
+ return
94
+ }
95
+ // User logged in, return user data
96
+ const user = await connector.getUser(session)
97
+ res.json(user as ApiConnectorUserResponse)
98
+ } catch (error) {
99
+ console.error('Error in the user request', error, error.code)
100
+ res.status(validateStatus(error?.code ?? error?.httpStatusCode, 500)).json({
101
+ error: true,
102
+ message: error.message,
103
+ } as ApiResponseError)
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Express route to list the connectors
109
+ */
110
+ async function routeListConnectors(req: Request, res: Response) {
111
+ try {
112
+ const config = requiredParam(req.app.get('config') as ServerConfig, 'Config object on express js APP')
113
+ const session = requiredParam(req['session'], 'Session object')
114
+ const query = req.query as ApiConnectorListQuery
115
+ const type = toConnectorEnum(requiredParam(query.type, 'Connector type'))
116
+ const connectors = config.getConnectors(type)
117
+ try {
118
+ const list = await Promise.all(connectors.map(async connector => toConnectorData(session, connector)))
119
+ res.json(list as ApiConnectorListResponse)
120
+ } catch (error) {
121
+ console.error('Error while listing connectors', error)
122
+ res.status(validateStatus(error?.code ?? error?.httpStatusCode, 500)).json({
123
+ error: true,
124
+ message: 'Error while listing connectors: ' + error.message,
125
+ } as ApiResponseError)
126
+ }
127
+ } catch (error) {
128
+ console.error('Error in the list connectors request', error)
129
+ res.status(validateStatus(error?.code ?? error?.httpStatusCode, 400)).json({
130
+ error: true,
131
+ message: 'Error in the list connectors request: ' + error.message,
132
+ } as ApiResponseError)
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Route login
138
+ * Display the connector's login form if the connector is basic auth
139
+ * or redirect to oauth url if the connector is oauth
140
+ * or redirect to success page if the user is logged in
141
+ */
142
+ async function routeLogin(req: Request, res: Response) {
143
+ try {
144
+ const query = req.query as ApiConnectorLoginQuery
145
+ const connectorId = requiredParam(query.connectorId, 'Connector id')
146
+ const config = requiredParam(req.app.get('config') as ServerConfig, 'Config object on express js APP')
147
+ const type = toConnectorEnum(requiredParam(query.type, 'Connector type'))
148
+ const connector = await getConnector<Connector>(config, req['session'], type, connectorId)
149
+ const session = requiredParam(req['session'], 'Session object')
150
+ if (!connector) throw new Error(`Connector not found ${connectorId} ${type}`)
151
+ // Check if the user is already logged in
152
+ if (await connector.isLoggedIn(session)) {
153
+ res.redirect(`${config.url}${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN_CALLBACK}?connectorId=${connectorId}&type=${type}`)
154
+ return
155
+ }
156
+ const oauthUrl = await connector.getOAuthUrl(session)
157
+ if (oauthUrl) {
158
+ // Starts the OAuth flow
159
+ res.redirect(oauthUrl)
160
+ } else {
161
+ // Display the login form
162
+ const redirectTo = await connector.getSettingsForm(session, '') ?
163
+ `${config.url}${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_SETTINGS}?connectorId=${connectorId}&type=${type}` :
164
+ `${config.url}${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN_CALLBACK}?connectorId=${connectorId}&type=${type}`
165
+ res.send(await connector.getLoginForm(session, redirectTo))
166
+ }
167
+ } catch (error) {
168
+ console.error('Error in the login request', error)
169
+ res.status(validateStatus(error?.code ?? error?.httpStatusCode, 400)).json({
170
+ error: true,
171
+ message: 'Error in the login request: ' + error.message,
172
+ } as ApiResponseError)
173
+ }
174
+ }
175
+
176
+ // /**
177
+ // * Route connector settings
178
+ // * Display the connector's settings form or redirect to login page if the user is not logged in
179
+ // */
180
+ // function routeSettings(req: Request, res: Response) {
181
+ // try {
182
+ // const query = req.query as ApiConnectorSettingsQuery
183
+ //
184
+ // const connectorId = requiredParam(query.connectorId, 'Connector id')
185
+ // const config = requiredParam(req.app.get('config') as ServerConfig, 'Config object on express js APP')
186
+ // const type = toConnectorEnum(requiredParam(query.type, 'Connector type'))
187
+ // const connector = await getConnector<Connector>(config, req['session'], type, connectorId)
188
+ // const session = requiredParam(req['session'], 'Session object')
189
+ // if (!connector) throw new Error(`Connector not found ${connectorId} ${type}`)
190
+ // // Check if the user is already logged in
191
+ // if (!await connector.isLoggedIn(session)) {
192
+ // res.redirect(`${config.url}${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN_CALLBACK}?connectorId=${connectorId}&type=${type}&error=Not logged in`)
193
+ // return
194
+ // }
195
+ // // Display the login form
196
+ // res.send(await connector.getSettingsForm(session, ``) as ApiConnectorSettingsResponse)
197
+ // } catch (error) {
198
+ // console.error('Error in the login request', error)
199
+ // res.status(error?.code ?? error?.httpStatusCode ?? 400).json({
200
+ // error: true,
201
+ // message: 'Error in the login request: ' + error.message,
202
+ // } as ApiError)
203
+ // }
204
+ // }
205
+ //
206
+ // /**
207
+ // * Route connector settings post
208
+ // * Save the connector's settings in the website meta file
209
+ // */
210
+ // function routeSettingsPost(req: Request, res: Response) {
211
+ // try {
212
+ // const query = req.query as ApiConnectorSettingsPostQuery
213
+ //
214
+ // const connectorId = requiredParam(query.connectorId, 'Connector id')
215
+ // const config = requiredParam(req.app.get('config') as ServerConfig, 'Config object on express js APP')
216
+ // const type = toConnectorEnum(requiredParam(query.type, 'Connector type'))
217
+ // const connector = await getConnector<Connector>(config, req['session'], type, connectorId)
218
+ // const session = requiredParam(req['session'], 'Session object')
219
+ // const body = req.body as ApiConnectorSettingsPostBody
220
+ // if (!connector) throw new Error(`Connector not found ${connectorId} ${type}`)
221
+ // // Check if the user is already logged in
222
+ // if (!await connector.isLoggedIn(session)) {
223
+ // res.redirect(`${config.url}${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN_CALLBACK}?connectorId=${connectorId}&type=${type}&error=Not logged in`)
224
+ // return
225
+ // }
226
+ // // Save the settings
227
+ // await connector.setWebsiteMeta(session, websiteId, body)
228
+ // } catch (error) {
229
+ // console.error('Error in the login request', error)
230
+ // res.status(error?.code ?? error?.httpStatusCode ?? 400).json({
231
+ // error: true,
232
+ // message: 'Error in the login request: ' + error.message,
233
+ // } as ApiResponseError)
234
+ // }
235
+ // }
236
+
237
+ /**
238
+ * Express route to serve as redirect after a successful login
239
+ * The returned HTML will postMessage data and close the popup window
240
+ */
241
+ async function routeLoginSuccess(req: Request, res: Response) {
242
+ try {
243
+ const query = req.query as ApiConnectorLoginCbkQuery
244
+ if(query.error) throw new Error(query.error)
245
+
246
+ const body = req.body as ApiConnectorLoginCbkBody
247
+ const session = requiredParam(req['session'], 'Session object')
248
+
249
+ const connectorId = requiredParam(query.connectorId, 'Connector id')
250
+ const config = requiredParam(req.app.get('config') as ServerConfig, 'Config object on express js APP')
251
+ const type = toConnectorEnum(requiredParam(query.type, 'Connector type'))
252
+ const connector = await getConnector<Connector>(config, req['session'], type, connectorId)
253
+ if (!connector) throw new Error('Connector not found ' + connectorId)
254
+ // Check if the user is already logged in
255
+ if (await connector.isLoggedIn(session)) {
256
+ console.info('User already logged in for connector ' + connectorId)
257
+ } else {
258
+ // Store the auth info in the session
259
+ // This is useful for storage only
260
+ await connector.setToken(session, {
261
+ ...query,
262
+ ...body,
263
+ })
264
+ }
265
+ // End the auth flow
266
+ res.send(getEndAuthHtml('Logged in', false, connectorId, connector.getOptions(body)))
267
+ } catch (error) {
268
+ res
269
+ .status(validateStatus(error?.code ?? error?.httpStatusCode, 500))
270
+ .send(getEndAuthHtml(error.message, true))
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Express route to logout from a connector
276
+ */
277
+ async function routeLogout(req: Request, res: Response) {
278
+ try {
279
+ const query = req.query as ApiConnectorLogoutQuery
280
+ const session = requiredParam(req['session'], 'Session object')
281
+ // Get the connector
282
+ const config = requiredParam(req.app.get('config') as ServerConfig, 'Config object on express js APP')
283
+ const type = toConnectorEnum(requiredParam(query.type, 'Connector type'))
284
+ const connectorId = query.connectorId
285
+ const connector = await getConnector<Connector>(config, session, type, connectorId)
286
+ if (!connector) throw new Error(`Connector not found ${connectorId} ${type}`)
287
+ try {
288
+ // Logout
289
+ await connector.logout(session)
290
+ // Return success
291
+ res.json({
292
+ error: false,
293
+ message: 'OK',
294
+ } as ApiResponseError)
295
+ } catch (error) {
296
+ console.error('Error while logging out', error)
297
+ res.status(validateStatus(error?.code ?? error?.httpStatusCode, 500)).json({
298
+ error: true,
299
+ message: 'Error while logging out: ' + error.message,
300
+ } as ApiResponseError)
301
+ return
302
+ }
303
+ } catch (error) {
304
+ console.error('Error in the logout request', error)
305
+ res.status(validateStatus(error?.code ?? error?.httpStatusCode, 400)).json({
306
+ error: true,
307
+ message: 'Error in the logout request: ' + error.message,
308
+ } as ApiResponseError)
309
+ return
310
+ }
311
+ }
312
+
313
+ /**
314
+ * Utility function to send an HTML page to the browser
315
+ * is page will send a postMessage to the parent window and close itself
316
+ */
317
+ function getEndAuthHtml(message: string, error: boolean, connectorId?: ConnectorId, options?: ConnectorOptions): string {
318
+ // Data for postMessage
319
+ const data = {
320
+ type: 'login', // For postMessage
321
+ error,
322
+ message,
323
+ connectorId,
324
+ options,
325
+ } as ApiConnectorLoggedInPostMessage
326
+ // Send the HTML
327
+ return `
328
+ <html>
329
+ <head>
330
+ <script>
331
+ if(window.opener) {
332
+ window.opener.postMessage(${JSON.stringify(data)}, '*')
333
+ window.close()
334
+ } else {
335
+ window.location.href = '/'
336
+ }
337
+ </script>
338
+ </head>
339
+ <body>
340
+ <p>${message}</p>
341
+ <p>Close this window or <a href="/">go back to /</a></p>
342
+ </body>
343
+ </html>
344
+ `
345
+ }
@@ -0,0 +1,159 @@
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 { Router } from 'express'
19
+ import { API_PUBLICATION_PUBLISH, API_PUBLICATION_STATUS, API_WEBSITE_ASSET_READ } from '../../constants'
20
+ import { ApiPublicationPublishBody, ApiPublicationPublishQuery, ApiPublicationPublishResponse, ApiPublicationStatusQuery, ApiPublicationStatusResponse, JobId, PublicationData, ConnectorType, ConnectorId, WebsiteId, ClientSideFile, ClientSideFileWithContent, ClientSideFileWithSrc} from '../../types'
21
+ import { ConnectorFile, HostingConnector, StorageConnector, getConnector } from '../connectors/connectors'
22
+ import { ServerConfig } from '../config'
23
+ import { requiredParam } from '../utils/validation'
24
+ import { PublishEndEventType, PublishStartEventType, ServerEvent } from '../events'
25
+ import { getJob, jobManager } from '../jobs'
26
+
27
+ /**
28
+ * @fileoverview Publication plugin for Silex
29
+ * Adds a publication API to Silex server
30
+ */
31
+
32
+ /**
33
+ * Error thrown by the publication API
34
+ * @param message error message
35
+ * @param code http status code
36
+ */
37
+ export class PublicationError extends Error {
38
+ constructor(message: string, public code: number) {
39
+ super(message)
40
+ }
41
+ }
42
+
43
+ const PROJECT_ROOT = require.main ? require.main.path : process.cwd()
44
+
45
+ /**
46
+ * Get the desired connector
47
+ * Can be the default connector or a specific one
48
+ */
49
+ export async function getHostingConnector(session: any, config: ServerConfig, connectorId?: string): Promise<HostingConnector> {
50
+ const hostingConnector = await getConnector(config, session, ConnectorType.HOSTING, connectorId) // ?? config.getHostingConnectors()[0]
51
+
52
+ if (!hostingConnector) {
53
+ throw new PublicationError('No hosting connector found', 500)
54
+ }
55
+
56
+ if (!await hostingConnector.isLoggedIn(session)) {
57
+ throw new PublicationError('Not logged in', 401)
58
+ }
59
+
60
+ return hostingConnector as HostingConnector
61
+ }
62
+
63
+ export async function getStorageConnector(session: any, config: ServerConfig, connectorId?: string): Promise<StorageConnector> {
64
+ const storageConnector = await getConnector(config, session, ConnectorType.STORAGE, connectorId) // ?? config.getStorageConnectors()[0]
65
+
66
+ if (!storageConnector) {
67
+ throw new PublicationError('No storage connector found', 500)
68
+ }
69
+
70
+ if (!await storageConnector.isLoggedIn(session)) {
71
+ throw new PublicationError('Not logged in to the storage connector', 401)
72
+ }
73
+
74
+ return storageConnector as StorageConnector
75
+ }
76
+
77
+ export default function (config: ServerConfig): Router {
78
+ // Create a new router
79
+ const router = Router()
80
+
81
+ // Get publication status
82
+ router.get(API_PUBLICATION_STATUS, async function (req, res) {
83
+ const query: ApiPublicationStatusQuery = req.query as any
84
+ const jobId: JobId = query.jobId as string
85
+ const job = jobId && getJob(jobId)
86
+ if (!job) {
87
+ console.error(`Error: job not found with id ${jobId}`)
88
+ res.status(404).json({
89
+ message: 'Error: job not found.',
90
+ })
91
+ return
92
+ }
93
+ res.json({
94
+ ...job,
95
+ _timeout: undefined,
96
+ } as ApiPublicationStatusResponse)
97
+ })
98
+
99
+ // Publish website
100
+ router.post(API_PUBLICATION_PUBLISH, async function (req, res) {
101
+ try {
102
+ const query: ApiPublicationPublishQuery = req.query as any
103
+ const body: ApiPublicationPublishBody = req.body
104
+ const websiteId = requiredParam<WebsiteId>(query.websiteId as string, 'id in query')
105
+ const storageId = requiredParam<ConnectorId>(query.storageId as string, 'storageId in query')
106
+ const hostingId = requiredParam<ConnectorId>(query.hostingId as string, 'hostingId in query')
107
+ const session = requiredParam(req['session'], 'session on express request')
108
+
109
+ // Check params
110
+ const data = body as PublicationData
111
+ const { files } = data
112
+ if(!files) {
113
+ throw new PublicationError('Missing files in body', 400)
114
+ }
115
+
116
+ // Hook for plugins
117
+ config.emit(ServerEvent.PUBLISH_START, data as PublishStartEventType)
118
+
119
+ // Get hosting connector and make sure the user is logged in
120
+ const hostingConnector = await getHostingConnector(session, config, hostingId)
121
+
122
+ // Get storage connector which holds the assets
123
+ const storage = await getStorageConnector(session, config, storageId)
124
+
125
+ // Load the content if necessary
126
+ const filesList: ConnectorFile[] = await Promise.all(files.map(async (file: ClientSideFile) => {
127
+ const prefix = API_WEBSITE_ASSET_READ
128
+ const fileWithContent = file as ClientSideFileWithContent
129
+ const fileWithSrc = file as ClientSideFileWithSrc
130
+ if(!fileWithContent.content && !fileWithSrc.src) throw new PublicationError('Missing content or src in file', 400)
131
+ const src = fileWithSrc.src?.startsWith(prefix) ? fileWithSrc.src.substring(prefix.length) : fileWithSrc.src
132
+ return {
133
+ path: file.path,
134
+ content: fileWithContent.content ??
135
+ await storage.readAsset(session, websiteId, src),
136
+ }
137
+ }))
138
+
139
+ res.json({
140
+ url: await hostingConnector.getUrl(session, websiteId),
141
+ job: await hostingConnector.publish(session, websiteId, filesList, jobManager),
142
+ } as ApiPublicationPublishResponse)
143
+ } catch (err) {
144
+ console.error('Error publishing the website', err)
145
+ res
146
+ .status(typeof err.code === 'number' ? err.code : 500)
147
+ .json({
148
+ message: `Error publishing the website. ${err.message}`
149
+ })
150
+ // Hook for plugins
151
+ config.emit(ServerEvent.PUBLISH_END, err as PublishEndEventType)
152
+ return
153
+ }
154
+ // Hook for plugins
155
+ config.emit(ServerEvent.PUBLISH_END)
156
+ })
157
+
158
+ return router
159
+ }