@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.
- package/.env.default +3 -2
- package/.silex.js +39 -1
- package/README.md +37 -35
- package/dist/client/client/api.d.ts +8 -1
- package/dist/client/client/assetUrl.d.ts +54 -0
- package/dist/client/client/config.d.ts +60 -1
- package/dist/client/client/events.d.ts +19 -10
- package/dist/client/client/grapesjs/PublicationManager.d.ts +6 -8
- package/dist/client/client/grapesjs/PublicationUi.d.ts +8 -10
- package/dist/client/client/grapesjs/breadcrumbs.d.ts +1 -0
- package/dist/client/client/grapesjs/css-props.d.ts +6 -0
- package/dist/client/client/grapesjs/footer.d.ts +2 -0
- package/dist/client/client/grapesjs/img.d.ts +6 -0
- package/dist/client/client/grapesjs/index.d.ts +6 -2
- package/dist/client/client/grapesjs/internal-links.d.ts +1 -1
- package/dist/client/client/grapesjs/keymaps.d.ts +123 -0
- package/dist/client/client/grapesjs/new-page-dialog.d.ts +1 -1
- package/dist/client/client/grapesjs/page-panel.d.ts +7 -1
- package/dist/client/client/grapesjs/project-bar.d.ts +20 -0
- package/dist/client/client/grapesjs/resize-panel.d.ts +1 -0
- package/dist/client/client/grapesjs/rich-text.d.ts +4 -1
- package/dist/client/client/grapesjs/semantic.d.ts +1 -1
- package/dist/client/client/grapesjs/settings-sections.d.ts +16 -0
- package/dist/client/client/grapesjs/settings.d.ts +3 -1
- package/dist/client/client/grapesjs/storage.d.ts +1 -0
- package/dist/client/client/grapesjs/symbolDialogs.d.ts +1 -1
- package/dist/client/client/grapesjs/view-buttons.d.ts +1 -0
- package/dist/client/client/index.d.ts +5 -0
- package/dist/client/client/publication-transformers.d.ts +42 -0
- package/dist/client/client/publish-custom-code-block.d.ts +4 -0
- package/dist/client/client/publish-fonts.d.ts +4 -0
- package/dist/client/client/utils.d.ts +22 -1
- package/dist/client/constants.d.ts +3 -0
- package/dist/client/css/admin.css +1107 -411
- package/dist/client/css/admin.css.map +1 -0
- package/dist/client/grapesjs-filter-styles/dist/index.d.ts +4 -0
- package/dist/client/grapesjs-fonts/dist/index.d.ts +4 -0
- package/dist/client/grapesjs-fonts/src/commands.d.ts +3 -0
- package/dist/client/grapesjs-fonts/src/fonts.d.ts +4 -0
- package/dist/client/grapesjs-storage-rate-limit/dist/index.d.ts +4 -0
- package/dist/client/grapesjs-ui-suggest-classes/dist/index.d.ts +4 -0
- package/dist/client/index.html +3 -2
- package/dist/client/js/main.js +305 -170
- package/dist/client/js/main.js.LICENSE.txt +21 -1
- package/dist/client/page.d.ts +1 -1
- package/dist/client/silex-lib/src/ts/client/api.d.ts +90 -0
- package/dist/client/silex-lib/src/ts/client/assetUrl.d.ts +54 -0
- package/dist/client/silex-lib/src/ts/client/config.d.ts +102 -0
- package/dist/client/silex-lib/src/ts/client/events.d.ts +23 -0
- package/dist/client/silex-lib/src/ts/client/expose.d.ts +8 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/LoginDialog.d.ts +15 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/PublicationManager.d.ts +73 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/PublicationUi.d.ts +59 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/blocks.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/breadcrumbs.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/css-props.d.ts +6 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/footer.d.ts +2 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/img.d.ts +6 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/index.d.ts +9 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/internal-links.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/keymaps.d.ts +123 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/new-page-dialog.d.ts +2 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/openImport.d.ts +9 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/page-panel.d.ts +9 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/project-bar.d.ts +21 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/resize-panel.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/rich-text.d.ts +4 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/semantic.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/settings-sections.d.ts +16 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/settings.d.ts +4 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/storage.d.ts +2 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/symbolDialogs.d.ts +3 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/view-buttons.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/index.d.ts +10 -0
- package/dist/client/silex-lib/src/ts/client/publication-transformers.d.ts +42 -0
- package/dist/client/silex-lib/src/ts/client/publish-custom-code-block.d.ts +4 -0
- package/dist/client/silex-lib/src/ts/client/publish-fonts.d.ts +4 -0
- package/dist/client/silex-lib/src/ts/client/utils.d.ts +22 -0
- package/dist/client/silex-lib/src/ts/constants.d.ts +32 -0
- package/dist/client/silex-lib/src/ts/page.d.ts +2 -0
- package/dist/client/silex-lib/src/ts/types.d.ts +339 -0
- package/dist/client/types.d.ts +59 -35
- package/dist/plugins/client/client/events.d.ts +19 -10
- package/dist/plugins/client/client/events.js +11 -0
- package/dist/plugins/client/client/events.js.map +1 -1
- package/dist/plugins/client/plugins/client/template.js +119 -71
- package/dist/plugins/client/plugins/client/template.js.map +1 -1
- package/dist/plugins/client/plugins/client/website-info.d.ts +1 -0
- package/dist/plugins/client/plugins/client/website-info.js +72 -0
- package/dist/plugins/client/plugins/client/website-info.js.map +1 -0
- package/dist/plugins/client/types.d.ts +59 -35
- package/dist/plugins/client/types.js +23 -0
- package/dist/plugins/client/types.js.map +1 -1
- package/dist/plugins/server/constants.d.ts +3 -0
- package/dist/plugins/server/constants.js +28 -1
- package/dist/plugins/server/constants.js.map +1 -1
- package/dist/plugins/server/plugins/server/Cache.js +9 -5
- package/dist/plugins/server/plugins/server/Cache.js.map +1 -1
- package/dist/plugins/server/plugins/server/DownloadConnector.d.ts +29 -0
- package/dist/plugins/server/plugins/server/DownloadConnector.js +127 -0
- package/dist/plugins/server/plugins/server/DownloadConnector.js.map +1 -0
- package/dist/plugins/server/plugins/server/FtpConnector.d.ts +5 -2
- package/dist/plugins/server/plugins/server/FtpConnector.js +38 -23
- package/dist/plugins/server/plugins/server/FtpConnector.js.map +1 -1
- package/dist/plugins/server/plugins/server/GitlabConnector.d.ts +63 -9
- package/dist/plugins/server/plugins/server/GitlabConnector.js +367 -91
- package/dist/plugins/server/plugins/server/GitlabConnector.js.map +1 -1
- package/dist/plugins/server/plugins/server/GitlabHostingConnector.d.ts +16 -0
- package/dist/plugins/server/plugins/server/GitlabHostingConnector.js +209 -0
- package/dist/plugins/server/plugins/server/GitlabHostingConnector.js.map +1 -0
- package/dist/plugins/server/plugins/server/SslPlugin.js +6 -6
- package/dist/plugins/server/plugins/server/SslPlugin.js.map +1 -1
- package/dist/plugins/server/plugins/server/StaticPlugin.d.ts +8 -1
- package/dist/plugins/server/plugins/server/StaticPlugin.js +5 -31
- package/dist/plugins/server/plugins/server/StaticPlugin.js.map +1 -1
- package/dist/plugins/server/server/config.js +8 -9
- package/dist/plugins/server/server/config.js.map +1 -1
- package/dist/plugins/server/server/connectors/FsHosting.d.ts +3 -2
- package/dist/plugins/server/server/connectors/FsHosting.js +18 -7
- package/dist/plugins/server/server/connectors/FsHosting.js.map +1 -1
- package/dist/plugins/server/server/connectors/FsStorage.d.ts +3 -1
- package/dist/plugins/server/server/connectors/FsStorage.js +35 -28
- package/dist/plugins/server/server/connectors/FsStorage.js.map +1 -1
- package/dist/plugins/server/server/connectors/connectors.d.ts +7 -7
- package/dist/plugins/server/server/connectors/connectors.js +6 -7
- package/dist/plugins/server/server/connectors/connectors.js.map +1 -1
- package/dist/plugins/server/server/events.js +16 -0
- package/dist/plugins/server/server/events.js.map +1 -1
- package/dist/plugins/server/server/jobs.d.ts +6 -0
- package/dist/plugins/server/server/jobs.js +12 -8
- package/dist/plugins/server/server/jobs.js.map +1 -1
- package/dist/plugins/server/server/utils/validation.js +1 -2
- package/dist/plugins/server/server/utils/validation.js.map +1 -1
- package/dist/plugins/server/types.d.ts +59 -35
- package/dist/plugins/server/types.js +24 -1
- package/dist/plugins/server/types.js.map +1 -1
- package/dist/server/constants.js +27 -1
- package/dist/server/plugins/server/Cache.js +9 -5
- package/dist/server/server/api/api.js +1 -1
- package/dist/server/server/api/connectorApi.js +154 -25
- package/dist/server/server/api/publicationApi.js +17 -25
- package/dist/server/server/api/websiteApi.js +30 -8
- package/dist/server/server/cli.js +9 -6
- package/dist/server/server/config.js +8 -9
- package/dist/server/server/connectors/FsHosting.js +17 -7
- package/dist/server/server/connectors/FsStorage.js +34 -28
- package/dist/server/server/connectors/connectors.js +6 -7
- package/dist/server/server/express.js +4 -5
- package/dist/server/server/index.js +5 -1
- package/dist/server/server/jobs.js +11 -8
- package/dist/server/server/utils/validation.js +1 -2
- package/dist/server/types.js +13 -1
- package/package.json +56 -42
- package/public/assets/download.png +0 -0
- package/public/assets/favicon.ico +0 -0
- package/public/assets/favicon.png +0 -0
- package/public/assets/ftp.png +0 -0
- package/public/assets/gitlab.png +0 -0
- package/public/assets/gitlab2.png +0 -0
- package/public/assets/laptop.png +0 -0
- package/public/assets/logo-silex-small.png +0 -0
- package/public/silex.js +5 -23
- package/src/css/_variables.scss +23 -20
- package/src/css/dialog.scss +27 -1
- package/src/css/fonts.scss +31 -0
- package/src/css/footer.scss +12 -0
- package/src/css/grapesjs-light.scss +180 -0
- package/src/css/grapesjs-plugins.scss +227 -0
- package/src/css/grapesjs.scss +84 -10
- package/src/css/index.scss +12 -1
- package/src/css/light.scss +7 -0
- package/src/css/project-bar.scss +32 -16
- package/src/css/silex.scss +79 -5
- package/src/css/website-info.scss +14 -0
- package/src/html/index.pug +3 -3
- package/src/ts/client/api.ts +38 -14
- package/src/ts/client/assetUrl.test.ts +164 -0
- package/src/ts/client/assetUrl.ts +254 -0
- package/src/ts/client/config.ts +133 -5
- package/src/ts/client/events.ts +21 -9
- package/src/ts/client/grapesjs/LoginDialog.ts +4 -2
- package/src/ts/client/grapesjs/PublicationManager.ts +180 -54
- package/src/ts/client/grapesjs/PublicationUi.ts +122 -86
- package/src/ts/client/grapesjs/breadcrumbs.ts +79 -0
- package/src/ts/client/grapesjs/css-props.ts +910 -0
- package/src/ts/client/grapesjs/eleventy.ts +11 -8
- package/{dist/plugins/client/page.js → src/ts/client/grapesjs/footer.ts} +24 -13
- package/src/ts/client/grapesjs/img.ts +147 -0
- package/src/ts/client/grapesjs/index.ts +234 -52
- package/src/ts/client/grapesjs/internal-links.test.ts +2 -1
- package/src/ts/client/grapesjs/internal-links.ts +14 -7
- package/src/ts/client/grapesjs/keymaps.ts +207 -0
- package/src/ts/client/grapesjs/new-page-dialog.ts +2 -3
- package/src/ts/client/grapesjs/page-panel.ts +92 -7
- package/src/ts/client/grapesjs/project-bar.ts +116 -72
- package/src/ts/client/grapesjs/resize-panel.ts +20 -0
- package/src/ts/client/grapesjs/rich-text.ts +21 -13
- package/src/ts/client/grapesjs/semantic.ts +3 -3
- package/src/ts/client/grapesjs/settings-sections.ts +117 -0
- package/src/ts/client/grapesjs/settings.ts +174 -96
- package/src/ts/client/grapesjs/storage.ts +98 -33
- package/src/ts/client/grapesjs/symbolDialogs.ts +3 -4
- package/src/ts/{plugins/server/AuthPlugin.ts → client/grapesjs/view-buttons.ts} +23 -19
- package/src/ts/client/index.ts +35 -16
- package/src/ts/client/publication-transformers.test.ts +207 -0
- package/src/ts/client/publication-transformers.ts +280 -0
- package/{dist/plugins/client/client/utils.js → src/ts/client/publish-custom-code-block.ts} +14 -8
- package/src/ts/client/publish-fonts.ts +36 -0
- package/src/ts/client/utils.ts +53 -1
- package/src/ts/constants.ts +31 -0
- package/src/ts/page.ts +2 -2
- package/src/ts/plugins/client/template.ts +125 -75
- package/src/ts/plugins/client/website-info.ts +84 -0
- package/src/ts/plugins/server/Cache.ts +6 -2
- package/src/ts/plugins/server/DownloadConnector.ts +146 -0
- package/src/ts/plugins/server/FtpConnector.ts +47 -21
- package/src/ts/plugins/server/GitlabConnector.ts +423 -108
- package/src/ts/plugins/server/GitlabHostingConnector.ts +215 -0
- package/src/ts/plugins/server/SslPlugin.ts +5 -5
- package/src/ts/plugins/server/StaticPlugin.ts +3 -30
- package/src/ts/server/api/connectorApi.ts +165 -28
- package/src/ts/server/api/publicationApi.ts +17 -24
- package/src/ts/server/api/websiteApi.ts +57 -14
- package/src/ts/server/cli.ts +7 -5
- package/src/ts/server/config.ts +9 -10
- package/src/ts/server/connectors/FsHosting.ts +14 -8
- package/src/ts/server/connectors/FsStorage.ts +40 -7
- package/src/ts/server/connectors/connectors.ts +8 -6
- package/src/ts/server/events.ts +17 -0
- package/src/ts/server/express.ts +2 -2
- package/src/ts/server/index.ts +6 -0
- package/src/ts/server/jobs.ts +14 -2
- package/src/ts/server/utils/BackwardCompat.ts +530 -529
- package/src/ts/types.ts +78 -34
- package/dist/plugins/client/client/utils.d.ts +0 -1
- package/dist/plugins/client/client/utils.js.map +0 -1
- package/dist/plugins/client/page.d.ts +0 -2
- package/dist/plugins/client/page.js.map +0 -1
- package/dist/plugins/client/plugins/client/publicationRenderer.d.ts +0 -2
- package/dist/plugins/client/plugins/client/publicationRenderer.js +0 -128
- package/dist/plugins/client/plugins/client/publicationRenderer.js.map +0 -1
- package/dist/plugins/server/plugins/server/AuthPlugin.d.ts +0 -1
- package/dist/plugins/server/plugins/server/AuthPlugin.js +0 -38
- package/dist/plugins/server/plugins/server/AuthPlugin.js.map +0 -1
- package/dist/plugins/server/plugins/server/HooksPlugin.d.ts +0 -7
- package/dist/plugins/server/plugins/server/HooksPlugin.js +0 -74
- package/dist/plugins/server/plugins/server/HooksPlugin.js.map +0 -1
- package/src/example-client-config.js +0 -64
- package/src/ts/plugins/client/publicationRenderer.ts +0 -128
- package/src/ts/plugins/server/HooksPlugin.ts +0 -76
|
@@ -17,13 +17,12 @@
|
|
|
17
17
|
|
|
18
18
|
import { Router } from 'express'
|
|
19
19
|
import { API_PUBLICATION_PUBLISH, API_PUBLICATION_STATUS, API_WEBSITE_ASSET_READ } from '../../constants'
|
|
20
|
-
import {
|
|
21
|
-
import { ApiPublicationPublishBody, ApiPublicationPublishQuery, ApiPublicationPublishResponse, ApiPublicationStatusQuery, ApiPublicationStatusResponse, JobId, PublicationSettings, PublicationData as PublicationData, ConnectorType, ConnectorId, WebsiteId} from '../../types'
|
|
20
|
+
import { ApiPublicationPublishBody, ApiPublicationPublishQuery, ApiPublicationPublishResponse, ApiPublicationStatusQuery, ApiPublicationStatusResponse, JobId, PublicationData, ConnectorType, ConnectorId, WebsiteId, ClientSideFile, ClientSideFileWithContent, ClientSideFileWithSrc} from '../../types'
|
|
22
21
|
import { ConnectorFile, HostingConnector, StorageConnector, getConnector } from '../connectors/connectors'
|
|
23
22
|
import { ServerConfig } from '../config'
|
|
24
23
|
import { requiredParam } from '../utils/validation'
|
|
25
|
-
import { join } from 'path'
|
|
26
24
|
import { PublishEndEventType, PublishStartEventType, ServerEvent } from '../events'
|
|
25
|
+
import { getJob, jobManager } from '../jobs'
|
|
27
26
|
|
|
28
27
|
/**
|
|
29
28
|
* @fileoverview Publication plugin for Silex
|
|
@@ -109,9 +108,9 @@ export default function (config: ServerConfig): Router {
|
|
|
109
108
|
|
|
110
109
|
// Check params
|
|
111
110
|
const data = body as PublicationData
|
|
112
|
-
const { files
|
|
113
|
-
if(!files
|
|
114
|
-
throw new PublicationError('Missing
|
|
111
|
+
const { files } = data
|
|
112
|
+
if(!files) {
|
|
113
|
+
throw new PublicationError('Missing files in body', 400)
|
|
115
114
|
}
|
|
116
115
|
|
|
117
116
|
// Hook for plugins
|
|
@@ -123,29 +122,23 @@ export default function (config: ServerConfig): Router {
|
|
|
123
122
|
// Get storage connector which holds the assets
|
|
124
123
|
const storage = await getStorageConnector(session, config, storageId)
|
|
125
124
|
|
|
126
|
-
//
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
// Get the asset url relative to the asset root
|
|
133
|
-
const src = asset.src.startsWith(prefix) ? asset.src.substring(prefix.length) : asset.src
|
|
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
|
|
134
131
|
return {
|
|
135
|
-
|
|
136
|
-
|
|
132
|
+
// Destination
|
|
133
|
+
path: file.permalink ?? file.path,
|
|
134
|
+
// Content
|
|
135
|
+
content: fileWithContent.content ?? await storage.readAsset(session, websiteId, fileWithSrc.src),
|
|
137
136
|
}
|
|
138
137
|
}))
|
|
139
|
-
|
|
140
|
-
path: file.htmlPath,
|
|
141
|
-
content: file.html,
|
|
142
|
-
}, {
|
|
143
|
-
path: file.cssPath,
|
|
144
|
-
content: file.css,
|
|
145
|
-
}])).concat(assetsFiles)
|
|
138
|
+
|
|
146
139
|
res.json({
|
|
147
140
|
url: await hostingConnector.getUrl(session, websiteId),
|
|
148
|
-
job: await hostingConnector.publish(session, websiteId, filesList),
|
|
141
|
+
job: await hostingConnector.publish(session, websiteId, filesList, jobManager),
|
|
149
142
|
} as ApiPublicationPublishResponse)
|
|
150
143
|
} catch (err) {
|
|
151
144
|
console.error('Error publishing the website', err)
|
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
import { Router } from 'express'
|
|
19
19
|
import formidable from 'formidable'
|
|
20
20
|
import PersistentFile from 'formidable/src/PersistentFile'
|
|
21
|
-
import { API_WEBSITE_ASSET_READ, API_WEBSITE_ASSETS_WRITE, API_WEBSITE_READ, API_WEBSITE_WRITE, API_WEBSITE_DELETE, API_WEBSITE_META_READ, API_WEBSITE_META_WRITE, API_WEBSITE_LIST, API_WEBSITE_CREATE, API_PATH, API_WEBSITE_PATH } from '../../constants'
|
|
21
|
+
import { API_WEBSITE_ASSET_READ, API_WEBSITE_ASSETS_WRITE, API_WEBSITE_READ, API_WEBSITE_WRITE, API_WEBSITE_DELETE, API_WEBSITE_META_READ, API_WEBSITE_META_WRITE, API_WEBSITE_LIST, API_WEBSITE_CREATE, API_PATH, API_WEBSITE_PATH, API_WEBSITE_DUPLICATE } from '../../constants'
|
|
22
22
|
import { createReadStream } from 'fs'
|
|
23
|
-
import { ApiError, ApiWebsiteAssetsReadParams, ApiWebsiteAssetsReadQuery, ApiWebsiteAssetsReadResponse, ApiWebsiteAssetsWriteQuery, ApiWebsiteAssetsWriteResponse, ApiWebsiteDeleteQuery, ApiWebsiteReadQuery, ApiWebsiteReadResponse, ApiWebsiteWriteBody, ApiWebsiteWriteQuery, ConnectorId, ConnectorType, WebsiteMeta, WebsiteData, WebsiteId, ApiWebsiteListQuery, ApiWebsiteListResponse, ApiWebsiteMetaReadQuery, ApiWebsiteMetaReadResponse, ApiWebsiteMetaWriteQuery, ApiWebsiteMetaWriteBody, WebsiteMetaFileContent, ApiWebsiteMetaWriteResponse, ApiWebsiteWriteResponse, ApiWebsiteCreateQuery, ApiWebsiteCreateBody } from '../../types'
|
|
23
|
+
import { ApiError, ApiWebsiteAssetsReadParams, ApiWebsiteAssetsReadQuery, ApiWebsiteAssetsReadResponse, ApiWebsiteAssetsWriteQuery, ApiWebsiteAssetsWriteResponse, ApiWebsiteDeleteQuery, ApiWebsiteReadQuery, ApiWebsiteReadResponse, ApiWebsiteWriteBody, ApiWebsiteWriteQuery, ConnectorId, ConnectorType, WebsiteMeta, WebsiteData, WebsiteId, ApiWebsiteListQuery, ApiWebsiteListResponse, ApiWebsiteMetaReadQuery, ApiWebsiteMetaReadResponse, ApiWebsiteMetaWriteQuery, ApiWebsiteMetaWriteBody, WebsiteMetaFileContent, ApiWebsiteMetaWriteResponse, ApiWebsiteWriteResponse, ApiWebsiteCreateQuery, ApiWebsiteCreateBody, ApiWebsiteDuplicateQuery } from '../../types'
|
|
24
24
|
import { ConnectorFile, ConnectorFileContent, ConnectorSession, StorageConnector, getConnector } from '../connectors/connectors'
|
|
25
25
|
import { Readable } from 'stream'
|
|
26
26
|
import { requiredParam } from '../utils/validation'
|
|
27
|
-
import { basename } from 'path'
|
|
27
|
+
import { basename, join } from 'path'
|
|
28
28
|
import { ServerConfig } from '../config'
|
|
29
29
|
import { ServerEvent, WebsiteStoreEndEventType, WebsiteStoreStartEventType, WebsiteAssetStoreStartEventType, WebsiteAssetStoreEndEventType } from '../events'
|
|
30
30
|
|
|
@@ -226,6 +226,23 @@ export default function (config: ServerConfig, opts = {}): Router {
|
|
|
226
226
|
}
|
|
227
227
|
})
|
|
228
228
|
|
|
229
|
+
// Duplicate website
|
|
230
|
+
router.post(API_WEBSITE_DUPLICATE, async (req, res) => {
|
|
231
|
+
try {
|
|
232
|
+
const query: ApiWebsiteDuplicateQuery = req.query as any
|
|
233
|
+
const websiteId= requiredParam<WebsiteId>(query.websiteId, 'New website id')
|
|
234
|
+
await duplicateWebsite(req['session'], websiteId, query.connectorId)
|
|
235
|
+
res.status(200).json({ message: 'Website duplicated' } as ApiError)
|
|
236
|
+
} catch (e) {
|
|
237
|
+
console.error('Error duplicating website data', e)
|
|
238
|
+
if (e.httpStatusCode) {
|
|
239
|
+
res.status(e.httpStatusCode).json({ message: e.message } as ApiError)
|
|
240
|
+
} else {
|
|
241
|
+
res.status(500).json({ message: e.message } as ApiError)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
})
|
|
245
|
+
|
|
229
246
|
// Load assets
|
|
230
247
|
router.get(API_WEBSITE_ASSET_READ + '/:path', async (req, res) => {
|
|
231
248
|
{
|
|
@@ -261,11 +278,10 @@ export default function (config: ServerConfig, opts = {}): Router {
|
|
|
261
278
|
try {
|
|
262
279
|
// Check input
|
|
263
280
|
const query: ApiWebsiteAssetsWriteQuery = req.query as any
|
|
264
|
-
const websiteId= requiredParam<WebsiteId>(query.websiteId as WebsiteId, 'Website id')
|
|
281
|
+
const websiteId = requiredParam<WebsiteId>(query.websiteId as WebsiteId, 'Website id')
|
|
265
282
|
|
|
266
283
|
// Get the file data from the request
|
|
267
284
|
const form = formidable({
|
|
268
|
-
filename: (name, ext, part, _form) => `${name}${ext}`,
|
|
269
285
|
multiples: true,
|
|
270
286
|
keepExtensions: true,
|
|
271
287
|
})
|
|
@@ -278,7 +294,7 @@ export default function (config: ServerConfig, opts = {}): Router {
|
|
|
278
294
|
console.error('Error parsing upload data', err)
|
|
279
295
|
reject(new ApiError('Error parsing upload data: ' + err.message, 400))
|
|
280
296
|
} else {
|
|
281
|
-
const files = ([].concat(_files['files[]']) as PersistentFile[])
|
|
297
|
+
const files = ([].concat(_files['files[]'] as PersistentFile) as PersistentFile[])
|
|
282
298
|
.map(file => file.toJSON())
|
|
283
299
|
.map(file => ({
|
|
284
300
|
path: `/${file.originalFilename}`,
|
|
@@ -293,16 +309,26 @@ export default function (config: ServerConfig, opts = {}): Router {
|
|
|
293
309
|
config.emit(ServerEvent.WEBSITE_ASSET_STORE_START, { files, websiteId, connectorId } as WebsiteAssetStoreStartEventType)
|
|
294
310
|
|
|
295
311
|
// Write the files
|
|
296
|
-
await writeAssets(req['session'], websiteId, files, connectorId)
|
|
312
|
+
const result = await writeAssets(req['session'], websiteId, files, connectorId)
|
|
313
|
+
|
|
314
|
+
// Base URL of silex serve
|
|
315
|
+
const baseUrl = new URL(config.url).pathname.replace(/\/$/, '')
|
|
297
316
|
|
|
298
317
|
// Return the file URLs to insert in the website
|
|
299
318
|
// As expected by grapesjs (https://grapesjs.com/docs/modules/Assets.html#uploading-assets)
|
|
319
|
+
const data = result.map(path =>
|
|
320
|
+
join(
|
|
321
|
+
// We should return path without this line, as it is saved, not as it is displayed
|
|
322
|
+
// But this url is sent straight to grapesjs, so we need to return the url as it is displayed
|
|
323
|
+
baseUrl, API_PATH, API_WEBSITE_PATH, API_WEBSITE_ASSET_READ,
|
|
324
|
+
path,
|
|
325
|
+
)
|
|
326
|
+
+ `?websiteId=${websiteId}&connectorId=${connectorId ? connectorId : ''}` // As expected by wesite API (readAsset)
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
// Return the file URLs
|
|
300
330
|
res.json({
|
|
301
|
-
data
|
|
302
|
-
API_PATH + API_WEBSITE_PATH + API_WEBSITE_ASSET_READ
|
|
303
|
-
+ file.path
|
|
304
|
-
+ `?websiteId=${websiteId}&connectorId=${connectorId ? connectorId : ''}` // As expected by wesite API (readAsset)
|
|
305
|
-
),
|
|
331
|
+
data,
|
|
306
332
|
} as ApiWebsiteAssetsWriteResponse)
|
|
307
333
|
|
|
308
334
|
// Hook for plugins
|
|
@@ -381,6 +407,17 @@ export default function (config: ServerConfig, opts = {}): Router {
|
|
|
381
407
|
return storageConnector.deleteWebsite(session, websiteId)
|
|
382
408
|
}
|
|
383
409
|
|
|
410
|
+
/**
|
|
411
|
+
* Duplicate a website
|
|
412
|
+
*/
|
|
413
|
+
async function duplicateWebsite(session: any, websiteId: string, connectorId?: string): Promise<void> {
|
|
414
|
+
// Get the desired connector
|
|
415
|
+
const storageConnector = await getStorageConnector(session, connectorId)
|
|
416
|
+
|
|
417
|
+
// Duplicate the website
|
|
418
|
+
return storageConnector.duplicateWebsite(session, websiteId)
|
|
419
|
+
}
|
|
420
|
+
|
|
384
421
|
/**
|
|
385
422
|
* Read an asset
|
|
386
423
|
*/
|
|
@@ -394,6 +431,7 @@ export default function (config: ServerConfig, opts = {}): Router {
|
|
|
394
431
|
|
|
395
432
|
/**
|
|
396
433
|
* Write an asset to the connector
|
|
434
|
+
* @returns File names on the storage connector, always starting with a slash
|
|
397
435
|
*/
|
|
398
436
|
async function writeAssets(session: any, websiteId: string, files: ConnectorFile[], connectorId?: string): Promise<string[]> {
|
|
399
437
|
// Get the desired connector
|
|
@@ -406,14 +444,19 @@ export default function (config: ServerConfig, opts = {}): Router {
|
|
|
406
444
|
}))
|
|
407
445
|
|
|
408
446
|
// Write the asset to the connector
|
|
409
|
-
await storageConnector.writeAssets(
|
|
447
|
+
const result = await storageConnector.writeAssets(
|
|
410
448
|
session,
|
|
411
449
|
websiteId,
|
|
412
450
|
cleanPathFiles,
|
|
413
451
|
)
|
|
414
452
|
|
|
415
453
|
// Return the files URLs with the website id
|
|
416
|
-
return files
|
|
454
|
+
return files
|
|
455
|
+
// Use the original path or the one returned by the connector
|
|
456
|
+
.map(({ path }, idx) => result && result[idx] ? result[idx] : path)
|
|
457
|
+
// Make it an absolute path with the website id and the connector id as query params
|
|
458
|
+
//.map((path) => toAssetUrl(path, config.url, websiteId, connectorId))
|
|
417
459
|
}
|
|
460
|
+
|
|
418
461
|
return router
|
|
419
462
|
}
|
package/src/ts/server/cli.ts
CHANGED
|
@@ -25,11 +25,12 @@ const options = parse({
|
|
|
25
25
|
'host': ['', 'Sets the host param of the express module "cookie-session".', 'string'],
|
|
26
26
|
'port': [ 'p', 'Port to listen to', 'int'],
|
|
27
27
|
'protocol': ['', 'Sets the protocol param of the express module "cookie-session".', 'string'],
|
|
28
|
-
'config': [ '
|
|
29
|
-
'client-config': ['', 'Path to client config file to be served on .silex-client.js', '
|
|
30
|
-
'fs-root': ['', 'Path to the root folder where to store websites. Used by the default connector (fs).', 'string'],
|
|
28
|
+
'server-config': [ 's', 'Path for the server side config file to load at startup', 'file'],
|
|
29
|
+
'client-config': ['c', 'Path to client config file to be served on .silex-client.js', 'file'],
|
|
30
|
+
'fs-root': ['', 'Path to the root folder where to store websites. Used by the default storage connector (fs).', 'string'],
|
|
31
|
+
'fs-hosting-root': ['', 'Path to the root folder where to publish websites. Used by the default hosting connector (fs).', 'string'],
|
|
31
32
|
'session-name': ['', 'Sets the name param of the express module "cookie-session".', 'string'],
|
|
32
|
-
'session-secret': ['
|
|
33
|
+
'session-secret': ['', 'Session secret', 'string'],
|
|
33
34
|
'ssl-port': [ false, 'Port to listen to for SSL/HTTPS', 'int'],
|
|
34
35
|
'force-https': [ false, 'Force HTTPS', 'boolean'],
|
|
35
36
|
'ssl-private-key': [ false, 'Path to private key for SSL', 'file'],
|
|
@@ -46,9 +47,10 @@ if(options['url']) process.env.SILEX_URL = options['url']
|
|
|
46
47
|
if(options['host']) process.env.SILEX_HOST = options['host']
|
|
47
48
|
if(options.port) process.env.SILEX_PORT = options.port
|
|
48
49
|
if(options['protocol']) process.env.SILEX_PROTOCOL = options['protocol']
|
|
49
|
-
if(options
|
|
50
|
+
if(options['server-config']) process.env.SILEX_SERVER_CONFIG = options['server-config']
|
|
50
51
|
if(options['client-config']) process.env.SILEX_CLIENT_CONFIG = options['client-config']
|
|
51
52
|
if(options['fs-root']) process.env.SILEX_FS_ROOT = options['fs-root']
|
|
53
|
+
if(options['fs-hosting-root']) process.env.SILEX_FS_HOSTING_ROOT = options['fs-hosting-root']
|
|
52
54
|
if(options['session-name']) process.env.SILEX_SESSION_NAME = options['session-name']
|
|
53
55
|
if(options['session-secret']) process.env.SILEX_SESSION_SECRET = options['session-secret']
|
|
54
56
|
if(options['ssl-port']) process.env.SILEX_SSL_PORT = options['ssl-port']
|
package/src/ts/server/config.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
import { Config, Plugin } from '@silexlabs/silex-plugins'
|
|
28
|
-
import { resolve } from 'path'
|
|
28
|
+
import { join, resolve } from 'path'
|
|
29
29
|
import { CLIENT_CONFIG_FILE_NAME } from '../constants'
|
|
30
30
|
import { Application, Request, Response, Router } from 'express'
|
|
31
31
|
import { readFile } from 'fs/promises'
|
|
@@ -49,8 +49,8 @@ export class ServerConfig extends Config {
|
|
|
49
49
|
}
|
|
50
50
|
public port = process.env.SILEX_PORT
|
|
51
51
|
public debug = process.env.SILEX_DEBUG === 'true'
|
|
52
|
-
public url = process.env.SILEX_URL ?? `${process.env.SILEX_PROTOCOL}://${process.env.SILEX_HOST}:${process.env.SILEX_PORT}`
|
|
53
|
-
public userConfigPath: Plugin | undefined = process.env.
|
|
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
54
|
public configFilePath: Plugin = resolve(__dirname, '../../../.silex.js')
|
|
55
55
|
|
|
56
56
|
// All connectors or just the storage or hosting connectors
|
|
@@ -103,7 +103,7 @@ export class ServerConfig extends Config {
|
|
|
103
103
|
async addRoutes(app: Application) {
|
|
104
104
|
const path = process.env.SILEX_CLIENT_CONFIG
|
|
105
105
|
if (path) {
|
|
106
|
-
console.
|
|
106
|
+
console.info(`> Serving client side config ${path} at ${CLIENT_CONFIG_FILE_NAME}`)
|
|
107
107
|
try {
|
|
108
108
|
// Load the client config file
|
|
109
109
|
let clientConfig = (await readFile(path as string)).toString()
|
|
@@ -111,7 +111,7 @@ export class ServerConfig extends Config {
|
|
|
111
111
|
app.get(`/${CLIENT_CONFIG_FILE_NAME}`, async (req: Request, res: Response) => {
|
|
112
112
|
// Reload each time in debug mode
|
|
113
113
|
if (this.debug) {
|
|
114
|
-
console.
|
|
114
|
+
console.info('[Debug mode] Reloading config file', path)
|
|
115
115
|
clientConfig = (await readFile(path)).toString()
|
|
116
116
|
}
|
|
117
117
|
// Send the config file
|
|
@@ -143,7 +143,7 @@ export class ServerConfig extends Config {
|
|
|
143
143
|
*/
|
|
144
144
|
async loadUserConfig() {
|
|
145
145
|
if (this.userConfigPath) {
|
|
146
|
-
console.
|
|
146
|
+
console.info('> Loading user config', this.userConfigPath)
|
|
147
147
|
try {
|
|
148
148
|
// Initiate the process with the config file which is just another plugin
|
|
149
149
|
await this.addPlugin(this.userConfigPath, {})
|
|
@@ -158,12 +158,11 @@ export class ServerConfig extends Config {
|
|
|
158
158
|
* This is the main config file
|
|
159
159
|
*/
|
|
160
160
|
async loadSilexConfig() {
|
|
161
|
-
console.
|
|
161
|
+
console.info('> Loading config', this.configFilePath)
|
|
162
162
|
try {
|
|
163
163
|
// Initiate the process with the config file which is just another plugin
|
|
164
164
|
await this.addPlugin(this.configFilePath, {})
|
|
165
165
|
} catch(e) {
|
|
166
|
-
console.log(e.requireStack[0])
|
|
167
166
|
// Check if the error is about the config file not found and not another module not found in the config file
|
|
168
167
|
if(e.code === 'MODULE_NOT_FOUND' && (!e.requireStack || !e.requireStack.find(path => path === this.configFilePath))) {
|
|
169
168
|
console.info('> /!\\ Config file not found', this.configFilePath)
|
|
@@ -177,13 +176,13 @@ export class ServerConfig extends Config {
|
|
|
177
176
|
// Add default storage connectors
|
|
178
177
|
if (!this.storageConnectors.length) {
|
|
179
178
|
this.addStorageConnector(new FsStorage(null, {
|
|
180
|
-
path: process.env.SILEX_FS_ROOT,
|
|
179
|
+
path: process.env.SILEX_FS_ROOT || join(process.cwd(), '/silex/storage'),
|
|
181
180
|
}))
|
|
182
181
|
}
|
|
183
182
|
// Add default hosting connectors
|
|
184
183
|
if (!this.hostingConnectors.length) {
|
|
185
184
|
this.addHostingConnector(new FsHosting(null, {
|
|
186
|
-
path: process.env.
|
|
185
|
+
path: process.env.SILEX_FS_HOSTING_ROOT || join(process.cwd(), '/silex/hosting'),
|
|
187
186
|
}))
|
|
188
187
|
}
|
|
189
188
|
}
|
|
@@ -15,29 +15,35 @@
|
|
|
15
15
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
import fs from 'fs/promises'
|
|
18
19
|
import { ConnectorFile, StorageConnector, HostingConnector, StatusCallback, ConnectorSession, contentToString, toConnectorData, ConnectorFileContent} from './connectors'
|
|
19
20
|
import { join } from 'path'
|
|
20
|
-
import { jobError, jobSuccess, startJob } from '../jobs'
|
|
21
21
|
import { FsStorage } from './FsStorage'
|
|
22
22
|
import { ConnectorType, JobData, JobStatus, PublicationJobData, WebsiteId } from '../../types'
|
|
23
|
+
import { JobManager } from '../jobs'
|
|
23
24
|
|
|
24
25
|
type FsSession = ConnectorSession
|
|
25
26
|
|
|
26
|
-
interface FsOptions {
|
|
27
|
-
path?: string
|
|
28
|
-
}
|
|
29
|
-
|
|
30
27
|
export class FsHosting extends FsStorage implements HostingConnector<FsSession> {
|
|
31
28
|
connectorId = 'fs-hosting'
|
|
32
29
|
displayName = 'File system hosting'
|
|
33
|
-
icon = 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M6%202L6%2022%2018%2022%2018%207%2012%202%206%202Z%22%3E%3C%2Fpath%3E%3Cpath%20d%3D%22M18%202L12%202%2012%208%2018%208%2018%202Z%22%3E%3C%2Fpath%3E%3C%2Fsvg%3E'
|
|
34
30
|
connectorType = ConnectorType.HOSTING
|
|
35
31
|
|
|
36
|
-
async
|
|
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> {
|
|
37
42
|
const job = startJob(`Publishing to ${this.displayName}`) as PublicationJobData
|
|
38
43
|
job.logs = [[`Publishing to ${this.displayName}`]]
|
|
39
44
|
job.errors = [[]]
|
|
40
|
-
|
|
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}) => {
|
|
41
47
|
// Update the job status
|
|
42
48
|
job.status = status
|
|
43
49
|
job.message = message
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import fs
|
|
18
|
+
import fs from 'fs/promises'
|
|
19
19
|
import { createWriteStream } from 'fs'
|
|
20
20
|
import { ConnectorFile, StorageConnector, StatusCallback, ConnectorSession, toConnectorData, ConnectorFileContent} from './connectors'
|
|
21
21
|
import { dirname, join } from 'path'
|
|
@@ -32,13 +32,29 @@ import { fileURLToPath } from 'url'
|
|
|
32
32
|
if(!globalThis.__dirname) {
|
|
33
33
|
// @ts-ignore
|
|
34
34
|
globalThis.__dirname = dirname(process.cwd() + '/src/ts/server/connectors/FsStorage.ts')
|
|
35
|
-
console.
|
|
35
|
+
console.info('Redefining __dirname', globalThis.__dirname)
|
|
36
36
|
}
|
|
37
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
|
+
|
|
38
54
|
type FsSession = ConnectorSession
|
|
39
55
|
|
|
40
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'
|
|
41
|
-
const FILE_ICON = '
|
|
57
|
+
const FILE_ICON = '/assets/laptop.png'
|
|
42
58
|
|
|
43
59
|
interface FsOptions {
|
|
44
60
|
path: string
|
|
@@ -64,7 +80,7 @@ export class FsStorage implements StorageConnector<FsSession> {
|
|
|
64
80
|
this.initFs()
|
|
65
81
|
}
|
|
66
82
|
|
|
67
|
-
|
|
83
|
+
protected async initFs() {
|
|
68
84
|
const stat = await fs.stat(this.options.path).catch(() => null)
|
|
69
85
|
if (!stat) {
|
|
70
86
|
// create data folder with a default website
|
|
@@ -72,6 +88,7 @@ export class FsStorage implements StorageConnector<FsSession> {
|
|
|
72
88
|
await fs.mkdir(join(this.options.path, id, this.options.assetsFolder), { recursive: true })
|
|
73
89
|
await this.setWebsiteMeta({}, id, { name: 'Default website', connectorUserSettings: {} })
|
|
74
90
|
await this.updateWebsite({}, id, defaultWebsiteData)
|
|
91
|
+
console.info(`> [FsStorage] Created ${id} in ${this.options.path}`)
|
|
75
92
|
}
|
|
76
93
|
}
|
|
77
94
|
|
|
@@ -136,7 +153,7 @@ export class FsStorage implements StorageConnector<FsSession> {
|
|
|
136
153
|
async getWebsiteMeta(session: FsSession, id: WebsiteId): Promise<WebsiteMeta> {
|
|
137
154
|
const websiteId = requiredParam<WebsiteId>(id, 'website id')
|
|
138
155
|
// Get stats for website folder
|
|
139
|
-
const fileStat = await stat(join(this.options.path, websiteId))
|
|
156
|
+
const fileStat = await fs.stat(join(this.options.path, websiteId))
|
|
140
157
|
const path = join(this.options.path, websiteId, WEBSITE_META_DATA_FILE)
|
|
141
158
|
// Get meta file
|
|
142
159
|
const content = await fs.readFile(path)
|
|
@@ -182,6 +199,18 @@ export class FsStorage implements StorageConnector<FsSession> {
|
|
|
182
199
|
return fs.rmdir(path, { recursive: true })
|
|
183
200
|
}
|
|
184
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
|
+
|
|
185
214
|
async listWebsites(session: any): Promise<WebsiteMeta[]> {
|
|
186
215
|
const list = await fs.readdir(this.options.path)
|
|
187
216
|
return Promise.all(list.map(async fileName => {
|
|
@@ -197,16 +226,20 @@ export class FsStorage implements StorageConnector<FsSession> {
|
|
|
197
226
|
}
|
|
198
227
|
|
|
199
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> {
|
|
200
233
|
const filesStatuses = this.initStatus(files)
|
|
201
234
|
let error: Error | null = null
|
|
202
235
|
for (const fileStatus of filesStatuses) {
|
|
203
236
|
const {file} = fileStatus
|
|
204
|
-
const path = join(this.options.path, id,
|
|
237
|
+
const path = join(this.options.path, id, assetsFolder, file.path)
|
|
205
238
|
if (typeof file.content === 'string' || Buffer.isBuffer(file.content)) {
|
|
206
239
|
fileStatus.message = 'Writing'
|
|
207
240
|
this.updateStatus(filesStatuses, JobStatus.IN_PROGRESS, statusCbk)
|
|
208
241
|
try {
|
|
209
|
-
await fs.writeFile(path, file.content
|
|
242
|
+
await fs.writeFile(path, file.content)
|
|
210
243
|
} catch(err) {
|
|
211
244
|
fileStatus.message = `Error (${err})`
|
|
212
245
|
this.updateStatus(filesStatuses, JobStatus.IN_PROGRESS, statusCbk)
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import { Readable } from 'stream'
|
|
19
19
|
import { ServerConfig } from '../config'
|
|
20
20
|
import { JobStatus, JobData, ConnectorData, ConnectorId, WebsiteId, ConnectorType, ConnectorUser, WebsiteMeta, WebsiteMetaFileContent, WebsiteData, ConnectorOptions } from '../../types'
|
|
21
|
+
import { JobManager } from '../jobs'
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* @fileoverview define types for Silex connectors
|
|
@@ -66,10 +67,7 @@ export interface Connector<Session extends ConnectorSession = ConnectorSession>
|
|
|
66
67
|
isLoggedIn(session: Session): Promise<boolean>
|
|
67
68
|
setToken(session: Session, token: object): Promise<void>
|
|
68
69
|
logout(session: Session): Promise<void>
|
|
69
|
-
getUser(session: Session): Promise<ConnectorUser>
|
|
70
|
-
// Handle website meta file
|
|
71
|
-
getWebsiteMeta(session: Session, websiteId: WebsiteId): Promise<WebsiteMeta>
|
|
72
|
-
setWebsiteMeta(session: Session, websiteId: WebsiteId, data: WebsiteMetaFileContent): Promise<void>
|
|
70
|
+
getUser(session: Session): Promise<ConnectorUser | null>
|
|
73
71
|
// Get the connector options from login form
|
|
74
72
|
// They are stored in the website meta file for hosting connectors
|
|
75
73
|
// And in the user session for storage connectors
|
|
@@ -88,18 +86,22 @@ export interface StorageConnector<Session extends ConnectorSession = ConnectorSe
|
|
|
88
86
|
createWebsite(session: Session, data: WebsiteMetaFileContent): Promise<WebsiteId>
|
|
89
87
|
updateWebsite(session: Session, websiteId: WebsiteId, data: WebsiteData): Promise<void>
|
|
90
88
|
deleteWebsite(session: Session, websiteId: WebsiteId): Promise<void>
|
|
89
|
+
duplicateWebsite(session: Session, websiteId: WebsiteId): Promise<void>
|
|
91
90
|
// CRUD on assets
|
|
92
|
-
writeAssets(session: Session, websiteId: WebsiteId, files: ConnectorFile[], status?: StatusCallback): Promise<void>
|
|
91
|
+
writeAssets(session: Session, websiteId: WebsiteId, files: ConnectorFile[], status?: StatusCallback): Promise<string[] | void>
|
|
93
92
|
readAsset(session: Session, websiteId: WebsiteId, fileName: string): Promise<ConnectorFileContent>
|
|
94
93
|
deleteAssets(session: Session, websiteId: WebsiteId, fileNames: string[]): Promise<void>
|
|
95
94
|
//getFileUrl(session: Session, websiteId: WebsiteId, path: string): Promise<string>
|
|
95
|
+
// Handle website meta file
|
|
96
|
+
getWebsiteMeta(session: Session, websiteId: WebsiteId): Promise<WebsiteMeta>
|
|
97
|
+
setWebsiteMeta(session: Session, websiteId: WebsiteId, data: WebsiteMetaFileContent): Promise<void>
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
/**
|
|
99
101
|
* Hosting connectors are used to publish the website
|
|
100
102
|
*/
|
|
101
103
|
export interface HostingConnector<Session extends ConnectorSession = ConnectorSession> extends Connector<Session> {
|
|
102
|
-
publish(session: Session, websiteId: WebsiteId, files: ConnectorFile[]): Promise<JobData>
|
|
104
|
+
publish(session: Session, websiteId: WebsiteId, files: ConnectorFile[], jobManager: JobManager): Promise<JobData> // Pass the jobManager as plugins do not neccessarily share the same module instance
|
|
103
105
|
getUrl(session: Session, websiteId: WebsiteId): Promise<string>
|
|
104
106
|
}
|
|
105
107
|
|
package/src/ts/server/events.ts
CHANGED
|
@@ -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
|
import { ConnectorId, PublicationData, WebsiteData, WebsiteId } from '../types'
|
|
2
19
|
import { ConnectorFile } from './connectors/connectors'
|
|
3
20
|
|
package/src/ts/server/express.ts
CHANGED
|
@@ -33,7 +33,7 @@ export function create(config: ServerConfig): Application {
|
|
|
33
33
|
// CORS
|
|
34
34
|
const options = config.expressOptions
|
|
35
35
|
if (options.cors) {
|
|
36
|
-
console.
|
|
36
|
+
console.info('> CORS are ENABLED:', options.cors)
|
|
37
37
|
app.use(cors({
|
|
38
38
|
origin: options.cors,
|
|
39
39
|
}))
|
|
@@ -45,7 +45,7 @@ export function create(config: ServerConfig): Application {
|
|
|
45
45
|
app.use(bodyParser.json({ limit: options.jsonLimit }))
|
|
46
46
|
app.use(bodyParser.text({ limit: options.textLimit }))
|
|
47
47
|
app.use(bodyParser.urlencoded({ limit: options.urlencodedLimit }))
|
|
48
|
-
console.
|
|
48
|
+
console.info('> Session name:', options.sessionName)
|
|
49
49
|
app.use(cookieParser() as any)
|
|
50
50
|
app.use(session({
|
|
51
51
|
name: options.sessionName,
|
package/src/ts/server/index.ts
CHANGED
|
@@ -20,12 +20,18 @@ import api from './api/api'
|
|
|
20
20
|
import { ServerConfig } from './config'
|
|
21
21
|
import dotenv from 'dotenv'
|
|
22
22
|
import { join } from 'path'
|
|
23
|
+
import { DEV_MESSAGE } from '../constants'
|
|
23
24
|
|
|
24
25
|
// Expose API to calling app as function silex()
|
|
25
26
|
export * from './expose'
|
|
26
27
|
|
|
27
28
|
// Main app
|
|
28
29
|
export default async function silex() {
|
|
30
|
+
// Log Silex version
|
|
31
|
+
console.info(DEV_MESSAGE)
|
|
32
|
+
console.info(`Node version: ${process.version}`)
|
|
33
|
+
console.info(`CWD: ${process.cwd()}`)
|
|
34
|
+
|
|
29
35
|
// Load env vars from .env file if any
|
|
30
36
|
const curDirEnv = dotenv.config()
|
|
31
37
|
if (curDirEnv.error) {
|
package/src/ts/server/jobs.ts
CHANGED
|
@@ -22,6 +22,7 @@ const TIME_TO_KEEP_AFTER_STOP = 60*10*1000 // 10 min
|
|
|
22
22
|
|
|
23
23
|
// Store the jobs in memory
|
|
24
24
|
const jobs = new Array<JobData>()
|
|
25
|
+
const timers = new WeakMap<JobData, NodeJS.Timeout>()
|
|
25
26
|
|
|
26
27
|
// Key which will change on server restart
|
|
27
28
|
const processKey = Math.ceil(Math.random() * 1000000)
|
|
@@ -60,7 +61,8 @@ export function jobError(id: JobId, message = ''): boolean {
|
|
|
60
61
|
// Remove a job from memory
|
|
61
62
|
// This is also used in unit tests to avoid memory lea
|
|
62
63
|
export function killJob(job: JobData) {
|
|
63
|
-
clearTimeout(job
|
|
64
|
+
clearTimeout(timers.get(job))
|
|
65
|
+
timers.delete(job)
|
|
64
66
|
jobs.splice(jobs.findIndex(j => job.jobId === j.jobId), 1)
|
|
65
67
|
}
|
|
66
68
|
|
|
@@ -70,8 +72,18 @@ function end(id: JobId, status: JobStatus, message = ''): boolean {
|
|
|
70
72
|
if(job) {
|
|
71
73
|
job.status = status
|
|
72
74
|
if(message) job.message = message
|
|
73
|
-
job
|
|
75
|
+
timers.set(job, setTimeout(() => killJob(job), TIME_TO_KEEP_AFTER_STOP))
|
|
74
76
|
return true
|
|
75
77
|
}
|
|
76
78
|
return false
|
|
77
79
|
}
|
|
80
|
+
|
|
81
|
+
// Type definition of the job manager
|
|
82
|
+
export interface JobManager {
|
|
83
|
+
startJob: typeof startJob
|
|
84
|
+
jobSuccess: typeof jobSuccess
|
|
85
|
+
jobError: typeof jobError
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Export the job manager
|
|
89
|
+
export const jobManager: JobManager = { startJob, jobSuccess, jobError }
|