@silexlabs/silex 3.0.1-alpha.0 → 3.0.1

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 +94 -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 +79 -16
  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
@@ -1,3 +1,20 @@
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
+
1
18
  /**
2
19
  * @fileoverview This is where the default config is defined. Also use this object as an event emitter to listen to Silex events
3
20
  * The values can be overriden with env vars or before passing the config to Silex
@@ -8,84 +25,165 @@
8
25
  */
9
26
 
10
27
  import { Config, Plugin } from '@silexlabs/silex-plugins'
11
- import { resolve } from 'path'
28
+ import { join, resolve } from 'path'
12
29
  import { CLIENT_CONFIG_FILE_NAME } from '../constants'
13
- import { Router } from 'express'
30
+ import { Application, Request, Response, Router } from 'express'
14
31
  import { readFile } from 'fs/promises'
15
- import { EVENT_STARTUP_START } from '../events'
32
+ import { HostingConnector, StorageConnector, toConnectorEnum } from './connectors/connectors'
33
+ import { FsStorage } from './connectors/FsStorage'
34
+ import { Connector } from './connectors/connectors'
35
+ import { ConnectorType } from '../types'
36
+ import { FsHosting } from './connectors/FsHosting'
16
37
 
17
38
  /**
18
39
  * Config types definitions
19
40
  */
20
41
  export class ServerConfig extends Config {
21
- constructor(
22
- public url: string,
23
- public debug: boolean,
24
- public port: string,
25
- public apiPath: string,
26
- ) {
27
- super()
42
+ public expressOptions = {
43
+ jsonLimit: process.env.SILEX_EXPRESS_JSON_LIMIT,
44
+ textLimit: process.env.SILEX_EXPRESS_TEXT_LIMIT,
45
+ urlencodedLimit: process.env.SILEX_EXPRESS_URLENCODED_LIMIT,
46
+ sessionName: process.env.SILEX_SESSION_NAME,
47
+ sessionSecret: process.env.SILEX_SESSION_SECRET,
48
+ cors: process.env.SILEX_CORS_URL,
28
49
  }
29
- }
50
+ public port = process.env.SILEX_PORT
51
+ public debug = process.env.SILEX_DEBUG === 'true'
52
+ public url = process.env.SILEX_URL?.replace(/\/$/, '') ?? `${process.env.SILEX_PROTOCOL}://${process.env.SILEX_HOST}:${process.env.SILEX_PORT}`
53
+ public userConfigPath: Plugin | undefined = process.env.SILEX_SERVER_CONFIG
54
+ public configFilePath: Plugin = resolve(__dirname, '../../../.silex.js')
55
+
56
+ // All connectors or just the storage or hosting connectors
57
+ getConnectors(type: ConnectorType | string | null = null): Connector[] {
58
+ // All connectors including storage and hosting connectors
59
+ if (!type) {
60
+ // Concat storage and hosting connectors
61
+ const allConnectors: Connector[] = (this.getStorageConnectors() as Connector[]).concat(this.getHostingConnectors())
62
+
63
+ // Remove duplicates (if a connector is both a storage and a hosting connector)
64
+ return [...new Set(allConnectors)]
65
+ }
30
66
 
31
- // Get config async function
32
- export default async function(): Promise<ServerConfig> {
33
- const port = process.env.PORT || '6805' // 6805 is the date of sexual revolution started in paris france 8-)
34
- const debug = process.env.SILEX_DEBUG === 'true'
35
- const clientConfigPath = process.env.SILEX_CLIENT_CONFIG
67
+ // Convert to enum in case it is a string
68
+ const connectorType = toConnectorEnum(type.toString())
36
69
 
37
- const url = process.env.SERVER_URL || `http://localhost:${port}`
38
- const config = new ServerConfig(url, debug, port, '/api')
70
+ // Only one type of connector
71
+ switch(connectorType) {
72
+ case ConnectorType.HOSTING: return this.getHostingConnectors()
73
+ case ConnectorType.STORAGE: return this.getStorageConnectors()
74
+ default: throw new Error(`Unknown connector type ${type}`)
75
+ }
76
+ }
39
77
 
40
- // Serve the client side config file
41
- if (clientConfigPath) {
42
- config.on(EVENT_STARTUP_START, async ({ app }) => {
43
- console.log(`> Serving client side config ${clientConfigPath} at ${CLIENT_CONFIG_FILE_NAME}`)
44
- // Attach the route immediately
45
- const router = Router()
46
- app.use(router)
47
- // Load the config file and serve it
48
- let clientConfig = (await readFile(clientConfigPath as string)).toString()
49
- router.get(`/${CLIENT_CONFIG_FILE_NAME}`, async (res, req, next) => {
50
- // Reload each time in debug mode
51
- if (config.debug) {
52
- console.log('[Debug mode] Reloading config file', clientConfigPath)
53
- clientConfig = (await readFile(clientConfigPath)).toString()
54
- }
55
- // Send the config file
56
- req.header('Content-Type', 'application/javascript').send(clientConfig)
57
- })
58
- })
78
+ // Storage connectors to store the website data and assets
79
+ private storageConnectors: StorageConnector[] = []
80
+ addStorageConnector(storage: StorageConnector | StorageConnector[]) {
81
+ this.setStorageConnectors(this.storageConnectors.concat(storage))
82
+ }
83
+ setStorageConnectors(storageConnectors: StorageConnector[]) {
84
+ this.storageConnectors =storageConnectors
85
+ }
86
+ getStorageConnectors(): StorageConnector[] {
87
+ return this.storageConnectors
59
88
  }
60
89
 
61
- // Optional config file
62
- // Load this config file before the main config file so that routes can be overriden
63
- // Or listen for event silex:startup:end to add routes after the default ones
64
- if (process.env.CONFIG) {
65
- const userConfigPath: Plugin = process.env.CONFIG
66
- console.log('> Loading user config', userConfigPath)
90
+ // Hosting connectors to publish the website online
91
+ private hostingConnectors: HostingConnector[] = []
92
+ addHostingConnector(hosting: HostingConnector | HostingConnector[]) {
93
+ this.setHostingConnectors(this.hostingConnectors.concat(hosting))
94
+ }
95
+ setHostingConnectors(hostings: HostingConnector[]) {
96
+ this.hostingConnectors = hostings
97
+ }
98
+ getHostingConnectors(): HostingConnector[] { return this.hostingConnectors }
99
+
100
+ /**
101
+ * Add routes to serve the client config file
102
+ */
103
+ async addRoutes(app: Application) {
104
+ const path = process.env.SILEX_CLIENT_CONFIG
105
+ if (path) {
106
+ console.info(`> Serving client side config ${path} at ${CLIENT_CONFIG_FILE_NAME}`)
107
+ try {
108
+ // Load the client config file
109
+ let clientConfig = (await readFile(path as string)).toString()
110
+ // Serve the config file
111
+ app.get(`/${CLIENT_CONFIG_FILE_NAME}`, async (req: Request, res: Response) => {
112
+ // Reload each time in debug mode
113
+ if (this.debug) {
114
+ console.info('[Debug mode] Reloading config file', path)
115
+ clientConfig = (await readFile(path)).toString()
116
+ }
117
+ // Send the config file
118
+ res
119
+ .contentType('application/javascript')
120
+ .send(clientConfig)
121
+ })
122
+ } catch (e) {
123
+ console.error(`Error loading client config file ${path}`, e)
124
+ throw new Error(`Error loading client config file ${path}: ${e.message}`)
125
+ }
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Load config files
131
+ * This is the main config file and the user config file
132
+ */
133
+ async loadConfigFiles() {
134
+ // Load the user config file
135
+ await this.loadUserConfig()
136
+ // Load the main config file
137
+ await this.loadSilexConfig()
138
+ }
139
+
140
+ /**
141
+ * Load user config file
142
+ * This is the config file passed as env var CONFIG
143
+ */
144
+ async loadUserConfig() {
145
+ if (this.userConfigPath) {
146
+ console.info('> Loading user config', this.userConfigPath)
147
+ try {
148
+ // Initiate the process with the config file which is just another plugin
149
+ await this.addPlugin(this.userConfigPath, {})
150
+ } catch (e) {
151
+ throw new Error(`\nUser config file ${this.userConfigPath} error:\n\n\t${e.message}\n\n`)
152
+ }
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Load .silex.js in the root folder
158
+ * This is the main config file
159
+ */
160
+ async loadSilexConfig() {
161
+ console.info('> Loading config', this.configFilePath)
67
162
  try {
68
163
  // Initiate the process with the config file which is just another plugin
69
- await config.addPlugin(userConfigPath, {})
70
- } catch (e) {
71
- throw new Error(`User config file ${userConfigPath} error: ${e.message}`)
164
+ await this.addPlugin(this.configFilePath, {})
165
+ } catch(e) {
166
+ // Check if the error is about the config file not found and not another module not found in the config file
167
+ if(e.code === 'MODULE_NOT_FOUND' && (!e.requireStack || !e.requireStack.find(path => path === this.configFilePath))) {
168
+ console.info('> /!\\ Config file not found', this.configFilePath)
169
+ } else {
170
+ throw new Error(`Error in config file ${this.configFilePath}: ${e.message}`)
171
+ }
72
172
  }
73
173
  }
74
174
 
75
- // Config file in root folder
76
- const configFilePath: Plugin = resolve(__dirname, '../../.silex.js')
77
- console.log('> Loading config', configFilePath)
78
- try {
79
- // Initiate the process with the config file which is just another plugin
80
- await config.addPlugin(configFilePath, {})
81
- } catch(e) {
82
- if(e.code === 'MODULE_NOT_FOUND') {
83
- console.info('No config found', configFilePath)
84
- } else {
85
- throw new Error(`Error in config file ${configFilePath}: ${e.message}`)
175
+ async initDefaultConnectors() {
176
+ // Add default storage connectors
177
+ if (!this.storageConnectors.length) {
178
+ this.addStorageConnector(new FsStorage(null, {
179
+ path: process.env.SILEX_FS_ROOT || join(process.cwd(), '/silex/storage'),
180
+ }))
181
+ }
182
+ // Add default hosting connectors
183
+ if (!this.hostingConnectors.length) {
184
+ this.addHostingConnector(new FsHosting(null, {
185
+ path: process.env.SILEX_FS_HOSTING_ROOT || join(process.cwd(), '/silex/hosting'),
186
+ }))
86
187
  }
87
188
  }
88
-
89
- // Return the config file
90
- return config
91
189
  }
@@ -0,0 +1,66 @@
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 fs from 'fs/promises'
19
+ import { ConnectorFile, StorageConnector, HostingConnector, StatusCallback, ConnectorSession, contentToString, toConnectorData, ConnectorFileContent} from './connectors'
20
+ import { join } from 'path'
21
+ import { FsStorage } from './FsStorage'
22
+ import { ConnectorType, JobData, JobStatus, PublicationJobData, WebsiteId } from '../../types'
23
+ import { JobManager } from '../jobs'
24
+
25
+ type FsSession = ConnectorSession
26
+
27
+ export class FsHosting extends FsStorage implements HostingConnector<FsSession> {
28
+ connectorId = 'fs-hosting'
29
+ displayName = 'File system hosting'
30
+ connectorType = ConnectorType.HOSTING
31
+
32
+ protected async initFs() {
33
+ const stat = await fs.stat(this.options.path).catch(() => null)
34
+ if (!stat) {
35
+ await fs.mkdir(join(this.options.path, 'assets'), { recursive: true })
36
+ await fs.mkdir(join(this.options.path, 'css'), { recursive: true })
37
+ console.info(`> [FsHosting] Created folders assets/ and css/ in ${this.options.path}`)
38
+ }
39
+ }
40
+
41
+ async publish(session: FsSession, id: WebsiteId, files: ConnectorFile[], {startJob, jobSuccess, jobError}: JobManager): Promise<JobData> {
42
+ const job = startJob(`Publishing to ${this.displayName}`) as PublicationJobData
43
+ job.logs = [[`Publishing to ${this.displayName}`]]
44
+ job.errors = [[]]
45
+ // Call write without id or folder so that it goes in / (path will be modified by publication transformers)
46
+ await this.write(session, '', files, '', async ({status, message}) => {
47
+ // Update the job status
48
+ job.status = status
49
+ job.message = message
50
+ job.logs[0].push(message)
51
+ if(status === JobStatus.SUCCESS) {
52
+ jobSuccess(job.jobId, message)
53
+ } else if(status === JobStatus.ERROR) {
54
+ job.errors[0].push(message)
55
+ jobError(job.jobId, message)
56
+ }
57
+ })
58
+ return job
59
+ }
60
+
61
+ async getUrl(session: FsSession, id: WebsiteId): Promise<string> {
62
+ const filePath = join(this.options.path, id, 'index.html')
63
+ const fileUrl = new URL(filePath, 'file://')
64
+ return fileUrl.toString()
65
+ }
66
+ }
@@ -0,0 +1,51 @@
1
+ import { expect, beforeEach, afterAll, it, describe } from '@jest/globals'
2
+ import { ServerConfig } from '../config'
3
+ import { FsStorage } from './FsStorage'
4
+ import { readFileSync, rmdirSync, statSync } from 'fs'
5
+ import { join } from 'path'
6
+
7
+ const storageRootPath = '/tmp/silex-tests'
8
+ const assetsFolder = 'assets'
9
+ const dummySession = {}
10
+ const dummyId = 'dummy id'
11
+ const dummyWebsite = {
12
+ pages: [],
13
+ assets: [],
14
+ styles: [],
15
+ settings: {},
16
+ fonts: [],
17
+ symbols: [],
18
+ publication: {},
19
+ }
20
+
21
+ beforeEach(() => {
22
+ })
23
+
24
+ afterAll(() => {
25
+ rmdirSync(storageRootPath, { recursive: true })
26
+ })
27
+
28
+ async function sleep(ms: number) {
29
+ return new Promise(resolve => setTimeout(resolve, ms))
30
+ }
31
+ describe('FsStorage website', () => {
32
+ it('should create the root folder', async () => {
33
+ expect(() => statSync(storageRootPath)).toThrow()
34
+ const connector = new FsStorage({} as ServerConfig, {
35
+ path: storageRootPath,
36
+ assetsFolder,
37
+ })
38
+ await sleep(100)
39
+ expect(() => statSync(storageRootPath)).not.toThrow()
40
+ })
41
+ it('should create a new website', async () => {
42
+ const connector = new FsStorage({} as ServerConfig, {
43
+ path: storageRootPath,
44
+ assetsFolder,
45
+ })
46
+ const id = await connector.createWebsite(dummySession, { name: 'dummy name', connectorUserSettings: {} })
47
+ expect(id).toBeDefined()
48
+ expect(() => statSync(join(storageRootPath, id, 'website.json'))).not.toThrow()
49
+ expect(readFileSync(join(storageRootPath, id, 'website.json'), 'utf8')).toBe(JSON.stringify({}))
50
+ })
51
+ })
@@ -0,0 +1,290 @@
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 fs from 'fs/promises'
19
+ import { createWriteStream } from 'fs'
20
+ import { ConnectorFile, StorageConnector, StatusCallback, ConnectorSession, toConnectorData, ConnectorFileContent} from './connectors'
21
+ import { dirname, join } from 'path'
22
+ import { ConnectorUser, WebsiteMeta, JobStatus, WebsiteId, ConnectorType, WebsiteMetaFileContent, WebsiteData, defaultWebsiteData, ConnectorOptions } from '../../types'
23
+ import { userInfo } from 'os'
24
+ import { requiredParam } from '../utils/validation'
25
+ import { ServerConfig } from '../config'
26
+ import { DEFAULT_WEBSITE_ID, WEBSITE_DATA_FILE, WEBSITE_META_DATA_FILE } from '../../constants'
27
+ import { Readable } from 'stream'
28
+ import { v4 as uuid } from 'uuid'
29
+ import { fileURLToPath } from 'url'
30
+
31
+ // Variables needed for jest tests
32
+ if(!globalThis.__dirname) {
33
+ // @ts-ignore
34
+ globalThis.__dirname = dirname(process.cwd() + '/src/ts/server/connectors/FsStorage.ts')
35
+ console.info('Redefining __dirname', globalThis.__dirname)
36
+ }
37
+
38
+ // Copy a folder recursively
39
+ async function copyDir(src, dest) {
40
+ await fs.mkdir(dest, { recursive: true })
41
+ const entries = await fs.readdir(src, { withFileTypes: true })
42
+
43
+ for (const entry of entries) {
44
+ const srcPath = join(src, entry.name)
45
+ const destPath = join(dest, entry.name)
46
+
47
+ entry.isDirectory() ?
48
+ await copyDir(srcPath, destPath) :
49
+ await fs.copyFile(srcPath, destPath)
50
+ }
51
+ }
52
+
53
+
54
+ type FsSession = ConnectorSession
55
+
56
+ const USER_ICON = 'data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' height=\'1em\' viewBox=\'0 0 448 512\'%3E%3Cpath d=\'M304 128a80 80 0 1 0 -160 0 80 80 0 1 0 160 0zM96 128a128 128 0 1 1 256 0A128 128 0 1 1 96 128zM49.3 464H398.7c-8.9-63.3-63.3-112-129-112H178.3c-65.7 0-120.1 48.7-129 112zM0 482.3C0 383.8 79.8 304 178.3 304h91.4C368.2 304 448 383.8 448 482.3c0 16.4-13.3 29.7-29.7 29.7H29.7C13.3 512 0 498.7 0 482.3z\'/%3E%3C/svg%3E'
57
+ const FILE_ICON = '/assets/laptop.png'
58
+
59
+ interface FsOptions {
60
+ path: string
61
+ assetsFolder: string
62
+ }
63
+
64
+ export class FsStorage implements StorageConnector<FsSession> {
65
+ connectorId = 'fs-storage'
66
+ displayName = 'File system storage'
67
+ icon = FILE_ICON
68
+ disableLogout = true
69
+ options: FsOptions
70
+ connectorType = ConnectorType.STORAGE
71
+ color = '#ffffff'
72
+ background = '#006400'
73
+
74
+ constructor(config: ServerConfig | null, opts: Partial<FsOptions>) {
75
+ this.options = {
76
+ path: join(__dirname, '..', '..', '..', '..', 'data'),
77
+ assetsFolder: '/assets',
78
+ ...opts,
79
+ }
80
+ this.initFs()
81
+ }
82
+
83
+ protected async initFs() {
84
+ const stat = await fs.stat(this.options.path).catch(() => null)
85
+ if (!stat) {
86
+ // create data folder with a default website
87
+ const id = DEFAULT_WEBSITE_ID
88
+ await fs.mkdir(join(this.options.path, id, this.options.assetsFolder), { recursive: true })
89
+ await this.setWebsiteMeta({}, id, { name: 'Default website', connectorUserSettings: {} })
90
+ await this.updateWebsite({}, id, defaultWebsiteData)
91
+ console.info(`> [FsStorage] Created ${id} in ${this.options.path}`)
92
+ }
93
+ }
94
+
95
+ // ********************
96
+ // Job utils methods
97
+ // ********************
98
+ private updateStatus(filesStatuses, status, statusCbk) {
99
+ statusCbk && statusCbk({
100
+ message: `<p>Writing files:<ul><li>${filesStatuses.map(({file, message}) => `${file.path}: ${message}`).join('</li><li>')}</li></ul></p>`,
101
+ status,
102
+ })
103
+ }
104
+
105
+ private initStatus(files) {
106
+ return files.map(file => ({
107
+ file,
108
+ message: 'Waiting',
109
+ status: JobStatus.IN_PROGRESS,
110
+ }))
111
+ }
112
+
113
+ // ********************
114
+ // Connector interface
115
+ // ********************
116
+ getOptions(formData: object): ConnectorOptions {
117
+ return {}
118
+ }
119
+
120
+ async getOAuthUrl(session: FsSession): Promise<null> { return null }
121
+
122
+ async getLoginForm(session: FsSession, redirectTo: string): Promise<string | null> {
123
+ return null
124
+ }
125
+ async getSettingsForm(session: FsSession, redirectTo: string): Promise<string | null> {
126
+ return null
127
+ }
128
+
129
+ async isLoggedIn(session: FsSession): Promise<boolean> {
130
+ return true
131
+ }
132
+
133
+ async setToken(session: FsSession, query: object): Promise<void> {}
134
+
135
+ async logout(session: FsSession): Promise<void> {}
136
+
137
+ async getUser(session: FsSession): Promise<ConnectorUser> {
138
+ const { username, } = userInfo()
139
+ return {
140
+ name: username,
141
+ picture: USER_ICON,
142
+ storage: await toConnectorData(session, this),
143
+ }
144
+ }
145
+
146
+ async setWebsiteMeta(session: any, id: string, data: WebsiteMetaFileContent): Promise<void> {
147
+ const websiteId = requiredParam<WebsiteId>(id, 'website id')
148
+ const content = JSON.stringify(data)
149
+ const path = join(this.options.path, id, WEBSITE_META_DATA_FILE)
150
+ await fs.writeFile(path, content)
151
+ }
152
+
153
+ async getWebsiteMeta(session: FsSession, id: WebsiteId): Promise<WebsiteMeta> {
154
+ const websiteId = requiredParam<WebsiteId>(id, 'website id')
155
+ // Get stats for website folder
156
+ const fileStat = await fs.stat(join(this.options.path, websiteId))
157
+ const path = join(this.options.path, websiteId, WEBSITE_META_DATA_FILE)
158
+ // Get meta file
159
+ const content = await fs.readFile(path)
160
+ const meta = await JSON.parse(content.toString())
161
+ // Return all meta
162
+ return {
163
+ websiteId,
164
+ name: meta.name,
165
+ imageUrl: meta.imageUrl,
166
+ connectorUserSettings: meta.connectorUserSettings,
167
+ createdAt: fileStat.birthtime,
168
+ updatedAt: fileStat.mtime,
169
+ }
170
+ }
171
+
172
+ // ********************
173
+ // Storage interface
174
+ // ********************
175
+ async createWebsite(session: FsSession, meta: WebsiteMetaFileContent): Promise<WebsiteId> {
176
+ const id = uuid()
177
+ await fs.mkdir(join(this.options.path, id, this.options.assetsFolder), { recursive: true })
178
+ await this.setWebsiteMeta(session, id, meta)
179
+ await this.updateWebsite(session, id, defaultWebsiteData)
180
+ return id
181
+ }
182
+
183
+ async readWebsite(session: FsSession, websiteId: WebsiteId): Promise<WebsiteData> {
184
+ const id = requiredParam<WebsiteId>(websiteId, 'website id')
185
+ const path = join(this.options.path, id, WEBSITE_DATA_FILE)
186
+ const content = await fs.readFile(path)
187
+ return JSON.parse(content.toString())
188
+ }
189
+
190
+ async updateWebsite(session: FsSession, websiteId: WebsiteId, data: WebsiteData): Promise<void> {
191
+ const id = requiredParam<WebsiteId>(websiteId, 'website id')
192
+ const path = join(this.options.path, id, WEBSITE_DATA_FILE)
193
+ await fs.writeFile(path, JSON.stringify(data))
194
+ }
195
+
196
+ async deleteWebsite(session: FsSession, websiteId: WebsiteId): Promise<void> {
197
+ const id = requiredParam<WebsiteId>(websiteId, 'website id')
198
+ const path = join(this.options.path, id)
199
+ return fs.rmdir(path, { recursive: true })
200
+ }
201
+
202
+ async duplicateWebsite(session: FsSession, websiteId: WebsiteId): Promise<void> {
203
+ const newWebsiteId = uuid()
204
+ const from = join(this.options.path, websiteId)
205
+ const to = join(this.options.path, newWebsiteId)
206
+ await copyDir(from, to)
207
+ const meta = await this.getWebsiteMeta(session, websiteId)
208
+ await this.setWebsiteMeta(session, newWebsiteId, {
209
+ ...meta,
210
+ name: `${meta.name} copy`,
211
+ })
212
+ }
213
+
214
+ async listWebsites(session: any): Promise<WebsiteMeta[]> {
215
+ const list = await fs.readdir(this.options.path)
216
+ return Promise.all(list.map(async fileName => {
217
+ const websiteId = fileName as WebsiteId
218
+ return this.getWebsiteMeta(session, websiteId)
219
+ }))
220
+ }
221
+
222
+ async getAsset(session: FsSession, id: WebsiteId, path: string): Promise<ConnectorFile> {
223
+ const fullPath = join(this.options.path, id, this.options.assetsFolder, path)
224
+ const content = await fs.readFile(fullPath)
225
+ return { path, content }
226
+ }
227
+
228
+ async writeAssets(session: FsSession, id: WebsiteId, files: ConnectorFile[], statusCbk?: StatusCallback): Promise<void> {
229
+ return this.write(session, id, files, this.options.assetsFolder, statusCbk)
230
+ }
231
+
232
+ async write(session: FsSession, id: WebsiteId, files: ConnectorFile[], assetsFolder: string, statusCbk?: StatusCallback): Promise<void> {
233
+ const filesStatuses = this.initStatus(files)
234
+ let error: Error | null = null
235
+ for (const fileStatus of filesStatuses) {
236
+ const {file} = fileStatus
237
+ const path = join(this.options.path, id, assetsFolder, file.path)
238
+ if (typeof file.content === 'string' || Buffer.isBuffer(file.content)) {
239
+ fileStatus.message = 'Writing'
240
+ this.updateStatus(filesStatuses, JobStatus.IN_PROGRESS, statusCbk)
241
+ try {
242
+ await fs.writeFile(path, file.content)
243
+ } catch(err) {
244
+ fileStatus.message = `Error (${err})`
245
+ this.updateStatus(filesStatuses, JobStatus.IN_PROGRESS, statusCbk)
246
+ error = err
247
+ continue
248
+ }
249
+ fileStatus.message = 'Success'
250
+ this.updateStatus(filesStatuses, JobStatus.IN_PROGRESS, statusCbk)
251
+ } else if (file.content instanceof Readable) {
252
+ fileStatus.message = 'Writing'
253
+ this.updateStatus(filesStatuses, JobStatus.IN_PROGRESS, statusCbk)
254
+ const writeStream = createWriteStream(path)
255
+ file.content.pipe(writeStream)
256
+ await new Promise((resolve) => {
257
+ writeStream.on('finish', () => {
258
+ fileStatus.message = 'Success'
259
+ this.updateStatus(filesStatuses, JobStatus.IN_PROGRESS, statusCbk)
260
+ resolve(file)
261
+ })
262
+ writeStream.on('error', err => {
263
+ console.error('writeStream error', err)
264
+ fileStatus.message = `Error (${err})`
265
+ this.updateStatus(filesStatuses, JobStatus.IN_PROGRESS, statusCbk)
266
+ error = err
267
+ resolve(file)
268
+ })
269
+ })
270
+ } else {
271
+ console.error('Invalid file content', typeof file.content)
272
+ throw new Error('Invalid file content: ' + typeof file.content)
273
+ }
274
+ }
275
+ this.updateStatus(filesStatuses, error ? JobStatus.ERROR : JobStatus.SUCCESS, statusCbk)
276
+ if(error) throw error
277
+ }
278
+
279
+ async deleteAssets(session: FsSession, id: WebsiteId, paths: string[]): Promise<void> {
280
+ for (const path of paths) {
281
+ await fs.unlink(join(this.options.path, id, path))
282
+ }
283
+ }
284
+
285
+ async readAsset(session: object, websiteId: string, fileName: string): Promise<ConnectorFileContent> {
286
+ const id = requiredParam<WebsiteId>(websiteId, 'website id')
287
+ const path = join(this.options.path, id, this.options.assetsFolder, fileName)
288
+ return await fs.readFile(path)
289
+ }
290
+ }