@silexlabs/silex 3.0.0-alpha.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.default +19 -0
- package/.silex.js +37 -0
- package/LICENSE +661 -0
- package/README.md +96 -0
- package/dist/client/client/api.d.ts +85 -0
- package/dist/client/client/assetUrl.d.ts +51 -0
- package/dist/client/client/config.d.ts +58 -0
- package/dist/client/client/events.d.ts +14 -0
- package/dist/client/client/expose.d.ts +8 -0
- package/dist/client/client/grapesjs/LoginDialog.d.ts +15 -0
- package/dist/client/client/grapesjs/PublicationManager.d.ts +72 -0
- package/dist/client/client/grapesjs/PublicationUi.d.ts +62 -0
- package/dist/client/client/grapesjs/blocks.d.ts +1 -0
- package/dist/client/client/grapesjs/breadcrumbs.d.ts +1 -0
- package/dist/client/client/grapesjs/footer.d.ts +2 -0
- package/dist/client/client/grapesjs/index.d.ts +5 -0
- package/dist/client/client/grapesjs/internal-links.d.ts +1 -0
- package/dist/client/client/grapesjs/new-page-dialog.d.ts +2 -0
- package/dist/client/client/grapesjs/openImport.d.ts +9 -0
- package/dist/client/client/grapesjs/page-panel.d.ts +3 -0
- package/dist/client/client/grapesjs/project-bar.d.ts +1 -0
- package/dist/client/client/grapesjs/rich-text.d.ts +1 -0
- package/dist/client/client/grapesjs/semantic.d.ts +1 -0
- package/dist/client/client/grapesjs/settings.d.ts +2 -0
- package/dist/client/client/grapesjs/storage.d.ts +1 -0
- package/dist/client/client/grapesjs/symbolDialogs.d.ts +3 -0
- package/dist/client/client/index.d.ts +5 -0
- package/dist/client/client/publication-transformers.d.ts +41 -0
- package/dist/client/client/utils.d.ts +2 -0
- package/dist/client/constants.d.ts +29 -0
- package/dist/client/css/admin.css +4491 -0
- package/dist/client/index.html +9 -0
- package/dist/client/js/main.js +321 -0
- package/dist/client/js/main.js.LICENSE.txt +24 -0
- package/dist/client/page.d.ts +2 -0
- package/dist/client/types.d.ts +334 -0
- package/dist/plugins/client/client/api.d.ts +85 -0
- package/dist/plugins/client/client/api.js +114 -0
- package/dist/plugins/client/client/api.js.map +1 -0
- package/dist/plugins/client/client/assetUrl.d.ts +51 -0
- package/dist/plugins/client/client/assetUrl.js +224 -0
- package/dist/plugins/client/client/assetUrl.js.map +1 -0
- package/dist/plugins/client/client/config.d.ts +58 -0
- package/dist/plugins/client/client/config.js +90 -0
- package/dist/plugins/client/client/config.js.map +1 -0
- package/dist/plugins/client/client/events.d.ts +14 -0
- package/dist/plugins/client/client/events.js +34 -0
- package/dist/plugins/client/client/events.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/LoginDialog.d.ts +15 -0
- package/dist/plugins/client/client/grapesjs/LoginDialog.js +195 -0
- package/dist/plugins/client/client/grapesjs/LoginDialog.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/PublicationManager.d.ts +72 -0
- package/dist/plugins/client/client/grapesjs/PublicationManager.js +356 -0
- package/dist/plugins/client/client/grapesjs/PublicationManager.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/PublicationUi.d.ts +62 -0
- package/dist/plugins/client/client/grapesjs/PublicationUi.js +336 -0
- package/dist/plugins/client/client/grapesjs/PublicationUi.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/blocks.d.ts +1 -0
- package/dist/plugins/client/client/grapesjs/blocks.js +37 -0
- package/dist/plugins/client/client/grapesjs/blocks.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/breadcrumbs.d.ts +1 -0
- package/dist/plugins/client/client/grapesjs/breadcrumbs.js +75 -0
- package/dist/plugins/client/client/grapesjs/breadcrumbs.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/footer.d.ts +2 -0
- package/dist/plugins/client/client/grapesjs/footer.js +28 -0
- package/dist/plugins/client/client/grapesjs/footer.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/index.d.ts +5 -0
- package/dist/plugins/client/client/grapesjs/index.js +313 -0
- package/dist/plugins/client/client/grapesjs/index.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/internal-links.d.ts +1 -0
- package/dist/plugins/client/client/grapesjs/internal-links.js +219 -0
- package/dist/plugins/client/client/grapesjs/internal-links.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/new-page-dialog.d.ts +2 -0
- package/dist/plugins/client/client/grapesjs/new-page-dialog.js +83 -0
- package/dist/plugins/client/client/grapesjs/new-page-dialog.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/openImport.d.ts +9 -0
- package/dist/plugins/client/client/grapesjs/openImport.js +74 -0
- package/dist/plugins/client/client/grapesjs/openImport.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/page-panel.d.ts +3 -0
- package/dist/plugins/client/client/grapesjs/page-panel.js +113 -0
- package/dist/plugins/client/client/grapesjs/page-panel.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/project-bar.d.ts +1 -0
- package/dist/plugins/client/client/grapesjs/project-bar.js +104 -0
- package/dist/plugins/client/client/grapesjs/project-bar.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/rich-text.d.ts +1 -0
- package/dist/plugins/client/client/grapesjs/rich-text.js +31 -0
- package/dist/plugins/client/client/grapesjs/rich-text.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/semantic.d.ts +1 -0
- package/dist/plugins/client/client/grapesjs/semantic.js +110 -0
- package/dist/plugins/client/client/grapesjs/semantic.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/settings.d.ts +2 -0
- package/dist/plugins/client/client/grapesjs/settings.js +198 -0
- package/dist/plugins/client/client/grapesjs/settings.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/storage.d.ts +1 -0
- package/dist/plugins/client/client/grapesjs/storage.js +126 -0
- package/dist/plugins/client/client/grapesjs/storage.js.map +1 -0
- package/dist/plugins/client/client/grapesjs/symbolDialogs.d.ts +3 -0
- package/dist/plugins/client/client/grapesjs/symbolDialogs.js +36 -0
- package/dist/plugins/client/client/grapesjs/symbolDialogs.js.map +1 -0
- package/dist/plugins/client/client/publication-transformers.d.ts +41 -0
- package/dist/plugins/client/client/publication-transformers.js +238 -0
- package/dist/plugins/client/client/publication-transformers.js.map +1 -0
- package/dist/plugins/client/client/utils.d.ts +2 -0
- package/dist/plugins/client/client/utils.js +25 -0
- package/dist/plugins/client/client/utils.js.map +1 -0
- package/dist/plugins/client/constants.d.ts +29 -0
- package/dist/plugins/client/constants.js +46 -0
- package/dist/plugins/client/constants.js.map +1 -0
- package/dist/plugins/client/page.d.ts +2 -0
- package/dist/plugins/client/page.js +31 -0
- package/dist/plugins/client/page.js.map +1 -0
- package/dist/plugins/client/plugins/client/11ty.d.ts +17 -0
- package/dist/plugins/client/plugins/client/11ty.js +86 -0
- package/dist/plugins/client/plugins/client/11ty.js.map +1 -0
- package/dist/plugins/client/plugins/client/publicationRenderer.d.ts +2 -0
- package/dist/plugins/client/plugins/client/publicationRenderer.js +170 -0
- package/dist/plugins/client/plugins/client/publicationRenderer.js.map +1 -0
- package/dist/plugins/client/plugins/client/template.d.ts +2 -0
- package/dist/plugins/client/plugins/client/template.js +183 -0
- package/dist/plugins/client/plugins/client/template.js.map +1 -0
- 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 +334 -0
- package/dist/plugins/client/types.js +74 -0
- package/dist/plugins/client/types.js.map +1 -0
- package/dist/plugins/server/constants.d.ts +29 -0
- package/dist/plugins/server/constants.js +49 -0
- package/dist/plugins/server/constants.js.map +1 -0
- package/dist/plugins/server/plugins/server/AuthPlugin.d.ts +1 -0
- package/dist/plugins/server/plugins/server/AuthPlugin.js +40 -0
- package/dist/plugins/server/plugins/server/AuthPlugin.js.map +1 -0
- package/dist/plugins/server/plugins/server/Cache.d.ts +2 -0
- package/dist/plugins/server/plugins/server/Cache.js +32 -0
- package/dist/plugins/server/plugins/server/Cache.js.map +1 -0
- package/dist/plugins/server/plugins/server/DownloadConnector.d.ts +29 -0
- package/dist/plugins/server/plugins/server/DownloadConnector.js +129 -0
- package/dist/plugins/server/plugins/server/DownloadConnector.js.map +1 -0
- package/dist/plugins/server/plugins/server/FtpConnector.d.ts +86 -0
- package/dist/plugins/server/plugins/server/FtpConnector.js +526 -0
- package/dist/plugins/server/plugins/server/FtpConnector.js.map +1 -0
- package/dist/plugins/server/plugins/server/GitlabConnector.d.ts +87 -0
- package/dist/plugins/server/plugins/server/GitlabConnector.js +564 -0
- package/dist/plugins/server/plugins/server/GitlabConnector.js.map +1 -0
- package/dist/plugins/server/plugins/server/PublicationOptimizer.d.ts +0 -0
- package/dist/plugins/server/plugins/server/PublicationOptimizer.js +14 -0
- package/dist/plugins/server/plugins/server/PublicationOptimizer.js.map +1 -0
- package/dist/plugins/server/plugins/server/SslPlugin.d.ts +1 -0
- package/dist/plugins/server/plugins/server/SslPlugin.js +86 -0
- package/dist/plugins/server/plugins/server/SslPlugin.js.map +1 -0
- package/dist/plugins/server/plugins/server/StaticPlugin.d.ts +1 -0
- package/dist/plugins/server/plugins/server/StaticPlugin.js +78 -0
- package/dist/plugins/server/plugins/server/StaticPlugin.js.map +1 -0
- package/dist/plugins/server/server/config.d.ts +59 -0
- package/dist/plugins/server/server/config.js +181 -0
- package/dist/plugins/server/server/config.js.map +1 -0
- package/dist/plugins/server/server/connectors/FsHosting.d.ts +13 -0
- package/dist/plugins/server/server/connectors/FsHosting.js +54 -0
- package/dist/plugins/server/server/connectors/FsHosting.js.map +1 -0
- package/dist/plugins/server/server/connectors/FsStorage.d.ts +43 -0
- package/dist/plugins/server/server/connectors/FsStorage.js +257 -0
- package/dist/plugins/server/server/connectors/FsStorage.js.map +1 -0
- package/dist/plugins/server/server/connectors/connectors.d.ts +87 -0
- package/dist/plugins/server/server/connectors/connectors.js +127 -0
- package/dist/plugins/server/server/connectors/connectors.js.map +1 -0
- package/dist/plugins/server/server/events.d.ts +26 -0
- package/dist/plugins/server/server/events.js +31 -0
- package/dist/plugins/server/server/events.js.map +1 -0
- package/dist/plugins/server/server/jobs.d.ts +12 -0
- package/dist/plugins/server/server/jobs.js +77 -0
- package/dist/plugins/server/server/jobs.js.map +1 -0
- package/dist/plugins/server/server/utils/validation.d.ts +7 -0
- package/dist/plugins/server/server/utils/validation.js +38 -0
- package/dist/plugins/server/server/utils/validation.js.map +1 -0
- package/dist/plugins/server/types.d.ts +334 -0
- package/dist/plugins/server/types.js +78 -0
- package/dist/plugins/server/types.js.map +1 -0
- package/dist/server/constants.js +29 -0
- package/dist/server/plugins/server/Cache.js +15 -0
- package/dist/server/server/api/api.js +20 -0
- package/dist/server/server/api/connectorApi.js +216 -0
- package/dist/server/server/api/publicationApi.js +108 -0
- package/dist/server/server/api/websiteApi.js +296 -0
- package/dist/server/server/cli.js +68 -0
- package/dist/server/server/config.js +120 -0
- package/dist/server/server/connectors/FsHosting.js +35 -0
- package/dist/server/server/connectors/FsStorage.js +225 -0
- package/dist/server/server/connectors/connectors.js +89 -0
- package/dist/server/server/events.js +14 -0
- package/dist/server/server/expose.js +27 -0
- package/dist/server/server/express.js +47 -0
- package/dist/server/server/index.js +90 -0
- package/dist/server/server/jobs.js +48 -0
- package/dist/server/server/utils/validation.js +15 -0
- package/dist/server/types.js +34 -0
- package/package.json +209 -0
- package/public/assets/container.png +0 -0
- package/public/assets/favicon.png +0 -0
- package/public/assets/logo-silex-small.png +0 -0
- package/public/robots.txt +2 -0
- package/public/silex.js +24 -0
- package/src/css/_mixins.scss +8 -0
- package/src/css/_variables.scss +33 -0
- package/src/css/dialog.scss +41 -0
- package/src/css/footer.scss +12 -0
- package/src/css/grapesjs-light.scss +180 -0
- package/src/css/grapesjs.scss +152 -0
- package/src/css/index.scss +32 -0
- package/src/css/light.scss +7 -0
- package/src/css/loader.scss +12 -0
- package/src/css/page-panel.scss +65 -0
- package/src/css/project-bar.scss +102 -0
- package/src/css/silex.scss +189 -0
- package/src/css/website-info.scss +14 -0
- package/src/html/debug.pug +8 -0
- package/src/html/index.pug +39 -0
- package/src/html/templates/includes.pug +1 -0
- package/src/html/templates/workspace.pug +2 -0
- package/src/ts/client/api.ts +144 -0
- package/src/ts/client/assetUrl.test.ts +125 -0
- package/src/ts/client/assetUrl.ts +235 -0
- package/src/ts/client/config.ts +105 -0
- package/src/ts/client/events.ts +34 -0
- package/src/ts/client/expose.ts +30 -0
- package/src/ts/client/grapesjs/LoginDialog.ts +204 -0
- package/src/ts/client/grapesjs/PublicationManager.ts +370 -0
- package/src/ts/client/grapesjs/PublicationUi.ts +342 -0
- package/src/ts/client/grapesjs/blocks.ts +37 -0
- package/src/ts/client/grapesjs/breadcrumbs.ts +79 -0
- package/src/ts/client/grapesjs/eleventy.ts +47 -0
- package/src/ts/client/grapesjs/footer.ts +27 -0
- package/src/ts/client/grapesjs/index.ts +335 -0
- package/src/ts/client/grapesjs/internal-links.test.ts +105 -0
- package/src/ts/client/grapesjs/internal-links.ts +225 -0
- package/src/ts/client/grapesjs/new-page-dialog.ts +90 -0
- package/src/ts/client/grapesjs/openImport.ts +81 -0
- package/src/ts/client/grapesjs/page-panel.ts +118 -0
- package/src/ts/client/grapesjs/project-bar.ts +107 -0
- package/src/ts/client/grapesjs/rich-text.ts +33 -0
- package/src/ts/client/grapesjs/semantic.ts +114 -0
- package/src/ts/client/grapesjs/settings.ts +208 -0
- package/src/ts/client/grapesjs/storage.ts +121 -0
- package/src/ts/client/grapesjs/symbolDialogs.ts +38 -0
- package/src/ts/client/index.ts +98 -0
- package/src/ts/client/publication-transformers.test.ts +199 -0
- package/src/ts/client/publication-transformers.ts +263 -0
- package/src/ts/client/utils.ts +28 -0
- package/src/ts/constants.ts +52 -0
- package/src/ts/externs.ts +18 -0
- package/src/ts/page.ts +31 -0
- package/src/ts/plugins/client/11ty.ts +104 -0
- package/src/ts/plugins/client/publicationRenderer.ts +174 -0
- package/src/ts/plugins/client/template.ts +192 -0
- package/src/ts/plugins/client/website-info.ts +84 -0
- package/src/ts/plugins/server/AuthPlugin.ts +38 -0
- package/src/ts/plugins/server/Cache.ts +28 -0
- package/src/ts/plugins/server/DownloadConnector.ts +148 -0
- package/src/ts/plugins/server/FtpConnector.test.ts +82 -0
- package/src/ts/plugins/server/FtpConnector.ts +609 -0
- package/src/ts/plugins/server/GitlabConnector.ts +644 -0
- package/src/ts/plugins/server/PublicationOptimizer.ts +14 -0
- package/src/ts/plugins/server/SslPlugin.ts +84 -0
- package/src/ts/plugins/server/StaticPlugin.ts +79 -0
- package/src/ts/server/api/api.ts +19 -0
- package/src/ts/server/api/connectorApi.ts +345 -0
- package/src/ts/server/api/publicationApi.ts +159 -0
- package/src/ts/server/api/websiteApi.ts +432 -0
- package/src/ts/server/cli.ts +65 -0
- package/src/ts/server/config.ts +189 -0
- package/src/ts/server/connectors/FsHosting.ts +60 -0
- package/src/ts/server/connectors/FsStorage.test.ts +51 -0
- package/src/ts/server/connectors/FsStorage.ts +261 -0
- package/src/ts/server/connectors/connectors.ts +203 -0
- package/src/ts/server/events.ts +37 -0
- package/src/ts/server/expose.ts +19 -0
- package/src/ts/server/express.ts +70 -0
- package/src/ts/server/index.ts +100 -0
- package/src/ts/server/jobs.test.ts +75 -0
- package/src/ts/server/jobs.ts +87 -0
- package/src/ts/server/utils/BackwardCompat.ts +552 -0
- package/src/ts/server/utils/validation.ts +34 -0
- package/src/ts/types.ts +339 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Silex website builder, free/libre no-code tool for makers.
|
|
3
|
+
* Copyright (c) 2023 lexoyo and Silex Labs foundation
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
* the Free Software Foundation, either version 3 of the License, or any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU Affero General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { Router } from 'express'
|
|
19
|
+
import formidable from 'formidable'
|
|
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'
|
|
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'
|
|
24
|
+
import { ConnectorFile, ConnectorFileContent, ConnectorSession, StorageConnector, getConnector } from '../connectors/connectors'
|
|
25
|
+
import { Readable } from 'stream'
|
|
26
|
+
import { requiredParam } from '../utils/validation'
|
|
27
|
+
import { basename } from 'path'
|
|
28
|
+
import { ServerConfig } from '../config'
|
|
29
|
+
import { ServerEvent, WebsiteStoreEndEventType, WebsiteStoreStartEventType, WebsiteAssetStoreStartEventType, WebsiteAssetStoreEndEventType } from '../events'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @fileoverview Website plugin for Silex
|
|
33
|
+
* This plugin provides the website API to Silex server
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
export default function (config: ServerConfig, opts = {}): Router {
|
|
37
|
+
// Options with defaults
|
|
38
|
+
const options = {
|
|
39
|
+
// Default constants
|
|
40
|
+
assetsPath: '/assets',
|
|
41
|
+
// Options
|
|
42
|
+
...opts
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Create a new router
|
|
46
|
+
const router = Router()
|
|
47
|
+
|
|
48
|
+
// Load website data
|
|
49
|
+
router.get(API_WEBSITE_READ, async (req, res, next) => {
|
|
50
|
+
const query = req.query as ApiWebsiteReadQuery
|
|
51
|
+
const { websiteId, connectorId } = query
|
|
52
|
+
const session = req['session'] as ConnectorSession
|
|
53
|
+
if(!websiteId) {
|
|
54
|
+
// List websites
|
|
55
|
+
next()
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
// Get website data
|
|
60
|
+
const websiteData: WebsiteData | Readable = await readWebsite(
|
|
61
|
+
session,
|
|
62
|
+
websiteId,
|
|
63
|
+
connectorId,
|
|
64
|
+
)
|
|
65
|
+
if (websiteData instanceof Readable) {
|
|
66
|
+
websiteData.pipe(res.type('application/json'))
|
|
67
|
+
} else {
|
|
68
|
+
res.json(websiteData as ApiWebsiteReadResponse)
|
|
69
|
+
}
|
|
70
|
+
} catch (e) {
|
|
71
|
+
if (e.httpStatusCode) {
|
|
72
|
+
res.status(e.httpStatusCode).json({ message: e.message } as ApiError)
|
|
73
|
+
} else {
|
|
74
|
+
res.status(500).json({ message: e.message } as ApiError)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// List websites
|
|
80
|
+
router.get(API_WEBSITE_LIST, async (req, res) => {
|
|
81
|
+
const query: ApiWebsiteListQuery = req.query
|
|
82
|
+
const { connectorId } = query
|
|
83
|
+
const session = req['session'] as ConnectorSession
|
|
84
|
+
try {
|
|
85
|
+
// List websites
|
|
86
|
+
const websites = await listWebsites(req['session'], query.connectorId as string | undefined)
|
|
87
|
+
res.json(websites as ApiWebsiteListResponse)
|
|
88
|
+
} catch (e) {
|
|
89
|
+
console.error('Error getting website data', e)
|
|
90
|
+
if (e.httpStatusCode) {
|
|
91
|
+
res.status(e.httpStatusCode).json({ message: e.message } as ApiError)
|
|
92
|
+
} else {
|
|
93
|
+
res.status(500).json({ message: e.message } as ApiError)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
// Save website data
|
|
99
|
+
router.post(API_WEBSITE_WRITE, async (req, res) => {
|
|
100
|
+
try {
|
|
101
|
+
// Check input
|
|
102
|
+
const query: ApiWebsiteWriteQuery = req.query as any
|
|
103
|
+
const body: ApiWebsiteWriteBody = req.body
|
|
104
|
+
const websiteId= requiredParam<WebsiteId>(query.websiteId, 'Website id')
|
|
105
|
+
const websiteData = requiredParam<WebsiteData>(body, 'Website data') as WebsiteData
|
|
106
|
+
const connectorId = query.connectorId // Optional
|
|
107
|
+
// Hook to modify the website data before saving
|
|
108
|
+
config.emit(ServerEvent.WEBSITE_STORE_START, { websiteId, websiteData, connectorId } as WebsiteStoreStartEventType)
|
|
109
|
+
// Save website data
|
|
110
|
+
await writeWebsite(
|
|
111
|
+
req['session'],
|
|
112
|
+
websiteId,
|
|
113
|
+
websiteData,
|
|
114
|
+
connectorId,
|
|
115
|
+
)
|
|
116
|
+
config.emit(ServerEvent.WEBSITE_STORE_END, null as WebsiteStoreEndEventType)
|
|
117
|
+
res.status(200).json({ message: 'Website saved' } as ApiWebsiteWriteResponse)
|
|
118
|
+
} catch (e) {
|
|
119
|
+
console.error('Error saving website data', e)
|
|
120
|
+
config.emit(ServerEvent.WEBSITE_STORE_END, e as WebsiteStoreEndEventType)
|
|
121
|
+
if (e.httpStatusCode) {
|
|
122
|
+
res.status(e.httpStatusCode).json({ message: e.message } as ApiError)
|
|
123
|
+
} else {
|
|
124
|
+
res.status(500).json({ message: e.message } as ApiError)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
// Create website or update website meta
|
|
130
|
+
router.put(API_WEBSITE_CREATE, async (req, res) => {
|
|
131
|
+
try {
|
|
132
|
+
// Check input
|
|
133
|
+
const session = req['session'] as ConnectorSession
|
|
134
|
+
const query: ApiWebsiteCreateQuery = req.query as any
|
|
135
|
+
const body: ApiWebsiteCreateBody = req.body
|
|
136
|
+
const websiteMeta = requiredParam<WebsiteMetaFileContent>(body, 'Website meta') as WebsiteMeta
|
|
137
|
+
const connectorId = query.connectorId
|
|
138
|
+
const connector = await getConnector<StorageConnector>(config, session, ConnectorType.STORAGE, connectorId)
|
|
139
|
+
if(!connector) {
|
|
140
|
+
throw new ApiError(`Connector ${connectorId} not found`, 500)
|
|
141
|
+
}
|
|
142
|
+
// Create website
|
|
143
|
+
await connector.createWebsite(
|
|
144
|
+
session,
|
|
145
|
+
websiteMeta,
|
|
146
|
+
)
|
|
147
|
+
res.json({ message: 'Website meta saved' } as ApiWebsiteMetaWriteResponse)
|
|
148
|
+
} catch (e) {
|
|
149
|
+
console.error('Error saving website meta', e)
|
|
150
|
+
if (e.httpStatusCode) {
|
|
151
|
+
res.status(e.httpStatusCode).json({ message: e.message } as ApiError)
|
|
152
|
+
} else {
|
|
153
|
+
res.status(500).json({ message: e.message } as ApiError)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
// Update website meta
|
|
159
|
+
router.post(API_WEBSITE_META_WRITE, async (req, res) => {
|
|
160
|
+
try {
|
|
161
|
+
// Check input
|
|
162
|
+
const session = req['session'] as ConnectorSession
|
|
163
|
+
const query: ApiWebsiteMetaWriteQuery = req.query as any
|
|
164
|
+
const body: ApiWebsiteMetaWriteBody = req.body
|
|
165
|
+
const websiteId= requiredParam<WebsiteId>(query.websiteId, 'Website id')
|
|
166
|
+
const websiteMeta = requiredParam<WebsiteMetaFileContent>(body, 'Website meta') as WebsiteMeta
|
|
167
|
+
const connectorId = query.connectorId
|
|
168
|
+
const connector = await getConnector<StorageConnector>(config, session, ConnectorType.STORAGE, connectorId)
|
|
169
|
+
if(!connector) {
|
|
170
|
+
throw new ApiError(`Connector ${connectorId} not found`, 500)
|
|
171
|
+
}
|
|
172
|
+
// Update website meta
|
|
173
|
+
await connector.setWebsiteMeta(
|
|
174
|
+
session,
|
|
175
|
+
websiteId,
|
|
176
|
+
websiteMeta,
|
|
177
|
+
)
|
|
178
|
+
res.json({ message: 'Website meta saved' } as ApiWebsiteMetaWriteResponse)
|
|
179
|
+
} catch (e) {
|
|
180
|
+
console.error('Error saving website meta', e)
|
|
181
|
+
if (e.httpStatusCode) {
|
|
182
|
+
res.status(e.httpStatusCode).json({ message: e.message } as ApiError)
|
|
183
|
+
} else {
|
|
184
|
+
res.status(500).json({ message: e.message } as ApiError)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
// Get website meta
|
|
190
|
+
router.get(API_WEBSITE_META_READ, async (req, res) => {
|
|
191
|
+
try {
|
|
192
|
+
const session = req['session'] as ConnectorSession
|
|
193
|
+
const query: ApiWebsiteMetaReadQuery = req.query as any
|
|
194
|
+
const websiteId= requiredParam<WebsiteId>(query.websiteId, 'Website id')
|
|
195
|
+
const connectorId = query.connectorId
|
|
196
|
+
const connector = await getConnector<StorageConnector>(config, session, ConnectorType.STORAGE, connectorId)
|
|
197
|
+
if(!connector) {
|
|
198
|
+
throw new ApiError(`Connector ${connectorId} not found`, 500)
|
|
199
|
+
}
|
|
200
|
+
const websiteMeta: WebsiteMeta = await connector.getWebsiteMeta(session, websiteId)
|
|
201
|
+
res.json(websiteMeta as ApiWebsiteMetaReadResponse)
|
|
202
|
+
} catch (e) {
|
|
203
|
+
console.error('Error getting website meta', e)
|
|
204
|
+
if (e.httpStatusCode) {
|
|
205
|
+
res.status(e.httpStatusCode).json({ message: e.message } as ApiError)
|
|
206
|
+
} else {
|
|
207
|
+
res.status(500).json({ message: e.message } as ApiError)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
// Delete website
|
|
213
|
+
router.delete(API_WEBSITE_DELETE, async (req, res) => {
|
|
214
|
+
try {
|
|
215
|
+
const query: ApiWebsiteDeleteQuery = req.query as any
|
|
216
|
+
const websiteId= requiredParam<WebsiteId>(query.websiteId, 'Website id')
|
|
217
|
+
await deleteWebsite(req['session'], websiteId, query.connectorId)
|
|
218
|
+
res.status(200).json({ message: 'Website deleted' } as ApiError)
|
|
219
|
+
} catch (e) {
|
|
220
|
+
console.error('Error deleting website data', e)
|
|
221
|
+
if (e.httpStatusCode) {
|
|
222
|
+
res.status(e.httpStatusCode).json({ message: e.message } as ApiError)
|
|
223
|
+
} else {
|
|
224
|
+
res.status(500).json({ message: e.message } as ApiError)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
// Load assets
|
|
230
|
+
router.get(API_WEBSITE_ASSET_READ + '/:path', async (req, res) => {
|
|
231
|
+
{
|
|
232
|
+
try {
|
|
233
|
+
const query: ApiWebsiteAssetsReadQuery = req.query as any
|
|
234
|
+
const params: ApiWebsiteAssetsReadParams = req.params as any
|
|
235
|
+
const websiteId= requiredParam<WebsiteId>(query.websiteId, 'Website id')
|
|
236
|
+
const path = requiredParam<string>(params.path, 'path')
|
|
237
|
+
const asset: ConnectorFileContent = await readAsset(req['session'], websiteId, path, query.connectorId)
|
|
238
|
+
// Set content type
|
|
239
|
+
res.contentType(basename(path))
|
|
240
|
+
// Send the file
|
|
241
|
+
if (asset instanceof Readable) {
|
|
242
|
+
// Stream
|
|
243
|
+
asset.pipe(res)
|
|
244
|
+
} else {
|
|
245
|
+
// Buffer or string
|
|
246
|
+
res.send(asset as ApiWebsiteAssetsReadResponse)
|
|
247
|
+
}
|
|
248
|
+
} catch (e) {
|
|
249
|
+
console.error('Error getting asset', e)
|
|
250
|
+
if (e.httpStatusCode) {
|
|
251
|
+
res.status(e.httpStatusCode).json({ message: e.message } as ApiError)
|
|
252
|
+
} else {
|
|
253
|
+
res.status(500).json({ message: e.message } as ApiError)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
// Upload assets
|
|
260
|
+
router.post(API_WEBSITE_ASSETS_WRITE, async (req, res) => {
|
|
261
|
+
try {
|
|
262
|
+
// Check input
|
|
263
|
+
const query: ApiWebsiteAssetsWriteQuery = req.query as any
|
|
264
|
+
const websiteId= requiredParam<WebsiteId>(query.websiteId as WebsiteId, 'Website id')
|
|
265
|
+
|
|
266
|
+
// Get the file data from the request
|
|
267
|
+
const form = formidable({
|
|
268
|
+
filename: (name, ext, part, _form) => `${name}${ext}`,
|
|
269
|
+
multiples: true,
|
|
270
|
+
keepExtensions: true,
|
|
271
|
+
})
|
|
272
|
+
const connectorId = query.connectorId // Optional
|
|
273
|
+
|
|
274
|
+
// Retrive the files
|
|
275
|
+
const files: ConnectorFile[] = await new Promise<ConnectorFile[]>((resolve, reject) => {
|
|
276
|
+
form.parse(req, async (err, fields, _files) => {
|
|
277
|
+
if (err) {
|
|
278
|
+
console.error('Error parsing upload data', err)
|
|
279
|
+
reject(new ApiError('Error parsing upload data: ' + err.message, 400))
|
|
280
|
+
} else {
|
|
281
|
+
const files = ([].concat(_files['files[]']) as PersistentFile[])
|
|
282
|
+
.map(file => file.toJSON())
|
|
283
|
+
.map(file => ({
|
|
284
|
+
path: `/${file.originalFilename}`,
|
|
285
|
+
content: createReadStream(file.filepath),
|
|
286
|
+
}))
|
|
287
|
+
resolve(files)
|
|
288
|
+
}
|
|
289
|
+
})
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
// Hook to modify the files
|
|
293
|
+
config.emit(ServerEvent.WEBSITE_ASSET_STORE_START, { files, websiteId, connectorId } as WebsiteAssetStoreStartEventType)
|
|
294
|
+
|
|
295
|
+
// Write the files
|
|
296
|
+
const result = await writeAssets(req['session'], websiteId, files, connectorId)
|
|
297
|
+
|
|
298
|
+
// Base URL of silex serve
|
|
299
|
+
const baseUrl = new URL(config.url).pathname.replace(/\/$/, '')
|
|
300
|
+
|
|
301
|
+
// Return the file URLs to insert in the website
|
|
302
|
+
// As expected by grapesjs (https://grapesjs.com/docs/modules/Assets.html#uploading-assets)
|
|
303
|
+
const data = result.map(path =>
|
|
304
|
+
// FIXME: We should return path without this line, as it is saved, not as it is displayed
|
|
305
|
+
baseUrl + API_PATH + API_WEBSITE_PATH + API_WEBSITE_ASSET_READ
|
|
306
|
+
+ path
|
|
307
|
+
+ `?websiteId=${websiteId}&connectorId=${connectorId ? connectorId : ''}` // As expected by wesite API (readAsset)
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
// Return the file URLs
|
|
311
|
+
res.json({
|
|
312
|
+
data,
|
|
313
|
+
} as ApiWebsiteAssetsWriteResponse)
|
|
314
|
+
|
|
315
|
+
// Hook for plugins
|
|
316
|
+
config.emit(ServerEvent.WEBSITE_ASSET_STORE_END, null as WebsiteAssetStoreEndEventType)
|
|
317
|
+
} catch (e) {
|
|
318
|
+
console.error('Error uploading assets', e)
|
|
319
|
+
if (e.httpStatusCode) {
|
|
320
|
+
res.status(e.httpStatusCode).json({ message: e.message } as ApiError)
|
|
321
|
+
} else {
|
|
322
|
+
res.status(500).json({ message: e.message } as ApiError)
|
|
323
|
+
}
|
|
324
|
+
// Hook for plugins
|
|
325
|
+
config.emit(ServerEvent.WEBSITE_ASSET_STORE_END, e as WebsiteAssetStoreEndEventType)
|
|
326
|
+
}
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Get the desired connector
|
|
331
|
+
* Can be the default connector or a specific one
|
|
332
|
+
*/
|
|
333
|
+
async function getStorageConnector(session: any, connectorId?: string): Promise<StorageConnector> {
|
|
334
|
+
const storageConnector = await getConnector(config, session, ConnectorType.STORAGE, connectorId) // ?? config.getStorageConnectors()[0]
|
|
335
|
+
|
|
336
|
+
if (!storageConnector) {
|
|
337
|
+
throw new ApiError('No storage connector found', 404)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (!await storageConnector.isLoggedIn(session)) {
|
|
341
|
+
throw new ApiError('Not logged in', 401)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return storageConnector as StorageConnector
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Read the website data
|
|
349
|
+
*/
|
|
350
|
+
async function readWebsite(session: any, websiteId: string, connectorId?: string): Promise<WebsiteData | Readable> {
|
|
351
|
+
// Get the desired connector
|
|
352
|
+
const storageConnector = await getStorageConnector(session, connectorId)
|
|
353
|
+
|
|
354
|
+
// Return website data
|
|
355
|
+
return storageConnector.readWebsite(session, websiteId)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* List existing websites
|
|
360
|
+
*/
|
|
361
|
+
async function listWebsites(session: any, connectorId?: string): Promise<WebsiteMeta[]> {
|
|
362
|
+
// Get the desired connector
|
|
363
|
+
const storageConnector = await getStorageConnector(session, connectorId)
|
|
364
|
+
|
|
365
|
+
// List websites
|
|
366
|
+
return storageConnector.listWebsites(session)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Write the website data to the connector
|
|
371
|
+
*/
|
|
372
|
+
async function writeWebsite(session: any, websiteId: WebsiteId, websiteData: WebsiteData, connectorId?: ConnectorId): Promise<void> {
|
|
373
|
+
// Get the desired connector
|
|
374
|
+
const storageConnector = await getStorageConnector(session, connectorId)
|
|
375
|
+
|
|
376
|
+
// Write the website data
|
|
377
|
+
await storageConnector.updateWebsite(session, websiteId, websiteData)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Delete a website
|
|
382
|
+
*/
|
|
383
|
+
async function deleteWebsite(session: any, websiteId: string, connectorId?: string): Promise<void> {
|
|
384
|
+
// Get the desired connector
|
|
385
|
+
const storageConnector = await getStorageConnector(session, connectorId)
|
|
386
|
+
|
|
387
|
+
// Delete the website
|
|
388
|
+
return storageConnector.deleteWebsite(session, websiteId)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Read an asset
|
|
393
|
+
*/
|
|
394
|
+
async function readAsset(session: any, websiteId: string, fileName: string, connectorId?: string): Promise<ConnectorFileContent> {
|
|
395
|
+
// Get the desired connector
|
|
396
|
+
const storageConnector = await getStorageConnector(session, connectorId)
|
|
397
|
+
|
|
398
|
+
// Read the asset from the connector
|
|
399
|
+
return storageConnector.readAsset(session, websiteId, `/${fileName}`)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Write an asset to the connector
|
|
404
|
+
* @returns File names on the storage connector, always starting with a slash
|
|
405
|
+
*/
|
|
406
|
+
async function writeAssets(session: any, websiteId: string, files: ConnectorFile[], connectorId?: string): Promise<string[]> {
|
|
407
|
+
// Get the desired connector
|
|
408
|
+
const storageConnector = await getStorageConnector(session, connectorId)
|
|
409
|
+
|
|
410
|
+
// Clean up the path
|
|
411
|
+
const cleanPathFiles = files.map(file => ({
|
|
412
|
+
...file,
|
|
413
|
+
path: file.path.replace('/assets/', '/'), // Remove the assets folder added by GrapesJS
|
|
414
|
+
}))
|
|
415
|
+
|
|
416
|
+
// Write the asset to the connector
|
|
417
|
+
const result = await storageConnector.writeAssets(
|
|
418
|
+
session,
|
|
419
|
+
websiteId,
|
|
420
|
+
cleanPathFiles,
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
// Return the files URLs with the website id
|
|
424
|
+
return files
|
|
425
|
+
// Use the original path or the one returned by the connector
|
|
426
|
+
.map(({ path }, idx) => result && result[idx] ? result[idx] : path)
|
|
427
|
+
// Make it an absolute path with the website id and the connector id as query params
|
|
428
|
+
//.map((path) => toAssetUrl(path, config.url, websiteId, connectorId))
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return router
|
|
432
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Silex website builder, free/libre no-code tool for makers.
|
|
5
|
+
* Copyright (c) 2023 lexoyo and Silex Labs foundation
|
|
6
|
+
*
|
|
7
|
+
* This program is free software: you can redistribute it and/or modify
|
|
8
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
9
|
+
* the Free Software Foundation, either version 3 of the License, or any later version.
|
|
10
|
+
*
|
|
11
|
+
* This program is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU Affero General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
17
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import silex from './index'
|
|
21
|
+
import { parse } from 'cli'
|
|
22
|
+
|
|
23
|
+
const options = parse({
|
|
24
|
+
'url': ['', 'URL of your server', 'string'],
|
|
25
|
+
'host': ['', 'Sets the host param of the express module "cookie-session".', 'string'],
|
|
26
|
+
'port': [ 'p', 'Port to listen to', 'int'],
|
|
27
|
+
'protocol': ['', 'Sets the protocol param of the express module "cookie-session".', 'string'],
|
|
28
|
+
'config': [ 'c', 'Path for the server side config file to load at startup', 'file'],
|
|
29
|
+
'client-config': ['', 'Path to client config file to be served on .silex-client.js', 'string'],
|
|
30
|
+
'fs-root': ['', 'Path to the root folder where to store websites. Used by the default connector (fs).', 'string'],
|
|
31
|
+
'session-name': ['', 'Sets the name param of the express module "cookie-session".', 'string'],
|
|
32
|
+
'session-secret': ['s', 'Session secret', 'string'],
|
|
33
|
+
'ssl-port': [ false, 'Port to listen to for SSL/HTTPS', 'int'],
|
|
34
|
+
'force-https': [ false, 'Force HTTPS', 'boolean'],
|
|
35
|
+
'ssl-private-key': [ false, 'Path to private key for SSL', 'file'],
|
|
36
|
+
'ssl-certificate': [ false, 'Path to SSL certificate', 'file'],
|
|
37
|
+
'force-https-trust-xfp-header': [ false, 'Sets the trustXFPHeader param of the express module "cookie-session". Use only with --force-https', 'boolean'],
|
|
38
|
+
'cors-url': ['', 'Enable CORS for URL (can be "*")', 'string'],
|
|
39
|
+
'express-json-limit': ['', 'Sets the limit param of the express module "json".', 'string'],
|
|
40
|
+
'express-text-limit': ['', 'Sets the limit param of the express module "text".', 'string'],
|
|
41
|
+
'express-urlencoded-limit': ['', 'Sets the limit param of the express module "urlencoded".', 'string'],
|
|
42
|
+
'debug': [false, 'Debug mode, with live reload, source maps etc.', 'boolean'],
|
|
43
|
+
}, {})
|
|
44
|
+
|
|
45
|
+
if(options['url']) process.env.SILEX_URL = options['url']
|
|
46
|
+
if(options['host']) process.env.SILEX_HOST = options['host']
|
|
47
|
+
if(options.port) process.env.SILEX_PORT = options.port
|
|
48
|
+
if(options['protocol']) process.env.SILEX_PROTOCOL = options['protocol']
|
|
49
|
+
if(options.config) process.env.SILEX_CONFIG = options.config
|
|
50
|
+
if(options['client-config']) process.env.SILEX_CLIENT_CONFIG = options['client-config']
|
|
51
|
+
if(options['fs-root']) process.env.SILEX_FS_ROOT = options['fs-root']
|
|
52
|
+
if(options['session-name']) process.env.SILEX_SESSION_NAME = options['session-name']
|
|
53
|
+
if(options['session-secret']) process.env.SILEX_SESSION_SECRET = options['session-secret']
|
|
54
|
+
if(options['ssl-port']) process.env.SILEX_SSL_PORT = options['ssl-port']
|
|
55
|
+
if(options['force-https']) process.env.SILEX_FORCE_HTTPS = options['force-https']
|
|
56
|
+
if(options['ssl-private-key']) process.env.SILEX_SSL_PRIVATE_KEY = options['ssl-private-key']
|
|
57
|
+
if(options['ssl-certificate']) process.env.SILEX_SSL_CERTIFICATE = options['ssl-certificate']
|
|
58
|
+
if(options['force-https-trust-xfp-header']) process.env.SILEX_FORCE_HTTPS_TRUST_XFP_HEADER = options['force-https-trust-xfp-header']
|
|
59
|
+
if(options['cors-url']) process.env.SILEX_CORS_URL = options['cors-url']
|
|
60
|
+
if(options['express-json-limit']) process.env.SILEX_EXPRESS_JSON_LIMIT = options['express-json-limit']
|
|
61
|
+
if(options['express-text-limit']) process.env.SILEX_EXPRESS_TEXT_LIMIT = options['express-text-limit']
|
|
62
|
+
if(options['express-urlencoded-limit']) process.env.SILEX_EXPRESS_URLENCODED_LIMIT = options['express-urlencoded-limit']
|
|
63
|
+
if(options.debug) process.env.SILEX_DEBUG = options.debug
|
|
64
|
+
|
|
65
|
+
silex()
|
|
@@ -0,0 +1,189 @@
|
|
|
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
|
+
* @fileoverview This is where the default config is defined. Also use this object as an event emitter to listen to Silex events
|
|
20
|
+
* The values can be overriden with env vars or before passing the config to Silex
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @class default config for Silex server
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { Config, Plugin } from '@silexlabs/silex-plugins'
|
|
28
|
+
import { resolve } from 'path'
|
|
29
|
+
import { CLIENT_CONFIG_FILE_NAME } from '../constants'
|
|
30
|
+
import { Application, Request, Response, Router } from 'express'
|
|
31
|
+
import { readFile } from 'fs/promises'
|
|
32
|
+
import { HostingConnector, StorageConnector, toConnectorEnum } from './connectors/connectors'
|
|
33
|
+
import { FsStorage } from './connectors/FsStorage'
|
|
34
|
+
import { Connector } from './connectors/connectors'
|
|
35
|
+
import { ConnectorType } from '../types'
|
|
36
|
+
import { FsHosting } from './connectors/FsHosting'
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Config types definitions
|
|
40
|
+
*/
|
|
41
|
+
export class ServerConfig extends Config {
|
|
42
|
+
public expressOptions = {
|
|
43
|
+
jsonLimit: process.env.SILEX_EXPRESS_JSON_LIMIT,
|
|
44
|
+
textLimit: process.env.SILEX_EXPRESS_TEXT_LIMIT,
|
|
45
|
+
urlencodedLimit: process.env.SILEX_EXPRESS_URLENCODED_LIMIT,
|
|
46
|
+
sessionName: process.env.SILEX_SESSION_NAME,
|
|
47
|
+
sessionSecret: process.env.SILEX_SESSION_SECRET,
|
|
48
|
+
cors: process.env.SILEX_CORS_URL,
|
|
49
|
+
}
|
|
50
|
+
public port = process.env.SILEX_PORT
|
|
51
|
+
public debug = process.env.SILEX_DEBUG === 'true'
|
|
52
|
+
public url = process.env.SILEX_URL?.replace(/\/$/, '') ?? `${process.env.SILEX_PROTOCOL}://${process.env.SILEX_HOST}:${process.env.SILEX_PORT}`
|
|
53
|
+
public userConfigPath: Plugin | undefined = process.env.SILEX_CONFIG
|
|
54
|
+
public configFilePath: Plugin = resolve(__dirname, '../../../.silex.js')
|
|
55
|
+
|
|
56
|
+
// All connectors or just the storage or hosting connectors
|
|
57
|
+
getConnectors(type: ConnectorType | string | null = null): Connector[] {
|
|
58
|
+
// All connectors including storage and hosting connectors
|
|
59
|
+
if (!type) {
|
|
60
|
+
// Concat storage and hosting connectors
|
|
61
|
+
const allConnectors: Connector[] = (this.getStorageConnectors() as Connector[]).concat(this.getHostingConnectors())
|
|
62
|
+
|
|
63
|
+
// Remove duplicates (if a connector is both a storage and a hosting connector)
|
|
64
|
+
return [...new Set(allConnectors)]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Convert to enum in case it is a string
|
|
68
|
+
const connectorType = toConnectorEnum(type.toString())
|
|
69
|
+
|
|
70
|
+
// Only one type of connector
|
|
71
|
+
switch(connectorType) {
|
|
72
|
+
case ConnectorType.HOSTING: return this.getHostingConnectors()
|
|
73
|
+
case ConnectorType.STORAGE: return this.getStorageConnectors()
|
|
74
|
+
default: throw new Error(`Unknown connector type ${type}`)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Storage connectors to store the website data and assets
|
|
79
|
+
private storageConnectors: StorageConnector[] = []
|
|
80
|
+
addStorageConnector(storage: StorageConnector | StorageConnector[]) {
|
|
81
|
+
this.setStorageConnectors(this.storageConnectors.concat(storage))
|
|
82
|
+
}
|
|
83
|
+
setStorageConnectors(storageConnectors: StorageConnector[]) {
|
|
84
|
+
this.storageConnectors =storageConnectors
|
|
85
|
+
}
|
|
86
|
+
getStorageConnectors(): StorageConnector[] {
|
|
87
|
+
return this.storageConnectors
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Hosting connectors to publish the website online
|
|
91
|
+
private hostingConnectors: HostingConnector[] = []
|
|
92
|
+
addHostingConnector(hosting: HostingConnector | HostingConnector[]) {
|
|
93
|
+
this.setHostingConnectors(this.hostingConnectors.concat(hosting))
|
|
94
|
+
}
|
|
95
|
+
setHostingConnectors(hostings: HostingConnector[]) {
|
|
96
|
+
this.hostingConnectors = hostings
|
|
97
|
+
}
|
|
98
|
+
getHostingConnectors(): HostingConnector[] { return this.hostingConnectors }
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Add routes to serve the client config file
|
|
102
|
+
*/
|
|
103
|
+
async addRoutes(app: Application) {
|
|
104
|
+
const path = process.env.SILEX_CLIENT_CONFIG
|
|
105
|
+
if (path) {
|
|
106
|
+
console.info(`> Serving client side config ${path} at ${CLIENT_CONFIG_FILE_NAME}`)
|
|
107
|
+
try {
|
|
108
|
+
// Load the client config file
|
|
109
|
+
let clientConfig = (await readFile(path as string)).toString()
|
|
110
|
+
// Serve the config file
|
|
111
|
+
app.get(`/${CLIENT_CONFIG_FILE_NAME}`, async (req: Request, res: Response) => {
|
|
112
|
+
// Reload each time in debug mode
|
|
113
|
+
if (this.debug) {
|
|
114
|
+
console.info('[Debug mode] Reloading config file', path)
|
|
115
|
+
clientConfig = (await readFile(path)).toString()
|
|
116
|
+
}
|
|
117
|
+
// Send the config file
|
|
118
|
+
res
|
|
119
|
+
.contentType('application/javascript')
|
|
120
|
+
.send(clientConfig)
|
|
121
|
+
})
|
|
122
|
+
} catch (e) {
|
|
123
|
+
console.error(`Error loading client config file ${path}`, e)
|
|
124
|
+
throw new Error(`Error loading client config file ${path}: ${e.message}`)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Load config files
|
|
131
|
+
* This is the main config file and the user config file
|
|
132
|
+
*/
|
|
133
|
+
async loadConfigFiles() {
|
|
134
|
+
// Load the user config file
|
|
135
|
+
await this.loadUserConfig()
|
|
136
|
+
// Load the main config file
|
|
137
|
+
await this.loadSilexConfig()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Load user config file
|
|
142
|
+
* This is the config file passed as env var CONFIG
|
|
143
|
+
*/
|
|
144
|
+
async loadUserConfig() {
|
|
145
|
+
if (this.userConfigPath) {
|
|
146
|
+
console.info('> Loading user config', this.userConfigPath)
|
|
147
|
+
try {
|
|
148
|
+
// Initiate the process with the config file which is just another plugin
|
|
149
|
+
await this.addPlugin(this.userConfigPath, {})
|
|
150
|
+
} catch (e) {
|
|
151
|
+
throw new Error(`\nUser config file ${this.userConfigPath} error:\n\n\t${e.message}\n\n`)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Load .silex.js in the root folder
|
|
158
|
+
* This is the main config file
|
|
159
|
+
*/
|
|
160
|
+
async loadSilexConfig() {
|
|
161
|
+
console.info('> Loading config', this.configFilePath)
|
|
162
|
+
try {
|
|
163
|
+
// Initiate the process with the config file which is just another plugin
|
|
164
|
+
await this.addPlugin(this.configFilePath, {})
|
|
165
|
+
} catch(e) {
|
|
166
|
+
// Check if the error is about the config file not found and not another module not found in the config file
|
|
167
|
+
if(e.code === 'MODULE_NOT_FOUND' && (!e.requireStack || !e.requireStack.find(path => path === this.configFilePath))) {
|
|
168
|
+
console.info('> /!\\ Config file not found', this.configFilePath)
|
|
169
|
+
} else {
|
|
170
|
+
throw new Error(`Error in config file ${this.configFilePath}: ${e.message}`)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async initDefaultConnectors() {
|
|
176
|
+
// Add default storage connectors
|
|
177
|
+
if (!this.storageConnectors.length) {
|
|
178
|
+
this.addStorageConnector(new FsStorage(null, {
|
|
179
|
+
path: process.env.SILEX_FS_ROOT,
|
|
180
|
+
}))
|
|
181
|
+
}
|
|
182
|
+
// Add default hosting connectors
|
|
183
|
+
if (!this.hostingConnectors.length) {
|
|
184
|
+
this.addHostingConnector(new FsHosting(null, {
|
|
185
|
+
path: process.env.SILEX_FS_ROOT,
|
|
186
|
+
}))
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|