@silexlabs/silex 3.0.1-alpha.0 → 3.0.2

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 (323) hide show
  1. package/.env.default +20 -0
  2. package/.silex.js +67 -8
  3. package/LICENSE +661 -203
  4. package/README.md +37 -35
  5. package/dist/client/client/api.d.ts +90 -0
  6. package/dist/client/client/assetUrl.d.ts +54 -0
  7. package/dist/client/client/config.d.ts +102 -0
  8. package/dist/client/client/events.d.ts +23 -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 +73 -0
  12. package/dist/client/client/grapesjs/PublicationUi.d.ts +59 -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/css-props.d.ts +6 -0
  16. package/dist/client/client/grapesjs/footer.d.ts +2 -0
  17. package/dist/client/client/grapesjs/img.d.ts +6 -0
  18. package/dist/client/client/grapesjs/index.d.ts +9 -0
  19. package/dist/client/client/grapesjs/internal-links.d.ts +1 -0
  20. package/dist/client/client/grapesjs/keymaps.d.ts +123 -0
  21. package/dist/client/client/grapesjs/new-page-dialog.d.ts +2 -0
  22. package/dist/client/client/grapesjs/openImport.d.ts +9 -0
  23. package/dist/client/client/grapesjs/page-panel.d.ts +9 -0
  24. package/dist/client/client/grapesjs/project-bar.d.ts +21 -0
  25. package/dist/client/client/grapesjs/resize-panel.d.ts +1 -0
  26. package/dist/client/client/grapesjs/rich-text.d.ts +4 -0
  27. package/dist/client/client/grapesjs/semantic.d.ts +1 -0
  28. package/dist/client/client/grapesjs/settings-sections.d.ts +16 -0
  29. package/dist/client/client/grapesjs/settings.d.ts +4 -0
  30. package/dist/client/client/grapesjs/storage.d.ts +2 -0
  31. package/dist/client/client/grapesjs/symbolDialogs.d.ts +3 -0
  32. package/dist/client/client/grapesjs/view-buttons.d.ts +1 -0
  33. package/dist/client/client/index.d.ts +10 -0
  34. package/dist/client/client/publication-transformers.d.ts +42 -0
  35. package/dist/client/client/publish-custom-code-block.d.ts +4 -0
  36. package/dist/client/client/publish-fonts.d.ts +4 -0
  37. package/dist/client/client/utils.d.ts +22 -0
  38. package/dist/client/constants.d.ts +32 -0
  39. package/dist/client/css/admin.css +1114 -328
  40. package/dist/client/css/admin.css.map +1 -0
  41. package/dist/client/grapesjs-filter-styles/dist/index.d.ts +4 -0
  42. package/dist/client/grapesjs-fonts/dist/index.d.ts +4 -0
  43. package/dist/client/grapesjs-fonts/src/commands.d.ts +3 -0
  44. package/dist/client/grapesjs-fonts/src/fonts.d.ts +4 -0
  45. package/dist/client/grapesjs-storage-rate-limit/dist/index.d.ts +4 -0
  46. package/dist/client/grapesjs-ui-suggest-classes/dist/index.d.ts +4 -0
  47. package/dist/client/index.html +3 -2
  48. package/dist/client/js/main.js +356 -131
  49. package/dist/client/js/main.js.LICENSE.txt +15 -1
  50. package/dist/client/page.d.ts +2 -0
  51. package/dist/client/silex-lib/src/ts/client/api.d.ts +90 -0
  52. package/dist/client/silex-lib/src/ts/client/assetUrl.d.ts +54 -0
  53. package/dist/client/silex-lib/src/ts/client/config.d.ts +102 -0
  54. package/dist/client/silex-lib/src/ts/client/events.d.ts +23 -0
  55. package/dist/client/silex-lib/src/ts/client/expose.d.ts +8 -0
  56. package/dist/client/silex-lib/src/ts/client/grapesjs/LoginDialog.d.ts +15 -0
  57. package/dist/client/silex-lib/src/ts/client/grapesjs/PublicationManager.d.ts +73 -0
  58. package/dist/client/silex-lib/src/ts/client/grapesjs/PublicationUi.d.ts +59 -0
  59. package/dist/client/silex-lib/src/ts/client/grapesjs/blocks.d.ts +1 -0
  60. package/dist/client/silex-lib/src/ts/client/grapesjs/breadcrumbs.d.ts +1 -0
  61. package/dist/client/silex-lib/src/ts/client/grapesjs/css-props.d.ts +6 -0
  62. package/dist/client/silex-lib/src/ts/client/grapesjs/footer.d.ts +2 -0
  63. package/dist/client/silex-lib/src/ts/client/grapesjs/img.d.ts +6 -0
  64. package/dist/client/silex-lib/src/ts/client/grapesjs/index.d.ts +9 -0
  65. package/dist/client/silex-lib/src/ts/client/grapesjs/internal-links.d.ts +1 -0
  66. package/dist/client/silex-lib/src/ts/client/grapesjs/keymaps.d.ts +123 -0
  67. package/dist/client/silex-lib/src/ts/client/grapesjs/new-page-dialog.d.ts +2 -0
  68. package/dist/client/silex-lib/src/ts/client/grapesjs/openImport.d.ts +9 -0
  69. package/dist/client/silex-lib/src/ts/client/grapesjs/page-panel.d.ts +9 -0
  70. package/dist/client/silex-lib/src/ts/client/grapesjs/project-bar.d.ts +21 -0
  71. package/dist/client/silex-lib/src/ts/client/grapesjs/resize-panel.d.ts +1 -0
  72. package/dist/client/silex-lib/src/ts/client/grapesjs/rich-text.d.ts +4 -0
  73. package/dist/client/silex-lib/src/ts/client/grapesjs/semantic.d.ts +1 -0
  74. package/dist/client/silex-lib/src/ts/client/grapesjs/settings-sections.d.ts +16 -0
  75. package/dist/client/silex-lib/src/ts/client/grapesjs/settings.d.ts +4 -0
  76. package/dist/client/silex-lib/src/ts/client/grapesjs/storage.d.ts +2 -0
  77. package/dist/client/silex-lib/src/ts/client/grapesjs/symbolDialogs.d.ts +3 -0
  78. package/dist/client/silex-lib/src/ts/client/grapesjs/view-buttons.d.ts +1 -0
  79. package/dist/client/silex-lib/src/ts/client/index.d.ts +10 -0
  80. package/dist/client/silex-lib/src/ts/client/publication-transformers.d.ts +42 -0
  81. package/dist/client/silex-lib/src/ts/client/publish-custom-code-block.d.ts +4 -0
  82. package/dist/client/silex-lib/src/ts/client/publish-fonts.d.ts +4 -0
  83. package/dist/client/silex-lib/src/ts/client/utils.d.ts +22 -0
  84. package/dist/client/silex-lib/src/ts/constants.d.ts +32 -0
  85. package/dist/client/silex-lib/src/ts/page.d.ts +2 -0
  86. package/dist/client/silex-lib/src/ts/types.d.ts +339 -0
  87. package/dist/client/types.d.ts +339 -0
  88. package/dist/plugins/client/client/events.d.ts +23 -0
  89. package/dist/plugins/client/client/events.js +43 -0
  90. package/dist/plugins/client/client/events.js.map +1 -0
  91. package/dist/plugins/client/plugins/client/template.d.ts +2 -0
  92. package/dist/plugins/client/plugins/client/template.js +234 -0
  93. package/dist/plugins/client/plugins/client/template.js.map +1 -0
  94. package/dist/plugins/client/plugins/client/website-info.d.ts +1 -0
  95. package/dist/plugins/client/plugins/client/website-info.js +72 -0
  96. package/dist/plugins/client/plugins/client/website-info.js.map +1 -0
  97. package/dist/plugins/client/types.d.ts +339 -0
  98. package/dist/plugins/client/types.js +80 -0
  99. package/dist/plugins/client/types.js.map +1 -0
  100. package/dist/plugins/server/constants.d.ts +32 -0
  101. package/dist/plugins/server/constants.js +76 -0
  102. package/dist/plugins/server/constants.js.map +1 -0
  103. package/dist/plugins/server/plugins/server/Cache.d.ts +2 -0
  104. package/dist/plugins/server/plugins/server/Cache.js +36 -0
  105. package/dist/plugins/server/plugins/server/Cache.js.map +1 -0
  106. package/dist/plugins/server/plugins/server/DownloadConnector.d.ts +29 -0
  107. package/dist/plugins/server/plugins/server/DownloadConnector.js +127 -0
  108. package/dist/plugins/server/plugins/server/DownloadConnector.js.map +1 -0
  109. package/dist/plugins/server/plugins/server/FtpConnector.d.ts +86 -0
  110. package/dist/plugins/server/plugins/server/FtpConnector.js +536 -0
  111. package/dist/plugins/server/plugins/server/FtpConnector.js.map +1 -0
  112. package/dist/plugins/server/plugins/server/GitlabConnector.d.ts +135 -0
  113. package/dist/plugins/server/plugins/server/GitlabConnector.js +649 -0
  114. package/dist/plugins/server/plugins/server/GitlabConnector.js.map +1 -0
  115. package/dist/plugins/server/plugins/server/GitlabHostingConnector.d.ts +16 -0
  116. package/dist/plugins/server/plugins/server/GitlabHostingConnector.js +209 -0
  117. package/dist/plugins/server/plugins/server/GitlabHostingConnector.js.map +1 -0
  118. package/dist/plugins/server/plugins/server/PublicationOptimizer.d.ts +0 -0
  119. package/dist/plugins/server/plugins/server/PublicationOptimizer.js +14 -0
  120. package/dist/plugins/server/plugins/server/PublicationOptimizer.js.map +1 -0
  121. package/dist/plugins/server/plugins/server/SslPlugin.d.ts +1 -0
  122. package/dist/plugins/server/plugins/server/SslPlugin.js +86 -0
  123. package/dist/plugins/server/plugins/server/SslPlugin.js.map +1 -0
  124. package/dist/plugins/server/plugins/server/StaticPlugin.d.ts +8 -0
  125. package/dist/plugins/server/plugins/server/StaticPlugin.js +45 -0
  126. package/dist/plugins/server/plugins/server/StaticPlugin.js.map +1 -0
  127. package/dist/plugins/server/server/config.d.ts +59 -0
  128. package/dist/plugins/server/server/config.js +181 -0
  129. package/dist/plugins/server/server/config.js.map +1 -0
  130. package/dist/plugins/server/server/connectors/FsHosting.d.ts +14 -0
  131. package/dist/plugins/server/server/connectors/FsHosting.js +66 -0
  132. package/dist/plugins/server/server/connectors/FsHosting.js.map +1 -0
  133. package/dist/plugins/server/server/connectors/FsStorage.d.ts +44 -0
  134. package/dist/plugins/server/server/connectors/FsStorage.js +261 -0
  135. package/dist/plugins/server/server/connectors/FsStorage.js.map +1 -0
  136. package/dist/plugins/server/server/connectors/connectors.d.ts +86 -0
  137. package/dist/plugins/server/server/connectors/connectors.js +126 -0
  138. package/dist/plugins/server/server/connectors/connectors.js.map +1 -0
  139. package/dist/plugins/server/server/events.d.ts +26 -0
  140. package/dist/plugins/server/server/events.js +31 -0
  141. package/dist/plugins/server/server/events.js.map +1 -0
  142. package/dist/plugins/server/server/jobs.d.ts +12 -0
  143. package/dist/plugins/server/server/jobs.js +79 -0
  144. package/dist/plugins/server/server/jobs.js.map +1 -0
  145. package/dist/plugins/server/server/utils/validation.d.ts +7 -0
  146. package/dist/plugins/server/server/utils/validation.js +37 -0
  147. package/dist/plugins/server/server/utils/validation.js.map +1 -0
  148. package/dist/plugins/server/types.d.ts +339 -0
  149. package/dist/plugins/server/types.js +84 -0
  150. package/dist/plugins/server/types.js.map +1 -0
  151. package/dist/server/constants.js +55 -0
  152. package/dist/server/plugins/server/Cache.js +19 -0
  153. package/dist/server/server/api/api.js +20 -0
  154. package/dist/server/server/api/connectorApi.js +334 -0
  155. package/dist/server/server/api/publicationApi.js +106 -0
  156. package/dist/server/server/api/websiteApi.js +315 -0
  157. package/dist/server/server/cli.js +71 -0
  158. package/dist/server/server/config.js +120 -0
  159. package/dist/server/server/connectors/FsHosting.js +47 -0
  160. package/dist/server/server/connectors/FsStorage.js +228 -0
  161. package/dist/server/server/connectors/connectors.js +88 -0
  162. package/dist/server/server/events.js +14 -0
  163. package/dist/server/server/expose.js +27 -0
  164. package/dist/server/server/express.js +46 -0
  165. package/dist/server/server/index.js +92 -0
  166. package/dist/server/server/jobs.js +50 -0
  167. package/dist/server/server/utils/validation.js +14 -0
  168. package/dist/server/types.js +40 -0
  169. package/package.json +99 -49
  170. package/public/assets/download.png +0 -0
  171. package/public/assets/favicon.ico +0 -0
  172. package/public/assets/favicon.png +0 -0
  173. package/public/assets/ftp.png +0 -0
  174. package/public/assets/gitlab.png +0 -0
  175. package/public/assets/gitlab2.png +0 -0
  176. package/public/assets/laptop.png +0 -0
  177. package/public/assets/logo-silex-small.png +0 -0
  178. package/public/silex.js +22 -23
  179. package/src/css/_variables.scss +24 -20
  180. package/src/css/dialog.scss +35 -2
  181. package/src/css/fonts.scss +31 -0
  182. package/src/css/footer.scss +12 -0
  183. package/src/css/grapesjs-light.scss +180 -0
  184. package/src/css/grapesjs-plugins.scss +227 -0
  185. package/src/css/grapesjs.scss +159 -1
  186. package/src/css/index.scss +12 -1
  187. package/src/css/light.scss +7 -0
  188. package/src/css/loader.scss +2 -0
  189. package/src/css/project-bar.scss +32 -16
  190. package/src/css/silex.scss +79 -5
  191. package/src/css/website-info.scss +14 -0
  192. package/src/html/index.pug +3 -3
  193. package/src/html/templates/includes.pug +0 -1
  194. package/src/ts/client/api.ts +156 -0
  195. package/src/ts/client/assetUrl.test.ts +164 -0
  196. package/src/ts/client/assetUrl.ts +254 -0
  197. package/src/ts/client/config.ts +175 -13
  198. package/src/ts/client/events.ts +43 -0
  199. package/src/ts/client/expose.ts +27 -1
  200. package/src/ts/client/grapesjs/LoginDialog.ts +204 -0
  201. package/src/ts/client/grapesjs/PublicationManager.ts +433 -0
  202. package/src/ts/client/grapesjs/PublicationUi.ts +366 -0
  203. package/src/ts/client/grapesjs/blocks.ts +17 -0
  204. package/src/ts/client/grapesjs/breadcrumbs.ts +79 -0
  205. package/src/ts/client/grapesjs/css-props.ts +910 -0
  206. package/src/ts/client/grapesjs/eleventy.ts +30 -7
  207. package/src/ts/client/grapesjs/footer.ts +42 -0
  208. package/src/ts/client/grapesjs/img.ts +147 -0
  209. package/src/ts/client/grapesjs/index.ts +311 -122
  210. package/src/ts/client/grapesjs/internal-links.test.ts +19 -1
  211. package/src/ts/client/grapesjs/internal-links.ts +31 -7
  212. package/src/ts/client/grapesjs/keymaps.ts +207 -0
  213. package/src/ts/client/grapesjs/new-page-dialog.ts +19 -3
  214. package/src/ts/client/grapesjs/openImport.ts +22 -0
  215. package/src/ts/client/grapesjs/page-panel.ts +109 -7
  216. package/src/ts/client/grapesjs/project-bar.ts +133 -72
  217. package/src/ts/client/grapesjs/resize-panel.ts +20 -0
  218. package/src/ts/client/grapesjs/rich-text.ts +38 -13
  219. package/src/ts/client/grapesjs/semantic.ts +20 -3
  220. package/src/ts/client/grapesjs/settings-sections.ts +117 -0
  221. package/src/ts/client/grapesjs/settings.ts +191 -96
  222. package/src/ts/client/grapesjs/storage.ts +187 -0
  223. package/src/ts/client/grapesjs/symbolDialogs.ts +21 -5
  224. package/src/ts/client/grapesjs/view-buttons.ts +42 -0
  225. package/src/ts/client/index.ts +80 -21
  226. package/src/ts/client/publication-transformers.test.ts +207 -0
  227. package/src/ts/client/publication-transformers.ts +280 -0
  228. package/src/ts/client/publish-custom-code-block.ts +31 -0
  229. package/src/ts/client/publish-fonts.ts +36 -0
  230. package/src/ts/client/utils.ts +70 -1
  231. package/src/ts/constants.ts +78 -0
  232. package/src/ts/externs.ts +17 -0
  233. package/src/ts/page.ts +20 -3
  234. package/src/ts/plugins/client/template.ts +246 -0
  235. package/src/ts/plugins/client/website-info.ts +84 -0
  236. package/src/ts/plugins/server/Cache.ts +32 -0
  237. package/src/ts/plugins/server/DownloadConnector.ts +146 -0
  238. package/src/ts/plugins/server/FtpConnector.test.ts +82 -0
  239. package/src/ts/plugins/server/FtpConnector.ts +621 -0
  240. package/src/ts/plugins/server/GitlabConnector.ts +749 -0
  241. package/src/ts/plugins/server/GitlabHostingConnector.ts +215 -0
  242. package/src/ts/plugins/server/PublicationOptimizer.ts +14 -0
  243. package/src/ts/plugins/server/SslPlugin.ts +84 -0
  244. package/src/ts/plugins/server/StaticPlugin.ts +45 -0
  245. package/src/ts/server/api/api.ts +19 -0
  246. package/src/ts/server/api/connectorApi.ts +464 -0
  247. package/src/ts/server/api/publicationApi.ts +159 -0
  248. package/src/ts/server/api/websiteApi.ts +462 -0
  249. package/src/ts/server/cli.ts +48 -14
  250. package/src/ts/server/config.ts +158 -60
  251. package/src/ts/server/connectors/FsHosting.ts +66 -0
  252. package/src/ts/server/connectors/FsStorage.test.ts +51 -0
  253. package/src/ts/server/connectors/FsStorage.ts +290 -0
  254. package/src/ts/server/connectors/connectors.ts +203 -0
  255. package/src/ts/server/events.ts +37 -0
  256. package/src/ts/server/expose.ts +19 -0
  257. package/src/ts/server/express.ts +57 -5
  258. package/src/ts/server/index.ts +80 -19
  259. package/src/ts/server/jobs.test.ts +75 -0
  260. package/src/ts/server/jobs.ts +89 -0
  261. package/src/ts/server/utils/BackwardCompat.ts +546 -528
  262. package/src/ts/server/utils/validation.ts +34 -0
  263. package/src/ts/types.ts +295 -85
  264. package/.dockerignore +0 -5
  265. package/.editorconfig +0 -14
  266. package/.eslintrc.json +0 -43
  267. package/.github/FUNDING.yml +0 -13
  268. package/.github/workflows/docker-hub-publish.yml +0 -36
  269. package/.github/workflows/npm-publish.yml +0 -19
  270. package/.github/workflows/test.yml +0 -26
  271. package/.nvmrc +0 -1
  272. package/.silex/default/.silex.data.json +0 -1
  273. package/CODE_OF_CONDUCT.md +0 -128
  274. package/CONTRIBUTING.md +0 -131
  275. package/Dockerfile +0 -15
  276. package/directus.silex.js +0 -7
  277. package/dist/constants.js +0 -4
  278. package/dist/events.js +0 -5
  279. package/dist/page.js +0 -15
  280. package/dist/server/cli.js +0 -43
  281. package/dist/server/config.js +0 -70
  282. package/dist/server/express.js +0 -18
  283. package/dist/server/index.js +0 -58
  284. package/dist/types.js +0 -34
  285. package/jest.config.js +0 -10
  286. package/logos/logo_by_ceubri/silex-icon-2018@200px.png +0 -0
  287. package/logos/logo_by_ceubri/silex-icon-2018@4x.png +0 -0
  288. package/logos/logo_by_ceubri/silex-logo-2018-white-transp.pdf +0 -0
  289. package/logos/logo_by_ceubri/silex-logo-2018-white.jpg +0 -0
  290. package/logos/logo_by_ceubri/silex-logo-2018-white.pdf +0 -0
  291. package/logos/logo_by_ceubri/silex-logo-2018-white.png +0 -0
  292. package/logos/logo_by_ceubri/silex-logo-2018-white@0.5x.png +0 -0
  293. package/logos/logo_by_ceubri/silex-logo-2018-white@0.75x.png +0 -0
  294. package/logos/logo_by_ceubri/silex-logo-2018-white@4x.jpg +0 -0
  295. package/logos/logo_by_ceubri/silex-logo-2018-white@4x.png +0 -0
  296. package/logos/logo_by_ceubri/silex-logo-2018.ai +5 -2521
  297. package/logos/logo_by_ceubri/silex-logo-2018.jpg +0 -0
  298. package/logos/logo_by_ceubri/silex-logo-2018.pdf +0 -0
  299. package/logos/logo_by_ceubri/silex-logo-2018.png +0 -0
  300. package/logos/logo_by_ceubri/silex-logo-2018@0.5x.png +0 -0
  301. package/logos/logo_by_ceubri/silex-logo-2018@0.75x.png +0 -0
  302. package/logos/logo_by_ceubri/silex-logo-2018@4x.jpg +0 -0
  303. package/logos/logo_by_ceubri/silex-logo-2018@4x.png +0 -0
  304. package/logos/silexlabs.png +0 -0
  305. package/src/example-client-config.js +0 -47
  306. package/src/plugins/AuthPlugin.js +0 -21
  307. package/src/plugins/Cache.js +0 -11
  308. package/src/plugins/DefaultBackend.js +0 -149
  309. package/src/plugins/ExpressPlugin.js +0 -39
  310. package/src/plugins/HooksPlugin.js +0 -58
  311. package/src/plugins/PublishPlugin.js +0 -69
  312. package/src/plugins/SslPlugin.js +0 -66
  313. package/src/plugins/StaticPlugin.js +0 -48
  314. package/src/plugins/WebsitePlugin.js +0 -207
  315. package/src/ts/client/grapesjs/publish.ts +0 -386
  316. package/src/ts/client/grapesjs/template.ts +0 -166
  317. package/src/ts/events.ts +0 -2
  318. package/tsconfig-server-debug.json +0 -19
  319. package/tsconfig-server-release.json +0 -19
  320. package/tsconfig.jest.json +0 -9
  321. package/tsconfig.json +0 -26
  322. package/webpack.config.js +0 -28
  323. package/webpack.debug.config.js +0 -8
@@ -0,0 +1,464 @@
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, ConnectorType } 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, type, connector.getOptions(body)))
267
+ } catch (error) {
268
+ res
269
+ .status(validateStatus(error?.code ?? error?.httpStatusCode, 500))
270
+ .send(getEndAuthHtml(error.message, true, req.query.connectorId as ConnectorId, req.query.type as ConnectorType))
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, connectorType: ConnectorType, options?: ConnectorOptions): string {
318
+ // Data for postMessage
319
+ const data = {
320
+ type: 'login', // For postMessage
321
+ error,
322
+ message,
323
+ connectorId,
324
+ connectorType,
325
+ options,
326
+ } as ApiConnectorLoggedInPostMessage
327
+ // Determine status title and heading based on the error
328
+ const status = error ? 'Error' : 'Success'
329
+ // Return the HTML template
330
+ return `
331
+ <!DOCTYPE html><html lang="en">
332
+ <head>
333
+ <title>Authentication ${status}</title>
334
+ <style>
335
+ :root {
336
+ --primaryColor: #333333;
337
+ --secondaryColor: #ddd;
338
+ --tertiaryColor: #8873FE;
339
+ --quaternaryColor: #A291FF;
340
+ --darkerPrimaryColor: #292929;
341
+ --lighterPrimaryColor: #575757;
342
+ }
343
+
344
+ body {
345
+ font-family: Arial, sans-serif;
346
+ text-align: center;
347
+ margin: 50px;
348
+ color: var(--primaryColor);
349
+ background-color: var(--secondaryColor);
350
+ }
351
+
352
+ h1 {
353
+ color: var(--tertiaryColor);
354
+ }
355
+
356
+ p {
357
+ font-size: 18px;
358
+ }
359
+
360
+ a {
361
+ color: var(--tertiaryColor);
362
+ text-decoration: none;
363
+ }
364
+
365
+ a:hover {
366
+ text-decoration: underline;
367
+ }
368
+
369
+ .container {
370
+ max-width: 600px;
371
+ margin: auto;
372
+ }
373
+
374
+ .button {
375
+ display: inline-block;
376
+ margin-top: 20px;
377
+ padding: 10px 20px;
378
+ font-size: 16px;
379
+ color: var(--secondaryColor);
380
+ background-color: var(--tertiaryColor);
381
+ border: none;
382
+ border-radius: 5px;
383
+ text-decoration: none;
384
+ cursor: pointer;
385
+ }
386
+
387
+ .button:hover {
388
+ background-color: var(--quaternaryColor); /* Slightly lighter shade of tertiary color */
389
+ }
390
+
391
+ .error {
392
+ display: none;
393
+ margin-top: 20px;
394
+ padding: 15px;
395
+ border: 1px solid var(--tertiaryColor);
396
+ border-radius: 5px;
397
+ text-wrap: wrap;
398
+ }
399
+
400
+ </style>
401
+ </head>
402
+ <body>
403
+ <div class="container">
404
+ <div id="message">
405
+ <h1>Authentication ${status}</h1>
406
+ <p>${error ? '' : message}</p>
407
+ </div>
408
+ ${ error ? `
409
+ <p><a data-link-to href="/">click here to continue</a>.</p>
410
+ <a data-link-to href="/" class="button">Retry</a>
411
+ <pre id="error-container" class="error">${message}</pre>
412
+ <script>
413
+ document.getElementById('error-container').style.display = "block";
414
+ fetch("${ API_PATH }${ API_CONNECTOR_PATH }${ API_CONNECTOR_LOGOUT }?connectorId=${ connectorId }&type=${ connectorType }", {
415
+ method: 'POST',
416
+ headers: {
417
+ 'Content-Type': 'application/json',
418
+ credentials: 'include', // sends the cookies with the request
419
+ },
420
+ })
421
+ if(window.opener && window.opener !== window) {
422
+ const linksTo = document.querySelectorAll('[data-link-to]');
423
+ linksTo.forEach(link => {
424
+ link.addEventListener('click', (e) => {
425
+ e.preventDefault();
426
+ const data = ${JSON.stringify(data)};
427
+ window.opener.postMessage(data, '*');
428
+ window.close();
429
+ });
430
+ });
431
+ }
432
+ </script>
433
+ ` : `
434
+ <p>If this window doesn’t close automatically, <a href="/">click here to return to the homepage</a>.</p>
435
+ <a href="/" class="button">Go to Homepage</a>
436
+ <pre id="error-container" class="error"></pre>
437
+ <script>
438
+ const data = ${JSON.stringify(data)};
439
+ const errorContainer = document.getElementById('error-container');
440
+ const messageContainer = document.getElementById('message');
441
+
442
+ // Check if the window was opened programmatically and is valid
443
+ if (window.opener && window.opener !== window) {
444
+ try {
445
+ // Send a postMessage to the opener
446
+ window.opener.postMessage(data, '*');
447
+ // Attempt to close the window
448
+ window.close();
449
+ } catch (e) {
450
+ // Display error message if closing fails
451
+ errorContainer.innerText = "Unable to close the window. Please close it manually.";
452
+ errorContainer.style.display = "block";
453
+ }
454
+ } else {
455
+ messageContainer.innerHTML = '<h1>Redirecting, please wait...</h1>';
456
+ window.location.href = '/'
457
+ }
458
+ </script>
459
+ `}
460
+ </div>
461
+ </body>
462
+ </html>
463
+ `
464
+ }
@@ -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 fileWithContent = file as ClientSideFileWithContent
128
+ const fileWithSrc = file as ClientSideFileWithSrc
129
+ if(!fileWithContent.content && !fileWithSrc.src) throw new PublicationError('Missing content or src in file', 400)
130
+ const src = fileWithSrc.src
131
+ return {
132
+ // Destination
133
+ path: file.permalink ?? file.path,
134
+ // Content
135
+ content: fileWithContent.content ?? await storage.readAsset(session, websiteId, fileWithSrc.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
+ }