@silexlabs/silex 3.0.1-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 (250) hide show
  1. package/.env.default +3 -2
  2. package/.silex.js +39 -1
  3. package/README.md +37 -35
  4. package/dist/client/client/api.d.ts +8 -1
  5. package/dist/client/client/assetUrl.d.ts +54 -0
  6. package/dist/client/client/config.d.ts +60 -1
  7. package/dist/client/client/events.d.ts +19 -10
  8. package/dist/client/client/grapesjs/PublicationManager.d.ts +6 -8
  9. package/dist/client/client/grapesjs/PublicationUi.d.ts +8 -10
  10. package/dist/client/client/grapesjs/breadcrumbs.d.ts +1 -0
  11. package/dist/client/client/grapesjs/css-props.d.ts +6 -0
  12. package/dist/client/client/grapesjs/footer.d.ts +2 -0
  13. package/dist/client/client/grapesjs/img.d.ts +6 -0
  14. package/dist/client/client/grapesjs/index.d.ts +6 -2
  15. package/dist/client/client/grapesjs/internal-links.d.ts +1 -1
  16. package/dist/client/client/grapesjs/keymaps.d.ts +123 -0
  17. package/dist/client/client/grapesjs/new-page-dialog.d.ts +1 -1
  18. package/dist/client/client/grapesjs/page-panel.d.ts +7 -1
  19. package/dist/client/client/grapesjs/project-bar.d.ts +20 -0
  20. package/dist/client/client/grapesjs/resize-panel.d.ts +1 -0
  21. package/dist/client/client/grapesjs/rich-text.d.ts +4 -1
  22. package/dist/client/client/grapesjs/semantic.d.ts +1 -1
  23. package/dist/client/client/grapesjs/settings-sections.d.ts +16 -0
  24. package/dist/client/client/grapesjs/settings.d.ts +3 -1
  25. package/dist/client/client/grapesjs/storage.d.ts +1 -0
  26. package/dist/client/client/grapesjs/symbolDialogs.d.ts +1 -1
  27. package/dist/client/client/grapesjs/view-buttons.d.ts +1 -0
  28. package/dist/client/client/index.d.ts +5 -0
  29. package/dist/client/client/publication-transformers.d.ts +42 -0
  30. package/dist/client/client/publish-custom-code-block.d.ts +4 -0
  31. package/dist/client/client/publish-fonts.d.ts +4 -0
  32. package/dist/client/client/utils.d.ts +22 -1
  33. package/dist/client/constants.d.ts +3 -0
  34. package/dist/client/css/admin.css +1107 -411
  35. package/dist/client/css/admin.css.map +1 -0
  36. package/dist/client/grapesjs-filter-styles/dist/index.d.ts +4 -0
  37. package/dist/client/grapesjs-fonts/dist/index.d.ts +4 -0
  38. package/dist/client/grapesjs-fonts/src/commands.d.ts +3 -0
  39. package/dist/client/grapesjs-fonts/src/fonts.d.ts +4 -0
  40. package/dist/client/grapesjs-storage-rate-limit/dist/index.d.ts +4 -0
  41. package/dist/client/grapesjs-ui-suggest-classes/dist/index.d.ts +4 -0
  42. package/dist/client/index.html +3 -2
  43. package/dist/client/js/main.js +305 -170
  44. package/dist/client/js/main.js.LICENSE.txt +21 -1
  45. package/dist/client/page.d.ts +1 -1
  46. package/dist/client/silex-lib/src/ts/client/api.d.ts +90 -0
  47. package/dist/client/silex-lib/src/ts/client/assetUrl.d.ts +54 -0
  48. package/dist/client/silex-lib/src/ts/client/config.d.ts +102 -0
  49. package/dist/client/silex-lib/src/ts/client/events.d.ts +23 -0
  50. package/dist/client/silex-lib/src/ts/client/expose.d.ts +8 -0
  51. package/dist/client/silex-lib/src/ts/client/grapesjs/LoginDialog.d.ts +15 -0
  52. package/dist/client/silex-lib/src/ts/client/grapesjs/PublicationManager.d.ts +73 -0
  53. package/dist/client/silex-lib/src/ts/client/grapesjs/PublicationUi.d.ts +59 -0
  54. package/dist/client/silex-lib/src/ts/client/grapesjs/blocks.d.ts +1 -0
  55. package/dist/client/silex-lib/src/ts/client/grapesjs/breadcrumbs.d.ts +1 -0
  56. package/dist/client/silex-lib/src/ts/client/grapesjs/css-props.d.ts +6 -0
  57. package/dist/client/silex-lib/src/ts/client/grapesjs/footer.d.ts +2 -0
  58. package/dist/client/silex-lib/src/ts/client/grapesjs/img.d.ts +6 -0
  59. package/dist/client/silex-lib/src/ts/client/grapesjs/index.d.ts +9 -0
  60. package/dist/client/silex-lib/src/ts/client/grapesjs/internal-links.d.ts +1 -0
  61. package/dist/client/silex-lib/src/ts/client/grapesjs/keymaps.d.ts +123 -0
  62. package/dist/client/silex-lib/src/ts/client/grapesjs/new-page-dialog.d.ts +2 -0
  63. package/dist/client/silex-lib/src/ts/client/grapesjs/openImport.d.ts +9 -0
  64. package/dist/client/silex-lib/src/ts/client/grapesjs/page-panel.d.ts +9 -0
  65. package/dist/client/silex-lib/src/ts/client/grapesjs/project-bar.d.ts +21 -0
  66. package/dist/client/silex-lib/src/ts/client/grapesjs/resize-panel.d.ts +1 -0
  67. package/dist/client/silex-lib/src/ts/client/grapesjs/rich-text.d.ts +4 -0
  68. package/dist/client/silex-lib/src/ts/client/grapesjs/semantic.d.ts +1 -0
  69. package/dist/client/silex-lib/src/ts/client/grapesjs/settings-sections.d.ts +16 -0
  70. package/dist/client/silex-lib/src/ts/client/grapesjs/settings.d.ts +4 -0
  71. package/dist/client/silex-lib/src/ts/client/grapesjs/storage.d.ts +2 -0
  72. package/dist/client/silex-lib/src/ts/client/grapesjs/symbolDialogs.d.ts +3 -0
  73. package/dist/client/silex-lib/src/ts/client/grapesjs/view-buttons.d.ts +1 -0
  74. package/dist/client/silex-lib/src/ts/client/index.d.ts +10 -0
  75. package/dist/client/silex-lib/src/ts/client/publication-transformers.d.ts +42 -0
  76. package/dist/client/silex-lib/src/ts/client/publish-custom-code-block.d.ts +4 -0
  77. package/dist/client/silex-lib/src/ts/client/publish-fonts.d.ts +4 -0
  78. package/dist/client/silex-lib/src/ts/client/utils.d.ts +22 -0
  79. package/dist/client/silex-lib/src/ts/constants.d.ts +32 -0
  80. package/dist/client/silex-lib/src/ts/page.d.ts +2 -0
  81. package/dist/client/silex-lib/src/ts/types.d.ts +339 -0
  82. package/dist/client/types.d.ts +59 -35
  83. package/dist/plugins/client/client/events.d.ts +19 -10
  84. package/dist/plugins/client/client/events.js +11 -0
  85. package/dist/plugins/client/client/events.js.map +1 -1
  86. package/dist/plugins/client/plugins/client/template.js +119 -71
  87. package/dist/plugins/client/plugins/client/template.js.map +1 -1
  88. package/dist/plugins/client/plugins/client/website-info.d.ts +1 -0
  89. package/dist/plugins/client/plugins/client/website-info.js +72 -0
  90. package/dist/plugins/client/plugins/client/website-info.js.map +1 -0
  91. package/dist/plugins/client/types.d.ts +59 -35
  92. package/dist/plugins/client/types.js +23 -0
  93. package/dist/plugins/client/types.js.map +1 -1
  94. package/dist/plugins/server/constants.d.ts +3 -0
  95. package/dist/plugins/server/constants.js +28 -1
  96. package/dist/plugins/server/constants.js.map +1 -1
  97. package/dist/plugins/server/plugins/server/Cache.js +9 -5
  98. package/dist/plugins/server/plugins/server/Cache.js.map +1 -1
  99. package/dist/plugins/server/plugins/server/DownloadConnector.d.ts +29 -0
  100. package/dist/plugins/server/plugins/server/DownloadConnector.js +127 -0
  101. package/dist/plugins/server/plugins/server/DownloadConnector.js.map +1 -0
  102. package/dist/plugins/server/plugins/server/FtpConnector.d.ts +5 -2
  103. package/dist/plugins/server/plugins/server/FtpConnector.js +38 -23
  104. package/dist/plugins/server/plugins/server/FtpConnector.js.map +1 -1
  105. package/dist/plugins/server/plugins/server/GitlabConnector.d.ts +63 -9
  106. package/dist/plugins/server/plugins/server/GitlabConnector.js +367 -91
  107. package/dist/plugins/server/plugins/server/GitlabConnector.js.map +1 -1
  108. package/dist/plugins/server/plugins/server/GitlabHostingConnector.d.ts +16 -0
  109. package/dist/plugins/server/plugins/server/GitlabHostingConnector.js +209 -0
  110. package/dist/plugins/server/plugins/server/GitlabHostingConnector.js.map +1 -0
  111. package/dist/plugins/server/plugins/server/SslPlugin.js +6 -6
  112. package/dist/plugins/server/plugins/server/SslPlugin.js.map +1 -1
  113. package/dist/plugins/server/plugins/server/StaticPlugin.d.ts +8 -1
  114. package/dist/plugins/server/plugins/server/StaticPlugin.js +5 -31
  115. package/dist/plugins/server/plugins/server/StaticPlugin.js.map +1 -1
  116. package/dist/plugins/server/server/config.js +8 -9
  117. package/dist/plugins/server/server/config.js.map +1 -1
  118. package/dist/plugins/server/server/connectors/FsHosting.d.ts +3 -2
  119. package/dist/plugins/server/server/connectors/FsHosting.js +18 -7
  120. package/dist/plugins/server/server/connectors/FsHosting.js.map +1 -1
  121. package/dist/plugins/server/server/connectors/FsStorage.d.ts +3 -1
  122. package/dist/plugins/server/server/connectors/FsStorage.js +35 -28
  123. package/dist/plugins/server/server/connectors/FsStorage.js.map +1 -1
  124. package/dist/plugins/server/server/connectors/connectors.d.ts +7 -7
  125. package/dist/plugins/server/server/connectors/connectors.js +6 -7
  126. package/dist/plugins/server/server/connectors/connectors.js.map +1 -1
  127. package/dist/plugins/server/server/events.js +16 -0
  128. package/dist/plugins/server/server/events.js.map +1 -1
  129. package/dist/plugins/server/server/jobs.d.ts +6 -0
  130. package/dist/plugins/server/server/jobs.js +12 -8
  131. package/dist/plugins/server/server/jobs.js.map +1 -1
  132. package/dist/plugins/server/server/utils/validation.js +1 -2
  133. package/dist/plugins/server/server/utils/validation.js.map +1 -1
  134. package/dist/plugins/server/types.d.ts +59 -35
  135. package/dist/plugins/server/types.js +24 -1
  136. package/dist/plugins/server/types.js.map +1 -1
  137. package/dist/server/constants.js +27 -1
  138. package/dist/server/plugins/server/Cache.js +9 -5
  139. package/dist/server/server/api/api.js +1 -1
  140. package/dist/server/server/api/connectorApi.js +154 -25
  141. package/dist/server/server/api/publicationApi.js +17 -25
  142. package/dist/server/server/api/websiteApi.js +30 -8
  143. package/dist/server/server/cli.js +9 -6
  144. package/dist/server/server/config.js +8 -9
  145. package/dist/server/server/connectors/FsHosting.js +17 -7
  146. package/dist/server/server/connectors/FsStorage.js +34 -28
  147. package/dist/server/server/connectors/connectors.js +6 -7
  148. package/dist/server/server/express.js +4 -5
  149. package/dist/server/server/index.js +5 -1
  150. package/dist/server/server/jobs.js +11 -8
  151. package/dist/server/server/utils/validation.js +1 -2
  152. package/dist/server/types.js +13 -1
  153. package/package.json +56 -42
  154. package/public/assets/download.png +0 -0
  155. package/public/assets/favicon.ico +0 -0
  156. package/public/assets/favicon.png +0 -0
  157. package/public/assets/ftp.png +0 -0
  158. package/public/assets/gitlab.png +0 -0
  159. package/public/assets/gitlab2.png +0 -0
  160. package/public/assets/laptop.png +0 -0
  161. package/public/assets/logo-silex-small.png +0 -0
  162. package/public/silex.js +5 -23
  163. package/src/css/_variables.scss +23 -20
  164. package/src/css/dialog.scss +27 -1
  165. package/src/css/fonts.scss +31 -0
  166. package/src/css/footer.scss +12 -0
  167. package/src/css/grapesjs-light.scss +180 -0
  168. package/src/css/grapesjs-plugins.scss +227 -0
  169. package/src/css/grapesjs.scss +84 -10
  170. package/src/css/index.scss +12 -1
  171. package/src/css/light.scss +7 -0
  172. package/src/css/project-bar.scss +32 -16
  173. package/src/css/silex.scss +79 -5
  174. package/src/css/website-info.scss +14 -0
  175. package/src/html/index.pug +3 -3
  176. package/src/ts/client/api.ts +38 -14
  177. package/src/ts/client/assetUrl.test.ts +164 -0
  178. package/src/ts/client/assetUrl.ts +254 -0
  179. package/src/ts/client/config.ts +133 -5
  180. package/src/ts/client/events.ts +21 -9
  181. package/src/ts/client/grapesjs/LoginDialog.ts +4 -2
  182. package/src/ts/client/grapesjs/PublicationManager.ts +180 -54
  183. package/src/ts/client/grapesjs/PublicationUi.ts +122 -86
  184. package/src/ts/client/grapesjs/breadcrumbs.ts +79 -0
  185. package/src/ts/client/grapesjs/css-props.ts +910 -0
  186. package/src/ts/client/grapesjs/eleventy.ts +11 -8
  187. package/{dist/plugins/client/page.js → src/ts/client/grapesjs/footer.ts} +24 -13
  188. package/src/ts/client/grapesjs/img.ts +147 -0
  189. package/src/ts/client/grapesjs/index.ts +234 -52
  190. package/src/ts/client/grapesjs/internal-links.test.ts +2 -1
  191. package/src/ts/client/grapesjs/internal-links.ts +14 -7
  192. package/src/ts/client/grapesjs/keymaps.ts +207 -0
  193. package/src/ts/client/grapesjs/new-page-dialog.ts +2 -3
  194. package/src/ts/client/grapesjs/page-panel.ts +92 -7
  195. package/src/ts/client/grapesjs/project-bar.ts +116 -72
  196. package/src/ts/client/grapesjs/resize-panel.ts +20 -0
  197. package/src/ts/client/grapesjs/rich-text.ts +21 -13
  198. package/src/ts/client/grapesjs/semantic.ts +3 -3
  199. package/src/ts/client/grapesjs/settings-sections.ts +117 -0
  200. package/src/ts/client/grapesjs/settings.ts +174 -96
  201. package/src/ts/client/grapesjs/storage.ts +98 -33
  202. package/src/ts/client/grapesjs/symbolDialogs.ts +3 -4
  203. package/src/ts/{plugins/server/AuthPlugin.ts → client/grapesjs/view-buttons.ts} +23 -19
  204. package/src/ts/client/index.ts +35 -16
  205. package/src/ts/client/publication-transformers.test.ts +207 -0
  206. package/src/ts/client/publication-transformers.ts +280 -0
  207. package/{dist/plugins/client/client/utils.js → src/ts/client/publish-custom-code-block.ts} +14 -8
  208. package/src/ts/client/publish-fonts.ts +36 -0
  209. package/src/ts/client/utils.ts +53 -1
  210. package/src/ts/constants.ts +31 -0
  211. package/src/ts/page.ts +2 -2
  212. package/src/ts/plugins/client/template.ts +125 -75
  213. package/src/ts/plugins/client/website-info.ts +84 -0
  214. package/src/ts/plugins/server/Cache.ts +6 -2
  215. package/src/ts/plugins/server/DownloadConnector.ts +146 -0
  216. package/src/ts/plugins/server/FtpConnector.ts +47 -21
  217. package/src/ts/plugins/server/GitlabConnector.ts +423 -108
  218. package/src/ts/plugins/server/GitlabHostingConnector.ts +215 -0
  219. package/src/ts/plugins/server/SslPlugin.ts +5 -5
  220. package/src/ts/plugins/server/StaticPlugin.ts +3 -30
  221. package/src/ts/server/api/connectorApi.ts +165 -28
  222. package/src/ts/server/api/publicationApi.ts +17 -24
  223. package/src/ts/server/api/websiteApi.ts +57 -14
  224. package/src/ts/server/cli.ts +7 -5
  225. package/src/ts/server/config.ts +9 -10
  226. package/src/ts/server/connectors/FsHosting.ts +14 -8
  227. package/src/ts/server/connectors/FsStorage.ts +40 -7
  228. package/src/ts/server/connectors/connectors.ts +8 -6
  229. package/src/ts/server/events.ts +17 -0
  230. package/src/ts/server/express.ts +2 -2
  231. package/src/ts/server/index.ts +6 -0
  232. package/src/ts/server/jobs.ts +14 -2
  233. package/src/ts/server/utils/BackwardCompat.ts +530 -529
  234. package/src/ts/types.ts +78 -34
  235. package/dist/plugins/client/client/utils.d.ts +0 -1
  236. package/dist/plugins/client/client/utils.js.map +0 -1
  237. package/dist/plugins/client/page.d.ts +0 -2
  238. package/dist/plugins/client/page.js.map +0 -1
  239. package/dist/plugins/client/plugins/client/publicationRenderer.d.ts +0 -2
  240. package/dist/plugins/client/plugins/client/publicationRenderer.js +0 -128
  241. package/dist/plugins/client/plugins/client/publicationRenderer.js.map +0 -1
  242. package/dist/plugins/server/plugins/server/AuthPlugin.d.ts +0 -1
  243. package/dist/plugins/server/plugins/server/AuthPlugin.js +0 -38
  244. package/dist/plugins/server/plugins/server/AuthPlugin.js.map +0 -1
  245. package/dist/plugins/server/plugins/server/HooksPlugin.d.ts +0 -7
  246. package/dist/plugins/server/plugins/server/HooksPlugin.js +0 -74
  247. package/dist/plugins/server/plugins/server/HooksPlugin.js.map +0 -1
  248. package/src/example-client-config.js +0 -64
  249. package/src/ts/plugins/client/publicationRenderer.ts +0 -128
  250. package/src/ts/plugins/server/HooksPlugin.ts +0 -76
@@ -0,0 +1,215 @@
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
+ /**
19
+ * Gitlab hosting connector
20
+ * @fileoverview Gitlab hosting connector for Silex, connect to the user's Gitlab account to host websites with .gitlab-ci.yml file template for plain html by default
21
+ * @see https://docs.gitlab.com/ee/api/oauth2.html
22
+ * @see https://docs.gitlab.com/ee/user/project/pages/getting_started/pages_ci_cd_template.html
23
+ */
24
+
25
+ import dedent from 'dedent'
26
+ import GitlabConnector, { GitlabOptions, GitlabSession} from './GitlabConnector'
27
+ import { HostingConnector, ConnectorFile } from '../../server/connectors/connectors'
28
+ import { ConnectorType, WebsiteId, JobData, JobStatus, PublicationJobData } from '../../types'
29
+ import { JobManager } from '../../server/jobs'
30
+ import { ServerConfig } from '../../server/config'
31
+ import { setTimeout } from 'timers/promises'
32
+
33
+ const waitTimeOut = 5000 /* for wait loop in job pages getting */
34
+ const SILEX_OVERWRITE_NOTICE = '# silexOverwrite: true'
35
+
36
+ export default class GitlabHostingConnector extends GitlabConnector implements HostingConnector {
37
+
38
+ displayName = 'Gitlab hosting'
39
+ connectorType = ConnectorType.HOSTING
40
+
41
+ constructor( config: ServerConfig, opts: Partial<GitlabOptions>) {
42
+ super (config, opts)
43
+ // public directory for standard gitlab pages
44
+ this.options.assetsFolder= 'public'
45
+ }
46
+
47
+ async publish(session: GitlabSession, websiteId: WebsiteId, files: ConnectorFile[], {startJob, jobSuccess, jobError}: JobManager): Promise<JobData> {
48
+ const job = startJob(`Publishing to ${this.displayName}`) as PublicationJobData
49
+ job.logs = [[`Publishing to ${this.displayName}`]]
50
+ job.errors = [[]]
51
+ /* Configuration file .gitlab-ci.yml contains template for plain html Gitlab pages*/
52
+ const pathYml = '.gitlab-ci.yml'
53
+ const contentYml = dedent`
54
+ ${SILEX_OVERWRITE_NOTICE}
55
+ # You will want to remove these lines if you customize your build process
56
+ # Silex will overwrite this file unless you remove these lines
57
+ # This is the default build process for plain eleventy sites
58
+ image: node:20
59
+ pages:
60
+ stage: deploy
61
+ environment: production
62
+ script:${files.find(file => file.path.includes('.11tydata.')) ? `
63
+ - npx @11ty/eleventy@v3.0.0-alpha.20 --input=public --output=_site
64
+ - mkdir -p public/css public/assets && cp -R public/css public/assets _site/
65
+ - rm -rf public && mv _site public`
66
+ : ''}
67
+ - echo "The site will be deployed to $CI_PAGES_URL"
68
+ artifacts:
69
+ paths:
70
+ - public
71
+ rules:
72
+ - if: '$CI_COMMIT_TAG'
73
+ - if: '$CI_PIPELINE_SOURCE == "trigger"'
74
+ `
75
+ try {
76
+ const originalCi = await this.readFile(session, websiteId, pathYml)
77
+ if(originalCi.toString().startsWith(SILEX_OVERWRITE_NOTICE)) {
78
+ await this.updateFile(session, websiteId, pathYml, contentYml)
79
+ }
80
+ } catch (e) {
81
+ // If the file .gitlab-ci.yml does not exist, create it, otherwise do nothing (do not overwriting existing one)
82
+ if (e.statusCode === 404 || e.httpStatusCode === 404 || e.message.endsWith('A file with this name doesn\'t exist')) {
83
+ await this.createFile(session, websiteId, pathYml, contentYml)
84
+ } else {
85
+ jobError(job.jobId, e.message)
86
+ }
87
+ }
88
+
89
+ // publishing all files for website
90
+ // Do not await for the result, return the job and continue the publication in the background
91
+ this.writeAssets(session, websiteId, files, async ({status, message}) => {
92
+ // Update the job status
93
+ if(status === JobStatus.SUCCESS) {
94
+ /* Squash and tag the commits */
95
+ const successTag = await this.createTag(session, websiteId, job, { startJob, jobSuccess, jobError })
96
+ if(!successTag) {
97
+ // jobError will have been called in createTag
98
+ return
99
+ }
100
+ try {
101
+ job.message = 'Getting the website URL...'
102
+ job.logs[0].push(job.message)
103
+ const gitlabUrl = await this.getUrl(session, websiteId)
104
+ job.logs[0].push(`Website URL: ${gitlabUrl}`)
105
+ job.message = 'Getting the admin URL...'
106
+ job.logs[0].push(job.message)
107
+ const adminUrl = await this.getAdminUrl(session, websiteId)
108
+ job.logs[0].push(`Admin URL: ${adminUrl}`)
109
+ job.message = 'Getting the page URL...'
110
+ job.logs[0].push(job.message)
111
+ const pageUrl = await this.getPageUrl(session, websiteId, adminUrl)
112
+ job.logs[0].push(`Page URL: ${pageUrl}`)
113
+ job.message = 'Getting the deployment logs URL...'
114
+ job.logs[0].push(job.message)
115
+ const gitlabJobLogsUrl = await this.getGitlabJobLogsUrl(session, websiteId, job, { startJob, jobSuccess, jobError }, adminUrl, successTag)
116
+ // Because of the GitLab policy, this can be null (and we suggest the user to verify their account)
117
+ if (!gitlabJobLogsUrl) {
118
+ let errorMessage = 'Could not retrieve the deployment logs URL.'
119
+
120
+ if (this.isUsingOfficialInstance()) {
121
+ const verifyURL = 'https://gitlab.com/-/identity_verification'
122
+ errorMessage +=
123
+ `<div class="notice">
124
+ If your GitLab account is recent, you may need to verify it <a href="${verifyURL}" target="_blank">here</a>
125
+ in order to be able to use pipelines (this is GitLab's policy, not Silex's).
126
+ </div>`
127
+ }
128
+
129
+ throw new Error(errorMessage)
130
+ }
131
+ job.logs[0].push(`Deployment logs URL: ${gitlabJobLogsUrl}`)
132
+ const message = `
133
+ <p><a href="${gitlabUrl}" target="_blank">Your website will soon be live here</a>.</p>
134
+ <p>Changes may take a few minutes to appear, <a href="${gitlabJobLogsUrl}" target="_blank">follow deployment here</a>.</p>
135
+ <p>Manage <a href="${pageUrl}" target="_blank">GitLab Pages settings</a>.</p>
136
+ `
137
+ job.logs[0].push(message)
138
+ jobSuccess(job.jobId, message)
139
+ } catch (error) {
140
+ console.error('Error during getting the website URLs:', error.message)
141
+ jobError(job.jobId, `Failed to get the website URLs: ${error.message}`)
142
+ }
143
+ } else if (status === JobStatus.ERROR) {
144
+ job.errors[0].push(message)
145
+ jobError(job.jobId, message)
146
+ } else {
147
+ // Update the job status while uploading files
148
+ job.status = status
149
+ job.message = message
150
+ job.logs[0].push(message)
151
+ }
152
+ })
153
+ .catch(e => {
154
+ console.error('Error uploading files to gitlab:', e.message)
155
+ jobError(job.jobId, `Failed to upload files: ${e.message}`)
156
+ })
157
+
158
+ return job
159
+ }
160
+
161
+ /* Get and return Url Gitlab Pages */
162
+ async getUrl(session: GitlabSession, websiteId: WebsiteId): Promise<string> {
163
+ const response = await this.callApi(session, `api/v4/projects/${websiteId}/pages`, 'GET')
164
+ return response.url
165
+ }
166
+
167
+ async getAdminUrl(session: GitlabSession, websiteId: WebsiteId): Promise<string> {
168
+ const projectInfo = await this.callApi(session, `api/v4/projects/${websiteId}`, 'GET')
169
+ return projectInfo.web_url
170
+ }
171
+
172
+ async getPageUrl(session: GitlabSession, websiteId: WebsiteId, projectUrl: string): Promise<string> {
173
+ return `${projectUrl}/pages`
174
+ }
175
+
176
+ // waiting for the job corresponding to the current tag
177
+ async getGitlabJobLogsUrl(session: GitlabSession, websiteId: WebsiteId, job: PublicationJobData, { startJob, jobSuccess, jobError }: JobManager, projectUrl: string, tag): Promise<string | null> {
178
+ const t0 = Date.now()
179
+ do {
180
+ const jobs = await this.callApi(session, `api/v4/projects/${websiteId}/jobs`, 'GET')
181
+ if (!jobs.length) return null
182
+ if (jobs[0].ref === tag) {return `${projectUrl}/-/jobs/${jobs[0].id}`}
183
+ await setTimeout(waitTimeOut)
184
+ } while ((Date.now() - t0) < this.options.timeOut)
185
+
186
+ // failed in timelaps allowed (avoiding infinite loop)
187
+ jobError(job.jobId, 'Failed to get job id')
188
+ job.message = 'Unable to get job id'
189
+ job.logs[0].push(job.message)
190
+ return `${projectUrl}/-/jobs/`
191
+ }
192
+
193
+ async createTag(session: GitlabSession, websiteId: WebsiteId, job: PublicationJobData, { startJob, jobSuccess, jobError }: JobManager): Promise<string | null> {
194
+ const projectId = websiteId // Assuming websiteId corresponds to GitLab project ID
195
+ const newTag = '_silex_' + Date.now()
196
+ // Create a new tag
197
+ try {
198
+ job.message = `Creating new tag ${newTag}...`
199
+ job.logs[0].push(job.message)
200
+ await this.callApi(session, `api/v4/projects/${projectId}/repository/tags`, 'POST', {
201
+ tag_name: newTag,
202
+ ref: 'main',
203
+ message: 'Publication from Silex',
204
+ })
205
+ } catch (error) {
206
+ console.error('Error during creating new tag:', error.message)
207
+ jobError(job.jobId, `Failed to create new tag: ${error.message}`)
208
+ return null
209
+ }
210
+
211
+ // Return new tag
212
+ return newTag
213
+ }
214
+
215
+ }
@@ -47,18 +47,18 @@ export default async function(config, opts = {}) {
47
47
  // SSL
48
48
  // force ssl if the env var SILEX_FORCE_HTTPS is set
49
49
  if (options.forceHttps) {
50
- console.log('> [SslPlugin] Force SSL option is enabled')
50
+ console.info('> [SslPlugin] Force SSL option is enabled')
51
51
  app.set('forceSSLOptions', {
52
52
  trustXFPHeader: !!options.trustXFPHeader,
53
53
  })
54
54
  router.use(forceSSL)
55
55
  } else {
56
- console.log('> [SslPlugin] Force SSL option is disabled, env var SILEX_FORCE_HTTPS not set')
56
+ console.info('> [SslPlugin] Force SSL option is disabled, env var SILEX_FORCE_HTTPS not set')
57
57
  }
58
58
 
59
59
  // SSL certificate
60
60
  if (options.privateKey && options.certificate) {
61
- console.log('> [SslPlugin] SSL certificate is enabled, found certificate:', options.certificate)
61
+ console.info('> [SslPlugin] SSL certificate is enabled, found certificate:', options.certificate)
62
62
  try {
63
63
  const privateKey = readFileSync(options.privateKey).toString()
64
64
  const certificate = readFileSync(options.certificate).toString()
@@ -71,13 +71,13 @@ export default async function(config, opts = {}) {
71
71
  }
72
72
 
73
73
  createServer(sslServerOptions, this).listen(options.sslPort, () => {
74
- console.log('> [SslPlugin] Listening on port ', options.sslPort)
74
+ console.info('> [SslPlugin] Listening on port ', options.sslPort)
75
75
  })
76
76
  } catch (e) {
77
77
  console.error('> [SslPlugin] Load certificate failed.', e)
78
78
  }
79
79
  } else {
80
- console.log('> [SslPlugin] Certificate disabled, env vars SILEX_SSL_CERTIFICATE and SILEX_SSL_PRIVATE_KEY not set')
80
+ console.info('> [SslPlugin] Certificate disabled, env vars SILEX_SSL_CERTIFICATE and SILEX_SSL_PRIVATE_KEY not set')
81
81
  }
82
82
  app.use(router)
83
83
  })
@@ -16,10 +16,8 @@
16
16
  */
17
17
 
18
18
  import express from 'express'
19
- import nodeModules from 'node_modules-path'
20
19
  import serveStatic from 'serve-static'
21
20
  import { withCache } from './Cache'
22
- import { join } from 'path'
23
21
  import { ServerEvent } from '../../server/events'
24
22
 
25
23
  type StaticOptions = {
@@ -29,34 +27,9 @@ type StaticOptions = {
29
27
  }[]
30
28
  }
31
29
 
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: '/node_modules/',
47
- path: nodeModules(),
48
- }, {
49
- route: '/',
50
- path: join(__dirname, '../../../../..', 'dist', 'client'),
51
- },
52
- ]
53
- // add project route for source maps
54
- .concat(config.debug ? [{
55
- route: '/',
56
- path: './',
57
- }] : []),
58
- ...opts,
59
- }
30
+ export default async function(config, options: StaticOptions = { routes: [] }) {
31
+ if(!options.routes) throw new Error('The config for static module has no `routes` attribute')
32
+ console.info(`> [StaticPlugin] Serving ${options.routes.length} static files`)
60
33
 
61
34
  config.on(ServerEvent.STARTUP_START, ({app}) => {
62
35
  const router = express.Router()
@@ -3,7 +3,7 @@ import { API_CONNECTOR_LIST, API_CONNECTOR_LOGIN_CALLBACK, API_CONNECTOR_USER, A
3
3
  import { ServerConfig } from '../config'
4
4
  import { requiredParam } from '../utils/validation'
5
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'
6
+ import { ApiConnectorListQuery, ApiConnectorListResponse, ApiConnectorLoggedInPostMessage, ApiConnectorLoginQuery, ApiConnectorUserQuery, ApiConnectorUserResponse, ApiConnectorLogoutQuery, ConnectorId, ApiConnectorLoginCbkQuery, ApiConnectorLoginCbkBody, ConnectorOptions, ApiResponseError, ConnectorType } from '../../types'
7
7
 
8
8
  /**
9
9
  * @fileoverview The connector API adds routes to handle the connectors and the methods they implement, this includes authentication and user data.
@@ -45,6 +45,24 @@ export default function(config: ServerConfig) {
45
45
  return router
46
46
  }
47
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
+
48
66
  /**
49
67
  * Express route to check if the user is logged in
50
68
  * Returns user data and connector data
@@ -78,8 +96,8 @@ async function routeUser(req: Request, res: Response) {
78
96
  const user = await connector.getUser(session)
79
97
  res.json(user as ApiConnectorUserResponse)
80
98
  } catch (error) {
81
- console.error('Error in the login status request', error)
82
- res.status(error?.code ?? error?.httpStatusCode ?? 500).json({
99
+ console.error('Error in the user request', error, error.code)
100
+ res.status(validateStatus(error.code ?? error.httpStatusCode, 500)).json({
83
101
  error: true,
84
102
  message: error.message,
85
103
  } as ApiResponseError)
@@ -101,14 +119,14 @@ async function routeListConnectors(req: Request, res: Response) {
101
119
  res.json(list as ApiConnectorListResponse)
102
120
  } catch (error) {
103
121
  console.error('Error while listing connectors', error)
104
- res.status(error?.code ?? error?.httpStatusCode ?? 500).json({
122
+ res.status(validateStatus(error?.code ?? error?.httpStatusCode, 500)).json({
105
123
  error: true,
106
124
  message: 'Error while listing connectors: ' + error.message,
107
125
  } as ApiResponseError)
108
126
  }
109
127
  } catch (error) {
110
128
  console.error('Error in the list connectors request', error)
111
- res.status(error?.code ?? error?.httpStatusCode ?? 400).json({
129
+ res.status(validateStatus(error?.code ?? error?.httpStatusCode, 400)).json({
112
130
  error: true,
113
131
  message: 'Error in the list connectors request: ' + error.message,
114
132
  } as ApiResponseError)
@@ -132,7 +150,7 @@ async function routeLogin(req: Request, res: Response) {
132
150
  if (!connector) throw new Error(`Connector not found ${connectorId} ${type}`)
133
151
  // Check if the user is already logged in
134
152
  if (await connector.isLoggedIn(session)) {
135
- res.redirect(`${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN_CALLBACK}?connectorId=${connectorId}&type=${type}`)
153
+ res.redirect(`${config.url}${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN_CALLBACK}?connectorId=${connectorId}&type=${type}`)
136
154
  return
137
155
  }
138
156
  const oauthUrl = await connector.getOAuthUrl(session)
@@ -142,13 +160,13 @@ async function routeLogin(req: Request, res: Response) {
142
160
  } else {
143
161
  // Display the login form
144
162
  const redirectTo = await connector.getSettingsForm(session, '') ?
145
- `${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_SETTINGS}?connectorId=${connectorId}&type=${type}` :
146
- `${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN_CALLBACK}?connectorId=${connectorId}&type=${type}`
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}`
147
165
  res.send(await connector.getLoginForm(session, redirectTo))
148
166
  }
149
167
  } catch (error) {
150
168
  console.error('Error in the login request', error)
151
- res.status(error?.code ?? error?.httpStatusCode ?? 400).json({
169
+ res.status(validateStatus(error?.code ?? error?.httpStatusCode, 400)).json({
152
170
  error: true,
153
171
  message: 'Error in the login request: ' + error.message,
154
172
  } as ApiResponseError)
@@ -171,7 +189,7 @@ async function routeLogin(req: Request, res: Response) {
171
189
  // if (!connector) throw new Error(`Connector not found ${connectorId} ${type}`)
172
190
  // // Check if the user is already logged in
173
191
  // if (!await connector.isLoggedIn(session)) {
174
- // res.redirect(`${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN_CALLBACK}?connectorId=${connectorId}&type=${type}&error=Not logged in`)
192
+ // res.redirect(`${config.url}${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN_CALLBACK}?connectorId=${connectorId}&type=${type}&error=Not logged in`)
175
193
  // return
176
194
  // }
177
195
  // // Display the login form
@@ -202,7 +220,7 @@ async function routeLogin(req: Request, res: Response) {
202
220
  // if (!connector) throw new Error(`Connector not found ${connectorId} ${type}`)
203
221
  // // Check if the user is already logged in
204
222
  // if (!await connector.isLoggedIn(session)) {
205
- // res.redirect(`${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN_CALLBACK}?connectorId=${connectorId}&type=${type}&error=Not logged in`)
223
+ // res.redirect(`${config.url}${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN_CALLBACK}?connectorId=${connectorId}&type=${type}&error=Not logged in`)
206
224
  // return
207
225
  // }
208
226
  // // Save the settings
@@ -245,11 +263,11 @@ async function routeLoginSuccess(req: Request, res: Response) {
245
263
  })
246
264
  }
247
265
  // End the auth flow
248
- res.send(getEndAuthHtml('Logged in', false, connectorId, connector.getOptions(body)))
266
+ res.send(getEndAuthHtml('Logged in', false, connectorId, type, connector.getOptions(body)))
249
267
  } catch (error) {
250
268
  res
251
- .status(error?.code ?? error?.httpStatusCode ?? 500)
252
- .send(getEndAuthHtml(error.message, true))
269
+ .status(validateStatus(error?.code ?? error?.httpStatusCode, 500))
270
+ .send(getEndAuthHtml(error.message, true, req.query.connectorId as ConnectorId, req.query.type as ConnectorType))
253
271
  }
254
272
  }
255
273
 
@@ -276,7 +294,7 @@ async function routeLogout(req: Request, res: Response) {
276
294
  } as ApiResponseError)
277
295
  } catch (error) {
278
296
  console.error('Error while logging out', error)
279
- res.status(error?.code ?? error?.httpStatusCode ?? 500).json({
297
+ res.status(validateStatus(error?.code ?? error?.httpStatusCode, 500)).json({
280
298
  error: true,
281
299
  message: 'Error while logging out: ' + error.message,
282
300
  } as ApiResponseError)
@@ -284,7 +302,7 @@ async function routeLogout(req: Request, res: Response) {
284
302
  }
285
303
  } catch (error) {
286
304
  console.error('Error in the logout request', error)
287
- res.status(error?.code ?? error?.httpStatusCode ?? 400).json({
305
+ res.status(validateStatus(error?.code ?? error?.httpStatusCode, 400)).json({
288
306
  error: true,
289
307
  message: 'Error in the logout request: ' + error.message,
290
308
  } as ApiResponseError)
@@ -296,31 +314,150 @@ async function routeLogout(req: Request, res: Response) {
296
314
  * Utility function to send an HTML page to the browser
297
315
  * is page will send a postMessage to the parent window and close itself
298
316
  */
299
- function getEndAuthHtml(message: string, error: boolean, connectorId?: ConnectorId, options?: ConnectorOptions): string {
317
+ function getEndAuthHtml(message: string, error: boolean, connectorId: ConnectorId, connectorType: ConnectorType, options?: ConnectorOptions): string {
300
318
  // Data for postMessage
301
319
  const data = {
302
320
  type: 'login', // For postMessage
303
321
  error,
304
322
  message,
305
323
  connectorId,
324
+ connectorType,
306
325
  options,
307
326
  } as ApiConnectorLoggedInPostMessage
308
- // Send the HTML
327
+ // Determine status title and heading based on the error
328
+ const status = error ? 'Error' : 'Success'
329
+ // Return the HTML template
309
330
  return `
310
- <html>
331
+ <!DOCTYPE html><html lang="en">
311
332
  <head>
312
- <script>
313
- if(window.opener) {
314
- window.opener.postMessage(${JSON.stringify(data)}, '*')
315
- window.close()
316
- } else {
317
- window.location.href = '/'
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;
318
363
  }
319
- </script>
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>
320
401
  </head>
321
402
  <body>
322
- <p>${message}</p>
323
- <p>Close this window or <a href="/">go back to /</a></p>
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>
324
461
  </body>
325
462
  </html>
326
463
  `